EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
cwa-pos-api.ts
Go to the documentation of this file.
1/**
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.
5 */
6
7export interface POSApiResponse<T = any> {
8 data?: T;
9 error?: string;
10 status?: string;
11}
12
13export interface ProductSearchResult {
14 id: string;
15 name: string;
16 barcode?: string;
17 price: number;
18 stock?: number;
19}
20
21export interface RetailConfig {
22 settings?: Record<string, any>;
23 version?: string;
24 store?: {
25 name?: string;
26 id?: string;
27 };
28}
29
30export class CWAPosApi {
31 private baseUrl: string;
32 private apiKey?: string;
33
34 constructor(storeBaseUrl?: string) {
35 // POS API uses /openapi/ endpoint
36 this.baseUrl = storeBaseUrl ? `${storeBaseUrl}/openapi` : 'https://murwillumbah.cwanz.online/openapi';
37 }
38
39 /**
40 * Set the API key for authenticated requests
41 */
42 setApiKey(apiKey: string) {
43 this.apiKey = apiKey;
44 }
45
46 /**
47 * Make a request to the POS API
48 */
49 private async posRequest(endpoint: string, params: Record<string, string | number> = {}): Promise<POSApiResponse> {
50 let url: string;
51
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();
56 } else {
57 url = new URL(endpoint, this.baseUrl).toString();
58 }
59
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));
65 });
66 url = urlObj.toString();
67 }
68
69 const headers: HeadersInit = {
70 'Accept': '*/*',
71 'Cache-Control': 'no-cache',
72 };
73
74 // Add authentication if available
75 if (this.apiKey) {
76 headers['Cookie'] = `FieldpineApiKey=${this.apiKey}`;
77 }
78
79 try {
80 console.log('POS API Request:', url);
81
82 const response = await fetch(url, {
83 method: 'GET',
84 headers,
85 });
86
87 if (!response.ok) {
88 return {
89 error: `HTTP ${response.status}: ${response.statusText}`,
90 status: 'error'
91 };
92 }
93
94 // Try to parse as JSON, fallback to text
95 const contentType = response.headers.get('content-type');
96 let data: any;
97
98 if (contentType && contentType.includes('application/json')) {
99 data = await response.json();
100 } else {
101 data = await response.text();
102 }
103
104 return {
105 data,
106 status: 'success'
107 };
108 } catch (error) {
109 return {
110 error: error instanceof Error ? error.message : 'Unknown error',
111 status: 'error'
112 };
113 }
114 }
115
116 // POS-specific API methods based on discovered endpoints
117
118 /**
119 * Get retail configuration
120 * /openapi/retailconfig
121 */
122 async getRetailConfig(): Promise<POSApiResponse<RetailConfig>> {
123 return this.posRequest('retailconfig');
124 }
125
126 /**
127 * Get a specific page
128 * /openapi/page?key=/report/rr/sell5.htm
129 */
130 async getPage(pageKey: string): Promise<POSApiResponse> {
131 return this.posRequest(`page?key=${encodeURIComponent(pageKey)}`);
132 }
133
134 /**
135 * Get sale list template
136 * /openapi/salelist1.htm
137 */
138 async getSaleListTemplate(): Promise<POSApiResponse> {
139 return this.posRequest('salelist1.htm');
140 }
141
142 /**
143 * Get sale header template
144 * /openapi/saleheader1.htm
145 */
146 async getSaleHeaderTemplate(): Promise<POSApiResponse> {
147 return this.posRequest('saleheader1.htm');
148 }
149
150 /**
151 * Get payment single template
152 * /openapi/paymentsingle.htm
153 */
154 async getPaymentSingleTemplate(): Promise<POSApiResponse> {
155 return this.posRequest('paymentsingle.htm');
156 }
157
158 /**
159 * Get payment types
160 * /openapi/PaymentTypes
161 */
162 async getPaymentTypes(): Promise<POSApiResponse> {
163 return this.posRequest('PaymentTypes');
164 }
165
166 /**
167 * Search products using BUCK API
168 * BUCK?3=retailmax.elink.products.list&8=30&9=f506,8,searchterm
169 */
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);
173 }
174
175 /**
176 * Get product by ID or barcode
177 */
178 async getProduct(idOrBarcode: string): Promise<POSApiResponse<ProductSearchResult>> {
179 // Try exact match first
180 const result = await this.searchProducts(idOrBarcode, 1);
181
182 if (result.data && Array.isArray(result.data) && result.data.length > 0) {
183 return {
184 data: result.data[0],
185 status: 'success'
186 };
187 }
188
189 return {
190 error: 'Product not found',
191 status: 'error'
192 };
193 }
194
195 /**
196 * Get all products with pagination
197 */
198 async getAllProducts(limit = 100, offset = 0): Promise<POSApiResponse<ProductSearchResult[]>> {
199 // Use empty search to get all products
200 return this.searchProducts('', limit);
201 }
202
203 /**
204 * Submit a sale (this would need to be implemented based on actual POS API)
205 */
206 async submitSale(saleData: {
207 items: Array<{
208 productId: string;
209 quantity: number;
210 price: number;
211 }>;
212 payment: {
213 type: string;
214 amount: number;
215 };
216 customer?: {
217 id?: string;
218 name?: string;
219 };
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);
223 return {
224 error: 'Sale submission endpoint not yet discovered',
225 status: 'error'
226 };
227 }
228
229 /**
230 * Get specific item by ID (for barcode scanning)
231 */
232 async getItem(itemId: string): Promise<POSApiResponse> {
233 return this.posRequest(itemId);
234 }
235}
236
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');
241
242// Default POS API instance (Murwillumbah)
243export const cwaPosApi = cwaPosMurwillumbah;
244
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);
250}
251
252// Helper function to get POS API for specific store
253export function getCWAPosApi(storeUrl: string): CWAPosApi {
254 switch (storeUrl) {
255 case 'https://murwillumbah.cwanz.online':
256 return cwaPosMurwillumbah;
257 case 'https://iig.cwanz.online':
258 return cwaPosIig;
259 case 'https://cowra.cwanz.online':
260 return cwaPosCowra;
261 default:
262 return new CWAPosApi(storeUrl);
263 }
264}