Scroll to Top
💻
Free Code
Users get ready-to-use code at no cost.
📋
Easy Copy
Copy and use the code instantly.
Quick Learning
Understand concepts fast and clearly.
📝
Step-by-Step
Follow simple instructions to implement.
📅 October 24, 2025 💻 Tutorial ⭐ Beginner Friendly

inventory systems app script

Author Avatar

Ashish Dwivedi

Editorial Team • Tech Writer

About This Tutorial

Inventory Systems AppScript
  1. Step 1: Click on the Copy button to copy the code snippet.
  2. Step 2: Paste the copied code into your project’s script editor.

Apps Scripts Blog

Read Blog

📂 javascript
⚡ script1.js
📂 html
🌐 template1.html
🌐 template2.html
🌐 template3.html
⚡ script1.js
🌐 template1.html
🌐 template2.html
🌐 template3.html
// Main Code.gs - Complete Inventory Management System

function onOpen() {
  try {
    var ui = SpreadsheetApp.getUi();
    ui.createMenu('📊 Inventory Management')
      .addItem('➕ Add New Item', 'showAddItemDialog')
      .addItem('📦 Update Stock', 'showUpdateStockDialog')
      .addItem('🚀 Initialize Dashboard', 'initializeDashboard')
      .addItem('🔄 Refresh Dashboard', 'updateDashboard')
      .addItem('📊 Create Visual Charts', 'createEmbeddedCharts')
      .addItem('📋 Generate Report', 'generateReport')
      .addItem('🛠️ Fix Dashboard Charts', 'fixDashboardCharts')
      .addSeparator()
      .addItem('🎨 Show Visual Dashboard', 'showVisualDashboard')
      .addToUi();
  } catch (error) {
    // If getUi() fails, we'll create the menu using the spreadsheet directly
    var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
    spreadsheet.addMenu('📊 Inventory Management', [
      { name: '➕ Add New Item', functionName: 'showAddItemDialog' },
      { name: '📦 Update Stock', functionName: 'showUpdateStockDialog' },
      { name: '🚀 Initialize Dashboard', functionName: 'initializeDashboard' },
      { name: '🔄 Refresh Dashboard', functionName: 'updateDashboard' },
      { name: '📊 Create Visual Charts', functionName: 'createEmbeddedCharts' },
      { name: '📋 Generate Report', functionName: 'generateReport' },
      { name: '🛠️ Fix Dashboard Charts', functionName: 'fixDashboardCharts' },
      null, // Separator
      { name: '🎨 Show Visual Dashboard', functionName: 'showVisualDashboard' }
    ]);
  }
}

// Initialize the complete dashboard
function initializeDashboard() {
  try {
    var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
    var dashboardSheet = spreadsheet.getSheetByName('Dashboard');
    if (!dashboardSheet) {
      dashboardSheet = spreadsheet.insertSheet('Dashboard');
    }
    
    // Clear and setup dashboard
    dashboardSheet.clear();
    setupDashboardLayout();
    
    // Create sample data for charts
    createSampleChartData();
    
    // Update with actual data
    updateDashboard();
    
    // Create embedded charts
    createEmbeddedCharts();
    
    SpreadsheetApp.getActiveSpreadsheet().toast('Dashboard initialized successfully!', 'Success', 5);
  } catch (error) {
    SpreadsheetApp.getActiveSpreadsheet().toast('Error initializing dashboard: ' + error.toString(), 'Error', 10);
  }
}

function setupDashboardLayout() {
  var dashboardSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Dashboard');
  
  // Set column widths for optimal layout
  var columnWidths = [
    {column: 1, width: 150}, {column: 2, width: 120}, {column: 3, width: 150},
    {column: 4, width: 120}, {column: 5, width: 150}, {column: 6, width: 120},
    {column: 7, width: 20},  {column: 8, width: 150}, {column: 9, width: 120},
    {column: 10, width: 150}, {column: 11, width: 120}, {column: 12, width: 150}
  ];
  
  columnWidths.forEach(function(col) {
    dashboardSheet.setColumnWidth(col.column, col.width);
  });
}

function createSampleChartData() {
  var dashboardSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Dashboard');
  
  // Sample data for Stock Levels chart
  var stockData = [
    ['Item', 'Current Stock', 'Min Stock'],
    ['Laptop Dell XPS', 15, 5],
    ['Wireless Mouse Logi', 45, 10],
    ['Mechanical Keyboard', 32, 8],
    ['Monitor 24" Samsung', 12, 4],
    ['Webcam HD 1080p', 28, 6],
    ['Docking Station USB', 8, 3],
    ['Headphones Sony', 35, 12],
    ['USB-C Cable 3.1', 67, 20],
    ['Tablet Stand Adjust', 14, 5],
    ['Power Adapter 65W', 22, 8]
  ];
  dashboardSheet.getRange('A5:C15').setValues(stockData);
  dashboardSheet.getRange('A4').setValue('Stock Levels - Top 10 Items');

  // Sample data for Category Distribution
  var categoryData = [
    ['Category', 'Total Value'],
    ['Electronics', 12500],
    ['Accessories', 3200],
    ['Peripherals', 2800],
    ['Networking', 1500],
    ['Storage', 2200]
  ];
  dashboardSheet.getRange('H5:I10').setValues(categoryData);
  dashboardSheet.getRange('H4').setValue('Value by Category');

  // Sample data for Price Distribution
  var priceData = [
    ['Price Range', 'Number of Items'],
    ['$0-50', 8],
    ['$51-100', 12],
    ['$101-500', 15],
    ['$501-1000', 5],
    ['$1000+', 3]
  ];
  dashboardSheet.getRange('A15:B20').setValues(priceData);
  dashboardSheet.getRange('A14').setValue('Price Distribution');

  // Sample data for Monthly Movement
  var monthlyData = [
    ['Month', 'Stock In', 'Stock Out'],
    ['Jan 24', 150, 120],
    ['Feb 24', 180, 145],
    ['Mar 24', 220, 180],
    ['Apr 24', 190, 165],
    ['May 24', 210, 190],
    ['Jun 24', 240, 210]
  ];
  dashboardSheet.getRange('H15:J21').setValues(monthlyData);
  dashboardSheet.getRange('H14').setValue('Monthly Stock Movement');
}

