Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically add number of employees to cost centers in step-down cost allocation reports #5977

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion client/src/i18n/en/cost_center.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"TITLE" : "Cost Centers",
"ADD_ALLOCATION_KEYS" : "Add Allocation Keys",
"ADD_COST_CENTER" : "Add a Cost Center",
"ADD_ALLOCATION_BASIS_TOOLTIP": "Add a new cost allocation basis",
"ALLOCATION_BASES":"Allocation Bases",
"ALLOCATION_BASIS": "Allocation basis",
"ALLOCATION_BASIS_TABLE": "Table of allocation bases",
Expand All @@ -19,7 +20,9 @@
"NO_COST_CENTER_DEFINED": "** Cost Center Not Defined **",
"SELECT_ALLOCATION_BASIS": "Select allocation basis",
"STEP_DOWN_COST_ALLOCATION": "Step-down cost allocation",
"ONLY_FOR_EXPLOITATION_ACCOUNTS": "Only for operating accounts (income and expenses). For others accounts, the cost center will not be considered"
"ONLY_FOR_EXPLOITATION_ACCOUNTS": "Only for operating accounts (income and expenses). For others accounts, the cost center will not be considered",
"UPDATE_ALLOCATION_QUANTITIES": "Update",
"UPDATE_ALLOCATION_QUANTITIES_TOOLTIP": "Update system-computed cost center allocation quantities"
},
"ALLOCATION_METHOD": "Allocation method",
"ALLOCATION_METHOD_FLAT": "Flat",
Expand Down
5 changes: 4 additions & 1 deletion client/src/i18n/fr/cost_center.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"TITLE" : "Centres de coût",
"ADD_ALLOCATION_KEYS" : "Ajouter les clés de répartition",
"ADD_COST_CENTER" : "Ajouter un centre de coût",
"ADD_ALLOCATION_BASIS_TOOLTIP": "Ajouter une nouvelle base d'allocation",
"ALLOCATION_BASES":"Les bases d'allocation",
"ALLOCATION_BASIS": "Base d'allocation",
"ALLOCATION_BASIS_TABLE": "Tableau des bases d'allocation'",
Expand All @@ -19,7 +20,9 @@
"NO_COST_CENTER_DEFINED": "** Aucun centre de cout **",
"SELECT_ALLOCATION_BASIS": "Sélectionnez la base d'allocation",
"STEP_DOWN_COST_ALLOCATION": "Allocation séquentielle des coûts",
"ONLY_FOR_EXPLOITATION_ACCOUNTS": "Uniquement pour les comptes d'exploitations (produits et charges). Pour les autres comptes, le centre de cout ne sera pas pris en compte"
"ONLY_FOR_EXPLOITATION_ACCOUNTS": "Uniquement pour les comptes d'exploitations (produits et charges). Pour les autres comptes, le centre de cout ne sera pas pris en compte",
"UPDATE_ALLOCATION_QUANTITIES": "Mettre à jour",
"UPDATE_ALLOCATION_QUANTITIES_TOOLTIP": "Mettre à jour les quantités d'allocation des centres de coûts calculées par le système"
},
"ALLOCATION_METHOD": "Méthode d'allocation séquentielle des coûts",
"ALLOCATION_METHOD_FLAT": "Uniform",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,19 @@

