@lonca/trendyol 0.3.0 → 0.4.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/dist/index.js CHANGED
@@ -357,8 +357,9 @@ function normalizeCustomer(node) {
357
357
  return out;
358
358
  }
359
359
  function normalizePackage(node) {
360
+ const pkgId = node.shipmentPackageId ?? node.id;
360
361
  const out = {
361
- id: node.shipmentPackageId !== void 0 ? String(node.shipmentPackageId) : "",
362
+ id: pkgId !== void 0 ? String(pkgId) : "",
362
363
  orderNumber: node.orderNumber ?? "",
363
364
  status: node.status ?? "",
364
365
  customer: normalizeCustomer(node),
@@ -466,6 +467,342 @@ var OrdersResource = class {
466
467
  }
467
468
  return result;
468
469
  }
470
+ /**
471
+ * Push a shipment-package status update.
472
+ *
473
+ * Trendyol restricts the seller-side push to two transitions:
474
+ * - `Picking` — order picked up from the shelf / being prepared
475
+ * - `Invoiced` — invoice issued, ready for cargo handoff
476
+ *
477
+ * Other transitions (`Shipped`, `Delivered`, etc.) are driven by Trendyol
478
+ * or the cargo provider — call `processAlternativeDelivery` or
479
+ * `manualDeliverByPackageId` if you ship outside Trendyol's cargo
480
+ * network.
481
+ *
482
+ * Returns void; Trendyol responds with 200 + empty body on success.
483
+ */
484
+ async updatePackageStatus(packageId, input) {
485
+ await this.transport.request({
486
+ method: "PUT",
487
+ path: this.packagePath(packageId),
488
+ body: input,
489
+ rateLimiter: this.limiter
490
+ });
491
+ }
492
+ /**
493
+ * Notify Trendyol that one or more line items cannot be supplied
494
+ * ("Tedarik Edememe Bildirimi"). Marks the listed line IDs as
495
+ * `UnSupplied`. Trendyol cancels those quantities and notifies the
496
+ * customer.
497
+ *
498
+ * `reasonId` is a numeric code Trendyol publishes separately — consult
499
+ * the seller panel or the "Tedarik Edememe" docs for current values.
500
+ *
501
+ * Returns void; Trendyol responds with 200 + empty body on success.
502
+ */
503
+ async cancelPackageItem(packageId, input) {
504
+ if (!Array.isArray(input?.lines) || input.lines.length === 0) {
505
+ throw new ValidationError({ message: "cancelPackageItem: lines must not be empty" });
506
+ }
507
+ await this.transport.request({
508
+ method: "PUT",
509
+ path: `${this.packagePath(packageId)}/items/unsupplied`,
510
+ body: input,
511
+ rateLimiter: this.limiter
512
+ });
513
+ }
514
+ /**
515
+ * Extend the agreed delivery date for a shipment package by 1, 2, or 3 days.
516
+ * Trendyol enforces the [1, 3] range server-side; the SDK validates client-side
517
+ * to fail fast.
518
+ *
519
+ * Returns void; Trendyol responds with 200 + empty body on success.
520
+ */
521
+ async extendDeliveryDate(packageId, extendedDayCount) {
522
+ if (![1, 2, 3].includes(extendedDayCount)) {
523
+ throw new ValidationError({
524
+ message: `extendDeliveryDate: extendedDayCount must be 1, 2, or 3 (got ${extendedDayCount})`
525
+ });
526
+ }
527
+ await this.transport.request({
528
+ method: "PUT",
529
+ path: `${this.packagePath(packageId)}/extended-agreed-delivery-date`,
530
+ body: { extendedDayCount },
531
+ rateLimiter: this.limiter
532
+ });
533
+ }
534
+ /**
535
+ * Notify Trendyol of an alternative delivery channel — used when the
536
+ * seller is shipping via a non-Trendyol cargo provider. Trendyol then
537
+ * either SMSes the customer the tracking link (when `isPhoneNumber` is
538
+ * `true`) or stores the tracking URL on the package directly.
539
+ *
540
+ * Returns void; Trendyol responds with 200 + empty body on success.
541
+ */
542
+ async processAlternativeDelivery(packageId, input) {
543
+ await this.transport.request({
544
+ method: "PUT",
545
+ path: `${this.packagePath(packageId)}/alternative-delivery`,
546
+ body: input,
547
+ rateLimiter: this.limiter
548
+ });
549
+ }
550
+ /**
551
+ * Split a shipment package by moving a set of line IDs into a new
552
+ * package. The original package keeps the remaining lines.
553
+ *
554
+ * @param packageId The package to split.
555
+ * @param orderLineIds Line IDs to move into the new package (1+).
556
+ * @throws {ValidationError} when `orderLineIds` is empty.
557
+ */
558
+ async splitPackage(packageId, orderLineIds) {
559
+ if (!Array.isArray(orderLineIds) || orderLineIds.length === 0) {
560
+ throw new ValidationError({ message: "splitPackage: orderLineIds must not be empty" });
561
+ }
562
+ await this.transport.request({
563
+ method: "POST",
564
+ path: `${this.packagePath(packageId)}/split`,
565
+ body: { orderLineIds },
566
+ rateLimiter: this.limiter
567
+ });
568
+ }
569
+ /**
570
+ * Split a shipment package by quantity. Each `QuantitySplit` entry
571
+ * carves a single line into multiple packages — e.g. `{ orderLineId: 100,
572
+ * quantities: [2, 2, 1] }` splits 5 units of line 100 into three packages
573
+ * of 2 + 2 + 1.
574
+ *
575
+ * @throws {ValidationError} when `quantitySplit` is empty.
576
+ */
577
+ async splitPackageByQuantity(packageId, quantitySplit) {
578
+ if (!Array.isArray(quantitySplit) || quantitySplit.length === 0) {
579
+ throw new ValidationError({
580
+ message: "splitPackageByQuantity: quantitySplit must not be empty"
581
+ });
582
+ }
583
+ await this.transport.request({
584
+ method: "POST",
585
+ path: `${this.packagePath(packageId)}/quantity-split`,
586
+ body: { quantitySplit },
587
+ rateLimiter: this.limiter
588
+ });
589
+ }
590
+ /**
591
+ * Split a shipment package into multiple new packages by grouping line
592
+ * IDs. Each `SplitGroup` becomes one new package containing the listed
593
+ * line IDs.
594
+ *
595
+ * @throws {ValidationError} when `splitGroups` is empty.
596
+ */
597
+ async multiSplitPackage(packageId, splitGroups) {
598
+ if (!Array.isArray(splitGroups) || splitGroups.length === 0) {
599
+ throw new ValidationError({ message: "multiSplitPackage: splitGroups must not be empty" });
600
+ }
601
+ await this.transport.request({
602
+ method: "POST",
603
+ path: `${this.packagePath(packageId)}/multi-split`,
604
+ body: { splitGroups },
605
+ rateLimiter: this.limiter
606
+ });
607
+ }
608
+ /**
609
+ * Split a shipment package into multiple new packages, each containing a
610
+ * mix of line items at specific quantities. This is the most expressive
611
+ * split — use it when you need fine-grained control over which line IDs
612
+ * and how many of each end up in each new package.
613
+ *
614
+ * @throws {ValidationError} when `splitPackages` is empty.
615
+ */
616
+ async splitMultiPackagesByQuantity(packageId, splitPackages) {
617
+ if (!Array.isArray(splitPackages) || splitPackages.length === 0) {
618
+ throw new ValidationError({
619
+ message: "splitMultiPackagesByQuantity: splitPackages must not be empty"
620
+ });
621
+ }
622
+ await this.transport.request({
623
+ method: "POST",
624
+ path: `${this.packagePath(packageId)}/split-packages`,
625
+ body: { splitPackages },
626
+ rateLimiter: this.limiter
627
+ });
628
+ }
629
+ /**
630
+ * Change the cargo provider on an existing shipment package. Use one of
631
+ * Trendyol's documented marketplace cargo codes (`'YKMP'`, `'ARASMP'`,
632
+ * `'SURATMP'`, etc.) — see `TrendyolCargoProvider` for the full list.
633
+ */
634
+ async changeCargoProvider(packageId, cargoProvider) {
635
+ await this.transport.request({
636
+ method: "PUT",
637
+ path: `${this.packagePath(packageId)}/cargo-providers`,
638
+ body: { cargoProvider },
639
+ rateLimiter: this.limiter
640
+ });
641
+ }
642
+ /**
643
+ * Mark a shipment package as manually delivered via its package ID.
644
+ * Used when the seller delivered the order outside Trendyol's cargo
645
+ * network and needs to flip the package to `Delivered` after handover.
646
+ *
647
+ * No request body; Trendyol responds with 200 + empty body on success.
648
+ */
649
+ async manualDeliverByPackageId(packageId) {
650
+ await this.transport.request({
651
+ method: "PUT",
652
+ path: `${this.packagePath(packageId)}/manual-invoice-delivery`,
653
+ rateLimiter: this.limiter
654
+ });
655
+ }
656
+ /**
657
+ * Manual-deliver variant that takes the cargo tracking number instead
658
+ * of the package ID. Useful when you only have the tracking number on
659
+ * hand (e.g., from a cargo provider webhook).
660
+ *
661
+ * Note the path structure: tracking number sits at a sibling location,
662
+ * not under `/shipment-packages/{id}/...`.
663
+ */
664
+ async manualDeliverByTrackingNumber(cargoTrackingNumber) {
665
+ await this.transport.request({
666
+ method: "PUT",
667
+ path: `/integration/order/sellers/${this.sellerId}/shipment-packages/manual-invoice-delivery-by-tracking-number/${encodeURIComponent(String(cargoTrackingNumber))}`,
668
+ rateLimiter: this.limiter
669
+ });
670
+ }
671
+ /**
672
+ * Mark a package as delivered through an authorized service ("yetkili
673
+ * servis"). For appliance / installation-required products that are
674
+ * delivered + installed by a third-party service partner.
675
+ */
676
+ async markDeliveredByService(packageId) {
677
+ await this.transport.request({
678
+ method: "PUT",
679
+ path: `${this.packagePath(packageId)}/delivered-by-service`,
680
+ rateLimiter: this.limiter
681
+ });
682
+ }
683
+ /**
684
+ * Update box / packaging metadata on a shipment package (desi value
685
+ * and/or number of boxes). Either field can be sent alone.
686
+ */
687
+ async updateBoxInfo(packageId, input) {
688
+ await this.transport.request({
689
+ method: "PUT",
690
+ path: `${this.packagePath(packageId)}/box-info`,
691
+ body: input,
692
+ rateLimiter: this.limiter
693
+ });
694
+ }
695
+ /**
696
+ * Update labor costs for one or more order lines.
697
+ *
698
+ * **Wire note:** Trendyol's request body is a raw array (no envelope),
699
+ * not `{ items: [...] }`. The SDK forwards `items` verbatim.
700
+ *
701
+ * @throws {ValidationError} when `items` is empty.
702
+ */
703
+ async updateLaborCosts(packageId, items) {
704
+ if (!Array.isArray(items) || items.length === 0) {
705
+ throw new ValidationError({ message: "updateLaborCosts: items must not be empty" });
706
+ }
707
+ await this.transport.request({
708
+ method: "PUT",
709
+ path: `${this.packagePath(packageId)}/labor-costs`,
710
+ body: items,
711
+ rateLimiter: this.limiter
712
+ });
713
+ }
714
+ /**
715
+ * Reassign a shipment package to a different warehouse. `warehouseId`
716
+ * comes from `client.suppliers.getAddresses()` (filter by `isShipmentAddress`).
717
+ */
718
+ async updateWarehouse(packageId, warehouseId) {
719
+ await this.transport.request({
720
+ method: "PUT",
721
+ path: `${this.packagePath(packageId)}/warehouse`,
722
+ body: { warehouseId },
723
+ rateLimiter: this.limiter
724
+ });
725
+ }
726
+ /**
727
+ * Stream variant of `list()`. Trendyol's `getShipmentPackagesStream`
728
+ * returns the same `ShipmentPackage` shape but paginates with an opaque
729
+ * cursor — useful when the dataset is large and page-based pagination
730
+ * would hit the 10 000-record cap.
731
+ *
732
+ * @example
733
+ * ```ts
734
+ * import { paginate } from '@lonca/core';
735
+ * for await (const pkg of paginate((p) =>
736
+ * client.orders.listStream({ ...p, packageItemStatuses: 'Created,Picking' }),
737
+ * )) {
738
+ * console.log(pkg.id, pkg.status);
739
+ * }
740
+ * ```
741
+ */
742
+ async listStream(params = {}) {
743
+ const size = Math.min(params.limit ?? DEFAULT_PAGE_SIZE2, MAX_PAGE_SIZE);
744
+ const query = { size };
745
+ if (params.cursor) query.nextCursor = params.cursor;
746
+ if (params.packageItemStatuses) query.packageItemStatuses = params.packageItemStatuses;
747
+ if (params.lastModifiedStartDate) {
748
+ query.lastModifiedStartDate = params.lastModifiedStartDate.getTime();
749
+ }
750
+ if (params.lastModifiedEndDate) {
751
+ query.lastModifiedEndDate = params.lastModifiedEndDate.getTime();
752
+ }
753
+ const data = await this.transport.request({
754
+ method: "GET",
755
+ path: `/integration/order/sellers/${this.sellerId}/orders/stream`,
756
+ query,
757
+ rateLimiter: this.limiter
758
+ });
759
+ const items = (data.content ?? []).map(normalizePackage);
760
+ const result = { items };
761
+ if (data.hasMore && data.nextCursor) {
762
+ result.nextCursor = data.nextCursor;
763
+ }
764
+ return result;
765
+ }
766
+ /**
767
+ * Fetch the per-parcel cargo-fee breakdown for a single cargo invoice
768
+ * (Trendyol's `getCargoInvoiceItems`). Useful for reconciling Trendyol's
769
+ * cargo deductions against your shipped packages.
770
+ *
771
+ * `invoiceSerialNumber` is sourced from the Current Account Statement
772
+ * ("Cari Hesap Ekstresi") with `transactionType=DeductionInvoices`.
773
+ *
774
+ * Page-based pagination internally (cursor encodes the page index).
775
+ */
776
+ async getCargoInvoiceItems(invoiceSerialNumber, params = {}) {
777
+ const size = Math.min(params.limit ?? 500, 500);
778
+ const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
779
+ const data = await this.transport.request({
780
+ method: "GET",
781
+ path: `/integration/finance/che/sellers/${this.sellerId}/cargo-invoice/${encodeURIComponent(invoiceSerialNumber)}/items`,
782
+ query: { page, size },
783
+ rateLimiter: this.limiter
784
+ });
785
+ const items = (data.content ?? []).map((row) => {
786
+ const out = { raw: row };
787
+ if (row.shipmentPackageType !== void 0) out.shipmentPackageType = row.shipmentPackageType;
788
+ if (row.parcelUniqueId !== void 0) {
789
+ out.parcelUniqueId = typeof row.parcelUniqueId === "string" ? row.parcelUniqueId : String(row.parcelUniqueId);
790
+ }
791
+ if (row.orderNumber !== void 0) out.orderNumber = row.orderNumber;
792
+ if (typeof row.amount === "number") out.amount = row.amount;
793
+ if (typeof row.desi === "number") out.desi = row.desi;
794
+ return out;
795
+ });
796
+ const totalPages = typeof data.totalPages === "number" ? data.totalPages : 0;
797
+ const result = { items };
798
+ if (page + 1 < totalPages) {
799
+ result.nextCursor = String(page + 1);
800
+ }
801
+ return result;
802
+ }
803
+ packagePath(packageId) {
804
+ return `/integration/order/sellers/${this.sellerId}/shipment-packages/${encodeURIComponent(String(packageId))}`;
805
+ }
469
806
  };
470
807
  var DEFAULT_PAGE_SIZE3 = 50;
471
808
  var MAX_PAGE_SIZE2 = 1e3;