// Main dashboard update function
function updateDashboard() {
  try {
    var inventorySheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Inventory');
    var transactionsSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Transactions');
    var dashboardSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Dashboard');
    
    // Create dashboard if it doesn't exist
    if (!dashboardSheet) {
      dashboardSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet('Dashboard');
      setupDashboardLayout();
    }
    
    var inventoryData = inventorySheet.getDataRange().getValues();
    var transactionData = transactionsSheet.getDataRange().getValues();
    
    // Calculate metrics
    var metrics = calculateMetrics(inventoryData, transactionData);
    
    // Update KPI cards
    updateKPICards(dashboardSheet, metrics);
    
    // Update chart data with real data
    updateChartData(dashboardSheet, inventoryData, transactionData);
    
    // Update tables
    updateRecentActivity(dashboardSheet, transactionData);
    updateAlerts(dashboardSheet, inventoryData);
    
    // Apply styling
    applyDashboardStyling(dashboardSheet);
    
    SpreadsheetApp.getActiveSpreadsheet().toast('Dashboard updated successfully!', 'Success', 5);
  } catch (error) {
    SpreadsheetApp.getActiveSpreadsheet().toast('Error updating dashboard: ' + error.toString(), 'Error', 10);
  }
}

function calculateMetrics(inventoryData, transactionData) {
  var totalItems = 0;
  var totalValue = 0;
  var lowStockCount = 0;
  var outOfStockCount = 0;
  var categories = {};
  var monthlyData = {};
  
  // Calculate inventory metrics
  for (var i = 1; i < inventoryData.length; i++) {
    if (inventoryData[i][0] && inventoryData[i][0] !== '') {
      var quantity = Number(inventoryData[i][3]) || 0;
      var price = Number(inventoryData[i][4]) || 0;
      var status = inventoryData[i][6];
      var category = inventoryData[i][2];
      
      totalItems += quantity;
      totalValue += quantity * price;
      
      if (status === 'Low Stock') lowStockCount++;
      if (status === 'Out of Stock') outOfStockCount++;
      
      // Category data
      if (category) {
        if (!categories[category]) {
          categories[category] = { count: 0, value: 0 };
        }
        categories[category].count += quantity;
        categories[category].value += quantity * price;
      }
    }
  }
  
  // Calculate transaction metrics (last 30 days)
  var thirtyDaysAgo = new Date();
  thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
  var monthlyIn = 0;
  var monthlyOut = 0;
  
  for (var j = 1; j < transactionData.length; j++) {
    if (transactionData[j][0] && transactionData[j][0] !== '') {
      var transactionDate = new Date(transactionData[j][4]);
      if (transactionDate >= thirtyDaysAgo) {
        var type = transactionData[j][2];
        var qty = Number(transactionData[j][3]) || 0;
        
        if (type === 'IN') monthlyIn += qty;
        if (type === 'OUT') monthlyOut += qty;
        
        // Monthly data for chart
        var monthKey = Utilities.formatDate(transactionDate, Session.getScriptTimeZone(), 'MMM yy');
        if (!monthlyData[monthKey]) {
          monthlyData[monthKey] = { in: 0, out: 0 };
        }
        if (type === 'IN') monthlyData[monthKey].in += qty;
        if (type === 'OUT') monthlyData[monthKey].out += qty;
      }
    }
  }
  
  var avgItemValue = totalItems > 0 ? totalValue / totalItems : 0;
  var stockTurnover = monthlyOut > 0 ? (monthlyOut / totalItems * 12).toFixed(1) : 0;
  
  return {
    totalItems: totalItems,
    totalValue: totalValue,
    lowStockCount: lowStockCount,
    outOfStockCount: outOfStockCount,
    avgItemValue: avgItemValue,
    stockTurnover: stockTurnover,
    categories: categories,
    monthlyData: monthlyData,
    monthlyIn: monthlyIn,
    monthlyOut: monthlyOut
  };
}

function updateKPICards(dashboardSheet, metrics) {
  // Header row
  dashboardSheet.getRange("A1:F1").setValues([[
    "Total Items", "Value", "Low Stock Items", "Value", "Out of Stock", "Value"
  ]]);
  
  // KPI values
  dashboardSheet.getRange("A2:F2").setValues([[
    metrics.totalItems,
    Utilities.formatString('$%s', metrics.totalValue.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2})),
    metrics.lowStockCount,
    Utilities.formatString('$%s', metrics.avgItemValue.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2})),
    metrics.outOfStockCount,
    metrics.stockTurnover
  ]]);
  
  // Second row headers
  dashboardSheet.getRange("A3:F3").setValues([[
    "", "Total Value", "", "Avg Item Value", "", "Stock Turnover (Annual)"
  ]]);
}

function updateChartData(dashboardSheet, inventoryData, transactionData) {
  updateStockLevelsChart(dashboardSheet, inventoryData);
  updateCategoryChart(dashboardSheet, inventoryData);
  updatePriceDistributionChart(dashboardSheet, inventoryData);
  updateMonthlyMovementChart(dashboardSheet, transactionData);
}

function updateStockLevelsChart(dashboardSheet, inventoryData) {
  var itemsWithStock = [];
  
  for (var i = 1; i < inventoryData.length; i++) {
    if (inventoryData[i][0] && inventoryData[i][0] !== '') {
      itemsWithStock.push({
        name: inventoryData[i][1] || 'Unnamed Item',
        stock: Number(inventoryData[i][3]) || 0,
        minStock: Number(inventoryData[i][5]) || 0
      });
    }
  }
  
  // Sort by stock quantity (descending) and take top 10
  itemsWithStock.sort(function(a, b) { return b.stock - a.stock; });
  var topItems = itemsWithStock.slice(0, 10);
  
  var stockChartData = [['Item', 'Current Stock', 'Min Stock']];
  topItems.forEach(function(item) {
    var displayName = item.name.length > 15 ? item.name.substring(0, 15) + '...' : item.name;
    stockChartData.push([displayName, item.stock, item.minStock]);
  });
  
  // Pad with empty data if needed
  while (stockChartData.length < 11) {
    stockChartData.push(['No Data', 0, 0]);
  }
  
  dashboardSheet.getRange('A5:C15').setValues(stockChartData);
}

