Skip to main content

Overview

P.FLEX provides 10+ integrated modules designed specifically for flexible packaging manufacturing. Each feature is built to handle the unique challenges of multi-stage production, complex tooling management, and real-time plant operations.

Dashboard & KPIs

Work Order Management

Production Scheduling

Inventory Control

Quality & CAPA

Production Reports

User Administration

Audit Trail

Analytics

Offline Sync

1. Real-Time Dashboard

The control center for your entire plant operation.

Live KPI Monitoring

src/features/dashboard/dashboard.component.ts
// Dashboard provides real-time calculated metrics
get activeProduction() {
  return this.ordersService.ots
    .filter(ot => ot.Estado_pedido === 'EN PROCESO')
    .slice(0, 5);
}

get stats() {
  const all = this.ordersService.ots;
  let totalMeters = 0;
  
  all.forEach(ot => {
    const mtl = parseFloat(String(ot.total_mtl || '0').replace(/,/g, ''));
    if (!isNaN(mtl)) totalMeters += mtl;
  });

  return {
    pending: all.filter(ot => ot.Estado_pedido === 'PENDIENTE').length,
    totalMeters: Math.round(totalMeters)
  };
}
Key Metrics:
  • OEE (Overall Equipment Effectiveness): Target >80%, tracks availability × performance × quality
  • Active Work Orders: In-progress production with real-time status
  • Active Incidents: Quality issues and machine problems requiring attention
  • Production Volume: Total meters produced (current shift/day/month)
  • Energy Efficiency: Power consumption relative to output
  • Sync Queue: Offline operations pending synchronization
All KPIs update in real-time using Angular signals. No manual refresh required.

Operations Feed

Live activity stream with three filtered views:

Production

Active work orders, machines running, meters produced

Alerts

High-priority incidents, machine failures, quality issues

Stock

Material arrivals, critical inventory levels, reorder alerts
get feedItems() {
  const items: any[] = [];

  // 1. Production OTs
  this.ordersService.ots.forEach(ot => {
    if (ot.Estado_pedido === 'EN PROCESO') {
      items.push({
        type: 'PRODUCTION',
        title: `OT #${ot.OT} - Producción en Curso`,
        description: ot.descripcion,
        machine: ot.maquina,
        time: new Date(),
        icon: 'conveyor_belt',
        data: ot
      });
    }
  });

  // 2. Incidents
  this.qualityService.activeIncidents.forEach(inc => {
    items.push({
      type: 'ALERTS',
      title: `Incidencia: ${inc.title}`,
      description: inc.description,
      machine: inc.machineRef,
      icon: inc.priority === 'Alta' ? 'warning' : 'info',
      colorClass: inc.priority === 'Alta' ? 'text-red-400' : 'text-yellow-400'
    });
  });

  return items.sort((a, b) => b.time.getTime() - a.time.getTime());
}

Quick Actions

Nueva OT

Create work order directly from dashboard

Importar

Bulk upload orders from Excel

Reportar Falla

Log incident for immediate attention

Escanear QR

Quick tooling or material lookup

Export Capabilities

Generate reports in multiple formats:
// Export to PDF (visual snapshot)
async exportToPdf() {
  const element = this.dashboardContent.nativeElement;
  
  const canvas = await html2canvas(element, {
    scale: 2,
    backgroundColor: '#0B0E14',
    logging: false,
    useCORS: true
  });

  const imgData = canvas.toDataURL('image/png');
  const pdf = new jsPDF('l', 'mm', 'a4');
  pdf.addImage(imgData, 'PNG', 0, 0, 297, pageHeight);
  pdf.save(`Dashboard_Report_${dateStr}.pdf`);
}

// Export to Excel (data tables)
exportToExcel() {
  const wb = XLSX.utils.book_new();
  
  // Multiple sheets: KPIs, Active Orders, Feed Activity
  XLSX.writeFile(wb, `Dashboard_Data_${dateStr}.xlsx`);
}

2. Work Order Management

Comprehensive OT (Orden de Trabajo) lifecycle management.

OT Data Model

src/features/orders/models/orders.models.ts
export interface OT {
  OT: string;                    // Unique order number
  'Razon Social': string;        // Client name
  descripcion: string;           // Product description
  impresion?: string;            // Print specifications
  total_mtl: number | string;    // Total meters
  maquina?: string;              // Assigned machine
  Estado_pedido: 'PENDIENTE' | 'EN PROCESO' | 'FINALIZADA';
  fecha_ingreso?: string;
  fecha_entrega?: string;
  observaciones?: string;
}

