← back to Designer Wallcoverings
DW-Agents/dw-agents/accounting-agent.ts
1129 lines
/**
* DW-Agents: Accounting Agent
*
* Financial management and reporting system
* - P&L generation
* - Expense tracking and categorization
* - Invoice management
* - Budget forecasting
* - Chat interface for financial queries
*/
import Anthropic from '@anthropic-ai/sdk';
import express, { Request, Response } from 'express';
import cookieParser from 'cookie-parser';
import session from 'express-session';
import { AgentMemory } from './shared-memory-system';
import { requireGlobalAuth } from './shared-global-auth';
import axios from 'axios';
// Import theme utilities
import { getThemeStyles } from './shared-ui-components.js';
const app = express();
const PORT = process.env.PORT || 9885;
// Anthropic AI client
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY || '',
});
// Initialize Agent Memory
const agentMemory = new AgentMemory('accounting-agent');
// Shopify Configuration
const SHOPIFY_CONFIG = {
store: 'designer-laboratory-sandbox.myshopify.com',
accessToken: (process.env.SHOPIFY_ADMIN_TOKEN || ''),
apiVersion: '2024-07'
};
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(requireGlobalAuth);
// Session configuration with extended TypeScript types
declare module 'express-session' {
interface SessionData {
authenticated: boolean;
chatHistory: Array<{ role: 'user' | 'assistant', content: string }>;
}
}
app.use(
session({
secret: 'dw-agents-accounting-2025',
resave: false,
saveUninitialized: true,
rolling: true,
cookie: {
secure: false,
httpOnly: true,
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
},
})
);
// Statistics tracking
const stats = {
totalQueries: 0,
expensesTracked: 0,
invoicesManaged: 0,
reportsGenerated: 0,
budgetForecasts: 0,
lastActivity: new Date(),
};
// Sample financial data (in production, integrate with QuickBooks/accounting system)
interface Expense {
id: string;
date: Date;
category: string;
vendor: string;
amount: number;
description: string;
status: 'pending' | 'approved' | 'paid';
}
interface Invoice {
id: string;
invoiceNumber: string;
customerName: string;
issueDate: Date;
dueDate: Date;
amount: number;
status: 'draft' | 'sent' | 'paid' | 'overdue';
}
interface ShopifyOrder {
id: string;
name: string;
createdAt: string;
totalPriceSet: {
shopMoney: {
amount: string;
currencyCode: string;
};
};
displayFinancialStatus: string;
displayFulfillmentStatus: string;
lineItems: {
edges: Array<{
node: {
title: string;
sku: string | null;
quantity: number;
};
}>;
};
}
const expenses: Expense[] = [
{
id: 'exp-001',
date: new Date('2025-11-01'),
category: 'Office Supplies',
vendor: 'Amazon Business',
amount: 247.50,
description: 'Printer paper, pens, staplers',
status: 'paid'
},
{
id: 'exp-002',
date: new Date('2025-11-03'),
category: 'Marketing',
vendor: 'Constant Contact',
amount: 95.00,
description: 'Monthly email marketing subscription',
status: 'paid'
},
{
id: 'exp-003',
date: new Date('2025-11-04'),
category: 'Software',
vendor: 'Shopify',
amount: 299.00,
description: 'Monthly e-commerce platform fee',
status: 'approved'
},
{
id: 'exp-004',
date: new Date('2025-11-05'),
category: 'Shipping',
vendor: 'UPS',
amount: 156.75,
description: 'Customer order shipping fees',
status: 'pending'
},
];
const invoices: Invoice[] = [
{
id: 'inv-001',
invoiceNumber: 'INV-2025-1001',
customerName: 'ABC Design Studio',
issueDate: new Date('2025-10-15'),
dueDate: new Date('2025-11-15'),
amount: 4250.00,
status: 'paid'
},
{
id: 'inv-002',
invoiceNumber: 'INV-2025-1002',
customerName: 'Interior Elegance LLC',
issueDate: new Date('2025-10-28'),
dueDate: new Date('2025-11-28'),
amount: 3890.50,
status: 'sent'
},
{
id: 'inv-003',
invoiceNumber: 'INV-2025-1003',
customerName: 'Luxury Homes Inc',
issueDate: new Date('2025-11-01'),
dueDate: new Date('2025-12-01'),
amount: 6750.00,
status: 'sent'
},
{
id: 'inv-004',
invoiceNumber: 'INV-2025-1004',
customerName: 'Modern Living Design',
issueDate: new Date('2025-09-20'),
dueDate: new Date('2025-10-20'),
amount: 2340.00,
status: 'overdue'
},
];
// Activity log
const activityLog: Array<{
timestamp: Date;
action: string;
details: string;
}> = [];
function logActivity(action: string, details: string) {
activityLog.unshift({
timestamp: new Date(),
action,
details,
});
if (activityLog.length > 50) {
activityLog.pop();
}
stats.lastActivity = new Date();
}
// Shopify GraphQL function to fetch orders
async function fetchShopifyOrders(): Promise<ShopifyOrder[]> {
const graphqlQuery = `
query {
orders(first: 20, reverse: true, sortKey: CREATED_AT) {
edges {
node {
id
name
createdAt
totalPriceSet {
shopMoney {
amount
currencyCode
}
}
displayFinancialStatus
displayFulfillmentStatus
lineItems(first: 10) {
edges {
node {
title
sku
quantity
}
}
}
}
}
}
}
`;
try {
const response = await axios.post(
`https://${SHOPIFY_CONFIG.store}/admin/api/${SHOPIFY_CONFIG.apiVersion}/graphql.json`,
{ query: graphqlQuery },
{
headers: {
'X-Shopify-Access-Token': SHOPIFY_CONFIG.accessToken,
'Content-Type': 'application/json',
},
}
);
if (response.data.errors) {
console.error('GraphQL errors:', response.data.errors);
throw new Error('Failed to fetch orders from Shopify');
}
const orders = response.data.data.orders.edges.map((edge: any) => edge.node);
return orders;
} catch (error) {
console.error('Error fetching Shopify orders:', error);
throw error;
}
}
// Routes
// Main dashboard
app.get('/', (req: Request, res: Response) => {
res.send(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Accounting Agent - Dashboard</title>
${getThemeStyles('modern-minimalist')}
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #36454f 0%, #708090 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.header {
background: white;
padding: 30px;
border-radius: 16px;
margin-bottom: 20px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.header h1 {
color: #333;
font-size: 32px;
margin-bottom: 10px;
}
.agent-status {
display: inline-block;
background: #10b981;
color: white;
padding: 6px 12px;
border-radius: 20px;
font-size: 14px;
font-weight: 600;
}
.tabs {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.tab-button {
background: white;
color: #36454f;
padding: 12px 24px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
font-weight: 600;
transition: all 0.3s;
}
.tab-button:hover {
background: #f3f4f6;
}
.tab-button.active {
background: linear-gradient(135deg, #36454f 0%, #708090 100%);
color: white;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
padding: 24px;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.stat-value {
font-size: 32px;
font-weight: 700;
color: #36454f;
margin-bottom: 5px;
}
.stat-label {
color: #666;
font-size: 14px;
}
.section {
background: white;
padding: 30px;
border-radius: 16px;
margin-bottom: 20px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.section h2 {
color: #333;
font-size: 24px;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.chat-container {
display: flex;
flex-direction: column;
height: 600px;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 20px;
background: #f9fafb;
border-radius: 12px;
margin-bottom: 20px;
}
.message {
margin-bottom: 16px;
padding: 12px 16px;
border-radius: 12px;
max-width: 80%;
}
.message.user {
background: #36454f;
color: white;
margin-left: auto;
}
.message.assistant {
background: white;
color: #333;
border: 1px solid #e0e0e0;
}
.chat-input-container {
display: flex;
gap: 10px;
}
.chat-input {
flex: 1;
padding: 14px;
border: 2px solid #e0e0e0;
border-radius: 12px;
font-size: 16px;
font-family: inherit;
}
.send-button {
padding: 14px 28px;
background: linear-gradient(135deg, #36454f 0%, #708090 100%);
color: white;
border: none;
border-radius: 12px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s;
}
.send-button:hover {
transform: translateY(-2px);
}
.suggestion-chips {
display: flex;
gap: 10px;
margin-bottom: 15px;
flex-wrap: wrap;
}
.chip {
background: #f3f4f6;
color: #36454f;
padding: 8px 16px;
border-radius: 20px;
font-size: 14px;
cursor: pointer;
border: 2px solid transparent;
transition: all 0.3s;
}
.chip:hover {
background: #36454f;
color: white;
border-color: #36454f;
}
.table-container {
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #e0e0e0;
}
th {
background: #f9fafb;
color: #36454f;
font-weight: 600;
}
tr:hover {
background: #f9fafb;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
}
.status-paid { background: #d1fae5; color: #065f46; }
.status-approved { background: #dbeafe; color: #1e40af; }
.status-pending { background: #fef3c7; color: #92400e; }
.status-sent { background: #dbeafe; color: #1e40af; }
.status-overdue { background: #fee2e2; color: #991b1b; }
.status-draft { background: #f3f4f6; color: #374151; }
.action-button {
padding: 6px 12px;
background: #36454f;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
}
.action-button:hover {
background: #5568d3;
}
.chart-placeholder {
height: 300px;
background: #f9fafb;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: #666;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>💰 Accounting Agent</h1>
<p style="color: #666; margin-top: 5px;">Financial Management & Reporting System</p>
<div style="margin-top: 15px;">
<span class="agent-status">● ONLINE</span>
<span style="color: #888; margin-left: 15px; font-size: 14px;">Port 9885</span>
</div>
</div>
<div class="tabs">
<button class="tab-button active" onclick="switchTab('overview')">📊 Overview</button>
<button class="tab-button" onclick="switchTab('chat')">💬 Chat Assistant</button>
<button class="tab-button" onclick="switchTab('orders')">🛍️ Orders</button>
<button class="tab-button" onclick="switchTab('expenses')">📝 Expenses</button>
<button class="tab-button" onclick="switchTab('invoices')">📄 Invoices</button>
<button class="tab-button" onclick="switchTab('reports')">📈 Reports</button>
</div>
<!-- Overview Tab -->
<div id="tab-overview" class="tab-content active">
<div class="stats-grid">
<div class="stat-card">
<div class="stat-value">${stats.totalQueries}</div>
<div class="stat-label">Total Queries</div>
</div>
<div class="stat-card">
<div class="stat-value">${stats.expensesTracked}</div>
<div class="stat-label">Expenses Tracked</div>
</div>
<div class="stat-card">
<div class="stat-value">${stats.invoicesManaged}</div>
<div class="stat-label">Invoices Managed</div>
</div>
<div class="stat-card">
<div class="stat-value">${stats.reportsGenerated}</div>
<div class="stat-label">Reports Generated</div>
</div>
</div>
<div class="section">
<h2>📊 Financial Summary</h2>
<div class="chart-placeholder">
Chart: Monthly Revenue vs Expenses (Integration with QuickBooks recommended)
</div>
</div>
<div class="section">
<h2>🎯 Quick Actions</h2>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
<button class="action-button" style="padding: 20px; font-size: 16px;" onclick="switchTab('expenses')">
Track New Expense
</button>
<button class="action-button" style="padding: 20px; font-size: 16px;" onclick="switchTab('invoices')">
Create Invoice
</button>
<button class="action-button" style="padding: 20px; font-size: 16px;" onclick="switchTab('reports')">
Generate P&L Report
</button>
<button class="action-button" style="padding: 20px; font-size: 16px;" onclick="switchTab('chat')">
Ask Financial Question
</button>
</div>
</div>
</div>
<!-- Chat Tab -->
<div id="tab-chat" class="tab-content">
<div class="section">
<h2>💬 Financial Chat Assistant</h2>
<p style="color: #666; margin-bottom: 20px;">Ask questions about finances, expenses, invoices, or request reports</p>
<div class="suggestion-chips">
<span class="chip" onclick="sendSuggestion('What are my pending expenses?')">What are my pending expenses?</span>
<span class="chip" onclick="sendSuggestion('Show me overdue invoices')">Show overdue invoices</span>
<span class="chip" onclick="sendSuggestion('Generate monthly P&L report')">Generate monthly P&L</span>
<span class="chip" onclick="sendSuggestion('What is my cash flow status?')">Cash flow status</span>
</div>
<div class="chat-container">
<div class="chat-messages" id="chatMessages">
<div class="message assistant">
Hello! I'm your Accounting Agent powered by Claude 4. I can help you with:
<br><br>
• Track and categorize expenses<br>
• Manage invoices and payments<br>
• Generate P&L reports<br>
• Budget forecasting<br>
• Financial analysis and insights<br>
<br>
How can I assist you today?
</div>
</div>
<div class="chat-input-container">
<input
type="text"
class="chat-input"
id="chatInput"
placeholder="Ask about finances, expenses, invoices..."
onkeypress="if(event.key === 'Enter') sendMessage()"
>
<button class="send-button" onclick="sendMessage()">Send</button>
</div>
</div>
</div>
</div>
<!-- Orders Tab -->
<div id="tab-orders" class="tab-content">
<div class="section">
<h2>🛍️ Shopify Orders</h2>
<div style="display: flex; align-items: center; gap: 20px; margin-bottom: 20px;">
<input
type="text"
id="orderSearch"
placeholder="Search by order number or customer..."
style="flex: 1; padding: 10px; border: 2px solid #e0e0e0; border-radius: 8px; font-size: 14px;"
onkeyup="filterOrders()"
>
<select
id="orderSort"
style="padding: 10px; border: 2px solid #e0e0e0; border-radius: 8px; font-size: 14px;"
onchange="sortOrders()"
>
<option value="date-desc">Newest First</option>
<option value="date-asc">Oldest First</option>
<option value="amount-desc">Highest Amount</option>
<option value="amount-asc">Lowest Amount</option>
</select>
<button class="action-button" onclick="loadOrders()">Refresh Orders</button>
</div>
<div class="table-container" id="ordersTable">
<p style="color: #888;">Loading orders from Shopify...</p>
</div>
</div>
</div>
<!-- Expenses Tab -->
<div id="tab-expenses" class="tab-content">
<div class="section">
<h2>📝 Expense Tracking</h2>
<button class="action-button" style="margin-bottom: 20px;">+ Add New Expense</button>
<div class="table-container" id="expensesTable">
<p style="color: #888;">Loading expenses...</p>
</div>
</div>
</div>
<!-- Invoices Tab -->
<div id="tab-invoices" class="tab-content">
<div class="section">
<h2>📄 Invoice Management</h2>
<button class="action-button" style="margin-bottom: 20px;">+ Create New Invoice</button>
<div class="table-container" id="invoicesTable">
<p style="color: #888;">Loading invoices...</p>
</div>
</div>
</div>
<!-- Reports Tab -->
<div id="tab-reports" class="tab-content">
<div class="section">
<h2>📈 Financial Reports</h2>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-bottom: 30px;">
<div style="border: 2px solid #e0e0e0; padding: 20px; border-radius: 12px;">
<h3 style="color: #36454f; margin-bottom: 10px;">P&L Statement</h3>
<p style="color: #666; font-size: 14px; margin-bottom: 15px;">Profit and Loss report for selected period</p>
<button class="action-button">Generate Report</button>
</div>
<div style="border: 2px solid #e0e0e0; padding: 20px; border-radius: 12px;">
<h3 style="color: #36454f; margin-bottom: 10px;">Budget Analysis</h3>
<p style="color: #666; font-size: 14px; margin-bottom: 15px;">Compare actual vs budgeted amounts</p>
<button class="action-button">Generate Report</button>
</div>
<div style="border: 2px solid #e0e0e0; padding: 20px; border-radius: 12px;">
<h3 style="color: #36454f; margin-bottom: 10px;">Cash Flow</h3>
<p style="color: #666; font-size: 14px; margin-bottom: 15px;">Track cash inflows and outflows</p>
<button class="action-button">Generate Report</button>
</div>
<div style="border: 2px solid #e0e0e0; padding: 20px; border-radius: 12px;">
<h3 style="color: #36454f; margin-bottom: 10px;">Expense Breakdown</h3>
<p style="color: #666; font-size: 14px; margin-bottom: 15px;">Category-wise expense analysis</p>
<button class="action-button">Generate Report</button>
</div>
</div>
<div class="chart-placeholder">
Chart: Year-over-Year Financial Comparison
</div>
</div>
</div>
</div>
<script>
let ordersData = [];
let filteredOrdersData = [];
function switchTab(tabName) {
// Hide all tabs
document.querySelectorAll('.tab-content').forEach(tab => {
tab.classList.remove('active');
});
document.querySelectorAll('.tab-button').forEach(btn => {
btn.classList.remove('active');
});
// Show selected tab
document.getElementById('tab-' + tabName).classList.add('active');
event.target.classList.add('active');
// Load data if needed
if (tabName === 'expenses') {
loadExpenses();
} else if (tabName === 'invoices') {
loadInvoices();
} else if (tabName === 'orders') {
loadOrders();
}
}
async function sendMessage() {
const input = document.getElementById('chatInput');
const message = input.value.trim();
if (!message) return;
// Add user message
const chatMessages = document.getElementById('chatMessages');
chatMessages.innerHTML += \`
<div class="message user">\${message}</div>
\`;
input.value = '';
chatMessages.scrollTop = chatMessages.scrollHeight;
// Send to API
try {
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message })
});
const data = await response.json();
// Add assistant response
chatMessages.innerHTML += \`
<div class="message assistant">\${data.response.replace(/\\n/g, '<br>')}</div>
\`;
chatMessages.scrollTop = chatMessages.scrollHeight;
} catch (error) {
console.error('Chat error:', error);
chatMessages.innerHTML += \`
<div class="message assistant">Sorry, I encountered an error. Please try again.</div>
\`;
}
}
function sendSuggestion(text) {
document.getElementById('chatInput').value = text;
sendMessage();
}
async function loadExpenses() {
const container = document.getElementById('expensesTable');
try {
const response = await fetch('/api/expenses');
const data = await response.json();
let html = '<table><thead><tr><th>Date</th><th>Category</th><th>Vendor</th><th>Description</th><th>Amount</th><th>Status</th><th>Actions</th></tr></thead><tbody>';
data.expenses.forEach(exp => {
const statusClass = 'status-' + exp.status;
html += \`
<tr>
<td>\${new Date(exp.date).toLocaleDateString()}</td>
<td>\${exp.category}</td>
<td>\${exp.vendor}</td>
<td>\${exp.description}</td>
<td>$\${exp.amount.toFixed(2)}</td>
<td><span class="status-badge \${statusClass}">\${exp.status.toUpperCase()}</span></td>
<td><button class="action-button">View</button></td>
</tr>
\`;
});
html += '</tbody></table>';
container.innerHTML = html;
} catch (error) {
console.error('Load expenses error:', error);
container.innerHTML = '<p style="color: #ef4444;">Error loading expenses</p>';
}
}
async function loadInvoices() {
const container = document.getElementById('invoicesTable');
try {
const response = await fetch('/api/invoices');
const data = await response.json();
let html = '<table><thead><tr><th>Invoice #</th><th>Customer</th><th>Issue Date</th><th>Due Date</th><th>Amount</th><th>Status</th><th>Actions</th></tr></thead><tbody>';
data.invoices.forEach(inv => {
const statusClass = 'status-' + inv.status;
html += \`
<tr>
<td>\${inv.invoiceNumber}</td>
<td>\${inv.customerName}</td>
<td>\${new Date(inv.issueDate).toLocaleDateString()}</td>
<td>\${new Date(inv.dueDate).toLocaleDateString()}</td>
<td>$\${inv.amount.toFixed(2)}</td>
<td><span class="status-badge \${statusClass}">\${inv.status.toUpperCase()}</span></td>
<td><button class="action-button">View</button></td>
</tr>
\`;
});
html += '</tbody></table>';
container.innerHTML = html;
} catch (error) {
console.error('Load invoices error:', error);
container.innerHTML = '<p style="color: #ef4444;">Error loading invoices</p>';
}
}
async function loadOrders() {
const container = document.getElementById('ordersTable');
container.innerHTML = '<p style="color: #888;">Loading orders from Shopify...</p>';
try {
const response = await fetch('/api/orders');
const data = await response.json();
ordersData = data.orders || [];
filteredOrdersData = [...ordersData];
displayOrders();
} catch (error) {
console.error('Load orders error:', error);
container.innerHTML = '<p style="color: #ef4444;">Error loading orders from Shopify. Please check your connection.</p>';
}
}
function displayOrders() {
const container = document.getElementById('ordersTable');
if (filteredOrdersData.length === 0) {
container.innerHTML = '<p style="color: #888;">No orders found.</p>';
return;
}
let html = '<table><thead><tr><th>Order #</th><th>Date</th><th>Financial Status</th><th>Fulfillment Status</th><th>Total</th><th>Items</th><th>Actions</th></tr></thead><tbody>';
filteredOrdersData.forEach(order => {
const date = new Date(order.createdAt).toLocaleDateString();
const amount = parseFloat(order.totalPriceSet.shopMoney.amount);
const currency = order.totalPriceSet.shopMoney.currencyCode;
// Create line items list
let itemsList = '<ul style="margin: 0; padding-left: 20px; font-size: 12px;">';
order.lineItems.edges.forEach(edge => {
const item = edge.node;
const sku = item.sku ? \` (SKU: \${item.sku})\` : '';
itemsList += \`<li>\${item.title} x\${item.quantity}\${sku}</li>\`;
});
itemsList += '</ul>';
const financialClass = order.displayFinancialStatus.toLowerCase().includes('paid')
? 'status-paid'
: order.displayFinancialStatus.toLowerCase().includes('pending')
? 'status-pending'
: 'status-draft';
const fulfillmentClass = order.displayFulfillmentStatus && order.displayFulfillmentStatus.toLowerCase().includes('fulfilled')
? 'status-paid'
: order.displayFulfillmentStatus && order.displayFulfillmentStatus.toLowerCase().includes('partial')
? 'status-approved'
: 'status-pending';
html += \`
<tr>
<td style="font-weight: 600;">\${order.name}</td>
<td>\${date}</td>
<td><span class="status-badge \${financialClass}">\${order.displayFinancialStatus || 'N/A'}</span></td>
<td><span class="status-badge \${fulfillmentClass}">\${order.displayFulfillmentStatus || 'Unfulfilled'}</span></td>
<td style="font-weight: 600;">\${currency} $\${amount.toFixed(2)}</td>
<td>\${itemsList}</td>
<td><button class="action-button" onclick="viewOrderDetails('\${order.id}')">View</button></td>
</tr>
\`;
});
html += '</tbody></table>';
container.innerHTML = html;
}
function filterOrders() {
const searchTerm = document.getElementById('orderSearch').value.toLowerCase();
if (searchTerm === '') {
filteredOrdersData = [...ordersData];
} else {
filteredOrdersData = ordersData.filter(order => {
return order.name.toLowerCase().includes(searchTerm) ||
order.lineItems.edges.some(edge =>
edge.node.title.toLowerCase().includes(searchTerm) ||
(edge.node.sku && edge.node.sku.toLowerCase().includes(searchTerm))
);
});
}
sortOrders();
}
function sortOrders() {
const sortValue = document.getElementById('orderSort').value;
switch (sortValue) {
case 'date-desc':
filteredOrdersData.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
break;
case 'date-asc':
filteredOrdersData.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
break;
case 'amount-desc':
filteredOrdersData.sort((a, b) => parseFloat(b.totalPriceSet.shopMoney.amount) - parseFloat(a.totalPriceSet.shopMoney.amount));
break;
case 'amount-asc':
filteredOrdersData.sort((a, b) => parseFloat(a.totalPriceSet.shopMoney.amount) - parseFloat(b.totalPriceSet.shopMoney.amount));
break;
}
displayOrders();
}
function viewOrderDetails(orderId) {
const order = ordersData.find(o => o.id === orderId);
if (order) {
alert(\`Order Details:\\n\\nOrder: \${order.name}\\nCreated: \${new Date(order.createdAt).toLocaleString()}\\nTotal: \${order.totalPriceSet.shopMoney.currencyCode} $\${order.totalPriceSet.shopMoney.amount}\\nFinancial Status: \${order.displayFinancialStatus}\\nFulfillment Status: \${order.displayFulfillmentStatus || 'Unfulfilled'}\\n\\nNote: Full order details view can be implemented with a modal or separate page.\`);
}
}
</script>
</body>
</html>
`);
});
// API Endpoints
// Chat endpoint
app.post('/api/chat', async (req: Request, res: Response) => {
try {
const { message } = req.body;
stats.totalQueries++;
if (!req.session.chatHistory) {
req.session.chatHistory = [];
}
// Memory system commands
if (message.toLowerCase().match(/^(remember|note|preference|learning):/i)) {
const parts = message.split(':');
const type = parts[0].toLowerCase();
const content = parts.slice(1).join(':').trim();
const memType = type === 'remember' ? 'note' : type as any;
agentMemory.add(content, memType);
return res.json({
response: `✅ Memory saved: "${content}"`,
success: true,
memoryAdded: true
});
}
if (message.toLowerCase().includes('show') && message.toLowerCase().includes('memor')) {
const summary = agentMemory.getContextSummary();
return res.json({ response: summary, success: true });
}
if (message.toLowerCase().includes('search memor')) {
const query = message.split(/for|about/i)[1]?.trim() || '';
const results = agentMemory.search(query);
return res.json({
response: results.length > 0
? 'Found memories:\n' + results.map(m => `- ${m.content}`).join('\n')
: 'No matching memories found.',
success: true
});
}
if (message.toLowerCase().includes('forget') || message.toLowerCase().includes('delete memor')) {
const query = message.split(/forget|delete/i)[1]?.trim() || '';
const results = agentMemory.search(query);
if (results.length > 0) {
agentMemory.delete(results[0].id);
return res.json({ response: `✅ Forgot: "${results[0].content}"`, success: true });
}
return res.json({ response: 'Nothing found to forget.', success: true });
}
// Add user message to history
req.session.chatHistory.push({
role: 'user',
content: message
});
// Keep only last 20 messages for context
if (req.session.chatHistory.length > 20) {
req.session.chatHistory = req.session.chatHistory.slice(-20);
}
const systemPrompt = `You are the Accounting Agent for Designer Wallcoverings, powered by Claude 4 Sonnet.
Your role is to assist with financial management tasks:
CAPABILITIES:
- Expense tracking and categorization
- Invoice management and tracking
- P&L report generation
- Budget forecasting and analysis
- Financial insights and recommendations
- Cash flow analysis
- Vendor payment tracking
CURRENT SYSTEM DATA:
- Total expenses tracked: ${expenses.length}
- Pending expenses: ${expenses.filter(e => e.status === 'pending').length}
- Total invoices: ${invoices.length}
- Overdue invoices: ${invoices.filter(i => i.status === 'overdue').length}
SAMPLE EXPENSE CATEGORIES:
- Office Supplies
- Marketing
- Software/Subscriptions
- Shipping
- Inventory
- Utilities
- Professional Services
When users ask about specific financial data, provide helpful summaries and actionable insights. If they need to integrate with QuickBooks or other accounting software, recommend that enhancement.
Be professional, accurate, and provide clear financial guidance.`;
const response = await anthropic.messages.create({
model: 'claude-opus-4-8',
max_tokens: 1024,
system: systemPrompt,
messages: req.session.chatHistory
});
const assistantMessage = response.content[0].type === 'text'
? response.content[0].text
: 'I apologize, but I encountered an error processing your request.';
// Add assistant response to history
req.session.chatHistory.push({
role: 'assistant',
content: assistantMessage
});
logActivity('Chat Query', `User asked: "${message.substring(0, 50)}..."`);
res.json({ response: assistantMessage });
} catch (error) {
console.error('Chat error:', error);
res.status(500).json({ error: 'Failed to process chat message' });
}
});
// Get expenses
app.get('/api/expenses', (req: Request, res: Response) => {
res.json({ expenses });
});
// Get invoices
app.get('/api/invoices', (req: Request, res: Response) => {
res.json({ invoices });
});
// Get Shopify orders
app.get('/api/orders', async (req: Request, res: Response) => {
try {
const orders = await fetchShopifyOrders();
logActivity('Fetch Orders', `Retrieved ${orders.length} orders from Shopify`);
res.json({ orders });
} catch (error) {
console.error('Error in /api/orders:', error);
res.status(500).json({ error: 'Failed to fetch orders from Shopify' });
}
});
// Get statistics
app.get('/api/stats', (req: Request, res: Response) => {
res.json(stats);
});
// Get activity log
app.get('/api/activity', (req: Request, res: Response) => {
res.json({ activities: activityLog });
});
// Start server
app.listen(PORT, () => {
console.log(`💰 Accounting Agent running on port ${PORT}`);
console.log(`Dashboard: http://45.61.58.125:${PORT}`);
logActivity('System Start', 'Accounting Agent initialized');
});