function updateCategoryChart(dashboardSheet, inventoryData) {
  var categories = {};
  
  for (var i = 1; i < inventoryData.length; i++) {
    if (inventoryData[i][0] && inventoryData[i][0] !== '') {
      var category = inventoryData[i][2] || 'Uncategorized';
      var quantity = Number(inventoryData[i][3]) || 0;
      var price = Number(inventoryData[i][4]) || 0;
      var value = quantity * price;
      
      if (!categories[category]) categories[category] = 0;
      categories[category] += value;
    }
  }
  
  var categoryChartData = [['Category', 'Total Value']];
  for (var category in categories) {
    categoryChartData.push([category, categories[category]]);
  }
  
  // Sort by value (descending) and take top 6
  categoryChartData.sort(function(a, b) {
    if (a[0] === 'Category') return -1;
    if (b[0] === 'Category') return 1;
    return b[1] - a[1];
  });
  
  // Pad category data
  while (categoryChartData.length < 7) {
    categoryChartData.push(['No Data', 0]);
  }
  
  dashboardSheet.getRange('H5:I11').setValues(categoryChartData.slice(0, 7));
}

function updatePriceDistributionChart(dashboardSheet, inventoryData) {
  var priceRanges = {
    '$0-50': 0,
    '$51-100': 0,
    '$101-500': 0,
    '$501-1000': 0,
    '$1000+': 0
  };
  
  for (var i = 1; i < inventoryData.length; i++) {
    if (inventoryData[i][0] && inventoryData[i][0] !== '') {
      var price = Number(inventoryData[i][4]) || 0;
      if (price <= 50) priceRanges['$0-50']++;
      else if (price <= 100) priceRanges['$51-100']++;
      else if (price <= 500) priceRanges['$101-500']++;
      else if (price <= 1000) priceRanges['$501-1000']++;
      else priceRanges['$1000+']++;
    }
  }
  
  var priceChartData = [['Price Range', 'Number of Items']];
  for (var range in priceRanges) {
    priceChartData.push([range, priceRanges[range]]);
  }
  
  dashboardSheet.getRange('A15:B20').setValues(priceChartData);
}

function updateMonthlyMovementChart(dashboardSheet, transactionData) {
  var monthlyData = {};
  var sixMonthsAgo = new Date();
  sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
  
  for (var i = 1; i < transactionData.length; i++) {
    if (transactionData[i][0] && transactionData[i][0] !== '') {
      var date = new Date(transactionData[i][4]);
      if (date >= sixMonthsAgo) {
        var monthKey = Utilities.formatDate(date, Session.getScriptTimeZone(), 'MMM yy');
        var type = transactionData[i][2];
        var quantity = Number(transactionData[i][3]) || 0;
        
        if (!monthlyData[monthKey]) {
          monthlyData[monthKey] = { IN: 0, OUT: 0 };
        }
        monthlyData[monthKey][type] += quantity;
      }
    }
  }
  
  var monthlyChartData = [['Month', 'Stock In', 'Stock Out']];
  var months = Object.keys(monthlyData).sort();
  
  // Get last 6 months
  var last6Months = [];
  for (var m = 5; m >= 0; m--) {
    var month = new Date();
    month.setMonth(month.getMonth() - m);
    last6Months.push(Utilities.formatDate(month, Session.getScriptTimeZone(), 'MMM yy'));
  }
  
  last6Months.forEach(function(month) {
    var inQty = monthlyData[month] ? monthlyData[month].IN : 0;
    var outQty = monthlyData[month] ? monthlyData[month].OUT : 0;
    monthlyChartData.push([month, inQty, outQty]);
  });
  
  dashboardSheet.getRange('H15:J21').setValues(monthlyChartData);
}

function updateRecentActivity(dashboardSheet, transactionData) {
  dashboardSheet.getRange("A23").setValue("Recent Activity");
  
  var headers = [['Date', 'Item ID', 'Type', 'Qty', 'User', 'Notes']];
  dashboardSheet.getRange("A24:F24").setValues(headers);
  
  // Get last 10 transactions
  var recentData = [];
  var startIdx = Math.max(transactionData.length - 10, 1);
  
  for (var i = startIdx; i < transactionData.length; i++) {
    if (transactionData[i][0] && transactionData[i][0] !== '') {
      var date = new Date(transactionData[i][4]);
      var dateStr = Utilities.formatDate(date, Session.getScriptTimeZone(), 'MM/dd/yy');
      
      recentData.push([
        dateStr,
        transactionData[i][1],
        transactionData[i][2],
        transactionData[i][3],
        transactionData[i][5],
        transactionData[i][6]
      ]);
    }
  }
  
  if (recentData.length > 0) {
    dashboardSheet.getRange("A25:F" + (25 + recentData.length - 1)).setValues(recentData);
  } else {
    // Add sample recent activity
    var sampleData = [
      [Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'MM/dd/yy'), 'INV001', 'IN', 10, 'System', 'Initial stock'],
      [Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'MM/dd/yy'), 'INV002', 'OUT', 5, 'Admin', 'Sales order']
    ];
    dashboardSheet.getRange("A25:F26").setValues(sampleData);
  }
}

