washday-sdk 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +126 -0
- package/babel.config.js +8 -0
- package/dist/api/attendance/delete.js +2 -0
- package/dist/api/attendance/get.js +83 -0
- package/dist/api/attendance/index.js +4 -0
- package/dist/api/attendance/post.js +39 -0
- package/dist/api/attendance/put.js +25 -0
- package/dist/api/auth/index.js +3 -0
- package/dist/api/auth/post.js +170 -0
- package/dist/api/axiosInstance.js +15 -11
- package/dist/api/cashierbox/delete.js +40 -0
- package/dist/api/cashierbox/get.js +63 -0
- package/dist/api/cashierbox/post.js +42 -0
- package/dist/api/cashierbox/put.js +42 -0
- package/dist/api/cashups/delete.js +25 -0
- package/dist/api/cashups/get.js +60 -0
- package/dist/api/cashups/index.js +4 -0
- package/dist/api/cashups/post.js +25 -0
- package/dist/api/cashups/put.js +52 -0
- package/dist/api/cfdi/delete.js +25 -0
- package/dist/api/cfdi/get.js +126 -0
- package/dist/api/cfdi/index.js +4 -0
- package/dist/api/cfdi/post.js +41 -0
- package/dist/api/cfdi/put.js +52 -0
- package/dist/api/companies/get.js +29 -0
- package/dist/api/companies/post.js +41 -0
- package/dist/api/companies/put.js +57 -0
- package/dist/api/countries/get.js +29 -0
- package/dist/api/countries/post.js +1 -0
- package/dist/api/countries/put.js +1 -0
- package/dist/api/csv/get.js +325 -0
- package/dist/api/csv/index.js +1 -0
- package/dist/api/customers/delete.js +40 -0
- package/dist/api/customers/get.js +75 -20
- package/dist/api/customers/index.js +4 -1
- package/dist/api/customers/post.js +41 -0
- package/dist/api/customers/put.js +52 -0
- package/dist/api/discounts/get.js +120 -0
- package/dist/api/discounts/post.js +40 -0
- package/dist/api/discounts/put.js +68 -0
- package/dist/api/index.js +316 -8
- package/dist/api/inventory/delete.js +26 -0
- package/dist/api/inventory/get.js +62 -0
- package/dist/api/inventory/index.js +4 -0
- package/dist/api/inventory/post.js +26 -0
- package/dist/api/inventory/put.js +41 -0
- package/dist/api/order/delete.js +40 -0
- package/dist/api/order/get.js +181 -0
- package/dist/api/order/index.js +4 -0
- package/dist/api/order/post.js +115 -0
- package/dist/api/order/put.js +202 -0
- package/dist/api/outsourcedOrders/delete.js +25 -0
- package/dist/api/outsourcedOrders/get.js +62 -0
- package/dist/api/outsourcedOrders/index.js +4 -0
- package/dist/api/outsourcedOrders/post.js +25 -0
- package/dist/api/outsourcedOrders/put.js +25 -0
- package/dist/api/partners/delete.js +25 -0
- package/dist/api/partners/get.js +40 -0
- package/dist/api/partners/index.js +4 -0
- package/dist/api/partners/post.js +25 -0
- package/dist/api/partners/put.js +25 -0
- package/dist/api/pdf/get.js +43 -0
- package/dist/api/pdf/index.js +1 -0
- package/dist/api/products/delete.js +40 -0
- package/dist/api/products/get.js +25 -0
- package/dist/api/products/index.js +4 -0
- package/dist/api/products/post.js +60 -0
- package/dist/api/products/put.js +40 -0
- package/dist/api/publics/get.js +26 -0
- package/dist/api/publics/index.js +1 -0
- package/dist/api/reports/get.js +303 -0
- package/dist/api/reports/index.js +1 -0
- package/dist/api/reviews/delete.js +15 -0
- package/dist/api/reviews/get.js +41 -0
- package/dist/api/reviews/index.js +4 -0
- package/dist/api/reviews/post.js +20 -0
- package/dist/api/reviews/put.js +52 -0
- package/dist/api/routes/delete.js +25 -0
- package/dist/api/routes/get.js +95 -0
- package/dist/api/routes/index.js +4 -0
- package/dist/api/routes/post.js +67 -0
- package/dist/api/routes/put.js +54 -0
- package/dist/api/sections/delete.js +25 -0
- package/dist/api/sections/get.js +71 -0
- package/dist/api/sections/index.js +4 -0
- package/dist/api/sections/post.js +26 -0
- package/dist/api/sections/put.js +25 -0
- package/dist/api/staff/delete.js +26 -0
- package/dist/api/staff/get.js +40 -0
- package/dist/api/staff/post.js +26 -0
- package/dist/api/staff/put.js +26 -0
- package/dist/api/stores/get.js +110 -0
- package/dist/api/stores/post.js +61 -0
- package/dist/api/stores/put.js +44 -0
- package/dist/api/stripe/get.js +1 -0
- package/dist/api/stripe/post.js +71 -0
- package/dist/api/stripe/put.js +1 -0
- package/dist/api/supplies/delete.js +26 -0
- package/dist/api/supplies/get.js +62 -0
- package/dist/api/supplies/post.js +26 -0
- package/dist/api/supplies/put.js +41 -0
- package/dist/api/users/delete.js +26 -0
- package/dist/api/users/post.js +25 -0
- package/dist/api/users/put.js +29 -0
- package/dist/enum/index.js +12 -7
- package/dist/index.js +3 -33
- package/dist/interfaces/Api.js +1 -3
- package/dist/interfaces/Apple.js +16 -0
- package/dist/interfaces/Attendance.js +1 -0
- package/dist/interfaces/Customer.js +1 -2
- package/dist/interfaces/Order.js +1 -2
- package/dist/interfaces/Permission.js +1 -2
- package/dist/interfaces/Product.js +1 -2
- package/dist/interfaces/Section.js +1 -2
- package/dist/interfaces/Store.js +1 -2
- package/dist/interfaces/StoreImage.js +1 -2
- package/dist/interfaces/User.js +1 -2
- package/dist/utils/apiUtils.js +9 -0
- package/dist/utils/index.js +2 -17
- package/dist/utils/orders/calculateOrderTotal.js +30 -21
- package/dist/utils/orders/calculateTotalTaxesIncluded.js +55 -29
- package/dist/utils/orders/calculateTotalTaxesOverPrice.js +56 -45
- package/dist/utils/orders/helpers.js +126 -17
- package/dist/utils/orders/index.js +3 -5
- package/dist/utils/receipt/generateReceiptHTML.js +157 -0
- package/dist/utils/util.js +63 -0
- package/docs/README.md +66 -0
- package/docs/examples/common-use-cases.md +487 -0
- package/docs/getting-started.md +237 -0
- package/docs/modules/attendance.md +404 -0
- package/jest.config.js +0 -0
- package/package.json +12 -4
- package/src/api/attendance/delete.ts +1 -0
- package/src/api/attendance/get.ts +81 -0
- package/src/api/attendance/index.ts +4 -0
- package/src/api/attendance/post.ts +37 -0
- package/src/api/attendance/put.ts +20 -0
- package/src/api/auth/index.ts +3 -0
- package/src/api/auth/post.ts +198 -0
- package/src/api/axiosInstance.ts +13 -3
- package/src/api/cashierbox/delete.ts +28 -0
- package/src/api/cashierbox/get.ts +53 -0
- package/src/api/cashierbox/post.ts +39 -0
- package/src/api/cashierbox/put.ts +32 -0
- package/src/api/cashups/delete.ts +15 -0
- package/src/api/cashups/get.ts +52 -0
- package/src/api/cashups/index.ts +4 -0
- package/src/api/cashups/post.ts +23 -0
- package/src/api/cashups/put.ts +53 -0
- package/src/api/cfdi/delete.ts +21 -0
- package/src/api/cfdi/get.ts +119 -0
- package/src/api/cfdi/index.ts +4 -0
- package/src/api/cfdi/post.ts +60 -0
- package/src/api/cfdi/put.ts +53 -0
- package/src/api/companies/get.ts +23 -0
- package/src/api/companies/post.ts +29 -0
- package/src/api/companies/put.ts +43 -0
- package/src/api/countries/get.ts +23 -0
- package/src/api/countries/post.ts +0 -0
- package/src/api/countries/put.ts +0 -0
- package/src/api/csv/get.ts +354 -0
- package/src/api/csv/index.ts +1 -0
- package/src/api/customers/delete.ts +29 -0
- package/src/api/customers/get.ts +80 -8
- package/src/api/customers/index.ts +4 -0
- package/src/api/customers/post.ts +48 -0
- package/src/api/customers/put.ts +68 -0
- package/src/api/discounts/get.ts +100 -0
- package/src/api/discounts/post.ts +35 -0
- package/src/api/discounts/put.ts +66 -0
- package/src/api/index.ts +318 -11
- package/src/api/inventory/delete.ts +16 -0
- package/src/api/inventory/get.ts +53 -0
- package/src/api/inventory/index.ts +4 -0
- package/src/api/inventory/post.ts +22 -0
- package/src/api/inventory/put.ts +35 -0
- package/src/api/order/delete.ts +29 -0
- package/src/api/order/get.ts +207 -0
- package/src/api/order/index.ts +4 -0
- package/src/api/order/post.ts +118 -0
- package/src/api/order/put.ts +224 -0
- package/src/api/outsourcedOrders/delete.ts +15 -0
- package/src/api/outsourcedOrders/get.ts +54 -0
- package/src/api/outsourcedOrders/index.ts +4 -0
- package/src/api/outsourcedOrders/post.ts +24 -0
- package/src/api/outsourcedOrders/put.ts +21 -0
- package/src/api/partners/delete.ts +15 -0
- package/src/api/partners/get.ts +28 -0
- package/src/api/partners/index.ts +4 -0
- package/src/api/partners/post.ts +21 -0
- package/src/api/partners/put.ts +21 -0
- package/src/api/pdf/get.ts +43 -0
- package/src/api/pdf/index.ts +1 -0
- package/src/api/products/delete.ts +28 -0
- package/src/api/products/get.ts +16 -0
- package/src/api/products/index.ts +4 -0
- package/src/api/products/post.ts +73 -0
- package/src/api/products/put.ts +49 -0
- package/src/api/publics/get.ts +16 -0
- package/src/api/publics/index.ts +1 -0
- package/src/api/reports/get.ts +329 -0
- package/src/api/reports/index.ts +1 -0
- package/src/api/reviews/delete.ts +15 -0
- package/src/api/reviews/get.ts +31 -0
- package/src/api/reviews/index.ts +4 -0
- package/src/api/reviews/post.ts +20 -0
- package/src/api/reviews/put.ts +53 -0
- package/src/api/routes/delete.ts +15 -0
- package/src/api/routes/get.ts +86 -0
- package/src/api/routes/index.ts +4 -0
- package/src/api/routes/post.ts +60 -0
- package/src/api/routes/put.ts +44 -0
- package/src/api/sections/delete.ts +15 -0
- package/src/api/sections/get.ts +67 -0
- package/src/api/sections/index.ts +4 -0
- package/src/api/sections/post.ts +22 -0
- package/src/api/sections/put.ts +23 -0
- package/src/api/staff/delete.ts +16 -0
- package/src/api/staff/get.ts +29 -0
- package/src/api/staff/post.ts +17 -0
- package/src/api/staff/put.ts +17 -0
- package/src/api/stores/get.ts +93 -0
- package/src/api/stores/post.ts +49 -0
- package/src/api/stores/put.ts +35 -0
- package/src/api/stripe/get.ts +0 -0
- package/src/api/stripe/post.ts +59 -0
- package/src/api/stripe/put.ts +0 -0
- package/src/api/supplies/delete.ts +16 -0
- package/src/api/supplies/get.ts +53 -0
- package/src/api/supplies/post.ts +26 -0
- package/src/api/supplies/put.ts +33 -0
- package/src/api/users/delete.ts +16 -0
- package/src/api/users/post.ts +18 -0
- package/src/api/users/put.ts +36 -0
- package/src/enum/index.ts +9 -1
- package/src/index.ts +1 -4
- package/src/interfaces/Api.ts +307 -2
- package/src/interfaces/Apple.ts +74 -0
- package/src/interfaces/Attendance.ts +45 -0
- package/src/interfaces/Customer.ts +15 -11
- package/src/interfaces/Order.ts +38 -1
- package/src/interfaces/Product.ts +1 -0
- package/src/interfaces/Store.ts +41 -0
- package/src/utils/apiUtils.ts +11 -0
- package/src/utils/index.ts +6 -1
- package/src/utils/orders/calculateOrderTotal.test.js +930 -0
- package/src/utils/orders/calculateOrderTotal.ts +60 -15
- package/src/utils/orders/calculateTotalTaxesIncluded.ts +57 -25
- package/src/utils/orders/calculateTotalTaxesOverPrice.ts +57 -41
- package/src/utils/orders/helpers.ts +195 -47
- package/src/utils/orders/index.ts +3 -1
- package/src/utils/receipt/generateReceiptHTML.ts +163 -0
- package/src/utils/util.ts +65 -0
- package/tsconfig.json +13 -9
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
# 📋 Attendance Module
|
|
2
|
+
|
|
3
|
+
The Attendance module provides comprehensive employee time tracking functionality including clock-in/out, shift management, and reporting capabilities.
|
|
4
|
+
|
|
5
|
+
## 🚀 Quick Start
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { WashdayClient } from 'washday-sdk';
|
|
9
|
+
|
|
10
|
+
const client = new WashdayClient('your-api-token');
|
|
11
|
+
|
|
12
|
+
// Clock in
|
|
13
|
+
await client.attendance.clockIn({
|
|
14
|
+
storeId: 'store-id-here',
|
|
15
|
+
notes: 'Starting shift'
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Check status
|
|
19
|
+
const status = await client.attendance.getStatus();
|
|
20
|
+
console.log(status); // { active: true, clockInTime: '2025-01-01T08:00:00.000Z' }
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 📝 Available Methods
|
|
24
|
+
|
|
25
|
+
### 1. Clock In
|
|
26
|
+
Clock in to start a work shift at a specific store.
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
const clockInResult = await client.attendance.clockIn({
|
|
30
|
+
storeId: 'store-id-here', // Required: Store where employee is clocking in
|
|
31
|
+
userId: 'user-id-here', // Optional: Defaults to token user
|
|
32
|
+
ipAddress: '192.168.1.1', // Optional: Client IP address
|
|
33
|
+
notes: 'Starting shift' // Optional: Additional notes
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Response
|
|
37
|
+
console.log(clockInResult);
|
|
38
|
+
// {
|
|
39
|
+
// status: 201,
|
|
40
|
+
// data: {
|
|
41
|
+
// attendanceRecord: {
|
|
42
|
+
// _id: 'record-id',
|
|
43
|
+
// employee: 'user-id',
|
|
44
|
+
// store: 'store-id',
|
|
45
|
+
// type: 'IN',
|
|
46
|
+
// timestamp: '2025-01-01T08:00:00.000Z',
|
|
47
|
+
// notes: 'Starting shift'
|
|
48
|
+
// },
|
|
49
|
+
// message: 'Entrada registrada exitosamente'
|
|
50
|
+
// }
|
|
51
|
+
// }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 2. Clock Out
|
|
55
|
+
Clock out to end the current work shift. The system automatically uses the same store as the last clock-in.
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
const clockOutResult = await client.attendance.clockOut({
|
|
59
|
+
userId: 'user-id-here', // Optional: Defaults to token user
|
|
60
|
+
ipAddress: '192.168.1.1', // Optional: Client IP address
|
|
61
|
+
notes: 'End of shift' // Optional: Additional notes
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Response
|
|
65
|
+
console.log(clockOutResult);
|
|
66
|
+
// {
|
|
67
|
+
// status: 201,
|
|
68
|
+
// data: {
|
|
69
|
+
// clockOutEntry: { /* OUT record */ },
|
|
70
|
+
// clockInEntry: { /* matching IN record */ },
|
|
71
|
+
// shiftDuration: {
|
|
72
|
+
// hours: 8,
|
|
73
|
+
// minutes: 30,
|
|
74
|
+
// totalMinutes: 510
|
|
75
|
+
// }
|
|
76
|
+
// }
|
|
77
|
+
// }
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3. Get Attendance Status
|
|
81
|
+
Check if an employee has an active shift (clocked in without clocking out).
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
const statusResult = await client.attendance.getStatus({
|
|
85
|
+
storeId: 'store-id-here' // Optional: Check specific store, omit for all stores
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Active shift response
|
|
89
|
+
console.log(statusResult);
|
|
90
|
+
// {
|
|
91
|
+
// active: true,
|
|
92
|
+
// clockInTime: '2025-01-01T08:00:00.000Z',
|
|
93
|
+
// clockInStore: 'store-id'
|
|
94
|
+
// }
|
|
95
|
+
|
|
96
|
+
// No active shift response
|
|
97
|
+
// {
|
|
98
|
+
// active: false
|
|
99
|
+
// }
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 4. Get Attendance History
|
|
103
|
+
Retrieve attendance records with filtering and pagination.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
const historyResult = await client.attendance.getHistory({
|
|
107
|
+
storeId: 'store-id-here', // Optional: Filter by store
|
|
108
|
+
startDate: '2025-01-01', // Optional: Start date (YYYY-MM-DD)
|
|
109
|
+
endDate: '2025-01-31', // Optional: End date (YYYY-MM-DD)
|
|
110
|
+
pageNum: '1', // Optional: Page number (default: 1)
|
|
111
|
+
limit: '50' // Optional: Records per page (default: 50)
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Response
|
|
115
|
+
console.log(historyResult);
|
|
116
|
+
// {
|
|
117
|
+
// attendanceRecords: [
|
|
118
|
+
// {
|
|
119
|
+
// _id: 'record-1',
|
|
120
|
+
// employee: { name: 'John Doe', _id: 'user-id' },
|
|
121
|
+
// store: { name: 'Main Store', _id: 'store-id' },
|
|
122
|
+
// type: 'IN',
|
|
123
|
+
// timestamp: '2025-01-01T08:00:00.000Z',
|
|
124
|
+
// notes: 'Starting shift'
|
|
125
|
+
// },
|
|
126
|
+
// {
|
|
127
|
+
// _id: 'record-2',
|
|
128
|
+
// employee: { name: 'John Doe', _id: 'user-id' },
|
|
129
|
+
// store: { name: 'Main Store', _id: 'store-id' },
|
|
130
|
+
// type: 'OUT',
|
|
131
|
+
// timestamp: '2025-01-01T17:00:00.000Z',
|
|
132
|
+
// notes: 'End of shift'
|
|
133
|
+
// }
|
|
134
|
+
// ],
|
|
135
|
+
// totalRecords: 100,
|
|
136
|
+
// currentPage: 1,
|
|
137
|
+
// totalPages: 2
|
|
138
|
+
// }
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 5. Get Store Report
|
|
142
|
+
Generate a comprehensive attendance report for a specific store.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
const reportResult = await client.attendance.getStoreReport('store-id-here', {
|
|
146
|
+
startDate: '2025-01-01', // Optional: Report start date
|
|
147
|
+
endDate: '2025-01-31', // Optional: Report end date
|
|
148
|
+
pageNum: '1', // Optional: Page number
|
|
149
|
+
limit: '100' // Optional: Records per page
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Response
|
|
153
|
+
console.log(reportResult);
|
|
154
|
+
// {
|
|
155
|
+
// storeId: 'store-id',
|
|
156
|
+
// storeName: 'Main Store',
|
|
157
|
+
// employees: [
|
|
158
|
+
// {
|
|
159
|
+
// employee: {
|
|
160
|
+
// _id: 'user-id',
|
|
161
|
+
// name: 'John Doe',
|
|
162
|
+
// email: 'john@example.com'
|
|
163
|
+
// },
|
|
164
|
+
// records: [
|
|
165
|
+
// { type: 'IN', timestamp: '...', notes: '...' },
|
|
166
|
+
// { type: 'OUT', timestamp: '...', notes: '...' }
|
|
167
|
+
// ],
|
|
168
|
+
// totalHours: 160,
|
|
169
|
+
// totalShifts: 20
|
|
170
|
+
// }
|
|
171
|
+
// ],
|
|
172
|
+
// dateRange: {
|
|
173
|
+
// startDate: '2025-01-01T00:00:00.000Z',
|
|
174
|
+
// endDate: '2025-01-31T23:59:59.000Z'
|
|
175
|
+
// }
|
|
176
|
+
// }
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### 6. Update Attendance Entry
|
|
180
|
+
Update an existing attendance record (typically admin-only functionality).
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
const updateResult = await client.attendance.updateById('attendance-record-id', {
|
|
184
|
+
timestamp: '2025-01-01T09:00:00.000Z', // Optional: Correct the timestamp
|
|
185
|
+
notes: 'Corrected time', // Optional: Update notes
|
|
186
|
+
editorId: 'admin-user-id' // Optional: Who made the edit
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Response
|
|
190
|
+
console.log(updateResult);
|
|
191
|
+
// {
|
|
192
|
+
// status: 200,
|
|
193
|
+
// data: {
|
|
194
|
+
// updatedRecord: {
|
|
195
|
+
// _id: 'record-id',
|
|
196
|
+
// timestamp: '2025-01-01T09:00:00.000Z',
|
|
197
|
+
// notes: 'Corrected time',
|
|
198
|
+
// editedBy: 'admin-user-id',
|
|
199
|
+
// editedAt: '2025-01-01T10:00:00.000Z'
|
|
200
|
+
// }
|
|
201
|
+
// }
|
|
202
|
+
// }
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## 🎯 TypeScript Interfaces
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
import {
|
|
209
|
+
IAttendanceRecord,
|
|
210
|
+
IAttendanceHistory,
|
|
211
|
+
IAttendanceStatus,
|
|
212
|
+
IAttendanceReport
|
|
213
|
+
} from 'washday-sdk';
|
|
214
|
+
|
|
215
|
+
interface IAttendanceRecord {
|
|
216
|
+
_id?: string;
|
|
217
|
+
employee: IUser | string;
|
|
218
|
+
store: IStore | string;
|
|
219
|
+
type: 'IN' | 'OUT';
|
|
220
|
+
timestamp: Date;
|
|
221
|
+
ipAddress?: string;
|
|
222
|
+
notes?: string;
|
|
223
|
+
editedBy?: IUser | string;
|
|
224
|
+
editedAt?: Date;
|
|
225
|
+
autoClosed?: boolean; // Set to true if automatically closed by system
|
|
226
|
+
createdAt?: Date;
|
|
227
|
+
updatedAt?: Date;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
interface IAttendanceStatus {
|
|
231
|
+
active: boolean;
|
|
232
|
+
clockInTime?: Date; // Only present if active is true
|
|
233
|
+
clockInStore?: IStore | string; // Store where employee clocked in
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
interface IAttendanceHistory {
|
|
237
|
+
attendanceRecords: IAttendanceRecord[];
|
|
238
|
+
totalRecords?: number;
|
|
239
|
+
currentPage?: number;
|
|
240
|
+
totalPages?: number;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
interface IAttendanceReport {
|
|
244
|
+
storeId: string;
|
|
245
|
+
storeName?: string;
|
|
246
|
+
employees: {
|
|
247
|
+
employee: IUser | string;
|
|
248
|
+
records: IAttendanceRecord[];
|
|
249
|
+
totalHours?: number;
|
|
250
|
+
totalShifts?: number;
|
|
251
|
+
}[];
|
|
252
|
+
dateRange?: {
|
|
253
|
+
startDate: Date;
|
|
254
|
+
endDate: Date;
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## 📊 Error Handling
|
|
260
|
+
|
|
261
|
+
The attendance module returns standard HTTP status codes and error messages:
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
try {
|
|
265
|
+
const result = await client.attendance.clockIn({
|
|
266
|
+
storeId: 'store-id',
|
|
267
|
+
notes: 'Starting work'
|
|
268
|
+
});
|
|
269
|
+
console.log('Success:', result.data);
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.error('Error:', error.response?.data?.errors || error.message);
|
|
272
|
+
|
|
273
|
+
// Handle specific error cases
|
|
274
|
+
if (error.response?.status === 400) {
|
|
275
|
+
// Validation error examples:
|
|
276
|
+
// - Already clocked in
|
|
277
|
+
// - Invalid store ID
|
|
278
|
+
// - User not found
|
|
279
|
+
console.log('Validation error:', error.response.data.errors);
|
|
280
|
+
} else if (error.response?.status === 401) {
|
|
281
|
+
// Authentication error
|
|
282
|
+
console.log('Please check your API token');
|
|
283
|
+
} else if (error.response?.status === 403) {
|
|
284
|
+
// Permission error
|
|
285
|
+
console.log('Insufficient permissions');
|
|
286
|
+
} else if (error.response?.status === 500) {
|
|
287
|
+
// Server error
|
|
288
|
+
console.log('Internal server error');
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Common Error Scenarios
|
|
294
|
+
|
|
295
|
+
| Error | Status | Description | Solution |
|
|
296
|
+
|-------|--------|-------------|----------|
|
|
297
|
+
| Already clocked in | 400 | Employee has active shift | Clock out first |
|
|
298
|
+
| No active shift | 400 | Trying to clock out without clock-in | Clock in first |
|
|
299
|
+
| Invalid store | 400 | Store ID doesn't exist | Verify store ID |
|
|
300
|
+
| User not found | 400 | User ID doesn't exist | Verify user ID |
|
|
301
|
+
| Unauthorized | 401 | Invalid API token | Check authentication |
|
|
302
|
+
| Forbidden | 403 | Insufficient permissions | Check user permissions |
|
|
303
|
+
|
|
304
|
+
## 🔧 Advanced Features
|
|
305
|
+
|
|
306
|
+
### Auto Clock-Out Configuration
|
|
307
|
+
The attendance system supports automatic clock-out after a configurable number of hours per store:
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
// This is managed through store configuration endpoints
|
|
311
|
+
const store = await client.stores.getStoreById('store-id');
|
|
312
|
+
console.log(store.attendanceConfig);
|
|
313
|
+
// {
|
|
314
|
+
// maxShiftHours: 12, // Auto clock-out after 12 hours
|
|
315
|
+
// requireClockInOnLogin: false, // Force clock-in on POS login
|
|
316
|
+
// autoClosedFlag: true // Enable auto clock-out feature
|
|
317
|
+
// }
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Multi-Store Support
|
|
321
|
+
- Employees can work across multiple stores
|
|
322
|
+
- Clock-out automatically occurs in the same store as the last clock-in
|
|
323
|
+
- History and reports can be filtered by store or show all stores
|
|
324
|
+
- Status checks can be store-specific or global
|
|
325
|
+
|
|
326
|
+
### IP Address Tracking
|
|
327
|
+
IP addresses are automatically captured when available for:
|
|
328
|
+
- Security auditing
|
|
329
|
+
- Location verification
|
|
330
|
+
- Fraud prevention
|
|
331
|
+
|
|
332
|
+
### Shift Duration Calculation
|
|
333
|
+
The system automatically calculates shift durations:
|
|
334
|
+
- Hours and minutes worked
|
|
335
|
+
- Total minutes for easy calculations
|
|
336
|
+
- Handles overnight shifts correctly
|
|
337
|
+
|
|
338
|
+
## 📋 Best Practices
|
|
339
|
+
|
|
340
|
+
### For Client Applications
|
|
341
|
+
1. **Always check status before actions**:
|
|
342
|
+
```typescript
|
|
343
|
+
const status = await client.attendance.getStatus();
|
|
344
|
+
if (status.active) {
|
|
345
|
+
// Show clock-out button
|
|
346
|
+
} else {
|
|
347
|
+
// Show clock-in button
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
2. **Handle errors gracefully**:
|
|
352
|
+
```typescript
|
|
353
|
+
try {
|
|
354
|
+
await client.attendance.clockIn({ storeId });
|
|
355
|
+
} catch (error) {
|
|
356
|
+
if (error.response?.status === 400) {
|
|
357
|
+
// Show user-friendly message
|
|
358
|
+
alert('You are already clocked in');
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
3. **Use pagination for large datasets**:
|
|
364
|
+
```typescript
|
|
365
|
+
const history = await client.attendance.getHistory({
|
|
366
|
+
limit: '50',
|
|
367
|
+
pageNum: '1'
|
|
368
|
+
});
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### For Managers/Admins
|
|
372
|
+
1. **Regular report generation**:
|
|
373
|
+
```typescript
|
|
374
|
+
const weeklyReport = await client.attendance.getStoreReport(storeId, {
|
|
375
|
+
startDate: getWeekStart(),
|
|
376
|
+
endDate: getWeekEnd()
|
|
377
|
+
});
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
2. **Monitor auto-closed shifts**:
|
|
381
|
+
```typescript
|
|
382
|
+
const history = await client.attendance.getHistory({
|
|
383
|
+
storeId,
|
|
384
|
+
startDate: 'today'
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
const autoClosedShifts = history.attendanceRecords.filter(
|
|
388
|
+
record => record.autoClosed
|
|
389
|
+
);
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Performance Optimization
|
|
393
|
+
1. **Use store-specific queries when possible**
|
|
394
|
+
2. **Implement proper pagination for large date ranges**
|
|
395
|
+
3. **Cache status information for short periods**
|
|
396
|
+
4. **Use appropriate date ranges to limit data volume**
|
|
397
|
+
|
|
398
|
+
## 🚨 Important Notes
|
|
399
|
+
|
|
400
|
+
- **Clock-out enforcement**: The system enforces that clock-out occurs in the same store as the corresponding clock-in
|
|
401
|
+
- **Auto clock-out**: Shifts are automatically closed after the configured maximum hours
|
|
402
|
+
- **Data integrity**: All records are immutable once created (updates create edit logs)
|
|
403
|
+
- **Time zones**: All timestamps are stored in UTC and should be converted to local time for display
|
|
404
|
+
- **Permissions**: Some operations (like editing records) require administrative permissions
|
package/jest.config.js
ADDED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "washday-sdk",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Washday utilities functions and API",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"scripts": {
|
|
7
|
-
"test": "
|
|
8
|
+
"test": "jest",
|
|
8
9
|
"build": "tsc",
|
|
9
|
-
"
|
|
10
|
+
"publishVersion": "npm run build && npm publish --access public"
|
|
10
11
|
},
|
|
11
12
|
"keywords": [
|
|
12
13
|
"washday"
|
|
@@ -14,10 +15,17 @@
|
|
|
14
15
|
"author": "Washday",
|
|
15
16
|
"license": "ISC",
|
|
16
17
|
"devDependencies": {
|
|
18
|
+
"@babel/core": "^7.24.4",
|
|
19
|
+
"@babel/preset-env": "^7.24.4",
|
|
20
|
+
"@babel/preset-typescript": "^7.24.1",
|
|
21
|
+
"babel-jest": "^29.7.0",
|
|
22
|
+
"jest": "^29.7.0",
|
|
17
23
|
"typescript": "^5.4.4"
|
|
18
24
|
},
|
|
19
25
|
"dependencies": {
|
|
20
26
|
"axios": "^1.6.8",
|
|
21
|
-
"joi": "^17.12.3"
|
|
27
|
+
"joi": "^17.12.3",
|
|
28
|
+
"moment": "^2.30.1",
|
|
29
|
+
"uuid": "^11.1.0"
|
|
22
30
|
}
|
|
23
31
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// No delete endpoints implemented for attendance module
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { WashdayClientInstance } from "../../interfaces/Api";
|
|
2
|
+
import axiosInstance from "../axiosInstance";
|
|
3
|
+
|
|
4
|
+
const ATTENDANCE_API = 'api/attendance';
|
|
5
|
+
|
|
6
|
+
export const getHistory = async function (this: WashdayClientInstance, params: {
|
|
7
|
+
storeId?: string;
|
|
8
|
+
startDate?: string;
|
|
9
|
+
endDate?: string;
|
|
10
|
+
pageNum?: string;
|
|
11
|
+
limit?: string;
|
|
12
|
+
}): Promise<any> {
|
|
13
|
+
try {
|
|
14
|
+
const config = {
|
|
15
|
+
headers: { Authorization: `Bearer ${this.apiToken}` }
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
let queryParams = '';
|
|
19
|
+
if (params.storeId) queryParams += `storeId=${params.storeId}&`;
|
|
20
|
+
if (params.startDate) queryParams += `startDate=${params.startDate}&`;
|
|
21
|
+
if (params.endDate) queryParams += `endDate=${params.endDate}&`;
|
|
22
|
+
if (params.pageNum) queryParams += `pageNum=${params.pageNum}&`;
|
|
23
|
+
if (params.limit) queryParams += `limit=${params.limit}&`;
|
|
24
|
+
|
|
25
|
+
// Remove trailing '&'
|
|
26
|
+
queryParams = queryParams.slice(0, -1);
|
|
27
|
+
|
|
28
|
+
const url = queryParams ? `${ATTENDANCE_API}/history?${queryParams}` : `${ATTENDANCE_API}/history`;
|
|
29
|
+
return await axiosInstance.get(url, config);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error('Error fetching attendance history:', error);
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const getStatus = async function (this: WashdayClientInstance, params?: {
|
|
37
|
+
storeId?: string;
|
|
38
|
+
}): Promise<any> {
|
|
39
|
+
try {
|
|
40
|
+
const config = {
|
|
41
|
+
headers: { Authorization: `Bearer ${this.apiToken}` }
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
let queryParams = '';
|
|
45
|
+
if (params?.storeId) queryParams += `storeId=${params.storeId}`;
|
|
46
|
+
|
|
47
|
+
const url = queryParams ? `${ATTENDANCE_API}/status?${queryParams}` : `${ATTENDANCE_API}/status`;
|
|
48
|
+
return await axiosInstance.get(url, config);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('Error fetching attendance status:', error);
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const getStoreReport = async function (this: WashdayClientInstance, storeId: string, params?: {
|
|
56
|
+
startDate?: string;
|
|
57
|
+
endDate?: string;
|
|
58
|
+
pageNum?: string;
|
|
59
|
+
limit?: string;
|
|
60
|
+
}): Promise<any> {
|
|
61
|
+
try {
|
|
62
|
+
const config = {
|
|
63
|
+
headers: { Authorization: `Bearer ${this.apiToken}` }
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
let queryParams = '';
|
|
67
|
+
if (params?.startDate) queryParams += `startDate=${params.startDate}&`;
|
|
68
|
+
if (params?.endDate) queryParams += `endDate=${params.endDate}&`;
|
|
69
|
+
if (params?.pageNum) queryParams += `pageNum=${params.pageNum}&`;
|
|
70
|
+
if (params?.limit) queryParams += `limit=${params.limit}&`;
|
|
71
|
+
|
|
72
|
+
// Remove trailing '&'
|
|
73
|
+
queryParams = queryParams.slice(0, -1);
|
|
74
|
+
|
|
75
|
+
const url = queryParams ? `${ATTENDANCE_API}/store/${storeId}/report?${queryParams}` : `${ATTENDANCE_API}/store/${storeId}/report`;
|
|
76
|
+
return await axiosInstance.get(url, config);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error('Error fetching store attendance report:', error);
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { WashdayClientInstance } from "../../interfaces/Api";
|
|
2
|
+
import axiosInstance from "../axiosInstance";
|
|
3
|
+
|
|
4
|
+
const ATTENDANCE_API = 'api/attendance';
|
|
5
|
+
|
|
6
|
+
export const clockIn = async function (this: WashdayClientInstance, data: {
|
|
7
|
+
storeId: string;
|
|
8
|
+
userId?: string;
|
|
9
|
+
ipAddress?: string;
|
|
10
|
+
notes?: string;
|
|
11
|
+
}): Promise<any> {
|
|
12
|
+
try {
|
|
13
|
+
const config = {
|
|
14
|
+
headers: { Authorization: `Bearer ${this.apiToken}` }
|
|
15
|
+
};
|
|
16
|
+
return await axiosInstance.post(`${ATTENDANCE_API}/clock-in`, data, config);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error('Error clock-in:', error);
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const clockOut = async function (this: WashdayClientInstance, data: {
|
|
24
|
+
userId?: string;
|
|
25
|
+
ipAddress?: string;
|
|
26
|
+
notes?: string;
|
|
27
|
+
}): Promise<any> {
|
|
28
|
+
try {
|
|
29
|
+
const config = {
|
|
30
|
+
headers: { Authorization: `Bearer ${this.apiToken}` }
|
|
31
|
+
};
|
|
32
|
+
return await axiosInstance.post(`${ATTENDANCE_API}/clock-out`, data, config);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('Error clock-out:', error);
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { WashdayClientInstance } from "../../interfaces/Api";
|
|
2
|
+
import axiosInstance from "../axiosInstance";
|
|
3
|
+
|
|
4
|
+
const ATTENDANCE_API = 'api/attendance';
|
|
5
|
+
|
|
6
|
+
export const updateById = async function (this: WashdayClientInstance, id: string, data: {
|
|
7
|
+
timestamp?: string;
|
|
8
|
+
notes?: string;
|
|
9
|
+
editorId?: string;
|
|
10
|
+
}): Promise<any> {
|
|
11
|
+
try {
|
|
12
|
+
const config = {
|
|
13
|
+
headers: { Authorization: `Bearer ${this.apiToken}` }
|
|
14
|
+
};
|
|
15
|
+
return await axiosInstance.put(`${ATTENDANCE_API}/${id}`, data, config);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
console.error('Error updating attendance entry:', error);
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
};
|