washday-sdk 1.6.62 → 1.6.64
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/dist/api/attendance/post.js +44 -0
- package/dist/api/attendance/put.js +2 -1
- package/dist/api/companies/get.js +20 -0
- package/dist/api/companies/post.js +30 -0
- package/dist/api/index.js +5 -2
- package/package.json +1 -1
- package/src/api/attendance/post.ts +56 -0
- package/src/api/attendance/put.ts +12 -5
- package/src/api/companies/get.ts +28 -1
- package/src/api/companies/post.ts +37 -1
- package/src/api/index.ts +5 -2
- package/src/interfaces/Api.ts +5 -2
- package/src/interfaces/Attendance.ts +62 -0
- package/src/interfaces/Company.ts +91 -0
- package/src/interfaces/Permission.ts +1 -0
- package/test/attendance.metadata.test.ts +30 -0
- package/test/attendance.update.test.ts +30 -0
- package/test/companies.subscriptionPlanChange.test.ts +92 -0
|
@@ -9,9 +9,52 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
const ATTENDANCE_API = 'api/attendance';
|
|
11
11
|
const ATTENDANCE_SOURCES = ["web_pos", "pos_v2", "unknown"];
|
|
12
|
+
const LOCATION_PERMISSION_STATUSES = [
|
|
13
|
+
"granted",
|
|
14
|
+
"denied",
|
|
15
|
+
"unavailable",
|
|
16
|
+
"not_requested",
|
|
17
|
+
"error",
|
|
18
|
+
];
|
|
12
19
|
const stringValue = (value) => (typeof value === "string" && value.trim() ? value.trim() : undefined);
|
|
13
20
|
const numberValue = (value) => (typeof value === "number" && Number.isFinite(value) ? value : undefined);
|
|
14
21
|
const sourceValue = (value) => (ATTENDANCE_SOURCES.includes(value) ? value : undefined);
|
|
22
|
+
const permissionStatusValue = (value) => (LOCATION_PERMISSION_STATUSES.includes(value)
|
|
23
|
+
? value
|
|
24
|
+
: undefined);
|
|
25
|
+
const dateValue = (value) => {
|
|
26
|
+
if (value instanceof Date && !Number.isNaN(value.getTime()))
|
|
27
|
+
return value;
|
|
28
|
+
if (typeof value !== "string")
|
|
29
|
+
return undefined;
|
|
30
|
+
return Number.isNaN(new Date(value).getTime()) ? undefined : value;
|
|
31
|
+
};
|
|
32
|
+
const buildSafeAttendanceLocation = (location) => {
|
|
33
|
+
if (!location)
|
|
34
|
+
return undefined;
|
|
35
|
+
const permissionStatus = permissionStatusValue(location.permissionStatus);
|
|
36
|
+
if (!permissionStatus)
|
|
37
|
+
return undefined;
|
|
38
|
+
const safeLocation = {
|
|
39
|
+
permissionStatus,
|
|
40
|
+
capturedAt: dateValue(location.capturedAt),
|
|
41
|
+
errorCode: stringValue(location.errorCode),
|
|
42
|
+
errorMessage: stringValue(location.errorMessage),
|
|
43
|
+
};
|
|
44
|
+
if (permissionStatus === "granted") {
|
|
45
|
+
safeLocation.latitude = numberValue(location.latitude);
|
|
46
|
+
safeLocation.longitude = numberValue(location.longitude);
|
|
47
|
+
safeLocation.accuracy = numberValue(location.accuracy);
|
|
48
|
+
safeLocation.altitude = numberValue(location.altitude);
|
|
49
|
+
safeLocation.altitudeAccuracy = numberValue(location.altitudeAccuracy);
|
|
50
|
+
safeLocation.heading = numberValue(location.heading);
|
|
51
|
+
safeLocation.speed = numberValue(location.speed);
|
|
52
|
+
}
|
|
53
|
+
const filteredEntries = Object.entries(safeLocation).filter(([, value]) => value !== undefined);
|
|
54
|
+
return filteredEntries.length
|
|
55
|
+
? Object.fromEntries(filteredEntries)
|
|
56
|
+
: undefined;
|
|
57
|
+
};
|
|
15
58
|
const buildSafeAttendanceMetadata = (metadata) => {
|
|
16
59
|
if (!metadata)
|
|
17
60
|
return undefined;
|
|
@@ -24,6 +67,7 @@ const buildSafeAttendanceMetadata = (metadata) => {
|
|
|
24
67
|
timezone: stringValue(metadata.timezone),
|
|
25
68
|
screenWidth: numberValue(metadata.screenWidth),
|
|
26
69
|
screenHeight: numberValue(metadata.screenHeight),
|
|
70
|
+
location: buildSafeAttendanceLocation(metadata.location),
|
|
27
71
|
};
|
|
28
72
|
const filteredEntries = Object.entries(safeMetadata).filter(([, value]) => value !== undefined);
|
|
29
73
|
return filteredEntries.length
|
|
@@ -14,7 +14,8 @@ export const updateById = function (id, data) {
|
|
|
14
14
|
const config = {
|
|
15
15
|
headers: { Authorization: `Bearer ${this.apiToken}` }
|
|
16
16
|
};
|
|
17
|
-
|
|
17
|
+
const { timestamp, notes, reason } = data;
|
|
18
|
+
return yield this.axiosInstance.put(`${ATTENDANCE_API}/${id}`, Object.assign(Object.assign(Object.assign({}, (timestamp !== undefined ? { timestamp } : {})), (notes !== undefined ? { notes } : {})), { reason }), config);
|
|
18
19
|
}
|
|
19
20
|
catch (error) {
|
|
20
21
|
console.error('Error updating attendance entry:', error);
|
|
@@ -69,3 +69,23 @@ export const getCFDIAddonStatus = function (data) {
|
|
|
69
69
|
}
|
|
70
70
|
});
|
|
71
71
|
};
|
|
72
|
+
export const getAvailableSubscriptionPlans = function () {
|
|
73
|
+
return __awaiter(this, arguments, void 0, function* (data = {}) {
|
|
74
|
+
try {
|
|
75
|
+
const config = {
|
|
76
|
+
headers: { Authorization: `Bearer ${this.apiToken}` }
|
|
77
|
+
};
|
|
78
|
+
const params = new URLSearchParams();
|
|
79
|
+
if (data.companyId) {
|
|
80
|
+
params.set("companyId", data.companyId);
|
|
81
|
+
}
|
|
82
|
+
const query = params.toString();
|
|
83
|
+
const response = yield this.axiosInstance.get(`${GET_SET_BILLING}/subscription/available-plans${query ? `?${query}` : ""}`, config);
|
|
84
|
+
return response;
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
console.error('Error fetching getAvailableSubscriptionPlans:', error);
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
};
|
|
@@ -99,3 +99,33 @@ export const disableCFDIAddon = function (data) {
|
|
|
99
99
|
}
|
|
100
100
|
});
|
|
101
101
|
};
|
|
102
|
+
export const previewSubscriptionPlanChange = function (data) {
|
|
103
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
104
|
+
try {
|
|
105
|
+
const config = {
|
|
106
|
+
headers: { Authorization: `Bearer ${this.apiToken}` }
|
|
107
|
+
};
|
|
108
|
+
const response = yield this.axiosInstance.post(`${GET_SET_BILLING}/subscription/change-plan/preview`, data, config);
|
|
109
|
+
return response;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.error('Error fetching previewSubscriptionPlanChange:', error);
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
export const changeSubscriptionPlan = function (data) {
|
|
118
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
119
|
+
try {
|
|
120
|
+
const config = {
|
|
121
|
+
headers: { Authorization: `Bearer ${this.apiToken}` }
|
|
122
|
+
};
|
|
123
|
+
const response = yield this.axiosInstance.post(`${GET_SET_BILLING}/subscription/change-plan`, data, config);
|
|
124
|
+
return response;
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
console.error('Error fetching changeSubscriptionPlan:', error);
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
};
|
package/dist/api/index.js
CHANGED
|
@@ -2,8 +2,8 @@ import { deleteCashierBoxById, deleteCashierBoxMovementById } from "./cashierbox
|
|
|
2
2
|
import { getCashierBoxMovementsHistory, getCashierboxesById, getCashierboxesByStoreId } from "./cashierbox/get";
|
|
3
3
|
import { addCashierBoxMovement, createCashierBox } from "./cashierbox/post";
|
|
4
4
|
import { updateCashierBoxById, updateCashierBoxMovementById } from "./cashierbox/put";
|
|
5
|
-
import { getCFDIAddonStatus, getCompanyById, getCompanyOrdersMetrics, getGarmentAttributeCatalogs } from "./companies/get";
|
|
6
|
-
import { disableBillingOverage, disableCFDIAddon, enableBillingOverage, enableCFDIAddon, updateCFDIOrgLogo, updateCompanyTaxInfoCertificates } from "./companies/post";
|
|
5
|
+
import { getAvailableSubscriptionPlans, getCFDIAddonStatus, getCompanyById, getCompanyOrdersMetrics, getGarmentAttributeCatalogs } from "./companies/get";
|
|
6
|
+
import { changeSubscriptionPlan, disableBillingOverage, disableCFDIAddon, enableBillingOverage, enableCFDIAddon, previewSubscriptionPlanChange, updateCFDIOrgLogo, updateCompanyTaxInfoCertificates } from "./companies/post";
|
|
7
7
|
import { updateCompanyById, updateCompanyLogoById, updateCompanySubscriptionBillingInfoById, updateCompanyTaxInfoById, updateGarmentAttributeCatalogsById } from "./companies/put";
|
|
8
8
|
import { getCountries } from "./countries/get";
|
|
9
9
|
import * as configEndpoints from "./config";
|
|
@@ -288,6 +288,9 @@ const WashdayClient = function WashdayClient(apiToken, env = 'PROD', clientId, c
|
|
|
288
288
|
enableCFDIAddon: enableCFDIAddon,
|
|
289
289
|
disableCFDIAddon: disableCFDIAddon,
|
|
290
290
|
getCFDIAddonStatus: getCFDIAddonStatus,
|
|
291
|
+
getAvailableSubscriptionPlans: getAvailableSubscriptionPlans,
|
|
292
|
+
previewSubscriptionPlanChange: previewSubscriptionPlanChange,
|
|
293
|
+
changeSubscriptionPlan: changeSubscriptionPlan,
|
|
291
294
|
});
|
|
292
295
|
this.stripe = bindMethods(this, {
|
|
293
296
|
createCreateSuscriptionCheckoutSession: createCreateSuscriptionCheckoutSession,
|
package/package.json
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { WashdayClientInstance } from "../../interfaces/Api";
|
|
2
2
|
import type {
|
|
3
3
|
AttendanceClientMetadata,
|
|
4
|
+
AttendanceLocationMetadata,
|
|
5
|
+
AttendanceLocationPermissionStatus,
|
|
4
6
|
AttendanceSource,
|
|
5
7
|
ClockInRequest,
|
|
6
8
|
ClockOutRequest,
|
|
@@ -8,6 +10,13 @@ import type {
|
|
|
8
10
|
|
|
9
11
|
const ATTENDANCE_API = 'api/attendance';
|
|
10
12
|
const ATTENDANCE_SOURCES: AttendanceSource[] = ["web_pos", "pos_v2", "unknown"];
|
|
13
|
+
const LOCATION_PERMISSION_STATUSES: AttendanceLocationPermissionStatus[] = [
|
|
14
|
+
"granted",
|
|
15
|
+
"denied",
|
|
16
|
+
"unavailable",
|
|
17
|
+
"not_requested",
|
|
18
|
+
"error",
|
|
19
|
+
];
|
|
11
20
|
|
|
12
21
|
const stringValue = (value: unknown): string | undefined => (
|
|
13
22
|
typeof value === "string" && value.trim() ? value.trim() : undefined
|
|
@@ -21,6 +30,52 @@ const sourceValue = (value: unknown): AttendanceSource | undefined => (
|
|
|
21
30
|
ATTENDANCE_SOURCES.includes(value as AttendanceSource) ? value as AttendanceSource : undefined
|
|
22
31
|
);
|
|
23
32
|
|
|
33
|
+
const permissionStatusValue = (value: unknown): AttendanceLocationPermissionStatus | undefined => (
|
|
34
|
+
LOCATION_PERMISSION_STATUSES.includes(value as AttendanceLocationPermissionStatus)
|
|
35
|
+
? value as AttendanceLocationPermissionStatus
|
|
36
|
+
: undefined
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const dateValue = (value: unknown): string | Date | undefined => {
|
|
40
|
+
if (value instanceof Date && !Number.isNaN(value.getTime())) return value;
|
|
41
|
+
if (typeof value !== "string") return undefined;
|
|
42
|
+
return Number.isNaN(new Date(value).getTime()) ? undefined : value;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const buildSafeAttendanceLocation = (
|
|
46
|
+
location?: AttendanceLocationMetadata & Record<string, unknown>
|
|
47
|
+
): AttendanceLocationMetadata | undefined => {
|
|
48
|
+
if (!location) return undefined;
|
|
49
|
+
|
|
50
|
+
const permissionStatus = permissionStatusValue(location.permissionStatus);
|
|
51
|
+
if (!permissionStatus) return undefined;
|
|
52
|
+
|
|
53
|
+
const safeLocation: AttendanceLocationMetadata = {
|
|
54
|
+
permissionStatus,
|
|
55
|
+
capturedAt: dateValue(location.capturedAt),
|
|
56
|
+
errorCode: stringValue(location.errorCode),
|
|
57
|
+
errorMessage: stringValue(location.errorMessage),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (permissionStatus === "granted") {
|
|
61
|
+
safeLocation.latitude = numberValue(location.latitude);
|
|
62
|
+
safeLocation.longitude = numberValue(location.longitude);
|
|
63
|
+
safeLocation.accuracy = numberValue(location.accuracy);
|
|
64
|
+
safeLocation.altitude = numberValue(location.altitude);
|
|
65
|
+
safeLocation.altitudeAccuracy = numberValue(location.altitudeAccuracy);
|
|
66
|
+
safeLocation.heading = numberValue(location.heading);
|
|
67
|
+
safeLocation.speed = numberValue(location.speed);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const filteredEntries = Object.entries(safeLocation).filter(
|
|
71
|
+
([, value]) => value !== undefined
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return filteredEntries.length
|
|
75
|
+
? Object.fromEntries(filteredEntries) as AttendanceLocationMetadata
|
|
76
|
+
: undefined;
|
|
77
|
+
};
|
|
78
|
+
|
|
24
79
|
const buildSafeAttendanceMetadata = (
|
|
25
80
|
metadata?: AttendanceClientMetadata & Record<string, unknown>
|
|
26
81
|
): AttendanceClientMetadata | undefined => {
|
|
@@ -35,6 +90,7 @@ const buildSafeAttendanceMetadata = (
|
|
|
35
90
|
timezone: stringValue(metadata.timezone),
|
|
36
91
|
screenWidth: numberValue(metadata.screenWidth),
|
|
37
92
|
screenHeight: numberValue(metadata.screenHeight),
|
|
93
|
+
location: buildSafeAttendanceLocation(metadata.location as any),
|
|
38
94
|
};
|
|
39
95
|
|
|
40
96
|
const filteredEntries = Object.entries(safeMetadata).filter(
|
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import { WashdayClientInstance } from "../../interfaces/Api";
|
|
2
|
-
import axiosInstance from "../axiosInstance";
|
|
3
2
|
|
|
4
3
|
const ATTENDANCE_API = 'api/attendance';
|
|
5
4
|
|
|
6
|
-
export
|
|
5
|
+
export interface UpdateAttendanceEntryRequest {
|
|
7
6
|
timestamp?: string;
|
|
8
7
|
notes?: string;
|
|
8
|
+
reason: string;
|
|
9
9
|
editorId?: string;
|
|
10
|
-
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const updateById = async function (this: WashdayClientInstance, id: string, data: UpdateAttendanceEntryRequest): Promise<any> {
|
|
11
13
|
try {
|
|
12
14
|
const config = {
|
|
13
15
|
headers: { Authorization: `Bearer ${this.apiToken}` }
|
|
14
16
|
};
|
|
15
|
-
|
|
17
|
+
const { timestamp, notes, reason } = data;
|
|
18
|
+
return await this.axiosInstance.put(`${ATTENDANCE_API}/${id}`, {
|
|
19
|
+
...(timestamp !== undefined ? { timestamp } : {}),
|
|
20
|
+
...(notes !== undefined ? { notes } : {}),
|
|
21
|
+
reason,
|
|
22
|
+
}, config);
|
|
16
23
|
} catch (error) {
|
|
17
24
|
console.error('Error updating attendance entry:', error);
|
|
18
25
|
throw error;
|
|
19
26
|
}
|
|
20
|
-
};
|
|
27
|
+
};
|
package/src/api/companies/get.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { WashdayClientInstance } from "../../interfaces/Api";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
GetAvailableSubscriptionPlansRequest,
|
|
4
|
+
GetCFDIAddonStatusRequest,
|
|
5
|
+
} from "../../interfaces/Company";
|
|
3
6
|
|
|
4
7
|
const GET_SET_COMPANIES = 'api/company';
|
|
5
8
|
const GET_SET_BILLING = 'api/billing';
|
|
@@ -58,3 +61,27 @@ export const getCFDIAddonStatus = async function (
|
|
|
58
61
|
throw error;
|
|
59
62
|
}
|
|
60
63
|
};
|
|
64
|
+
|
|
65
|
+
export const getAvailableSubscriptionPlans = async function (
|
|
66
|
+
this: WashdayClientInstance,
|
|
67
|
+
data: GetAvailableSubscriptionPlansRequest = {},
|
|
68
|
+
): Promise<any> {
|
|
69
|
+
try {
|
|
70
|
+
const config = {
|
|
71
|
+
headers: { Authorization: `Bearer ${this.apiToken}` }
|
|
72
|
+
};
|
|
73
|
+
const params = new URLSearchParams();
|
|
74
|
+
if (data.companyId) {
|
|
75
|
+
params.set("companyId", data.companyId);
|
|
76
|
+
}
|
|
77
|
+
const query = params.toString();
|
|
78
|
+
const response = await this.axiosInstance.get(
|
|
79
|
+
`${GET_SET_BILLING}/subscription/available-plans${query ? `?${query}` : ""}`,
|
|
80
|
+
config,
|
|
81
|
+
);
|
|
82
|
+
return response;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.error('Error fetching getAvailableSubscriptionPlans:', error);
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { WashdayClientInstance } from "../../interfaces/Api";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ApplySubscriptionPlanChangeRequest,
|
|
4
|
+
CFDIAddonToggleRequest,
|
|
5
|
+
PreviewSubscriptionPlanChangeRequest,
|
|
6
|
+
} from "../../interfaces/Company";
|
|
3
7
|
import axiosInstance from "../axiosInstance";
|
|
4
8
|
const GET_SET_COMPANIES = 'api/company';
|
|
5
9
|
const GET_SET_BILLING = 'api/billing';
|
|
@@ -91,3 +95,35 @@ export const disableCFDIAddon = async function (
|
|
|
91
95
|
throw error;
|
|
92
96
|
}
|
|
93
97
|
}
|
|
98
|
+
|
|
99
|
+
export const previewSubscriptionPlanChange = async function (
|
|
100
|
+
this: WashdayClientInstance,
|
|
101
|
+
data: PreviewSubscriptionPlanChangeRequest
|
|
102
|
+
): Promise<any> {
|
|
103
|
+
try {
|
|
104
|
+
const config = {
|
|
105
|
+
headers: { Authorization: `Bearer ${this.apiToken}` }
|
|
106
|
+
};
|
|
107
|
+
const response = await this.axiosInstance.post(`${GET_SET_BILLING}/subscription/change-plan/preview`, data, config);
|
|
108
|
+
return response;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error('Error fetching previewSubscriptionPlanChange:', error);
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const changeSubscriptionPlan = async function (
|
|
116
|
+
this: WashdayClientInstance,
|
|
117
|
+
data: ApplySubscriptionPlanChangeRequest
|
|
118
|
+
): Promise<any> {
|
|
119
|
+
try {
|
|
120
|
+
const config = {
|
|
121
|
+
headers: { Authorization: `Bearer ${this.apiToken}` }
|
|
122
|
+
};
|
|
123
|
+
const response = await this.axiosInstance.post(`${GET_SET_BILLING}/subscription/change-plan`, data, config);
|
|
124
|
+
return response;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error('Error fetching changeSubscriptionPlan:', error);
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
}
|
package/src/api/index.ts
CHANGED
|
@@ -3,8 +3,8 @@ import { deleteCashierBoxById, deleteCashierBoxMovementById } from "./cashierbox
|
|
|
3
3
|
import { getCashierBoxMovementsHistory, getCashierboxesById, getCashierboxesByStoreId } from "./cashierbox/get";
|
|
4
4
|
import { addCashierBoxMovement, createCashierBox } from "./cashierbox/post";
|
|
5
5
|
import { updateCashierBoxById, updateCashierBoxMovementById } from "./cashierbox/put";
|
|
6
|
-
import { getCFDIAddonStatus, getCompanyById, getCompanyOrdersMetrics, getGarmentAttributeCatalogs } from "./companies/get";
|
|
7
|
-
import { disableBillingOverage, disableCFDIAddon, enableBillingOverage, enableCFDIAddon, updateCFDIOrgLogo, updateCompanyTaxInfoCertificates } from "./companies/post";
|
|
6
|
+
import { getAvailableSubscriptionPlans, getCFDIAddonStatus, getCompanyById, getCompanyOrdersMetrics, getGarmentAttributeCatalogs } from "./companies/get";
|
|
7
|
+
import { changeSubscriptionPlan, disableBillingOverage, disableCFDIAddon, enableBillingOverage, enableCFDIAddon, previewSubscriptionPlanChange, updateCFDIOrgLogo, updateCompanyTaxInfoCertificates } from "./companies/post";
|
|
8
8
|
import { updateCompanyById, updateCompanyLogoById, updateCompanySubscriptionBillingInfoById, updateCompanyTaxInfoById, updateGarmentAttributeCatalogsById } from "./companies/put";
|
|
9
9
|
import { getCountries } from "./countries/get";
|
|
10
10
|
import * as configEndpoints from "./config";
|
|
@@ -295,6 +295,9 @@ const WashdayClient: WashdayClientConstructor = function WashdayClient(this: Was
|
|
|
295
295
|
enableCFDIAddon: enableCFDIAddon,
|
|
296
296
|
disableCFDIAddon: disableCFDIAddon,
|
|
297
297
|
getCFDIAddonStatus: getCFDIAddonStatus,
|
|
298
|
+
getAvailableSubscriptionPlans: getAvailableSubscriptionPlans,
|
|
299
|
+
previewSubscriptionPlanChange: previewSubscriptionPlanChange,
|
|
300
|
+
changeSubscriptionPlan: changeSubscriptionPlan,
|
|
298
301
|
});
|
|
299
302
|
this.stripe = bindMethods(this, {
|
|
300
303
|
createCreateSuscriptionCheckoutSession: createCreateSuscriptionCheckoutSession,
|
package/src/interfaces/Api.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { deleteCashierBoxById, deleteCashierBoxMovementById } from "../api/cashi
|
|
|
2
2
|
import { getCashierBoxMovementsHistory, getCashierboxesById, getCashierboxesByStoreId } from "../api/cashierbox/get";
|
|
3
3
|
import { addCashierBoxMovement, createCashierBox } from "../api/cashierbox/post";
|
|
4
4
|
import { updateCashierBoxById, updateCashierBoxMovementById } from "../api/cashierbox/put";
|
|
5
|
-
import { getCFDIAddonStatus, getCompanyById, getCompanyOrdersMetrics, getGarmentAttributeCatalogs } from "../api/companies/get";
|
|
6
|
-
import { disableBillingOverage, disableCFDIAddon, enableBillingOverage, enableCFDIAddon, updateCFDIOrgLogo, updateCompanyTaxInfoCertificates } from "../api/companies/post";
|
|
5
|
+
import { getAvailableSubscriptionPlans, getCFDIAddonStatus, getCompanyById, getCompanyOrdersMetrics, getGarmentAttributeCatalogs } from "../api/companies/get";
|
|
6
|
+
import { changeSubscriptionPlan, disableBillingOverage, disableCFDIAddon, enableBillingOverage, enableCFDIAddon, previewSubscriptionPlanChange, updateCFDIOrgLogo, updateCompanyTaxInfoCertificates } from "../api/companies/post";
|
|
7
7
|
import { updateCompanyById, updateCompanyLogoById, updateCompanySubscriptionBillingInfoById, updateCompanyTaxInfoById, updateGarmentAttributeCatalogsById } from "../api/companies/put";
|
|
8
8
|
import { getCountries } from "../api/countries/get";
|
|
9
9
|
import * as configEndpoints from "../api/config";
|
|
@@ -280,6 +280,9 @@ export interface WashdayClientInstance {
|
|
|
280
280
|
enableCFDIAddon: typeof enableCFDIAddon;
|
|
281
281
|
disableCFDIAddon: typeof disableCFDIAddon;
|
|
282
282
|
getCFDIAddonStatus: typeof getCFDIAddonStatus;
|
|
283
|
+
getAvailableSubscriptionPlans: typeof getAvailableSubscriptionPlans;
|
|
284
|
+
previewSubscriptionPlanChange: typeof previewSubscriptionPlanChange;
|
|
285
|
+
changeSubscriptionPlan: typeof changeSubscriptionPlan;
|
|
283
286
|
};
|
|
284
287
|
stripe: {
|
|
285
288
|
createCreateSuscriptionCheckoutSession: typeof createCreateSuscriptionCheckoutSession;
|
|
@@ -3,6 +3,27 @@ import { IStore } from "./Store";
|
|
|
3
3
|
|
|
4
4
|
export type AttendanceSource = "web_pos" | "pos_v2" | "unknown";
|
|
5
5
|
|
|
6
|
+
export type AttendanceLocationPermissionStatus =
|
|
7
|
+
| "granted"
|
|
8
|
+
| "denied"
|
|
9
|
+
| "unavailable"
|
|
10
|
+
| "not_requested"
|
|
11
|
+
| "error";
|
|
12
|
+
|
|
13
|
+
export interface AttendanceLocationMetadata {
|
|
14
|
+
latitude?: number;
|
|
15
|
+
longitude?: number;
|
|
16
|
+
accuracy?: number;
|
|
17
|
+
altitude?: number;
|
|
18
|
+
altitudeAccuracy?: number;
|
|
19
|
+
heading?: number;
|
|
20
|
+
speed?: number;
|
|
21
|
+
permissionStatus?: AttendanceLocationPermissionStatus;
|
|
22
|
+
capturedAt?: string | Date;
|
|
23
|
+
errorCode?: string;
|
|
24
|
+
errorMessage?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
6
27
|
export interface AttendanceClientMetadata {
|
|
7
28
|
platform?: string;
|
|
8
29
|
appVersion?: string;
|
|
@@ -12,6 +33,7 @@ export interface AttendanceClientMetadata {
|
|
|
12
33
|
timezone?: string;
|
|
13
34
|
screenWidth?: number;
|
|
14
35
|
screenHeight?: number;
|
|
36
|
+
location?: AttendanceLocationMetadata;
|
|
15
37
|
}
|
|
16
38
|
|
|
17
39
|
export interface AttendanceServerMetadata extends AttendanceClientMetadata {
|
|
@@ -46,6 +68,8 @@ export interface IAttendanceRecord {
|
|
|
46
68
|
notes?: string;
|
|
47
69
|
editedBy?: IUser | string;
|
|
48
70
|
editedAt?: Date;
|
|
71
|
+
lastEditReason?: string;
|
|
72
|
+
revision?: number;
|
|
49
73
|
autoClosed?: boolean;
|
|
50
74
|
createdAt?: Date;
|
|
51
75
|
updatedAt?: Date;
|
|
@@ -64,6 +88,36 @@ export interface IAttendanceStatus {
|
|
|
64
88
|
clockInStore?: IStore | string;
|
|
65
89
|
}
|
|
66
90
|
|
|
91
|
+
export interface AttendanceShiftDuration {
|
|
92
|
+
hours: number;
|
|
93
|
+
minutes: number;
|
|
94
|
+
totalMinutes: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface AttendanceReportTableInfoRow {
|
|
98
|
+
_id?: string;
|
|
99
|
+
employee: IUser | string | {
|
|
100
|
+
_id?: string;
|
|
101
|
+
name?: string;
|
|
102
|
+
email?: string;
|
|
103
|
+
};
|
|
104
|
+
clockInTime?: Date | string;
|
|
105
|
+
clockOutTime?: Date | string | null;
|
|
106
|
+
shiftDuration?: AttendanceShiftDuration | null;
|
|
107
|
+
clockInStore?: string;
|
|
108
|
+
clockOutStore?: string | null;
|
|
109
|
+
notes?: string;
|
|
110
|
+
clockInIpAddress?: string;
|
|
111
|
+
clockOutIpAddress?: string;
|
|
112
|
+
clockInMetadata?: AttendanceServerMetadata | null;
|
|
113
|
+
clockOutMetadata?: AttendanceServerMetadata | null;
|
|
114
|
+
clockInLocation?: AttendanceLocationMetadata | null;
|
|
115
|
+
clockOutLocation?: AttendanceLocationMetadata | null;
|
|
116
|
+
clockInLocationSummary?: string;
|
|
117
|
+
clockOutLocationSummary?: string;
|
|
118
|
+
autoClosed?: boolean;
|
|
119
|
+
}
|
|
120
|
+
|
|
67
121
|
export interface IAttendanceReport {
|
|
68
122
|
storeId: string;
|
|
69
123
|
storeName?: string;
|
|
@@ -77,4 +131,12 @@ export interface IAttendanceReport {
|
|
|
77
131
|
startDate: Date;
|
|
78
132
|
endDate: Date;
|
|
79
133
|
};
|
|
134
|
+
tableInfo?: AttendanceReportTableInfoRow[];
|
|
135
|
+
totalRows?: number;
|
|
136
|
+
pagination?: {
|
|
137
|
+
currentPage: number;
|
|
138
|
+
totalPages: number;
|
|
139
|
+
totalRecords: number;
|
|
140
|
+
limit?: number;
|
|
141
|
+
};
|
|
80
142
|
}
|
|
@@ -54,7 +54,98 @@ export interface GetCFDIAddonStatusRequest {
|
|
|
54
54
|
companyId: string;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
export type SubscriptionPlanChangePlan = "basic" | "standard" | "premium";
|
|
58
|
+
export type SubscriptionPlanChangeDirection = "upgrade" | "downgrade" | "same_plan";
|
|
59
|
+
export type SubscriptionPlanChangeBehavior =
|
|
60
|
+
| "immediate_prorated_charge"
|
|
61
|
+
| "contact_support"
|
|
62
|
+
| "no_change";
|
|
63
|
+
export type SubscriptionPlanChangeAddon = "cfdi" | "orders_overage";
|
|
64
|
+
|
|
65
|
+
export interface GetAvailableSubscriptionPlansRequest {
|
|
66
|
+
companyId?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface AvailableSubscriptionPlan {
|
|
70
|
+
plan: SubscriptionPlanChangePlan;
|
|
71
|
+
priceId: string;
|
|
72
|
+
amount: number;
|
|
73
|
+
currency: string;
|
|
74
|
+
interval: string | null;
|
|
75
|
+
displayPrice: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface AvailableSubscriptionPlansResponse {
|
|
79
|
+
currentPlan: SubscriptionPlanChangePlan;
|
|
80
|
+
currentPlanPriceId: string;
|
|
81
|
+
country: string;
|
|
82
|
+
currency: string;
|
|
83
|
+
plans: AvailableSubscriptionPlan[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface PreviewSubscriptionPlanChangeRequest {
|
|
87
|
+
companyId?: string;
|
|
88
|
+
targetPlan: SubscriptionPlanChangePlan;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface PlanChangePreviewLine {
|
|
92
|
+
description: string;
|
|
93
|
+
amount: number;
|
|
94
|
+
currency: string;
|
|
95
|
+
amountFormatted?: string;
|
|
96
|
+
proration?: boolean;
|
|
97
|
+
priceId?: string;
|
|
98
|
+
type?: "credit" | "charge" | "tax" | "addon" | "plan";
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface PlanChangePreviewResponse {
|
|
102
|
+
status: "preview_ready" | "downgrade_not_supported" | "no_change";
|
|
103
|
+
currentPlan: SubscriptionPlanChangePlan;
|
|
104
|
+
targetPlan: SubscriptionPlanChangePlan;
|
|
105
|
+
direction: SubscriptionPlanChangeDirection;
|
|
106
|
+
behavior: SubscriptionPlanChangeBehavior;
|
|
107
|
+
prorationDate?: number;
|
|
108
|
+
currency: string;
|
|
109
|
+
amountDueNow: number;
|
|
110
|
+
amountDueNowFormatted?: string;
|
|
111
|
+
subtotal?: number;
|
|
112
|
+
tax?: number | null;
|
|
113
|
+
total?: number;
|
|
114
|
+
currentPeriodEnd?: string;
|
|
115
|
+
nextBillingDate?: string;
|
|
116
|
+
currentPlanPriceId?: string;
|
|
117
|
+
targetPlanPriceId: string;
|
|
118
|
+
preservedAddons: SubscriptionPlanChangeAddon[];
|
|
119
|
+
lines: PlanChangePreviewLine[];
|
|
120
|
+
message: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface ApplySubscriptionPlanChangeRequest {
|
|
124
|
+
companyId?: string;
|
|
125
|
+
targetPlan: SubscriptionPlanChangePlan;
|
|
126
|
+
expectedCurrentPlanPriceId: string;
|
|
127
|
+
expectedTargetPlanPriceId: string;
|
|
128
|
+
expectedProrationDate: number;
|
|
129
|
+
previewIdempotencyKey?: string;
|
|
130
|
+
idempotencyKey?: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface ApplySubscriptionPlanChangeResponse {
|
|
134
|
+
status: "applied" | "requires_payment_action" | "payment_failed" | "pending_update" | "no_change";
|
|
135
|
+
currentPlan: SubscriptionPlanChangePlan;
|
|
136
|
+
targetPlan: SubscriptionPlanChangePlan;
|
|
137
|
+
subscriptionId: string;
|
|
138
|
+
invoiceId?: string;
|
|
139
|
+
hostedInvoiceUrl?: string;
|
|
140
|
+
paymentIntentClientSecret?: string;
|
|
141
|
+
amountDueNow?: number;
|
|
142
|
+
currency?: string;
|
|
143
|
+
currentPeriodEnd?: string;
|
|
144
|
+
message: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
57
147
|
export interface IStripeSettings {
|
|
148
|
+
planPriceId?: string | null;
|
|
58
149
|
stripeCFDISubID: string | null;
|
|
59
150
|
stripeCFDISubItemID: string | null;
|
|
60
151
|
cfdiBillingMode?: CFDIBillingMode;
|
|
@@ -13,6 +13,7 @@ export interface IPermissionMutationType {
|
|
|
13
13
|
|
|
14
14
|
export interface IPermission {
|
|
15
15
|
canEditOrders: IPermissionMutationType,
|
|
16
|
+
canManageAttendance: IPermissionMutationType,
|
|
16
17
|
mutateOrderPayments: IPermissionMutationType,
|
|
17
18
|
markOrderAsCollectedWithPendingAmount: IPermissionMutationType,
|
|
18
19
|
markOrderAsUncollected: IPermissionMutationType,
|
|
@@ -18,6 +18,14 @@ describe("attendance metadata requests", () => {
|
|
|
18
18
|
timezone: "America/Mexico_City",
|
|
19
19
|
screenWidth: 1440,
|
|
20
20
|
screenHeight: 900,
|
|
21
|
+
location: {
|
|
22
|
+
permissionStatus: "granted",
|
|
23
|
+
latitude: 19.4326,
|
|
24
|
+
longitude: -99.1332,
|
|
25
|
+
accuracy: 24,
|
|
26
|
+
capturedAt: "2026-05-26T14:00:00.000Z",
|
|
27
|
+
unknown: "drop-me",
|
|
28
|
+
},
|
|
21
29
|
ipAddress: "1.1.1.1",
|
|
22
30
|
cfConnectingIp: "1.1.1.2",
|
|
23
31
|
xForwardedFor: "1.1.1.3",
|
|
@@ -36,6 +44,13 @@ describe("attendance metadata requests", () => {
|
|
|
36
44
|
timezone: "America/Mexico_City",
|
|
37
45
|
screenWidth: 1440,
|
|
38
46
|
screenHeight: 900,
|
|
47
|
+
location: {
|
|
48
|
+
permissionStatus: "granted",
|
|
49
|
+
latitude: 19.4326,
|
|
50
|
+
longitude: -99.1332,
|
|
51
|
+
accuracy: 24,
|
|
52
|
+
capturedAt: "2026-05-26T14:00:00.000Z",
|
|
53
|
+
},
|
|
39
54
|
},
|
|
40
55
|
},
|
|
41
56
|
{
|
|
@@ -59,6 +74,15 @@ describe("attendance metadata requests", () => {
|
|
|
59
74
|
platform: "ios",
|
|
60
75
|
appVersion: "2.5.0",
|
|
61
76
|
deviceId: "device-1",
|
|
77
|
+
location: {
|
|
78
|
+
permissionStatus: "denied",
|
|
79
|
+
latitude: 19.4326,
|
|
80
|
+
longitude: -99.1332,
|
|
81
|
+
accuracy: 24,
|
|
82
|
+
capturedAt: "2026-05-26T14:00:00.000Z",
|
|
83
|
+
errorCode: "1",
|
|
84
|
+
errorMessage: "Permission denied",
|
|
85
|
+
},
|
|
62
86
|
ipAddress: "1.1.1.1",
|
|
63
87
|
},
|
|
64
88
|
} as any);
|
|
@@ -73,6 +97,12 @@ describe("attendance metadata requests", () => {
|
|
|
73
97
|
platform: "ios",
|
|
74
98
|
appVersion: "2.5.0",
|
|
75
99
|
deviceId: "device-1",
|
|
100
|
+
location: {
|
|
101
|
+
permissionStatus: "denied",
|
|
102
|
+
capturedAt: "2026-05-26T14:00:00.000Z",
|
|
103
|
+
errorCode: "1",
|
|
104
|
+
errorMessage: "Permission denied",
|
|
105
|
+
},
|
|
76
106
|
},
|
|
77
107
|
},
|
|
78
108
|
{
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { updateById } from "../src/api/attendance/put";
|
|
2
|
+
|
|
3
|
+
describe("attendance update requests", () => {
|
|
4
|
+
it("sends reason with editable attendance fields", async () => {
|
|
5
|
+
const put = jest.fn().mockResolvedValue({ data: { data: {} } });
|
|
6
|
+
const client = {
|
|
7
|
+
apiToken: "token-123",
|
|
8
|
+
axiosInstance: { put },
|
|
9
|
+
} as any;
|
|
10
|
+
|
|
11
|
+
await updateById.call(client, "entry-1", {
|
|
12
|
+
timestamp: "2026-05-26T14:00:00.000Z",
|
|
13
|
+
notes: "Corrected note",
|
|
14
|
+
reason: "El empleado olvidó marcar a tiempo",
|
|
15
|
+
editorId: "ignored-by-backend",
|
|
16
|
+
} as any);
|
|
17
|
+
|
|
18
|
+
expect(put).toHaveBeenCalledWith(
|
|
19
|
+
"api/attendance/entry-1",
|
|
20
|
+
{
|
|
21
|
+
timestamp: "2026-05-26T14:00:00.000Z",
|
|
22
|
+
notes: "Corrected note",
|
|
23
|
+
reason: "El empleado olvidó marcar a tiempo",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
headers: { Authorization: "Bearer token-123" },
|
|
27
|
+
},
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { getAvailableSubscriptionPlans } from "../src/api/companies/get";
|
|
2
|
+
import {
|
|
3
|
+
changeSubscriptionPlan,
|
|
4
|
+
previewSubscriptionPlanChange,
|
|
5
|
+
} from "../src/api/companies/post";
|
|
6
|
+
import type {
|
|
7
|
+
ApplySubscriptionPlanChangeRequest,
|
|
8
|
+
PlanChangePreviewResponse,
|
|
9
|
+
} from "../src/interfaces/Company";
|
|
10
|
+
|
|
11
|
+
describe("subscription plan change company endpoints", () => {
|
|
12
|
+
it("fetches available plans through the billing endpoint", async () => {
|
|
13
|
+
const get = jest.fn().mockResolvedValue({ data: { data: { plans: [] } } });
|
|
14
|
+
const client = {
|
|
15
|
+
apiToken: "token-1",
|
|
16
|
+
axiosInstance: { get },
|
|
17
|
+
} as any;
|
|
18
|
+
|
|
19
|
+
await getAvailableSubscriptionPlans.call(client, { companyId: "company-1" });
|
|
20
|
+
|
|
21
|
+
expect(get).toHaveBeenCalledWith(
|
|
22
|
+
"api/billing/subscription/available-plans?companyId=company-1",
|
|
23
|
+
{ headers: { Authorization: "Bearer token-1" } },
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("fetches available plans without sending an undefined companyId", async () => {
|
|
28
|
+
const get = jest.fn().mockResolvedValue({ data: { data: { plans: [] } } });
|
|
29
|
+
const client = {
|
|
30
|
+
apiToken: "token-1",
|
|
31
|
+
axiosInstance: { get },
|
|
32
|
+
} as any;
|
|
33
|
+
|
|
34
|
+
await getAvailableSubscriptionPlans.call(client, {});
|
|
35
|
+
|
|
36
|
+
expect(get).toHaveBeenCalledWith(
|
|
37
|
+
"api/billing/subscription/available-plans",
|
|
38
|
+
{ headers: { Authorization: "Bearer token-1" } },
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("previews a subscription plan change through the billing endpoint", async () => {
|
|
43
|
+
const post = jest.fn().mockResolvedValue({
|
|
44
|
+
data: {
|
|
45
|
+
data: {
|
|
46
|
+
status: "preview_ready",
|
|
47
|
+
prorationDate: 1770000000,
|
|
48
|
+
} satisfies Partial<PlanChangePreviewResponse>,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
const client = {
|
|
52
|
+
apiToken: "token-1",
|
|
53
|
+
axiosInstance: { post },
|
|
54
|
+
} as any;
|
|
55
|
+
|
|
56
|
+
await previewSubscriptionPlanChange.call(client, {
|
|
57
|
+
companyId: "company-1",
|
|
58
|
+
targetPlan: "premium",
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
expect(post).toHaveBeenCalledWith(
|
|
62
|
+
"api/billing/subscription/change-plan/preview",
|
|
63
|
+
{ companyId: "company-1", targetPlan: "premium" },
|
|
64
|
+
{ headers: { Authorization: "Bearer token-1" } },
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("applies a subscription plan change with expected preview fields", async () => {
|
|
69
|
+
const post = jest.fn().mockResolvedValue({ data: { data: { status: "applied" } } });
|
|
70
|
+
const client = {
|
|
71
|
+
apiToken: "token-1",
|
|
72
|
+
axiosInstance: { post },
|
|
73
|
+
} as any;
|
|
74
|
+
const payload: ApplySubscriptionPlanChangeRequest = {
|
|
75
|
+
companyId: "company-1",
|
|
76
|
+
targetPlan: "premium",
|
|
77
|
+
expectedCurrentPlanPriceId: "price_standard",
|
|
78
|
+
expectedTargetPlanPriceId: "price_premium",
|
|
79
|
+
expectedProrationDate: 1770000000,
|
|
80
|
+
previewIdempotencyKey: "preview-1",
|
|
81
|
+
idempotencyKey: "apply-1",
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
await changeSubscriptionPlan.call(client, payload);
|
|
85
|
+
|
|
86
|
+
expect(post).toHaveBeenCalledWith(
|
|
87
|
+
"api/billing/subscription/change-plan",
|
|
88
|
+
payload,
|
|
89
|
+
{ headers: { Authorization: "Bearer token-1" } },
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
});
|