function updateAlerts(dashboardSheet, inventoryData) {
  dashboardSheet.getRange("H23").setValue("Stock Alerts");
  
  var alerts = [['Item', 'Status', 'Current', 'Min', 'Category']];
  var alertData = [];
  
  for (var i = 1; i < inventoryData.length; i++) {
    if (inventoryData[i][0] && inventoryData[i][0] !== '') {
      var status = inventoryData[i][6];
      if (status === 'Low Stock' || status === 'Out of Stock') {
        alertData.push([
          inventoryData[i][1],
          status,
          inventoryData[i][3],
          inventoryData[i][5],
          inventoryData[i][2]
        ]);
      }
    }
  }
  
  if (alertData.length > 0) {
    dashboardSheet.getRange("H24:L24").setValues(alerts);
    dashboardSheet.getRange("H25:L" + (25 + alertData.length - 1)).setValues(alertData);
  } else {
    // Add sample alerts header
    dashboardSheet.getRange("H24:L24").setValues(alerts);
    dashboardSheet.getRange("H25").setValue("No current alerts");
  }
}

function applyDashboardStyling(dashboardSheet) {
  // Clear existing formatting by setting default styles
  var entireSheet = dashboardSheet.getRange(1, 1, dashboardSheet.getMaxRows(), dashboardSheet.getMaxColumns());
  entireSheet.setBackground('white')
    .setFontColor('black')
    .setFontWeight('normal')
    .setBorder(false, false, false, false, false, false);
  
  // KPI Cards Styling
  var kpiHeaders = dashboardSheet.getRange("A1:F1");
  kpiHeaders.setBackground('#4285f4')
    .setFontColor('white')
    .setFontWeight('bold')
    .setHorizontalAlignment('center')
    .setBorder(true, true, true, true, true, true);
  
  // KPI Values
  var kpiValues = dashboardSheet.getRange("A2:F2");
  kpiValues.setBackground('#f8f9fa')
    .setFontSize(14)
    .setFontWeight('bold')
    .setHorizontalAlignment('center')
    .setBorder(true, true, true, true, true, true);
  
  // KPI Labels
  var kpiLabels = dashboardSheet.getRange("A3:F3");
  kpiLabels.setBackground('#e8f0fe')
    .setFontColor('#4285f4')
    .setFontWeight('bold')
    .setHorizontalAlignment('center')
    .setBorder(true, true, true, true, true, true);
  
  // Chart headers
  var chartHeaders = ['A4', 'H4', 'A14', 'H14'];
  chartHeaders.forEach(function(header) {
    dashboardSheet.getRange(header)
      .setFontWeight('bold')
      .setFontSize(12)
      .setBackground('#34a853')
      .setFontColor('white')
      .setHorizontalAlignment('center');
  });
  
  // Table headers
  var tableHeaders = ['A23', 'H23'];
  tableHeaders.forEach(function(header) {
    dashboardSheet.getRange(header)
      .setFontWeight('bold')
      .setFontSize(12)
      .setBackground('#fbbc05')
      .setFontColor('white')
      .setHorizontalAlignment('center');
  });
  
  // Data tables styling
  var dataRanges = ['A24:F34', 'H24:L34'];
  dataRanges.forEach(function(range) {
    var dataRange = dashboardSheet.getRange(range);
    dataRange.setBorder(true, true, true, true, true, true);
    
    // Header row
    if (dataRange.getNumRows() > 0) {
      dataRange.offset(0, 0, 1, dataRange.getNumColumns())
        .setBackground('#f8f9fa')
        .setFontWeight('bold');
    }
    
    // Alternating row colors
    var numRows = dataRange.getNumRows();
    for (var i = 1; i < numRows; i++) {
      if (i % 2 === 0) {
        dataRange.offset(i, 0, 1, dataRange.getNumColumns())
          .setBackground('#f8f9fa');
      }
    }
  });
  
  // Chart data styling
  var chartDataRanges = ['A5:C15', 'H5:I11', 'A15:B20', 'H15:J21'];
  chartDataRanges.forEach(function(range) {
    var rangeObj = dashboardSheet.getRange(range);
    rangeObj.setBorder(true, true, true, true, true, true)
      .setHorizontalAlignment('center');
    
    // Style header row of chart data
    if (rangeObj.getNumRows() > 0) {
      rangeObj.offset(0, 0, 1, rangeObj.getNumColumns())
        .setBackground('#e8f0fe')
        .setFontWeight('bold');
    }
  });
}

// Create embedded charts
function createEmbeddedCharts() {
  try {
    var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
    var dashboardSheet = spreadsheet.getSheetByName('Dashboard');
    
    // Create dashboard if it doesn't exist
    if (!dashboardSheet) {
      initializeDashboard();
      return;
    }
    
    // Remove existing charts
    var charts = dashboardSheet.getCharts();
    charts.forEach(function(chart) {
      dashboardSheet.removeChart(chart);
    });
    
    // Create Stock Level Chart
    var stockChart = dashboardSheet.newChart()
      .setChartType(Charts.ChartType.COLUMN)
      .addRange(dashboardSheet.getRange('A5:B15'))
      .setPosition(5, 7, 0, 0)
      .setOption('title', 'Current Stock Levels')
      .setOption('legend', { position: 'none' })
      .setOption('hAxis', { 
        title: 'Items',
        slantedText: true,
        slantedTextAngle: 45
      })
      .setOption('vAxis', { title: 'Quantity' })
      .setOption('colors', ['#4285f4'])
      .setOption('width', 500)
      .setOption('height', 300)
      .build();
    
    dashboardSheet.insertChart(stockChart);
    
    // Create Category Pie Chart
    var categoryChart = dashboardSheet.newChart()
      .setChartType(Charts.ChartType.PIE)
      .addRange(dashboardSheet.getRange('H5:I11'))
      .setPosition(5, 17, 0, 0)
      .setOption('title', 'Inventory Value by Category')
      .setOption('pieHole', 0.4)
      .setOption('is3D', true)
      .setOption('width', 450)
      .setOption('height', 300)
      .build();
    
    dashboardSheet.insertChart(categoryChart);
    
    // Create Price Distribution Chart
    var priceChart = dashboardSheet.newChart()
      .setChartType(Charts.ChartType.BAR)
      .addRange(dashboardSheet.getRange('A15:B20'))
      .setPosition(20, 7, 0, 0)
      .setOption('title', 'Price Distribution')
      .setOption('legend', { position: 'none' })
      .setOption('hAxis', { title: 'Number of Items' })
      .setOption('vAxis', { title: 'Price Range' })
      .setOption('colors', ['#34a853'])
      .setOption('width', 500)
      .setOption('height', 300)
      .build();
    
    dashboardSheet.insertChart(priceChart);
    
    // Create Monthly Movement Chart
    var monthlyChart = dashboardSheet.newChart()
      .setChartType(Charts.ChartType.LINE)
      .addRange(dashboardSheet.getRange('H15:J21'))
      .setPosition(20, 17, 0, 0)
      .setOption('title', 'Monthly Stock Movement')
      .setOption('hAxis', { title: 'Month' })
      .setOption('vAxis', { title: 'Quantity' })
      .setOption('series', {
        0: { color: '#4285f4' },
        1: { color: '#ea4335' }
      })
      .setOption('width', 450)
      .setOption('height', 300)
      .build();
    
    dashboardSheet.insertChart(monthlyChart);
    
    SpreadsheetApp.getActiveSpreadsheet().toast('Visual charts created successfully!', 'Success', 5);
  } catch (error) {
    SpreadsheetApp.getActiveSpreadsheet().toast('Error creating charts: ' + error.toString(), 'Error', 10);
  }
}