<div class="toolbar">
<div class="toolbar-item">
<button class="btn btn-default text-capitalize" ui-sref="cost_center_allocation_bases.create" data-method="create">
<button class="btn btn-default text-capitalize"
ui-sref="cost_center_allocation_bases.create"
uib-tooltip="{{'COST_CENTER.ADD_ALLOCATION_BASIS_TOOLTIP' | translate}}"
tooltip-append-to-body="true" tooltip-placement="left"
data-method="create">
<span class="fa fa-plus"></span> <span translate>FORM.LABELS.ADD</span>
</button>
<button class="btn btn-default text-capitalize"
uib-tooltip="{{'COST_CENTER.UPDATE_ALLOCATION_QUANTITIES_TOOLTIP' | translate}}"
tooltip-append-to-body="true" tooltip-placement="left"
ng-click="CostCenterAllocationBasesCtrl.updateComputableQuantities()">
<span class="fa fa-plus"></span> <span translate>COST_CENTER.UPDATE_ALLOCATION_QUANTITIES</span>
</button>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ function CostCenterAllocationBasesController(
vm.gridApi = {};
vm.editAllocationBasis = editAllocationBasis;
vm.removeAllocationBasis = removeAllocationBasis;
vm.updateComputableQuantities = updateComputableQuantities;

// options for the UI grid
vm.gridOptions = {
Expand Down Expand Up @@ -50,6 +51,18 @@ function CostCenterAllocationBasesController(
});
}

function updateComputableQuantities() {
vm.loading = true;
AllocationBasisQuantity.updateQuantities()
.then(() => {
Notify.success('FORM.INFO.SUCCESS');
$state.go('cost_center_allocation_bases', null, { reload : true });
})
.finally(() => {
vm.loading = false;
});
}

