@odooconnector/medusa-odoo-connector 0.1.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/.medusa/server/src/admin/index.js +2211 -0
- package/.medusa/server/src/admin/index.mjs +2212 -0
- package/.medusa/server/src/api/admin/odoo/billing/route.js +15 -0
- package/.medusa/server/src/api/admin/odoo/clear-orders/route.js +33 -0
- package/.medusa/server/src/api/admin/odoo/clear-products/route.js +30 -0
- package/.medusa/server/src/api/admin/odoo/config/route.js +44 -0
- package/.medusa/server/src/api/admin/odoo/customer-options/route.js +15 -0
- package/.medusa/server/src/api/admin/odoo/inventory-options/route.js +15 -0
- package/.medusa/server/src/api/admin/odoo/jobs/[id]/route.js +25 -0
- package/.medusa/server/src/api/admin/odoo/logs/route.js +27 -0
- package/.medusa/server/src/api/admin/odoo/options/route.js +15 -0
- package/.medusa/server/src/api/admin/odoo/order-options/route.js +15 -0
- package/.medusa/server/src/api/admin/odoo/product-options/route.js +15 -0
- package/.medusa/server/src/api/admin/odoo/sections/[section]/route.js +55 -0
- package/.medusa/server/src/api/admin/odoo/settings/route.js +91 -0
- package/.medusa/server/src/api/admin/odoo/stripe/change/route.js +20 -0
- package/.medusa/server/src/api/admin/odoo/stripe/checkout/route.js +20 -0
- package/.medusa/server/src/api/admin/odoo/stripe/portal/route.js +16 -0
- package/.medusa/server/src/api/admin/odoo/sync/[entity]/route.js +99 -0
- package/.medusa/server/src/api/admin/odoo/sync-order/[id]/route.js +17 -0
- package/.medusa/server/src/api/admin/odoo/verify-connection/route.js +39 -0
- package/.medusa/server/src/api/odoo/billing/sync/route.js +42 -0
- package/.medusa/server/src/index.js +3 -0
- package/.medusa/server/src/jobs/import-worker.js +61 -0
- package/.medusa/server/src/lib/export-products.js +141 -0
- package/.medusa/server/src/lib/import-products.js +304 -0
- package/.medusa/server/src/lib/odoo-api-client.js +32 -0
- package/.medusa/server/src/lib/plan.js +83 -0
- package/.medusa/server/src/lib/queue.js +79 -0
- package/.medusa/server/src/lib/sync-customers.js +183 -0
- package/.medusa/server/src/lib/sync-inventory.js +135 -0
- package/.medusa/server/src/lib/sync-order.js +134 -0
- package/.medusa/server/src/lib/sync-products.js +32 -0
- package/.medusa/server/src/modules/odoo/index.js +13 -0
- package/.medusa/server/src/modules/odoo/migrations/Migration20260619101616.js +17 -0
- package/.medusa/server/src/modules/odoo/models/odoo-setting.js +19 -0
- package/.medusa/server/src/modules/odoo/service.js +28 -0
- package/.medusa/server/src/subscribers/order-placed.js +24 -0
- package/.medusa/server/src/workflows/index.js +3 -0
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/package.json +81 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.odooApi = odooApi;
|
|
4
|
+
/**
|
|
5
|
+
* Server-side client for the standalone odoo-api service.
|
|
6
|
+
*
|
|
7
|
+
* Runs inside Medusa (Node), so the API key never reaches the browser. Configure
|
|
8
|
+
* via env on the Medusa app:
|
|
9
|
+
* ODOO_API_URL (default http://localhost:4000)
|
|
10
|
+
* ODOO_API_KEY (default dev-odoo-api-key)
|
|
11
|
+
*/
|
|
12
|
+
const BASE = process.env.ODOO_API_URL || "http://localhost:4000";
|
|
13
|
+
const KEY = process.env.ODOO_API_KEY || "dev-odoo-api-key";
|
|
14
|
+
async function odooApi(path, init = {}) {
|
|
15
|
+
const res = await fetch(`${BASE}${path}`, {
|
|
16
|
+
...init,
|
|
17
|
+
headers: {
|
|
18
|
+
"Content-Type": "application/json",
|
|
19
|
+
"x-api-key": KEY,
|
|
20
|
+
...(init.headers || {}),
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
const text = await res.text();
|
|
24
|
+
const data = text ? JSON.parse(text) : null;
|
|
25
|
+
if (!res.ok) {
|
|
26
|
+
const message = (data && (data.message || data.error)) ||
|
|
27
|
+
`odoo-api request failed (${res.status})`;
|
|
28
|
+
throw new Error(message);
|
|
29
|
+
}
|
|
30
|
+
return data;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2Rvby1hcGktY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9vZG9vLWFwaS1jbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFXQSwwQkFxQkM7QUFoQ0Q7Ozs7Ozs7R0FPRztBQUNILE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsWUFBWSxJQUFJLHVCQUF1QixDQUFBO0FBQ2hFLE1BQU0sR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsWUFBWSxJQUFJLGtCQUFrQixDQUFBO0FBRW5ELEtBQUssVUFBVSxPQUFPLENBQzNCLElBQVksRUFDWixPQUFvQixFQUFFO0lBRXRCLE1BQU0sR0FBRyxHQUFHLE1BQU0sS0FBSyxDQUFDLEdBQUcsSUFBSSxHQUFHLElBQUksRUFBRSxFQUFFO1FBQ3hDLEdBQUcsSUFBSTtRQUNQLE9BQU8sRUFBRTtZQUNQLGNBQWMsRUFBRSxrQkFBa0I7WUFDbEMsV0FBVyxFQUFFLEdBQUc7WUFDaEIsR0FBRyxDQUFDLElBQUksQ0FBQyxPQUFPLElBQUksRUFBRSxDQUFDO1NBQ3hCO0tBQ0YsQ0FBQyxDQUFBO0lBQ0YsTUFBTSxJQUFJLEdBQUcsTUFBTSxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUE7SUFDN0IsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUE7SUFDM0MsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUNaLE1BQU0sT0FBTyxHQUNYLENBQUMsSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDdEMsNEJBQTRCLEdBQUcsQ0FBQyxNQUFNLEdBQUcsQ0FBQTtRQUMzQyxNQUFNLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFBO0lBQzFCLENBQUM7SUFDRCxPQUFPLElBQVMsQ0FBQTtBQUNsQixDQUFDIn0=
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ENTITY_MIN_PLAN = exports.PLANS = void 0;
|
|
4
|
+
exports.getPlanLimits = getPlanLimits;
|
|
5
|
+
exports.clampSchedule = clampSchedule;
|
|
6
|
+
const odoo_1 = require("../modules/odoo");
|
|
7
|
+
exports.PLANS = {
|
|
8
|
+
free: {
|
|
9
|
+
id: "free",
|
|
10
|
+
name: "Free",
|
|
11
|
+
productLimit: 100,
|
|
12
|
+
bothDirections: false,
|
|
13
|
+
maxSchedule: "off",
|
|
14
|
+
entities: { products: true, orders: false, inventory: false, customers: false },
|
|
15
|
+
},
|
|
16
|
+
starter: {
|
|
17
|
+
id: "starter",
|
|
18
|
+
name: "Starter",
|
|
19
|
+
productLimit: 1000,
|
|
20
|
+
bothDirections: true,
|
|
21
|
+
maxSchedule: "daily",
|
|
22
|
+
entities: { products: true, orders: true, inventory: false, customers: false },
|
|
23
|
+
},
|
|
24
|
+
pro: {
|
|
25
|
+
id: "pro",
|
|
26
|
+
name: "Pro",
|
|
27
|
+
productLimit: 10000,
|
|
28
|
+
bothDirections: true,
|
|
29
|
+
maxSchedule: "hourly",
|
|
30
|
+
entities: { products: true, orders: true, inventory: true, customers: true },
|
|
31
|
+
},
|
|
32
|
+
enterprise: {
|
|
33
|
+
id: "enterprise",
|
|
34
|
+
name: "Enterprise",
|
|
35
|
+
productLimit: null,
|
|
36
|
+
bothDirections: true,
|
|
37
|
+
maxSchedule: "realtime",
|
|
38
|
+
entities: { products: true, orders: true, inventory: true, customers: true },
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
/** Reads the active plan from settings; defaults to Free. */
|
|
42
|
+
async function getPlanLimits(scope) {
|
|
43
|
+
try {
|
|
44
|
+
const svc = scope.resolve(odoo_1.ODOO_MODULE);
|
|
45
|
+
const s = (await svc.getValue("section:plans"));
|
|
46
|
+
return (s?.planId && exports.PLANS[s.planId]) || exports.PLANS.free;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return exports.PLANS.free;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Which plan first unlocks each entity (for upgrade messaging). */
|
|
53
|
+
exports.ENTITY_MIN_PLAN = {
|
|
54
|
+
products: "Free",
|
|
55
|
+
orders: "Starter",
|
|
56
|
+
inventory: "Pro",
|
|
57
|
+
customers: "Pro",
|
|
58
|
+
};
|
|
59
|
+
// Schedule frequency ordering (coarse → fine).
|
|
60
|
+
const SCHED_RANK = {
|
|
61
|
+
off: 0,
|
|
62
|
+
daily: 1,
|
|
63
|
+
twice_daily: 2,
|
|
64
|
+
hourly: 3,
|
|
65
|
+
"30min": 4,
|
|
66
|
+
};
|
|
67
|
+
const PLAN_MAX_SCHEDULE_NAME = {
|
|
68
|
+
off: "off",
|
|
69
|
+
daily: "daily",
|
|
70
|
+
hourly: "hourly",
|
|
71
|
+
realtime: "30min",
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Clamps a requested import schedule to what the plan allows. Anything finer
|
|
75
|
+
* than the plan's ceiling is reduced to that ceiling (Free → always "off").
|
|
76
|
+
*/
|
|
77
|
+
function clampSchedule(requested, plan) {
|
|
78
|
+
const maxName = PLAN_MAX_SCHEDULE_NAME[plan.maxSchedule];
|
|
79
|
+
const reqRank = SCHED_RANK[requested ?? "off"] ?? 0;
|
|
80
|
+
const maxRank = SCHED_RANK[maxName] ?? 0;
|
|
81
|
+
return reqRank <= maxRank ? requested ?? "off" : maxName;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGxhbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvcGxhbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUE0REEsc0NBUUM7QUFnQ0Qsc0NBS0M7QUF4R0QsMENBQTZDO0FBdUJoQyxRQUFBLEtBQUssR0FBK0I7SUFDL0MsSUFBSSxFQUFFO1FBQ0osRUFBRSxFQUFFLE1BQU07UUFDVixJQUFJLEVBQUUsTUFBTTtRQUNaLFlBQVksRUFBRSxHQUFHO1FBQ2pCLGNBQWMsRUFBRSxLQUFLO1FBQ3JCLFdBQVcsRUFBRSxLQUFLO1FBQ2xCLFFBQVEsRUFBRSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUU7S0FDaEY7SUFDRCxPQUFPLEVBQUU7UUFDUCxFQUFFLEVBQUUsU0FBUztRQUNiLElBQUksRUFBRSxTQUFTO1FBQ2YsWUFBWSxFQUFFLElBQUk7UUFDbEIsY0FBYyxFQUFFLElBQUk7UUFDcEIsV0FBVyxFQUFFLE9BQU87UUFDcEIsUUFBUSxFQUFFLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRTtLQUMvRTtJQUNELEdBQUcsRUFBRTtRQUNILEVBQUUsRUFBRSxLQUFLO1FBQ1QsSUFBSSxFQUFFLEtBQUs7UUFDWCxZQUFZLEVBQUUsS0FBSztRQUNuQixjQUFjLEVBQUUsSUFBSTtRQUNwQixXQUFXLEVBQUUsUUFBUTtRQUNyQixRQUFRLEVBQUUsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFO0tBQzdFO0lBQ0QsVUFBVSxFQUFFO1FBQ1YsRUFBRSxFQUFFLFlBQVk7UUFDaEIsSUFBSSxFQUFFLFlBQVk7UUFDbEIsWUFBWSxFQUFFLElBQUk7UUFDbEIsY0FBYyxFQUFFLElBQUk7UUFDcEIsV0FBVyxFQUFFLFVBQVU7UUFDdkIsUUFBUSxFQUFFLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRTtLQUM3RTtDQUNGLENBQUE7QUFFRCw2REFBNkQ7QUFDdEQsS0FBSyxVQUFVLGFBQWEsQ0FBQyxLQUFzQjtJQUN4RCxJQUFJLENBQUM7UUFDSCxNQUFNLEdBQUcsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFxQixrQkFBVyxDQUFDLENBQUE7UUFDMUQsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxRQUFRLENBQUMsZUFBZSxDQUFDLENBQStCLENBQUE7UUFDN0UsT0FBTyxDQUFDLENBQUMsRUFBRSxNQUFNLElBQUksYUFBSyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxJQUFJLGFBQUssQ0FBQyxJQUFJLENBQUE7SUFDckQsQ0FBQztJQUFDLE1BQU0sQ0FBQztRQUNQLE9BQU8sYUFBSyxDQUFDLElBQUksQ0FBQTtJQUNuQixDQUFDO0FBQ0gsQ0FBQztBQUtELG9FQUFvRTtBQUN2RCxRQUFBLGVBQWUsR0FBZ0M7SUFDMUQsUUFBUSxFQUFFLE1BQU07SUFDaEIsTUFBTSxFQUFFLFNBQVM7SUFDakIsU0FBUyxFQUFFLEtBQUs7SUFDaEIsU0FBUyxFQUFFLEtBQUs7Q0FDakIsQ0FBQTtBQUVELCtDQUErQztBQUMvQyxNQUFNLFVBQVUsR0FBMkI7SUFDekMsR0FBRyxFQUFFLENBQUM7SUFDTixLQUFLLEVBQUUsQ0FBQztJQUNSLFdBQVcsRUFBRSxDQUFDO0lBQ2QsTUFBTSxFQUFFLENBQUM7SUFDVCxPQUFPLEVBQUUsQ0FBQztDQUNYLENBQUE7QUFDRCxNQUFNLHNCQUFzQixHQUFpQztJQUMzRCxHQUFHLEVBQUUsS0FBSztJQUNWLEtBQUssRUFBRSxPQUFPO0lBQ2QsTUFBTSxFQUFFLFFBQVE7SUFDaEIsUUFBUSxFQUFFLE9BQU87Q0FDbEIsQ0FBQTtBQUVEOzs7R0FHRztBQUNILFNBQWdCLGFBQWEsQ0FBQyxTQUE2QixFQUFFLElBQWdCO0lBQzNFLE1BQU0sT0FBTyxHQUFHLHNCQUFzQixDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQTtJQUN4RCxNQUFNLE9BQU8sR0FBRyxVQUFVLENBQUMsU0FBUyxJQUFJLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQTtJQUNuRCxNQUFNLE9BQU8sR0FBRyxVQUFVLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFBO0lBQ3hDLE9BQU8sT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLENBQUMsU0FBUyxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFBO0FBQzFELENBQUMifQ==
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PRODUCT_SCHEDULER_ID = exports.redisConnection = exports.INVENTORY_SYNC_QUEUE = exports.CUSTOMER_SYNC_QUEUE = exports.ORDER_SYNC_QUEUE = exports.PRODUCT_IMPORT_QUEUE = exports.REDIS_URL = void 0;
|
|
4
|
+
exports.getProductImportQueue = getProductImportQueue;
|
|
5
|
+
exports.getOrderSyncQueue = getOrderSyncQueue;
|
|
6
|
+
exports.getCustomerSyncQueue = getCustomerSyncQueue;
|
|
7
|
+
exports.getInventorySyncQueue = getInventorySyncQueue;
|
|
8
|
+
exports.reconcileProductSchedule = reconcileProductSchedule;
|
|
9
|
+
const bullmq_1 = require("bullmq");
|
|
10
|
+
exports.REDIS_URL = process.env.REDIS_URL || "redis://localhost:6379";
|
|
11
|
+
exports.PRODUCT_IMPORT_QUEUE = "odoo-product-import";
|
|
12
|
+
exports.ORDER_SYNC_QUEUE = "odoo-order-sync";
|
|
13
|
+
exports.CUSTOMER_SYNC_QUEUE = "odoo-customer-sync";
|
|
14
|
+
exports.INVENTORY_SYNC_QUEUE = "odoo-inventory-sync";
|
|
15
|
+
/**
|
|
16
|
+
* Connection options for BullMQ (it creates its own ioredis internally — don't
|
|
17
|
+
* pass a separately-installed instance or you hit a dual-package type clash).
|
|
18
|
+
* `maxRetriesPerRequest: null` is required by BullMQ workers.
|
|
19
|
+
*/
|
|
20
|
+
const u = new URL(exports.REDIS_URL);
|
|
21
|
+
exports.redisConnection = {
|
|
22
|
+
host: u.hostname || "localhost",
|
|
23
|
+
port: Number(u.port || 6379),
|
|
24
|
+
...(u.password ? { password: u.password } : {}),
|
|
25
|
+
...(u.username ? { username: u.username } : {}),
|
|
26
|
+
maxRetriesPerRequest: null,
|
|
27
|
+
};
|
|
28
|
+
/** Single lazily-created producer queue (opens Redis on first use). */
|
|
29
|
+
let _queue;
|
|
30
|
+
function getProductImportQueue() {
|
|
31
|
+
if (!_queue) {
|
|
32
|
+
_queue = new bullmq_1.Queue(exports.PRODUCT_IMPORT_QUEUE, { connection: exports.redisConnection });
|
|
33
|
+
}
|
|
34
|
+
return _queue;
|
|
35
|
+
}
|
|
36
|
+
let _orderQueue;
|
|
37
|
+
function getOrderSyncQueue() {
|
|
38
|
+
if (!_orderQueue) {
|
|
39
|
+
_orderQueue = new bullmq_1.Queue(exports.ORDER_SYNC_QUEUE, { connection: exports.redisConnection });
|
|
40
|
+
}
|
|
41
|
+
return _orderQueue;
|
|
42
|
+
}
|
|
43
|
+
let _customerQueue;
|
|
44
|
+
function getCustomerSyncQueue() {
|
|
45
|
+
if (!_customerQueue) {
|
|
46
|
+
_customerQueue = new bullmq_1.Queue(exports.CUSTOMER_SYNC_QUEUE, { connection: exports.redisConnection });
|
|
47
|
+
}
|
|
48
|
+
return _customerQueue;
|
|
49
|
+
}
|
|
50
|
+
let _inventoryQueue;
|
|
51
|
+
function getInventorySyncQueue() {
|
|
52
|
+
if (!_inventoryQueue) {
|
|
53
|
+
_inventoryQueue = new bullmq_1.Queue(exports.INVENTORY_SYNC_QUEUE, { connection: exports.redisConnection });
|
|
54
|
+
}
|
|
55
|
+
return _inventoryQueue;
|
|
56
|
+
}
|
|
57
|
+
exports.PRODUCT_SCHEDULER_ID = "product-import-schedule";
|
|
58
|
+
/** Cron pattern per schedule option (null = no schedule). */
|
|
59
|
+
const SCHEDULE_CRON = {
|
|
60
|
+
off: null,
|
|
61
|
+
"30min": "*/30 * * * *",
|
|
62
|
+
hourly: "0 * * * *",
|
|
63
|
+
twice_daily: "0 0,12 * * *",
|
|
64
|
+
daily: "0 0 * * *",
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Creates/updates/removes the repeatable import job to match the chosen
|
|
68
|
+
* schedule. Idempotent — safe to call on every settings save and on boot.
|
|
69
|
+
*/
|
|
70
|
+
async function reconcileProductSchedule(schedule) {
|
|
71
|
+
const queue = getProductImportQueue();
|
|
72
|
+
const pattern = SCHEDULE_CRON[schedule ?? "off"];
|
|
73
|
+
if (!pattern) {
|
|
74
|
+
await queue.removeJobScheduler(exports.PRODUCT_SCHEDULER_ID).catch(() => { });
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
await queue.upsertJobScheduler(exports.PRODUCT_SCHEDULER_ID, { pattern }, { name: "import", data: { scheduled: true } });
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXVldWUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3F1ZXVlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQXdCQSxzREFLQztBQUdELDhDQUtDO0FBR0Qsb0RBS0M7QUFHRCxzREFLQztBQWlCRCw0REFZQztBQWxGRCxtQ0FBc0Q7QUFFekMsUUFBQSxTQUFTLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLElBQUksd0JBQXdCLENBQUE7QUFDN0QsUUFBQSxvQkFBb0IsR0FBRyxxQkFBcUIsQ0FBQTtBQUM1QyxRQUFBLGdCQUFnQixHQUFHLGlCQUFpQixDQUFBO0FBQ3BDLFFBQUEsbUJBQW1CLEdBQUcsb0JBQW9CLENBQUE7QUFDMUMsUUFBQSxvQkFBb0IsR0FBRyxxQkFBcUIsQ0FBQTtBQUV6RDs7OztHQUlHO0FBQ0gsTUFBTSxDQUFDLEdBQUcsSUFBSSxHQUFHLENBQUMsaUJBQVMsQ0FBQyxDQUFBO0FBQ2YsUUFBQSxlQUFlLEdBQXNCO0lBQ2hELElBQUksRUFBRSxDQUFDLENBQUMsUUFBUSxJQUFJLFdBQVc7SUFDL0IsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQztJQUM1QixHQUFHLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7SUFDL0MsR0FBRyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0lBQy9DLG9CQUFvQixFQUFFLElBQUk7Q0FDM0IsQ0FBQTtBQUVELHVFQUF1RTtBQUN2RSxJQUFJLE1BQXlCLENBQUE7QUFDN0IsU0FBZ0IscUJBQXFCO0lBQ25DLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNaLE1BQU0sR0FBRyxJQUFJLGNBQUssQ0FBQyw0QkFBb0IsRUFBRSxFQUFFLFVBQVUsRUFBRSx1QkFBZSxFQUFFLENBQUMsQ0FBQTtJQUMzRSxDQUFDO0lBQ0QsT0FBTyxNQUFNLENBQUE7QUFDZixDQUFDO0FBRUQsSUFBSSxXQUE4QixDQUFBO0FBQ2xDLFNBQWdCLGlCQUFpQjtJQUMvQixJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDakIsV0FBVyxHQUFHLElBQUksY0FBSyxDQUFDLHdCQUFnQixFQUFFLEVBQUUsVUFBVSxFQUFFLHVCQUFlLEVBQUUsQ0FBQyxDQUFBO0lBQzVFLENBQUM7SUFDRCxPQUFPLFdBQVcsQ0FBQTtBQUNwQixDQUFDO0FBRUQsSUFBSSxjQUFpQyxDQUFBO0FBQ3JDLFNBQWdCLG9CQUFvQjtJQUNsQyxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDcEIsY0FBYyxHQUFHLElBQUksY0FBSyxDQUFDLDJCQUFtQixFQUFFLEVBQUUsVUFBVSxFQUFFLHVCQUFlLEVBQUUsQ0FBQyxDQUFBO0lBQ2xGLENBQUM7SUFDRCxPQUFPLGNBQWMsQ0FBQTtBQUN2QixDQUFDO0FBRUQsSUFBSSxlQUFrQyxDQUFBO0FBQ3RDLFNBQWdCLHFCQUFxQjtJQUNuQyxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7UUFDckIsZUFBZSxHQUFHLElBQUksY0FBSyxDQUFDLDRCQUFvQixFQUFFLEVBQUUsVUFBVSxFQUFFLHVCQUFlLEVBQUUsQ0FBQyxDQUFBO0lBQ3BGLENBQUM7SUFDRCxPQUFPLGVBQWUsQ0FBQTtBQUN4QixDQUFDO0FBRVksUUFBQSxvQkFBb0IsR0FBRyx5QkFBeUIsQ0FBQTtBQUU3RCw2REFBNkQ7QUFDN0QsTUFBTSxhQUFhLEdBQWtDO0lBQ25ELEdBQUcsRUFBRSxJQUFJO0lBQ1QsT0FBTyxFQUFFLGNBQWM7SUFDdkIsTUFBTSxFQUFFLFdBQVc7SUFDbkIsV0FBVyxFQUFFLGNBQWM7SUFDM0IsS0FBSyxFQUFFLFdBQVc7Q0FDbkIsQ0FBQTtBQUVEOzs7R0FHRztBQUNJLEtBQUssVUFBVSx3QkFBd0IsQ0FBQyxRQUFpQjtJQUM5RCxNQUFNLEtBQUssR0FBRyxxQkFBcUIsRUFBRSxDQUFBO0lBQ3JDLE1BQU0sT0FBTyxHQUFHLGFBQWEsQ0FBQyxRQUFRLElBQUksS0FBSyxDQUFDLENBQUE7SUFDaEQsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ2IsTUFBTSxLQUFLLENBQUMsa0JBQWtCLENBQUMsNEJBQW9CLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLEdBQUUsQ0FBQyxDQUFDLENBQUE7UUFDcEUsT0FBTTtJQUNSLENBQUM7SUFDRCxNQUFNLEtBQUssQ0FBQyxrQkFBa0IsQ0FDNUIsNEJBQW9CLEVBQ3BCLEVBQUUsT0FBTyxFQUFFLEVBQ1gsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsRUFBRSxDQUM5QyxDQUFBO0FBQ0gsQ0FBQyJ9
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.importCustomers = importCustomers;
|
|
4
|
+
exports.exportCustomers = exportCustomers;
|
|
5
|
+
exports.syncCustomers = syncCustomers;
|
|
6
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
7
|
+
const odoo_1 = require("../modules/odoo");
|
|
8
|
+
const odoo_api_client_1 = require("./odoo-api-client");
|
|
9
|
+
const plan_1 = require("./plan");
|
|
10
|
+
const splitName = (name) => {
|
|
11
|
+
const parts = (name || "").trim().split(/\s+/);
|
|
12
|
+
return { first: parts[0] || undefined, last: parts.slice(1).join(" ") || undefined };
|
|
13
|
+
};
|
|
14
|
+
const log = async (status, message) => {
|
|
15
|
+
try {
|
|
16
|
+
await (0, odoo_api_client_1.odooApi)("/odoo/logs", {
|
|
17
|
+
method: "POST",
|
|
18
|
+
body: JSON.stringify({ type: "customer_sync", status, message }),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
/* non-critical */
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
/** Odoo → Medusa: create/update Medusa customers from Odoo res.partner (by email). */
|
|
26
|
+
async function importCustomers(scope) {
|
|
27
|
+
const svc = scope.resolve(odoo_1.ODOO_MODULE);
|
|
28
|
+
const query = scope.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
29
|
+
const settings = ((await svc.getValue("section:customers")) ?? {});
|
|
30
|
+
const mode = settings.creationMode ?? "create_update";
|
|
31
|
+
const resp = await (0, odoo_api_client_1.odooApi)("/odoo/customers");
|
|
32
|
+
if (resp.connected === false || resp.error) {
|
|
33
|
+
return { ok: false, created: 0, updated: 0, skipped: 0, failed: 0, message: resp.error || "Could not reach Odoo." };
|
|
34
|
+
}
|
|
35
|
+
const customers = resp.customers ?? [];
|
|
36
|
+
if (!customers.length) {
|
|
37
|
+
return { ok: true, created: 0, updated: 0, skipped: 0, failed: 0, message: "No customers to import from Odoo." };
|
|
38
|
+
}
|
|
39
|
+
const { data: existing } = await query.graph({
|
|
40
|
+
entity: "customer",
|
|
41
|
+
fields: ["id", "email"],
|
|
42
|
+
pagination: { take: 2000 },
|
|
43
|
+
});
|
|
44
|
+
const byEmail = new Map(existing.map((c) => [(c.email || "").toLowerCase(), c.id]));
|
|
45
|
+
const toCreate = [];
|
|
46
|
+
const toUpdate = [];
|
|
47
|
+
let skipped = 0;
|
|
48
|
+
for (const c of customers) {
|
|
49
|
+
const email = (c.email || "").toLowerCase();
|
|
50
|
+
if (!email) {
|
|
51
|
+
skipped++;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const { first, last } = splitName(c.name);
|
|
55
|
+
const existingId = byEmail.get(email);
|
|
56
|
+
if (existingId) {
|
|
57
|
+
if (mode === "create") {
|
|
58
|
+
skipped++;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
toUpdate.push({ id: existingId, first_name: first, last_name: last, phone: c.phone || undefined });
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
if (mode === "update") {
|
|
65
|
+
skipped++;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
toCreate.push({ email: c.email, first_name: first, last_name: last, phone: c.phone || undefined });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const customerModule = scope.resolve(utils_1.Modules.CUSTOMER);
|
|
72
|
+
let created = 0;
|
|
73
|
+
let updated = 0;
|
|
74
|
+
let failed = 0;
|
|
75
|
+
try {
|
|
76
|
+
if (toCreate.length) {
|
|
77
|
+
await customerModule.createCustomers(toCreate);
|
|
78
|
+
created = toCreate.length;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
failed += toCreate.length;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
if (toUpdate.length) {
|
|
86
|
+
await customerModule.updateCustomers(toUpdate);
|
|
87
|
+
updated = toUpdate.length;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
failed += toUpdate.length;
|
|
92
|
+
}
|
|
93
|
+
const message = `Odoo → Medusa customers: ${created} created, ${updated} updated, ${skipped} skipped${failed ? `, ${failed} failed` : ""}.`;
|
|
94
|
+
await log(failed ? "failed" : "success", message);
|
|
95
|
+
return { ok: failed === 0, created, updated, skipped, failed, message };
|
|
96
|
+
}
|
|
97
|
+
/** Medusa → Odoo: push Medusa customers into Odoo res.partner. */
|
|
98
|
+
async function exportCustomers(scope) {
|
|
99
|
+
const svc = scope.resolve(odoo_1.ODOO_MODULE);
|
|
100
|
+
const query = scope.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
101
|
+
const settings = ((await svc.getValue("section:customers")) ?? {});
|
|
102
|
+
const mode = settings.creationMode ?? "create_update";
|
|
103
|
+
const { data: customers } = await query.graph({
|
|
104
|
+
entity: "customer",
|
|
105
|
+
fields: ["id", "email", "first_name", "last_name", "phone", "metadata"],
|
|
106
|
+
pagination: { take: 2000 },
|
|
107
|
+
});
|
|
108
|
+
const inputs = [];
|
|
109
|
+
const byId = new Map();
|
|
110
|
+
let skipped = 0;
|
|
111
|
+
for (const c of customers) {
|
|
112
|
+
if (!c.email) {
|
|
113
|
+
skipped++;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const odooId = c.metadata?.odoo_id ?? null;
|
|
117
|
+
if (odooId && mode === "create") {
|
|
118
|
+
skipped++;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (!odooId && mode === "update") {
|
|
122
|
+
skipped++;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
inputs.push({
|
|
126
|
+
externalId: c.id,
|
|
127
|
+
odooId,
|
|
128
|
+
name: [c.first_name, c.last_name].filter(Boolean).join(" ") || c.email,
|
|
129
|
+
email: c.email,
|
|
130
|
+
phone: c.phone || null,
|
|
131
|
+
});
|
|
132
|
+
byId.set(c.id, c);
|
|
133
|
+
}
|
|
134
|
+
if (!inputs.length) {
|
|
135
|
+
return { ok: true, created: 0, updated: 0, skipped, failed: 0, message: "Medusa → Odoo customers: nothing to export." };
|
|
136
|
+
}
|
|
137
|
+
let resp;
|
|
138
|
+
try {
|
|
139
|
+
resp = await (0, odoo_api_client_1.odooApi)("/odoo/customers-export", { method: "POST", body: JSON.stringify({ customers: inputs }) });
|
|
140
|
+
}
|
|
141
|
+
catch (e) {
|
|
142
|
+
return { ok: false, created: 0, updated: 0, skipped, failed: inputs.length, message: e?.message ?? "Export failed" };
|
|
143
|
+
}
|
|
144
|
+
let created = 0;
|
|
145
|
+
let updated = 0;
|
|
146
|
+
let failed = 0;
|
|
147
|
+
const updates = [];
|
|
148
|
+
for (const r of resp.results ?? []) {
|
|
149
|
+
if (r.error || !r.odooId) {
|
|
150
|
+
failed++;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (r.created)
|
|
154
|
+
created++;
|
|
155
|
+
else
|
|
156
|
+
updated++;
|
|
157
|
+
const c = byId.get(r.externalId);
|
|
158
|
+
updates.push({ id: r.externalId, metadata: { ...(c?.metadata || {}), odoo_id: r.odooId } });
|
|
159
|
+
}
|
|
160
|
+
if (updates.length) {
|
|
161
|
+
const customerModule = scope.resolve(utils_1.Modules.CUSTOMER);
|
|
162
|
+
await customerModule.updateCustomers(updates);
|
|
163
|
+
}
|
|
164
|
+
const message = `Medusa → Odoo customers: ${created} created, ${updated} updated, ${skipped} skipped${failed ? `, ${failed} failed` : ""}.`;
|
|
165
|
+
await log(failed ? "failed" : "success", message);
|
|
166
|
+
return { ok: failed === 0, created, updated, skipped, failed, message };
|
|
167
|
+
}
|
|
168
|
+
/** Routes by direction; gated by the customerSync toggle and the active plan. */
|
|
169
|
+
async function syncCustomers(scope) {
|
|
170
|
+
const svc = scope.resolve(odoo_1.ODOO_MODULE);
|
|
171
|
+
const settings = ((await svc.getValue("section:customers")) ?? {});
|
|
172
|
+
if (!settings.customerSync) {
|
|
173
|
+
return { ok: true, created: 0, updated: 0, skipped: 0, failed: 0, message: "Customer sync is disabled." };
|
|
174
|
+
}
|
|
175
|
+
const plan = await (0, plan_1.getPlanLimits)(scope);
|
|
176
|
+
if (!plan.entities.customers) {
|
|
177
|
+
const message = `Customer sync requires the Pro plan or higher (current: ${plan.name}).`;
|
|
178
|
+
await log("skipped", message);
|
|
179
|
+
return { ok: false, created: 0, updated: 0, skipped: 0, failed: 0, message };
|
|
180
|
+
}
|
|
181
|
+
return settings.direction === "medusa_to_odoo" ? exportCustomers(scope) : importCustomers(scope);
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3luYy1jdXN0b21lcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3N5bmMtY3VzdG9tZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBdUNBLDBDQXdFQztBQUdELDBDQXVFQztBQUdELHNDQWFDO0FBeE1ELHFEQUE4RTtBQUM5RSwwQ0FBNkM7QUFFN0MsdURBQTJDO0FBQzNDLGlDQUFzQztBQWlCdEMsTUFBTSxTQUFTLEdBQUcsQ0FBQyxJQUFZLEVBQUUsRUFBRTtJQUNqQyxNQUFNLEtBQUssR0FBRyxDQUFDLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUE7SUFDOUMsT0FBTyxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksU0FBUyxFQUFFLElBQUksRUFBRSxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxTQUFTLEVBQUUsQ0FBQTtBQUN0RixDQUFDLENBQUE7QUFFRCxNQUFNLEdBQUcsR0FBRyxLQUFLLEVBQUUsTUFBYyxFQUFFLE9BQWUsRUFBRSxFQUFFO0lBQ3BELElBQUksQ0FBQztRQUNILE1BQU0sSUFBQSx5QkFBTyxFQUFDLFlBQVksRUFBRTtZQUMxQixNQUFNLEVBQUUsTUFBTTtZQUNkLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsSUFBSSxFQUFFLGVBQWUsRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLENBQUM7U0FDakUsQ0FBQyxDQUFBO0lBQ0osQ0FBQztJQUFDLE1BQU0sQ0FBQztRQUNQLGtCQUFrQjtJQUNwQixDQUFDO0FBQ0gsQ0FBQyxDQUFBO0FBRUQsc0ZBQXNGO0FBQy9FLEtBQUssVUFBVSxlQUFlLENBQUMsS0FBc0I7SUFDMUQsTUFBTSxHQUFHLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBcUIsa0JBQVcs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
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.importInventory = importInventory;
|
|
4
|
+
exports.exportInventory = exportInventory;
|
|
5
|
+
exports.syncInventory = syncInventory;
|
|
6
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
7
|
+
const odoo_1 = require("../modules/odoo");
|
|
8
|
+
const odoo_api_client_1 = require("./odoo-api-client");
|
|
9
|
+
const plan_1 = require("./plan");
|
|
10
|
+
const log = async (status, message) => {
|
|
11
|
+
try {
|
|
12
|
+
await (0, odoo_api_client_1.odooApi)("/odoo/logs", {
|
|
13
|
+
method: "POST",
|
|
14
|
+
body: JSON.stringify({ type: "inventory_sync", status, message }),
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
/* non-critical */
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
/** Odoo → Medusa: read Odoo stock per mapped warehouse, set Medusa levels by SKU. */
|
|
22
|
+
async function importInventory(scope) {
|
|
23
|
+
const svc = scope.resolve(odoo_1.ODOO_MODULE);
|
|
24
|
+
const query = scope.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
25
|
+
const inventory = scope.resolve(utils_1.Modules.INVENTORY);
|
|
26
|
+
const settings = ((await svc.getValue("section:inventory")) ?? {});
|
|
27
|
+
if (!settings.enableStock) {
|
|
28
|
+
return { ok: true, updated: 0, skipped: 0, failed: 0, message: "Stock sync is disabled." };
|
|
29
|
+
}
|
|
30
|
+
const mappings = (settings.locationMappings ?? []).filter((m) => m.medusaLocationId && m.odooWarehouseId);
|
|
31
|
+
if (!mappings.length) {
|
|
32
|
+
return { ok: true, updated: 0, skipped: 0, failed: 0, message: "No location mappings configured." };
|
|
33
|
+
}
|
|
34
|
+
const { data: items } = await query.graph({
|
|
35
|
+
entity: "inventory_item",
|
|
36
|
+
fields: ["id", "sku"],
|
|
37
|
+
pagination: { take: 5000 },
|
|
38
|
+
});
|
|
39
|
+
const itemBySku = new Map(items.filter((i) => i.sku).map((i) => [i.sku, i.id]));
|
|
40
|
+
const skus = [...itemBySku.keys()];
|
|
41
|
+
if (!skus.length) {
|
|
42
|
+
return { ok: true, updated: 0, skipped: 0, failed: 0, message: "No SKUs to sync." };
|
|
43
|
+
}
|
|
44
|
+
let updated = 0;
|
|
45
|
+
let skipped = 0;
|
|
46
|
+
let failed = 0;
|
|
47
|
+
for (const map of mappings) {
|
|
48
|
+
const resp = await (0, odoo_api_client_1.odooApi)("/odoo/inventory/read", { method: "POST", body: JSON.stringify({ skus, warehouseId: map.odooWarehouseId, qtyType: settings.qtyType }) });
|
|
49
|
+
if (resp.connected === false || resp.error) {
|
|
50
|
+
return { ok: false, updated, skipped, failed, message: resp.error || "Could not reach Odoo." };
|
|
51
|
+
}
|
|
52
|
+
for (const lvl of resp.levels ?? []) {
|
|
53
|
+
const itemId = itemBySku.get(lvl.sku);
|
|
54
|
+
if (!itemId)
|
|
55
|
+
continue;
|
|
56
|
+
if (settings.doNotSyncZeroStock && lvl.qty === 0) {
|
|
57
|
+
skipped++;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const existing = await inventory.listInventoryLevels({
|
|
62
|
+
inventory_item_id: itemId,
|
|
63
|
+
location_id: map.medusaLocationId,
|
|
64
|
+
});
|
|
65
|
+
if (existing[0]) {
|
|
66
|
+
await inventory.updateInventoryLevels([{ id: existing[0].id, stocked_quantity: lvl.qty }]);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
await inventory.createInventoryLevels([
|
|
70
|
+
{ inventory_item_id: itemId, location_id: map.medusaLocationId, stocked_quantity: lvl.qty },
|
|
71
|
+
]);
|
|
72
|
+
}
|
|
73
|
+
updated++;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
failed++;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const message = `Odoo → Medusa inventory: ${updated} levels updated, ${skipped} skipped${failed ? `, ${failed} failed` : ""}.`;
|
|
81
|
+
await log(failed ? "failed" : "success", message);
|
|
82
|
+
return { ok: failed === 0, updated, skipped, failed, message };
|
|
83
|
+
}
|
|
84
|
+
/** Medusa → Odoo: push Medusa levels at mapped locations to the Odoo warehouse. */
|
|
85
|
+
async function exportInventory(scope) {
|
|
86
|
+
const svc = scope.resolve(odoo_1.ODOO_MODULE);
|
|
87
|
+
const query = scope.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
88
|
+
const inventory = scope.resolve(utils_1.Modules.INVENTORY);
|
|
89
|
+
const settings = ((await svc.getValue("section:inventory")) ?? {});
|
|
90
|
+
if (!settings.enableStock) {
|
|
91
|
+
return { ok: true, updated: 0, skipped: 0, failed: 0, message: "Stock sync is disabled." };
|
|
92
|
+
}
|
|
93
|
+
const mappings = (settings.locationMappings ?? []).filter((m) => m.medusaLocationId && m.odooWarehouseId);
|
|
94
|
+
if (!mappings.length) {
|
|
95
|
+
return { ok: true, updated: 0, skipped: 0, failed: 0, message: "No location mappings configured." };
|
|
96
|
+
}
|
|
97
|
+
const { data: items } = await query.graph({
|
|
98
|
+
entity: "inventory_item",
|
|
99
|
+
fields: ["id", "sku"],
|
|
100
|
+
pagination: { take: 5000 },
|
|
101
|
+
});
|
|
102
|
+
const skuByItem = new Map(items.filter((i) => i.sku).map((i) => [i.id, i.sku]));
|
|
103
|
+
let updated = 0;
|
|
104
|
+
let failed = 0;
|
|
105
|
+
for (const map of mappings) {
|
|
106
|
+
const levels = await inventory.listInventoryLevels({ location_id: map.medusaLocationId });
|
|
107
|
+
const out = levels
|
|
108
|
+
.map((l) => ({ sku: skuByItem.get(l.inventory_item_id), qty: l.stocked_quantity ?? 0 }))
|
|
109
|
+
.filter((x) => x.sku);
|
|
110
|
+
if (!out.length)
|
|
111
|
+
continue;
|
|
112
|
+
const resp = await (0, odoo_api_client_1.odooApi)("/odoo/inventory/write", { method: "POST", body: JSON.stringify({ levels: out, warehouseId: map.odooWarehouseId }) });
|
|
113
|
+
updated += resp.updated ?? 0;
|
|
114
|
+
failed += resp.failed ?? 0;
|
|
115
|
+
}
|
|
116
|
+
const message = `Medusa → Odoo inventory: ${updated} levels written${failed ? `, ${failed} failed` : ""}.`;
|
|
117
|
+
await log(failed ? "failed" : "success", message);
|
|
118
|
+
return { ok: failed === 0, updated, skipped: 0, failed, message };
|
|
119
|
+
}
|
|
120
|
+
/** Routes by direction; gated by the stock toggle and the active plan. */
|
|
121
|
+
async function syncInventory(scope) {
|
|
122
|
+
const svc = scope.resolve(odoo_1.ODOO_MODULE);
|
|
123
|
+
const settings = ((await svc.getValue("section:inventory")) ?? {});
|
|
124
|
+
if (!settings.enableStock) {
|
|
125
|
+
return { ok: true, updated: 0, skipped: 0, failed: 0, message: "Stock sync is disabled." };
|
|
126
|
+
}
|
|
127
|
+
const plan = await (0, plan_1.getPlanLimits)(scope);
|
|
128
|
+
if (!plan.entities.inventory) {
|
|
129
|
+
const message = `Inventory sync requires the Pro plan or higher (current: ${plan.name}).`;
|
|
130
|
+
await log("skipped", message);
|
|
131
|
+
return { ok: false, updated: 0, skipped: 0, failed: 0, message };
|
|
132
|
+
}
|
|
133
|
+
return settings.direction === "medusa_to_odoo" ? exportInventory(scope) : importInventory(scope);
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3luYy1pbnZlbnRvcnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3N5bmMtaW52ZW50b3J5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBb0NBLDBDQWtFQztBQUdELDBDQXdDQztBQUdELHNDQWFDO0FBaEtELHFEQUE4RTtBQUM5RSwwQ0FBNkM7QUFFN0MsdURBQTJDO0FBQzNDLGlDQUFzQztBQW1CdEMsTUFBTSxHQUFHLEdBQUcsS0FBSyxFQUFFLE1BQWMsRUFBRSxPQUFlLEVBQUUsRUFBRTtJQUNwRCxJQUFJLENBQUM7UUFDSCxNQUFNLElBQUEseUJBQU8sRUFBQyxZQUFZLEVBQUU7WUFDMUIsTUFBTSxFQUFFLE1BQU07WUFDZCxJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLElBQUksRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLENBQUM7U0FDbEUsQ0FBQyxDQUFBO0lBQ0osQ0FBQztJQUFDLE1BQU0sQ0FBQztRQUNQLGtCQUFrQjtJQUNwQixDQUFDO0FBQ0gsQ0FBQyxDQUFBO0FBRUQscUZBQXFGO0FBQzlFLEtBQUssVUFBVSxlQUFlLENBQUMsS0FBc0I7SUFDMUQsTUFBTSxHQUFHLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBcUIsa0JBQVcsQ0FBQyxDQUFBO0lBQzFELE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsaUNBQXlCLENBQUMsS0FBSyxDQUFDLENBQUE7SUFDNUQsTUFBTSxTQUFTLEdBQVEsS0FBSyxDQUFDLE9BQU8sQ0FBQyxlQUFPLENBQUMsU0FBUyxDQUFDLENBQUE7SUFDdkQsTUFBTSxRQUFRLEdBQUcsQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLFFBQVEsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFzQixDQUFBO0lBRXZGLElBQUksQ0FBQyxRQUFRLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDMUIsT0FBTyxF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
|