Create, Edit, and Delete

1

Manual Entry

Form with validation for all required fields:
  • Auto-incrementing OT numbers
  • Client search/autocomplete
  • Machine availability check
2

Excel Import

Bulk upload with intelligent column mapping:
src/features/orders/components/ot-import.component.ts
// Excel import supports flexible column headers
const COLUMN_MAPPINGS = {
  'ot': ['ot', 'orden', 'op', 'numero', 'pedido'],
  'cliente': ['razon social', 'cliente', 'client', 'customer'],
  'descripcion': ['descripcion', 'producto', 'item'],
  'metros': ['metros', 'total mtl', 'cantidad']
};

normalizeColumnName(raw: string): string {
  return raw.toLowerCase().trim()
    .replace(/[áàäâ]/g, 'a')
    .replace(/[éèëê]/g, 'e')
    .replace(/\s+/g, ' ');
}
3

Database Search

Query internal database of historical orders:
  • Search by OT number, client, or product
  • Copy/clone previous orders
  • Maintain production history

Status Workflow

Status updates trigger audit logs and notifications:
statusColors = {
  'PENDIENTE': 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',
  'EN PROCESO': 'bg-blue-500/10 text-blue-400 border-blue-500/20',
  'FINALIZADA': 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',
  'DESPACHADA': 'bg-slate-500/10 text-slate-400 border-slate-500/20'
};

OT Detail View

Click any order to see comprehensive information:
  • Client and product details
  • Production specifications (print colors, substrate, finishing)
  • Assigned machine and operator
  • Progress tracking (meters completed / total)
  • Related quality incidents
  • Attached files (artwork, specifications)
Orders in “EN PROCESO” status automatically appear in the production feed and operator assignments.

3. Production Scheduling

Visual Gantt chart for time-based planning.

Multi-Area Support

src/features/planning/schedule.component.ts
selectedArea: 'IMPRESION' | 'TROQUELADO' | 'REBOBINADO' = 'IMPRESION';

get filteredMachines() {
  let typeFilter = 'Impresión';
  if (this.selectedArea === 'TROQUELADO') typeFilter = 'Troquelado';
  if (this.selectedArea === 'REBOBINADO') typeFilter = 'Acabado';
  return this.state.adminMachines().filter(m => m.type === typeFilter);
}
Switch between production areas:
  • Impresión: 9 flexographic printing presses
  • Troquelado: 12 rotary die-cutting machines
  • Rebobinado: 13 rewinding/finishing lines

Shift-Based Scheduling

Turno Día

07:00 - 18:00 (12-hour timeline)

Turno Noche

19:00 - 06:00 (12-hour timeline)
get timeSlots() {
  return this.selectedShift === 'DIA' 
    ? ['07:00', '08:00', '09:00', '10:00', '11:00', '12:00', 
       '13:00', '14:00', '15:00', '16:00', '17:00', '18:00']
    : ['19:00', '20:00', '21:00', '22:00', '23:00', '00:00', 
       '01:00', '02:00', '03:00', '04:00', '05:00', '06:00'];
}

Real-Time Machine Status

Edit machine status directly from the schedule:
// Machine status with visual indicators
type MachineStatus = 
  | 'Operativa'      // Green - Ready for production
  | 'Mantenimiento'  // Amber - Scheduled maintenance
  | 'Detenida'       // Red - Unplanned downtime
  | 'Sin Operador';  // Gray - Waiting for operator

updateMachineStatus(machine: any, newStatus: any) {
  this.state.updateMachine({ ...machine, status: newStatus });
  // Automatically logged to audit trail
}

Job Assignment

Drag-and-drop scheduling interface:
1

Select OT and Machine

Choose work order and target machine
2

Set Time Window

saveJob() {
  if (this.tempStartDateTime) {
    const d = new Date(this.tempStartDateTime);
    this.currentJob.start = 
      `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`;
  }
  this.currentJob.duration = Math.round(this.tempDurationHours * 60);
  
  if (this.isEditing) {
    this._jobs = this._jobs.map(j => 
      j.id === this.currentJob.id ? this.currentJob : j
    );
  } else {
    this.currentJob.id = Math.random().toString(36).substr(2, 9);
    this._jobs.push(this.currentJob);
  }
}
3

