Skip to main content

Overview

The OrdersService manages work orders (Órdenes de Trabajo - OTs) using RxJS BehaviorSubjects. It maintains two separate data stores:
  1. Active OTs - Currently visible work orders in the application
  2. Internal Database - Complete imported OT database for search and reference
Location: src/features/orders/services/orders.service.ts Provider: Root (singleton)

Properties

Active OT List

ots
Partial<OT>[]
Snapshot of currently active work orders visible in the application.
ots$
Observable<Partial<OT>[]>
Observable stream of active OTs for reactive subscriptions.

Internal Database

internalDatabase
Partial<OT>[]
Snapshot of the complete imported OT database.
dbLastUpdated
Date | null
Timestamp of the last database update, null if never updated.
dbLastUpdated$
Observable<Date | null>
Observable stream of last update timestamp.

Methods

updateOts

Replaces the entire active OT list with new data.
updateOts(newOts: Partial<OT>[]): void
newOts
Partial<OT>[]
required
Array of OT objects to set as the active list.
Behavior:
  • Completely replaces current active OT list
  • Emits new value to all ots$ subscribers
  • Does not affect internal database
import { inject } from '@angular/core';
import { OrdersService } from './features/orders/services/orders.service';

const ordersService = inject(OrdersService);

const newOrders: Partial<OT>[] = [
  {
    OT: '45001',
    'Razon Social': 'Coca Cola',
    descripcion: 'Etiqueta 500ml Original',
    cantidad: 50000,
    estado: 'En Proceso'
  },
  {
    OT: '45002',
    'Razon Social': 'Nestle',
    descripcion: 'Empaque Galleta Chocolate',
    cantidad: 30000,
    estado: 'Pendiente'
  }
];

ordersService.updateOts(newOrders);

deleteOt

Removes a specific OT from the active list by its ID.
deleteOt(otId: string): void
otId
string
required
The OT identifier to remove. Converted to string for comparison.
Behavior:
  • Filters out the OT matching the provided ID
  • Converts both stored and provided IDs to strings for comparison
  • Emits updated list to subscribers
  • Does not affect internal database
// Delete OT by ID
ordersService.deleteOt('45001');

// Also works with numeric IDs
ordersService.deleteOt('45002');

// Verify deletion
const remaining = ordersService.ots;
console.log('Remaining OTs:', remaining.length);

updateInternalDatabase

Updates the internal database with imported OT data and records timestamp.
updateInternalDatabase(data: Partial<OT>[]): void
data
Partial<OT>[]
required
Complete array of OT data to store in the internal database.
Behavior:
  • Replaces entire internal database
  • Updates dbLastUpdated timestamp to current date/time
  • Does not affect active OT list
  • Enables search functionality via findInDatabase()
import { inject } from '@angular/core';
import { OrdersService } from './features/orders/services/orders.service';
import * as XLSX from 'xlsx';

const ordersService = inject(OrdersService);

// After importing Excel file
const workbook = XLSX.read(fileData);
const sheet = workbook.Sheets[workbook.SheetNames[0]];
const importedData: any[] = XLSX.utils.sheet_to_json(sheet);

// Store in internal database
ordersService.updateInternalDatabase(importedData);

// Check last update time
const lastUpdate = ordersService.dbLastUpdated;
console.log('Database updated:', lastUpdate);

findInDatabase

Searches the internal database for OTs matching the search term.
findInDatabase(searchTerm: string): Partial<OT>[]
searchTerm
string
required
Search query to match against OT number, client name, or description.
Returns: Array of matching OT objects Search Fields:
  • OT - Work order number
  • Razon Social - Client/company name
  • descripcion - Product description
Behavior:
  • Case-insensitive search
  • Matches partial strings (includes)
  • Searches across OT number, client name, and description
  • Returns empty array if no matches found
import { inject } from '@angular/core';
import { OrdersService } from './features/orders/services/orders.service';

const ordersService = inject(OrdersService);

// Search by OT number
const byNumber = ordersService.findInDatabase('45001');
console.log('Found by number:', byNumber);

// Search by client name
const byClient = ordersService.findInDatabase('coca');
console.log('Coca Cola orders:', byClient);

// Search by description
const byProduct = ordersService.findInDatabase('etiqueta');
console.log('Label orders:', byProduct);

// Case insensitive
const results = ordersService.findInDatabase('NESTLE');
console.log('Results:', results.length);

Usage Examples

Subscribe to Active OTs

import { Component, inject, OnInit } from '@angular/core';
import { OrdersService } from './features/orders/services/orders.service';
import { OT } from './features/orders/models/orders.models';

