@lonca/trendyol 0.3.0 → 0.5.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 +172 -14
- package/dist/index.cjs +1182 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +959 -1
- package/dist/index.d.ts +959 -1
- package/dist/index.js +1175 -49
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -232,6 +232,217 @@ function extractAttributeList(data) {
|
|
|
232
232
|
}
|
|
233
233
|
return null;
|
|
234
234
|
}
|
|
235
|
+
function toIso(epochMs) {
|
|
236
|
+
if (typeof epochMs !== "number" || !Number.isFinite(epochMs) || epochMs === 0) {
|
|
237
|
+
return void 0;
|
|
238
|
+
}
|
|
239
|
+
return new Date(epochMs).toISOString();
|
|
240
|
+
}
|
|
241
|
+
function normalizeClaim(node) {
|
|
242
|
+
const out = {
|
|
243
|
+
id: node.id ?? node.claimId ?? "",
|
|
244
|
+
orderNumber: node.orderNumber ?? "",
|
|
245
|
+
raw: node
|
|
246
|
+
};
|
|
247
|
+
const orderDate = toIso(node.orderDate);
|
|
248
|
+
if (orderDate) out.orderDate = orderDate;
|
|
249
|
+
const claimDate = toIso(node.claimDate);
|
|
250
|
+
if (claimDate) out.claimDate = claimDate;
|
|
251
|
+
if (node.customerFirstName !== void 0) out.customerFirstName = node.customerFirstName;
|
|
252
|
+
if (node.customerLastName !== void 0) out.customerLastName = node.customerLastName;
|
|
253
|
+
return out;
|
|
254
|
+
}
|
|
255
|
+
var ClaimsResource = class {
|
|
256
|
+
constructor(transport, sellerId, limiter) {
|
|
257
|
+
this.transport = transport;
|
|
258
|
+
this.sellerId = sellerId;
|
|
259
|
+
this.limiter = limiter ?? new TokenBucketRateLimiter({ capacity: 1e3, intervalMs: 6e4 });
|
|
260
|
+
}
|
|
261
|
+
transport;
|
|
262
|
+
sellerId;
|
|
263
|
+
limiter;
|
|
264
|
+
/**
|
|
265
|
+
* Create a return claim against an order. Use this to file a return on
|
|
266
|
+
* behalf of a customer (e.g. when they called your CS line). For
|
|
267
|
+
* customer-initiated returns coming from trendyol.com, you receive them
|
|
268
|
+
* via `claims.list()` — no need to call `create`.
|
|
269
|
+
*
|
|
270
|
+
* Returns whatever Trendyol returns (typically the new claim's identifier).
|
|
271
|
+
*
|
|
272
|
+
* @throws {ValidationError} when `claimItems` is empty.
|
|
273
|
+
*/
|
|
274
|
+
async create(input) {
|
|
275
|
+
if (!Array.isArray(input?.claimItems) || input.claimItems.length === 0) {
|
|
276
|
+
throw new ValidationError({ message: "claims.create: claimItems must not be empty" });
|
|
277
|
+
}
|
|
278
|
+
return this.transport.request({
|
|
279
|
+
method: "POST",
|
|
280
|
+
path: `/integration/order/sellers/${this.sellerId}/claims/create`,
|
|
281
|
+
body: input,
|
|
282
|
+
rateLimiter: this.limiter
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* File a seller-side rejection ("ret talebi") against a customer claim.
|
|
287
|
+
*
|
|
288
|
+
* **Wire format: `multipart/form-data`** — the SDK builds the FormData
|
|
289
|
+
* internally from the typed input. `claimItemIdList` is joined with
|
|
290
|
+
* commas (Trendyol expects a single comma-separated string field).
|
|
291
|
+
* Attach supporting docs (PDF / JPEG) via `files: [Blob, ...]`.
|
|
292
|
+
*/
|
|
293
|
+
async createIssue(claimId, input) {
|
|
294
|
+
if (!Array.isArray(input?.claimItemIdList) || input.claimItemIdList.length === 0) {
|
|
295
|
+
throw new ValidationError({
|
|
296
|
+
message: "claims.createIssue: claimItemIdList must not be empty"
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
if (typeof input.description !== "string" || input.description.length === 0) {
|
|
300
|
+
throw new ValidationError({ message: "claims.createIssue: description is required" });
|
|
301
|
+
}
|
|
302
|
+
if (input.description.length > 500) {
|
|
303
|
+
throw new ValidationError({
|
|
304
|
+
message: `claims.createIssue: description must be \u2264500 chars (got ${input.description.length})`
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
const form = new FormData();
|
|
308
|
+
form.append("claimIssueReasonId", String(input.claimIssueReasonId));
|
|
309
|
+
form.append("claimItemIdList", input.claimItemIdList.join(","));
|
|
310
|
+
form.append("description", input.description);
|
|
311
|
+
if (input.files) {
|
|
312
|
+
for (const file of input.files) {
|
|
313
|
+
form.append("files", file);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return this.transport.request({
|
|
317
|
+
method: "POST",
|
|
318
|
+
path: `/integration/order/sellers/${this.sellerId}/claims/${encodeURIComponent(claimId)}/issue`,
|
|
319
|
+
body: form,
|
|
320
|
+
rateLimiter: this.limiter
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Approve specific claim line items. After approval, Trendyol moves
|
|
325
|
+
* those line items into the post-approval refund / return-shipping flow.
|
|
326
|
+
*
|
|
327
|
+
* @throws {ValidationError} when `claimLineItemIdList` is empty.
|
|
328
|
+
*/
|
|
329
|
+
async approveLineItems(claimId, input) {
|
|
330
|
+
if (!Array.isArray(input?.claimLineItemIdList) || input.claimLineItemIdList.length === 0) {
|
|
331
|
+
throw new ValidationError({
|
|
332
|
+
message: "claims.approveLineItems: claimLineItemIdList must not be empty"
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
return this.transport.request({
|
|
336
|
+
method: "PUT",
|
|
337
|
+
path: `/integration/order/sellers/${this.sellerId}/claims/${encodeURIComponent(claimId)}/items/approve`,
|
|
338
|
+
body: input,
|
|
339
|
+
rateLimiter: this.limiter
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* List claims (page-based; SDK exposes opaque cursor convention).
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
* ```ts
|
|
347
|
+
* import { paginate } from '@lonca/core';
|
|
348
|
+
* for await (const c of paginate((p) =>
|
|
349
|
+
* client.claims.list({ ...p, claimItemStatus: 'WaitingInAction' }),
|
|
350
|
+
* )) {
|
|
351
|
+
* console.log(c.id, c.orderNumber, c.claimDate);
|
|
352
|
+
* }
|
|
353
|
+
* ```
|
|
354
|
+
*/
|
|
355
|
+
async list(params = {}) {
|
|
356
|
+
const size = Math.min(params.limit ?? 50, 200);
|
|
357
|
+
const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
|
|
358
|
+
const query = { page, size };
|
|
359
|
+
if (params.startDate) query.startDate = params.startDate.getTime();
|
|
360
|
+
if (params.endDate) query.endDate = params.endDate.getTime();
|
|
361
|
+
if (params.claimItemStatus) query.claimItemStatus = params.claimItemStatus;
|
|
362
|
+
const data = await this.transport.request({
|
|
363
|
+
method: "GET",
|
|
364
|
+
path: `/integration/order/sellers/${this.sellerId}/claims`,
|
|
365
|
+
query,
|
|
366
|
+
rateLimiter: this.limiter
|
|
367
|
+
});
|
|
368
|
+
const items = (data.content ?? []).map(normalizeClaim);
|
|
369
|
+
const result = { items };
|
|
370
|
+
const totalPages = typeof data.totalPages === "number" ? data.totalPages : 0;
|
|
371
|
+
if (page + 1 < totalPages) {
|
|
372
|
+
result.nextCursor = String(page + 1);
|
|
373
|
+
}
|
|
374
|
+
return result;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Fetch the catalog of rejection-reason IDs the seller can use on
|
|
378
|
+
* `claims.createIssue()`. Cache the result — it changes rarely.
|
|
379
|
+
*
|
|
380
|
+
* Note: this endpoint is **not seller-scoped** (no `sellerId` in path).
|
|
381
|
+
*/
|
|
382
|
+
async getIssueReasons() {
|
|
383
|
+
const data = await this.transport.request({
|
|
384
|
+
method: "GET",
|
|
385
|
+
path: `/integration/order/claim-issue-reasons`,
|
|
386
|
+
rateLimiter: this.limiter
|
|
387
|
+
});
|
|
388
|
+
return (data ?? []).map((r) => ({
|
|
389
|
+
id: typeof r.id === "number" ? r.id : 0,
|
|
390
|
+
name: r.name ?? ""
|
|
391
|
+
}));
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Fetch the audit log for a single claim item (state transitions,
|
|
395
|
+
* actor, timestamp). Trendyol's response shape varies — the SDK
|
|
396
|
+
* surfaces each row as `{ raw }` and leaves field extraction to the
|
|
397
|
+
* caller until we observe a stable shape on the wire.
|
|
398
|
+
*/
|
|
399
|
+
async getItemAudits(claimItemId) {
|
|
400
|
+
const data = await this.transport.request({
|
|
401
|
+
method: "GET",
|
|
402
|
+
path: `/integration/order/sellers/${this.sellerId}/claims/items/${encodeURIComponent(claimItemId)}/audit`,
|
|
403
|
+
rateLimiter: this.limiter
|
|
404
|
+
});
|
|
405
|
+
const rows = Array.isArray(data) ? data : Array.isArray(data?.content) ? data.content : [];
|
|
406
|
+
return rows.map((row) => ({ raw: row }));
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
var FinanceResource = class {
|
|
410
|
+
constructor(transport, sellerId, limiter) {
|
|
411
|
+
this.transport = transport;
|
|
412
|
+
this.sellerId = sellerId;
|
|
413
|
+
this.limiter = limiter ?? new TokenBucketRateLimiter({ capacity: 100, intervalMs: 6e4 });
|
|
414
|
+
}
|
|
415
|
+
transport;
|
|
416
|
+
sellerId;
|
|
417
|
+
limiter;
|
|
418
|
+
async getSettlements(params = {}) {
|
|
419
|
+
return this.queryPage(`/integration/sellers/${this.sellerId}/settlements`, params);
|
|
420
|
+
}
|
|
421
|
+
async getOtherFinancials(params = {}) {
|
|
422
|
+
return this.queryPage(`/integration/sellers/${this.sellerId}/otherfinancials`, params);
|
|
423
|
+
}
|
|
424
|
+
async queryPage(path, params) {
|
|
425
|
+
const size = Math.min(params.limit ?? 50, 200);
|
|
426
|
+
const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
|
|
427
|
+
const query = { page, size };
|
|
428
|
+
if (params.startDate) query.startDate = params.startDate.getTime();
|
|
429
|
+
if (params.endDate) query.endDate = params.endDate.getTime();
|
|
430
|
+
if (params.transactionType) query.transactionType = params.transactionType;
|
|
431
|
+
const data = await this.transport.request({
|
|
432
|
+
method: "GET",
|
|
433
|
+
path,
|
|
434
|
+
query,
|
|
435
|
+
rateLimiter: this.limiter
|
|
436
|
+
});
|
|
437
|
+
const items = (data.content ?? []).map((row) => ({ raw: row }));
|
|
438
|
+
const result = { items };
|
|
439
|
+
const totalPages = typeof data.totalPages === "number" ? data.totalPages : 0;
|
|
440
|
+
if (page + 1 < totalPages) {
|
|
441
|
+
result.nextCursor = String(page + 1);
|
|
442
|
+
}
|
|
443
|
+
return result;
|
|
444
|
+
}
|
|
445
|
+
};
|
|
235
446
|
var MAX_ITEMS_PER_REQUEST = 1e3;
|
|
236
447
|
var InventoryResource = class {
|
|
237
448
|
constructor(transport, sellerId) {
|
|
@@ -271,9 +482,201 @@ var InventoryResource = class {
|
|
|
271
482
|
return { batchRequestId: data.batchRequestId ?? "" };
|
|
272
483
|
}
|
|
273
484
|
};
|
|
485
|
+
var InvoicesResource = class {
|
|
486
|
+
constructor(transport, sellerId, limiter) {
|
|
487
|
+
this.transport = transport;
|
|
488
|
+
this.sellerId = sellerId;
|
|
489
|
+
this.limiter = limiter ?? new TokenBucketRateLimiter({ capacity: 500, intervalMs: 6e4 });
|
|
490
|
+
}
|
|
491
|
+
transport;
|
|
492
|
+
sellerId;
|
|
493
|
+
limiter;
|
|
494
|
+
/**
|
|
495
|
+
* Upload an invoice file for a shipment package. **Multipart**: the
|
|
496
|
+
* SDK builds the FormData internally from the typed input.
|
|
497
|
+
*
|
|
498
|
+
* Max 10 MB. Accepted formats: PDF, JPEG, PNG.
|
|
499
|
+
*/
|
|
500
|
+
async uploadFile(input) {
|
|
501
|
+
if (!input?.file) {
|
|
502
|
+
throw new ValidationError({ message: "invoices.uploadFile: file is required" });
|
|
503
|
+
}
|
|
504
|
+
if (!input.shipmentPackageId) {
|
|
505
|
+
throw new ValidationError({ message: "invoices.uploadFile: shipmentPackageId is required" });
|
|
506
|
+
}
|
|
507
|
+
const form = new FormData();
|
|
508
|
+
form.append("shipmentPackageId", String(input.shipmentPackageId));
|
|
509
|
+
form.append("file", input.file);
|
|
510
|
+
if (input.invoiceDateTime !== void 0) {
|
|
511
|
+
form.append("invoiceDateTime", String(input.invoiceDateTime));
|
|
512
|
+
}
|
|
513
|
+
if (input.invoiceNumber !== void 0) {
|
|
514
|
+
form.append("invoiceNumber", input.invoiceNumber);
|
|
515
|
+
}
|
|
516
|
+
return this.transport.request({
|
|
517
|
+
method: "POST",
|
|
518
|
+
path: `/integration/sellers/${this.sellerId}/seller-invoice-file`,
|
|
519
|
+
body: form,
|
|
520
|
+
rateLimiter: this.limiter
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
/** Register an invoice URL with Trendyol (alternative to uploading the file). */
|
|
524
|
+
async sendLink(input) {
|
|
525
|
+
if (!input?.invoiceLink) {
|
|
526
|
+
throw new ValidationError({ message: "invoices.sendLink: invoiceLink is required" });
|
|
527
|
+
}
|
|
528
|
+
return this.transport.request({
|
|
529
|
+
method: "POST",
|
|
530
|
+
path: `/integration/sellers/${this.sellerId}/seller-invoice-links`,
|
|
531
|
+
body: input,
|
|
532
|
+
rateLimiter: this.limiter
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
/** Remove a previously-registered invoice link. */
|
|
536
|
+
async deleteLink(input) {
|
|
537
|
+
return this.transport.request({
|
|
538
|
+
method: "POST",
|
|
539
|
+
path: `/integration/sellers/${this.sellerId}/seller-invoice-links/delete`,
|
|
540
|
+
body: input,
|
|
541
|
+
rateLimiter: this.limiter
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
var LabelsResource = class {
|
|
546
|
+
constructor(transport, sellerId, limiter) {
|
|
547
|
+
this.transport = transport;
|
|
548
|
+
this.sellerId = sellerId;
|
|
549
|
+
this.limiter = limiter ?? new TokenBucketRateLimiter({ capacity: 100, intervalMs: 6e4 });
|
|
550
|
+
}
|
|
551
|
+
transport;
|
|
552
|
+
sellerId;
|
|
553
|
+
limiter;
|
|
554
|
+
/**
|
|
555
|
+
* Request a common ZPL label for a cargo tracking number. After this
|
|
556
|
+
* returns, call `getCommon()` with the same `cargoTrackingNumber` to
|
|
557
|
+
* retrieve the generated label.
|
|
558
|
+
*
|
|
559
|
+
* @throws {ValidationError} when `format` is missing.
|
|
560
|
+
*/
|
|
561
|
+
async createCommon(cargoTrackingNumber, input) {
|
|
562
|
+
if (!input?.format) {
|
|
563
|
+
throw new ValidationError({ message: "labels.createCommon: format is required" });
|
|
564
|
+
}
|
|
565
|
+
return this.transport.request({
|
|
566
|
+
method: "POST",
|
|
567
|
+
path: `/integration/sellers/${this.sellerId}/common-label/${encodeURIComponent(String(cargoTrackingNumber))}`,
|
|
568
|
+
body: input,
|
|
569
|
+
rateLimiter: this.limiter
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
/** Retrieve the previously-created common label. */
|
|
573
|
+
async getCommon(cargoTrackingNumber) {
|
|
574
|
+
const data = await this.transport.request({
|
|
575
|
+
method: "GET",
|
|
576
|
+
path: `/integration/sellers/${this.sellerId}/common-label/${encodeURIComponent(String(cargoTrackingNumber))}`,
|
|
577
|
+
rateLimiter: this.limiter
|
|
578
|
+
});
|
|
579
|
+
return { raw: data ?? {} };
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
function n(rows, extract) {
|
|
583
|
+
const list = Array.isArray(rows) ? rows : [];
|
|
584
|
+
return list.map((r) => {
|
|
585
|
+
const node = r;
|
|
586
|
+
return { ...extract(node), raw: node };
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
var LocationsResource = class {
|
|
590
|
+
constructor(transport, limiter) {
|
|
591
|
+
this.transport = transport;
|
|
592
|
+
this.limiter = limiter ?? new TokenBucketRateLimiter({ capacity: 60, intervalMs: 6e4 });
|
|
593
|
+
}
|
|
594
|
+
transport;
|
|
595
|
+
limiter;
|
|
596
|
+
/** List all supported countries (Türkiye + AZ + GULF + CEE). */
|
|
597
|
+
async getCountries() {
|
|
598
|
+
const data = await this.transport.request({
|
|
599
|
+
method: "GET",
|
|
600
|
+
path: `/integration/member/countries`,
|
|
601
|
+
rateLimiter: this.limiter
|
|
602
|
+
});
|
|
603
|
+
return n(data, (node) => ({
|
|
604
|
+
code: String(node.code ?? node.id ?? ""),
|
|
605
|
+
name: node.name
|
|
606
|
+
}));
|
|
607
|
+
}
|
|
608
|
+
// ─── Domestic (TR / AZ) ───────────────────────────────────────────────
|
|
609
|
+
async getTurkeyCities() {
|
|
610
|
+
return this.cities(`/integration/member/countries/domestic/TR/cities`);
|
|
611
|
+
}
|
|
612
|
+
async getTurkeyDistricts(cityCode) {
|
|
613
|
+
return this.districts(
|
|
614
|
+
`/integration/member/countries/domestic/TR/cities/${encodeURIComponent(String(cityCode))}/districts`
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
async getTurkeyNeighborhoods(cityCode, districtCode) {
|
|
618
|
+
return this.neighborhoods(
|
|
619
|
+
`/integration/member/countries/domestic/TR/cities/${encodeURIComponent(String(cityCode))}/districts/${encodeURIComponent(String(districtCode))}/neighborhoods`
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
async getAzerbaijanCities() {
|
|
623
|
+
return this.cities(`/integration/member/countries/domestic/AZ/cities`);
|
|
624
|
+
}
|
|
625
|
+
async getAzerbaijanDistricts(cityCode) {
|
|
626
|
+
return this.districts(
|
|
627
|
+
`/integration/member/countries/domestic/AZ/cities/${encodeURIComponent(String(cityCode))}/districts`
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
// ─── International (GULF / CEE) ───────────────────────────────────────
|
|
631
|
+
async getCitiesByCountry(countryCode) {
|
|
632
|
+
return this.cities(`/integration/member/countries/${encodeURIComponent(countryCode)}/cities`);
|
|
633
|
+
}
|
|
634
|
+
async getDistrictsByCity(countryCode, cityId) {
|
|
635
|
+
return this.districts(
|
|
636
|
+
`/integration/member/countries/${encodeURIComponent(countryCode)}/cities/${encodeURIComponent(String(cityId))}/districts`
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
// ─── Shared paginators ────────────────────────────────────────────────
|
|
640
|
+
async cities(path) {
|
|
641
|
+
const data = await this.transport.request({
|
|
642
|
+
method: "GET",
|
|
643
|
+
path,
|
|
644
|
+
rateLimiter: this.limiter
|
|
645
|
+
});
|
|
646
|
+
return n(data, (node) => ({
|
|
647
|
+
code: String(node.code ?? node.id ?? ""),
|
|
648
|
+
name: node.name,
|
|
649
|
+
countryCode: node.countryCode
|
|
650
|
+
}));
|
|
651
|
+
}
|
|
652
|
+
async districts(path) {
|
|
653
|
+
const data = await this.transport.request({
|
|
654
|
+
method: "GET",
|
|
655
|
+
path,
|
|
656
|
+
rateLimiter: this.limiter
|
|
657
|
+
});
|
|
658
|
+
return n(data, (node) => ({
|
|
659
|
+
code: String(node.code ?? node.id ?? ""),
|
|
660
|
+
name: node.name,
|
|
661
|
+
cityCode: node.cityCode !== void 0 ? String(node.cityCode) : void 0
|
|
662
|
+
}));
|
|
663
|
+
}
|
|
664
|
+
async neighborhoods(path) {
|
|
665
|
+
const data = await this.transport.request({
|
|
666
|
+
method: "GET",
|
|
667
|
+
path,
|
|
668
|
+
rateLimiter: this.limiter
|
|
669
|
+
});
|
|
670
|
+
return n(data, (node) => ({
|
|
671
|
+
code: String(node.code ?? node.id ?? ""),
|
|
672
|
+
name: node.name,
|
|
673
|
+
districtCode: node.districtCode !== void 0 ? String(node.districtCode) : void 0
|
|
674
|
+
}));
|
|
675
|
+
}
|
|
676
|
+
};
|
|
274
677
|
var MAX_PAGE_SIZE = 200;
|
|
275
678
|
var DEFAULT_PAGE_SIZE2 = 50;
|
|
276
|
-
function
|
|
679
|
+
function toIso2(epochMs) {
|
|
277
680
|
if (typeof epochMs !== "number" || !Number.isFinite(epochMs) || epochMs === 0) {
|
|
278
681
|
return void 0;
|
|
279
682
|
}
|
|
@@ -341,7 +744,7 @@ function normalizeLine(node) {
|
|
|
341
744
|
function normalizeHistory(node) {
|
|
342
745
|
const out = { raw: node };
|
|
343
746
|
if (node.status !== void 0) out.status = node.status;
|
|
344
|
-
const createdAt =
|
|
747
|
+
const createdAt = toIso2(node.createdDate);
|
|
345
748
|
if (createdAt) out.createdAt = createdAt;
|
|
346
749
|
return out;
|
|
347
750
|
}
|
|
@@ -357,13 +760,14 @@ function normalizeCustomer(node) {
|
|
|
357
760
|
return out;
|
|
358
761
|
}
|
|
359
762
|
function normalizePackage(node) {
|
|
763
|
+
const pkgId = node.shipmentPackageId ?? node.id;
|
|
360
764
|
const out = {
|
|
361
|
-
id:
|
|
765
|
+
id: pkgId !== void 0 ? String(pkgId) : "",
|
|
362
766
|
orderNumber: node.orderNumber ?? "",
|
|
363
767
|
status: node.status ?? "",
|
|
364
768
|
customer: normalizeCustomer(node),
|
|
365
|
-
orderDate:
|
|
366
|
-
lastModifiedDate:
|
|
769
|
+
orderDate: toIso2(node.orderDate) ?? "",
|
|
770
|
+
lastModifiedDate: toIso2(node.lastModifiedDate) ?? "",
|
|
367
771
|
currencyCode: node.currencyCode ?? "",
|
|
368
772
|
packageTotalPrice: typeof node.packageTotalPrice === "number" ? node.packageTotalPrice : 0,
|
|
369
773
|
packageGrossAmount: typeof node.packageGrossAmount === "number" ? node.packageGrossAmount : 0,
|
|
@@ -383,13 +787,13 @@ function normalizePackage(node) {
|
|
|
383
787
|
if (node.shipmentPackageStatus !== void 0) {
|
|
384
788
|
out.shipmentPackageStatus = node.shipmentPackageStatus;
|
|
385
789
|
}
|
|
386
|
-
const agreed =
|
|
790
|
+
const agreed = toIso2(node.agreedDeliveryDate);
|
|
387
791
|
if (agreed) out.agreedDeliveryDate = agreed;
|
|
388
|
-
const estStart =
|
|
792
|
+
const estStart = toIso2(node.estimatedDeliveryStartDate);
|
|
389
793
|
if (estStart) out.estimatedDeliveryStartDate = estStart;
|
|
390
|
-
const estEnd =
|
|
794
|
+
const estEnd = toIso2(node.estimatedDeliveryEndDate);
|
|
391
795
|
if (estEnd) out.estimatedDeliveryEndDate = estEnd;
|
|
392
|
-
const origin =
|
|
796
|
+
const origin = toIso2(node.originShipmentDate);
|
|
393
797
|
if (origin) out.originShipmentDate = origin;
|
|
394
798
|
const invoice = normalizeAddress(node.invoiceAddress);
|
|
395
799
|
if (invoice) out.invoiceAddress = invoice;
|
|
@@ -415,50 +819,383 @@ function normalizePackage(node) {
|
|
|
415
819
|
if (typeof node["3pByTrendyol"] === "boolean") {
|
|
416
820
|
out.threePByTrendyol = node["3pByTrendyol"];
|
|
417
821
|
}
|
|
418
|
-
if (typeof node.containsDangerousProduct === "boolean") {
|
|
419
|
-
out.containsDangerousProduct = node.containsDangerousProduct;
|
|
822
|
+
if (typeof node.containsDangerousProduct === "boolean") {
|
|
823
|
+
out.containsDangerousProduct = node.containsDangerousProduct;
|
|
824
|
+
}
|
|
825
|
+
if (typeof node.isCod === "boolean") out.isCod = node.isCod;
|
|
826
|
+
if (typeof node.is4P === "boolean") out.is4P = node.is4P;
|
|
827
|
+
if (node.invoiceLink !== void 0) out.invoiceLink = node.invoiceLink;
|
|
828
|
+
if (node.createdBy !== void 0) out.createdBy = node.createdBy;
|
|
829
|
+
return out;
|
|
830
|
+
}
|
|
831
|
+
var OrdersResource = class {
|
|
832
|
+
constructor(transport, sellerId, limiter) {
|
|
833
|
+
this.transport = transport;
|
|
834
|
+
this.sellerId = sellerId;
|
|
835
|
+
this.limiter = limiter ?? new TokenBucketRateLimiter({ capacity: 1e3, intervalMs: 6e4 });
|
|
836
|
+
}
|
|
837
|
+
transport;
|
|
838
|
+
sellerId;
|
|
839
|
+
limiter;
|
|
840
|
+
/**
|
|
841
|
+
* List shipment packages for the seller.
|
|
842
|
+
*
|
|
843
|
+
* @example
|
|
844
|
+
* ```ts
|
|
845
|
+
* import { paginate } from '@lonca/core';
|
|
846
|
+
* for await (const pkg of paginate((p) => client.orders.list({ ...p, status: 'Created' }))) {
|
|
847
|
+
* console.log(pkg.id, pkg.status, pkg.customer.firstName);
|
|
848
|
+
* }
|
|
849
|
+
* ```
|
|
850
|
+
*/
|
|
851
|
+
async list(params = {}) {
|
|
852
|
+
const size = Math.min(params.limit ?? DEFAULT_PAGE_SIZE2, MAX_PAGE_SIZE);
|
|
853
|
+
const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
|
|
854
|
+
const query = { page, size };
|
|
855
|
+
if (params.status) query.status = params.status;
|
|
856
|
+
if (params.orderNumber) query.orderNumber = params.orderNumber;
|
|
857
|
+
if (params.startDate) query.startDate = params.startDate.getTime();
|
|
858
|
+
if (params.endDate) query.endDate = params.endDate.getTime();
|
|
859
|
+
const data = await this.transport.request({
|
|
860
|
+
method: "GET",
|
|
861
|
+
path: `/integration/order/sellers/${this.sellerId}/orders`,
|
|
862
|
+
query,
|
|
863
|
+
rateLimiter: this.limiter
|
|
864
|
+
});
|
|
865
|
+
const items = (data.content ?? []).map(normalizePackage);
|
|
866
|
+
const totalPages = typeof data.totalPages === "number" ? data.totalPages : 0;
|
|
867
|
+
const result = { items };
|
|
868
|
+
if (page + 1 < totalPages) {
|
|
869
|
+
result.nextCursor = String(page + 1);
|
|
870
|
+
}
|
|
871
|
+
return result;
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Push a shipment-package status update.
|
|
875
|
+
*
|
|
876
|
+
* Trendyol restricts the seller-side push to two transitions:
|
|
877
|
+
* - `Picking` — order picked up from the shelf / being prepared
|
|
878
|
+
* - `Invoiced` — invoice issued, ready for cargo handoff
|
|
879
|
+
*
|
|
880
|
+
* Other transitions (`Shipped`, `Delivered`, etc.) are driven by Trendyol
|
|
881
|
+
* or the cargo provider — call `processAlternativeDelivery` or
|
|
882
|
+
* `manualDeliverByPackageId` if you ship outside Trendyol's cargo
|
|
883
|
+
* network.
|
|
884
|
+
*
|
|
885
|
+
* Returns void; Trendyol responds with 200 + empty body on success.
|
|
886
|
+
*/
|
|
887
|
+
async updatePackageStatus(packageId, input) {
|
|
888
|
+
await this.transport.request({
|
|
889
|
+
method: "PUT",
|
|
890
|
+
path: this.packagePath(packageId),
|
|
891
|
+
body: input,
|
|
892
|
+
rateLimiter: this.limiter
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Notify Trendyol that one or more line items cannot be supplied
|
|
897
|
+
* ("Tedarik Edememe Bildirimi"). Marks the listed line IDs as
|
|
898
|
+
* `UnSupplied`. Trendyol cancels those quantities and notifies the
|
|
899
|
+
* customer.
|
|
900
|
+
*
|
|
901
|
+
* `reasonId` is a numeric code Trendyol publishes separately — consult
|
|
902
|
+
* the seller panel or the "Tedarik Edememe" docs for current values.
|
|
903
|
+
*
|
|
904
|
+
* Returns void; Trendyol responds with 200 + empty body on success.
|
|
905
|
+
*/
|
|
906
|
+
async cancelPackageItem(packageId, input) {
|
|
907
|
+
if (!Array.isArray(input?.lines) || input.lines.length === 0) {
|
|
908
|
+
throw new ValidationError({ message: "cancelPackageItem: lines must not be empty" });
|
|
909
|
+
}
|
|
910
|
+
await this.transport.request({
|
|
911
|
+
method: "PUT",
|
|
912
|
+
path: `${this.packagePath(packageId)}/items/unsupplied`,
|
|
913
|
+
body: input,
|
|
914
|
+
rateLimiter: this.limiter
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Extend the agreed delivery date for a shipment package by 1, 2, or 3 days.
|
|
919
|
+
* Trendyol enforces the [1, 3] range server-side; the SDK validates client-side
|
|
920
|
+
* to fail fast.
|
|
921
|
+
*
|
|
922
|
+
* Returns void; Trendyol responds with 200 + empty body on success.
|
|
923
|
+
*/
|
|
924
|
+
async extendDeliveryDate(packageId, extendedDayCount) {
|
|
925
|
+
if (![1, 2, 3].includes(extendedDayCount)) {
|
|
926
|
+
throw new ValidationError({
|
|
927
|
+
message: `extendDeliveryDate: extendedDayCount must be 1, 2, or 3 (got ${extendedDayCount})`
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
await this.transport.request({
|
|
931
|
+
method: "PUT",
|
|
932
|
+
path: `${this.packagePath(packageId)}/extended-agreed-delivery-date`,
|
|
933
|
+
body: { extendedDayCount },
|
|
934
|
+
rateLimiter: this.limiter
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Notify Trendyol of an alternative delivery channel — used when the
|
|
939
|
+
* seller is shipping via a non-Trendyol cargo provider. Trendyol then
|
|
940
|
+
* either SMSes the customer the tracking link (when `isPhoneNumber` is
|
|
941
|
+
* `true`) or stores the tracking URL on the package directly.
|
|
942
|
+
*
|
|
943
|
+
* Returns void; Trendyol responds with 200 + empty body on success.
|
|
944
|
+
*/
|
|
945
|
+
async processAlternativeDelivery(packageId, input) {
|
|
946
|
+
await this.transport.request({
|
|
947
|
+
method: "PUT",
|
|
948
|
+
path: `${this.packagePath(packageId)}/alternative-delivery`,
|
|
949
|
+
body: input,
|
|
950
|
+
rateLimiter: this.limiter
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* Split a shipment package by moving a set of line IDs into a new
|
|
955
|
+
* package. The original package keeps the remaining lines.
|
|
956
|
+
*
|
|
957
|
+
* @param packageId The package to split.
|
|
958
|
+
* @param orderLineIds Line IDs to move into the new package (1+).
|
|
959
|
+
* @throws {ValidationError} when `orderLineIds` is empty.
|
|
960
|
+
*/
|
|
961
|
+
async splitPackage(packageId, orderLineIds) {
|
|
962
|
+
if (!Array.isArray(orderLineIds) || orderLineIds.length === 0) {
|
|
963
|
+
throw new ValidationError({ message: "splitPackage: orderLineIds must not be empty" });
|
|
964
|
+
}
|
|
965
|
+
await this.transport.request({
|
|
966
|
+
method: "POST",
|
|
967
|
+
path: `${this.packagePath(packageId)}/split`,
|
|
968
|
+
body: { orderLineIds },
|
|
969
|
+
rateLimiter: this.limiter
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Split a shipment package by quantity. Each `QuantitySplit` entry
|
|
974
|
+
* carves a single line into multiple packages — e.g. `{ orderLineId: 100,
|
|
975
|
+
* quantities: [2, 2, 1] }` splits 5 units of line 100 into three packages
|
|
976
|
+
* of 2 + 2 + 1.
|
|
977
|
+
*
|
|
978
|
+
* @throws {ValidationError} when `quantitySplit` is empty.
|
|
979
|
+
*/
|
|
980
|
+
async splitPackageByQuantity(packageId, quantitySplit) {
|
|
981
|
+
if (!Array.isArray(quantitySplit) || quantitySplit.length === 0) {
|
|
982
|
+
throw new ValidationError({
|
|
983
|
+
message: "splitPackageByQuantity: quantitySplit must not be empty"
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
await this.transport.request({
|
|
987
|
+
method: "POST",
|
|
988
|
+
path: `${this.packagePath(packageId)}/quantity-split`,
|
|
989
|
+
body: { quantitySplit },
|
|
990
|
+
rateLimiter: this.limiter
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Split a shipment package into multiple new packages by grouping line
|
|
995
|
+
* IDs. Each `SplitGroup` becomes one new package containing the listed
|
|
996
|
+
* line IDs.
|
|
997
|
+
*
|
|
998
|
+
* @throws {ValidationError} when `splitGroups` is empty.
|
|
999
|
+
*/
|
|
1000
|
+
async multiSplitPackage(packageId, splitGroups) {
|
|
1001
|
+
if (!Array.isArray(splitGroups) || splitGroups.length === 0) {
|
|
1002
|
+
throw new ValidationError({ message: "multiSplitPackage: splitGroups must not be empty" });
|
|
1003
|
+
}
|
|
1004
|
+
await this.transport.request({
|
|
1005
|
+
method: "POST",
|
|
1006
|
+
path: `${this.packagePath(packageId)}/multi-split`,
|
|
1007
|
+
body: { splitGroups },
|
|
1008
|
+
rateLimiter: this.limiter
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Split a shipment package into multiple new packages, each containing a
|
|
1013
|
+
* mix of line items at specific quantities. This is the most expressive
|
|
1014
|
+
* split — use it when you need fine-grained control over which line IDs
|
|
1015
|
+
* and how many of each end up in each new package.
|
|
1016
|
+
*
|
|
1017
|
+
* @throws {ValidationError} when `splitPackages` is empty.
|
|
1018
|
+
*/
|
|
1019
|
+
async splitMultiPackagesByQuantity(packageId, splitPackages) {
|
|
1020
|
+
if (!Array.isArray(splitPackages) || splitPackages.length === 0) {
|
|
1021
|
+
throw new ValidationError({
|
|
1022
|
+
message: "splitMultiPackagesByQuantity: splitPackages must not be empty"
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
await this.transport.request({
|
|
1026
|
+
method: "POST",
|
|
1027
|
+
path: `${this.packagePath(packageId)}/split-packages`,
|
|
1028
|
+
body: { splitPackages },
|
|
1029
|
+
rateLimiter: this.limiter
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Change the cargo provider on an existing shipment package. Use one of
|
|
1034
|
+
* Trendyol's documented marketplace cargo codes (`'YKMP'`, `'ARASMP'`,
|
|
1035
|
+
* `'SURATMP'`, etc.) — see `TrendyolCargoProvider` for the full list.
|
|
1036
|
+
*/
|
|
1037
|
+
async changeCargoProvider(packageId, cargoProvider) {
|
|
1038
|
+
await this.transport.request({
|
|
1039
|
+
method: "PUT",
|
|
1040
|
+
path: `${this.packagePath(packageId)}/cargo-providers`,
|
|
1041
|
+
body: { cargoProvider },
|
|
1042
|
+
rateLimiter: this.limiter
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Mark a shipment package as manually delivered via its package ID.
|
|
1047
|
+
* Used when the seller delivered the order outside Trendyol's cargo
|
|
1048
|
+
* network and needs to flip the package to `Delivered` after handover.
|
|
1049
|
+
*
|
|
1050
|
+
* No request body; Trendyol responds with 200 + empty body on success.
|
|
1051
|
+
*/
|
|
1052
|
+
async manualDeliverByPackageId(packageId) {
|
|
1053
|
+
await this.transport.request({
|
|
1054
|
+
method: "PUT",
|
|
1055
|
+
path: `${this.packagePath(packageId)}/manual-invoice-delivery`,
|
|
1056
|
+
rateLimiter: this.limiter
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1060
|
+
* Manual-deliver variant that takes the cargo tracking number instead
|
|
1061
|
+
* of the package ID. Useful when you only have the tracking number on
|
|
1062
|
+
* hand (e.g., from a cargo provider webhook).
|
|
1063
|
+
*
|
|
1064
|
+
* Note the path structure: tracking number sits at a sibling location,
|
|
1065
|
+
* not under `/shipment-packages/{id}/...`.
|
|
1066
|
+
*/
|
|
1067
|
+
async manualDeliverByTrackingNumber(cargoTrackingNumber) {
|
|
1068
|
+
await this.transport.request({
|
|
1069
|
+
method: "PUT",
|
|
1070
|
+
path: `/integration/order/sellers/${this.sellerId}/shipment-packages/manual-invoice-delivery-by-tracking-number/${encodeURIComponent(String(cargoTrackingNumber))}`,
|
|
1071
|
+
rateLimiter: this.limiter
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Mark a package as delivered through an authorized service ("yetkili
|
|
1076
|
+
* servis"). For appliance / installation-required products that are
|
|
1077
|
+
* delivered + installed by a third-party service partner.
|
|
1078
|
+
*/
|
|
1079
|
+
async markDeliveredByService(packageId) {
|
|
1080
|
+
await this.transport.request({
|
|
1081
|
+
method: "PUT",
|
|
1082
|
+
path: `${this.packagePath(packageId)}/delivered-by-service`,
|
|
1083
|
+
rateLimiter: this.limiter
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Update box / packaging metadata on a shipment package (desi value
|
|
1088
|
+
* and/or number of boxes). Either field can be sent alone.
|
|
1089
|
+
*/
|
|
1090
|
+
async updateBoxInfo(packageId, input) {
|
|
1091
|
+
await this.transport.request({
|
|
1092
|
+
method: "PUT",
|
|
1093
|
+
path: `${this.packagePath(packageId)}/box-info`,
|
|
1094
|
+
body: input,
|
|
1095
|
+
rateLimiter: this.limiter
|
|
1096
|
+
});
|
|
420
1097
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
1098
|
+
/**
|
|
1099
|
+
* Update labor costs for one or more order lines.
|
|
1100
|
+
*
|
|
1101
|
+
* **Wire note:** Trendyol's request body is a raw array (no envelope),
|
|
1102
|
+
* not `{ items: [...] }`. The SDK forwards `items` verbatim.
|
|
1103
|
+
*
|
|
1104
|
+
* @throws {ValidationError} when `items` is empty.
|
|
1105
|
+
*/
|
|
1106
|
+
async updateLaborCosts(packageId, items) {
|
|
1107
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
1108
|
+
throw new ValidationError({ message: "updateLaborCosts: items must not be empty" });
|
|
1109
|
+
}
|
|
1110
|
+
await this.transport.request({
|
|
1111
|
+
method: "PUT",
|
|
1112
|
+
path: `${this.packagePath(packageId)}/labor-costs`,
|
|
1113
|
+
body: items,
|
|
1114
|
+
rateLimiter: this.limiter
|
|
1115
|
+
});
|
|
432
1116
|
}
|
|
433
|
-
transport;
|
|
434
|
-
sellerId;
|
|
435
|
-
limiter;
|
|
436
1117
|
/**
|
|
437
|
-
*
|
|
1118
|
+
* Reassign a shipment package to a different warehouse. `warehouseId`
|
|
1119
|
+
* comes from `client.suppliers.getAddresses()` (filter by `isShipmentAddress`).
|
|
1120
|
+
*/
|
|
1121
|
+
async updateWarehouse(packageId, warehouseId) {
|
|
1122
|
+
await this.transport.request({
|
|
1123
|
+
method: "PUT",
|
|
1124
|
+
path: `${this.packagePath(packageId)}/warehouse`,
|
|
1125
|
+
body: { warehouseId },
|
|
1126
|
+
rateLimiter: this.limiter
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Stream variant of `list()`. Trendyol's `getShipmentPackagesStream`
|
|
1131
|
+
* returns the same `ShipmentPackage` shape but paginates with an opaque
|
|
1132
|
+
* cursor — useful when the dataset is large and page-based pagination
|
|
1133
|
+
* would hit the 10 000-record cap.
|
|
438
1134
|
*
|
|
439
1135
|
* @example
|
|
440
1136
|
* ```ts
|
|
441
1137
|
* import { paginate } from '@lonca/core';
|
|
442
|
-
* for await (const pkg of paginate((p) =>
|
|
443
|
-
*
|
|
1138
|
+
* for await (const pkg of paginate((p) =>
|
|
1139
|
+
* client.orders.listStream({ ...p, packageItemStatuses: 'Created,Picking' }),
|
|
1140
|
+
* )) {
|
|
1141
|
+
* console.log(pkg.id, pkg.status);
|
|
444
1142
|
* }
|
|
445
1143
|
* ```
|
|
446
1144
|
*/
|
|
447
|
-
async
|
|
1145
|
+
async listStream(params = {}) {
|
|
448
1146
|
const size = Math.min(params.limit ?? DEFAULT_PAGE_SIZE2, MAX_PAGE_SIZE);
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
if (params.
|
|
452
|
-
if (params.
|
|
453
|
-
|
|
454
|
-
|
|
1147
|
+
const query = { size };
|
|
1148
|
+
if (params.cursor) query.nextCursor = params.cursor;
|
|
1149
|
+
if (params.packageItemStatuses) query.packageItemStatuses = params.packageItemStatuses;
|
|
1150
|
+
if (params.lastModifiedStartDate) {
|
|
1151
|
+
query.lastModifiedStartDate = params.lastModifiedStartDate.getTime();
|
|
1152
|
+
}
|
|
1153
|
+
if (params.lastModifiedEndDate) {
|
|
1154
|
+
query.lastModifiedEndDate = params.lastModifiedEndDate.getTime();
|
|
1155
|
+
}
|
|
455
1156
|
const data = await this.transport.request({
|
|
456
1157
|
method: "GET",
|
|
457
|
-
path: `/integration/order/sellers/${this.sellerId}/orders`,
|
|
1158
|
+
path: `/integration/order/sellers/${this.sellerId}/orders/stream`,
|
|
458
1159
|
query,
|
|
459
1160
|
rateLimiter: this.limiter
|
|
460
1161
|
});
|
|
461
1162
|
const items = (data.content ?? []).map(normalizePackage);
|
|
1163
|
+
const result = { items };
|
|
1164
|
+
if (data.hasMore && data.nextCursor) {
|
|
1165
|
+
result.nextCursor = data.nextCursor;
|
|
1166
|
+
}
|
|
1167
|
+
return result;
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* Fetch the per-parcel cargo-fee breakdown for a single cargo invoice
|
|
1171
|
+
* (Trendyol's `getCargoInvoiceItems`). Useful for reconciling Trendyol's
|
|
1172
|
+
* cargo deductions against your shipped packages.
|
|
1173
|
+
*
|
|
1174
|
+
* `invoiceSerialNumber` is sourced from the Current Account Statement
|
|
1175
|
+
* ("Cari Hesap Ekstresi") with `transactionType=DeductionInvoices`.
|
|
1176
|
+
*
|
|
1177
|
+
* Page-based pagination internally (cursor encodes the page index).
|
|
1178
|
+
*/
|
|
1179
|
+
async getCargoInvoiceItems(invoiceSerialNumber, params = {}) {
|
|
1180
|
+
const size = Math.min(params.limit ?? 500, 500);
|
|
1181
|
+
const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
|
|
1182
|
+
const data = await this.transport.request({
|
|
1183
|
+
method: "GET",
|
|
1184
|
+
path: `/integration/finance/che/sellers/${this.sellerId}/cargo-invoice/${encodeURIComponent(invoiceSerialNumber)}/items`,
|
|
1185
|
+
query: { page, size },
|
|
1186
|
+
rateLimiter: this.limiter
|
|
1187
|
+
});
|
|
1188
|
+
const items = (data.content ?? []).map((row) => {
|
|
1189
|
+
const out = { raw: row };
|
|
1190
|
+
if (row.shipmentPackageType !== void 0) out.shipmentPackageType = row.shipmentPackageType;
|
|
1191
|
+
if (row.parcelUniqueId !== void 0) {
|
|
1192
|
+
out.parcelUniqueId = typeof row.parcelUniqueId === "string" ? row.parcelUniqueId : String(row.parcelUniqueId);
|
|
1193
|
+
}
|
|
1194
|
+
if (row.orderNumber !== void 0) out.orderNumber = row.orderNumber;
|
|
1195
|
+
if (typeof row.amount === "number") out.amount = row.amount;
|
|
1196
|
+
if (typeof row.desi === "number") out.desi = row.desi;
|
|
1197
|
+
return out;
|
|
1198
|
+
});
|
|
462
1199
|
const totalPages = typeof data.totalPages === "number" ? data.totalPages : 0;
|
|
463
1200
|
const result = { items };
|
|
464
1201
|
if (page + 1 < totalPages) {
|
|
@@ -466,6 +1203,104 @@ var OrdersResource = class {
|
|
|
466
1203
|
}
|
|
467
1204
|
return result;
|
|
468
1205
|
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Notify Trendyol that a shipped package was returned to you (manual
|
|
1208
|
+
* return flow, e.g. customer dropped it at your address or you got the
|
|
1209
|
+
* package back without going through Trendyol's return cargo).
|
|
1210
|
+
*
|
|
1211
|
+
* No body; Trendyol responds with 200 + empty body on success.
|
|
1212
|
+
*/
|
|
1213
|
+
async manualReturnByPackageId(packageId) {
|
|
1214
|
+
await this.transport.request({
|
|
1215
|
+
method: "PUT",
|
|
1216
|
+
path: `${this.packagePath(packageId)}/manual-return`,
|
|
1217
|
+
rateLimiter: this.limiter
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Manual-return variant that takes the cargo tracking number instead of
|
|
1222
|
+
* the package ID. Useful when the cargo provider's webhook only carries
|
|
1223
|
+
* the tracking number.
|
|
1224
|
+
*
|
|
1225
|
+
* Sibling path (not under `/{packageId}/...`).
|
|
1226
|
+
*/
|
|
1227
|
+
async manualReturnByTrackingNumber(cargoTrackingNumber) {
|
|
1228
|
+
await this.transport.request({
|
|
1229
|
+
method: "PUT",
|
|
1230
|
+
path: `/integration/order/sellers/${this.sellerId}/shipment-packages/manual-return-by-tracking-number/${encodeURIComponent(String(cargoTrackingNumber))}`,
|
|
1231
|
+
rateLimiter: this.limiter
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Fetch Trendyol Express compensation tickets (claims filed when a
|
|
1236
|
+
* shipment is lost or damaged in transit). Page-based pagination
|
|
1237
|
+
* internally; the SDK exposes the cursor convention.
|
|
1238
|
+
*
|
|
1239
|
+
* Note the different base path — `/integration/tex/compensation/...`,
|
|
1240
|
+
* not the regular `/integration/order/...`.
|
|
1241
|
+
*
|
|
1242
|
+
* @example
|
|
1243
|
+
* ```ts
|
|
1244
|
+
* import { paginate } from '@lonca/core';
|
|
1245
|
+
* const tickets = await client.orders.getCompensationTickets({
|
|
1246
|
+
* startDate: new Date('2026-01-01'),
|
|
1247
|
+
* endDate: new Date('2026-02-01'),
|
|
1248
|
+
* });
|
|
1249
|
+
* for (const t of tickets.items) {
|
|
1250
|
+
* console.log(t.orderNumber, t.currentState, t.stateMessage);
|
|
1251
|
+
* }
|
|
1252
|
+
* ```
|
|
1253
|
+
*/
|
|
1254
|
+
async getCompensationTickets(params = {}) {
|
|
1255
|
+
const size = Math.min(params.limit ?? 200, 200);
|
|
1256
|
+
const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
|
|
1257
|
+
const query = { page, size };
|
|
1258
|
+
if (params.startDate) query.startDate = params.startDate.getTime();
|
|
1259
|
+
if (params.endDate) query.endDate = params.endDate.getTime();
|
|
1260
|
+
const res = await this.transport.request({
|
|
1261
|
+
method: "GET",
|
|
1262
|
+
path: `/integration/tex/compensation/sellers/${this.sellerId}/tickets`,
|
|
1263
|
+
query,
|
|
1264
|
+
rateLimiter: this.limiter
|
|
1265
|
+
});
|
|
1266
|
+
const tickets = Array.isArray(res.data) ? res.data : res.data?.items ?? res.data?.content ?? res.content ?? [];
|
|
1267
|
+
const items = tickets.map((t) => {
|
|
1268
|
+
const itemDetails = (t.itemDetails ?? []).map((d) => {
|
|
1269
|
+
const out2 = {};
|
|
1270
|
+
if (typeof d.itemAmount === "number") out2.itemAmount = d.itemAmount;
|
|
1271
|
+
if (d.itemCode !== void 0) out2.itemCode = d.itemCode;
|
|
1272
|
+
if (typeof d.itemCount === "number") out2.itemCount = d.itemCount;
|
|
1273
|
+
if (d.itemName !== void 0) out2.itemName = d.itemName;
|
|
1274
|
+
return out2;
|
|
1275
|
+
});
|
|
1276
|
+
const out = {
|
|
1277
|
+
itemDetails,
|
|
1278
|
+
raw: t
|
|
1279
|
+
};
|
|
1280
|
+
if (t.cargoProvider !== void 0) out.cargoProvider = t.cargoProvider;
|
|
1281
|
+
if (t.compensateReason !== void 0) out.compensateReason = t.compensateReason;
|
|
1282
|
+
const createdAt = toIso2(t.createDate);
|
|
1283
|
+
if (createdAt) out.createdAt = createdAt;
|
|
1284
|
+
if (t.currentState !== void 0) {
|
|
1285
|
+
out.currentState = t.currentState;
|
|
1286
|
+
}
|
|
1287
|
+
if (t.deliveryNumber !== void 0) out.deliveryNumber = t.deliveryNumber;
|
|
1288
|
+
if (t.orderNumber !== void 0) out.orderNumber = t.orderNumber;
|
|
1289
|
+
if (t.requestedBy !== void 0) out.requestedBy = t.requestedBy;
|
|
1290
|
+
if (t.stateMessage !== void 0) out.stateMessage = t.stateMessage;
|
|
1291
|
+
if (t.totalItemsAmount !== void 0) out.totalItemsAmount = t.totalItemsAmount;
|
|
1292
|
+
return out;
|
|
1293
|
+
});
|
|
1294
|
+
const result = { items };
|
|
1295
|
+
const total = typeof res.totalCount === "number" ? res.totalCount : 0;
|
|
1296
|
+
if ((page + 1) * size < total) {
|
|
1297
|
+
result.nextCursor = String(page + 1);
|
|
1298
|
+
}
|
|
1299
|
+
return result;
|
|
1300
|
+
}
|
|
1301
|
+
packagePath(packageId) {
|
|
1302
|
+
return `/integration/order/sellers/${this.sellerId}/shipment-packages/${encodeURIComponent(String(packageId))}`;
|
|
1303
|
+
}
|
|
469
1304
|
};
|
|
470
1305
|
var DEFAULT_PAGE_SIZE3 = 50;
|
|
471
1306
|
var MAX_PAGE_SIZE2 = 1e3;
|
|
@@ -512,7 +1347,7 @@ function normalizeVariant(variant) {
|
|
|
512
1347
|
if (stock !== void 0) out.stock = stock;
|
|
513
1348
|
return out;
|
|
514
1349
|
}
|
|
515
|
-
function
|
|
1350
|
+
function toIso3(epochMs) {
|
|
516
1351
|
if (typeof epochMs !== "number" || !Number.isFinite(epochMs)) return void 0;
|
|
517
1352
|
return new Date(epochMs).toISOString();
|
|
518
1353
|
}
|
|
@@ -526,8 +1361,8 @@ function normalizeProduct(node) {
|
|
|
526
1361
|
images: normalizeImages(node.images),
|
|
527
1362
|
attributes: (node.attributes ?? []).map(normalizeAttribute2),
|
|
528
1363
|
variants: (node.variants ?? []).map(normalizeVariant),
|
|
529
|
-
createdAt:
|
|
530
|
-
updatedAt:
|
|
1364
|
+
createdAt: toIso3(node.creationDate) ?? "",
|
|
1365
|
+
updatedAt: toIso3(node.lastModifiedDate) ?? "",
|
|
531
1366
|
raw: node
|
|
532
1367
|
};
|
|
533
1368
|
if (node.description !== void 0) out.description = node.description;
|
|
@@ -573,13 +1408,13 @@ function normalizeUnapproved(node) {
|
|
|
573
1408
|
out.specialConsumptionTax = node.specialConsumptionTax;
|
|
574
1409
|
}
|
|
575
1410
|
if (node.sgrPrice !== void 0) out.sgrPrice = node.sgrPrice;
|
|
576
|
-
const createdAt =
|
|
1411
|
+
const createdAt = toIso3(node.createDateTime);
|
|
577
1412
|
if (createdAt) out.createdAt = createdAt;
|
|
578
|
-
const updatedAt =
|
|
1413
|
+
const updatedAt = toIso3(node.lastUpdateDate);
|
|
579
1414
|
if (updatedAt) out.updatedAt = updatedAt;
|
|
580
|
-
const priceChanged =
|
|
1415
|
+
const priceChanged = toIso3(node.lastPriceChangeDate);
|
|
581
1416
|
if (priceChanged) out.lastPriceChangedAt = priceChanged;
|
|
582
|
-
const stockChanged =
|
|
1417
|
+
const stockChanged = toIso3(node.lastStockChangeDate);
|
|
583
1418
|
if (stockChanged) out.lastStockChangedAt = stockChanged;
|
|
584
1419
|
return out;
|
|
585
1420
|
}
|
|
@@ -604,7 +1439,7 @@ function normalizeProductBase(data) {
|
|
|
604
1439
|
archived: !!data.archived,
|
|
605
1440
|
raw: data
|
|
606
1441
|
};
|
|
607
|
-
const approvedAt =
|
|
1442
|
+
const approvedAt = toIso3(data.approvedDate);
|
|
608
1443
|
if (approvedAt) out.approvedAt = approvedAt;
|
|
609
1444
|
if (data.listingId !== void 0) out.listingId = data.listingId;
|
|
610
1445
|
if (data.contentId !== void 0) out.contentId = String(data.contentId);
|
|
@@ -626,9 +1461,9 @@ function normalizeBatchResult(data) {
|
|
|
626
1461
|
};
|
|
627
1462
|
if (typeof data.itemCount === "number") out.itemCount = data.itemCount;
|
|
628
1463
|
if (typeof data.failedItemCount === "number") out.failedItemCount = data.failedItemCount;
|
|
629
|
-
const createdAt =
|
|
1464
|
+
const createdAt = toIso3(data.creationDate);
|
|
630
1465
|
if (createdAt) out.createdAt = createdAt;
|
|
631
|
-
const lastModifiedAt =
|
|
1466
|
+
const lastModifiedAt = toIso3(data.lastModification);
|
|
632
1467
|
if (lastModifiedAt) out.lastModifiedAt = lastModifiedAt;
|
|
633
1468
|
if (data.sourceType !== void 0) out.sourceType = data.sourceType;
|
|
634
1469
|
if (data.batchRequestType !== void 0) out.batchRequestType = data.batchRequestType;
|
|
@@ -974,6 +1809,120 @@ var ProductsResource = class {
|
|
|
974
1809
|
return { batchRequestId: data?.batchRequestId ?? "" };
|
|
975
1810
|
}
|
|
976
1811
|
};
|
|
1812
|
+
function toIso4(epochMs) {
|
|
1813
|
+
if (typeof epochMs !== "number" || !Number.isFinite(epochMs) || epochMs === 0) {
|
|
1814
|
+
return void 0;
|
|
1815
|
+
}
|
|
1816
|
+
return new Date(epochMs).toISOString();
|
|
1817
|
+
}
|
|
1818
|
+
function normalizeAnswer(a) {
|
|
1819
|
+
if (!a) return void 0;
|
|
1820
|
+
const out = {};
|
|
1821
|
+
if (a.text !== void 0) out.text = a.text;
|
|
1822
|
+
const createdAt = toIso4(a.creationDate);
|
|
1823
|
+
if (createdAt) out.createdAt = createdAt;
|
|
1824
|
+
if (a.status !== void 0) out.status = a.status;
|
|
1825
|
+
return out;
|
|
1826
|
+
}
|
|
1827
|
+
function normalizeQuestion(node) {
|
|
1828
|
+
const out = {
|
|
1829
|
+
id: node.id !== void 0 ? String(node.id) : "",
|
|
1830
|
+
raw: node
|
|
1831
|
+
};
|
|
1832
|
+
if (node.text !== void 0) out.text = node.text;
|
|
1833
|
+
if (node.customerId !== void 0) out.customerId = String(node.customerId);
|
|
1834
|
+
if (node.userName !== void 0) out.userName = node.userName;
|
|
1835
|
+
if (typeof node.showUserName === "boolean") out.showUserName = node.showUserName;
|
|
1836
|
+
if (node.status !== void 0) out.status = node.status;
|
|
1837
|
+
if (typeof node.public === "boolean") out.public = node.public;
|
|
1838
|
+
if (node.productMainId !== void 0) out.productMainId = node.productMainId;
|
|
1839
|
+
if (node.productName !== void 0) out.productName = node.productName;
|
|
1840
|
+
if (node.imageUrl !== void 0) out.imageUrl = node.imageUrl;
|
|
1841
|
+
if (node.webUrl !== void 0) out.webUrl = node.webUrl;
|
|
1842
|
+
const createdAt = toIso4(node.creationDate);
|
|
1843
|
+
if (createdAt) out.createdAt = createdAt;
|
|
1844
|
+
if (node.answeredDateMessage !== void 0) out.answeredDateMessage = node.answeredDateMessage;
|
|
1845
|
+
const answer = normalizeAnswer(node.answer);
|
|
1846
|
+
if (answer) out.answer = answer;
|
|
1847
|
+
const rejectedAnswer = normalizeAnswer(node.rejectedAnswer);
|
|
1848
|
+
if (rejectedAnswer) out.rejectedAnswer = rejectedAnswer;
|
|
1849
|
+
const rejectedAt = toIso4(node.rejectedDate);
|
|
1850
|
+
if (rejectedAt) out.rejectedAt = rejectedAt;
|
|
1851
|
+
if (node.reason !== void 0) out.reason = node.reason;
|
|
1852
|
+
if (node.reportReason !== void 0) out.reportReason = node.reportReason;
|
|
1853
|
+
const reportedAt = toIso4(node.reportedDate);
|
|
1854
|
+
if (reportedAt) out.reportedAt = reportedAt;
|
|
1855
|
+
return out;
|
|
1856
|
+
}
|
|
1857
|
+
var QuestionsResource = class {
|
|
1858
|
+
constructor(transport, sellerId, limiter) {
|
|
1859
|
+
this.transport = transport;
|
|
1860
|
+
this.sellerId = sellerId;
|
|
1861
|
+
this.limiter = limiter ?? new TokenBucketRateLimiter({ capacity: 500, intervalMs: 6e4 });
|
|
1862
|
+
}
|
|
1863
|
+
transport;
|
|
1864
|
+
sellerId;
|
|
1865
|
+
limiter;
|
|
1866
|
+
/** Fetch a single question by its numeric ID. */
|
|
1867
|
+
async get(questionId) {
|
|
1868
|
+
const data = await this.transport.request({
|
|
1869
|
+
method: "GET",
|
|
1870
|
+
path: `/integration/qna/sellers/${this.sellerId}/questions/${encodeURIComponent(String(questionId))}`,
|
|
1871
|
+
rateLimiter: this.limiter
|
|
1872
|
+
});
|
|
1873
|
+
return normalizeQuestion(data);
|
|
1874
|
+
}
|
|
1875
|
+
/**
|
|
1876
|
+
* Filter questions by barcode / date range / status. Page-based
|
|
1877
|
+
* pagination internally; SDK exposes the opaque-cursor convention.
|
|
1878
|
+
*/
|
|
1879
|
+
async list(params = {}) {
|
|
1880
|
+
const size = Math.min(params.limit ?? 50, 200);
|
|
1881
|
+
const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
|
|
1882
|
+
const query = { page, size };
|
|
1883
|
+
if (params.barcode) query.barcode = params.barcode;
|
|
1884
|
+
if (params.startDate) query.startDate = params.startDate.getTime();
|
|
1885
|
+
if (params.endDate) query.endDate = params.endDate.getTime();
|
|
1886
|
+
if (params.status) query.status = params.status;
|
|
1887
|
+
const data = await this.transport.request({
|
|
1888
|
+
method: "GET",
|
|
1889
|
+
path: `/integration/qna/sellers/${this.sellerId}/questions/filter`,
|
|
1890
|
+
query,
|
|
1891
|
+
rateLimiter: this.limiter
|
|
1892
|
+
});
|
|
1893
|
+
const items = (data.content ?? []).map(normalizeQuestion);
|
|
1894
|
+
const result = { items };
|
|
1895
|
+
const totalPages = typeof data.totalPages === "number" ? data.totalPages : 0;
|
|
1896
|
+
if (page + 1 < totalPages) {
|
|
1897
|
+
result.nextCursor = String(page + 1);
|
|
1898
|
+
}
|
|
1899
|
+
return result;
|
|
1900
|
+
}
|
|
1901
|
+
/**
|
|
1902
|
+
* Reply to a question. Trendyol enforces 10–2000 characters on the
|
|
1903
|
+
* answer text; the SDK pre-validates client-side.
|
|
1904
|
+
*
|
|
1905
|
+
* @throws {ValidationError} when `text` is outside the 10–2000 char range.
|
|
1906
|
+
*/
|
|
1907
|
+
async answer(questionId, text) {
|
|
1908
|
+
if (typeof text !== "string" || text.length < 10) {
|
|
1909
|
+
throw new ValidationError({
|
|
1910
|
+
message: `questions.answer: text must be at least 10 chars (got ${text?.length ?? 0})`
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
if (text.length > 2e3) {
|
|
1914
|
+
throw new ValidationError({
|
|
1915
|
+
message: `questions.answer: text must be at most 2000 chars (got ${text.length})`
|
|
1916
|
+
});
|
|
1917
|
+
}
|
|
1918
|
+
return this.transport.request({
|
|
1919
|
+
method: "POST",
|
|
1920
|
+
path: `/integration/qna/sellers/${this.sellerId}/questions/${encodeURIComponent(String(questionId))}/answers`,
|
|
1921
|
+
body: { text },
|
|
1922
|
+
rateLimiter: this.limiter
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
};
|
|
977
1926
|
var VALID_TYPES = /* @__PURE__ */ new Set([
|
|
978
1927
|
"SHIPMENT",
|
|
979
1928
|
"RETURNING",
|
|
@@ -1054,6 +2003,170 @@ var SuppliersResource = class {
|
|
|
1054
2003
|
return items;
|
|
1055
2004
|
}
|
|
1056
2005
|
};
|
|
2006
|
+
var TestOrdersResource = class {
|
|
2007
|
+
constructor(transport, sellerId, limiter) {
|
|
2008
|
+
this.transport = transport;
|
|
2009
|
+
this.sellerId = sellerId;
|
|
2010
|
+
this.limiter = limiter ?? new TokenBucketRateLimiter({ capacity: 60, intervalMs: 6e4 });
|
|
2011
|
+
}
|
|
2012
|
+
transport;
|
|
2013
|
+
sellerId;
|
|
2014
|
+
limiter;
|
|
2015
|
+
/**
|
|
2016
|
+
* Create a test order with the given customer / addresses / lines. The
|
|
2017
|
+
* SDK forwards the typed payload verbatim — drill into Trendyol's
|
|
2018
|
+
* `createTestOrder` doc for inner field rules.
|
|
2019
|
+
*
|
|
2020
|
+
* @throws {ValidationError} when required top-level fields are missing.
|
|
2021
|
+
*/
|
|
2022
|
+
async create(input) {
|
|
2023
|
+
for (const k of ["customer", "invoiceAddress", "shippingAddress", "seller", "lines"]) {
|
|
2024
|
+
if (!input?.[k]) {
|
|
2025
|
+
throw new ValidationError({ message: `testOrders.create: ${k} is required` });
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
return this.transport.request({
|
|
2029
|
+
method: "POST",
|
|
2030
|
+
path: `/integration/test/order/orders/core`,
|
|
2031
|
+
body: input,
|
|
2032
|
+
rateLimiter: this.limiter
|
|
2033
|
+
});
|
|
2034
|
+
}
|
|
2035
|
+
/** Push a test shipment package to the given status. */
|
|
2036
|
+
async updateStatus(packageId, status) {
|
|
2037
|
+
return this.transport.request({
|
|
2038
|
+
method: "PUT",
|
|
2039
|
+
path: `/integration/test/order/sellers/${this.sellerId}/shipment-packages/${encodeURIComponent(String(packageId))}/status`,
|
|
2040
|
+
body: { status },
|
|
2041
|
+
rateLimiter: this.limiter
|
|
2042
|
+
});
|
|
2043
|
+
}
|
|
2044
|
+
/** Move test claims to the `WaitingInAction` state. */
|
|
2045
|
+
async setClaimsWaitingInAction() {
|
|
2046
|
+
return this.transport.request({
|
|
2047
|
+
method: "PUT",
|
|
2048
|
+
path: `/integration/test/order/sellers/${this.sellerId}/claims/waiting-in-action`,
|
|
2049
|
+
rateLimiter: this.limiter
|
|
2050
|
+
});
|
|
2051
|
+
}
|
|
2052
|
+
};
|
|
2053
|
+
function normalizeWebhook(node) {
|
|
2054
|
+
const out = {
|
|
2055
|
+
id: node.id !== void 0 ? String(node.id) : "",
|
|
2056
|
+
raw: node
|
|
2057
|
+
};
|
|
2058
|
+
if (node.url !== void 0) out.url = node.url;
|
|
2059
|
+
if (node.authenticationType !== void 0) out.authenticationType = node.authenticationType;
|
|
2060
|
+
if (node.username !== void 0) out.username = node.username;
|
|
2061
|
+
if (node.apiKey !== void 0) out.apiKey = node.apiKey;
|
|
2062
|
+
if (node.subscribedStatuses !== void 0) out.subscribedStatuses = node.subscribedStatuses;
|
|
2063
|
+
if (typeof node.active === "boolean") out.active = node.active;
|
|
2064
|
+
else if (typeof node.isActive === "boolean") out.active = node.isActive;
|
|
2065
|
+
else if (typeof node.status === "string") out.active = node.status.toUpperCase() === "ACTIVE";
|
|
2066
|
+
return out;
|
|
2067
|
+
}
|
|
2068
|
+
var WebhooksResource = class {
|
|
2069
|
+
constructor(transport, sellerId, limiter) {
|
|
2070
|
+
this.transport = transport;
|
|
2071
|
+
this.sellerId = sellerId;
|
|
2072
|
+
this.limiter = limiter ?? new TokenBucketRateLimiter({ capacity: 50, intervalMs: 6e4 });
|
|
2073
|
+
}
|
|
2074
|
+
transport;
|
|
2075
|
+
sellerId;
|
|
2076
|
+
limiter;
|
|
2077
|
+
/**
|
|
2078
|
+
* Create a new webhook subscription. Trendyol caps subscriptions at 15
|
|
2079
|
+
* per seller — the SDK does NOT pre-check (you'd need to call `list()`
|
|
2080
|
+
* first), but Trendyol returns 400 when the cap is exceeded.
|
|
2081
|
+
*
|
|
2082
|
+
* @throws {ValidationError} when `url` or `authenticationType` is missing.
|
|
2083
|
+
*/
|
|
2084
|
+
async create(input) {
|
|
2085
|
+
this.validateInput(input, "create");
|
|
2086
|
+
return this.transport.request({
|
|
2087
|
+
method: "POST",
|
|
2088
|
+
path: `/integration/sellers/${this.sellerId}/webhooks`,
|
|
2089
|
+
body: input,
|
|
2090
|
+
rateLimiter: this.limiter
|
|
2091
|
+
});
|
|
2092
|
+
}
|
|
2093
|
+
/** List all registered webhook subscriptions. */
|
|
2094
|
+
async list() {
|
|
2095
|
+
const data = await this.transport.request({
|
|
2096
|
+
method: "GET",
|
|
2097
|
+
path: `/integration/sellers/${this.sellerId}/webhooks`,
|
|
2098
|
+
rateLimiter: this.limiter
|
|
2099
|
+
});
|
|
2100
|
+
const rows = Array.isArray(data) ? data : Array.isArray(data?.webhooks) ? data.webhooks : Array.isArray(data?.content) ? data.content : [];
|
|
2101
|
+
return rows.map((r) => normalizeWebhook(r));
|
|
2102
|
+
}
|
|
2103
|
+
/**
|
|
2104
|
+
* Update a webhook subscription. Same input shape as `create`; replaces
|
|
2105
|
+
* the whole subscription (Trendyol does NOT partially update).
|
|
2106
|
+
*/
|
|
2107
|
+
async update(webhookId, input) {
|
|
2108
|
+
this.validateInput(input, "update");
|
|
2109
|
+
return this.transport.request({
|
|
2110
|
+
method: "PUT",
|
|
2111
|
+
path: this.webhookPath(webhookId),
|
|
2112
|
+
body: input,
|
|
2113
|
+
rateLimiter: this.limiter
|
|
2114
|
+
});
|
|
2115
|
+
}
|
|
2116
|
+
/** Permanently delete a webhook subscription. */
|
|
2117
|
+
async delete(webhookId) {
|
|
2118
|
+
return this.transport.request({
|
|
2119
|
+
method: "DELETE",
|
|
2120
|
+
path: this.webhookPath(webhookId),
|
|
2121
|
+
rateLimiter: this.limiter
|
|
2122
|
+
});
|
|
2123
|
+
}
|
|
2124
|
+
/** Re-activate a previously-deactivated webhook subscription. */
|
|
2125
|
+
async activate(webhookId) {
|
|
2126
|
+
return this.transport.request({
|
|
2127
|
+
method: "PUT",
|
|
2128
|
+
path: `${this.webhookPath(webhookId)}/activate`,
|
|
2129
|
+
rateLimiter: this.limiter
|
|
2130
|
+
});
|
|
2131
|
+
}
|
|
2132
|
+
/**
|
|
2133
|
+
* Deactivate a webhook subscription. Trendyol automatically deactivates
|
|
2134
|
+
* a subscription after persistent delivery failures (and sends 2 emails);
|
|
2135
|
+
* use `activate()` to bring it back online once your endpoint is healthy.
|
|
2136
|
+
*/
|
|
2137
|
+
async deactivate(webhookId) {
|
|
2138
|
+
return this.transport.request({
|
|
2139
|
+
method: "PUT",
|
|
2140
|
+
path: `${this.webhookPath(webhookId)}/deactivate`,
|
|
2141
|
+
rateLimiter: this.limiter
|
|
2142
|
+
});
|
|
2143
|
+
}
|
|
2144
|
+
validateInput(input, method) {
|
|
2145
|
+
if (!input?.url || typeof input.url !== "string") {
|
|
2146
|
+
throw new ValidationError({ message: `webhooks.${method}: url is required` });
|
|
2147
|
+
}
|
|
2148
|
+
if (input.authenticationType !== "BASIC_AUTHENTICATION" && input.authenticationType !== "API_KEY") {
|
|
2149
|
+
throw new ValidationError({
|
|
2150
|
+
message: `webhooks.${method}: authenticationType must be 'BASIC_AUTHENTICATION' or 'API_KEY'`
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
if (input.authenticationType === "BASIC_AUTHENTICATION") {
|
|
2154
|
+
if (!input.username || !input.password) {
|
|
2155
|
+
throw new ValidationError({
|
|
2156
|
+
message: `webhooks.${method}: BASIC_AUTHENTICATION requires username + password`
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2159
|
+
} else if (!input.apiKey) {
|
|
2160
|
+
throw new ValidationError({
|
|
2161
|
+
message: `webhooks.${method}: API_KEY requires apiKey`
|
|
2162
|
+
});
|
|
2163
|
+
}
|
|
2164
|
+
if (input.subscribedStatuses && input.subscribedStatuses.length === 0) ;
|
|
2165
|
+
}
|
|
2166
|
+
webhookPath(webhookId) {
|
|
2167
|
+
return `/integration/sellers/${this.sellerId}/webhooks/${encodeURIComponent(String(webhookId))}`;
|
|
2168
|
+
}
|
|
2169
|
+
};
|
|
1057
2170
|
|
|
1058
2171
|
// src/auth.ts
|
|
1059
2172
|
function buildAuthHeader(apiKey, apiSecret) {
|
|
@@ -1146,7 +2259,12 @@ var TrendyolTransport = class {
|
|
|
1146
2259
|
signal: this.composeSignal(opts.signal)
|
|
1147
2260
|
};
|
|
1148
2261
|
if (opts.body !== void 0 && opts.method !== "GET") {
|
|
1149
|
-
|
|
2262
|
+
if (opts.body instanceof FormData) {
|
|
2263
|
+
init.body = opts.body;
|
|
2264
|
+
delete headers["Content-Type"];
|
|
2265
|
+
} else {
|
|
2266
|
+
init.body = JSON.stringify(opts.body);
|
|
2267
|
+
}
|
|
1150
2268
|
}
|
|
1151
2269
|
this.logger.debug("trendyol.request", {
|
|
1152
2270
|
method: opts.method,
|
|
@@ -1261,10 +2379,18 @@ function createTrendyolClient(opts) {
|
|
|
1261
2379
|
suppliers: new SuppliersResource(transport, opts.sellerId),
|
|
1262
2380
|
products: new ProductsResource(transport, opts.sellerId),
|
|
1263
2381
|
inventory: new InventoryResource(transport, opts.sellerId),
|
|
1264
|
-
orders: new OrdersResource(transport, opts.sellerId)
|
|
2382
|
+
orders: new OrdersResource(transport, opts.sellerId),
|
|
2383
|
+
claims: new ClaimsResource(transport, opts.sellerId),
|
|
2384
|
+
webhooks: new WebhooksResource(transport, opts.sellerId),
|
|
2385
|
+
questions: new QuestionsResource(transport, opts.sellerId),
|
|
2386
|
+
invoices: new InvoicesResource(transport, opts.sellerId),
|
|
2387
|
+
finance: new FinanceResource(transport, opts.sellerId),
|
|
2388
|
+
labels: new LabelsResource(transport, opts.sellerId),
|
|
2389
|
+
testOrders: new TestOrdersResource(transport, opts.sellerId),
|
|
2390
|
+
locations: new LocationsResource(transport)
|
|
1265
2391
|
};
|
|
1266
2392
|
}
|
|
1267
2393
|
|
|
1268
|
-
export { BrandsResource, CategoriesResource, InventoryResource, OrdersResource, ProductsResource, SuppliersResource, createTrendyolClient };
|
|
2394
|
+
export { BrandsResource, CategoriesResource, ClaimsResource, FinanceResource, InventoryResource, InvoicesResource, LabelsResource, LocationsResource, OrdersResource, ProductsResource, QuestionsResource, SuppliersResource, TestOrdersResource, WebhooksResource, createTrendyolClient };
|
|
1269
2395
|
//# sourceMappingURL=index.js.map
|
|
1270
2396
|
//# sourceMappingURL=index.js.map
|