Validate Resources

Automatic checks:
  • Cliché availability in warehouse
  • Die condition (OK / Desgaste / Dañado)
  • Substrate material stock levels
4

Assign Operator

Select responsible operator from shift roster

Visual Features

  • NOW Line: Real-time indicator showing current time position
  • Color-coded jobs: Different colors for different clients/priorities
  • Overlapping detection: Warns if assignments conflict
  • Maintenance patterns: Visual striping for non-operational machines
// Calculate NOW line position
updateNowLine() {
  const now = new Date();
  const offset = this.getHourOffset(now.getHours(), now.getMinutes());
  if (offset >= 0 && offset <= 12) {
    this.currentLinePosition = `calc((1 - ${offset / 12}) * 16rem + ${(offset / 12) * 100}%)`;
    this.showNowLine = true;
  } else {
    this.showNowLine = false;
  }
}

KPI Integration

Schedule screen includes live plant metrics:
get kpiCriticalAlerts() {
  return this.qualityService.activeIncidents
    .filter(i => i.priority === 'Alta').length;
}

get kpiEfficiency() {
  const machines = this.state.adminMachines();
  const active = machines.filter(m => m.status === 'Operativa').length;
  return Math.round((active / machines.length) * 100);
}

get kpiPendingJobs() {
  return this.ordersService.ots
    .filter(ot => ot.Estado_pedido === 'PENDIENTE').length;
}

4. Inventory Management

Three-tier inventory system: Tooling, Materials, and Finished Goods.

Clichés (Printing Plates)

Track thousands of printing plates with precise location mapping:
src/features/inventory/services/inventory.service.ts
export interface CliseItem {
  id: string;
  item: string;           // Unique code (e.g., "CL-1234")
  ubicacion: string;      // Rack location (e.g., "CL-1 / Nivel 2")
  descripcion: string;    // Product description
  cliente: string;        // Client name
  z: string;              // Teeth count
  ancho: number;          // Width in mm
  avance: number;         // Repeat length in mm
  col: number;            // Columns
  rep: number;            // Repeats per revolution
  n_clises: number;       // Number of plates in set
  colores: string;        // Colors (e.g., "CMYK + Barniz")
  mtl_acum: number;       // Accumulated meters
  ingreso: string;        // Entry date
  linkedDies: string[];   // Associated dies
  history: any[];         // Usage history
}
Features:
  • Excel bulk import with intelligent column mapping
  • Visual warehouse map with rack locations
  • Search by item code, client, or dimensions
  • Track accumulated wear (meters used)
  • Link clichés to specific dies for complete setups

Dies (Troqueles)

Rotary die management with condition tracking:
export interface DieItem {
  id: string;
  serie: string;          // Die series code
  cliente: string;
  medida: string;         // Dimensions (e.g., "250mm x 300mm")
  ubicacion: string;
  z: string;              // Teeth/gear
  ancho_mm: number;
  avance_mm: number;
  columnas: number;       // Cavities/columns
  repeticiones: number;
  material: string;       // Substrate (e.g., "Polipropileno")
  forma: string;          // Shape/geometry
  estado: 'OK' | 'Desgaste' | 'Dañado' | 'Reparación';
  mtl_acum: number;
  linkedClises: string[];
}
Condition Tracking:
  • OK: Ready for production (green badge)
  • Desgaste: Showing wear, plan replacement (yellow)
  • Dañado: Requires repair (red)
  • Reparación: Currently being serviced (gray)

Finished Goods (Stock PT)

Warehouse management for completed production:
export interface StockItem {
  id: string;
  ot: string;             // Work order number
  client: string;
  product: string;
  quantity: number;
  unit: 'Rollos' | 'Cajas' | 'Millares';
  rolls: number;          // Number of rolls
  millares: number;       // Quantity in thousands
  location: string;       // Warehouse position (e.g., "DES-A-01")
  status: 'Liberado' | 'Cuarentena' | 'Retenido' | 'Despachado';
  entryDate: string;
  palletId: string;
  notes?: string;
}
Quality Status Workflow:
  1. Cuarentena: Awaiting quality inspection
  2. Liberado: Approved for shipment (green)
  3. Retenido: Failed quality, held (red)
  4. Despachado: Shipped to customer (gray)