function load() {
vm.loading = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function AllocationBasisQuantityService(Api) {
service.bulkInsert = bulkInsert;
service.bulkDelete = bulkDelete;
service.bulkUpdate = bulkUpdate;
service.updateQuantities = updateQuantities;

function bulkDetails(id) {
const url = `/cost_center_allocation_basis_quantity/bulk/${id}`;
Expand All @@ -42,5 +43,11 @@ function AllocationBasisQuantityService(Api) {
.then(service.util.unwrapHttpResponse);
}

function updateQuantities() {
const url = `/cost_center_allocation_basis_quantities_update`;
return service.$http.put(url)
.then(service.util.unwrapHttpResponse);
}

return service;
}
6 changes: 6 additions & 0 deletions client/src/modules/cost_center/cost_center.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,11 @@ function CostCenterService(Api, $uibModal) {
.then(service.util.unwrapHttpResponse);
};

service.getAllocationQuantities = (options) => {
const url = `/cost_center/allocation_quantities`;
return service.$http.get(url, { params : options })
.then(service.util.unwrapHttpResponse);
};

return service;
}
9 changes: 9 additions & 0 deletions server/config/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ module.exports = {
EXPENSE : 5,
TITLE : 6,
},
allocationBasis : {
/* NOTE: Must match values in bhima.sql */
ALLOCATION_BASIS_DIRECT_COST : 1,
ALLOCATION_BASIS_NUM_EMPLOYEES : 2,
ALLOCATION_BASIS_AREA_USED : 3,
ALLOCATION_BASIS_ELECTRICITY_CONSUMED : 4,
ALLOCATION_BASIS_NUM_COMPUTERS : 5,
ALLOCATION_BASIS_NUM_LABOR_HOURS : 6,
},
settings : {
CONTACT_EMAIL : 'developers@imaworldhealth.org',
},
Expand Down
1 change: 1 addition & 0 deletions server/config/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,7 @@ exports.configure = function configure(app) {
app.post('/cost_center_allocation_basis_quantity', costAllocationBasisQuantity.create);
app.put('/cost_center_allocation_basis_quantity/:id', costAllocationBasisQuantity.update);
app.delete('/cost_center_allocation_basis_quantity/:id', costAllocationBasisQuantity.delete);
app.put('/cost_center_allocation_basis_quantities_update', costAllocationBasisQuantity.updateQuantities);

// Distribution Fees Centers API
app.get('/allocation_cost_center', distributionConfiguration.configuration);
Expand Down
95 changes: 93 additions & 2 deletions server/controllers/finance/stepDownCostAllocationQuantity.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const db = require('../../lib/db');
const FilterParser = require('../../lib/filter');
const constants = require('../../config/constants');

module.exports = {
create,
Expand All @@ -10,6 +11,7 @@ module.exports = {
bulkCreate,
bulkDelete,
bulkUpdate,
updateQuantities,
};

// add a new allocation basis quantity
Expand All @@ -33,7 +35,7 @@ function create(req, res, next) {
//
async function bulkDetails(req, res, next) {
const sql = `
SELECT
SELECT
abv.id, abv.quantity, abv.cost_center_id, abv.basis_id,
cc.label AS cost_center_label, ab.name AS allocation_basis_label, ab.units AS allocation_basis_units
FROM cost_center_allocation_basis_value abv
Expand Down Expand Up @@ -117,7 +119,7 @@ function bulkDelete(req, res, next) {
//
function list(req, res, next) {
const sql = `
SELECT
SELECT
abv.id, abv.quantity, abv.cost_center_id, abv.basis_id,
cc.label AS cost_center_label, ab.name AS allocation_basis_label, ab.units AS allocation_basis_units
FROM cost_center_allocation_basis_value abv
Expand Down Expand Up @@ -166,3 +168,92 @@ function remove(req, res, next) {
})
.catch(next);
}

// Update computable allocation basis quantities
//
// UPDATE /cost_center_allocation_basis_quantities_update
//

async function updateQuantities(req, res, next) {

try {
// Get the full list of cost center IDs
const costCentersQuery = 'SELECT id, label AS name from cost_center';
const costCenters = await db.exec(costCentersQuery);

// Get the allocation bases that are computable
const cabQuery = 'SELECT id, name FROM cost_center_allocation_basis WHERE is_computed = 1';
const computables = await db.exec(cabQuery);

// Queries to update the quantity records
const findQRec = 'SELECT id FROM `cost_center_allocation_basis_value` '
+ 'WHERE `cost_center_id` = ? AND `basis_id` = ?';
const updateQRec = 'UPDATE `cost_center_allocation_basis_value` '
+ 'SET `quantity` = ? WHERE `id` = ?';
const insertQRec = 'INSERT INTO `cost_center_allocation_basis_value` '
+ '(`cost_center_id`, `basis_id`, `quantity`) VALUES (?, ?, ?)';

// Compute and set each computable allocation basis quantity
const unUpdatedCenters = [];

/* eslint-disable no-await-in-loop */
for (let i = 0; i < computables.length; i++) {
const basis = computables[i];
const data = await allocationQuantities(basis.id);

// Update quantities for each cost center
for (let k = 0; k < costCenters.length; k++) {
const cc = costCenters[k];

// Get the data for this cost center (if any)
const ccData = data.find(item => item.cost_center_id === cc.id);
if (ccData) {
const newQuantity = ccData[basis.name];
// See if there is an existing quantity
const qRecordId = await db.exec(findQRec, [cc.id, basis.id]);
if (qRecordId.length > 0) {
// console.log("Updating: ", newQuantity, qRecordId[0].id);
await db.exec(updateQRec, [newQuantity, qRecordId[0].id]);
} else {
// console.log("Inserting: ", cc.id, basis.id, newQuantity);
await db.exec(insertQRec, [cc.id, basis.id, newQuantity]);
}
} else {
// TODO: Do we want to zero out the others here?
unUpdatedCenters.push({ cc });
}
}
}

// TODO: Do something with upUpdateCenters to inform the user
// console.log("Centers not updated: ", unUpdatedCenters);
res.sendStatus(200);
} catch (e) {
next(e);
}
}

/**
* Get cost allocation basis quantities
*
* This function returns a set of values for the specified
* allocation_basis_id for each cost center.
*
* @param {Number} allocation_basis_id
*/
function allocationQuantities(allocationBasisId) {
let query = null;
if (allocationBasisId === constants.allocationBasis.ALLOCATION_BASIS_NUM_EMPLOYEES) {
// Set up the query for number of employees
query = `
SELECT BUID(service.uuid) AS service_uuid, service.name AS service_name,
COUNT(employee.uuid) AS ALLOCATION_BASIS_NUM_EMPLOYEES,
GetCostCenterByServiceUuid(service.uuid) as cost_center_id
FROM service JOIN employee ON service.uuid = employee.service_uuid
GROUP BY service.uuid;
`;
} else {
return [];
}
return db.exec(query);
}
19 changes: 9 additions & 10 deletions server/models/bhima.sql
Original file line number Diff line number Diff line change
Expand Up @@ -486,21 +486,20 @@ INSERT INTO `analysis_tool_type` (`label`, `is_balance_sheet`, `rank`) VALUES
('FORM.LABELS.ANALYSIS_TOOLS.DEBTS', 1, 1);

-- Cost Center basis info
-- NOTE: 'id' field must match values assigned to the corresponding allocation
-- basis items in server/config/constants.js (search for allocationBasis)
INSERT INTO `cost_center_allocation_basis`
(`id`, `name`, `units`, `description`, `is_predefined`, `is_currency`, `decimal_places`)
(`id`, `name`, `units`, `description`, `is_predefined`, `is_currency`, `decimal_places`, `is_computed`)
VALUES
(1, 'ALLOCATION_BASIS_DIRECT_COST', '',
'ALLOCATION_BASIS_DIRECT_COST_DESCRIPTION', 1, 1, 2),
'ALLOCATION_BASIS_DIRECT_COST_DESCRIPTION', 1, 1, 2, 0),
(2, 'ALLOCATION_BASIS_NUM_EMPLOYEES', '',
'ALLOCATION_BASIS_NUM_EMPLOYEES_DESCRIPTION', 1, 0, 0),
'ALLOCATION_BASIS_NUM_EMPLOYEES_DESCRIPTION', 1, 0, 0, 1),
(3, 'ALLOCATION_BASIS_AREA_USED', 'ALLOCATION_BASIS_AREA_USED_UNITS',
'ALLOCATION_BASIS_AREA_USED_DESCRIPTION', 1, 0, 1),
'ALLOCATION_BASIS_AREA_USED_DESCRIPTION', 1, 0, 1, 0),
(4, 'ALLOCATION_BASIS_ELECTRICITY_CONSUMED', 'ALLOCATION_BASIS_ELECTRICITY_CONSUMED_UNITS',
'ALLOCATION_BASIS_ELECTRICITY_CONSUMED_DESCRIPTION', 1, 0, 1),
'ALLOCATION_BASIS_ELECTRICITY_CONSUMED_DESCRIPTION', 1, 0, 1, 0),
(5, 'ALLOCATION_BASIS_NUM_COMPUTERS', '',
'ALLOCATION_BASIS_NUM_COMPUTERS_DESCRIPTION', 1, 0, 0),
'ALLOCATION_BASIS_NUM_COMPUTERS_DESCRIPTION', 1, 0, 0, 0),
(6, 'ALLOCATION_BASIS_NUM_LABOR_HOURS', 'ALLOCATION_BASIS_NUM_LABOR_HOURS_UNITS',
'ALLOCATION_BASIS_NUM_LABOR_HOURS_DESCRIPTION', 1, 0, 1);