function fixDashboardCharts() {
  var dashboardSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Dashboard');
  
  // Create dashboard if it doesn't exist
  if (!dashboardSheet) {
    initializeDashboard();
    return;
  }
  
  // Check if we have data for charts
  var stockDataRange = dashboardSheet.getRange('A5:C15');
  var stockData = stockDataRange.getValues();
  
  var hasData = false;
  for (var i = 0; i < stockData.length; i++) {
    for (var j = 0; j < stockData[i].length; j++) {
      if (stockData[i][j] && stockData[i][j] !== '') {
        hasData = true;
        break;
      }
    }
    if (hasData) break;
  }
  
  if (!hasData) {
    createSampleChartData();
    SpreadsheetApp.getActiveSpreadsheet().toast('Sample data added to charts. Run "Create Visual Charts" to generate charts.', 'Info', 5);
  } else {
    createEmbeddedCharts();
  }
}

// Inventory Management Functions
function showAddItemDialog() {
  var html = HtmlService.createHtmlOutputFromFile('AddItemDialog')
    .setWidth(400)
    .setHeight(500);
  SpreadsheetApp.getUi().showModalDialog(html, 'Add New Item');
}

function showUpdateStockDialog() {
  var html = HtmlService.createHtmlOutputFromFile('UpdateStockDialog')
    .setWidth(400)
    .setHeight(400);
  SpreadsheetApp.getUi().showModalDialog(html, 'Update Stock');
}

function addItem(itemName, category, quantity, price, minStock) {
  try {
    var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Inventory');
    var lastRow = sheet.getLastRow();
    
    // Generate unique ID
    var itemId = 'INV' + String(lastRow).padStart(3, '0');
    
    // Determine status
    var status = 'Good';
    if (quantity == 0) {
      status = 'Out of Stock';
    } else if (quantity <= minStock) {
      status = 'Low Stock';
    }
    
    // Add item to inventory
    sheet.getRange(lastRow + 1, 1, 1, 8).setValues([[
      itemId,
      itemName,
      category,
      quantity,
      price,
      minStock,
      status,
      new Date()
    ]]);
    
    // Record transaction
    recordTransaction(itemId, 'IN', quantity, 'Initial stock');
    
    // Update dashboard
    updateDashboard();
    
    return 'Item added successfully! ID: ' + itemId;
  } catch (error) {
    return 'Error adding item: ' + error.toString();
  }
}

function updateStock(itemId, type, quantity, notes) {
  try {
    var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Inventory');
    var data = sheet.getDataRange().getValues();
    
    for (var i = 1; i < data.length; i++) {
      if (data[i][0] === itemId) {
        var currentQty = Number(data[i][3]) || 0;
        var newQty = type === 'IN' ? currentQty + quantity : currentQty - quantity;
        var minStock = Number(data[i][5]) || 0;
        
        if (newQty < 0) {
          return 'Error: Cannot have negative stock! Current: ' + currentQty + ', Trying to remove: ' + quantity;
        }
        
        // Update quantity
        sheet.getRange(i + 1, 4).setValue(newQty);
        
        // Update status
        var status = 'Good';
        if (newQty == 0) {
          status = 'Out of Stock';
        } else if (newQty <= minStock) {
          status = 'Low Stock';
        }
        sheet.getRange(i + 1, 7).setValue(status);
        
        // Update timestamp
        sheet.getRange(i + 1, 8).setValue(new Date());
        
        // Record transaction
        recordTransaction(itemId, type, quantity, notes);
        
        // Update dashboard
        updateDashboard();
        
        return 'Stock updated successfully! New quantity: ' + newQty;
      }
    }
    
    return 'Item not found! Please check the Item ID.';
  } catch (error) {
    return 'Error updating stock: ' + error.toString();
  }
}

function recordTransaction(itemId, type, quantity, notes) {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Transactions');
  var lastRow = sheet.getLastRow();
  var transactionId = 'T' + String(lastRow).padStart(3, '0');
  
  sheet.getRange(lastRow + 1, 1, 1, 7).setValues([[
    transactionId,
    itemId,
    type,
    quantity,
    new Date(),
    Session.getEffectiveUser().getEmail(),
    notes
  ]]);
}

function getItems() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Inventory');
  var data = sheet.getDataRange().getValues();
  var items = [];
  
  for (var i = 1; i < data.length; i++) {
    if (data[i][0] && data[i][0] !== '') {
      items.push({
        id: data[i][0],
        name: data[i][1],
        quantity: data[i][3],
        category: data[i][2]
      });
    }
  }
  
  return items;
}