Warehouse Visualization

Interactive rack layout showing capacity:
// Rack configuration with box ranges
readonly _layoutData = signal<RackConfig[]>([
  {
    id: 'CL1',
    name: 'RACK CL-1 (861-1190)',
    type: 'clise',
    orientation: 'vertical',
    levels: [
      {
        levelNumber: 3,
        boxes: [
          { label: '1081-1135', min: 1081, max: 1135, items: [] },
          { label: '1136-1190', min: 1136, max: 1190, items: [] }
        ]
      },
      // Additional levels...
    ]
  },
  // Additional racks...
]);

// Auto-map items to racks based on ubicacion
mapItemsToLayout() {
  const currentLayout = this.layoutData;
  const clises = this.cliseItems;
  
  clises.forEach(item => {
    const locationNumber = parseInt(item.ubicacion.replace(/\D/g, ''));
    for (const rack of currentLayout) {
      for (const level of rack.levels) {
        for (const box of level.boxes) {
          if (locationNumber >= box.min && locationNumber <= box.max) {
            box.items.push(item);
            return;
          }
        }
      }
    }
  });
}

Heat Map View

Color-coded boxes show capacity: Green (space available), Yellow (75%+ full), Red (at capacity)

Quick Search

Type item code to instantly highlight its location on the map

Excel Import/Export

Supports flexible column headers:
readonly CLISE_MAPPING = {
  'item': ['item', 'codigo', 'code', 'id', 'clise'],
  'ubicacion': ['ubicación', 'ubicacion', 'location'],
  'descripcion': ['descripción', 'descripcion', 'description'],
  'cliente': ['cliente', 'client'],
  'ancho': ['ancho', 'width'],
  'avance': ['avance', 'length'],
  'mtl_acum': ['mtl acum', 'mtl. acum.', 'metros acumulados']
};

normalizeData(rawData: any[], mapping: any): any[] {
  return rawData.map(row => {
    const normalized: any = {};
    for (const [targetKey, possibleHeaders] of Object.entries(mapping)) {
      for (const header of possibleHeaders as string[]) {
        if (row[header] !== undefined) {
          normalized[targetKey] = row[header];
          break;
        }
      }
    }
    return normalized;
  });
}
The import wizard shows conflicts (missing required fields) before committing, allowing you to fix data issues.

5. Quality Management & CAPA

Comprehensive incident tracking with Corrective and Preventive Action system.

Incident Reporting

src/features/quality/services/quality.service.ts
export interface Incident {
  id: string;
  code: string;                    // Auto-generated (e.g., "INC-2024-089")
  title: string;
  description: string;
  priority: 'Alta' | 'Media' | 'Baja';
  type: 'Maquinaria' | 'Material' | 'Calidad' | 'Proceso' | 'Seguridad' | 'Otro';
  status: 'Abierta' | 'Acción Correctiva' | 'Verificación' | 'Cerrada';
  otRef?: string;                  // Related work order
  machineRef?: string;             // Related machine
  reportedBy: string;
  reportedAt: Date;
  assignedTo: string;              // Department or person
  rootCause?: string;              // RCA (Root Cause Analysis)
  actions: CapaAction[];           // CAPA actions
}

export interface CapaAction {
  id: string;
  description: string;
  type: 'Correctiva' | 'Preventiva';
  responsible: string;
  deadline: string;
  completed: boolean;
}

Incident Workflow

1

Report Incident

Any user can create an incident with:
  • Title and detailed description
  • Priority level (determines response time)
  • Incident type for categorization
  • Optional: Related OT and machine
2

Assign Responsibility

Route to appropriate department:
  • Mantenimiento (Maintenance)
  • Almacén (Warehouse)
  • Calidad (Quality)
  • Producción (Production)
3

Root Cause Analysis

Document findings:
rootCause: 'Fatiga de material por falta de lubricación en el turno anterior.'
4

CAPA Actions

Add corrective and preventive measures:
addCapaAction(incidentId: string, action: Partial<CapaAction>) {
  const newAction: CapaAction = {
    id: Math.random().toString(36).substr(2, 9),
    description: action.description || '',
    type: action.type || 'Correctiva',
    responsible: action.responsible || '',
    deadline: action.deadline || new Date().toISOString().split('T')[0],
    completed: false
  };
  
  // Update status to 'Acción Correctiva'
  this.audit.log(this.state.userName(), this.state.userRole(), 
    'CALIDAD', 'Agregar Acción CAPA', 
    `Acción ${newAction.type} agregada a ${incident.code}`);
}
5

