2 * CWA Fieldpine POS API Integration
3 * Based on real POS API analysis from murwillumbah.cwanz.online/openapi/
4 * This handles actual POS operations like product search, sales processing, etc.
7export interface POSApiResponse<T = any> {
13export interface ProductSearchResult {
21export interface RetailConfig {
22 settings?: Record<string, any>;
30export class CWAPosApi {
31 private baseUrl: string;
32 private apiKey?: string;
34 constructor(storeBaseUrl?: string) {
35 // POS API uses /openapi/ endpoint
36 this.baseUrl = storeBaseUrl ? `${storeBaseUrl}/openapi` : 'https://murwillumbah.cwanz.online/openapi';
40 * Set the API key for authenticated requests
42 setApiKey(apiKey: string) {
47 * Make a request to the POS API
49 private async posRequest(endpoint: string, params: Record<string, string | number> = {}): Promise<POSApiResponse> {
52 if (endpoint.startsWith('BUCK?')) {
53 // For BUCK requests, use base store URL directly
54 const baseStoreUrl = this.baseUrl.replace('/openapi', '');
55 url = new URL(endpoint, baseStoreUrl).toString();
57 url = new URL(endpoint, this.baseUrl).toString();
60 // Add parameters for non-BUCK requests
61 if (!endpoint.startsWith('BUCK?') && Object.keys(params).length > 0) {
62 const urlObj = new URL(url);
63 Object.entries(params).forEach(([key, value]) => {
64 urlObj.searchParams.set(key, String(value));
66 url = urlObj.toString();
69 const headers: HeadersInit = {
71 'Cache-Control': 'no-cache',
74 // Add authentication if available
76 headers['Cookie'] = `FieldpineApiKey=${this.apiKey}`;
80 console.log('POS API Request:', url);
82 const response = await fetch(url, {
89 error: `HTTP ${response.status}: ${response.statusText}`,
94 // Try to parse as JSON, fallback to text
95 const contentType = response.headers.get('content-type');
98 if (contentType && contentType.includes('application/json')) {
99 data = await response.json();
101 data = await response.text();
110 error: error instanceof Error ? error.message : 'Unknown error',
116 // POS-specific API methods based on discovered endpoints
119 * Get retail configuration
120 * /openapi/retailconfig
122 async getRetailConfig(): Promise<POSApiResponse<RetailConfig>> {
123 return this.posRequest('retailconfig');
127 * Get a specific page
128 * /openapi/page?key=/report/rr/sell5.htm
130 async getPage(pageKey: string): Promise<POSApiResponse> {
131 return this.posRequest(`page?key=${encodeURIComponent(pageKey)}`);
135 * Get sale list template
136 * /openapi/salelist1.htm
138 async getSaleListTemplate(): Promise<POSApiResponse> {
139 return this.posRequest('salelist1.htm');
143 * Get sale header template
144 * /openapi/saleheader1.htm
146 async getSaleHeaderTemplate(): Promise<POSApiResponse> {
147 return this.posRequest('saleheader1.htm');
151 * Get payment single template
152 * /openapi/paymentsingle.htm
154 async getPaymentSingleTemplate(): Promise<POSApiResponse> {
155 return this.posRequest('paymentsingle.htm');
160 * /openapi/PaymentTypes
162 async getPaymentTypes(): Promise<POSApiResponse> {
163 return this.posRequest('PaymentTypes');
167 * Search products using BUCK API
168 * BUCK?3=retailmax.elink.products.list&8=30&9=f506,8,searchterm
170 async searchProducts(searchTerm: string, limit = 30): Promise<POSApiResponse<ProductSearchResult[]>> {
171 const query = `BUCK?3=retailmax.elink.products.list&8=${limit}&9=f506,8,${encodeURIComponent(searchTerm)}`;
172 return this.posRequest(query);
176 * Get product by ID or barcode
178 async getProduct(idOrBarcode: string): Promise<POSApiResponse<ProductSearchResult>> {
179 // Try exact match first
180 const result = await this.searchProducts(idOrBarcode, 1);
182 if (result.data && Array.isArray(result.data) && result.data.length > 0) {
184 data: result.data[0],
190 error: 'Product not found',
196 * Get all products with pagination
198 async getAllProducts(limit = 100, offset = 0): Promise<POSApiResponse<ProductSearchResult[]>> {
199 // Use empty search to get all products
200 return this.searchProducts('', limit);
204 * Submit a sale (this would need to be implemented based on actual POS API)
206 async submitSale(saleData: {
220 }): Promise<POSApiResponse> {
221 // This is a placeholder - would need to discover the actual sale submission endpoint
222 console.log('Sale submission not yet implemented. Sale data:', saleData);
224 error: 'Sale submission endpoint not yet discovered',
230 * Get specific item by ID (for barcode scanning)
232 async getItem(itemId: string): Promise<POSApiResponse> {
233 return this.posRequest(itemId);
237// Store-specific API instances
238export const cwaPosMurwillumbah = new CWAPosApi('https://murwillumbah.cwanz.online');
239export const cwaPosIig = new CWAPosApi('https://iig.cwanz.online');
240export const cwaPosCowra = new CWAPosApi('https://cowra.cwanz.online');
242// Default POS API instance (Murwillumbah)
243export const cwaPosApi = cwaPosMurwillumbah;
245// Helper function to set API key for all POS instances
246export function setCWAPosApiKey(apiKey: string) {
247 cwaPosMurwillumbah.setApiKey(apiKey);
248 cwaPosIig.setApiKey(apiKey);
249 cwaPosCowra.setApiKey(apiKey);
252// Helper function to get POS API for specific store
253export function getCWAPosApi(storeUrl: string): CWAPosApi {
255 case 'https://murwillumbah.cwanz.online':
256 return cwaPosMurwillumbah;
257 case 'https://iig.cwanz.online':
259 case 'https://cowra.cwanz.online':
262 return new CWAPosApi(storeUrl);