function generateReport() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var inventoryData = spreadsheet.getSheetByName('Inventory').getDataRange().getValues();
  
  // Create report sheet or clear existing
  var reportSheet = spreadsheet.getSheetByName('Inventory Report');
  if (!reportSheet) {
    reportSheet = spreadsheet.insertSheet('Inventory Report');
  } else {
    reportSheet.clear();
  }
  
  // Add headers and data
  reportSheet.getRange(1, 1, inventoryData.length, inventoryData[0].length)
    .setValues(inventoryData);
  
  // Add summary
  var summaryRow = inventoryData.length + 2;
  var totalItems = 0;
  var totalValue = 0;
  
  for (var i = 1; i < inventoryData.length; i++) {
    if (inventoryData[i][0]) {
      totalItems += Number(inventoryData[i][3]) || 0;
      totalValue += (Number(inventoryData[i][3]) || 0) * (Number(inventoryData[i][4]) || 0);
    }
  }
  
  reportSheet.getRange(summaryRow, 1).setValue('Report Summary:');
  reportSheet.getRange(summaryRow, 2).setValue('Generated on: ' + new Date());
  reportSheet.getRange(summaryRow + 1, 1).setValue('Total Items:');
  reportSheet.getRange(summaryRow + 1, 2).setValue(totalItems);
  reportSheet.getRange(summaryRow + 2, 1).setValue('Total Value:');
  reportSheet.getRange(summaryRow + 2, 2).setValue('$' + totalValue.toFixed(2));
  
  // Auto-resize columns
  reportSheet.autoResizeColumns(1, inventoryData[0].length);
  
  SpreadsheetApp.getActiveSpreadsheet().toast('Report generated in "Inventory Report" sheet!', 'Success', 5);
}

// Visual Dashboard HTML
function showVisualDashboard() {
  var html = HtmlService.createHtmlOutputFromFile('VisualDashboard')
    .setWidth(1200)
    .setHeight(800);
  SpreadsheetApp.getUi().showModalDialog(html, '📊 Visual Dashboard - PowerBI Style');
}

