← back to Designer Wallcoverings

DW-Agents/dw-agents/auto-reorder-ai.ts

228 lines

/**
 * Auto-Reorder AI - Intelligent Predictions
 * Analyzes usage patterns and predicts when to reorder
 */

import * as fs from 'fs';

interface InventoryItem {
  item: string;
  quantity: number;
  reorderLevel: number;
  lastOrdered: Date | null;
}

interface Order {
  id: string;
  item: string;
  vendor: string;
  price: number;
  status: string;
  timestamp: Date;
}

interface ReorderPrediction {
  item: string;
  currentQuantity: number;
  reorderLevel: number;
  daysUntilReorder: number;
  urgency: 'critical' | 'high' | 'medium' | 'low';
  suggestedQuantity: number;
  estimatedCost: number;
  reasoning: string;
  bulkOpportunity?: {
    savings: number;
    quantity: number;
    description: string;
  };
}

/**
 * Calculate usage rate based on order history
 */
function calculateUsageRate(item: string, orders: Order[], inventory: InventoryItem[]): number {
  // Find all orders for this item
  const itemOrders = orders.filter(o =>
    o.item.toLowerCase().includes(item.toLowerCase().split(' ')[0])
  ).sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());

  if (itemOrders.length < 2) {
    // Default usage rate if not enough data
    return 0.1; // Assume 10% per month
  }

  // Calculate average time between orders
  let totalDays = 0;
  for (let i = 1; i < itemOrders.length; i++) {
    const days = (new Date(itemOrders[i].timestamp).getTime() - new Date(itemOrders[i-1].timestamp).getTime()) / (1000 * 60 * 60 * 24);
    totalDays += days;
  }

  const avgDaysBetweenOrders = totalDays / (itemOrders.length - 1);

  // Find current inventory item
  const invItem = inventory.find(inv => inv.item.toLowerCase().includes(item.toLowerCase()));
  if (!invItem) return 0.1;

  // Calculate daily usage rate
  const dailyUsage = invItem.reorderLevel / avgDaysBetweenOrders;

  return dailyUsage;
}

/**
 * Generate AI-powered reorder predictions
 */
export async function generateReorderPredictions(): Promise<ReorderPrediction[]> {
  console.log('🤖 Analyzing inventory and generating AI predictions...\n');

  // Load data
  const ordersPath = '/root/Projects/Designer-Wallcoverings/DW-Agents/data/amazon-orders.json';
  const inventoryPath = '/root/Projects/Designer-Wallcoverings/DW-Agents/data/office-inventory.json';

  if (!fs.existsSync(ordersPath) || !fs.existsSync(inventoryPath)) {
    console.log('❌ Data files not found');
    return [];
  }

  const orders: Order[] = JSON.parse(fs.readFileSync(ordersPath, 'utf-8'));
  const inventory: InventoryItem[] = JSON.parse(fs.readFileSync(inventoryPath, 'utf-8'));

  const predictions: ReorderPrediction[] = [];

  for (const item of inventory) {
    // Calculate usage rate
    const dailyUsage = calculateUsageRate(item.item, orders, inventory);

    // Calculate days until reorder needed
    const quantityAboveReorder = item.quantity - item.reorderLevel;
    const daysUntilReorder = Math.max(0, Math.floor(quantityAboveReorder / dailyUsage));

    // Determine urgency
    let urgency: 'critical' | 'high' | 'medium' | 'low';
    if (daysUntilReorder <= 0) urgency = 'critical';
    else if (daysUntilReorder <= 7) urgency = 'high';
    else if (daysUntilReorder <= 30) urgency = 'medium';
    else urgency = 'low';

    // Suggested quantity (order enough for 60 days)
    const suggestedQuantity = Math.ceil(dailyUsage * 60);

    // Find last price
    const lastOrder = orders
      .filter(o => o.item.toLowerCase().includes(item.item.toLowerCase().split(' ')[0]))
      .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())[0];

    const estimatedCost = lastOrder ? lastOrder.price * suggestedQuantity : 0;

    // Generate reasoning
    let reasoning = '';
    if (urgency === 'critical') {
      reasoning = `⚠️ URGENT: At or below reorder level. Order immediately!`;
    } else if (urgency === 'high') {
      reasoning = `Will need reorder in ${daysUntilReorder} days. Order within this week.`;
    } else if (urgency === 'medium') {
      reasoning = `Good stock for now. Plan to reorder in ~${daysUntilReorder} days.`;
    } else {
      reasoning = `Well stocked. No action needed for ${daysUntilReorder}+ days.`;
    }

    // Bulk opportunity detection
    let bulkOpportunity = undefined;
    if (suggestedQuantity >= 10 && estimatedCost > 50) {
      bulkOpportunity = {
        savings: estimatedCost * 0.15, // Assume 15% bulk savings
        quantity: suggestedQuantity * 2, // Buy 2x for bigger discount
        description: `Buy ${suggestedQuantity * 2} units and save ~15% with bulk pricing`
      };
    }

    predictions.push({
      item: item.item,
      currentQuantity: item.quantity,
      reorderLevel: item.reorderLevel,
      daysUntilReorder,
      urgency,
      suggestedQuantity,
      estimatedCost,
      reasoning,
      bulkOpportunity
    });
  }

  // Sort by urgency
  const urgencyOrder = { critical: 0, high: 1, medium: 2, low: 3 };
  predictions.sort((a, b) => urgencyOrder[a.urgency] - urgencyOrder[b.urgency]);

  return predictions;
}