Track Completion

Monitor action progress:
  • Mark individual actions as completed
  • Verify effectiveness
  • Close incident when resolved

Priority-Based Alerts

Alta (High Priority):
  • Red badges throughout the system
  • Appears in dashboard critical alerts KPI
  • Requires immediate management attention
  • Examples: Machine breakdown, safety incident, customer complaint
Media (Medium Priority):
  • Yellow badges
  • Normal workflow
  • Examples: Minor quality deviation, material shortage
Baja (Low Priority):
  • Blue badges
  • Routine issues
  • Examples: Process improvement suggestion, documentation update

Metrics and Reporting

// Quality KPIs
get activeIncidents() {
  return this.incidents.filter(i => i.status !== 'Cerrada');
}

get closedIncidents() {
  return this.incidents.filter(i => i.status === 'Cerrada');
}

get incidentsByType() {
  return {
    maquinaria: this.incidents.filter(i => i.type === 'Maquinaria').length,
    material: this.incidents.filter(i => i.type === 'Material').length,
    calidad: this.incidents.filter(i => i.type === 'Calidad').length
  };
}
All incident actions are logged to the audit trail with user, timestamp, and IP for complete traceability.

6. Production Reports

Detailed reporting for print, die-cut, and finishing operations.
src/features/production/production-print.component.ts
export interface PrintReport {
  id: string;
  date: Date;
  ot: string;
  client: string;
  product: string;
  machine: string;
  operator: string;
  shift: string;
  activities: PrintActivity[];
  totalMeters: number;
  clise: { code: string; status: string };
  die: { status: string };
  observations: string;
  productionStatus: 'PARCIAL' | 'TOTAL';
}

export interface PrintActivity {
  type: string;          // 'Setup', 'Impresión', 'Parada', etc.
  startTime: string;
  endTime: string;
  meters: number;
  duration?: string;
}
Report Includes:
  • Complete time breakdown (setup, run, downtime)
  • Meters produced per activity
  • Tooling condition (cliché and die status)
  • Operator notes and observations
  • Quality checkpoints

Activity Breakdown

activities: [
  { type: 'Setup', startTime: '07:00', endTime: '08:30', meters: 0 },
  { type: 'Impresión', startTime: '08:30', endTime: '11:00', meters: 6000 },
  { type: 'Parada - Cambio Bobina', startTime: '11:00', endTime: '11:15', meters: 0 },
  { type: 'Impresión', startTime: '11:15', endTime: '13:00', meters: 9000 }
]

Production KPIs

get kpis() {
  const totalMeters = this.reports.reduce((acc, r) => acc + r.totalMeters, 0);
  const totalHours = this.reports.length * 6;
  const avgSpeed = (totalMeters / (totalHours * 60)) * 5; // m/min
  
  return {
    totalMeters,
    avgSpeed,
    completedOts: this.reports.filter(r => r.productionStatus === 'TOTAL').length,
    toolingIssues: this.reports.filter(r => 
      r.die.status !== 'OK' || r.clise.status !== 'OK'
    ).length
  };
}

Total Meters

Aggregated production volume (shift/day/month)

Average Speed

Meters per minute across all machines

Completed Orders

Number of fully completed work orders

Tooling Issues

Clichés or dies requiring attention

Tooling Status Tracking

Every report logs condition:
getStatusClass(status: string) {
  switch(status) {
    case 'OK':
      return 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20';
    case 'Desgaste':
      return 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20';
    case 'Dañado':
      return 'bg-red-500/10 text-red-400 border-red-500/20';
    default:
      return 'bg-slate-500/10 text-slate-400';
  }
}
This feeds into predictive maintenance:
  • Track accumulated meters per cliché/die
  • Alert when approaching replacement thresholds
  • Schedule maintenance proactively

7. User Administration

Role-based access control with granular permissions.

User Roles

src/services/state.service.ts
export type UserRole = 
  | 'Jefatura'     // Management - Full access
  | 'Supervisor'   // Shift supervision
  | 'Asistente'    // Assistant/coordinator
  | 'Operario'     // Machine operator
  | 'Encargado'    // Department head
  | 'Sistemas';    // IT administrator

