medusajs-documents-plugin 0.0.0-stage → 1.1.2
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/admin/index.js +2441 -0
- package/.medusa/server/src/admin/index.mjs +2442 -0
- package/.medusa/server/src/api/admin/documents/[id]/document/route.js +36 -0
- package/.medusa/server/src/api/admin/documents/[id]/send/route.js +41 -0
- package/.medusa/server/src/api/admin/documents/[id]/sync-zoho/route.js +68 -0
- package/.medusa/server/src/api/admin/documents/custom/route.js +48 -0
- package/.medusa/server/src/api/admin/documents/manual/route.js +46 -0
- package/.medusa/server/src/api/admin/documents/route.js +36 -0
- package/.medusa/server/src/api/admin/documents/settings/route.js +44 -0
- package/.medusa/server/src/api/admin/documents/templates/preview/route.js +159 -0
- package/.medusa/server/src/api/admin/documents/zoho/list/route.js +51 -0
- package/.medusa/server/src/api/documents/[id]/document/route.js +36 -0
- package/.medusa/server/src/api/documents/r/[code]/route.js +29 -0
- package/.medusa/server/src/api/documents/templates/preview/route.js +159 -0
- package/.medusa/server/src/api/store/documents/r/[code]/route.js +22 -0
- package/.medusa/server/src/index.js +27 -0
- package/.medusa/server/src/modules/documents/index.js +13 -0
- package/.medusa/server/src/modules/documents/migrations/Migration20261012164601.js +23 -0
- package/.medusa/server/src/modules/documents/migrations/Migration20261014150000.js +20 -0
- package/.medusa/server/src/modules/documents/migrations/Migration20261015160000.js +46 -0
- package/.medusa/server/src/modules/documents/models/document-link.js +12 -0
- package/.medusa/server/src/modules/documents/models/document-record.js +33 -0
- package/.medusa/server/src/modules/documents/models/document-settings.js +79 -0
- package/.medusa/server/src/modules/documents/service.js +587 -0
- package/.medusa/server/src/services/notification-sender.js +612 -0
- package/.medusa/server/src/services/order-notification-templates.js +510 -0
- package/.medusa/server/src/services/pdf-post-processor.js +64 -0
- package/.medusa/server/src/services/pdfkit-generator.js +438 -0
- package/.medusa/server/src/services/zoho-books.js +221 -0
- package/.medusa/server/src/subscribers/order-documents.js +424 -0
- package/.medusa/server/src/subscribers/repair-documents.js +216 -0
- package/.medusa/server/src/utils/integrate.js +37 -0
- package/.medusa/server/src/utils/payment-link.js +69 -0
- package/.medusa/server/src/workflows/create-manual-document.js +27 -0
- package/.medusa/server/src/workflows/generate-document.js +24 -0
- package/.medusa/server/src/workflows/index.js +20 -0
- package/.medusa/server/src/workflows/send-document-notification.js +41 -0
- package/CHANGELOG.md +87 -0
- package/LICENSE +21 -0
- package/README.md +201 -2
- package/package.json +86 -4
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ZohoBooksService = void 0;
|
|
4
|
+
class ZohoBooksService {
|
|
5
|
+
constructor(config, logger) {
|
|
6
|
+
this.access_token = null;
|
|
7
|
+
this.token_expires_at = 0;
|
|
8
|
+
this.config = config;
|
|
9
|
+
this.logger = logger;
|
|
10
|
+
}
|
|
11
|
+
get baseAccountsUrl() {
|
|
12
|
+
return `https://accounts.zoho.${this.config.domain || "com"}`;
|
|
13
|
+
}
|
|
14
|
+
get baseApiUrl() {
|
|
15
|
+
return `https://www.zohoapis.${this.config.domain || "com"}/books/v3`;
|
|
16
|
+
}
|
|
17
|
+
async getAccessToken() {
|
|
18
|
+
if (this.access_token && Date.now() < this.token_expires_at) {
|
|
19
|
+
return this.access_token;
|
|
20
|
+
}
|
|
21
|
+
this.logger?.info(`[Zoho Books] Refreshing access token...`);
|
|
22
|
+
const params = new URLSearchParams();
|
|
23
|
+
params.append("refresh_token", this.config.refresh_token);
|
|
24
|
+
params.append("client_id", this.config.client_id);
|
|
25
|
+
params.append("client_secret", this.config.client_secret);
|
|
26
|
+
params.append("grant_type", "refresh_token");
|
|
27
|
+
const response = await fetch(`${this.baseAccountsUrl}/oauth/v2/token`, {
|
|
28
|
+
method: "POST",
|
|
29
|
+
body: params,
|
|
30
|
+
});
|
|
31
|
+
const data = await response.json();
|
|
32
|
+
if (!response.ok || data.error) {
|
|
33
|
+
this.logger?.error(`[Zoho Books] Failed to refresh token: ${JSON.stringify(data)}`);
|
|
34
|
+
throw new Error(`Zoho authentication failed: ${data.error || response.statusText}`);
|
|
35
|
+
}
|
|
36
|
+
this.access_token = data.access_token;
|
|
37
|
+
this.token_expires_at = Date.now() + (data.expires_in - 300) * 1000;
|
|
38
|
+
return this.access_token;
|
|
39
|
+
}
|
|
40
|
+
async request(method, endpoint, body, isBlob = false) {
|
|
41
|
+
const token = await this.getAccessToken();
|
|
42
|
+
const separator = endpoint.includes("?") ? "&" : "?";
|
|
43
|
+
const url = `${this.baseApiUrl}${endpoint}${separator}organization_id=${this.config.organization_id}`;
|
|
44
|
+
const headers = {
|
|
45
|
+
Authorization: `Zoho-oauthtoken ${token}`,
|
|
46
|
+
};
|
|
47
|
+
if (body) {
|
|
48
|
+
headers["Content-Type"] = "application/json";
|
|
49
|
+
}
|
|
50
|
+
const res = await fetch(url, {
|
|
51
|
+
method,
|
|
52
|
+
headers,
|
|
53
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
54
|
+
});
|
|
55
|
+
if (isBlob) {
|
|
56
|
+
if (!res.ok) {
|
|
57
|
+
throw new Error(`Zoho API Blob Error: ${res.statusText}`);
|
|
58
|
+
}
|
|
59
|
+
return await res.arrayBuffer();
|
|
60
|
+
}
|
|
61
|
+
const data = await res.json();
|
|
62
|
+
if (!res.ok || data.code !== 0) {
|
|
63
|
+
this.logger?.error(`[Zoho Books] API Error: ${JSON.stringify(data)}`);
|
|
64
|
+
throw new Error(`Zoho API Error: ${data.message || res.statusText}`);
|
|
65
|
+
}
|
|
66
|
+
return data;
|
|
67
|
+
}
|
|
68
|
+
async syncContact(customer) {
|
|
69
|
+
const email = customer.email;
|
|
70
|
+
const name = customer.first_name
|
|
71
|
+
? `${customer.first_name} ${customer.last_name || ""}`.trim()
|
|
72
|
+
: customer.company_name || email || "Guest Customer";
|
|
73
|
+
// 1. Search by email
|
|
74
|
+
if (email && email.includes("@")) {
|
|
75
|
+
try {
|
|
76
|
+
const searchRes = await this.request("GET", `/contacts?email=${encodeURIComponent(email)}`);
|
|
77
|
+
if (searchRes.contacts && searchRes.contacts.length > 0) {
|
|
78
|
+
return searchRes.contacts[0].contact_id;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
this.logger?.warn(`[Zoho Books] Failed searching contact by email: ${e.message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// 2. Search by contact name
|
|
86
|
+
if (name) {
|
|
87
|
+
try {
|
|
88
|
+
const searchNameRes = await this.request("GET", `/contacts?contact_name=${encodeURIComponent(name)}`);
|
|
89
|
+
if (searchNameRes.contacts && searchNameRes.contacts.length > 0) {
|
|
90
|
+
return searchNameRes.contacts[0].contact_id;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
this.logger?.warn(`[Zoho Books] Failed searching contact by name: ${e.message}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// 3. Create new contact
|
|
98
|
+
const payload = {
|
|
99
|
+
contact_name: name,
|
|
100
|
+
company_name: customer.company_name || name,
|
|
101
|
+
contact_type: "customer",
|
|
102
|
+
contact_persons: [
|
|
103
|
+
{
|
|
104
|
+
first_name: customer.first_name || "Valued",
|
|
105
|
+
last_name: customer.last_name || "Customer",
|
|
106
|
+
email: email || "",
|
|
107
|
+
phone: customer.phone || "",
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
const createRes = await this.request("POST", "/contacts", payload);
|
|
112
|
+
return createRes.contact.contact_id;
|
|
113
|
+
}
|
|
114
|
+
async createInvoice(contactId, invoiceData) {
|
|
115
|
+
const payload = {
|
|
116
|
+
customer_id: contactId,
|
|
117
|
+
reference_number: invoiceData.reference_number,
|
|
118
|
+
invoice_number: invoiceData.invoice_number,
|
|
119
|
+
due_date: invoiceData.due_date,
|
|
120
|
+
payment_terms: invoiceData.payment_terms || 0,
|
|
121
|
+
line_items: invoiceData.line_items,
|
|
122
|
+
notes: invoiceData.notes || "Thank you for your business.",
|
|
123
|
+
terms: invoiceData.terms || "",
|
|
124
|
+
is_inclusive_tax: invoiceData.is_inclusive_tax ?? false,
|
|
125
|
+
};
|
|
126
|
+
if (invoiceData.custom_fields && invoiceData.custom_fields.length > 0) {
|
|
127
|
+
payload.custom_fields = invoiceData.custom_fields;
|
|
128
|
+
}
|
|
129
|
+
const res = await this.request("POST", "/invoices", payload);
|
|
130
|
+
return {
|
|
131
|
+
invoice_id: res.invoice.invoice_id,
|
|
132
|
+
invoice_number: res.invoice.invoice_number,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
async getInvoicePaymentQrCode(invoiceId) {
|
|
136
|
+
try {
|
|
137
|
+
return await this.request("GET", `/invoices/${invoiceId}/payment-qr-code`, undefined, true);
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
try {
|
|
141
|
+
return await this.request("GET", `/invoices/${invoiceId}/qr_code`, undefined, true);
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async createEstimate(contactId, estimateData) {
|
|
149
|
+
const payload = {
|
|
150
|
+
customer_id: contactId,
|
|
151
|
+
reference_number: estimateData.reference_number,
|
|
152
|
+
line_items: estimateData.line_items,
|
|
153
|
+
notes: estimateData.notes || "Estimate for goods/services.",
|
|
154
|
+
terms: estimateData.terms || "",
|
|
155
|
+
is_inclusive_tax: estimateData.is_inclusive_tax ?? false,
|
|
156
|
+
};
|
|
157
|
+
const res = await this.request("POST", "/estimates", payload);
|
|
158
|
+
return {
|
|
159
|
+
estimate_id: res.estimate.estimate_id,
|
|
160
|
+
estimate_number: res.estimate.estimate_number,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
async registerPayment(invoiceId, amount, contactId, paymentMode = "Online", referenceNumber) {
|
|
164
|
+
const payload = {
|
|
165
|
+
customer_id: contactId,
|
|
166
|
+
payment_mode: paymentMode,
|
|
167
|
+
amount: Number(amount).toFixed(2),
|
|
168
|
+
date: new Date().toISOString().split("T")[0],
|
|
169
|
+
invoices: [
|
|
170
|
+
{
|
|
171
|
+
invoice_id: invoiceId,
|
|
172
|
+
amount_applied: Number(amount).toFixed(2),
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
};
|
|
176
|
+
if (referenceNumber) {
|
|
177
|
+
payload.reference_number = referenceNumber;
|
|
178
|
+
}
|
|
179
|
+
const res = await this.request("POST", "/customerpayments", payload);
|
|
180
|
+
return res.payment.payment_id;
|
|
181
|
+
}
|
|
182
|
+
async getDocumentPdf(documentId, type) {
|
|
183
|
+
const endpoint = type === "estimate" ? `/estimates/${documentId}` : `/invoices/${documentId}`;
|
|
184
|
+
return await this.request("GET", `${endpoint}?accept=pdf`, undefined, true);
|
|
185
|
+
}
|
|
186
|
+
async getPaymentReceiptPdf(paymentId) {
|
|
187
|
+
return await this.request("GET", `/customerpayments/${paymentId}?accept=pdf`, undefined, true);
|
|
188
|
+
}
|
|
189
|
+
async listInvoices(params = {}) {
|
|
190
|
+
const query = new URLSearchParams();
|
|
191
|
+
if (params.page)
|
|
192
|
+
query.append("page", String(params.page));
|
|
193
|
+
if (params.per_page)
|
|
194
|
+
query.append("per_page", String(params.per_page));
|
|
195
|
+
if (params.search_text)
|
|
196
|
+
query.append("search_text", params.search_text);
|
|
197
|
+
const res = await this.request("GET", `/invoices?${query.toString()}`);
|
|
198
|
+
return res.invoices || [];
|
|
199
|
+
}
|
|
200
|
+
async listEstimates(params = {}) {
|
|
201
|
+
const query = new URLSearchParams();
|
|
202
|
+
if (params.page)
|
|
203
|
+
query.append("page", String(params.page));
|
|
204
|
+
if (params.per_page)
|
|
205
|
+
query.append("per_page", String(params.per_page));
|
|
206
|
+
const res = await this.request("GET", `/estimates?${query.toString()}`);
|
|
207
|
+
return res.estimates || [];
|
|
208
|
+
}
|
|
209
|
+
async listPayments(params = {}) {
|
|
210
|
+
const query = new URLSearchParams();
|
|
211
|
+
if (params.page)
|
|
212
|
+
query.append("page", String(params.page));
|
|
213
|
+
if (params.per_page)
|
|
214
|
+
query.append("per_page", String(params.per_page));
|
|
215
|
+
const res = await this.request("GET", `/customerpayments?${query.toString()}`);
|
|
216
|
+
return res.customerpayments || [];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
exports.ZohoBooksService = ZohoBooksService;
|
|
220
|
+
exports.default = ZohoBooksService;
|
|
221
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiem9oby1ib29rcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9zZXJ2aWNlcy96b2hvLWJvb2tzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQTZCQSxNQUFhLGdCQUFnQjtJQU0zQixZQUFZLE1BQWtCLEVBQUUsTUFBZTtRQUh2QyxpQkFBWSxHQUFrQixJQUFJLENBQUM7UUFDbkMscUJBQWdCLEdBQVcsQ0FBQyxDQUFDO1FBR25DLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO1FBQ3JCLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO0lBQ3ZCLENBQUM7SUFFRCxJQUFZLGVBQWU7UUFDekIsT0FBTyx5QkFBeUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLElBQUksS0FBSyxFQUFFLENBQUM7SUFDaEUsQ0FBQztJQUVELElBQVksVUFBVTtRQUNwQixPQUFPLHdCQUF3QixJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sSUFBSSxLQUFLLFdBQVcsQ0FBQztJQUN4RSxDQUFDO0lBRU8sS0FBSyxDQUFDLGNBQWM7UUFDMUIsSUFBSSxJQUFJLENBQUMsWUFBWSxJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztZQUM1RCxPQUFPLElBQUksQ0FBQyxZQUFZLENBQUM7UUFDM0IsQ0FBQztRQUVELElBQUksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLHlDQUF5QyxDQUFDLENBQUM7UUFDN0QsTUFBTSxNQUFNLEdBQUcsSUFBSSxlQUFlLEVBQUUsQ0FBQztRQUNyQyxNQUFNLENBQUMsTUFBTSxDQUFDLGVBQWUsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBQzFELE1BQU0sQ0FBQyxNQUFNLENBQUMsV0FBVyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDbEQsTUFBTSxDQUFDLE1BQU0sQ0FBQyxl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
|