=== UPDATES === - fieldedge: Added apps, tools, main server entry, full rebuild - lightspeed: Added complete src/ directory with tools + apps - squarespace: Full rebuild — new apps, clients, tools, types modules - toast: Full rebuild — api-client, apps, tools, types - touchbistro: Full rebuild — api-client, tools, types, gitignore - servicetitan: Added 4 React UI apps (call-tracking, lead-source-analytics, performance-metrics, schedule-calendar) All servers restructured from single-file to modular architecture.
529 lines
20 KiB
TypeScript
529 lines
20 KiB
TypeScript
/**
|
|
* Lightspeed API Client
|
|
* Supports both Retail (X-Series) and Restaurant (R-Series) APIs
|
|
*/
|
|
|
|
import axios, { AxiosInstance } from 'axios';
|
|
import type {
|
|
LightspeedConfig,
|
|
Product,
|
|
ProductInventory,
|
|
Customer,
|
|
Sale,
|
|
Order,
|
|
Employee,
|
|
Category,
|
|
Supplier,
|
|
Discount,
|
|
LoyaltyProgram,
|
|
CustomerLoyalty,
|
|
Shop,
|
|
Register,
|
|
PaymentType,
|
|
TaxCategory,
|
|
Manufacturer,
|
|
ApiResponse
|
|
} from '../types/index.js';
|
|
|
|
export class LightspeedClient {
|
|
private client: AxiosInstance;
|
|
private config: LightspeedConfig;
|
|
|
|
constructor(config: LightspeedConfig) {
|
|
this.config = config;
|
|
const baseUrl = config.baseUrl || 'https://api.lightspeedapp.com/API/V3';
|
|
|
|
this.client = axios.create({
|
|
baseURL: baseUrl,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${config.apiKey}`
|
|
},
|
|
timeout: 30000
|
|
});
|
|
|
|
// Add response interceptor for error handling
|
|
this.client.interceptors.response.use(
|
|
response => response,
|
|
error => {
|
|
console.error('Lightspeed API Error:', error.response?.data || error.message);
|
|
throw error;
|
|
}
|
|
);
|
|
}
|
|
|
|
private getAccountPath(): string {
|
|
return `/Account/${this.config.accountId}`;
|
|
}
|
|
|
|
// ========== PRODUCTS ==========
|
|
|
|
async getProducts(params?: { limit?: number; offset?: number; categoryID?: string }): Promise<ApiResponse<Product[]>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Item.json`, { params });
|
|
return { success: true, data: response.data.Item || [] };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async getProduct(productID: string): Promise<ApiResponse<Product>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Item/${productID}.json`);
|
|
return { success: true, data: response.data.Item };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async createProduct(product: Partial<Product>): Promise<ApiResponse<Product>> {
|
|
try {
|
|
const response = await this.client.post(`${this.getAccountPath()}/Item.json`, { Item: product });
|
|
return { success: true, data: response.data.Item };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async updateProduct(productID: string, updates: Partial<Product>): Promise<ApiResponse<Product>> {
|
|
try {
|
|
const response = await this.client.put(`${this.getAccountPath()}/Item/${productID}.json`, { Item: updates });
|
|
return { success: true, data: response.data.Item };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async deleteProduct(productID: string): Promise<ApiResponse<boolean>> {
|
|
try {
|
|
await this.client.delete(`${this.getAccountPath()}/Item/${productID}.json`);
|
|
return { success: true, data: true };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async getProductInventory(productID: string, shopID?: string): Promise<ApiResponse<ProductInventory[]>> {
|
|
try {
|
|
const params = shopID ? { shopID } : {};
|
|
const response = await this.client.get(`${this.getAccountPath()}/Item/${productID}/ItemShops.json`, { params });
|
|
return { success: true, data: response.data.ItemShop || [] };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async updateInventory(productID: string, shopID: string, qty: number): Promise<ApiResponse<ProductInventory>> {
|
|
try {
|
|
const response = await this.client.put(
|
|
`${this.getAccountPath()}/Item/${productID}/ItemShops/${shopID}.json`,
|
|
{ ItemShop: { qty } }
|
|
);
|
|
return { success: true, data: response.data.ItemShop };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
// ========== CUSTOMERS ==========
|
|
|
|
async getCustomers(params?: { limit?: number; offset?: number; email?: string }): Promise<ApiResponse<Customer[]>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Customer.json`, { params });
|
|
return { success: true, data: response.data.Customer || [] };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async getCustomer(customerID: string): Promise<ApiResponse<Customer>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Customer/${customerID}.json`);
|
|
return { success: true, data: response.data.Customer };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async createCustomer(customer: Partial<Customer>): Promise<ApiResponse<Customer>> {
|
|
try {
|
|
const response = await this.client.post(`${this.getAccountPath()}/Customer.json`, { Customer: customer });
|
|
return { success: true, data: response.data.Customer };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async updateCustomer(customerID: string, updates: Partial<Customer>): Promise<ApiResponse<Customer>> {
|
|
try {
|
|
const response = await this.client.put(`${this.getAccountPath()}/Customer/${customerID}.json`, { Customer: updates });
|
|
return { success: true, data: response.data.Customer };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async deleteCustomer(customerID: string): Promise<ApiResponse<boolean>> {
|
|
try {
|
|
await this.client.delete(`${this.getAccountPath()}/Customer/${customerID}.json`);
|
|
return { success: true, data: true };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
// ========== SALES ==========
|
|
|
|
async getSales(params?: { limit?: number; offset?: number; startDate?: string; endDate?: string }): Promise<ApiResponse<Sale[]>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Sale.json`, { params });
|
|
return { success: true, data: response.data.Sale || [] };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async getSale(saleID: string): Promise<ApiResponse<Sale>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Sale/${saleID}.json`);
|
|
return { success: true, data: response.data.Sale };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async createSale(sale: Partial<Sale>): Promise<ApiResponse<Sale>> {
|
|
try {
|
|
const response = await this.client.post(`${this.getAccountPath()}/Sale.json`, { Sale: sale });
|
|
return { success: true, data: response.data.Sale };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async updateSale(saleID: string, updates: Partial<Sale>): Promise<ApiResponse<Sale>> {
|
|
try {
|
|
const response = await this.client.put(`${this.getAccountPath()}/Sale/${saleID}.json`, { Sale: updates });
|
|
return { success: true, data: response.data.Sale };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async voidSale(saleID: string): Promise<ApiResponse<Sale>> {
|
|
try {
|
|
const response = await this.client.put(`${this.getAccountPath()}/Sale/${saleID}.json`, { Sale: { voided: true } });
|
|
return { success: true, data: response.data.Sale };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
// ========== ORDERS ==========
|
|
|
|
async getOrders(params?: { limit?: number; offset?: number; status?: string }): Promise<ApiResponse<Order[]>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Order.json`, { params });
|
|
return { success: true, data: response.data.Order || [] };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async getOrder(orderID: string): Promise<ApiResponse<Order>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Order/${orderID}.json`);
|
|
return { success: true, data: response.data.Order };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async createOrder(order: Partial<Order>): Promise<ApiResponse<Order>> {
|
|
try {
|
|
const response = await this.client.post(`${this.getAccountPath()}/Order.json`, { Order: order });
|
|
return { success: true, data: response.data.Order };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async updateOrder(orderID: string, updates: Partial<Order>): Promise<ApiResponse<Order>> {
|
|
try {
|
|
const response = await this.client.put(`${this.getAccountPath()}/Order/${orderID}.json`, { Order: updates });
|
|
return { success: true, data: response.data.Order };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async deleteOrder(orderID: string): Promise<ApiResponse<boolean>> {
|
|
try {
|
|
await this.client.delete(`${this.getAccountPath()}/Order/${orderID}.json`);
|
|
return { success: true, data: true };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
// ========== EMPLOYEES ==========
|
|
|
|
async getEmployees(params?: { limit?: number; offset?: number }): Promise<ApiResponse<Employee[]>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Employee.json`, { params });
|
|
return { success: true, data: response.data.Employee || [] };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async getEmployee(employeeID: string): Promise<ApiResponse<Employee>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Employee/${employeeID}.json`);
|
|
return { success: true, data: response.data.Employee };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async createEmployee(employee: Partial<Employee>): Promise<ApiResponse<Employee>> {
|
|
try {
|
|
const response = await this.client.post(`${this.getAccountPath()}/Employee.json`, { Employee: employee });
|
|
return { success: true, data: response.data.Employee };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async updateEmployee(employeeID: string, updates: Partial<Employee>): Promise<ApiResponse<Employee>> {
|
|
try {
|
|
const response = await this.client.put(`${this.getAccountPath()}/Employee/${employeeID}.json`, { Employee: updates });
|
|
return { success: true, data: response.data.Employee };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async deleteEmployee(employeeID: string): Promise<ApiResponse<boolean>> {
|
|
try {
|
|
await this.client.delete(`${this.getAccountPath()}/Employee/${employeeID}.json`);
|
|
return { success: true, data: true };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
// ========== CATEGORIES ==========
|
|
|
|
async getCategories(params?: { limit?: number; offset?: number }): Promise<ApiResponse<Category[]>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Category.json`, { params });
|
|
return { success: true, data: response.data.Category || [] };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async getCategory(categoryID: string): Promise<ApiResponse<Category>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Category/${categoryID}.json`);
|
|
return { success: true, data: response.data.Category };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async createCategory(category: Partial<Category>): Promise<ApiResponse<Category>> {
|
|
try {
|
|
const response = await this.client.post(`${this.getAccountPath()}/Category.json`, { Category: category });
|
|
return { success: true, data: response.data.Category };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async updateCategory(categoryID: string, updates: Partial<Category>): Promise<ApiResponse<Category>> {
|
|
try {
|
|
const response = await this.client.put(`${this.getAccountPath()}/Category/${categoryID}.json`, { Category: updates });
|
|
return { success: true, data: response.data.Category };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async deleteCategory(categoryID: string): Promise<ApiResponse<boolean>> {
|
|
try {
|
|
await this.client.delete(`${this.getAccountPath()}/Category/${categoryID}.json`);
|
|
return { success: true, data: true };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
// ========== SUPPLIERS ==========
|
|
|
|
async getSuppliers(params?: { limit?: number; offset?: number }): Promise<ApiResponse<Supplier[]>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Vendor.json`, { params });
|
|
return { success: true, data: response.data.Vendor || [] };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async getSupplier(supplierID: string): Promise<ApiResponse<Supplier>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Vendor/${supplierID}.json`);
|
|
return { success: true, data: response.data.Vendor };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async createSupplier(supplier: Partial<Supplier>): Promise<ApiResponse<Supplier>> {
|
|
try {
|
|
const response = await this.client.post(`${this.getAccountPath()}/Vendor.json`, { Vendor: supplier });
|
|
return { success: true, data: response.data.Vendor };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async updateSupplier(supplierID: string, updates: Partial<Supplier>): Promise<ApiResponse<Supplier>> {
|
|
try {
|
|
const response = await this.client.put(`${this.getAccountPath()}/Vendor/${supplierID}.json`, { Vendor: updates });
|
|
return { success: true, data: response.data.Vendor };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async deleteSupplier(supplierID: string): Promise<ApiResponse<boolean>> {
|
|
try {
|
|
await this.client.delete(`${this.getAccountPath()}/Vendor/${supplierID}.json`);
|
|
return { success: true, data: true };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
// ========== DISCOUNTS ==========
|
|
|
|
async getDiscounts(params?: { limit?: number; offset?: number }): Promise<ApiResponse<Discount[]>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Discount.json`, { params });
|
|
return { success: true, data: response.data.Discount || [] };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async getDiscount(discountID: string): Promise<ApiResponse<Discount>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Discount/${discountID}.json`);
|
|
return { success: true, data: response.data.Discount };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async createDiscount(discount: Partial<Discount>): Promise<ApiResponse<Discount>> {
|
|
try {
|
|
const response = await this.client.post(`${this.getAccountPath()}/Discount.json`, { Discount: discount });
|
|
return { success: true, data: response.data.Discount };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async updateDiscount(discountID: string, updates: Partial<Discount>): Promise<ApiResponse<Discount>> {
|
|
try {
|
|
const response = await this.client.put(`${this.getAccountPath()}/Discount/${discountID}.json`, { Discount: updates });
|
|
return { success: true, data: response.data.Discount };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async deleteDiscount(discountID: string): Promise<ApiResponse<boolean>> {
|
|
try {
|
|
await this.client.delete(`${this.getAccountPath()}/Discount/${discountID}.json`);
|
|
return { success: true, data: true };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
// ========== SHOPS & REGISTERS ==========
|
|
|
|
async getShops(): Promise<ApiResponse<Shop[]>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Shop.json`);
|
|
return { success: true, data: response.data.Shop || [] };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async getShop(shopID: string): Promise<ApiResponse<Shop>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Shop/${shopID}.json`);
|
|
return { success: true, data: response.data.Shop };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async getRegisters(shopID?: string): Promise<ApiResponse<Register[]>> {
|
|
try {
|
|
const params = shopID ? { shopID } : {};
|
|
const response = await this.client.get(`${this.getAccountPath()}/Register.json`, { params });
|
|
return { success: true, data: response.data.Register || [] };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
// ========== MANUFACTURERS ==========
|
|
|
|
async getManufacturers(): Promise<ApiResponse<Manufacturer[]>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/Manufacturer.json`);
|
|
return { success: true, data: response.data.Manufacturer || [] };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
async createManufacturer(name: string): Promise<ApiResponse<Manufacturer>> {
|
|
try {
|
|
const response = await this.client.post(`${this.getAccountPath()}/Manufacturer.json`, { Manufacturer: { name } });
|
|
return { success: true, data: response.data.Manufacturer };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
// ========== TAX CATEGORIES ==========
|
|
|
|
async getTaxCategories(): Promise<ApiResponse<TaxCategory[]>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/TaxCategory.json`);
|
|
return { success: true, data: response.data.TaxCategory || [] };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
|
|
// ========== PAYMENT TYPES ==========
|
|
|
|
async getPaymentTypes(): Promise<ApiResponse<PaymentType[]>> {
|
|
try {
|
|
const response = await this.client.get(`${this.getAccountPath()}/PaymentType.json`);
|
|
return { success: true, data: response.data.PaymentType || [] };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.message, details: error.response?.data };
|
|
}
|
|
}
|
|
}
|