+
+
+
+ SETTINGS.ODK_INTEGRATION_SETTINGS
+
+
diff --git a/client/src/modules/stock/settings/stock-settings.js b/client/src/modules/stock/settings/stock-settings.js
index 889f267eca..96a3107951 100644
--- a/client/src/modules/stock/settings/stock-settings.js
+++ b/client/src/modules/stock/settings/stock-settings.js
@@ -15,7 +15,7 @@ function StockSettingsController(
const vm = this;
vm.enterprise = {};
- vm.settings = {};
+ vm.settings = { odk : {} };
let $touched = false;
diff --git a/package.json b/package.json
index 12c7d94588..722047e397 100644
--- a/package.json
+++ b/package.json
@@ -80,6 +80,7 @@
],
"dependencies": {
"@ima-worldhealth/coral": "^2.9.0",
+ "@ima-worldhealth/odk-central-api": "^1.2.1",
"@ima-worldhealth/tree": "^2.5.1",
"@types/angular": "^1.8.4",
"@uirouter/angularjs": "^1.0.29",
diff --git a/server/controllers/stock/setting/index.js b/server/controllers/stock/setting/index.js
index c796b5fbed..5391dfcd04 100644
--- a/server/controllers/stock/setting/index.js
+++ b/server/controllers/stock/setting/index.js
@@ -12,12 +12,8 @@ const NotFound = require('../../../lib/errors/NotFound');
// Get the current stock settings for the Enterprise
// If req.query.enterprise_id is set, it will use that,
// otherwise it will look up the entry for Enterprise.id=1
-exports.list = function list(req, res, next) {
- let enterpriseId = req.session.enterprise.id;
- if (req.params.id) {
- // If the enterprise was passed in as a parameter, use it
- enterpriseId = req.params.id;
- }
+exports.list = async function list(req, res, next) {
+ const enterpriseId = req.params.id || req.session.enterprise.id;
const sql = `
SELECT month_average_consumption, default_min_months_security_stock,
@@ -30,39 +26,59 @@ exports.list = function list(req, res, next) {
WHERE enterprise_id = ? LIMIT 1;
`;
- db.exec(sql, [enterpriseId])
- .then(rows => {
- if (rows.length === 1) {
- res.status(200).json(rows);
- } else {
- throw new NotFound(`Could not find stock_setting data with enterprise id ${req.params.id} (get)`);
- }
-
- })
- .catch(next)
- .done();
+ try {
+ const rows = await db.exec(sql, [enterpriseId]);
+
+ if (rows.length !== 1) {
+ throw new NotFound(`Could not find stock_setting data with enterprise id ${req.params.id} (get)`);
+ }
+
+ const [settings] = rows;
+
+ const odkSettings = await db.exec('SELECT * FROM odk_central_integration WHERE enterprise_id = ?;', [enterpriseId]);
+
+ // merge in the settings if they are defined
+ if (odkSettings.length > 0) {
+ settings.odk = odkSettings.pop();
+ }
+
+ res.status(200).json([settings]);
+ } catch (e) {
+ next(e);
+ }
};
// PUT /stock/setting/:id
//
// Update the settings in stock_settings for the settings
// with enterprise_id given by the 'id' parameter
-exports.update = function update(req, res, next) {
+exports.update = async function update(req, res, next) {
const sql = 'UPDATE stock_setting SET ? WHERE enterprise_id = ?';
const { settings } = req.body;
+ const { odk } = settings;
- db.exec(sql, [settings, req.params.id])
- .then((row) => {
- if (!row.affectedRows) {
- throw new NotFound(`Could not find stock_setting row with enterprise id ${req.params.id} (put)`);
- }
- // Get the updated values
- return db.exec('UPDATE stock_setting SET ? WHERE enterprise_id = ?',
- [settings, req.params.id]);
- })
- .then((updatedSettings) => {
- res.status(200).json(updatedSettings);
- })
- .catch(next)
- .done();
+ delete settings.odk;
+
+ try {
+
+ const { affectedRows } = await db.exec(sql, [settings, req.params.id]);
+
+ if (!affectedRows) {
+ throw new NotFound(`Could not find stock_setting row with enterprise id ${req.params.id} (put)`);
+ }
+
+ const updatedSettings = await db.exec(
+ 'UPDATE stock_setting SET ? WHERE enterprise_id = ?',
+ [settings, req.params.id]);
+
+ // update the ODK settings
+ if (odk) {
+ await db.exec('DELETE FROM odk_central_integration WHERE enterprise_id = ?', [req.params.id]);
+ await db.exec('INSERT INTO odk_central_integration SET ?;', [{ ...odk, enterprise_id : req.params.id }]);
+ }
+
+ res.status(200).json(updatedSettings);
+ } catch (e) {
+ next(e);
+ }
};
diff --git a/server/models/migrations/next/migrate.sql b/server/models/migrations/next/migrate.sql
index e69de29bb2..60afbed18d 100644
--- a/server/models/migrations/next/migrate.sql
+++ b/server/models/migrations/next/migrate.sql
@@ -0,0 +1,15 @@
+/* migration file for next release */
+
+ALTER TABLE `enterprise_setting` ADD COLUMN `enable_odk_central_integration` BOOLEAN NOT NULL DEFAULT FALSE;
+
+
+DROP TABLE IF EXISTS `odk_central_integration`;
+CREATE TABLE `odk_central_integration` (
+ `enterprise_id` SMALLINT(5) UNSIGNED NOT NULL,
+ `odk_central_url` TEXT NOT NULL,
+ `odk_admin_user` TEXT NOT NULL,
+ `odk_admin_password` TEXT NOT NULL,
+ `odk_project_id` INTEGER UNSIGNED NULL,
+ KEY `enterprise_id` (`enterprise_id`),
+ CONSTRAINT `odk_central__enterprise` FOREIGN KEY (`enterprise_id`) REFERENCES `enterprise` (`id`)
+) ENGINE=InnoDB DEFAULT CHARACTER SET = utf8mb4 DEFAULT COLLATE = utf8mb4_unicode_ci;
diff --git a/server/models/schema.sql b/server/models/schema.sql
index 2a4dc6dd9b..68e46b9f0a 100644
--- a/server/models/schema.sql
+++ b/server/models/schema.sql
@@ -592,6 +592,7 @@ CREATE TABLE `enterprise_setting` (
`base_index_growth_rate` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0,
`posting_payroll_cost_center_mode` VARCHAR(100) NOT NULL DEFAULT 'default', -- With this function, transactions related to employee payment are done in bulk and require that each expense account be linked to a cost center
`enable_require_cost_center_for_posting` TINYINT(1) NOT NULL DEFAULT 0,
+ `enable_odk_central_integration` BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY (`enterprise_id`),
CONSTRAINT `enterprise_setting__enterprise` FOREIGN KEY (`enterprise_id`) REFERENCES `enterprise` (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET = utf8mb4 DEFAULT COLLATE = utf8mb4_unicode_ci;
@@ -2633,4 +2634,15 @@ CREATE TABLE `cost_center_aggregate` (
CONSTRAINT `cost_center_aggregate__cost_center_id` FOREIGN KEY (`cost_center_id`) REFERENCES `cost_center` (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET = utf8mb4 DEFAULT COLLATE = utf8mb4_unicode_ci;
+DROP TABLE IF EXISTS `odk_central_integration`;
+CREATE TABLE `odk_central_integration` (
+ `enterprise_id` SMALLINT(5) UNSIGNED NOT NULL,
+ `odk_central_url` TEXT NOT NULL,
+ `odk_admin_user` TEXT NOT NULL,
+ `odk_admin_password` TEXT NOT NULL,
+ `odk_project_id` INTEGER UNSIGNED NULL,
+ KEY `enterprise_id` (`enterprise_id`),
+ CONSTRAINT `odk_central__enterprise` FOREIGN KEY (`enterprise_id`) REFERENCES `enterprise` (`id`)
+) ENGINE=InnoDB DEFAULT CHARACTER SET = utf8mb4 DEFAULT COLLATE = utf8mb4_unicode_ci;
+
SET foreign_key_checks = 1;
diff --git a/yarn.lock b/yarn.lock
index 006f9e23a2..b23494dc57 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -81,6 +81,15 @@
puppeteer "^13.0.0"
puppeteer-cluster "^0.22.0"
+"@ima-worldhealth/odk-central-api@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.npmjs.org/@ima-worldhealth/odk-central-api/-/odk-central-api-1.2.1.tgz#2f98746349e0db0bc238557de5991fcf3db7efa7"
+ integrity sha512-6NGlFqGuyOXdc1/g5DvgxvYIuDgfU6tzNONGyjiswK95aCYEO/z0ZndVd1KJpzDrUxz3VjNteYuBXkdfKk1bLg==
+ dependencies:
+ debug "^4.3.3"
+ dotenv "^14.1.0"
+ got "^12.0.0"
+
"@ima-worldhealth/rewire@^4.1.0":
version "4.1.0"
resolved "https://registry.npmjs.org/@ima-worldhealth/rewire/-/rewire-4.1.0.tgz#6c50920a0e305a8c7d270b153710d0538b9f4db1"
@@ -227,6 +236,11 @@
resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz#667bfc6186ae7c9e0b45a08960c551437176e1ca"
integrity sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==
+"@sindresorhus/is@^4.2.0":
+ version "4.3.0"
+ resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.3.0.tgz#344fd9bf808a84567ba563d00cc54b2f428dbab1"
+ integrity sha512-wwOvh0eO3PiTEivGJWiZ+b946SlMSb4pe+y+Ur/4S87cwo09pYi+FWHHnbrM3W9W7cBYKDqQXcrFYjYUCOJUEQ==
+
"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.3":
version "1.8.3"
resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
@@ -276,6 +290,13 @@
dependencies:
defer-to-connect "^2.0.0"
+"@szmarczak/http-timer@^5.0.1":
+ version "5.0.1"
+ resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a"
+ integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==
+ dependencies:
+ defer-to-connect "^2.0.1"
+
"@trysound/sax@0.2.0":
version "0.2.0"
resolved "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
@@ -286,7 +307,7 @@
resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.8.4.tgz#a2cc163e508389c51d4c4119ebff6b9395cec472"
integrity sha512-wPS/ncJWhyxJsndsW1B6Ta8D4mi97x1yItSu+rkLDytU3oRIh2CFAjMuJceYwFAh9+DIohndWM0QBA9OU2Hv0g==
-"@types/cacheable-request@^6.0.1":
+"@types/cacheable-request@^6.0.1", "@types/cacheable-request@^6.0.2":
version "6.0.2"
resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9"
integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==
@@ -1207,6 +1228,11 @@ cacheable-lookup@^5.0.3:
resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005"
integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==
+cacheable-lookup@^6.0.4:
+ version "6.0.4"
+ resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.0.4.tgz#65c0e51721bb7f9f2cb513aed6da4a1b93ad7dc8"
+ integrity sha512-mbcDEZCkv2CZF4G01kr8eBd/5agkt9oCqz75tJMSIsquvRZ2sL6Hi5zGVKi/0OSC9oO1GHfJ2AV0ZIOY9vye0A==
+
cacheable-request@^6.0.0:
version "6.1.0"
resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912"
@@ -2316,7 +2342,7 @@ defer-to-connect@^1.0.1:
resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591"
integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==
-defer-to-connect@^2.0.0:
+defer-to-connect@^2.0.0, defer-to-connect@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587"
integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==
@@ -2564,6 +2590,11 @@ dotenv@^14.0.0:
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-14.0.0.tgz#a993719c572eb39066922fa2031f0bc40ad88311"
integrity sha512-lO8c74ruYOG0hCK6fWT7t/PVxqcLKhfOqTahXZQN2UCyfN+ZTJmu48wRUVpkBAXsUGjPOG+ndTh8rjZxyf2xqw==
+dotenv@^14.1.0:
+ version "14.1.0"
+ resolved "https://registry.npmjs.org/dotenv/-/dotenv-14.1.0.tgz#66e9c0c448501b006f4dd9f08080c9011c7a5e6c"
+ integrity sha512-h8V+Yfa8m0YSjf3Rgbno51cxWldb4PEixIJVL55VmW7uAfeLQKiaPrEUiBps+ARK9MeqjJgTf269OMmu6lOODQ==
+
dotgitignore@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/dotgitignore/-/dotgitignore-2.1.0.tgz#a4b15a4e4ef3cf383598aaf1dfa4a04bcc089b7b"
@@ -3534,6 +3565,11 @@ fork-stream@^0.0.4:
resolved "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz#db849fce77f6708a5f8f386ae533a0907b54ae70"
integrity sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA=
+form-data-encoder@1.7.1:
+ version "1.7.1"
+ resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz#ac80660e4f87ee0d3d3c3638b7da8278ddb8ec96"
+ integrity sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==
+
form-data@4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
@@ -3697,7 +3733,7 @@ get-stream@^5.0.0, get-stream@^5.1.0:
dependencies:
pump "^3.0.0"
-get-stream@^6.0.0:
+get-stream@^6.0.0, get-stream@^6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
@@ -3935,6 +3971,25 @@ got@11.8.3:
p-cancelable "^2.0.0"
responselike "^2.0.0"
+got@^12.0.0:
+ version "12.0.1"
+ resolved "https://registry.npmjs.org/got/-/got-12.0.1.tgz#78747f1c5bc7069bbd739636ed8b70c7f2140a39"
+ integrity sha512-1Zhoh+lDej3t7Ks1BP/Jufn+rNqdiHQgUOcTxHzg2Dao1LQfp5S4Iq0T3iBxN4Zdo7QqCJL+WJUNzDX6rCP2Ew==
+ dependencies:
+ "@sindresorhus/is" "^4.2.0"
+ "@szmarczak/http-timer" "^5.0.1"
+ "@types/cacheable-request" "^6.0.2"
+ "@types/responselike" "^1.0.0"
+ cacheable-lookup "^6.0.4"
+ cacheable-request "^7.0.2"
+ decompress-response "^6.0.0"
+ form-data-encoder "1.7.1"
+ get-stream "^6.0.1"
+ http2-wrapper "^2.1.9"
+ lowercase-keys "^3.0.0"
+ p-cancelable "^3.0.0"
+ responselike "^2.0.0"
+
got@^9.6.0:
version "9.6.0"
resolved "https://registry.npmjs.org/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
@@ -4327,6 +4382,14 @@ http2-wrapper@^1.0.0-beta.5.2:
quick-lru "^5.1.1"
resolve-alpn "^1.0.0"
+http2-wrapper@^2.1.9:
+ version "2.1.10"
+ resolved "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.1.10.tgz#307cd0cee2564723692ad34c2d570d12f10e83be"
+ integrity sha512-QHgsdYkieKp+6JbXP25P+tepqiHYd+FVnDwXpxi/BlUcoIB0nsmTOymTNvETuTO+pDuwcSklPE72VR3DqV+Haw==
+ dependencies:
+ quick-lru "^5.1.1"
+ resolve-alpn "^1.2.0"
+
https-proxy-agent@5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2"
@@ -5543,6 +5606,11 @@ lowercase-keys@^2.0.0:
resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
+lowercase-keys@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2"
+ integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==
+
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
@@ -6326,6 +6394,11 @@ p-cancelable@^2.0.0:
resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf"
integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==
+p-cancelable@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050"
+ integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==
+
p-limit@^1.1.0:
version "1.3.0"
resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
@@ -7447,7 +7520,7 @@ requires-port@^1.0.0:
resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
-resolve-alpn@^1.0.0:
+resolve-alpn@^1.0.0, resolve-alpn@^1.2.0:
version "1.2.1"
resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9"
integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==