readonly adminRoles = signal<RoleDefinition[]>([
  {
    id: 'r1',
    name: 'Jefatura',
    description: 'Acceso total a reportes, KPIs y aprobación.',
    permissions: ['Ver Dashboard', 'Aprobar OTs', 'Reportes', 'Gestión Usuarios']
  },
  {
    id: 'r2',
    name: 'Supervisor',
    description: 'Gestión de turno y asignación.',
    permissions: ['Asignar Tareas', 'Cerrar Turno', 'Validar Calidad', 'Ver OTs']
  },
  {
    id: 'r3',
    name: 'Operario',
    description: 'Registro de producción.',
    permissions: ['Registrar Producción', 'Ver OTs']
  },
  {
    id: 'r4',
    name: 'Sistemas',
    description: 'Configuración técnica.',
    permissions: ['Admin Total']
  }
]);

Permission Matrix

FeatureOperarioSupervisorJefaturaSistemas
View DashboardLimited
Create OTs
Schedule Production
Register Production
Manage Inventory
View ReportsLimited
Manage UsersPartial
System Config

User Management Interface

Administrators can:
  • Create/edit/deactivate users
  • Assign roles and permissions
  • Set password policies (expiry, complexity)
  • Configure auto-logout timeouts
  • Manage shift assignments
readonly config = signal<SystemConfig>({
  shiftName1: 'Turno Día',
  shiftTime1: '06:00',
  shiftName2: 'Turno Noche',
  shiftTime2: '18:00',
  passwordExpiryWarningDays: 15,
  passwordPolicyDays: 90,
  plantName: 'Planta Central - Zona Industrial',
  autoLogoutMinutes: 30
});

Machine Registry

readonly adminMachines = signal<Machine[]>([
  // IMPRESIÓN (9 machines)
  { id: 'p1', code: 'IMP-01', name: 'SUPERPRINT 1', 
    type: 'Impresión', area: 'Nave A', status: 'Operativa', active: true },
  { id: 'p2', code: 'IMP-02', name: 'SUPERPRINT 2', 
    type: 'Impresión', area: 'Nave A', status: 'Operativa', active: true },
  // ...
  
  // TROQUELADO (12 machines)
  { id: 'd1', code: 'TRQ-01', name: 'PLANA 1', 
    type: 'Troquelado', area: 'Nave C', status: 'Operativa', active: true },
  // ...
  
  // ACABADO (13 machines)
  { id: 'r1', code: 'RBB-01', name: 'REBOBINADORA 1', 
    type: 'Acabado', area: 'Nave D', status: 'Operativa', active: true },
  // ...
]);
P.FLEX tracks 34 machines across three production areas by default. Add or modify machines through the admin interface.

8. Audit Trail

Complete traceability of all system operations.
src/services/audit.service.ts
export interface AuditLog {
  id: string;
  timestamp: Date;
  user: string;
  role: string;
  module: string;        // 'ACCESO', 'OPERACIONES', 'INVENTARIO', 'CALIDAD', etc.
  action: string;        // 'Inicio de Sesión', 'Crear OT', 'Actualizar Estado', etc.
  details: string;       // Full description
  ip: string;            // Source IP address
}

@Injectable({ providedIn: 'root' })
export class AuditService {
  private _logs = signal<AuditLog[]>([]);
  
  logs = computed(() => this._logs());
  
  log(user: string, role: string, module: string, action: string, details: string) {
    const entry: AuditLog = {
      id: Math.random().toString(36).substr(2, 9),
      timestamp: new Date(),
      user,
      role,
      module,
      action,
      details,
      ip: this.getClientIP()
    };
    
    this._logs.update(logs => [entry, ...logs].slice(0, 1000)); // Keep last 1000
  }
  
  private getClientIP(): string {
    // In production, capture from request headers
    return '192.168.1.' + Math.floor(Math.random() * 255);
  }
}

Logged Events

Access Control:
  • User login/logout
  • Failed authentication attempts
  • Permission changes
Operations:
  • Work order creation/modification
  • Status changes
  • Production scheduling
  • Machine status updates
Inventory:
  • Tooling additions/edits
  • Stock movements
  • Location changes
Quality:
  • Incident reports
  • CAPA actions
  • Incident closure

Audit View