'ALLOCATION_BASIS_NUM_LABOR_HOURS_DESCRIPTION', 1, 0, 1, 0);
16 changes: 13 additions & 3 deletions server/models/migrations/next/migrate.sql
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ author: @jmcameron
date: 2021-09-15
description: Add cost basis items
*/
INSERT IGNORE INTO `cost_center_allocation_basis` VALUES
INSERT IGNORE INTO `cost_center_allocation_basis` (`id`, `name`, `units`, `description`, `is_predefined`) VALUES
(1, 'ALLOCATION_BASIS_DIRECT_COST', '', 'ALLOCATION_BASIS_DIRECT_COST_DESCRIPTION', 1),
(2, 'ALLOCATION_BASIS_NUM_EMPLOYEES', '', 'ALLOCATION_BASIS_NUM_EMPLOYEES_DESCRIPTION', 1),
(3, 'ALLOCATION_BASIS_AREA_USED', 'm²', 'ALLOCATION_BASIS_AREA_USED_DESCRIPTION', 1),
Expand Down Expand Up @@ -205,7 +205,7 @@ UPDATE `unit` SET `path` = '/cost_center/allocation_bases' WHERE id = 299;
INSERT IGNORE INTO `report` (`report_key`, `title_key`) VALUES
('cost_center_step_down', 'TREE.COST_CENTER_STEPDOWN');


