@pinelab/vendure-plugin-sendcloud 1.1.0 → 1.2.1
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/CHANGELOG.md +8 -0
- package/README.md +1 -3
- package/dist/util/src/order-state-util.d.ts +4 -2
- package/dist/util/src/order-state-util.js +18 -4
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.controller.js +1 -1
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.handler.d.ts +1 -6
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.handler.js +2 -8
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.service.d.ts +10 -7
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.service.js +37 -29
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# Vendure SendCloud plugin
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
### Visit [pinelab-plugins.com](https://pinelab-plugins.com/plugin/vendure-plugin-sendcloud) for more documentation and examples.
|
|
3
|
+
### [Official documentation here](https://pinelab-plugins.com/plugin/vendure-plugin-sendcloud)
|
|
6
4
|
|
|
7
5
|
This plugin syncs orders to the SendCloud fulfillment platform.
|
|
8
6
|
|
|
@@ -8,12 +8,14 @@ export declare function fulfillAll(ctx: RequestContext, orderService: OrderServi
|
|
|
8
8
|
/**
|
|
9
9
|
* Fulfills all items to shipped using transitionFulfillmentToState
|
|
10
10
|
*/
|
|
11
|
-
export declare function transitionToShipped(orderService: OrderService, ctx: RequestContext, order: Order, handler: ConfigurableOperationInput): Promise<Fulfillment>;
|
|
11
|
+
export declare function transitionToShipped(orderService: OrderService, ctx: RequestContext, order: Order, handler: ConfigurableOperationInput): Promise<Fulfillment | FulfillmentStateTransitionError>;
|
|
12
12
|
/**
|
|
13
13
|
* Fulfills all items to shipped, then to delivered using transitionFulfillmentToState
|
|
14
14
|
*/
|
|
15
|
-
export declare function transitionToDelivered(orderService: OrderService, ctx: RequestContext, order: Order, handler: ConfigurableOperationInput): Promise<Fulfillment>;
|
|
15
|
+
export declare function transitionToDelivered(orderService: OrderService, ctx: RequestContext, order: Order, handler: ConfigurableOperationInput): Promise<(Fulfillment | FulfillmentStateTransitionError)[]>;
|
|
16
16
|
/**
|
|
17
17
|
* Throws the error result if the transition failed
|
|
18
|
+
* Ignores transition errors where from and to state are the same,
|
|
19
|
+
* because that still results in the situation we want
|
|
18
20
|
*/
|
|
19
21
|
export declare function throwIfTransitionFailed(result: FulfillmentStateTransitionError | Fulfillment | AddFulfillmentToOrderResult): void;
|
|
@@ -36,14 +36,28 @@ exports.transitionToShipped = transitionToShipped;
|
|
|
36
36
|
* Fulfills all items to shipped, then to delivered using transitionFulfillmentToState
|
|
37
37
|
*/
|
|
38
38
|
async function transitionToDelivered(orderService, ctx, order, handler) {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
const shippedResult = await transitionToShipped(orderService, ctx, order, handler);
|
|
40
|
+
let fulfillments = [];
|
|
41
|
+
if (shippedResult.errorCode) {
|
|
42
|
+
fulfillments = await orderService.getOrderFulfillments(ctx, order);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// if not an error, shippedResult is the only fulfillment
|
|
46
|
+
fulfillments.push(shippedResult);
|
|
47
|
+
}
|
|
48
|
+
const results = [];
|
|
49
|
+
for (const fulfillment of fulfillments) {
|
|
50
|
+
const result = await orderService.transitionFulfillmentToState(ctx, fulfillment.id, 'Delivered');
|
|
51
|
+
throwIfTransitionFailed(result);
|
|
52
|
+
results.push(result);
|
|
53
|
+
}
|
|
54
|
+
return results;
|
|
43
55
|
}
|
|
44
56
|
exports.transitionToDelivered = transitionToDelivered;
|
|
45
57
|
/**
|
|
46
58
|
* Throws the error result if the transition failed
|
|
59
|
+
* Ignores transition errors where from and to state are the same,
|
|
60
|
+
* because that still results in the situation we want
|
|
47
61
|
*/
|
|
48
62
|
function throwIfTransitionFailed(result) {
|
|
49
63
|
const stateError = result;
|
|
@@ -46,7 +46,7 @@ let SendcloudController = class SendcloudController {
|
|
|
46
46
|
if (!body.parcel?.order_number) {
|
|
47
47
|
return core_1.Logger.warn(`No order_number in incoming Sendcloud webhook: ${JSON.stringify(body.parcel)}`, constants_1.loggerCtx);
|
|
48
48
|
}
|
|
49
|
-
await this.sendcloudService.updateOrderStatus(ctx, status, body.parcel.order_number
|
|
49
|
+
await this.sendcloudService.updateOrderStatus(ctx, status, body.parcel.order_number);
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
52
|
__decorate([
|
|
@@ -11,18 +11,12 @@ exports.sendcloudHandler = new core_1.FulfillmentHandler({
|
|
|
11
11
|
value: 'Send order to SendCloud',
|
|
12
12
|
},
|
|
13
13
|
],
|
|
14
|
-
args: {
|
|
15
|
-
trackingNumber: {
|
|
16
|
-
type: 'string',
|
|
17
|
-
required: false,
|
|
18
|
-
},
|
|
19
|
-
},
|
|
14
|
+
args: {},
|
|
20
15
|
createFulfillment: async (ctx, orders, orderItems, args) => {
|
|
21
16
|
const orderCodes = orders.map((o) => o.code);
|
|
22
17
|
core_1.Logger.info(`Fulfilled orders ${orderCodes.join(',')}`, constants_1.loggerCtx);
|
|
23
18
|
return {
|
|
24
|
-
method: `SendCloud - ${
|
|
25
|
-
trackingCode: args.trackingNumber,
|
|
19
|
+
method: `SendCloud - ${orderCodes.join(',')} `,
|
|
26
20
|
};
|
|
27
21
|
},
|
|
28
22
|
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { OnApplicationBootstrap
|
|
1
|
+
import { OnApplicationBootstrap } from '@nestjs/common';
|
|
2
2
|
import { ModuleRef } from '@nestjs/core';
|
|
3
3
|
import { ChannelService, EntityHydrator, EventBus, HistoryService, ID, JobQueueService, Order, OrderService, RequestContext, TransactionalConnection } from '@vendure/core';
|
|
4
4
|
import { SendcloudConfigEntity } from './sendcloud-config.entity';
|
|
5
5
|
import { SendcloudClient } from './sendcloud.client';
|
|
6
6
|
import { Parcel } from './types/sendcloud-api.types';
|
|
7
7
|
import { SendcloudParcelStatus, SendcloudPluginOptions } from './types/sendcloud.types';
|
|
8
|
-
export declare class SendcloudService implements OnApplicationBootstrap
|
|
8
|
+
export declare class SendcloudService implements OnApplicationBootstrap {
|
|
9
9
|
private eventBus;
|
|
10
10
|
private connection;
|
|
11
11
|
private orderService;
|
|
@@ -17,18 +17,21 @@ export declare class SendcloudService implements OnApplicationBootstrap, OnModul
|
|
|
17
17
|
private historyService;
|
|
18
18
|
private jobQueue;
|
|
19
19
|
constructor(eventBus: EventBus, connection: TransactionalConnection, orderService: OrderService, channelService: ChannelService, jobQueueService: JobQueueService, moduleRef: ModuleRef, options: SendcloudPluginOptions, entityHydrator: EntityHydrator, historyService: HistoryService);
|
|
20
|
-
|
|
21
|
-
onApplicationBootstrap(): void;
|
|
20
|
+
onApplicationBootstrap(): Promise<void>;
|
|
22
21
|
createOrderInSendcloud(userCtx: RequestContext, order: Order): Promise<Parcel | undefined>;
|
|
23
22
|
/**
|
|
24
23
|
* Update order by given orderCode, returns undefined if no action was taken
|
|
25
24
|
* Returns order if transition was successful
|
|
26
25
|
*/
|
|
27
|
-
updateOrderStatus(ctx: RequestContext, sendcloudStatus: SendcloudParcelStatus, orderCode: string
|
|
26
|
+
updateOrderStatus(ctx: RequestContext, sendcloudStatus: SendcloudParcelStatus, orderCode: string): Promise<void>;
|
|
28
27
|
/**
|
|
29
|
-
*
|
|
28
|
+
* Ship all items
|
|
30
29
|
*/
|
|
31
|
-
shipAll(ctx: RequestContext, order: Order
|
|
30
|
+
shipAll(ctx: RequestContext, order: Order): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Fulfill without throwing errors. Logs an error if fulfilment fails
|
|
33
|
+
*/
|
|
34
|
+
private safeFulfill;
|
|
32
35
|
upsertConfig(ctx: RequestContext, config: {
|
|
33
36
|
secret: string;
|
|
34
37
|
publicKey: string;
|
|
@@ -11,6 +11,9 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
11
11
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
12
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
13
|
};
|
|
14
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
15
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
16
|
+
};
|
|
14
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
18
|
exports.SendcloudService = void 0;
|
|
16
19
|
const common_1 = require("@nestjs/common");
|
|
@@ -22,6 +25,7 @@ const sendcloud_config_entity_1 = require("./sendcloud-config.entity");
|
|
|
22
25
|
const sendcloud_client_1 = require("./sendcloud.client");
|
|
23
26
|
const sendcloud_handler_1 = require("./sendcloud.handler");
|
|
24
27
|
const src_1 = require("../../../util/src");
|
|
28
|
+
const util_1 = __importDefault(require("util"));
|
|
25
29
|
let SendcloudService = class SendcloudService {
|
|
26
30
|
constructor(eventBus, connection,
|
|
27
31
|
// private rawConnection: Connection,
|
|
@@ -36,7 +40,15 @@ let SendcloudService = class SendcloudService {
|
|
|
36
40
|
this.entityHydrator = entityHydrator;
|
|
37
41
|
this.historyService = historyService;
|
|
38
42
|
}
|
|
39
|
-
async
|
|
43
|
+
async onApplicationBootstrap() {
|
|
44
|
+
// Listen for Settled orders to sync to sendcloud
|
|
45
|
+
this.eventBus.ofType(core_2.OrderPlacedEvent).subscribe(async (event) => {
|
|
46
|
+
await this.jobQueue.add({
|
|
47
|
+
orderCode: event.order.code,
|
|
48
|
+
ctx: event.ctx.serialize(),
|
|
49
|
+
}, { retries: 20 });
|
|
50
|
+
});
|
|
51
|
+
// Handle jobs
|
|
40
52
|
this.jobQueue = await this.jobQueueService.createQueue({
|
|
41
53
|
name: 'sendcloud',
|
|
42
54
|
process: async ({ data }) => {
|
|
@@ -51,15 +63,6 @@ let SendcloudService = class SendcloudService {
|
|
|
51
63
|
},
|
|
52
64
|
});
|
|
53
65
|
}
|
|
54
|
-
onApplicationBootstrap() {
|
|
55
|
-
// Listen for Settled orders to sync to sendcloud
|
|
56
|
-
this.eventBus.ofType(core_2.OrderPlacedEvent).subscribe(async (event) => {
|
|
57
|
-
await this.jobQueue.add({
|
|
58
|
-
orderCode: event.order.code,
|
|
59
|
-
ctx: event.ctx.serialize(),
|
|
60
|
-
}, { retries: 20 });
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
66
|
async createOrderInSendcloud(userCtx, order) {
|
|
64
67
|
if (this.options.disabled) {
|
|
65
68
|
core_2.Logger.info(`Plugin is disabled, not syncing order ${order.code}`, constants_1.loggerCtx);
|
|
@@ -94,7 +97,7 @@ let SendcloudService = class SendcloudService {
|
|
|
94
97
|
* Update order by given orderCode, returns undefined if no action was taken
|
|
95
98
|
* Returns order if transition was successful
|
|
96
99
|
*/
|
|
97
|
-
async updateOrderStatus(ctx, sendcloudStatus, orderCode
|
|
100
|
+
async updateOrderStatus(ctx, sendcloudStatus, orderCode) {
|
|
98
101
|
let order = await this.connection
|
|
99
102
|
.getRepository(ctx, core_2.Order)
|
|
100
103
|
.findOne({ where: { code: orderCode }, relations: ['lines'] });
|
|
@@ -106,42 +109,46 @@ let SendcloudService = class SendcloudService {
|
|
|
106
109
|
return core_2.Logger.info(`Not updating order with code ${orderCode}: Order already has state ${order.state}`, constants_1.loggerCtx);
|
|
107
110
|
}
|
|
108
111
|
if (sendcloudStatus.orderState === 'Shipped') {
|
|
109
|
-
await this.shipAll(ctx, order
|
|
110
|
-
return core_2.Logger.info(`Successfully
|
|
112
|
+
await this.shipAll(ctx, order);
|
|
113
|
+
return core_2.Logger.info(`Successfully updated order ${orderCode} to Shipped`, constants_1.loggerCtx);
|
|
111
114
|
}
|
|
112
115
|
order = await this.connection
|
|
113
116
|
.getRepository(ctx, core_2.Order)
|
|
114
117
|
.findOneOrFail({ where: { code: orderCode }, relations: ['lines'] }); // Refetch in case state was updated
|
|
115
118
|
if (sendcloudStatus.orderState === 'Delivered') {
|
|
116
|
-
//FIX ME
|
|
117
119
|
await (0, src_1.transitionToDelivered)(this.orderService, ctx, order, {
|
|
118
120
|
code: sendcloud_handler_1.sendcloudHandler.code,
|
|
119
|
-
arguments: [
|
|
120
|
-
{
|
|
121
|
-
name: 'trackingNumber',
|
|
122
|
-
value: trackingNumber,
|
|
123
|
-
},
|
|
124
|
-
],
|
|
121
|
+
arguments: [],
|
|
125
122
|
});
|
|
126
|
-
return core_2.Logger.info(`Successfully
|
|
123
|
+
return core_2.Logger.info(`Successfully updated order ${orderCode} to Delivered`, constants_1.loggerCtx);
|
|
127
124
|
}
|
|
128
125
|
// Fall through, means unhandled state
|
|
129
126
|
core_2.Logger.info(`Not handling state ${sendcloudStatus.orderState}`, constants_1.loggerCtx);
|
|
130
127
|
}
|
|
131
128
|
/**
|
|
132
|
-
*
|
|
129
|
+
* Ship all items
|
|
133
130
|
*/
|
|
134
|
-
async shipAll(ctx, order
|
|
131
|
+
async shipAll(ctx, order) {
|
|
135
132
|
await (0, src_1.transitionToShipped)(this.orderService, ctx, order, {
|
|
136
133
|
code: sendcloud_handler_1.sendcloudHandler.code,
|
|
137
|
-
arguments: [
|
|
138
|
-
{
|
|
139
|
-
name: 'trackingNumber',
|
|
140
|
-
value: trackingNumber,
|
|
141
|
-
},
|
|
142
|
-
],
|
|
134
|
+
arguments: [],
|
|
143
135
|
});
|
|
144
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Fulfill without throwing errors. Logs an error if fulfilment fails
|
|
139
|
+
*/
|
|
140
|
+
async safeFulfill(ctx, order) {
|
|
141
|
+
try {
|
|
142
|
+
const fulfillment = await (0, src_1.fulfillAll)(ctx, this.orderService, order, {
|
|
143
|
+
code: sendcloud_handler_1.sendcloudHandler.code,
|
|
144
|
+
arguments: [],
|
|
145
|
+
});
|
|
146
|
+
core_2.Logger.info(`Created fulfillment (${fulfillment.id}) for order ${order.code}`, constants_1.loggerCtx);
|
|
147
|
+
}
|
|
148
|
+
catch (e) {
|
|
149
|
+
core_2.Logger.error(`Failed to fulfill order ${order.code}: ${e?.message}. Transition this order manually to 'Delivered' after checking that it exists in Sendcloud.`, constants_1.loggerCtx, util_1.default.inspect(e));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
145
152
|
async upsertConfig(ctx, config) {
|
|
146
153
|
const repo = this.connection.getRepository(ctx, sendcloud_config_entity_1.SendcloudConfigEntity);
|
|
147
154
|
const existing = await repo.findOne({
|
|
@@ -209,6 +216,7 @@ let SendcloudService = class SendcloudService {
|
|
|
209
216
|
if (!hasSendcloudHandler) {
|
|
210
217
|
return core_2.Logger.info(`Order ${order.code} does not have SendCloud set as handler. Not syncing this order.`, constants_1.loggerCtx);
|
|
211
218
|
}
|
|
219
|
+
await this.safeFulfill(ctx, order);
|
|
212
220
|
core_2.Logger.info(`Syncing order ${orderCode} for channel ${ctx.channel.token}`, constants_1.loggerCtx);
|
|
213
221
|
const result = await this.createOrderInSendcloud(ctx, order);
|
|
214
222
|
if (result) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pinelab/vendure-plugin-sendcloud",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Vendure plugin for syncing orders with SendCloud",
|
|
5
|
+
"icon": "truck",
|
|
5
6
|
"author": "Martijn van de Brug <martijn@pinelab.studio>",
|
|
6
7
|
"homepage": "https://pinelab-plugins.com/",
|
|
7
8
|
"repository": "https://github.com/Pinelab-studio/pinelab-vendure-plugins",
|
|
@@ -22,5 +23,5 @@
|
|
|
22
23
|
"test": "vitest run",
|
|
23
24
|
"start": "yarn ts-node test/dev-server.ts"
|
|
24
25
|
},
|
|
25
|
-
"gitHead": "
|
|
26
|
+
"gitHead": "f2584b3c12624d023dcf9a10383e1c074e35b5d5"
|
|
26
27
|
}
|