@Component({
  selector: 'app-orders-list',
  template: `
    <div class="orders-list">
      @for (ot of activeOts; track ot.OT) {
        <div class="order-card">
          <h3>OT {{ ot.OT }}</h3>
          <p>{{ ot['Razon Social'] }}</p>
          <p>{{ ot.descripcion }}</p>
          <button (click)="removeOrder(ot.OT!)">Eliminar</button>
        </div>
      }
    </div>
  `
})
export class OrdersListComponent implements OnInit {
  ordersService = inject(OrdersService);
  activeOts: Partial<OT>[] = [];
  
  ngOnInit() {
    // Subscribe to active OT changes
    this.ordersService.ots$.subscribe(ots => {
      this.activeOts = ots;
      console.log('Active OTs updated:', ots.length);
    });
  }
  
  removeOrder(otId: string) {
    this.ordersService.deleteOt(otId);
  }
}

Import and Search Database

import { Component, inject } from '@angular/core';
import { OrdersService } from './features/orders/services/orders.service';
import * as XLSX from 'xlsx';

@Component({
  selector: 'app-orders-import',
  template: `
    <div>
      <input type="file" (change)="onFileSelected($event)" accept=".xlsx,.xls">
      <input type="text" [(ngModel)]="searchTerm" (input)="onSearch()">
      <p>Última actualización: {{ lastUpdate | date:'short' }}</p>
      
      <div class="search-results">
        @for (result of searchResults; track result.OT) {
          <div>{{ result.OT }} - {{ result['Razon Social'] }}</div>
        }
      </div>
    </div>
  `
})
export class OrdersImportComponent {
  ordersService = inject(OrdersService);
  searchTerm = '';
  searchResults: any[] = [];
  lastUpdate: Date | null = null;
  
  onFileSelected(event: any) {
    const file = event.target.files[0];
    const reader = new FileReader();
    
    reader.onload = (e: any) => {
      const data = new Uint8Array(e.target.result);
      const workbook = XLSX.read(data, { type: 'array' });
      const sheet = workbook.Sheets[workbook.SheetNames[0]];
      const jsonData = XLSX.utils.sheet_to_json(sheet);
      
      // Update internal database
      this.ordersService.updateInternalDatabase(jsonData);
      this.lastUpdate = this.ordersService.dbLastUpdated;
      
      console.log('Imported OTs:', jsonData.length);
    };
    
    reader.readAsArrayBuffer(file);
  }
  
  onSearch() {
    if (this.searchTerm.length >= 2) {
      this.searchResults = this.ordersService.findInDatabase(this.searchTerm);
    } else {
      this.searchResults = [];
    }
  }
}

Add Selected OTs to Active List

import { inject } from '@angular/core';
import { OrdersService } from './features/orders/services/orders.service';

const ordersService = inject(OrdersService);

// Search for specific OTs in database
const colaOrders = ordersService.findInDatabase('Coca Cola');

// Get current active list
const currentActive = ordersService.ots;

// Add found orders to active list (avoiding duplicates)
const newActive = [
  ...currentActive,
  ...colaOrders.filter(found => 
    !currentActive.some(active => active.OT === found.OT)
  )
];

// Update active list
ordersService.updateOts(newActive);

Monitor Database Updates

import { Component, inject, OnInit } from '@angular/core';
import { OrdersService } from './features/orders/services/orders.service';

@Component({
  selector: 'app-database-status',
  template: `
    <div class="db-status">
      @if (lastUpdate) {
        <p>Base de datos actualizada: {{ lastUpdate | date:'medium' }}</p>
        <p>Total de registros: {{ totalRecords }}</p>
      } @else {
        <p>No hay datos importados</p>
      }
    </div>
  `
})
export class DatabaseStatusComponent implements OnInit {
  ordersService = inject(OrdersService);
  lastUpdate: Date | null = null;
  totalRecords = 0;
  
  ngOnInit() {
    this.ordersService.dbLastUpdated$.subscribe(date => {
      this.lastUpdate = date;
      if (date) {
        this.totalRecords = this.ordersService.internalDatabase.length;
      }
    });
  }
}

OT Data Structure

The OT interface typically includes:
interface OT {
  OT: string | number;                  // Work order number
  'Razon Social': string;               // Client name
  descripcion: string;                  // Product description
  cantidad?: number;                    // Quantity
  estado?: string;                      // Status
  fechaEntrega?: string;                // Delivery date
  // ... additional fields
}
Note: The service uses Partial<OT>[] to allow flexible data structures from various import sources.

Data Flow

Excel Import → updateInternalDatabase() → Internal Database

                                         findInDatabase()

                                         Search Results

User Selection → updateOts() → Active OT List → UI Display

                                deleteOt() → Remove from Active

Notes

  • The service uses RxJS BehaviorSubject for reactive data management
  • Active OT list and internal database are independent
  • Search is performed only on internal database
  • Deleting from active list does not remove from database
  • All comparisons use string conversion for robust ID matching
  • The service does not perform validation - handle at component level

Build docs developers (and LLMs) love