/*
* @author: jmcameron
* @date: 2021-09-29
Expand All @@ -218,14 +218,24 @@ ALTER TABLE `cost_center_allocation_basis` MODIFY COLUMN `units` VARCHAR(200) DE
UPDATE `cost_center_allocation_basis` SET `decimal_places` = 2, `is_currency` = 1 WHERE id = 1;
UPDATE `cost_center_allocation_basis` SET `decimal_places` = 1, `units` = 'ALLOCATION_BASIS_AREA_USED_UNITS' WHERE id = 3;
UPDATE `cost_center_allocation_basis` SET `decimal_places` = 1, `units` = 'ALLOCATION_BASIS_ELECTRICITY_CONSUMED_UNITS' WHERE id = 4;
UPDATE `cost_center_allocation_basis` SET `decimal_places` = 1, `units` = 'ALLOCATION_BASIS_NUM_LABOR_HOURS_UNITS', WHERE id = 6;
UPDATE `cost_center_allocation_basis` SET `decimal_places` = 1, `units` = 'ALLOCATION_BASIS_NUM_LABOR_HOURS_UNITS' WHERE id = 6;

UPDATE `unit` SET `key` = 'TREE.DISTRIBUTION_KEYS_MANAGEMENT' WHERE id = 223;
UPDATE `unit` SET `name` = 'Allocation Bases', `key` = 'TREE.COST_CENTER_ALLOCATION_KEYS', `description` = 'List cost center allocation bases with values' WHERE `id` = 299;


/*
* @author: mbayopanda
* @desc: cost_center_id column in the account table
*/
CALL add_column_if_missing('account', 'cost_center_id', 'MEDIUMINT(8) UNSIGNED NULL');
CALL add_constraint_if_missing('account', 'account__cost_center', 'FOREIGN KEY (`cost_center_id`) REFERENCES `cost_center` (`id`)');


/*
* @author: jmcameron
* @date: 2021-10-01
* @desc: auto generation of number of employees
*/
CALL add_column_if_missing('cost_center_allocation_basis', 'is_computed', 'BOOLEAN NOT NULL DEFAULT 0 AFTER `decimal_places`');
UPDATE `cost_center_allocation_basis` SET `is_computed` = 1 WHERE id = 2;
2 changes: 1 addition & 1 deletion server/models/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -983,7 +983,6 @@ CREATE TABLE `price_list_item` (

-- TODO write schema change (transactions) INTo SQL update script
DROP TABLE IF EXISTS `patient`;

CREATE TABLE `patient` (
`uuid` BINARY(16) NOT NULL,
`project_id` SMALLINT(5) UNSIGNED NOT NULL,
Expand Down Expand Up @@ -2267,6 +2266,7 @@ CREATE TABLE `cost_center_allocation_basis` (
`is_predefined` BOOLEAN NOT NULL DEFAULT 0,
`is_currency` BOOLEAN NOT NULL DEFAULT 0,
`decimal_places` TINYINT(2) NOT NULL DEFAULT 0,
`is_computed` BOOLEAN NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARACTER SET = utf8mb4 DEFAULT COLLATE = utf8mb4_unicode_ci;
Expand Down