src/features/audit/audit.component.ts
@Component({
  template: `
    <table>
      <thead>
        <tr>
          <th>Fecha / Hora</th>
          <th>Usuario / Rol</th>
          <th>Módulo</th>
          <th>Acción</th>
          <th>IP Origen</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let log of audit.logs()">
          <td>{{ log.timestamp | date:'dd/MM/yyyy HH:mm:ss' }}</td>
          <td>
            <div>{{ log.user }}</div>
            <div class="text-xs">{{ log.role }}</div>
          </td>
          <td>
            <span class="badge">{{ log.module }}</span>
          </td>
          <td>
            <div>{{ log.action }}</div>
            <div class="text-xs">{{ log.details }}</div>
          </td>
          <td>{{ log.ip }}</td>
        </tr>
      </tbody>
    </table>
  `
})
export class AuditComponent {
  audit = inject(AuditService);
}
Audit logs are immutable and cannot be edited or deleted by any user, ensuring compliance and forensic capabilities.

9. Analytics & Reporting

Data visualization and business intelligence.

Report Types

Production Summary

Daily/weekly/monthly output by machine, operator, and client

Quality Metrics

Incident trends, CAPA effectiveness, defect rates

OEE Analysis

Availability, Performance, Quality breakdown by machine

Inventory Reports

Stock levels, turnover rates, tooling utilization

Export Formats

PDF:
import jsPDF from 'jspdf';
import html2canvas from 'html2canvas';

async exportToPdf() {
  const element = document.getElementById('report-content');
  const canvas = await html2canvas(element, {
    scale: 2,
    backgroundColor: '#0B0E14'
  });
  
  const imgData = canvas.toDataURL('image/png');
  const pdf = new jsPDF('l', 'mm', 'a4');
  pdf.addImage(imgData, 'PNG', 0, 0, 297, pageHeight);
  pdf.save(`Report_${dateStr}.pdf`);
}
Excel:
import * as XLSX from 'xlsx';

exportToExcel() {
  const wb = XLSX.utils.book_new();
  
  // Sheet 1: Summary
  const summaryData = [[...], [...]];
  const ws1 = XLSX.utils.aoa_to_sheet(summaryData);
  XLSX.utils.book_append_sheet(wb, ws1, "Resumen");
  
  // Sheet 2: Details
  const detailData = this.records.map(r => ({...}));
  const ws2 = XLSX.utils.json_to_sheet(detailData);
  XLSX.utils.book_append_sheet(wb, ws2, "Detalle");
  
  XLSX.writeFile(wb, `Report_${dateStr}.xlsx`);
}

10. Offline Sync

Resiliency for unreliable network environments.

Architecture

export type SyncStatus = 'online' | 'offline' | 'syncing' | 'conflict';

readonly syncStatus = signal<SyncStatus>('online');
readonly pendingSyncCount = signal<number>(0);
How it works:
  1. Local Storage: All operations save to browser IndexedDB immediately
  2. Queue Building: Offline changes accumulate in sync queue
  3. Auto-Sync: When connection restores, queued operations push to server
  4. Conflict Resolution: Detect and merge concurrent changes

User Experience

Online

Green badge - Normal operation, real-time sync

Offline

Red badge - Working locally, queue building

Syncing

Blue badge - Pushing queued changes to server

Conflict

Yellow badge - Manual intervention required
setSyncStatus(status: SyncStatus) {
  this.syncStatus.set(status);
  
  // Show notification to user
  if (status === 'offline') {
    this.showToast('Sin conexión - Los datos se guardarán localmente');
  } else if (status === 'online') {
    this.showToast('Conexión restaurada - Sincronizando...');
    this.triggerSync();
  }
}
The sync queue is visible in the dashboard KPI panel, showing how many operations are pending upload.

Summary

P.FLEX integrates all aspects of flexible packaging manufacturing into a unified, real-time system:

For Operators

Simple interface to register production, report issues, and view assignments without complexity

For Supervisors

Full visibility into plant status with tools to schedule, validate, and respond to incidents

For Management

Real-time KPIs, analytics, and reporting to drive strategic decisions

For IT/Systems

Robust configuration, user management, and audit capabilities for compliance

Next Steps

Quick Start

Install and configure P.FLEX

API Reference

Integrate with external systems

Best Practices

Optimization tips and workflows

Build docs developers (and LLMs) love