@gymspace/sdk 1.0.0 → 1.0.2
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/index.js +226 -30
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +225 -31
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
- package/src/client.ts +85 -17
- package/src/models/auth.ts +20 -0
- package/src/models/clients.ts +35 -7
- package/src/models/contracts.ts +23 -1
- package/src/models/dashboard.ts +42 -9
- package/src/models/gyms.ts +52 -0
- package/src/models/index.ts +2 -0
- package/src/models/organizations.ts +1 -4
- package/src/models/payment-methods.ts +41 -0
- package/src/models/products.ts +48 -1
- package/src/models/sales.ts +47 -0
- package/src/models/subscriptions.ts +74 -0
- package/src/resources/auth.ts +2 -2
- package/src/resources/clients.ts +14 -0
- package/src/resources/dashboard.ts +64 -12
- package/src/resources/gyms.ts +17 -1
- package/src/resources/index.ts +3 -1
- package/src/resources/payment-methods.ts +48 -0
- package/src/resources/products.ts +13 -0
- package/src/resources/sales.ts +20 -3
- package/src/resources/subscriptions.ts +65 -0
- package/src/sdk.ts +13 -0
package/src/client.ts
CHANGED
|
@@ -17,6 +17,11 @@ import {
|
|
|
17
17
|
export class ApiClient {
|
|
18
18
|
private axiosInstance: AxiosInstance;
|
|
19
19
|
private config: GymSpaceConfig;
|
|
20
|
+
private refreshPromise: Promise<any> | null = null; // To prevent concurrent refresh attempts
|
|
21
|
+
|
|
22
|
+
// Callbacks for token updates and auth errors
|
|
23
|
+
public onTokensUpdated?: (accessToken: string, refreshToken: string) => void;
|
|
24
|
+
public onAuthError?: (error: any) => void;
|
|
20
25
|
|
|
21
26
|
constructor(config: GymSpaceConfig) {
|
|
22
27
|
this.config = config;
|
|
@@ -42,6 +47,11 @@ export class ApiClient {
|
|
|
42
47
|
config.headers['Authorization'] = `Bearer ${this.config.apiKey}`;
|
|
43
48
|
}
|
|
44
49
|
|
|
50
|
+
// Add refresh token header if available
|
|
51
|
+
if (this.config.refreshToken && config.headers) {
|
|
52
|
+
config.headers['X-Refresh-Token'] = this.config.refreshToken;
|
|
53
|
+
}
|
|
54
|
+
|
|
45
55
|
return config;
|
|
46
56
|
},
|
|
47
57
|
(error) => {
|
|
@@ -49,33 +59,91 @@ export class ApiClient {
|
|
|
49
59
|
},
|
|
50
60
|
);
|
|
51
61
|
|
|
52
|
-
// Response interceptor
|
|
62
|
+
// Response interceptor with automatic token refresh
|
|
53
63
|
this.axiosInstance.interceptors.response.use(
|
|
54
|
-
(response) =>
|
|
55
|
-
|
|
64
|
+
(response) => {
|
|
65
|
+
// Check for new tokens in response headers
|
|
66
|
+
const newAccessToken = response.headers['x-new-access-token'];
|
|
67
|
+
const newRefreshToken = response.headers['x-new-refresh-token'];
|
|
68
|
+
|
|
69
|
+
if (newAccessToken && newRefreshToken) {
|
|
70
|
+
this.setTokens(newAccessToken, newRefreshToken);
|
|
71
|
+
// Emit token update event if needed
|
|
72
|
+
this.onTokensUpdated?.(newAccessToken, newRefreshToken);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return response;
|
|
76
|
+
},
|
|
77
|
+
async (error: AxiosError) => {
|
|
78
|
+
const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean };
|
|
79
|
+
|
|
80
|
+
// If it's a 401 error and we haven't retried yet, try to refresh the token
|
|
81
|
+
if (error.response?.status === 401 && !originalRequest._retry && this.config.refreshToken) {
|
|
82
|
+
originalRequest._retry = true;
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
// Prevent concurrent refresh attempts
|
|
86
|
+
if (!this.refreshPromise) {
|
|
87
|
+
this.refreshPromise = this.refreshAccessToken();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const newTokens = await this.refreshPromise;
|
|
91
|
+
this.refreshPromise = null;
|
|
92
|
+
|
|
93
|
+
if (newTokens) {
|
|
94
|
+
// Update the original request with new token
|
|
95
|
+
originalRequest.headers = originalRequest.headers || {};
|
|
96
|
+
originalRequest.headers['Authorization'] = `Bearer ${newTokens.access_token}`;
|
|
97
|
+
|
|
98
|
+
// Retry the original request
|
|
99
|
+
return this.axiosInstance(originalRequest);
|
|
100
|
+
}
|
|
101
|
+
} catch (refreshError) {
|
|
102
|
+
this.refreshPromise = null;
|
|
103
|
+
// If refresh fails, clear tokens and emit error
|
|
104
|
+
this.clearAuth();
|
|
105
|
+
this.onAuthError?.(refreshError);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
56
109
|
throw this.handleError(error);
|
|
57
110
|
},
|
|
58
111
|
);
|
|
59
112
|
}
|
|
60
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Refresh the access token using the stored refresh token
|
|
116
|
+
*/
|
|
117
|
+
private async refreshAccessToken(): Promise<any> {
|
|
118
|
+
if (!this.config.refreshToken) {
|
|
119
|
+
throw new AuthenticationError('No refresh token available');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
const response = await axios.post(
|
|
124
|
+
`${this.config.baseURL}/auth/refresh`,
|
|
125
|
+
{ refresh_token: this.config.refreshToken },
|
|
126
|
+
{
|
|
127
|
+
headers: {
|
|
128
|
+
'Content-Type': 'application/json',
|
|
129
|
+
},
|
|
130
|
+
timeout: this.config.timeout || 30000,
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const newTokens = response.data;
|
|
135
|
+
this.setTokens(newTokens.access_token, newTokens.refresh_token);
|
|
136
|
+
|
|
137
|
+
return newTokens;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
throw new AuthenticationError('Failed to refresh token');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
61
143
|
private handleError(error: AxiosError): GymSpaceError {
|
|
62
144
|
const requestPath = error.config?.url || 'unknown';
|
|
63
145
|
const method = error.config?.method?.toUpperCase() || 'unknown';
|
|
64
146
|
|
|
65
|
-
console.error(
|
|
66
|
-
'API Error:',
|
|
67
|
-
JSON.stringify(
|
|
68
|
-
{
|
|
69
|
-
method,
|
|
70
|
-
path: requestPath,
|
|
71
|
-
message: error.message,
|
|
72
|
-
baseURL: error.config?.baseURL,
|
|
73
|
-
},
|
|
74
|
-
null,
|
|
75
|
-
3,
|
|
76
|
-
),
|
|
77
|
-
);
|
|
78
|
-
|
|
79
147
|
if (!error.response) {
|
|
80
148
|
console.error('Network Error:', {
|
|
81
149
|
method,
|
package/src/models/auth.ts
CHANGED
|
@@ -150,6 +150,26 @@ export interface CurrentSessionResponse {
|
|
|
150
150
|
timezone: string;
|
|
151
151
|
settings?: Record<string, any>;
|
|
152
152
|
};
|
|
153
|
+
subscription?: {
|
|
154
|
+
id: string;
|
|
155
|
+
organizationId: string;
|
|
156
|
+
subscriptionPlanId: string;
|
|
157
|
+
subscriptionPlan?: {
|
|
158
|
+
id: string;
|
|
159
|
+
name: string;
|
|
160
|
+
price: any;
|
|
161
|
+
billingFrequency: string;
|
|
162
|
+
maxGyms: number;
|
|
163
|
+
maxClientsPerGym: number;
|
|
164
|
+
maxUsersPerGym: number;
|
|
165
|
+
features: any;
|
|
166
|
+
description?: string;
|
|
167
|
+
};
|
|
168
|
+
status: string;
|
|
169
|
+
startDate: Date;
|
|
170
|
+
endDate: Date;
|
|
171
|
+
isActive: boolean;
|
|
172
|
+
};
|
|
153
173
|
permissions: string[];
|
|
154
174
|
isAuthenticated: boolean;
|
|
155
175
|
}
|
package/src/models/clients.ts
CHANGED
|
@@ -80,14 +80,42 @@ export interface Client {
|
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
export interface ClientStat {
|
|
84
|
+
key: string;
|
|
85
|
+
name: string;
|
|
86
|
+
description: string;
|
|
87
|
+
category: 'activity' | 'contracts' | 'general';
|
|
88
|
+
value: any;
|
|
89
|
+
}
|
|
90
|
+
|
|
83
91
|
export interface ClientStats {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
92
|
+
client: {
|
|
93
|
+
id: string;
|
|
94
|
+
name: string;
|
|
95
|
+
email: string | null;
|
|
96
|
+
status: string;
|
|
97
|
+
registrationDate: string;
|
|
98
|
+
};
|
|
99
|
+
activity: {
|
|
100
|
+
totalCheckIns: number;
|
|
101
|
+
monthlyCheckIns: number;
|
|
102
|
+
lastCheckIn: string | null;
|
|
103
|
+
};
|
|
104
|
+
contracts: {
|
|
105
|
+
active: number;
|
|
106
|
+
totalSpent: number;
|
|
107
|
+
};
|
|
108
|
+
membershipHistory: Array<{
|
|
109
|
+
id: string;
|
|
110
|
+
status: string;
|
|
111
|
+
startDate: string;
|
|
112
|
+
endDate: string | null;
|
|
113
|
+
gymMembershipPlan: {
|
|
114
|
+
id: string;
|
|
115
|
+
name: string;
|
|
116
|
+
basePrice: number;
|
|
117
|
+
};
|
|
118
|
+
}>;
|
|
91
119
|
}
|
|
92
120
|
|
|
93
121
|
export interface SearchClientsParams extends PaginationQueryDto {
|
package/src/models/contracts.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { PaginationQueryDto } from '../types';
|
|
|
4
4
|
export interface CreateContractDto {
|
|
5
5
|
gymClientId: string;
|
|
6
6
|
gymMembershipPlanId: string;
|
|
7
|
+
paymentMethodId: string;
|
|
7
8
|
startDate: string;
|
|
8
9
|
discountPercentage?: number;
|
|
9
10
|
customPrice?: number;
|
|
@@ -15,7 +16,12 @@ export interface RenewContractDto {
|
|
|
15
16
|
startDate?: string;
|
|
16
17
|
discountPercentage?: number;
|
|
17
18
|
customPrice?: number;
|
|
18
|
-
|
|
19
|
+
// Payment method ID (UUID) - uses the existing payment method if not provided
|
|
20
|
+
paymentMethodId?: string;
|
|
21
|
+
applyAtEndOfContract?: boolean;
|
|
22
|
+
notes?: string;
|
|
23
|
+
contractDocumentId?: string;
|
|
24
|
+
receiptIds?: string[];
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
export interface FreezeContractDto {
|
|
@@ -30,6 +36,8 @@ export interface Contract {
|
|
|
30
36
|
contractNumber: string;
|
|
31
37
|
gymClientId: string;
|
|
32
38
|
gymMembershipPlanId: string;
|
|
39
|
+
paymentMethodId?: string;
|
|
40
|
+
parentId?: string; // Reference to parent contract for renewals
|
|
33
41
|
startDate: string;
|
|
34
42
|
endDate: string;
|
|
35
43
|
status: ContractStatus;
|
|
@@ -54,8 +62,22 @@ export interface Contract {
|
|
|
54
62
|
basePrice?: number;
|
|
55
63
|
durationMonths?: number;
|
|
56
64
|
};
|
|
65
|
+
paymentMethod?: {
|
|
66
|
+
id: string;
|
|
67
|
+
name: string;
|
|
68
|
+
description?: string;
|
|
69
|
+
code: string;
|
|
70
|
+
enabled: boolean;
|
|
71
|
+
};
|
|
72
|
+
renewals?: Contract[]; // Renewal contracts for this contract
|
|
57
73
|
}
|
|
58
74
|
|
|
59
75
|
export interface GetContractsParams extends PaginationQueryDto {
|
|
60
76
|
status?: ContractStatus;
|
|
77
|
+
clientName?: string;
|
|
78
|
+
clientId?: string;
|
|
79
|
+
startDateFrom?: string;
|
|
80
|
+
startDateTo?: string;
|
|
81
|
+
endDateFrom?: string;
|
|
82
|
+
endDateTo?: string;
|
|
61
83
|
}
|
package/src/models/dashboard.ts
CHANGED
|
@@ -5,21 +5,49 @@ export interface DashboardStats {
|
|
|
5
5
|
activeClients: number;
|
|
6
6
|
totalContracts: number;
|
|
7
7
|
activeContracts: number;
|
|
8
|
-
monthlyRevenue: number;
|
|
8
|
+
monthlyRevenue: number; // Deprecated, use getContractsRevenue instead
|
|
9
9
|
todayCheckIns: number;
|
|
10
10
|
expiringContractsCount: number;
|
|
11
11
|
newClientsThisMonth: number;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export
|
|
14
|
+
export interface ContractsRevenue {
|
|
15
|
+
totalRevenue: number;
|
|
16
|
+
contractCount: number;
|
|
17
|
+
averageRevenue: number;
|
|
18
|
+
startDate: string;
|
|
19
|
+
endDate: string;
|
|
20
|
+
}
|
|
15
21
|
|
|
16
|
-
export interface
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
export interface SalesRevenue {
|
|
23
|
+
totalRevenue: number;
|
|
24
|
+
salesCount: number;
|
|
25
|
+
averageRevenue: number;
|
|
26
|
+
startDate: string;
|
|
27
|
+
endDate: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface Debts {
|
|
31
|
+
totalDebt: number;
|
|
32
|
+
clientsWithDebt: number;
|
|
33
|
+
averageDebt: number;
|
|
34
|
+
startDate: string;
|
|
35
|
+
endDate: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface CheckIns {
|
|
39
|
+
totalCheckIns: number;
|
|
40
|
+
uniqueClients: number;
|
|
41
|
+
averagePerDay: number;
|
|
42
|
+
startDate: string;
|
|
43
|
+
endDate: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface NewClients {
|
|
47
|
+
totalNewClients: number;
|
|
48
|
+
averagePerDay: number;
|
|
49
|
+
startDate: string;
|
|
50
|
+
endDate: string;
|
|
23
51
|
}
|
|
24
52
|
|
|
25
53
|
export interface ExpiringContract {
|
|
@@ -29,4 +57,9 @@ export interface ExpiringContract {
|
|
|
29
57
|
planName: string;
|
|
30
58
|
endDate: string;
|
|
31
59
|
daysRemaining: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface DateRangeParams {
|
|
63
|
+
startDate?: string;
|
|
64
|
+
endDate?: string;
|
|
32
65
|
}
|
package/src/models/gyms.ts
CHANGED
|
@@ -57,11 +57,63 @@ export interface Gym {
|
|
|
57
57
|
capacity?: number;
|
|
58
58
|
amenities?: GymAmenities;
|
|
59
59
|
settings?: GymSettings;
|
|
60
|
+
schedule?: GymSchedule;
|
|
61
|
+
socialMedia?: GymSocialMedia;
|
|
60
62
|
isActive: boolean;
|
|
61
63
|
createdAt: string;
|
|
62
64
|
updatedAt: string;
|
|
63
65
|
}
|
|
64
66
|
|
|
67
|
+
export interface TimeSlot {
|
|
68
|
+
open: string;
|
|
69
|
+
close: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface DaySchedule {
|
|
73
|
+
isOpen: boolean;
|
|
74
|
+
slots?: TimeSlot[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface GymSchedule {
|
|
78
|
+
monday?: DaySchedule;
|
|
79
|
+
tuesday?: DaySchedule;
|
|
80
|
+
wednesday?: DaySchedule;
|
|
81
|
+
thursday?: DaySchedule;
|
|
82
|
+
friday?: DaySchedule;
|
|
83
|
+
saturday?: DaySchedule;
|
|
84
|
+
sunday?: DaySchedule;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface GymSocialMedia {
|
|
88
|
+
facebook?: string;
|
|
89
|
+
instagram?: string;
|
|
90
|
+
whatsapp?: string;
|
|
91
|
+
twitter?: string;
|
|
92
|
+
linkedin?: string;
|
|
93
|
+
youtube?: string;
|
|
94
|
+
tiktok?: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface UpdateGymScheduleDto {
|
|
98
|
+
monday?: DaySchedule;
|
|
99
|
+
tuesday?: DaySchedule;
|
|
100
|
+
wednesday?: DaySchedule;
|
|
101
|
+
thursday?: DaySchedule;
|
|
102
|
+
friday?: DaySchedule;
|
|
103
|
+
saturday?: DaySchedule;
|
|
104
|
+
sunday?: DaySchedule;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface UpdateGymSocialMediaDto {
|
|
108
|
+
facebook?: string;
|
|
109
|
+
instagram?: string;
|
|
110
|
+
whatsapp?: string;
|
|
111
|
+
twitter?: string;
|
|
112
|
+
linkedin?: string;
|
|
113
|
+
youtube?: string;
|
|
114
|
+
tiktok?: string;
|
|
115
|
+
}
|
|
116
|
+
|
|
65
117
|
export interface GymStats {
|
|
66
118
|
totalClients: number;
|
|
67
119
|
activeClients: number;
|
package/src/models/index.ts
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { PaginationQueryDto } from '../types';
|
|
2
|
+
|
|
3
|
+
export interface CreatePaymentMethodDto {
|
|
4
|
+
name: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
code: string;
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
metadata?: Record<string, any>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface UpdatePaymentMethodDto {
|
|
12
|
+
name?: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
code?: string;
|
|
15
|
+
enabled?: boolean;
|
|
16
|
+
metadata?: Record<string, any>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PaymentMethod {
|
|
20
|
+
id: string;
|
|
21
|
+
gymId: string;
|
|
22
|
+
name: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
code: string;
|
|
25
|
+
enabled: boolean;
|
|
26
|
+
metadata?: Record<string, any>;
|
|
27
|
+
createdAt: string;
|
|
28
|
+
updatedAt: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface SearchPaymentMethodsParams extends PaginationQueryDto {
|
|
32
|
+
search?: string;
|
|
33
|
+
enabledOnly?: boolean;
|
|
34
|
+
code?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface PaymentMethodStats {
|
|
38
|
+
totalPaymentMethods: number;
|
|
39
|
+
enabledPaymentMethods: number;
|
|
40
|
+
disabledPaymentMethods: number;
|
|
41
|
+
}
|
package/src/models/products.ts
CHANGED
|
@@ -42,9 +42,20 @@ export interface CreateProductDto {
|
|
|
42
42
|
description?: string;
|
|
43
43
|
price: number;
|
|
44
44
|
stock?: number;
|
|
45
|
+
minStock?: number;
|
|
46
|
+
maxStock?: number;
|
|
45
47
|
categoryId?: string;
|
|
46
48
|
imageId?: string;
|
|
47
49
|
status?: 'active' | 'inactive';
|
|
50
|
+
trackInventory?: 'none' | 'simple' | 'advanced' | 'capacity';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface CreateServiceDto {
|
|
54
|
+
name: string;
|
|
55
|
+
description?: string;
|
|
56
|
+
price: number;
|
|
57
|
+
categoryId?: string;
|
|
58
|
+
imageId?: string;
|
|
48
59
|
}
|
|
49
60
|
|
|
50
61
|
export interface UpdateProductDto {
|
|
@@ -52,13 +63,19 @@ export interface UpdateProductDto {
|
|
|
52
63
|
description?: string;
|
|
53
64
|
price?: number;
|
|
54
65
|
stock?: number;
|
|
66
|
+
minStock?: number;
|
|
67
|
+
maxStock?: number;
|
|
55
68
|
categoryId?: string;
|
|
56
69
|
imageId?: string;
|
|
57
70
|
status?: 'active' | 'inactive';
|
|
71
|
+
trackInventory?: 'none' | 'simple' | 'advanced' | 'capacity';
|
|
58
72
|
}
|
|
59
73
|
|
|
60
74
|
export interface UpdateStockDto {
|
|
61
75
|
quantity: number;
|
|
76
|
+
notes?: string;
|
|
77
|
+
supplierId?: string;
|
|
78
|
+
fileId?: string;
|
|
62
79
|
}
|
|
63
80
|
|
|
64
81
|
export interface Product {
|
|
@@ -68,9 +85,13 @@ export interface Product {
|
|
|
68
85
|
name: string;
|
|
69
86
|
description?: string;
|
|
70
87
|
price: number;
|
|
71
|
-
stock: number;
|
|
88
|
+
stock: number | null;
|
|
89
|
+
minStock?: number | null;
|
|
90
|
+
maxStock?: number | null;
|
|
72
91
|
imageId?: string;
|
|
73
92
|
status: 'active' | 'inactive';
|
|
93
|
+
type?: 'Product' | 'Service';
|
|
94
|
+
trackInventory?: 'none' | 'simple' | 'advanced' | 'capacity';
|
|
74
95
|
createdAt: string;
|
|
75
96
|
updatedAt: string;
|
|
76
97
|
category?: ProductCategory;
|
|
@@ -92,8 +113,34 @@ export interface Product {
|
|
|
92
113
|
export interface SearchProductsParams extends PaginationQueryDto {
|
|
93
114
|
search?: string;
|
|
94
115
|
categoryId?: string;
|
|
116
|
+
type?: 'Product' | 'Service';
|
|
95
117
|
status?: 'active' | 'inactive';
|
|
96
118
|
inStock?: boolean;
|
|
97
119
|
minPrice?: number;
|
|
98
120
|
maxPrice?: number;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Stock Movement Models
|
|
124
|
+
export interface StockMovement {
|
|
125
|
+
id: string;
|
|
126
|
+
productId: string;
|
|
127
|
+
gymId: string;
|
|
128
|
+
type: 'manual_entry' | 'sale' | 'return' | 'adjustment' | 'initial_stock';
|
|
129
|
+
quantity: number;
|
|
130
|
+
previousStock?: number;
|
|
131
|
+
newStock?: number;
|
|
132
|
+
notes?: string;
|
|
133
|
+
supplierId?: string;
|
|
134
|
+
fileId?: string;
|
|
135
|
+
createdByUserId: string;
|
|
136
|
+
createdAt: string;
|
|
137
|
+
product?: Product;
|
|
138
|
+
supplier?: {
|
|
139
|
+
id: string;
|
|
140
|
+
name: string;
|
|
141
|
+
};
|
|
142
|
+
createdBy?: {
|
|
143
|
+
id: string;
|
|
144
|
+
name: string;
|
|
145
|
+
};
|
|
99
146
|
}
|
package/src/models/sales.ts
CHANGED
|
@@ -31,15 +31,21 @@ export interface SaleItem {
|
|
|
31
31
|
// Sale Models
|
|
32
32
|
export interface CreateSaleDto {
|
|
33
33
|
items: SaleItemDto[];
|
|
34
|
+
customerId?: string;
|
|
34
35
|
customerName?: string;
|
|
35
36
|
notes?: string;
|
|
37
|
+
fileIds?: string[];
|
|
36
38
|
paymentStatus?: 'paid' | 'unpaid';
|
|
39
|
+
paymentMethodId?: string;
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
export interface UpdateSaleDto {
|
|
43
|
+
customerId?: string;
|
|
40
44
|
customerName?: string;
|
|
41
45
|
notes?: string;
|
|
46
|
+
fileIds?: string[];
|
|
42
47
|
paymentStatus?: 'paid' | 'unpaid';
|
|
48
|
+
paymentMethodId?: string;
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
export interface UpdatePaymentStatusDto {
|
|
@@ -49,15 +55,31 @@ export interface UpdatePaymentStatusDto {
|
|
|
49
55
|
export interface Sale {
|
|
50
56
|
id: string;
|
|
51
57
|
gymId: string;
|
|
58
|
+
customerId?: string;
|
|
52
59
|
saleNumber: string;
|
|
53
60
|
total: number;
|
|
54
61
|
paymentStatus: 'paid' | 'unpaid';
|
|
62
|
+
paymentMethodId?: string;
|
|
55
63
|
saleDate: string;
|
|
56
64
|
customerName?: string;
|
|
57
65
|
notes?: string;
|
|
66
|
+
fileIds?: string[];
|
|
58
67
|
createdAt: string;
|
|
59
68
|
updatedAt: string;
|
|
60
69
|
saleItems?: SaleItem[];
|
|
70
|
+
customer?: {
|
|
71
|
+
id: string;
|
|
72
|
+
clientNumber: string;
|
|
73
|
+
name: string;
|
|
74
|
+
phone?: string;
|
|
75
|
+
email?: string;
|
|
76
|
+
};
|
|
77
|
+
paymentMethod?: {
|
|
78
|
+
id: string;
|
|
79
|
+
name: string;
|
|
80
|
+
code: string;
|
|
81
|
+
description?: string;
|
|
82
|
+
};
|
|
61
83
|
createdBy?: {
|
|
62
84
|
id: string;
|
|
63
85
|
name: string;
|
|
@@ -75,6 +97,7 @@ export interface Sale {
|
|
|
75
97
|
|
|
76
98
|
export interface SearchSalesParams extends PaginationQueryDto {
|
|
77
99
|
customerName?: string;
|
|
100
|
+
customerId?: string;
|
|
78
101
|
paymentStatus?: 'paid' | 'unpaid';
|
|
79
102
|
startDate?: string;
|
|
80
103
|
endDate?: string;
|
|
@@ -105,4 +128,28 @@ export interface TopSellingProduct {
|
|
|
105
128
|
};
|
|
106
129
|
totalQuantity: number;
|
|
107
130
|
totalRevenue: number;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface CustomerSalesReport {
|
|
134
|
+
summary: {
|
|
135
|
+
totalCustomers: number;
|
|
136
|
+
totalSales: number;
|
|
137
|
+
totalRevenue: number;
|
|
138
|
+
};
|
|
139
|
+
customers: Array<{
|
|
140
|
+
customer: {
|
|
141
|
+
id: string | null;
|
|
142
|
+
clientNumber?: string;
|
|
143
|
+
name: string;
|
|
144
|
+
phone?: string;
|
|
145
|
+
email?: string;
|
|
146
|
+
};
|
|
147
|
+
totalSales: number;
|
|
148
|
+
totalRevenue: number;
|
|
149
|
+
sales: Array<{
|
|
150
|
+
id: string;
|
|
151
|
+
total: number;
|
|
152
|
+
saleDate: string;
|
|
153
|
+
}>;
|
|
154
|
+
}>;
|
|
108
155
|
}
|