/**
 * Display reorder predictions
 */
export async function displayReorderPredictions(): Promise<void> {
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
  console.log('🤖 AI Auto-Reorder Predictions');
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');

  const predictions = await generateReorderPredictions();

  // Group by urgency
  const critical = predictions.filter(p => p.urgency === 'critical');
  const high = predictions.filter(p => p.urgency === 'high');
  const medium = predictions.filter(p => p.urgency === 'medium');
  const low = predictions.filter(p => p.urgency === 'low');

  if (critical.length > 0) {
    console.log('🚨 CRITICAL - ORDER NOW:\n');
    critical.forEach(p => {
      console.log(`${p.item}`);
      console.log(`  Current: ${p.currentQuantity} | Reorder: ${p.reorderLevel}`);
      console.log(`  ${p.reasoning}`);
      console.log(`  Suggested: Order ${p.suggestedQuantity} units (~$${p.estimatedCost.toFixed(2)})\n`);
    });
  }

  if (high.length > 0) {
    console.log('⚠️  HIGH PRIORITY - ORDER THIS WEEK:\n');
    high.forEach(p => {
      console.log(`${p.item}`);
      console.log(`  Current: ${p.currentQuantity} | Reorder: ${p.reorderLevel}`);
      console.log(`  ${p.reasoning}`);
      console.log(`  Suggested: Order ${p.suggestedQuantity} units (~$${p.estimatedCost.toFixed(2)})`);
      if (p.bulkOpportunity) {
        console.log(`  💰 BULK DEAL: ${p.bulkOpportunity.description}`);
        console.log(`     Save ~$${p.bulkOpportunity.savings.toFixed(2)}`);
      }
      console.log('');
    });
  }

  if (medium.length > 0) {
    console.log('📋 MEDIUM - PLAN AHEAD:\n');
    medium.slice(0, 5).forEach(p => {
      console.log(`${p.item} - Reorder in ${p.daysUntilReorder} days`);
    });
    console.log('');
  }

  console.log(`✅ LOW PRIORITY: ${low.length} items well stocked\n`);

  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
}

// Save predictions to file for agent to use
export async function saveReorderPredictions(): Promise<void> {
  const predictions = await generateReorderPredictions();
  const outputPath = '/root/Projects/Designer-Wallcoverings/DW-Agents/data/reorder-predictions.json';
  fs.writeFileSync(outputPath, JSON.stringify(predictions, null, 2));
  console.log(`✅ Predictions saved to: ${outputPath}`);
}

// Run if called directly
if (require.main === module) {
  displayReorderPredictions().catch(console.error);
}

export { ReorderPrediction };