vantuz 3.0.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/LICENSE +45 -0
- package/README.md +44 -0
- package/cli.js +420 -0
- package/core/ai-analyst.js +46 -0
- package/core/database.js +74 -0
- package/core/license-manager.js +50 -0
- package/core/product-manager.js +134 -0
- package/package.json +72 -0
- package/plugins/vantuz/index.js +480 -0
- package/plugins/vantuz/memory/hippocampus.js +464 -0
- package/plugins/vantuz/package.json +21 -0
- package/plugins/vantuz/platforms/amazon.js +239 -0
- package/plugins/vantuz/platforms/ciceksepeti.js +175 -0
- package/plugins/vantuz/platforms/hepsiburada.js +189 -0
- package/plugins/vantuz/platforms/index.js +215 -0
- package/plugins/vantuz/platforms/n11.js +263 -0
- package/plugins/vantuz/platforms/pazarama.js +171 -0
- package/plugins/vantuz/platforms/pttavm.js +136 -0
- package/plugins/vantuz/platforms/trendyol.js +307 -0
- package/plugins/vantuz/services/alerts.js +253 -0
- package/plugins/vantuz/services/license.js +237 -0
- package/plugins/vantuz/services/scheduler.js +232 -0
- package/plugins/vantuz/tools/analytics.js +152 -0
- package/plugins/vantuz/tools/crossborder.js +187 -0
- package/plugins/vantuz/tools/nl-parser.js +211 -0
- package/plugins/vantuz/tools/product.js +110 -0
- package/plugins/vantuz/tools/quick-report.js +175 -0
- package/plugins/vantuz/tools/repricer.js +314 -0
- package/plugins/vantuz/tools/sentiment.js +115 -0
- package/plugins/vantuz/tools/vision.js +257 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ๐ฎ PTTavm API Entegrasyonu
|
|
3
|
+
* pttavm.com/entegrasyon
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import axios from 'axios';
|
|
7
|
+
|
|
8
|
+
const BASE_URL = 'https://api.pttavm.com/v1';
|
|
9
|
+
|
|
10
|
+
export class PttavmAPI {
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.apiKey = config.apiKey;
|
|
13
|
+
this.token = config.token;
|
|
14
|
+
this.shopId = config.shopId;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
_headers() {
|
|
18
|
+
return {
|
|
19
|
+
'Authorization': `Bearer ${this.token}`,
|
|
20
|
+
'X-API-Key': this.apiKey,
|
|
21
|
+
'Content-Type': 'application/json'
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async _request(method, endpoint, data = null, params = null) {
|
|
26
|
+
try {
|
|
27
|
+
const response = await axios({
|
|
28
|
+
method,
|
|
29
|
+
url: `${BASE_URL}${endpoint}`,
|
|
30
|
+
headers: this._headers(),
|
|
31
|
+
data,
|
|
32
|
+
params
|
|
33
|
+
});
|
|
34
|
+
return { success: true, data: response.data };
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
error: error.response?.data || error.message
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
44
|
+
// รRรN ฤฐลLEMLERฤฐ
|
|
45
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
46
|
+
|
|
47
|
+
async getProducts(params = {}) {
|
|
48
|
+
const { page = 1, limit = 100, barcode } = params;
|
|
49
|
+
return await this._request('GET', '/products', null, {
|
|
50
|
+
page, limit, barcode
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async createProduct(product) {
|
|
55
|
+
return await this._request('POST', '/products', product);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async updateProduct(productId, updates) {
|
|
59
|
+
return await this._request('PUT', `/products/${productId}`, updates);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async updatePrice(barcode, price) {
|
|
63
|
+
return await this._request('PUT', '/products/price', {
|
|
64
|
+
items: [{ barcode, salePrice: price }]
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async updateStock(barcode, quantity) {
|
|
69
|
+
return await this._request('PUT', '/products/stock', {
|
|
70
|
+
items: [{ barcode, quantity }]
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
75
|
+
// SฤฐPARฤฐล ฤฐลLEMLERฤฐ
|
|
76
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
77
|
+
|
|
78
|
+
async getOrders(params = {}) {
|
|
79
|
+
const { status, startDate, endDate, page = 1, limit = 100 } = params;
|
|
80
|
+
return await this._request('GET', '/orders', null, {
|
|
81
|
+
status, startDate, endDate, page, limit
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async getOrderDetail(orderId) {
|
|
86
|
+
return await this._request('GET', `/orders/${orderId}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async updateOrderStatus(orderId, status) {
|
|
90
|
+
return await this._request('PUT', `/orders/${orderId}/status`, { status });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async shipOrder(orderId, trackingNumber) {
|
|
94
|
+
return await this._request('PUT', `/orders/${orderId}/ship`, {
|
|
95
|
+
trackingNumber,
|
|
96
|
+
cargoCompany: 'PTT'
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
101
|
+
// KATEGORฤฐ
|
|
102
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
103
|
+
|
|
104
|
+
async getCategories() {
|
|
105
|
+
return await this._request('GET', '/categories');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
109
|
+
// HELPER
|
|
110
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
111
|
+
|
|
112
|
+
async testConnection() {
|
|
113
|
+
const result = await this.getProducts({ page: 1, limit: 1 });
|
|
114
|
+
return result.success;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
isConnected() {
|
|
118
|
+
return !!(this.apiKey && this.token);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let instance = null;
|
|
123
|
+
|
|
124
|
+
export const pttavmApi = {
|
|
125
|
+
init(config) {
|
|
126
|
+
instance = new PttavmAPI(config);
|
|
127
|
+
return instance;
|
|
128
|
+
},
|
|
129
|
+
getInstance() { return instance; },
|
|
130
|
+
isConnected() { return instance?.isConnected() || false; },
|
|
131
|
+
|
|
132
|
+
async getProducts(params) { return instance?.getProducts(params); },
|
|
133
|
+
async updatePrice(code, price) { return instance?.updatePrice(code, price); },
|
|
134
|
+
async updateStock(code, qty) { return instance?.updateStock(code, qty); },
|
|
135
|
+
async getOrders(params) { return instance?.getOrders(params); }
|
|
136
|
+
};
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ๐ TRENDYOL API Entegrasyonu
|
|
3
|
+
* developers.trendyol.com
|
|
4
|
+
*
|
|
5
|
+
* รzellikler:
|
|
6
|
+
* - รrรผn listeleme/gรผncelleme
|
|
7
|
+
* - Fiyat/stok yรถnetimi
|
|
8
|
+
* - Sipariล yรถnetimi
|
|
9
|
+
* - Rakip fiyat รงekme
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import axios from 'axios';
|
|
13
|
+
|
|
14
|
+
const BASE_URL = 'https://api.trendyol.com/sapigw';
|
|
15
|
+
const STAGE_URL = 'https://stageapi.trendyol.com/stagesapigw'; // Test ortamฤฑ
|
|
16
|
+
|
|
17
|
+
export class TrendyolAPI {
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.supplierId = config.supplierId;
|
|
20
|
+
this.apiKey = config.apiKey;
|
|
21
|
+
this.apiSecret = config.apiSecret;
|
|
22
|
+
this.isStage = config.isStage || false;
|
|
23
|
+
|
|
24
|
+
this.baseUrl = this.isStage ? STAGE_URL : BASE_URL;
|
|
25
|
+
this.auth = Buffer.from(`${this.apiKey}:${this.apiSecret}`).toString('base64');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
_headers() {
|
|
29
|
+
return {
|
|
30
|
+
'Authorization': `Basic ${this.auth}`,
|
|
31
|
+
'Content-Type': 'application/json',
|
|
32
|
+
'User-Agent': `${this.supplierId} - VantuzAI`
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async _request(method, endpoint, data = null, params = null) {
|
|
37
|
+
try {
|
|
38
|
+
const response = await axios({
|
|
39
|
+
method,
|
|
40
|
+
url: `${this.baseUrl}${endpoint}`,
|
|
41
|
+
headers: this._headers(),
|
|
42
|
+
data,
|
|
43
|
+
params
|
|
44
|
+
});
|
|
45
|
+
return { success: true, data: response.data };
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
error: error.response?.data?.errors || error.message,
|
|
50
|
+
status: error.response?.status
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
56
|
+
// รRรN ฤฐลLEMLERฤฐ
|
|
57
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
58
|
+
|
|
59
|
+
async getProducts(params = {}) {
|
|
60
|
+
const { page = 0, size = 50, barcode, approved, onSale } = params;
|
|
61
|
+
return await this._request('GET', `/suppliers/${this.supplierId}/products`, null, {
|
|
62
|
+
page, size, barcode, approved, onSale
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async getProductByBarcode(barcode) {
|
|
67
|
+
const result = await this.getProducts({ barcode });
|
|
68
|
+
if (result.success && result.data.content?.length > 0) {
|
|
69
|
+
return { success: true, data: result.data.content[0] };
|
|
70
|
+
}
|
|
71
|
+
return { success: false, error: 'รrรผn bulunamadฤฑ' };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async createProducts(products) {
|
|
75
|
+
// items: [{ barcode, title, productMainId, brandId, categoryId, ... }]
|
|
76
|
+
return await this._request('POST', `/suppliers/${this.supplierId}/v2/products`, { items: products });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async updateProducts(products) {
|
|
80
|
+
return await this._request('PUT', `/suppliers/${this.supplierId}/v2/products`, { items: products });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async deleteProducts(barcodes) {
|
|
84
|
+
const items = barcodes.map(barcode => ({ barcode }));
|
|
85
|
+
return await this._request('DELETE', `/suppliers/${this.supplierId}/products`, { items });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
89
|
+
// FฤฐYAT & STOK
|
|
90
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
91
|
+
|
|
92
|
+
async updatePriceAndStock(items) {
|
|
93
|
+
// items: [{ barcode, quantity, salePrice, listPrice }]
|
|
94
|
+
return await this._request('POST', `/suppliers/${this.supplierId}/products/price-and-inventory`, { items });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async updatePrice(barcode, salePrice, listPrice = null) {
|
|
98
|
+
return await this.updatePriceAndStock([{
|
|
99
|
+
barcode,
|
|
100
|
+
salePrice,
|
|
101
|
+
listPrice: listPrice || salePrice
|
|
102
|
+
}]);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async updateStock(barcode, quantity) {
|
|
106
|
+
return await this.updatePriceAndStock([{ barcode, quantity }]);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async bulkPriceUpdate(updates) {
|
|
110
|
+
// updates: [{ barcode, price, listPrice? }]
|
|
111
|
+
const items = updates.map(u => ({
|
|
112
|
+
barcode: u.barcode,
|
|
113
|
+
salePrice: u.price,
|
|
114
|
+
listPrice: u.listPrice || u.price
|
|
115
|
+
}));
|
|
116
|
+
return await this.updatePriceAndStock(items);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
120
|
+
// SฤฐPARฤฐล ฤฐลLEMLERฤฐ
|
|
121
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
122
|
+
|
|
123
|
+
async getOrders(params = {}) {
|
|
124
|
+
const {
|
|
125
|
+
status, // Created, Picking, Invoiced, Shipped, Cancelled, Delivered
|
|
126
|
+
startDate,
|
|
127
|
+
endDate,
|
|
128
|
+
page = 0,
|
|
129
|
+
size = 50,
|
|
130
|
+
orderNumber
|
|
131
|
+
} = params;
|
|
132
|
+
|
|
133
|
+
return await this._request('GET', `/suppliers/${this.supplierId}/orders`, null, {
|
|
134
|
+
status, startDate, endDate, page, size, orderNumber
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async getOrderByNumber(orderNumber) {
|
|
139
|
+
const result = await this.getOrders({ orderNumber });
|
|
140
|
+
if (result.success && result.data.content?.length > 0) {
|
|
141
|
+
return { success: true, data: result.data.content[0] };
|
|
142
|
+
}
|
|
143
|
+
return { success: false, error: 'Sipariล bulunamadฤฑ' };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async updateOrderStatus(lines, status, params = {}) {
|
|
147
|
+
// lines: [{ lineId, quantity }]
|
|
148
|
+
// status: Picking, Invoiced, UnSupplied
|
|
149
|
+
const endpoint = `/suppliers/${this.supplierId}/shipment-packages`;
|
|
150
|
+
|
|
151
|
+
if (status === 'Picking') {
|
|
152
|
+
return await this._request('PUT', endpoint, { lines, status: 'Picking' });
|
|
153
|
+
} else if (status === 'Invoiced') {
|
|
154
|
+
return await this._request('PUT', endpoint, {
|
|
155
|
+
lines,
|
|
156
|
+
status: 'Invoiced',
|
|
157
|
+
invoiceNumber: params.invoiceNumber
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return { success: false, error: 'Geรงersiz durum' };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async shipOrder(shipmentPackageId, trackingNumber, cargoKey = 'YURTICI') {
|
|
165
|
+
// cargoKey: YURTICI, MNG, ARAS, PTT, SURAT, UPS, HOROZ, CEVA, SENDEO
|
|
166
|
+
return await this._request('PUT',
|
|
167
|
+
`/suppliers/${this.supplierId}/shipment-packages/${shipmentPackageId}`,
|
|
168
|
+
{
|
|
169
|
+
trackingNumber,
|
|
170
|
+
cargoProviderCode: cargoKey,
|
|
171
|
+
status: 'Shipped'
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async getShipmentProviders() {
|
|
177
|
+
return await this._request('GET', '/shipment-providers');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
181
|
+
// KATEGORฤฐ & MARKA
|
|
182
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
183
|
+
|
|
184
|
+
async getCategories() {
|
|
185
|
+
return await this._request('GET', '/product-categories');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async getCategoryAttributes(categoryId) {
|
|
189
|
+
return await this._request('GET', `/product-categories/${categoryId}/attributes`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async searchBrands(name) {
|
|
193
|
+
return await this._request('GET', '/brands', null, { name });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async getBrandsByCategory(categoryId) {
|
|
197
|
+
return await this._request('GET', `/product-categories/${categoryId}/brands`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
201
|
+
// RAKฤฐP ANALฤฐZฤฐ (Scraping - Dikkat: Rate limit)
|
|
202
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
203
|
+
|
|
204
|
+
async getCompetitorPrices(barcode) {
|
|
205
|
+
// Bu endpoint resmi API'de yok, web scraping gerekir
|
|
206
|
+
// Alternatif: Brave Search API ile rakip aramasฤฑ
|
|
207
|
+
try {
|
|
208
|
+
// Trendyol'da aynฤฑ รผrรผnรผ satan diฤer satฤฑcฤฑlarฤฑ bul
|
|
209
|
+
const searchUrl = `https://www.trendyol.com/sr?q=${barcode}`;
|
|
210
|
+
// Not: Bu gerรงek scraping gerektirir, burada placeholder
|
|
211
|
+
return {
|
|
212
|
+
success: true,
|
|
213
|
+
competitors: [],
|
|
214
|
+
message: 'Rakip analizi iรงin web_search tool kullanฤฑn'
|
|
215
|
+
};
|
|
216
|
+
} catch (error) {
|
|
217
|
+
return { success: false, error: error.message };
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
222
|
+
// SORU & CEVAP
|
|
223
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
224
|
+
|
|
225
|
+
async getQuestions(params = {}) {
|
|
226
|
+
const { status = 'WAITING_FOR_ANSWER', page = 0, size = 50 } = params;
|
|
227
|
+
return await this._request('GET', `/suppliers/${this.supplierId}/questions/filter`, null, {
|
|
228
|
+
status, page, size
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async answerQuestion(questionId, answer) {
|
|
233
|
+
return await this._request('POST', `/suppliers/${this.supplierId}/questions/${questionId}/answers`, {
|
|
234
|
+
text: answer
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
239
|
+
// WEBHOOK
|
|
240
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
241
|
+
|
|
242
|
+
async getWebhooks() {
|
|
243
|
+
return await this._request('GET', `/suppliers/${this.supplierId}/webhooks`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async createWebhook(url, events = ['order.created', 'order.shipped']) {
|
|
247
|
+
return await this._request('POST', `/suppliers/${this.supplierId}/webhooks`, {
|
|
248
|
+
url,
|
|
249
|
+
events
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
254
|
+
// YARDIMCI METODLAR
|
|
255
|
+
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
256
|
+
|
|
257
|
+
async testConnection() {
|
|
258
|
+
const result = await this.getProducts({ page: 0, size: 1 });
|
|
259
|
+
return result.success;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
isConnected() {
|
|
263
|
+
return !!(this.supplierId && this.apiKey && this.apiSecret);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
formatProduct(raw) {
|
|
267
|
+
return {
|
|
268
|
+
id: raw.id,
|
|
269
|
+
barcode: raw.barcode,
|
|
270
|
+
title: raw.title,
|
|
271
|
+
brand: raw.brand,
|
|
272
|
+
category: raw.categoryName,
|
|
273
|
+
price: raw.salePrice,
|
|
274
|
+
listPrice: raw.listPrice,
|
|
275
|
+
stock: raw.quantity,
|
|
276
|
+
images: raw.images?.map(i => i.url) || [],
|
|
277
|
+
approved: raw.approved,
|
|
278
|
+
onSale: raw.onSale,
|
|
279
|
+
url: `https://www.trendyol.com/p/-p-${raw.id}`
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Singleton instance
|
|
285
|
+
let instance = null;
|
|
286
|
+
|
|
287
|
+
export const trendyolApi = {
|
|
288
|
+
init(config) {
|
|
289
|
+
instance = new TrendyolAPI(config);
|
|
290
|
+
return instance;
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
getInstance() {
|
|
294
|
+
return instance;
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
isConnected() {
|
|
298
|
+
return instance?.isConnected() || false;
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
// Proxy methods
|
|
302
|
+
async getProducts(params) { return instance?.getProducts(params); },
|
|
303
|
+
async updatePrice(barcode, price) { return instance?.updatePrice(barcode, price); },
|
|
304
|
+
async updateStock(barcode, qty) { return instance?.updateStock(barcode, qty); },
|
|
305
|
+
async getOrders(params) { return instance?.getOrders(params); },
|
|
306
|
+
async getCompetitors(barcode) { return instance?.getCompetitorPrices(barcode); }
|
|
307
|
+
};
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ๐ AKILLI UYARI SฤฐSTEMฤฐ
|
|
3
|
+
* Stok, fiyat ve sipariล uyarฤฑlarฤฑ
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const DEFAULT_THRESHOLDS = {
|
|
7
|
+
criticalStock: 5, // Kritik stok seviyesi
|
|
8
|
+
lowStock: 20, // Dรผลรผk stok uyarฤฑsฤฑ
|
|
9
|
+
priceDropPercent: 10, // Rakip fiyat dรผลรผลรผ uyarฤฑsฤฑ
|
|
10
|
+
orderDelay: 24, // Sipariล gecikme saati
|
|
11
|
+
reviewNegative: 3 // Negatif yorum eลiฤi
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export class AlertService {
|
|
15
|
+
constructor(config = {}) {
|
|
16
|
+
this.thresholds = { ...DEFAULT_THRESHOLDS, ...config.thresholds };
|
|
17
|
+
this.subscribers = [];
|
|
18
|
+
this.alerts = [];
|
|
19
|
+
this.notifiedToday = new Set(); // Aynฤฑ uyarฤฑyฤฑ tekrar gรถnderme
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Uyarฤฑ ekle
|
|
24
|
+
*/
|
|
25
|
+
addAlert(alert) {
|
|
26
|
+
const key = `${alert.type}-${alert.productId || alert.orderId || 'general'}`;
|
|
27
|
+
|
|
28
|
+
// Bugรผn zaten bildirildi mi?
|
|
29
|
+
if (this.notifiedToday.has(key)) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this.alerts.push({
|
|
34
|
+
...alert,
|
|
35
|
+
id: `alert-${Date.now()}`,
|
|
36
|
+
timestamp: new Date().toISOString(),
|
|
37
|
+
read: false
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
this.notifiedToday.add(key);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Stok kontrolรผ
|
|
46
|
+
*/
|
|
47
|
+
async checkStock(products, platform) {
|
|
48
|
+
const alerts = [];
|
|
49
|
+
|
|
50
|
+
for (const product of products) {
|
|
51
|
+
const stock = product.quantity || product.stock || product.availableStock || 0;
|
|
52
|
+
const name = product.title || product.name || product.barcode;
|
|
53
|
+
|
|
54
|
+
if (stock <= 0) {
|
|
55
|
+
alerts.push({
|
|
56
|
+
type: 'stock_out',
|
|
57
|
+
severity: 'critical',
|
|
58
|
+
icon: '๐ด',
|
|
59
|
+
message: `Stok bitti: ${name}`,
|
|
60
|
+
productId: product.barcode || product.id,
|
|
61
|
+
platform,
|
|
62
|
+
value: stock
|
|
63
|
+
});
|
|
64
|
+
} else if (stock <= this.thresholds.criticalStock) {
|
|
65
|
+
alerts.push({
|
|
66
|
+
type: 'stock_critical',
|
|
67
|
+
severity: 'high',
|
|
68
|
+
icon: '๐ ',
|
|
69
|
+
message: `Kritik stok (${stock}): ${name}`,
|
|
70
|
+
productId: product.barcode || product.id,
|
|
71
|
+
platform,
|
|
72
|
+
value: stock
|
|
73
|
+
});
|
|
74
|
+
} else if (stock <= this.thresholds.lowStock) {
|
|
75
|
+
alerts.push({
|
|
76
|
+
type: 'stock_low',
|
|
77
|
+
severity: 'medium',
|
|
78
|
+
icon: '๐ก',
|
|
79
|
+
message: `Dรผลรผk stok (${stock}): ${name}`,
|
|
80
|
+
productId: product.barcode || product.id,
|
|
81
|
+
platform,
|
|
82
|
+
value: stock
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
alerts.forEach(a => this.addAlert(a));
|
|
88
|
+
return alerts;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Rakip fiyat kontrolรผ
|
|
93
|
+
*/
|
|
94
|
+
async checkCompetitorPrices(comparisons) {
|
|
95
|
+
const alerts = [];
|
|
96
|
+
|
|
97
|
+
for (const comp of comparisons) {
|
|
98
|
+
const { product, ourPrice, lowestCompetitor, competitorName } = comp;
|
|
99
|
+
const diff = ((ourPrice - lowestCompetitor) / ourPrice) * 100;
|
|
100
|
+
|
|
101
|
+
if (lowestCompetitor < ourPrice && diff > this.thresholds.priceDropPercent) {
|
|
102
|
+
alerts.push({
|
|
103
|
+
type: 'competitor_undercut',
|
|
104
|
+
severity: 'high',
|
|
105
|
+
icon: '๐ธ',
|
|
106
|
+
message: `Rakip ${Math.round(diff)}% ucuz: ${product.name || product.barcode}`,
|
|
107
|
+
productId: product.barcode,
|
|
108
|
+
competitor: competitorName,
|
|
109
|
+
ourPrice,
|
|
110
|
+
competitorPrice: lowestCompetitor,
|
|
111
|
+
suggestion: `Fiyatฤฑ ${lowestCompetitor * 0.99}โบ'ye dรผลรผrmeyi dรผลรผnรผn`
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
alerts.forEach(a => this.addAlert(a));
|
|
117
|
+
return alerts;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Sipariล gecikme kontrolรผ
|
|
122
|
+
*/
|
|
123
|
+
async checkOrderDelays(orders) {
|
|
124
|
+
const alerts = [];
|
|
125
|
+
const now = Date.now();
|
|
126
|
+
|
|
127
|
+
for (const order of orders) {
|
|
128
|
+
const createdAt = new Date(order.createdDate || order.orderDate).getTime();
|
|
129
|
+
const hoursAgo = (now - createdAt) / (1000 * 60 * 60);
|
|
130
|
+
|
|
131
|
+
const status = order.status?.toLowerCase();
|
|
132
|
+
const isPending = ['created', 'new', 'pending', 'yeni'].some(s => status?.includes(s));
|
|
133
|
+
|
|
134
|
+
if (isPending && hoursAgo > this.thresholds.orderDelay) {
|
|
135
|
+
alerts.push({
|
|
136
|
+
type: 'order_delayed',
|
|
137
|
+
severity: 'high',
|
|
138
|
+
icon: 'โฐ',
|
|
139
|
+
message: `${Math.round(hoursAgo)} saat bekleyen sipariล: #${order.orderNumber || order.id}`,
|
|
140
|
+
orderId: order.id,
|
|
141
|
+
hoursDelayed: Math.round(hoursAgo),
|
|
142
|
+
suggestion: 'Sipariลi hazฤฑrlayฤฑn veya iptal edin'
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
alerts.forEach(a => this.addAlert(a));
|
|
148
|
+
return alerts;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Tรผm uyarฤฑlarฤฑ formatla
|
|
153
|
+
*/
|
|
154
|
+
formatAlerts(alerts) {
|
|
155
|
+
if (alerts.length === 0) {
|
|
156
|
+
return 'โ
Herhangi bir uyarฤฑ yok!';
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const grouped = {
|
|
160
|
+
critical: alerts.filter(a => a.severity === 'critical'),
|
|
161
|
+
high: alerts.filter(a => a.severity === 'high'),
|
|
162
|
+
medium: alerts.filter(a => a.severity === 'medium'),
|
|
163
|
+
low: alerts.filter(a => a.severity === 'low')
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
let message = '๐ **Uyarฤฑ รzeti**\n\n';
|
|
167
|
+
|
|
168
|
+
if (grouped.critical.length > 0) {
|
|
169
|
+
message += `๐ด **Kritik (${grouped.critical.length})**\n`;
|
|
170
|
+
grouped.critical.forEach(a => {
|
|
171
|
+
message += ` โข ${a.message}\n`;
|
|
172
|
+
});
|
|
173
|
+
message += '\n';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (grouped.high.length > 0) {
|
|
177
|
+
message += `๐ **Yรผksek (${grouped.high.length})**\n`;
|
|
178
|
+
grouped.high.slice(0, 5).forEach(a => {
|
|
179
|
+
message += ` โข ${a.message}\n`;
|
|
180
|
+
});
|
|
181
|
+
if (grouped.high.length > 5) {
|
|
182
|
+
message += ` ... ve ${grouped.high.length - 5} uyarฤฑ daha\n`;
|
|
183
|
+
}
|
|
184
|
+
message += '\n';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (grouped.medium.length > 0) {
|
|
188
|
+
message += `๐ก **Orta (${grouped.medium.length})**\n`;
|
|
189
|
+
grouped.medium.slice(0, 3).forEach(a => {
|
|
190
|
+
message += ` โข ${a.message}\n`;
|
|
191
|
+
});
|
|
192
|
+
if (grouped.medium.length > 3) {
|
|
193
|
+
message += ` ... ve ${grouped.medium.length - 3} uyarฤฑ daha\n`;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return message;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Okunmamฤฑล uyarฤฑ sayฤฑsฤฑ
|
|
202
|
+
*/
|
|
203
|
+
getUnreadCount() {
|
|
204
|
+
return this.alerts.filter(a => !a.read).length;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Uyarฤฑlarฤฑ okundu iลaretle
|
|
209
|
+
*/
|
|
210
|
+
markAllRead() {
|
|
211
|
+
this.alerts.forEach(a => a.read = true);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Gรผnlรผk cache'i temizle
|
|
216
|
+
*/
|
|
217
|
+
resetDaily() {
|
|
218
|
+
this.notifiedToday.clear();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export const alertTool = {
|
|
223
|
+
name: 'alert',
|
|
224
|
+
|
|
225
|
+
async execute(params, context) {
|
|
226
|
+
const { action, type } = params;
|
|
227
|
+
const alertService = new AlertService();
|
|
228
|
+
|
|
229
|
+
switch (action) {
|
|
230
|
+
case 'check':
|
|
231
|
+
// Tรผm kontrolleri yap ve uyarฤฑlarฤฑ topla
|
|
232
|
+
const allAlerts = [];
|
|
233
|
+
// TODO: Platform API'lerinden veri รงek ve kontrol et
|
|
234
|
+
return { success: true, alerts: allAlerts };
|
|
235
|
+
|
|
236
|
+
case 'list':
|
|
237
|
+
return {
|
|
238
|
+
success: true,
|
|
239
|
+
alerts: alertService.alerts,
|
|
240
|
+
unread: alertService.getUnreadCount()
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
case 'clear':
|
|
244
|
+
alertService.markAllRead();
|
|
245
|
+
return { success: true };
|
|
246
|
+
|
|
247
|
+
default:
|
|
248
|
+
return { success: false, error: 'Geรงersiz action' };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
export default AlertService;
|