function getDashboardData() {
  var inventorySheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Inventory');
  var transactionsSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Transactions');
  
  var inventoryData = inventorySheet.getDataRange().getValues();
  var transactionData = transactionsSheet.getDataRange().getValues();
  
  var metrics = calculateMetrics(inventoryData, transactionData);
  
  // Get recent activity
  var recentActivity = [];
  var startIdx = Math.max(transactionData.length - 5, 1);
  for (var i = startIdx; i < transactionData.length; i++) {
    if (transactionData[i][0]) {
      var date = new Date(transactionData[i][4]);
      recentActivity.push({
        date: Utilities.formatDate(date, Session.getScriptTimeZone(), 'MM/dd'),
        item: transactionData[i][1],
        type: transactionData[i][2],
        quantity: transactionData[i][3]
      });
    }
  }
  
  // Get alerts
  var alerts = [];
  for (var j = 1; j < inventoryData.length; j++) {
    if (inventoryData[j][0] && (inventoryData[j][6] === 'Low Stock' || inventoryData[j][6] === 'Out of Stock')) {
      alerts.push({
        item: inventoryData[j][1],
        status: inventoryData[j][6],
        current: inventoryData[j][3],
        min: inventoryData[j][5]
      });
    }
  }
  
  return {
    totalItems: metrics.totalItems,
    totalValue: metrics.totalValue,
    lowStockCount: metrics.lowStockCount,
    outOfStockCount: metrics.outOfStockCount,
    recentActivity: recentActivity,
    alerts: alerts
  };
}
<!DOCTYPE html>
<html>
<head>
  <base target="_top">
  <style>
    body { font-family: 'Segoe UI', Arial, sans-serif; margin: 20px; background: #f5f5f5; }
    .container { background: white; padding: 25px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
    .form-group { margin-bottom: 20px; }
    label { display: block; margin-bottom: 8px; font-weight: 600; color: #333; }
    input, select { 
      width: 100%; 
      padding: 12px; 
      border: 2px solid #e1e1e1; 
      border-radius: 6px; 
      font-size: 14px;
      transition: border-color 0.3s;
    }
    input:focus, select:focus { 
      border-color: #4285f4; 
      outline: none;
      box-shadow: 0 0 5px rgba(66, 133, 244, 0.3);
    }
    button { 
      background: #4285f4; 
      color: white; 
      padding: 12px 30px; 
      border: none; 
      border-radius: 6px; 
      cursor: pointer; 
      font-size: 14px;
      font-weight: 600;
      transition: background 0.3s;
    }
    button:hover { background: #3367d6; }
    .btn-cancel { background: #ea4335; margin-left: 10px; }
    .btn-cancel:hover { background: #d33426; }
    .form-row { display: flex; gap: 15px; }
    .form-row .form-group { flex: 1; }
    .title { 
      color: #4285f4; 
      text-align: center; 
      margin-bottom: 25px;
      font-size: 24px;
      font-weight: 700;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="title">➕ Add New Item</div>
    <form id="itemForm">
      <div class="form-group">
        <label for="itemName">Item Name:</label>
        <input type="text" id="itemName" required placeholder="Enter item name">
      </div>
      
      <div class="form-group">
        <label for="category">Category:</label>
        <input type="text" id="category" required placeholder="e.g., Electronics, Office Supplies">
      </div>
      
      <div class="form-row">
        <div class="form-group">
          <label for="quantity">Initial Quantity:</label>
          <input type="number" id="quantity" min="0" required placeholder="0">
        </div>
        
        <div class="form-group">
          <label for="price">Price ($):</label>
          <input type="number" id="price" step="0.01" min="0" required placeholder="0.00">
        </div>
      </div>
      
      <div class="form-group">
        <label for="minStock">Minimum Stock Level:</label>
        <input type="number" id="minStock" min="0" required placeholder="Set alert threshold">
      </div>
      
      <div style="text-align: center; margin-top: 25px;">
        <button type="submit">Add Item</button>
        <button type="button" class="btn-cancel" onclick="google.script.host.close()">Cancel</button>
      </div>
    </form>
  </div>

  <script>
    document.getElementById('itemForm').addEventListener('submit', function(e) {
      e.preventDefault();
      
      const submitBtn = this.querySelector('button[type="submit"]');
      submitBtn.textContent = 'Adding...';
      submitBtn.disabled = true;
      
      google.script.run
        .withSuccessHandler(function(result) {
          alert(result);
          google.script.host.close();
        })
        .withFailureHandler(function(error) {
          alert('Error: ' + error.message);
          submitBtn.textContent = 'Add Item';
          submitBtn.disabled = false;
        })
        .addItem(
          document.getElementById('itemName').value,
          document.getElementById('category').value,
          parseInt(document.getElementById('quantity').value),
          parseFloat(document.getElementById('price').value),
          parseInt(document.getElementById('minStock').value)
        );
    });
  </script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
  <base target="_top">
  <style>
    body { font-family: 'Segoe UI', Arial, sans-serif; margin: 20px; background: #f5f5f5; }
    .container { background: white; padding: 25px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
    .form-group { margin-bottom: 20px; }
    label { display: block; margin-bottom: 8px; font-weight: 600; color: #333; }
    input, select, textarea { 
      width: 100%; 
      padding: 12px; 
      border: 2px solid #e1e1e1; 
      border-radius: 6px; 
      font-size: 14px;
      transition: border-color 0.3s;
    }
    input:focus, select:focus, textarea:focus { 
      border-color: #4285f4; 
      outline: none;
      box-shadow: 0 0 5px rgba(66, 133, 244, 0.3);
    }
    button { 
      background: #4285f4; 
      color: white; 
      padding: 12px 30px; 
      border: none; 
      border-radius: 6px; 
      cursor: pointer; 
      font-size: 14px;
      font-weight: 600;
      transition: background 0.3s;
      margin-right: 10px;
    }
    button:hover { background: #3367d6; }
    .btn-cancel { background: #ea4335; }
    .btn-cancel:hover { background: #d33426; }
    .title { 
      color: #4285f4; 
      text-align: center; 
      margin-bottom: 25px;
      font-size: 24px;
      font-weight: 700;
    }
    .current-stock { 
      background: #e8f0fe; 
      padding: 10px; 
      border-radius: 5px; 
      margin-top: 5px;
      font-weight: 600;
      color: #4285f4;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="title">📦 Update Stock</div>
    <form id="stockForm">
      <div class="form-group">
        <label for="itemId">Select Item:</label>
        <select id="itemId" required>
          <option value="">Loading items...</option>
        </select>
        <div id="stockInfo" class="current-stock" style="display: none;">
          Current Stock: <span id="currentStock">0</span>
        </div>
      </div>
      
      <div class="form-group">
        <label for="type">Transaction Type:</label>
        <select id="type" required>
          <option value="IN">📥 Stock In (Add)</option>
          <option value="OUT">📤 Stock Out (Remove)</option>
        </select>
      </div>
      
      <div class="form-group">
        <label for="quantity">Quantity:</label>
        <input type="number" id="quantity" min="1" required placeholder="Enter quantity">
      </div>
      
      <div class="form-group">
        <label for="notes">Notes:</label>
        <textarea id="notes" rows="3" placeholder="Optional: Add transaction notes"></textarea>
      </div>
      
      <div style="text-align: center; margin-top: 25px;">
        <button type="submit">Update Stock</button>
        <button type="button" class="btn-cancel" onclick="google.script.host.close()">Cancel</button>
      </div>
    </form>
  </div>

  <script>
    // Load items when dialog opens
    google.script.run
      .withSuccessHandler(function(items) {
        var select = document.getElementById('itemId');
        select.innerHTML = '<option value="">Select an item...</option>';
        
        items.forEach(function(item) {
          var option = document.createElement('option');
          option.value = item.id;
          option.textContent = item.name + ' (Stock: ' + item.quantity + ')';
          option.setAttribute('data-stock', item.quantity);
          select.appendChild(option);
        });
      })
      .getItems();

    // Show current stock when item is selected
    document.getElementById('itemId').addEventListener('change', function() {
      var selectedOption = this.options[this.selectedIndex];
      var stockInfo = document.getElementById('stockInfo');
      var currentStock = document.getElementById('currentStock');
      
      if (selectedOption.value) {
        currentStock.textContent = selectedOption.getAttribute('data-stock');
        stockInfo.style.display = 'block';
      } else {
        stockInfo.style.display = 'none';
      }
    });

    document.getElementById('stockForm').addEventListener('submit', function(e) {
      e.preventDefault();
      
      const submitBtn = this.querySelector('button[type="submit"]');
      submitBtn.textContent = 'Updating...';
      submitBtn.disabled = true;
      
      google.script.run
        .withSuccessHandler(function(result) {
          alert(result);
          google.script.host.close();
        })
        .withFailureHandler(function(error) {
          alert('Error: ' + error.message);
          submitBtn.textContent = 'Update Stock';
          submitBtn.disabled = false;
        })
        .updateStock(
          document.getElementById('itemId').value,
          document.getElementById('type').value,
          parseInt(document.getElementById('quantity').value),
          document.getElementById('notes').value
        );
    });
  </script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
  <base target="_top">
  <style>
    :root {
      --primary: #4285f4;
      --secondary: #34a853;
      --warning: #fbbc05;
      --danger: #ea4335;
      --light: #f8f9fa;
      --dark: #343a40;
    }
    
    body {
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      margin: 0;
      padding: 20px;
      background-color: #f5f5f5;
    }
    
    .dashboard {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
      gap: 20px;
      max-width: 1400px;
      margin: 0 auto;
    }
    
    .kpi-card {
      background: white;
      padding: 20px;
      border-radius: 10px;
      box-shadow: 0 2px 10px rgba(0,0,0,0.1);
      text-align: center;
      border-left: 5px solid var(--primary);
    }
    
    .kpi-value {
      font-size: 2.5em;
      font-weight: bold;
      margin: 10px 0;
      color: var(--dark);
    }
    
    .kpi-title {
      color: #666;
      font-size: 0.9em;
      text-transform: uppercase;
      letter-spacing: 1px;
    }
    
    .chart-container {
      background: white;
      padding: 20px;
      border-radius: 10px;
      box-shadow: 0 2px 10px rgba(0,0,0,0.1);
      grid-column: span 2;
    }
    
    .chart-title {
      font-size: 1.2em;
      font-weight: bold;
      margin-bottom: 15px;
      color: var(--dark);
      border-bottom: 2px solid var(--light);
      padding-bottom: 10px;
    }
    
    .alert-badge {
      display: inline-block;
      padding: 8px 15px;
      border-radius: 20px;
      font-size: 0.8em;
      font-weight: bold;
      margin: 5px;
      color: white;
    }
    
    .alert-low { background: var(--warning); }
    .alert-out { background: var(--danger); }
    
    .table {
      width: 100%;
      border-collapse: collapse;
      margin-top: 10px;
    }
    
    .table th, .table td {
      padding: 12px;
      text-align: left;
      border-bottom: 1px solid #ddd;
    }
    
    .table th {
      background-color: var(--light);
      font-weight: bold;
      color: var(--dark);
    }
    
    .table tr:hover {
      background-color: #f8f9fa;
    }
    
    .loading {
      text-align: center;
      padding: 40px;
      color: #666;
    }
    
    .section-title {
      font-size: 1.5em;
      font-weight: bold;
      margin: 30px 0 15px 0;
      color: var(--dark);
      border-left: 4px solid var(--primary);
      padding-left: 15px;
    }
  </style>
</head>
<body>
  <div class="section-title">📊 Inventory Dashboard</div>
  
  <div class="dashboard">
    <!-- KPI Cards -->
    <div class="kpi-card">
      <div class="kpi-title">Total Items</div>
      <div class="kpi-value" id="totalItems">0</div>
      <div>in inventory</div>
    </div>
    
    <div class="kpi-card">
      <div class="kpi-title">Total Value</div>
      <div class="kpi-value" id="totalValue">$0</div>
      <div>current stock value</div>
    </div>
    
    <div class="kpi-card">
      <div class="kpi-title">Low Stock</div>
      <div class="kpi-value" id="lowStock">0</div>
      <div>items need attention</div>
    </div>
    
    <div class="kpi-card">
      <div class="kpi-title">Out of Stock</div>
      <div class="kpi-value" id="outOfStock">0</div>
      <div>items to restock</div>
    </div>
    
    <!-- Recent Activity -->
    <div class="chart-container">
      <div class="chart-title">📋 Recent Activity</div>
      <table class="table" id="recentActivity">
        <thead>
          <tr>
            <th>Date</th>
            <th>Item</th>
            <th>Type</th>
            <th>Qty</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td colspan="4" class="loading">Loading recent activity...</td>
          </tr>
        </tbody>
      </table>
    </div>
    
    <!-- Alerts -->
    <div class="chart-container">
      <div class="chart-title">⚠️ Stock Alerts</div>
      <div id="alertsContainer">
        <div class="loading">Loading alerts...</div>
      </div>
    </div>
  </div>

  <script>
    // Load dashboard data when page opens
    google.script.run
      .withSuccessHandler(updateDashboard)
      .withFailureHandler(showError)
      .getDashboardData();
    
    function updateDashboard(data) {
      // Update KPI cards
      document.getElementById('totalItems').textContent = data.totalItems.toLocaleString();
      document.getElementById('totalValue').textContent = '$' + data.totalValue.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
      document.getElementById('lowStock').textContent = data.lowStockCount;
      document.getElementById('outOfStock').textContent = data.outOfStockCount;
      
      // Update recent activity
      updateRecentActivity(data.recentActivity);
      
      // Update alerts
      updateAlerts(data.alerts);
    }
    
    function updateRecentActivity(activities) {
      const tbody = document.querySelector('#recentActivity tbody');
      
      if (activities.length === 0) {
        tbody.innerHTML = '<tr><td colspan="4" style="text-align: center; color: #666;">No recent activity</td></tr>';
        return;
      }
      
      tbody.innerHTML = '';
      
      activities.forEach(activity => {
        const row = document.createElement('tr');
        const typeIcon = activity.type === 'IN' ? '📥' : '📤';
        const typeClass = activity.type === 'IN' ? 'style="color: #34a853;"' : 'style="color: #ea4335;"';
        
        row.innerHTML = `
          <td>${activity.date}</td>
          <td>${activity.item}</td>
          <td ${typeClass}>${typeIcon} ${activity.type}</td>
          <td>${activity.quantity}</td>
        `;
        tbody.appendChild(row);
      });
    }
    
    function updateAlerts(alerts) {
      const container = document.getElementById('alertsContainer');
      
      if (alerts.length === 0) {
        container.innerHTML = '<div style="text-align: center; color: #34a853; padding: 20px;">✅ All items are well stocked</div>';
        return;
      }
      
      container.innerHTML = '';
      
      alerts.forEach(alert => {
        const badge = document.createElement('div');
        badge.className = `alert-badge ${alert.status === 'Low Stock' ? 'alert-low' : 'alert-out'}`;
        badge.innerHTML = `
          <strong>${alert.item}</strong><br>
          <small>Current: ${alert.current} | Min: ${alert.min}</small>
        `;
        badge.style.margin = '10px 0';
        badge.style.padding = '15px';
        badge.style.borderRadius = '8px';
        badge.style.textAlign = 'left';
        badge.style.width = '100%';
        container.appendChild(badge);
      });
    }
    
    function showError(error) {
      const container = document.getElementById('alertsContainer');
      container.innerHTML = `<div style="color: #ea4335; text-align: center; padding: 20px;">Error loading dashboard: ${error.message}</div>`;
    }
  </script>
</body>
</html>