@lodashventure/medusa-notification-webhook 0.5.5 → 0.5.6
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/.medusa/server/src/constants/customer-fields.js +2 -1
- package/.medusa/server/src/constants/quote-fields.js +4 -1
- package/.medusa/server/src/modules/event-hook/service.js +17 -2
- package/.medusa/server/src/subscribers/customer-created.js +115 -0
- package/.medusa/server/src/subscribers/quote-sent.js +354 -0
- package/.medusa/server/src/workflows/call-external-api.js +1 -1
- package/.medusa/server/src/workflows/create-novu-subscriber.js +68 -0
- package/README.md +34 -0
- package/package.json +19 -19
|
@@ -10,5 +10,6 @@ exports.customerFields = [
|
|
|
10
10
|
"customer.first_name",
|
|
11
11
|
"customer.last_name",
|
|
12
12
|
"customer.company_name",
|
|
13
|
+
"customer.metadata",
|
|
13
14
|
];
|
|
14
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
15
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tZXItZmllbGRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2NvbnN0YW50cy9jdXN0b21lci1maWVsZHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQWEsUUFBQSxjQUFjLEdBQUc7SUFDNUIsV0FBVztJQUNYLGFBQWE7SUFDYixnQkFBZ0I7SUFDaEIsZ0JBQWdCO0lBQ2hCLHNCQUFzQjtJQUN0QixxQkFBcUI7SUFDckIsb0JBQW9CO0lBQ3BCLHVCQUF1QjtJQUN2QixtQkFBbUI7Q0FDcEIsQ0FBQyJ9
|
|
@@ -3,7 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.quoteFields = void 0;
|
|
4
4
|
exports.quoteFields = [
|
|
5
5
|
"id",
|
|
6
|
+
"doc_no",
|
|
6
7
|
"status",
|
|
8
|
+
"created_at",
|
|
9
|
+
"updated_at",
|
|
7
10
|
"cart.id",
|
|
8
11
|
"draft_order.id",
|
|
9
12
|
"draft_order.currency_code",
|
|
@@ -42,4 +45,4 @@ exports.quoteFields = [
|
|
|
42
45
|
"draft_order.items.detail.*",
|
|
43
46
|
"draft_order.payment_collections.*",
|
|
44
47
|
];
|
|
45
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
48
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXVvdGUtZmllbGRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2NvbnN0YW50cy9xdW90ZS1maWVsZHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQWEsUUFBQSxXQUFXLEdBQUc7SUFDekIsSUFBSTtJQUNKLFFBQVE7SUFDUixRQUFRO0lBQ1IsWUFBWTtJQUNaLFlBQVk7SUFDWixTQUFTO0lBQ1QsZ0JBQWdCO0lBQ2hCLDJCQUEyQjtJQUMzQix3QkFBd0I7SUFDeEIsdUJBQXVCO0lBQ3ZCLG9CQUFvQjtJQUNwQixxQkFBcUI7SUFDckIscUJBQXFCO0lBQ3JCLG1CQUFtQjtJQUNuQixzQkFBc0I7SUFDdEIsdUJBQXVCO0lBQ3ZCLDBCQUEwQjtJQUMxQiw0QkFBNEI7SUFDNUIsZ0NBQWdDO0lBQ2hDLDRCQUE0QjtJQUM1QixnQ0FBZ0M7SUFDaEMsd0JBQXdCO0lBQ3hCLDJCQUEyQjtJQUMzQiw0QkFBNEI7SUFDNUIsaUNBQWlDO0lBQ2pDLG9DQUFvQztJQUNwQyxxQ0FBcUM7SUFDckMsNEJBQTRCO0lBQzVCLCtCQUErQjtJQUMvQixnQ0FBZ0M7SUFDaEMseUNBQXlDO0lBQ3pDLHdDQUF3QztJQUN4QyxxQ0FBcUM7SUFDckMsd0JBQXdCO0lBQ3hCLHdCQUF3QjtJQUN4QixxQkFBcUI7SUFDckIsK0JBQStCO0lBQy9CLGlDQUFpQztJQUNqQyw2QkFBNkI7SUFDN0IscUNBQXFDO0lBQ3JDLDRCQUE0QjtJQUM1QixtQ0FBbUM7Q0FDcEMsQ0FBQyJ9
|
|
@@ -8,9 +8,24 @@ class EventHookModuleService extends (0, utils_1.MedusaService)({}) {
|
|
|
8
8
|
webHookConfig: {},
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
|
+
normalizeConfig(config) {
|
|
12
|
+
const normalized = { ...config };
|
|
13
|
+
if (!normalized.requestQuotationHook &&
|
|
14
|
+
normalized.requestQuotationHookUrl) {
|
|
15
|
+
normalized.requestQuotationHook = normalized.requestQuotationHookUrl;
|
|
16
|
+
}
|
|
17
|
+
if (!normalized.deliveryStartedHook &&
|
|
18
|
+
normalized.deliveryStartedHookUrl) {
|
|
19
|
+
normalized.deliveryStartedHook = normalized.deliveryStartedHookUrl;
|
|
20
|
+
}
|
|
21
|
+
return normalized;
|
|
22
|
+
}
|
|
11
23
|
async getWebHookOptions() {
|
|
12
|
-
|
|
24
|
+
if (!this.normalizedConfig) {
|
|
25
|
+
this.normalizedConfig = this.normalizeConfig(this._options.webHookConfig);
|
|
26
|
+
}
|
|
27
|
+
return this.normalizedConfig;
|
|
13
28
|
}
|
|
14
29
|
}
|
|
15
30
|
exports.default = EventHookModuleService;
|
|
16
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
31
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9tb2R1bGVzL2V2ZW50LWhvb2svc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHFEQUEwRDtBQUcxRCxNQUFxQixzQkFBdUIsU0FBUSxJQUFBLHFCQUFhLEVBQUMsRUFBRSxDQUFDO0lBSW5FLFlBQVksRUFBRSxFQUFFLE9BQXVCO1FBQ3JDLEtBQUssQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDO1FBRXBCLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxJQUFJO1lBQ3pCLGFBQWEsRUFBRSxFQUFFO1NBQ2xCLENBQUM7SUFDSixDQUFDO0lBRVMsZUFBZSxDQUN2QixNQUFzQztRQUV0QyxNQUFNLFVBQVUsR0FBRyxFQUFFLEdBQUcsTUFBTSxFQUFFLENBQUM7UUFFakMsSUFDRSxDQUFDLFVBQVUsQ0FBQyxvQkFBb0I7WUFDaEMsVUFBVSxDQUFDLHVCQUF1QixFQUNsQyxDQUFDO1lBQ0QsVUFBVSxDQUFDLG9CQUFvQixHQUFHLFVBQVUsQ0FBQyx1QkFBdUIsQ0FBQztRQUN2RSxDQUFDO1FBRUQsSUFDRSxDQUFDLFVBQVUsQ0FBQyxtQkFBbUI7WUFDL0IsVUFBVSxDQUFDLHNCQUFzQixFQUNqQyxDQUFDO1lBQ0QsVUFBVSxDQUFDLG1CQUFtQixHQUFHLFVBQVUsQ0FBQyxzQkFBc0IsQ0FBQztRQUNyRSxDQUFDO1FBRUQsT0FBTyxVQUFVLENBQUM7SUFDcEIsQ0FBQztJQUVELEtBQUssQ0FBQyxpQkFBaUI7UUFDckIsSUFBSSxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1lBQzNCLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDNUUsQ0FBQztRQUVELE9BQU8sSUFBSSxDQUFDLGdCQUFnQixDQUFDO0lBQy9CLENBQUM7Q0FDRjtBQXpDRCx5Q0F5Q0MifQ==
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.config = void 0;
|
|
7
|
+
exports.default = customerCreatedNovuSubscriber;
|
|
8
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
9
|
+
const event_hook_1 = require("../modules/event-hook");
|
|
10
|
+
const create_novu_subscriber_1 = __importDefault(require("../workflows/create-novu-subscriber"));
|
|
11
|
+
const mapChannels = (customer) => {
|
|
12
|
+
const channels = [];
|
|
13
|
+
if (customer.email) {
|
|
14
|
+
channels.push({
|
|
15
|
+
type: "email",
|
|
16
|
+
identity: customer.email,
|
|
17
|
+
metadata: {
|
|
18
|
+
has_account: customer.has_account ?? false,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
const metadata = (customer.metadata ?? {});
|
|
23
|
+
const lineDisplayName = typeof metadata["line_display_name"] === "string"
|
|
24
|
+
? metadata["line_display_name"]
|
|
25
|
+
: undefined;
|
|
26
|
+
const linePictureUrl = typeof metadata["line_picture_url"] === "string"
|
|
27
|
+
? metadata["line_picture_url"]
|
|
28
|
+
: undefined;
|
|
29
|
+
const lineLinkedAt = typeof metadata["line_linked_at"] === "string"
|
|
30
|
+
? metadata["line_linked_at"]
|
|
31
|
+
: undefined;
|
|
32
|
+
const lineUserId = typeof metadata["line_user_id"] === "string"
|
|
33
|
+
? metadata["line_user_id"]
|
|
34
|
+
: undefined;
|
|
35
|
+
if (lineUserId) {
|
|
36
|
+
channels.push({
|
|
37
|
+
type: "line",
|
|
38
|
+
identity: lineUserId,
|
|
39
|
+
metadata: {
|
|
40
|
+
display_name: lineDisplayName,
|
|
41
|
+
picture_url: linePictureUrl,
|
|
42
|
+
linked_at: lineLinkedAt,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return channels;
|
|
47
|
+
};
|
|
48
|
+
async function customerCreatedNovuSubscriber({ event: { data }, container, }) {
|
|
49
|
+
const logger = container.resolve("logger");
|
|
50
|
+
const eventHookService = container.resolve(event_hook_1.EVENT_HOOK_MODULE);
|
|
51
|
+
const customerService = container.resolve(utils_1.Modules.CUSTOMER);
|
|
52
|
+
const webHookOptions = await eventHookService.getWebHookOptions();
|
|
53
|
+
const subscriberHook = webHookOptions.customerCreatedSubscriberHook;
|
|
54
|
+
if (!subscriberHook?.url) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (!subscriberHook.authHeader?.headerAuthKey ||
|
|
58
|
+
!subscriberHook.authHeader?.headerAuthValue) {
|
|
59
|
+
logger.warn("[Novu] Customer subscriber hook configured without auth headers. Skipping subscriber creation.");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
let customer = null;
|
|
63
|
+
try {
|
|
64
|
+
customer = await customerService.retrieveCustomer(data.id);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
logger.warn(`[Novu] Unable to retrieve customer ${data.id} for Novu sync. ${error instanceof Error ? error.message : ""}`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const channels = mapChannels(customer);
|
|
71
|
+
if (channels.length === 0) {
|
|
72
|
+
logger.info(`[Novu] Customer ${customer.id} does not have email or LINE channels. Skipping Novu subscriber creation.`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const metadata = (customer.metadata ?? {});
|
|
76
|
+
const subscriberPayload = {
|
|
77
|
+
subscriberId: customer.id,
|
|
78
|
+
email: customer.email ?? undefined,
|
|
79
|
+
firstName: customer.first_name ?? undefined,
|
|
80
|
+
lastName: customer.last_name ?? undefined,
|
|
81
|
+
phone: customer.phone ?? undefined,
|
|
82
|
+
data: {
|
|
83
|
+
has_account: customer.has_account ?? false,
|
|
84
|
+
company_name: customer.company_name,
|
|
85
|
+
metadata,
|
|
86
|
+
channels: channels.reduce((acc, channel) => {
|
|
87
|
+
acc[channel.type] = {
|
|
88
|
+
identity: channel.identity,
|
|
89
|
+
...channel.metadata,
|
|
90
|
+
};
|
|
91
|
+
return acc;
|
|
92
|
+
}, {}),
|
|
93
|
+
},
|
|
94
|
+
channels,
|
|
95
|
+
};
|
|
96
|
+
try {
|
|
97
|
+
await create_novu_subscriber_1.default.run({
|
|
98
|
+
input: {
|
|
99
|
+
name: subscriberHook.name ?? "customer-created",
|
|
100
|
+
url: subscriberHook.url,
|
|
101
|
+
authKey: subscriberHook.authHeader.headerAuthKey,
|
|
102
|
+
authValue: subscriberHook.authHeader.headerAuthValue,
|
|
103
|
+
subscriber: subscriberPayload,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
logger.info(`[Novu] Synced customer ${customer.id} as subscriber with ${channels.length} channel(s).`);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
logger.error(`[Novu] Failed to sync customer ${customer.id} with Novu subscriber API:`, error);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
exports.config = {
|
|
113
|
+
event: "customer.created",
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tZXItY3JlYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9zdWJzY3JpYmVycy9jdXN0b21lci1jcmVhdGVkLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQTJEQSxnREF1RkM7QUFoSkQscURBQW9EO0FBRXBELHNEQUEwRDtBQUMxRCxpR0FFNkM7QUFNN0MsTUFBTSxXQUFXLEdBQUcsQ0FBQyxRQUFxQixFQUF3QixFQUFFO0lBQ2xFLE1BQU0sUUFBUSxHQUF5QixFQUFFLENBQUM7SUFFMUMsSUFBSSxRQUFRLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDbkIsUUFBUSxDQUFDLElBQUksQ0FBQztZQUNaLElBQUksRUFBRSxPQUFPO1lBQ2IsUUFBUSxFQUFFLFFBQVEsQ0FBQyxLQUFLO1lBQ3hCLFFBQVEsRUFBRTtnQkFDUixXQUFXLEVBQUUsUUFBUSxDQUFDLFdBQVcsSUFBSSxLQUFLO2FBQzNDO1NBQ0YsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELE1BQU0sUUFBUSxHQUFHLENBQUMsUUFBUSxDQUFDLFFBQVEsSUFBSSxFQUFFLENBQTRCLENBQUM7SUFDdEUsTUFBTSxlQUFlLEdBQ25CLE9BQU8sUUFBUSxDQUFDLG1CQUFtQixDQUFDLEtBQUssUUFBUTtRQUMvQyxDQUFDLENBQUUsUUFBUSxDQUFDLG1CQUFtQixDQUFZO1FBQzNDLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFDaEIsTUFBTSxjQUFjLEdBQ2xCLE9BQU8sUUFBUSxDQUFDLGtCQUFrQixDQUFDLEtBQUssUUFBUTtRQUM5QyxDQUFDLENBQUUsUUFBUSxDQUFDLGtCQUFrQixDQUFZO1FBQzFDLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFDaEIsTUFBTSxZQUFZLEdBQ2hCLE9BQU8sUUFBUSxDQUFDLGdCQUFnQixDQUFDLEtBQUssUUFBUTtRQUM1QyxDQUFDLENBQUUsUUFBUSxDQUFDLGdCQUFnQixDQUFZO1FBQ3hDLENBQUMsQ0FBQyxTQUFT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
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.config = void 0;
|
|
7
|
+
exports.default = quoteSentSubscriber;
|
|
8
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const promises_1 = require("fs/promises");
|
|
11
|
+
const event_hook_1 = require("../modules/event-hook");
|
|
12
|
+
const quote_fields_1 = require("../constants/quote-fields");
|
|
13
|
+
const customer_fields_1 = require("../constants/customer-fields");
|
|
14
|
+
const call_external_api_1 = __importDefault(require("../workflows/call-external-api"));
|
|
15
|
+
const FLEX_TEMPLATE_PATH = path_1.default.join(process.cwd(), "flex/qoute.json");
|
|
16
|
+
const LINE_PUSH_ENDPOINT = "https://api.line.me/v2/bot/message/push";
|
|
17
|
+
const STATUS_LABELS = {
|
|
18
|
+
draft: "ร่าง",
|
|
19
|
+
pending_merchant: "รอตรวจสอบ",
|
|
20
|
+
pending_customer: "รอการยืนยัน",
|
|
21
|
+
approved: "อนุมัติแล้ว",
|
|
22
|
+
accepted: "ลูกค้ายืนยันแล้ว",
|
|
23
|
+
customer_rejected: "ลูกค้าปฏิเสธ",
|
|
24
|
+
merchant_rejected: "ยกเลิกโดยร้านค้า",
|
|
25
|
+
voided: "ยกเลิกแล้ว",
|
|
26
|
+
expired: "หมดอายุ",
|
|
27
|
+
converted: "แปลงเป็นออเดอร์แล้ว",
|
|
28
|
+
};
|
|
29
|
+
let cachedFlexTemplate = null;
|
|
30
|
+
const loadFlexTemplate = async () => {
|
|
31
|
+
if (cachedFlexTemplate) {
|
|
32
|
+
return cachedFlexTemplate;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const file = await (0, promises_1.readFile)(FLEX_TEMPLATE_PATH, "utf-8");
|
|
36
|
+
cachedFlexTemplate = JSON.parse(file);
|
|
37
|
+
return cachedFlexTemplate;
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
console.warn(`[Quote] Unable to load LINE flex template at ${FLEX_TEMPLATE_PATH}`, error);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const cloneTemplate = async () => {
|
|
45
|
+
const template = await loadFlexTemplate();
|
|
46
|
+
if (!template) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
return JSON.parse(JSON.stringify(template));
|
|
50
|
+
};
|
|
51
|
+
const formatCurrency = (amount, currency) => {
|
|
52
|
+
const safeCurrency = currency || "THB";
|
|
53
|
+
const safeAmount = typeof amount === "number" ? amount : 0;
|
|
54
|
+
try {
|
|
55
|
+
return new Intl.NumberFormat("th-TH", {
|
|
56
|
+
style: "currency",
|
|
57
|
+
currency: safeCurrency,
|
|
58
|
+
}).format(safeAmount);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return `${safeAmount.toFixed(2)} ${safeCurrency}`;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const formatDateTime = (value) => {
|
|
65
|
+
if (!value) {
|
|
66
|
+
return "";
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
return new Intl.DateTimeFormat("th-TH", {
|
|
70
|
+
dateStyle: "medium",
|
|
71
|
+
timeStyle: "short",
|
|
72
|
+
}).format(new Date(value));
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const itemQuantityText = (item) => {
|
|
79
|
+
const quantity = typeof item.detail?.quantity === "number"
|
|
80
|
+
? item.detail?.quantity
|
|
81
|
+
: item.quantity;
|
|
82
|
+
if (typeof quantity === "number" && !Number.isNaN(quantity)) {
|
|
83
|
+
return `${quantity} ชิ้น`;
|
|
84
|
+
}
|
|
85
|
+
return "1 ชิ้น";
|
|
86
|
+
};
|
|
87
|
+
const buildItemBox = (index, item, currency) => {
|
|
88
|
+
return {
|
|
89
|
+
type: "box",
|
|
90
|
+
layout: "horizontal",
|
|
91
|
+
contents: [
|
|
92
|
+
{
|
|
93
|
+
type: "box",
|
|
94
|
+
layout: "vertical",
|
|
95
|
+
contents: [
|
|
96
|
+
{
|
|
97
|
+
type: "text",
|
|
98
|
+
text: `${index + 1}.`,
|
|
99
|
+
size: "sm",
|
|
100
|
+
color: "#666666",
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
width: "25px",
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
type: "box",
|
|
107
|
+
layout: "vertical",
|
|
108
|
+
contents: [
|
|
109
|
+
{
|
|
110
|
+
type: "text",
|
|
111
|
+
text: item.title || "สินค้า",
|
|
112
|
+
size: "sm",
|
|
113
|
+
wrap: true,
|
|
114
|
+
color: "#333333",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
type: "box",
|
|
118
|
+
layout: "horizontal",
|
|
119
|
+
contents: [
|
|
120
|
+
{
|
|
121
|
+
type: "text",
|
|
122
|
+
text: itemQuantityText(item),
|
|
123
|
+
size: "xs",
|
|
124
|
+
color: "#666666",
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
type: "text",
|
|
128
|
+
text: formatCurrency(item.detail?.unit_price, currency),
|
|
129
|
+
size: "xs",
|
|
130
|
+
color: "#1DB446",
|
|
131
|
+
weight: "bold",
|
|
132
|
+
align: "end",
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
flex: 1,
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
margin: "md",
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
const getQuoteLabel = (quote) => {
|
|
144
|
+
if (quote.doc_no) {
|
|
145
|
+
return quote.doc_no;
|
|
146
|
+
}
|
|
147
|
+
if (quote.id?.length > 6) {
|
|
148
|
+
return `#${quote.id.slice(-6)}`;
|
|
149
|
+
}
|
|
150
|
+
return `#${quote.id}`;
|
|
151
|
+
};
|
|
152
|
+
const buildFlexBubble = async (quote) => {
|
|
153
|
+
const bubble = await cloneTemplate();
|
|
154
|
+
if (!bubble) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
const quoteLabel = getQuoteLabel(quote);
|
|
158
|
+
const statusLabel = STATUS_LABELS[quote.status] ?? quote.status ?? "สถานะไม่ระบุ";
|
|
159
|
+
const customerName = [quote.customer?.first_name, quote.customer?.last_name]
|
|
160
|
+
.filter(Boolean)
|
|
161
|
+
.join(" ")
|
|
162
|
+
.trim() ||
|
|
163
|
+
quote.customer?.email ||
|
|
164
|
+
"ลูกค้า";
|
|
165
|
+
const currency = quote.draft_order?.currency_code || "THB";
|
|
166
|
+
const items = quote.draft_order?.items || [];
|
|
167
|
+
const header = bubble.header;
|
|
168
|
+
if (header?.contents?.[0]?.contents?.[0]) {
|
|
169
|
+
header.contents[0].contents[0].text = `ใบเสนอราคา ${quoteLabel}`;
|
|
170
|
+
}
|
|
171
|
+
if (header?.contents?.[0]?.contents?.[1]) {
|
|
172
|
+
header.contents[0].contents[1].text = statusLabel;
|
|
173
|
+
}
|
|
174
|
+
const body = bubble.body;
|
|
175
|
+
if (body?.contents?.[0]?.contents?.[1]) {
|
|
176
|
+
body.contents[0].contents[1].text = items.length
|
|
177
|
+
? `${items.length} รายการ`
|
|
178
|
+
: "ไม่มีสินค้า";
|
|
179
|
+
}
|
|
180
|
+
const lineItemsBox = body?.contents?.[2];
|
|
181
|
+
if (lineItemsBox) {
|
|
182
|
+
const maxItems = 4;
|
|
183
|
+
const renderedItems = items
|
|
184
|
+
.slice(0, maxItems)
|
|
185
|
+
.map((item, index) => buildItemBox(index, item, currency));
|
|
186
|
+
if (items.length > maxItems) {
|
|
187
|
+
renderedItems.push({
|
|
188
|
+
type: "text",
|
|
189
|
+
text: `... และอีก ${items.length - maxItems} รายการ`,
|
|
190
|
+
size: "xs",
|
|
191
|
+
color: "#999999",
|
|
192
|
+
align: "center",
|
|
193
|
+
margin: "md",
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
if (renderedItems.length === 0) {
|
|
197
|
+
renderedItems.push({
|
|
198
|
+
type: "text",
|
|
199
|
+
text: "ยังไม่มีสินค้าในใบเสนอราคา",
|
|
200
|
+
size: "xs",
|
|
201
|
+
color: "#999999",
|
|
202
|
+
align: "center",
|
|
203
|
+
margin: "md",
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
lineItemsBox.contents = renderedItems;
|
|
207
|
+
delete lineItemsBox.height;
|
|
208
|
+
}
|
|
209
|
+
const totalsBox = body?.contents?.[4];
|
|
210
|
+
if (totalsBox?.contents?.[0]?.contents?.[1]) {
|
|
211
|
+
totalsBox.contents[0].contents[1].text = formatCurrency(quote.draft_order?.subtotal, currency);
|
|
212
|
+
}
|
|
213
|
+
if (totalsBox?.contents?.[1]?.contents?.[0]) {
|
|
214
|
+
totalsBox.contents[1].contents[0].text = "ภาษีทั้งหมด";
|
|
215
|
+
}
|
|
216
|
+
if (totalsBox?.contents?.[1]?.contents?.[1]) {
|
|
217
|
+
totalsBox.contents[1].contents[1].text = formatCurrency(quote.draft_order?.tax_total, currency);
|
|
218
|
+
}
|
|
219
|
+
const grandTotalBox = body?.contents?.[6];
|
|
220
|
+
if (grandTotalBox?.contents?.[1]) {
|
|
221
|
+
grandTotalBox.contents[1].text = formatCurrency(quote.draft_order?.total, currency);
|
|
222
|
+
}
|
|
223
|
+
const footer = bubble.footer;
|
|
224
|
+
const footerInfoBox = footer?.contents?.[0];
|
|
225
|
+
if (footerInfoBox?.contents?.[0]) {
|
|
226
|
+
footerInfoBox.contents[0].text = `ลูกค้า: ${customerName}`;
|
|
227
|
+
}
|
|
228
|
+
if (footerInfoBox?.contents?.[1]) {
|
|
229
|
+
footerInfoBox.contents[1].text = `วันที่: ${formatDateTime(quote.updated_at || quote.created_at)}`;
|
|
230
|
+
}
|
|
231
|
+
const buttonsBox = footer?.contents?.[2];
|
|
232
|
+
const baseUrl = process.env.STOREFRONT_URL || "https://localhost:3000";
|
|
233
|
+
const detailUrl = `${baseUrl.replace(/\/$/, "")}/quotation/${quote.id}`;
|
|
234
|
+
const confirmUrl = `${detailUrl}?action=accept`;
|
|
235
|
+
if (buttonsBox?.contents?.[0]?.action) {
|
|
236
|
+
buttonsBox.contents[0].action.uri = confirmUrl;
|
|
237
|
+
}
|
|
238
|
+
if (buttonsBox?.contents?.[1]?.action) {
|
|
239
|
+
buttonsBox.contents[1].action.uri = detailUrl;
|
|
240
|
+
}
|
|
241
|
+
return bubble;
|
|
242
|
+
};
|
|
243
|
+
const sendLineFlexMessage = async (logger, lineUserId, bubble, quote) => {
|
|
244
|
+
const accessToken = process.env.LINE_MESSAGING_CHANNEL_ACCESS_TOKEN;
|
|
245
|
+
if (!accessToken) {
|
|
246
|
+
logger.warn("[Quote] LINE_MESSAGING_CHANNEL_ACCESS_TOKEN is not configured. Skipping LINE notification.");
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
const payload = {
|
|
250
|
+
to: lineUserId,
|
|
251
|
+
messages: [
|
|
252
|
+
{
|
|
253
|
+
type: "flex",
|
|
254
|
+
altText: `ใบเสนอราคา ${getQuoteLabel(quote)}`,
|
|
255
|
+
contents: bubble,
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
};
|
|
259
|
+
const response = await fetch(LINE_PUSH_ENDPOINT, {
|
|
260
|
+
method: "POST",
|
|
261
|
+
headers: {
|
|
262
|
+
"Content-Type": "application/json",
|
|
263
|
+
Authorization: `Bearer ${accessToken}`,
|
|
264
|
+
},
|
|
265
|
+
body: JSON.stringify(payload),
|
|
266
|
+
});
|
|
267
|
+
if (!response.ok) {
|
|
268
|
+
const body = await response.text();
|
|
269
|
+
throw new Error(`LINE push message failed: ${response.status} ${response.statusText} - ${body}`);
|
|
270
|
+
}
|
|
271
|
+
return true;
|
|
272
|
+
};
|
|
273
|
+
const triggerNovu = async (logger, quote, hook) => {
|
|
274
|
+
if (!hook?.url ||
|
|
275
|
+
!hook.authHeader?.headerAuthKey ||
|
|
276
|
+
!hook.authHeader.headerAuthValue) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
const customerMetadata = (quote.customer?.metadata ?? {});
|
|
280
|
+
const subscriberId = (typeof customerMetadata.novu_subscriber_id === "string"
|
|
281
|
+
? customerMetadata.novu_subscriber_id
|
|
282
|
+
: undefined) ??
|
|
283
|
+
quote.customer?.id ??
|
|
284
|
+
quote.customer_id ??
|
|
285
|
+
quote.customer?.email;
|
|
286
|
+
if (!subscriberId) {
|
|
287
|
+
logger.warn(`[Quote] Unable to resolve Novu subscriber for quote ${quote.id}, skipping email trigger.`);
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
const novuPayload = {
|
|
291
|
+
name: hook.name ?? "quote-sent",
|
|
292
|
+
to: subscriberId,
|
|
293
|
+
payload: { quote },
|
|
294
|
+
};
|
|
295
|
+
await call_external_api_1.default.run({
|
|
296
|
+
input: {
|
|
297
|
+
name: hook.name ?? "quote-sent",
|
|
298
|
+
url: hook.url,
|
|
299
|
+
authKey: hook.authHeader.headerAuthKey,
|
|
300
|
+
authValue: hook.authHeader.headerAuthValue,
|
|
301
|
+
type: "QUOTE_SENT",
|
|
302
|
+
payload: novuPayload,
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
return true;
|
|
306
|
+
};
|
|
307
|
+
async function quoteSentSubscriber({ event: { data }, container, }) {
|
|
308
|
+
const logger = container.resolve("logger");
|
|
309
|
+
const query = container.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
310
|
+
const eventHookService = container.resolve(event_hook_1.EVENT_HOOK_MODULE);
|
|
311
|
+
const { data: [quote], } = await query.graph({
|
|
312
|
+
entity: "quote",
|
|
313
|
+
fields: [...quote_fields_1.quoteFields, ...customer_fields_1.customerFields],
|
|
314
|
+
filters: { id: data.id },
|
|
315
|
+
}, { throwIfKeyNotFound: true });
|
|
316
|
+
if (!quote) {
|
|
317
|
+
logger.warn(`[Quote] Unable to load quote ${data.id} for notification dispatch.`);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const customerMetadata = (quote.customer?.metadata ?? {});
|
|
321
|
+
const lineUserId = typeof customerMetadata.line_user_id === "string"
|
|
322
|
+
? customerMetadata.line_user_id
|
|
323
|
+
: undefined;
|
|
324
|
+
if (lineUserId) {
|
|
325
|
+
try {
|
|
326
|
+
const bubble = await buildFlexBubble(quote);
|
|
327
|
+
if (bubble) {
|
|
328
|
+
await sendLineFlexMessage(logger, lineUserId, bubble, quote);
|
|
329
|
+
logger.info(`[Quote] Sent LINE flex message for quote ${quote.id}`);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
catch (error) {
|
|
334
|
+
logger.error(`[Quote] Failed to send LINE flex message for quote ${quote.id}`, error);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const webHookOptions = await eventHookService.getWebHookOptions();
|
|
338
|
+
const quoteSentHook = webHookOptions.quoteSentHook || webHookOptions.requestQuotationHook;
|
|
339
|
+
if (!quoteSentHook?.url) {
|
|
340
|
+
logger.warn(`[Quote] No Novu webhook configured for quote sent event. Quote ${quote.id} was not notified via email channel.`);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
try {
|
|
344
|
+
await triggerNovu(logger, quote, quoteSentHook);
|
|
345
|
+
logger.info(`[Quote] Triggered Novu quote-sent hook for ${quote.id}`);
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
logger.error(`[Quote] Failed to trigger Novu quote-sent hook for ${quote.id}`, error);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
exports.config = {
|
|
352
|
+
event: "quote.sent",
|
|
353
|
+
};
|
|
354
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXVvdGUtc2VudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9zdWJzY3JpYmVycy9xdW90ZS1zZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQXdiQSxzQ0F3RUM7QUEvZkQscURBQXNFO0FBRXRFLGdEQUF3QjtBQUN4QiwwQ0FBdUM7QUFFdkMsc0RBQTBEO0FBQzFELDREQUF3RDtBQUN4RCxrRUFBOEQ7QUFDOUQsdUZBQXFFO0FBK0NyRSxNQUFNLGtCQUFrQixHQUFHLGNBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxFQUFFLGlCQUFpQixDQUFDLENBQUM7QUFDdkUsTUFBTSxrQkFBa0IsR0FBRyx5Q0FBeUMsQ0FBQztBQUNyRSxNQUFNLGFBQWEsR0FBMkI7SUFDNUMsS0FBSyxFQUFFLE1BQU07SUFDYixnQkFBZ0IsRUFBRSxXQUFXO0lBQzdCLGdCQUFnQixFQUFFLGFBQWE7SUFDL0IsUUFBUSxFQUFFLGFBQWE7SUFDdkIsUUFBUSxFQUFFLGtCQUFrQjtJQUM1QixpQkFBaUIsRUFBRSxjQUFjO0lBQ2pDLGlCQUFpQixFQUFFLGtCQUFrQjtJQUNyQyxNQUFNLEVBQUUsWUFBWTtJQUNwQixPQUFPLEVBQUUsU0FBUztJQUNsQixTQUFTLEVBQUUscUJBQXFCO0NBQ2pDLENBQUM7QUFFRixJQUFJLGtCQUFrQixHQUFtQyxJQUFJLENBQUM7QUFFOUQsTUFBTSxnQkFBZ0IsR0FBRyxLQUFLLElBQUksRUFBRTtJQUNsQyxJQUFJLGtCQUFrQixFQUFFLENBQUM7UUFDdkIsT0FBTyxrQkFBa0IsQ0FBQztJQUM1QixDQUFDO0lBRUQsSUFBSSxDQUFDO1FBQ0gsTUFBTSxJQUFJLEdBQUcsTUFBTSxJQUFBLG1CQUFRLEVBQUMsa0JBQWtCLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDekQsa0JBQWtCLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN0QyxPQUFPLGtCQUFrQixDQUFDO0lBQzVCLENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsT0FBTyxDQUFDLElBQUksQ0FDVixnREFBZ0Qsa0JBQWtCLEVBQUUsRUFDcEUsS0FBSyxDQUNOLENBQUM7UUFDRixPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7QUFDSCxDQUFDLENBQUM7QUFFRixNQUFNLGFBQWEsR0FBRyxLQUFLLElBQUksRUFBRTtJQUMvQixNQUFNLFFBQVEsR0FBRyxNQUFNLGdCQUFnQixFQUFFLENBQUM7SUFDMUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQ2QsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztBQUM5QyxDQUFDLENBQUM7QUFFRixNQUFNLGNBQWMsR0FBRyxDQUFDLE1BQXNCLEVBQUUsUUFBaUIsRUFBRSxFQUFFO0lBQ25FLE1BQU0sWUFBWSxHQUFHLFFBQVEsSUFBSSxLQUFLLENBQUM7SUFDdkMsTUFBTSxVQUFVLEdBQUcsT0FBTyxNQUFNLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUUzRCxJQUFJLENBQUM7UUFDSCxPQUFPLElBQUksSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLEVBQUU7WUFDcEMsS0FBSyxFQUFFLFVBQVU7WUFDakIsUUFBUSxFQUFFLFlBQVk7U0FDdkIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUN4QixDQUFDO0lBQUMsTUFBTSxDQUFDO1FBQ1AsT0FBTyxHQUFHLFVBQVUsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLElBQUksWUFBWSxFQUFFLENBQUM7SUFDcEQsQ0FBQztBQUNILENBQUMsQ0FBQztBQUVGLE1BQU0sY0FBYyxHQUFHLENBQUMsS0FBYyxFQUFFLEVBQUU7SUFDeEMsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ1gsT0FBTyxFQUFFLENBQUM7SUFDWixDQUFDO0lBRUQsSUFBSSxDQUFDO1FBQ0gsT0FBTyxJQUFJLElBQUksQ0FBQyxjQUFjLENBQUMsT0FBTyxFQUFFO1lBQ3RDLFNBQVMsRUFBRSxRQUFRO1lBQ25CLFNBQVMsRUFBRSxPQUFPO1NBQ25CLENBQUMsQ0FBQyxNQUFNLENBQUMsSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUM3QixDQUFDO0lBQUMsTUFBTSxDQUFDO1FBQ1AsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0FBQ0gsQ0FBQyxDQUFDO0FBRUYsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDLElBQW9CLEVBQUUsRUFBRTtJQUNoRCxNQUFNLFFBQVEsR0FDWixPQUFPLElBQUksQ0FBQyxNQUFNLEVBQUUsUUFBUSxLQUFLLFFBQVE7UUFDdkMsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsUUFBUTtRQUN2QixDQUFDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQztJQUVwQixJQUFJLE9BQU8sUUFBUSxLQUFLLFFBQVEsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUM1RCxPQUFPLEdBQUcsUUFBUSxPQUFPLENBQUM7SUFDNUIsQ0FBQztJQUVELE9BQU8sUUFBUSxDQUFDO0FBQ2xCLENBQUMsQ0FBQztBQUVGLE1BQU0sWUFBWSxHQUFHLENBQ25CLEtBQWEsRUFDYixJQUFvQixFQUNwQixRQUFpQixFQUNqQixFQUFFO0lBQ0YsT0FBTztRQUNMLElBQUksRUFBRSxLQUFLO1FBQ1gsTUFBTSxFQUFFLFlBQVk7UUFDcEIsUUFBUSxFQUFFO1lBQ1I7Z0JBQ0UsSUFBSSxFQUFFLEtBQUs7Z0JBQ1gsTUFBTSxFQUFFLFVBQVU7Z0JBQ2xCLFFBQVEsRUFBRTtvQkFDUjt3QkFDRSxJQUFJLEVBQUUsTUFBTTt3QkFDWixJQUFJLEVBQUUsR0FBRyxLQUFLLEdBQUcsQ0FBQyxHQUFHO3dCQUNyQixJQUFJLEVBQUUsSUFBSTt3QkFDVixLQUFLLEVBQUUsU0FBUztxQkFDakI7aUJBQ0Y7Z0JBQ0QsS0FBSyxFQUFFLE1BQU07YUFDZDtZQUNEO2dCQUNFLElBQUksRUFBRSxLQUFLO2dCQUNYLE1BQU0sRUFBRSxVQUFVO2dCQUNsQixRQUFRLEVBQUU7b0JBQ1I7d0JBQ0UsSUFBSSxFQUFFLE1BQU07d0JBQ1osSUFBSSxFQUFFLElBQUksQ0FBQyxLQUFLLElBQUksUUFBUTt3QkFDNUIsSUFBSSxFQUFFLElBQUk7d0JBQ1YsSUFBSSxFQUFFLElBQUk7d0JBQ1YsS0FBSyxFQUFFLFNBQVM7cUJBQ2pCO29CQUNEO3dCQUNFLElBQUksRUFBRSxLQUFLO3dCQUNYLE1BQU0sRUFBRSxZQUFZO3dCQUNwQixRQUFRLEVBQUU7NEJBQ1I7Z0NBQ0UsSUFBSSxFQUFFLE1BQU07Z0NBQ1osSUFBSSxFQUFFLGdCQUFnQixDQUFDLElBQUksQ0FBQztnQ0FDNUIsSUFBSSxFQUFFLElBQUk7Z0NBQ1YsS0FBSyxFQUFFLFNBQVM7NkJBQ2pCOzRCQUNEO2dDQUNFLElBQUksRUFBRSxNQUFNO2dDQUNaLElBQUksRUFBRSxjQUFjLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxVQUFVLEVBQUUsUUFBUSxDQUFDO2dDQUN2RCxJQUFJLEVBQUUsSUFBSTtnQ0FDVixLQUFLLEVBQUUsU0FBUztnQ0FDaEIsTUFBTSxFQUFFLE1BQU07Z0NBQ2QsS0FBSyxFQUFFLEtBQUs7NkJBQ2I7eUJBQ0Y7cUJBQ0Y7aUJBQ0Y7Z0JBQ0QsSUFBSSxFQUFFLENBQUM7YUFDUjtTQUNGO1FBQ0QsTUFBTSxFQUFFLElBQUk7S0FDYixDQUFDO0FBQ0osQ0FBQyxDQUFDO0FBRUYsTUFBTSxhQUFhLEdBQUcsQ0FBQyxLQUFrQixFQUFFLEVBQUU7SUFDM0MsSUFBSSxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDakIsT0FBTyxLQUFLLENBQUMsTUFBTSxDQUFDO0lBQ3RCLENBQUM7SUFFRCxJQUFJLEtBQUssQ0FBQyxFQUFFLEVBQUUsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3pCLE9BQU8sSUFBSSxLQUFLLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7SUFDbEMsQ0FBQztJQUVELE9BQU8sSUFBSSxLQUFLLENBQUMsRUFBRSxFQUFFLENBQUM7QUFDeEIsQ0FBQyxDQUFDO0FBRUYsTUFBTSxlQUFlLEdBQUcsS0FBSyxFQUFFLEtBQWtCLEVBQUUsRUFBRTtJQUNuRCxNQUFNLE1BQU0sR0FBRyxNQUFNLGFBQWEsRUFBRSxDQUFDO0lBQ3JDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNaLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVELE1BQU0sVUFBVSxHQUFHLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUN4QyxNQUFNLFdBQVcsR0FDZixhQUFhLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQyxNQUFNLElBQUksY0FBYyxDQUFDO0lBQ2hFLE1BQU0sWUFBWSxHQUNoQixDQUFDLEtBQUssQ0FBQyxRQUFRLEVBQUUsVUFBVSxFQUFFLEtBQUssQ0FBQyxRQUFRLEVBQUUsU0FBUyxDQUFDO1NBQ3BELE1BQU0sQ0FBQyxPQUFPLENBQUM7U0FDZixJQUFJLENBQUMsR0FBRyxDQUFDO1NBQ1QsSUFBSSxFQUFFO1FBQ1QsS0FBSyxDQUFDLFFBQVEsRUFBRSxLQUFLO1FBQ3JCLFFBQVEsQ0FBQztJQUVYLE1BQU0sUUFBUSxHQUFHLEtBQUssQ0FBQyxXQUFXLEVBQUUsYUFBYSxJQUFJLEtBQUssQ0FBQztJQUMzRCxNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMsV0FBVyxFQUFFLEtBQUssSUFBSSxFQUFFLENBQUM7SUFFN0MsTUFBTSxNQUFNLEdBQUksTUFBYyxDQUFDLE1BQU0sQ0FBQztJQUN0QyxJQUFJLE1BQU0sRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ3pDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksR0FBRyxjQUFjLFVBQVUsRUFBRSxDQUFDO0lBQ25FLENBQUM7SUFDRCxJQUFJLE1BQU0sRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ3pDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksR0FBRyxXQUFXLENBQUM7SUFDcEQsQ0FBQztJQUVELE1BQU0sSUFBSSxHQUFJLE1BQWMsQ0FBQyxJQUFJLENBQUM7SUFDbEMsSUFBSSxJQUFJLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUN2QyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEdBQUcsS0FBSyxDQUFDLE1BQU07WUFDOUMsQ0FBQyxDQUFDLEdBQUcsS0FBSyxDQUFDLE1BQU0sU0FBUztZQUMxQixDQUFDLENBQUMsYUFBYSxDQUFDO0lBQ3BCLENBQUM7SUFFRCxNQUFNLFlBQVksR0FBRyxJQUFJLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDekMsSUFBSSxZQUFZLEVBQUUsQ0FBQztRQUNqQixNQUFNLFFBQVEsR0FBRyxDQUFDLENBQUM7UUFDbkIsTUFBTSxhQUFhLEdBQThCLEtBQUs7YUFDbkQsS0FBSyxDQUFDLENBQUMsRUFBRSxRQUFRLENBQUM7YUFDbEIsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUMsWUFBWSxDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUU3RCxJQUFJLEtBQUssQ0FBQyxNQUFNLEdBQUcsUUFBUSxFQUFFLENBQUM7WUFDNUIsYUFBYSxDQUFDLElBQUksQ0FBQztnQkFDakIsSUFBSSxFQUFFLE1BQU07Z0JBQ1osSUFBSSxFQUFFLGNBQWMsS0FBSyxDQUFDLE1BQU0sR0FBRyxRQUFRLFNBQVM7Z0JBQ3BELElBQUksRUFBRSxJQUFJO2dCQUNWLEtBQUssRUFBRSxTQUFTO2dCQUNoQixLQUFLLEVBQUUsUUFBUTtnQkFDZixNQUFNLEVBQUUsSUFBSTthQUNiLENBQUMsQ0FBQztRQUNMLENBQUM7UUFFRCxJQUFJLGFBQWEsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDL0IsYUFBYSxDQUFDLElBQUksQ0FBQztnQkFDakIsSUFBSSxFQUFFLE1BQU07Z0JBQ1osSUFBSSxFQUFFLDRCQUE0QjtnQkFDbEMsSUFBSSxFQUFFLElBQUk7Z0JBQ1YsS0FBSyxFQUFFLFNBQVM7Z0JBQ2hCLEtBQUssRUFBRSxRQUFRO2dCQUNmLE1BQU0sRUFBRSxJQUFJO2FBQ2IsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUVELFlBQVksQ0FBQyxRQUFRLEdBQUcsYUFBYSxDQUFDO1FBQ3RDLE9BQU8sWUFBWSxDQUFDLE1BQU0sQ0FBQztJQUM3QixDQUFDO0lBRUQsTUFBTSxTQUFTLEdBQUcsSUFBSSxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3RDLElBQUksU0FBUyxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDNUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxHQUFHLGNBQWMsQ0FDckQsS0FBSyxDQUFDLFdBQVcsRUFBRSxRQUFRLEVBQzNCLFFBQVEsQ0FDVCxDQUFDO0lBQ0osQ0FBQztJQUVELElBQUksU0FBUyxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDNUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxHQUFHLGFBQWEsQ0FBQztJQUN6RCxDQUFDO0lBQ0QsSUFBSSxTQUFTLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUM1QyxTQUFTLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEdBQUcsY0FBYyxDQUNyRCxLQUFLLENBQUMsV0FBVyxFQUFFLFNBQVMsRUFDNUIsUUFBUSxDQUNULENBQUM7SUFDSixDQUFDO0lBRUQsTUFBTSxhQUFhLEdBQUcsSUFBSSxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzFDLElBQUksYUFBYSxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDakMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEdBQUcsY0FBYyxDQUM3QyxLQUFLLENBQUMsV0FBVyxFQUFFLEtBQUssRUFDeEIsUUFBUSxDQUNULENBQUM7SUFDSixDQUFDO0lBRUQsTUFBTSxNQUFNLEdBQUksTUFBYyxDQUFDLE1BQU0sQ0FBQztJQUN0QyxNQUFNLGFBQWEsR0FBRyxNQUFNLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDNUMsSUFBSSxhQUFhLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNqQyxhQUFhLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksR0FBRyxXQUFXLFlBQVksRUFBRSxDQUFDO0lBQzdELENBQUM7SUFDRCxJQUFJLGFBQWEsRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ2pDLGFBQWEsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxHQUFHLFdBQVcsY0FBYyxDQUN4RCxLQUFLLENBQUMsVUFBVSxJQUFJLEtBQUssQ0FBQyxVQUFVLENBQ3JDLEVBQUUsQ0FBQztJQUNOLENBQUM7SUFFRCxNQUFNLFVBQVUsR0FBRyxNQUFNLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDekMsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLElBQUksd0JBQXdCLENBQUM7SUFDdkUsTUFBTSxTQUFTLEdBQUcsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsY0FBYyxLQUFLLENBQUMsRUFBRSxFQUFFLENBQUM7SUFDeEUsTUFBTSxVQUFVLEdBQUcsR0FBRyxTQUFTLGdCQUFnQixDQUFDO0lBRWhELElBQUksVUFBVSxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO1FBQ3RDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsR0FBRyxVQUFVLENBQUM7SUFDakQsQ0FBQztJQUVELElBQUksVUFBVSxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO1FBQ3RDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsR0FBRyxTQUFTLENBQUM7SUFDaEQsQ0FBQztJQUVELE9BQU8sTUFBTSxDQUFDO0FBQ2hCLENBQUMsQ0FBQztBQUVGLE1BQU0sbUJBQW1CLEdBQUcsS0FBSyxFQUMvQixNQUFjLEVBQ2QsVUFBa0IsRUFDbEIsTUFBK0IsRUFDL0IsS0FBa0IsRUFDbEIsRUFBRTtJQUNGLE1BQU0sV0FBVyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsbUNBQW1DLENBQUM7SUFFcEUsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ2pCLE1BQU0sQ0FBQyxJQUFJLENBQ1QsNEZBQTRGLENBQzdGLENBQUM7UUFDRixPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRCxNQUFNLE9BQU8sR0FBRztRQUNkLEVBQUUsRUFBRSxVQUFVO1FBQ2QsUUFBUSxFQUFFO1lBQ1I7Z0JBQ0UsSUFBSSxFQUFFLE1BQU07Z0JBQ1osT0FBTyxFQUFFLGNBQWMsYUFBYSxDQUFDLEtBQUssQ0FBQyxFQUFFO2dCQUM3QyxRQUFRLEVBQUUsTUFBTTthQUNqQjtTQUNGO0tBQ0YsQ0FBQztJQUVGLE1BQU0sUUFBUSxHQUFHLE1BQU0sS0FBSyxDQUFDLGtCQUFrQixFQUFFO1FBQy9DLE1BQU0sRUFBRSxNQUFNO1FBQ2QsT0FBTyxFQUFFO1lBQ1AsY0FBYyxFQUFFLGtCQUFrQjtZQUNsQyxhQUFhLEVBQUUsVUFBVSxXQUFXLEVBQUU7U0FDdkM7UUFDRCxJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUM7S0FDOUIsQ0FBQyxDQUFDO0lBRUgsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUNqQixNQUFNLElBQUksR0FBRyxNQUFNLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNuQyxNQUFNLElBQUksS0FBSyxDQUNiLDZCQUE2QixRQUFRLENBQUMsTUFBTSxJQUFJLFFBQVEsQ0FBQyxVQUFVLE1BQU0sSUFBSSxFQUFFLENBQ2hGLENBQUM7SUFDSixDQUFDO0lBRUQsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDLENBQUM7QUFFRixNQUFNLFdBQVcsR0FBRyxLQUFLLEVBQ3ZCLE1BQWMsRUFDZCxLQUFrQixFQUNsQixJQU1hLEVBQ2IsRUFBRTtJQUNGLElBQ0UsQ0FBQyxJQUFJLEVBQUUsR0FBRztRQUNWLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxhQUFhO1FBQy9CLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxlQUFlLEVBQ2hDLENBQUM7UUFDRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRCxNQUFNLGdCQUFnQixHQUFHLENBQUMsS0FBSyxDQUFDLFFBQVEsRUFBRSxRQUFRLElBQUksRUFBRSxDQUd2RCxDQUFDO0lBRUYsTUFBTSxZQUFZLEdBQ2hCLENBQUMsT0FBTyxnQkFBZ0IsQ0FBQyxrQkFBa0IsS0FBSyxRQUFRO1FBQ3RELENBQUMsQ0FBRSxnQkFBZ0IsQ0FBQyxrQkFBNkI7UUFDakQsQ0FBQyxDQUFDLFNBQVMsQ0FBQztRQUNkLEtBQUssQ0FBQyxRQUFRLEVBQUUsRUFBRTtRQUNsQixLQUFLLENBQUMsV0FBVztRQUNqQixLQUFLLENBQUMsUUFBUSxFQUFFLEtBQUssQ0FBQztJQUV4QixJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDbEIsTUFBTSxDQUFDLElBQUksQ0FDVCx1REFBdUQsS0FBSyxDQUFDLEVBQUUsMkJBQTJCLENBQzNGLENBQUM7UUFDRixPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRCxNQUFNLFdBQVcsR0FBRztRQUNsQixJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksSUFBSSxZQUFZO1FBQy9CLEVBQUUsRUFBRSxZQUFZO1FBQ2hCLE9BQU8sRUFBRSxFQUFFLEtBQUssRUFBRTtLQUNuQixDQUFDO0lBRUYsTUFBTSwyQkFBdUIsQ0FBQyxHQUFHLENBQUM7UUFDaEMsS0FBSyxFQUFFO1lBQ0wsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLElBQUksWUFBWTtZQUMvQixHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUc7WUFDYixPQUFPLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxhQUFhO1lBQ3RDLFNBQVMsRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLGVBQWU7WUFDMUMsSUFBSSxFQUFFLFlBQVk7WUFDbEIsT0FBTyxFQUFFLFdBQVc7U0FDckI7S0FDRixDQUFDLENBQUM7SUFFSCxPQUFPLElBQUksQ0FBQztBQUNkLENBQUMsQ0FBQztBQUVhLEtBQUssVUFBVSxtQkFBbUIsQ0FBQyxFQUNoRCxLQUFLLEVBQUUsRUFBRSxJQUFJLEVBQUUsRUFDZixTQUFTLEdBQ3NCO0lBQy9CLE1BQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQVMsUUFBUSxDQUFDLENBQUM7SUFDbkQsTUFBTSxLQUFLLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxpQ0FBeUIsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNqRSxNQUFNLGdCQUFnQixHQUNwQixTQUFTLENBQUMsT0FBTyxDQUF5Qiw4QkFBaUIsQ0FBQyxDQUFDO0lBRS9ELE1BQU0sRUFDSixJQUFJLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FDZCxHQUE0QixNQUFNLEtBQUssQ0FBQyxLQUFLLENBQzVDO1FBQ0UsTUFBTSxFQUFFLE9BQU87UUFDZixNQUFNLEVBQUUsQ0FBQyxHQUFHLDBCQUFXLEVBQUUsR0FBRyxnQ0FBYyxDQUFDO1FBQzNDLE9BQU8sRUFBRSxFQUFFLEVBQUUsRUFBRSxJQUFJLENBQUMsRUFBRSxFQUFFO0tBQ3pCLEVBQ0QsRUFBRSxrQkFBa0IsRUFBRSxJQUFJLEVBQUUsQ0FDN0IsQ0FBQztJQUVGLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNYLE1BQU0sQ0FBQyxJQUFJLENBQ1QsZ0NBQWdDLElBQUksQ0FBQyxFQUFFLDZCQUE2QixDQUNyRSxDQUFDO1FBQ0YsT0FBTztJQUNULENBQUM7SUFFRCxNQUFNLGdCQUFnQixHQUFHLENBQUMsS0FBSyxDQUFDLFFBQVEsRUFBRSxRQUFRLElBQUksRUFBRSxDQUd2RCxDQUFDO0lBQ0YsTUFBTSxVQUFVLEdBQ2QsT0FBTyxnQkFBZ0IsQ0FBQyxZQUFZLEtBQUssUUFBUTtRQUMvQyxDQUFDLENBQUUsZ0JBQWdCLENBQUMsWUFBdUI7UUFDM0MsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUVoQixJQUFJLFVBQVUsRUFBRSxDQUFDO1FBQ2YsSUFBSSxDQUFDO1lBQ0gsTUFBTSxNQUFNLEdBQUcsTUFBTSxlQUFlLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDNUMsSUFBSSxNQUFNLEVBQUUsQ0FBQztnQkFDWCxNQUFNLG1CQUFtQixDQUFDLE1BQU0sRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxDQUFDO2dCQUM3RCxNQUFNLENBQUMsSUFBSSxDQUFDLDRDQUE0QyxLQUFLLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztnQkFDcEUsT0FBTztZQUNULENBQUM7UUFDSCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE1BQU0sQ0FBQyxLQUFLLENBQ1Ysc0RBQXNELEtBQUssQ0FBQyxFQUFFLEVBQUUsRUFDaEUsS0FBSyxDQUNOLENBQUM7UUFDSixDQUFDO0lBQ0gsQ0FBQztJQUVELE1BQU0sY0FBYyxHQUFHLE1BQU0sZ0JBQWdCLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztJQUNsRSxNQUFNLGFBQWEsR0FDakIsY0FBYyxDQUFDLGFBQWEsSUFBSSxjQUFjLENBQUMsb0JBQW9CLENBQUM7SUFFdEUsSUFBSSxDQUFDLGFBQWEsRUFBRSxHQUFHLEVBQUUsQ0FBQztRQUN4QixNQUFNLENBQUMsSUFBSSxDQUNULGtFQUFrRSxLQUFLLENBQUMsRUFBRSxzQ0FBc0MsQ0FDakgsQ0FBQztRQUNGLE9BQU87SUFDVCxDQUFDO0lBRUQsSUFBSSxDQUFDO1FBQ0gsTUFBTSxXQUFXLENBQUMsTUFBTSxFQUFFLEtBQUssRUFBRSxhQUFhLENBQUMsQ0FBQztRQUNoRCxNQUFNLENBQUMsSUFBSSxDQUFDLDhDQUE4QyxLQUFLLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUN4RSxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE1BQU0sQ0FBQyxLQUFLLENBQ1Ysc0RBQXNELEtBQUssQ0FBQyxFQUFFLEVBQUUsRUFDaEUsS0FBSyxDQUNOLENBQUM7SUFDSixDQUFDO0FBQ0gsQ0FBQztBQUVZLFFBQUEsTUFBTSxHQUFxQjtJQUN0QyxLQUFLLEVBQUUsWUFBWTtDQUNwQixDQUFDIn0=
|
|
@@ -20,4 +20,4 @@ const callExternalApiWorkflow = (0, workflows_sdk_1.createWorkflow)("call-extern
|
|
|
20
20
|
return new workflows_sdk_1.WorkflowResponse(input);
|
|
21
21
|
});
|
|
22
22
|
exports.default = callExternalApiWorkflow;
|
|
23
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
23
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FsbC1leHRlcm5hbC1hcGkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvd29ya2Zsb3dzL2NhbGwtZXh0ZXJuYWwtYXBpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEscUVBSzJDO0FBcUIzQyxNQUFNLG1CQUFtQixHQUFHLElBQUEsMEJBQVUsRUFDcEMsbUJBQW1CLEVBQ25CLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFpQixFQUFFLEVBQUU7SUFDeEUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFFOUMsTUFBTSxLQUFLLENBQUMsR0FBRyxFQUFFO1FBQ2YsTUFBTSxFQUFFLE1BQU07UUFDZCxPQUFPLEVBQUU7WUFDUCxjQUFjLEVBQUUsa0JBQWtCO1lBQ2xDLENBQUMsT0FBTyxDQUFDLEVBQUUsU0FBUztTQUNyQjtRQUNELElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQztLQUM5QixDQUFDLENBQUM7SUFFSCxPQUFPLElBQUksNEJBQVksQ0FBQztRQUN0QixPQUFPLEVBQUUsV0FBVyxJQUFJLElBQUksSUFBSSxhQUFhLEdBQUcsZUFBZTtLQUNoRSxDQUFDLENBQUM7QUFDTCxDQUFDLENBQ0YsQ0FBQztBQUVGLE1BQU0sdUJBQXVCLEdBQUcsSUFBQSw4QkFBYyxFQUM1QyxtQkFBbUIsRUFDbkIsQ0FBQyxLQUFvQixFQUFFLEVBQUU7SUFDdkIsbUJBQW1CLENBQUMsS0FBSyxDQUFDLENBQUM7SUFFM0IsT0FBTyxJQUFJLGdDQUFnQixDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQ3JDLENBQUMsQ0FDRixDQUFDO0FBRUYsa0JBQWUsdUJBQXVCLENBQUMifQ==
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
|
|
4
|
+
const normalizeUrl = (url) => url.replace(/\/$/, "");
|
|
5
|
+
const requestNovu = async (url, method, headers, body) => {
|
|
6
|
+
const response = await fetch(url, {
|
|
7
|
+
method,
|
|
8
|
+
headers: {
|
|
9
|
+
"Content-Type": "application/json",
|
|
10
|
+
...headers,
|
|
11
|
+
},
|
|
12
|
+
body: JSON.stringify(body),
|
|
13
|
+
});
|
|
14
|
+
const text = await response.text();
|
|
15
|
+
let data = text;
|
|
16
|
+
if (text) {
|
|
17
|
+
try {
|
|
18
|
+
data = JSON.parse(text);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Swallow JSON parse errors, keep raw text for debugging
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return { response, data };
|
|
25
|
+
};
|
|
26
|
+
const createNovuSubscriberStep = (0, workflows_sdk_1.createStep)("create-novu-subscriber", async ({ url, authKey, authValue, subscriber }) => {
|
|
27
|
+
const baseUrl = normalizeUrl(url);
|
|
28
|
+
const headers = {
|
|
29
|
+
[authKey]: authValue,
|
|
30
|
+
};
|
|
31
|
+
const { response, data } = await requestNovu(baseUrl, "POST", headers, {
|
|
32
|
+
...subscriber,
|
|
33
|
+
// Novu expects subscriberId field on create
|
|
34
|
+
subscriberId: subscriber.subscriberId,
|
|
35
|
+
});
|
|
36
|
+
if (response.ok) {
|
|
37
|
+
return new workflows_sdk_1.StepResponse({
|
|
38
|
+
message: `Created Novu subscriber ${subscriber.subscriberId}`,
|
|
39
|
+
wasUpdated: false,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const conflict = response.status === 409 ||
|
|
43
|
+
(typeof data === "object" &&
|
|
44
|
+
data !== null &&
|
|
45
|
+
typeof data.message === "string" &&
|
|
46
|
+
data.message.toLowerCase().includes("exists"));
|
|
47
|
+
if (!conflict) {
|
|
48
|
+
throw new Error(`Failed to create Novu subscriber ${subscriber.subscriberId}: ${data?.message ?? response.statusText}`);
|
|
49
|
+
}
|
|
50
|
+
const updateUrl = `${baseUrl}/${encodeURIComponent(subscriber.subscriberId)}`;
|
|
51
|
+
const { response: updateResponse, data: updateData } = await requestNovu(updateUrl, "PUT", headers, subscriber);
|
|
52
|
+
if (!updateResponse.ok) {
|
|
53
|
+
throw new Error(`Failed to update Novu subscriber ${subscriber.subscriberId}: ${updateData?.message ??
|
|
54
|
+
updateResponse.statusText}`);
|
|
55
|
+
}
|
|
56
|
+
return new workflows_sdk_1.StepResponse({
|
|
57
|
+
message: `Updated existing Novu subscriber ${subscriber.subscriberId}`,
|
|
58
|
+
wasUpdated: true,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
const createNovuSubscriberWorkflow = (0, workflows_sdk_1.createWorkflow)("create-novu-subscriber", (input) => {
|
|
62
|
+
createNovuSubscriberStep(input);
|
|
63
|
+
return new workflows_sdk_1.WorkflowResponse({
|
|
64
|
+
subscriberId: input.subscriber.subscriberId,
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
exports.default = createNovuSubscriberWorkflow;
|
|
68
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLW5vdnUtc3Vic2NyaWJlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy93b3JrZmxvd3MvY3JlYXRlLW5vdnUtc3Vic2NyaWJlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHFFQUsyQztBQTJCM0MsTUFBTSxZQUFZLEdBQUcsQ0FBQyxHQUFXLEVBQUUsRUFBRSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBRTdELE1BQU0sV0FBVyxHQUFHLEtBQUssRUFDdkIsR0FBVyxFQUNYLE1BQWMsRUFDZCxPQUErQixFQUMvQixJQUFhLEVBQ2IsRUFBRTtJQUNGLE1BQU0sUUFBUSxHQUFHLE1BQU0sS0FBSyxDQUFDLEdBQUcsRUFBRTtRQUNoQyxNQUFNO1FBQ04sT0FBTyxFQUFFO1lBQ1AsY0FBYyxFQUFFLGtCQUFrQjtZQUNsQyxHQUFHLE9BQU87U0FDWDtRQUNELElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQztLQUMzQixDQUFDLENBQUM7SUFFSCxNQUFNLElBQUksR0FBRyxNQUFNLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUNuQyxJQUFJLElBQUksR0FBWSxJQUFJLENBQUM7SUFFekIsSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUNULElBQUksQ0FBQztZQUNILElBQUksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzFCLENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCx5REFBeUQ7UUFDM0QsQ0FBQztJQUNILENBQUM7SUFFRCxPQUFPLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxDQUFDO0FBQzVCLENBQUMsQ0FBQztBQUVGLE1BQU0sd0JBQXdCLEdBQUcsSUFBQSwwQkFBVSxFQUN6Qyx3QkFBd0IsRUFDeEIsS0FBSyxFQUFFLEVBQUUsR0FBRyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFpQixFQUFFLEVBQUU7SUFDL0QsTUFBTSxPQUFPLEdBQUcsWUFBWSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ2xDLE1BQU0sT0FBTyxHQUFHO1FBQ2QsQ0FBQyxPQUFPLENBQUMsRUFBRSxTQUFTO0tBQ3JCLENBQUM7SUFFRixNQUFNLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxHQUFHLE1BQU0sV0FBVyxDQUMxQyxPQUFPLEVBQ1AsTUFBTSxFQUNOLE9BQU8sRUFDUDtRQUNFLEdBQUcsVUFBVTtRQUNiLDRDQUE0QztRQUM1QyxZQUFZLEVBQUUsVUFBVSxDQUFDLFlBQVk7S0FDdEMsQ0FDRixDQUFDO0lBRUYsSUFBSSxRQUFRLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDaEIsT0FBTyxJQUFJLDRCQUFZLENBQUM7WUFDdEIsT0FBTyxFQUFFLDJCQUEyQixVQUFVLENBQUMsWUFBWSxFQUFFO1lBQzdELFVBQVUsRUFBRSxLQUFLO1NBQ2xCLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxNQUFNLFFBQVEsR0FDWixRQUFRLENBQUMsTUFBTSxLQUFLLEdBQUc7UUFDdkIsQ0FBQyxPQUFPLElBQUksS0FBSyxRQUFRO1lBQ3ZCLElBQUksS0FBSyxJQUFJO1lBQ2IsT0FBUSxJQUE2QixDQUFDLE9BQU8sS0FBSyxRQUFRO1lBQ3pELElBQTRCLENBQUMsT0FBTyxDQUFDLFdBQVcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO0lBRTVFLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNkLE1BQU0sSUFBSSxLQUFLLENBQ2Isb0NBQW9DLFVBQVUsQ0FBQyxZQUFZLEtBQ3hELElBQTZCLEVBQUUsT0FBTyxJQUFJLFFBQVEsQ0FBQyxVQUN0RCxFQUFFLENBQ0gsQ0FBQztJQUNKLENBQUM7SUFFRCxNQUFNLFNBQVMsR0FBRyxHQUFHLE9BQU8sSUFBSSxrQkFBa0IsQ0FDaEQsVUFBVSxDQUFDLFlBQVksQ0FDeEIsRUFBRSxDQUFDO0lBQ0osTUFBTSxFQUFFLFFBQVEsRUFBRSxjQUFjLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxHQUFHLE1BQU0sV0FBVyxDQUN0RSxTQUFTLEVBQ1QsS0FBSyxFQUNMLE9BQU8sRUFDUCxVQUFVLENBQ1gsQ0FBQztJQUVGLElBQUksQ0FBQyxjQUFjLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDdkIsTUFBTSxJQUFJLEtBQUssQ0FDYixvQ0FBb0MsVUFBVSxDQUFDLFlBQVksS0FDeEQsVUFBbUMsRUFBRSxPQUFPO1lBQzdDLGNBQWMsQ0FBQyxVQUNqQixFQUFFLENBQ0gsQ0FBQztJQUNKLENBQUM7SUFFRCxPQUFPLElBQUksNEJBQVksQ0FBQztRQUN0QixPQUFPLEVBQUUsb0NBQW9DLFVBQVUsQ0FBQyxZQUFZLEVBQUU7UUFDdEUsVUFBVSxFQUFFLElBQUk7S0FDakIsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyxDQUNGLENBQUM7QUFFRixNQUFNLDRCQUE0QixHQUFHLElBQUEsOEJBQWMsRUFDakQsd0JBQXdCLEVBQ3hCLENBQUMsS0FBb0IsRUFBRSxFQUFFO0lBQ3ZCLHdCQUF3QixDQUFDLEtBQUssQ0FBQyxDQUFDO0lBRWhDLE9BQU8sSUFBSSxnQ0FBZ0IsQ0FBQztRQUMxQixZQUFZLEVBQUUsS0FBSyxDQUFDLFVBQVUsQ0FBQyxZQUFZO0tBQzVDLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FDRixDQUFDO0FBRUYsa0JBQWUsNEJBQTRCLENBQUMifQ==
|
package/README.md
CHANGED
|
@@ -20,6 +20,14 @@ module.exports = defineConfig({
|
|
|
20
20
|
headerAuthValue: `ApiKey ${process.env.NOTI_WEBHOOK_API_KEY_NOVU}`,
|
|
21
21
|
},
|
|
22
22
|
},
|
|
23
|
+
quoteSentHook: {
|
|
24
|
+
name: "quote-sent", // triggered when merchant sends a quote
|
|
25
|
+
url: process.env.NOTI_WEBHOOK_URL_QUOTE_SENT,
|
|
26
|
+
authHeader: {
|
|
27
|
+
headerAuthKey: "Authorization",
|
|
28
|
+
headerAuthValue: `ApiKey ${process.env.NOTI_WEBHOOK_API_KEY_NOVU}`,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
23
31
|
placedOrderHook: {
|
|
24
32
|
name: "placed-order", // for novu workflow name
|
|
25
33
|
url: process.env.NOTI_WEBHOOK_URL_PLACED_ORDER,
|
|
@@ -60,6 +68,16 @@ module.exports = defineConfig({
|
|
|
60
68
|
headerAuthValue: `ApiKey ${process.env.NOTI_WEBHOOK_API_KEY_NOVU}`,
|
|
61
69
|
},
|
|
62
70
|
},
|
|
71
|
+
customerCreatedSubscriberHook: {
|
|
72
|
+
name: "customer-created", // creates/updates Novu subscriber
|
|
73
|
+
url:
|
|
74
|
+
process.env.NOTI_WEBHOOK_URL_CUSTOMER_SUBSCRIBER ||
|
|
75
|
+
"https://api.novu.co/v1/subscribers",
|
|
76
|
+
authHeader: {
|
|
77
|
+
headerAuthKey: "Authorization",
|
|
78
|
+
headerAuthValue: `ApiKey ${process.env.NOTI_WEBHOOK_API_KEY_NOVU}`,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
63
81
|
},
|
|
64
82
|
},
|
|
65
83
|
},
|
|
@@ -75,7 +93,23 @@ NOTI_WEBHOOK_URL_DELIVERY_STARTED=<endpoint ex: "https://api.novu.co/v1/events/t
|
|
|
75
93
|
NOTI_WEBHOOK_URL_PICKUP_CREATED=<endpoint ex: "https://api.novu.co/v1/events/trigger">
|
|
76
94
|
NOTI_WEBHOOK_URL_PICKUP_UPDATED=<endpoint ex: "https://api.novu.co/v1/events/trigger">
|
|
77
95
|
NOTI_WEBHOOK_URL_PICKUP_CANCELLED=<endpoint ex: "https://api.novu.co/v1/events/trigger">
|
|
96
|
+
NOTI_WEBHOOK_URL_QUOTE_SENT=<endpoint ex: "https://api.novu.co/v1/events/trigger">
|
|
97
|
+
NOTI_WEBHOOK_URL_CUSTOMER_SUBSCRIBER=https://api.novu.co/v1/subscribers
|
|
98
|
+
LINE_MESSAGING_CHANNEL_ACCESS_TOKEN=<line-messaging-channel-access-token>
|
|
78
99
|
```
|
|
79
100
|
|
|
101
|
+
## Customer subscriber sync
|
|
102
|
+
|
|
103
|
+
When `customerCreatedSubscriberHook` is configured the plugin listens to the `customer.created` event and automatically creates (or updates) a subscriber in Novu using the configured endpoint. Email identities are stored separately from LINE identities, so Novu workflows can target each channel independently. LINE metadata is pulled from the customer's profile (`metadata.line_user_id`, `metadata.line_display_name`, etc.).
|
|
104
|
+
|
|
105
|
+
## Quote sent notifications
|
|
106
|
+
|
|
107
|
+
When an admin clicks **Send quote** the system emits a `quote.sent` event. This plugin listens for that event and:
|
|
108
|
+
|
|
109
|
+
- Pushes a LINE Flex message (based on `flex/qoute.json`) via `https://api.line.me/v2/bot/message/push` if the customer has a `metadata.line_user_id`. Set `LINE_MESSAGING_CHANNEL_ACCESS_TOKEN` with your bot channel token.
|
|
110
|
+
- Falls back to triggering the configured Novu webhook (`quoteSentHook`) for customers that only supplied an email address (or when LINE notifications are not configured). The payload includes the quote, draft order, and customer context so you can fan out to email/other channels from Novu.
|
|
111
|
+
|
|
112
|
+
Update the `flex/qoute.json` bubble template to adjust colors/text/layout without touching the code.
|
|
113
|
+
|
|
80
114
|
4. all done now just start your medusa backend
|
|
81
115
|
`yarn dev` `yarn start` or whatever you use to start your backend
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lodashventure/medusa-notification-webhook",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"description": "A starter for Medusa plugins.",
|
|
5
5
|
"author": "Medusa (https://medusajs.com)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,26 +28,26 @@
|
|
|
28
28
|
"prepublishOnly": "medusa plugin:build"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@medusajs/admin-sdk": "2.11.
|
|
32
|
-
"@medusajs/admin-shared": "2.11.
|
|
33
|
-
"@medusajs/cli": "2.11.
|
|
34
|
-
"@medusajs/framework": "2.11.
|
|
35
|
-
"@medusajs/icons": "^2.11.
|
|
36
|
-
"@medusajs/medusa": "2.11.
|
|
37
|
-
"@medusajs/test-utils": "2.11.
|
|
38
|
-
"@medusajs/ui": "4.0.
|
|
39
|
-
"@swc/core": "1.
|
|
40
|
-
"@types/jsonwebtoken": "^9.0.
|
|
41
|
-
"@types/node": "^
|
|
42
|
-
"@types/react": "^
|
|
43
|
-
"@types/react-dom": "^
|
|
31
|
+
"@medusajs/admin-sdk": "2.11.3",
|
|
32
|
+
"@medusajs/admin-shared": "2.11.3",
|
|
33
|
+
"@medusajs/cli": "2.11.3",
|
|
34
|
+
"@medusajs/framework": "2.11.3",
|
|
35
|
+
"@medusajs/icons": "^2.11.3",
|
|
36
|
+
"@medusajs/medusa": "2.11.3",
|
|
37
|
+
"@medusajs/test-utils": "2.11.3",
|
|
38
|
+
"@medusajs/ui": "4.0.27",
|
|
39
|
+
"@swc/core": "1.15.0",
|
|
40
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
41
|
+
"@types/node": "^24.10.0",
|
|
42
|
+
"@types/react": "^19.2.2",
|
|
43
|
+
"@types/react-dom": "^19.2.2",
|
|
44
44
|
"prop-types": "^15.8.1",
|
|
45
|
-
"react": "^
|
|
46
|
-
"react-dom": "^
|
|
45
|
+
"react": "^19.2.0",
|
|
46
|
+
"react-dom": "^19.2.0",
|
|
47
47
|
"ts-node": "^10.9.2",
|
|
48
|
-
"typescript": "^5.
|
|
49
|
-
"vite": "^
|
|
50
|
-
"yalc": "
|
|
48
|
+
"typescript": "^5.9.3",
|
|
49
|
+
"vite": "^7.2.2",
|
|
50
|
+
"yalc": "1.0.0-pre.53"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"@medusajs/admin-sdk": "2.11.2",
|