diff --git a/index.js b/index.js index ab607f1..b4e2411 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,8 @@ import express from "express"; import bodyParser from "body-parser"; import mongoose from "mongoose"; import cors from "cors"; +import atob from "atob"; + import dotenv from "dotenv"; import multer from "multer"; import helmet from "helmet"; @@ -120,10 +122,10 @@ function sendEmail(req, res, next) { headers: { "Content-Type": "application/json", Authorization: `Basic ${apiKey}`, - }, - }; - - axios + }, + }; + + axios .post(apiUrl, requestData, config) .then((response) => { // const status = response.data.Messages[0].Status; @@ -143,20 +145,41 @@ function sendEmail(req, res, next) { } app.post("/api/send-email/", sendEmail); -const odooClient = new Odoo({ - url: "https://gis.pivi.com.ph", - db: "gis.pivi.com.ph", - username: "egalang@obanana.com", +// const odooClient = new Odoo({ +// url: "http://192.168.50.15:8070", +// db: "gis.pivi.com.ph", +// username: "egalang@obanana.com", +// password: "P@$$w0rd!", +// }); +const odooClient2 = new Odoo({ + url: "http://192.168.50.15:8071", + db: "pos.obanana.com", + username: "mahipe@obanana.com", password: "P@$$w0rd!", }); -const odooClient2 = new Odoo({ - url: "http://localhost:8069/", - db: "Paymongo", - username: "mahipe@obanana.com", - password: "abcd123", -}); -app.get("/get-assets", async (req, res) => { +app.get("/get-assets", async (req, res) => { try { + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith('Basic ')) { + return res.status(400).json({ error: 'Authorization header is missing or invalid.' }); + } + + const base64Credentials = authHeader.split(' ')[1]; + const decodedCredentials = atob(base64Credentials); + const [email, password] = decodedCredentials.split(':'); + + if (!email || !password) { + return res.status(400).json({ error: 'Invalid credentials format.' }); + } + // console.log(email,password) + + const odooClient = new Odoo({ + url: "http://192.168.50.15:8070", + db: "gis.pivi.com.ph", + username: email, + password: password + }); + odooClient.connect(async function (err) { if (err) { console.log(err); @@ -166,7 +189,7 @@ app.get("/get-assets", async (req, res) => { } console.log("Connected to Odoo server."); - // Fetch asset IDs + // Fetch asset IDs const inParams = []; inParams.push([["name", "!=", false]]); inParams.push(parseInt(req.query.offset) || 0); //offset @@ -346,53 +369,117 @@ app.get("/get-assets", async (req, res) => { }); } else { images = image; - - const asset = recordsArray.map( - (item) => { - // Find matching attributes based on the numeric part of attr.assets - const matchedAttr = - attributes.filter( - (attribute) => - item.attributes.some( - (attrId) => - attrId === - attribute.id - ) // Compare to the first element of attr.assets - ); - - // Find matching bounds based on the numeric part of bounds.assets - const matchedBounds = - bounds.filter( - (bound) => - item.bounds.some( - (boundId) => - boundId === bound.id - ) // Compare to the first element of bounds.assets - ); - - // Find matching images based on data.id == images.res_id - const matchedImages = - images.filter( - (image) => - image.res_id === item.id - ); - - // Construct the asset object for this item - return { - attr: matchedAttr, - bounds: matchedBounds, - data: item, - images: matchedImages, - }; + const inParams = []; + inParams.push([["related_asset", "!=", false]]); + inParams.push(parseInt(req.query.offset) || 0); //offset + inParams.push(parseInt(req.query.limit) || 0); //Limit + const params1 = [inParams]; + odooClient.execute_kw( + "pivi_assets.related", + "search", + params1, + async function (err, ids) { + if (err) { + console.log(err); + return res.status(500).json({ + error: + "Search Error: " + err.message, + }); + } + + if (ids.length === 0) { + return res.json([]); // No assets found + } + + // Fetch asset records + const inParamsRead = []; + inParamsRead.push(ids); // IDs + inParamsRead.push([ + "id", + "related_asset", + "asset", + ]); + const paramsRead = [inParamsRead]; + + odooClient.execute_kw( + "pivi_assets.related", + "read", + paramsRead, + async function (err2, related) { + if (err2) { + console.log(err2); + return res.status(500).json({ + error: + "Read Error: " + err2.message, + }); + } else { + let relateds + relateds = related; + + const asset = recordsArray.map( + (item) => { + // Find matching attributes based on the numeric part of attr.assets + const matchedAttr = + attributes.filter( + (attribute) => + item.attributes.some( + (attrId) => + attrId === + attribute.id + ) // Compare to the first element of attr.assets + ); + + // Find matching bounds based on the numeric part of bounds.assets + const matchedBounds = + bounds.filter( + (bound) => + item.bounds.some( + (boundId) => + boundId === bound.id + ) // Compare to the first element of bounds.assets + ); + // Find matching bounds based on the numeric part of bounds.assets + const matchedRelated = + relateds.filter( + (related) => + item.related.some( + (relatedId) => + relatedId === related.id + ) + ); + + // Find matching images based on data.id == images.res_id + const matchedImages = + images.filter( + (image) => + image.res_id === item.id + ); + + // Construct the asset object for this item + return { + attr: matchedAttr, + bounds: matchedBounds, + data: item, + images: matchedImages, + related: matchedRelated + }; + } + ); + // res.json({ + // data: recordsArray, + // attr: attributes, + // bounds: bounds, + // images: images, + // }); + res.json(asset); + } + + let pendingRequests = + records.length; + } + ); } ); - // res.json({ - // data: recordsArray, - // attr: attributes, - // bounds: bounds, - // images: images, - // }); - res.json(asset); } let pendingRequests = @@ -719,10 +806,9 @@ app.get("/get-stages", async (req, res) => { res.status(500).json({ error: "Data Fetch Error: " + error.message }); } }); - + app.post('/update-order-state', async (req, res) => { - const { id, state } = req.body; // Order ID and new state passed in the request body - + const { id, state } = req.body; if (!id || !state) { return res.status(400).json({ error: 'Order ID and state are required.' }); } @@ -735,9 +821,8 @@ app.post('/update-order-state', async (req, res) => { } console.log('Connected to Odoo server.'); - // Search for the order by ID const inParams = []; - inParams.push([['id', '=', id]]); // Find the order by its ID + inParams.push([['id', '=', id]]); const params = [inParams]; odooClient2.execute_kw('pos.order', 'search', params, function (err, orderIds) { @@ -750,10 +835,9 @@ app.post('/update-order-state', async (req, res) => { return res.status(404).json({ error: 'Order not found.' }); } - // Update the state of the order const inParamsUpdate = []; - inParamsUpdate.push(orderIds); // Order IDs to update - inParamsUpdate.push({ state: state }); // Fields to update + inParamsUpdate.push(orderIds); + inParamsUpdate.push({ state: state }); const paramsUpdate = [inParamsUpdate]; odooClient2.execute_kw('pos.order', 'write', paramsUpdate, function (err2, result) { @@ -775,16 +859,293 @@ app.post('/update-order-state', async (req, res) => { return res.status(500).json({ error: 'Update Error: ' + error.message }); } }); +app.get("/get-tables", async (req, res) => { + try { + odooClient2.connect(async function (err) { + if (err) { + console.log(err); + return res + .status(500) + .json({ error: "Connection Error: " + err.message }); + } + console.log("Connected to Odoo server."); + // Fetch asset IDs + const inParams = []; + inParams.push([["name", "!=", false]]); + inParams.push(parseInt(req.query.offset) || 0); + inParams.push(parseInt(req.query.limit) || 0); + const params = [inParams]; + const assets = []; + let recordsArray = []; -function fetchOdooData(model, method, params) { - return new Promise((resolve, reject) => { - odooClient.execute_kw(model, method, params, (err, data) => { - if (err) return reject(err); - resolve(data); + let attributes = []; + let bounds = []; + let images = []; + + odooClient2.execute_kw( + "restaurant.table", + "search", + params, + async function (err, ids) { + if (err) { + console.log(err); + return res + .status(500) + .json({ error: "Search Error: " + err.message }); + } + + if (ids.length === 0) { + return res.json([]); // No assets found + } + + // Fetch asset records + const inParamsRead = []; + inParamsRead.push(ids); // IDs + // inParamsRead.push([ + // "id", + // "name", + + // ]); + const paramsRead = [inParamsRead]; + + odooClient2.execute_kw( + "restaurant.table", + "read", + paramsRead, + async function (err2, records) { + if (err2) { + console.log(err2); + return res + .status(500) + .json({ error: "Read Error: " + err2.message }); + } else { + recordsArray = records; + res.json(recordsArray); + } + + let pendingRequests = records.length; + } + ); + } + ); }); - }); -} + } catch (error) { + console.error("Error:", error); + res.status(500).json({ error: "Data Fetch Error: " + error.message }); + } +}); + +app.post('/create-asset-bounds', async (req, res) => { + try { + const postData = req.body; + + if (!postData || postData.length === 0) { + return res.status(200).json({ error: 'No data provided.' }); + } + + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith('Basic ')) { + return res.status(400).json({ error: 'Authorization header is missing or invalid.' }); + } + + const base64Credentials = authHeader.split(' ')[1]; + const decodedCredentials = Buffer.from(base64Credentials, 'base64').toString('ascii'); // safer than `atob` + const [email, password] = decodedCredentials.split(':'); + + if (!email || !password) { + return res.status(400).json({ error: 'Invalid credentials format.' }); + } + + const odooClient = new Odoo({ + url: "http://192.168.50.15:8070", + db: "gis.pivi.com.ph", + username: email, + password: password + }); + + odooClient.connect(async function (err) { + if (err) { + console.log(err); + return res.status(500).json({ error: 'Connection Error: ' + err.message }); + } + + console.log('Connected to Odoo server.'); + + // Use Promise.all to wait for all async operations to finish + try { + const createdIds = await Promise.all( + postData.map(async (data, index) => { + const { asset, name, longitude, latitude } = data; + + if (!asset || !name || !longitude || !latitude) { + throw new Error(`Missing required fields in entry ${index + 1}`); + } + + const params = [ + { + asset: asset, + name: name, + longitude: longitude, + latitude: latitude, + } + ]; + + // Return a promise for each create operation + return new Promise((resolve, reject) => { + odooClient.execute_kw('pivi_assets.bounds', 'create', [params], function (err2, id) { + if (err2) { + console.log('Create Error:', err2); + return reject(new Error('Create Error: ' + err2.message)); + } + + resolve(id); + }); + }); + }) + ); + + return res.json({ success: true, createdIds }); + } catch (error) { + return res.status(500).json({ error: error.message }); + } + }); + } catch (error) { + console.error('Error:', error); + return res.status(500).json({ error: 'Server Error: ' + error.message }); + } +}); + +app.post('/odoo-webhook', (req, res) => { + const newOrder = req.body; + console.log('New POS order received:', newOrder); + + // Process the order data as needed + // For example, send it to a WebSocket, log it, or store it in a database + + res.status(200).send('Webhook received'); +}); + + +app.delete('/delete-asset-bounds', async (req, res) => { + try { + const assetId = req.query.asset; + console.log(assetId) + if (!assetId) { + return res.status(400).json({ error: 'Asset ID is required.' }); + } + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith('Basic ')) { + return res.status(400).json({ error: 'Authorization header is missing or invalid.' }); + } + const base64Credentials = authHeader.split(' ')[1]; + const decodedCredentials = atob(base64Credentials); + const [email, password] = decodedCredentials.split(':'); + + if (!email || !password) { + return res.status(400).json({ error: 'Invalid credentials format.' }); + } + // console.log(email,password) + + const odooClient = new Odoo({ + url: "http://192.168.50.15:8070", + db: "gis.pivi.com.ph", + username: email, + password: password + }); + odooClient.connect(function (err) { + if (err) { + console.log(err); + return res.status(500).json({ error: 'Connection Error: ' + err.message }); + } + console.log('Connected to Odoo server.'); + + + const inParams = []; + inParams.push([['id', '=', assetId]]); + const searchParamsAsset = [inParams]; + // const searchParamsAsset = [ + // [['id', '=', assetId]] + // ]; + + odooClient.execute_kw('pivi_assets.pivi_assets', 'search', searchParamsAsset, function (err2, assetIds) { + if (err2) { + console.log(assetIds+" id") + console.log('Search Error:', err2); + return res.status(500).json({ error: 'Search Asset Error: ' + err2.message }); + } + if (assetIds.length === 0) { + return res.status(404).json({ error: 'Asset not found.' }); + } + + odooClient.execute_kw('pivi_assets.pivi_assets', 'read', [assetIds], function (err3, assets) { + if (err3) { + console.log('Read Error:', err3); + return res.status(500).json({ error: 'Read Error: ' + err3.message }); + } + + const assetName = assets[0].name; + + // const searchParamsBounds = [ + // [['asset', '=', assetName]] + // ]; + const inParams = []; + inParams.push([['asset', '=', assetName]]); + const searchParamsBounds = [inParams]; + + odooClient.execute_kw('pivi_assets.bounds', 'search', searchParamsBounds, function (err4, boundIds) { + if (err4) { + console.log('Search Bounds Error:', err4); + return res.status(500).json({ error: 'Search Bounds Error: ' + err4.message }); + } + + if (boundIds.length === 0) { + return res.status(200).json({ error: 'No bounds found for this asset.' }); + } + + odooClient.execute_kw('pivi_assets.bounds', 'read', [[boundIds]], function (err5, bounds) { + if (err5) { + console.log('Read Bounds Error:', err5); + return res.status(500).json({ error: 'Read Bounds Error: ' + err5.message }); + } + + const deletedIds = []; + bounds.forEach((bound, index) => { + const boundId = bound.id; + + odooClient.execute_kw('pivi_assets.bounds', 'unlink', [[boundId]], function (err6) { + if (err6) { + console.log('Unlink Error:', err6); + return res.status(500).json({ error: 'Unlink Error: ' + err6.message }); + } + + deletedIds.push(boundId); + + if (deletedIds.length === bounds.length) { + return res.json({ success: true, deletedIds }); + } + }); + }); + }); + }); + }); + }); + }); + } catch (error) { + console.error('Error:', error); + return res.status(500).json({ error: 'Server Error: ' + error.message }); + } +}); + + +// function fetchOdooData(model, method, params) { +// return new Promise((resolve, reject) => { +// odooClient.execute_kw(model, method, params, (err, data) => { +// if (err) return reject(err); +// resolve(data); +// }); +// }); +// } /* MONGOOSE SETUP */ const PORT = process.env.PORT || 3002; @@ -801,7 +1162,7 @@ const PORT = process.env.PORT || 3002; // .catch((error) => console.log(`${error} did not connect`)); mongoose - .connect(process.env.MONGO_URL) + .connect(process.env.DATABASE_URL) .then(() => { app.listen(PORT, () => console.log(`Server Port: ${PORT}`)); diff --git a/package-lock.json b/package-lock.json index b6eb8c8..97d1fca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "atob": "^2.1.2", "axios": "^1.7.4", "bcrypt": "^5.1.1", "body-parser": "^1.20.2", @@ -184,6 +185,18 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "license": "(MIT OR Apache-2.0)", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, "node_modules/axios": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", @@ -2437,6 +2450,11 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, "axios": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", diff --git a/package.json b/package.json index 41ff3b4..d32ceb3 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,12 @@ "type": "module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "nodemon index.js" + "start": "nodemon index.js" }, "author": "", "license": "ISC", "dependencies": { + "atob": "^2.1.2", "axios": "^1.7.4", "bcrypt": "^5.1.1", "body-parser": "^1.20.2",