From d936e221922db76f82cbd67c3678308c0d8177ce Mon Sep 17 00:00:00 2001 From: Bruce Mbayo Date: Mon, 27 Jun 2016 09:54:33 -0700 Subject: [PATCH 01/11] Transfer type for simple voucher --- bower.json | 3 +- client/src/i18n/en.json | 15 ++++++- client/src/i18n/fr.json | 15 ++++++- client/src/js/app.js | 18 ++++---- client/src/partials/vouchers/simple.html | 52 +++++++++++++++++++++++- client/src/partials/vouchers/simple.js | 47 +++++++++++++++++++-- 6 files changed, 134 insertions(+), 16 deletions(-) diff --git a/bower.json b/bower.json index bd2d20a721..11b52a75cd 100644 --- a/bower.json +++ b/bower.json @@ -35,7 +35,8 @@ "ngstorage": "^0.3.10", "Chart.js": "^2.1.6", "angular-chart.js": "~1.0.0-alpha6", - "components-font-awesome": "^4.6.3" + "components-font-awesome": "^4.6.3", + "angular-ui-select": "^0.18.0" }, "devDependencies": { "angular-mocks": "^1.5.6" diff --git a/client/src/i18n/en.json b/client/src/i18n/en.json index 53e3ea9a73..0f18c2597c 100644 --- a/client/src/i18n/en.json +++ b/client/src/i18n/en.json @@ -476,6 +476,7 @@ "TO_ACCOUNT" : "To Account", "TRANSACTION" : "Transaction", "TRANSFER_ACCOUNT" : "Transfer Account", + "TRANSFER_TYPE" : "Transfer Type", "TYPE" : "Type", "UNCONFIGURED" : "Unconfigured", "UPDATE" : "UPDATE", @@ -556,6 +557,7 @@ "SELECT_SECTOR" : "Select a sector", "SELECT_SERVICE" : "Select a Service", "SELECT_SEX" : "Select a sex", + "SELECT_TRANSFER_TYPE" : "Select a transfer type", "SELECT_USER" : "Select User", "SELECT_VILLAGE" : "Select village", "SELECT_ZS" : "Select an health zone" @@ -1137,7 +1139,18 @@ }, "VOUCHERS": { "SIMPLE": { - "TITLE" : "Simple Vouchers" + "CASH_PAYMENT" : "Client Payment", + "CASH_RETURN" : "Cash Return (Payback)", + "CONVENTION_PAYMENT" : "Convention Payment", + "EXPENSE" : "Expenses", + "INCOME" : "Incomes", + "SALARY_PAYMENT" : "Salary Payment", + "GENERIC_EXPENSE" : "Other Expenses", + "GENERIC_INCOME" : "Other Incomes", + "PURCHASES" : "Purchases", + "SUPPORT_INCOME" : "Support Incomes", + "TITLE" : "Simple Voucher", + "TRANSFER" : "Money Transfer" }, "COMPLEX": { "ADD_ITEMS" : "Add item(s)", diff --git a/client/src/i18n/fr.json b/client/src/i18n/fr.json index 1773d3c31d..ae20ac2585 100644 --- a/client/src/i18n/fr.json +++ b/client/src/i18n/fr.json @@ -475,6 +475,7 @@ "TO_ACCOUNT" : "Au Compte", "TRANSACTION" : "Transaction", "TRANSFER_ACCOUNT" : "Compte transfert", + "TRANSFER_TYPE" : "Type de Transfert", "TYPE" : "Type", "UNCONFIGURED" : "Non configuree", "UNIT" : "Forme galenique", @@ -555,6 +556,7 @@ "SELECT_SECTOR" : "Sélectionner secteur", "SELECT_SERVICE" : "Sélectionner un Service", "SELECT_SEX" : "Sélectionner le sexe", + "SELECT_TRANSFER_TYPE" : "Sélectionner le type de transfert", "SELECT_USER" : "Selectionner Utilisateur", "SELECT_VILLAGE" : "Sélectionner village", "SELECT_ZS" : "Sélectionner zone de sante" @@ -1144,7 +1146,18 @@ }, "VOUCHERS": { "SIMPLE": { - "TITLE" : "Bordereau de transfert" + "CASH_PAYMENT" : "Paiement des clients", + "CASH_RETURN" : "Remboursement", + "CONVENTION_PAYMENT" : "Paiement des Conventions", + "EXPENSE" : "Dépenses", + "INCOME" : "Récettes", + "SALARY_PAYMENT" : "Paiement des Salaires", + "GENERIC_EXPENSE" : "Autres Depenses", + "GENERIC_INCOME" : "Autres Recettes", + "PURCHASES" : "Achats", + "SUPPORT_INCOME" : "Paiement Prise en charge", + "TITLE" : "Bordereau de transfert", + "TRANSFER" : "Transfert d'argent" }, "COMPLEX": { "ADD_ITEMS" : "Ajouter item(s)", diff --git a/client/src/js/app.js b/client/src/js/app.js index f41ffc25a9..2e60d4dd56 100644 --- a/client/src/js/app.js +++ b/client/src/js/app.js @@ -358,14 +358,14 @@ function bhimaConfig($stateProvider, $urlRouterProvider, $urlMatcherFactoryProvi data : { label : null } }) - + /* Fiscal Year */ .state('fiscal', { url : '/fiscal/:id', abstract : true, params : { id : { squash : true, value : null } - }, + }, controller: 'FiscalController as FiscalCtrl', templateUrl: 'partials/fiscal/fiscal.html' }) @@ -960,7 +960,7 @@ function bhimaConfig($stateProvider, $urlRouterProvider, $urlMatcherFactoryProvi url : '/error404', templateUrl : 'partials/error404/error404.html' }); - + $urlRouterProvider.otherwise('/error404');} function translateConfig($translateProvider) { @@ -989,7 +989,7 @@ function startupConfig($rootScope, $state, SessionService, amMoment, Notify, $lo // navigating by URL. This is pure an authentication issue. $rootScope.$on('$locationChangeStart', function (event, next) { var isLoggedIn = !!SessionService.user; - var isLoginState = next.indexOf('#/login') !== -1; + var isLoginState = next.indexOf('#/login') !== -1; if (next.indexOf('/error403') !== -1) { $state.go('/error403'); @@ -1019,12 +1019,12 @@ function startupConfig($rootScope, $state, SessionService, amMoment, Notify, $lo // trigger a $state.go() to the login state, it will not be stopped - the // $locationChangeStart event will only prevent the URL from changing ... not // the actual state transition! So, we need this to stop $stateChange events. - + // var paths recovered all the path that the user is allowed to enter - // Tests if the path has elements and other common paths are not called - // if the test is positive, the current path is verified in the path list + // Tests if the path has elements and other common paths are not called + // if the test is positive, the current path is verified in the path list // if the current path does not exist in the path list in this case the user will rédirrigé to error403 page - + $rootScope.$on('$stateChangeStart', function (event, next) { var isLoggedIn = !!SessionService.user; var isLoginState = next.name.indexOf('login') !== -1; @@ -1043,7 +1043,7 @@ function startupConfig($rootScope, $state, SessionService, amMoment, Notify, $lo }); if(!authorized){ - $location.path('/error403'); + $location.path('/error403'); } } }); diff --git a/client/src/partials/vouchers/simple.html b/client/src/partials/vouchers/simple.html index 56e4e454d8..5ecdcf47ce 100644 --- a/client/src/partials/vouchers/simple.html +++ b/client/src/partials/vouchers/simple.html @@ -1,3 +1,24 @@ + @@ -24,10 +45,39 @@ > +
+ + +
+
- + @@ -100,7 +79,7 @@ + +
+ +
+
+

{{ model.enterprise.name }} ({{ model.enterprise.abbr}})

+ {{translate 'FORM.LABELS.PHONE'}}: {{ model.enterprise.phone }}
+ {{translate 'FORM.LABELS.EMAIL'}}: {{ model.enterprise.email }}
+
+
+   +
+
+ {{ model.project.name}} ({{ model.project.abbr }}) +
+
+ + +
+
+ + +

{{uppercase (translate 'VOUCHERS.TITLE') }}

+ + + + + + + + + + + + + + {{#each model.data }} + {{#if @first}} + + + + + + + + {{/if}} + {{/each}} + +
{{translate 'FORM.LABELS.REFERENCE'}}{{translate 'FORM.LABELS.DATE'}}{{translate 'FORM.LABELS.DESCRIPTION'}}{{translate 'FORM.LABELS.AMOUNT'}}{{translate 'FORM.LABELS.BY'}}
{{ reference }}{{date date }}{{ description }}{{currency amount }}{{ user }}
+
+
+ +
+ +
+ + + + + + + + + + + + {{#each model.data}} + + + + + + + {{/each}} + +
{{translate 'FORM.LABELS.ACCOUNT'}}{{translate 'FORM.LABELS.DESCRIPTION'}}{{translate 'FORM.LABELS.DEBIT'}}{{translate 'FORM.LABELS.CREDIT'}}
{{ number }}{{ label }}{{currency debit }}{{currency credit }}
+
+
+ + {{translate 'FORM.LABELS.SIGNATURE'}} +
+ +
diff --git a/server/controllers/finance/vouchers.js b/server/controllers/finance/vouchers.js index 8db351bad0..6712986f1a 100644 --- a/server/controllers/finance/vouchers.js +++ b/server/controllers/finance/vouchers.js @@ -17,6 +17,7 @@ const _ = require('lodash'); const uuid = require('node-uuid'); const util = require('../../lib/util'); const db = require('../../lib/db'); +const rm = require('../../lib/ReportManager'); const NotFound = require('../../lib/errors/NotFound'); const BadRequest = require('../../lib/errors/BadRequest'); const journal = require('./journal/voucher'); @@ -30,6 +31,9 @@ exports.detail = detail; /** Create a new voucher record */ exports.create = create; +/** Get the voucher report */ +exports.report = report; + /** * GET /vouchers * @@ -172,3 +176,35 @@ function create(req, res, next) { .catch(next) .done(); } + +/** +* GET /vouchers/report/:uuid +* +* @method report +*/ +function report(req, res, next) { + let reportUrl = './server/controllers/finance/reports/voucher.receipt.handlebars'; + let reportOptions = { pageSize : 'A5', orientation: 'landscape' }; + let sql = + `SELECT BUID(v.uuid) as uuid, v.date, v.project_id, v.currency_id, v.amount, + v.description, BUID(vi.document_uuid) as document_uuid, + v.user_id, BUID(vi.uuid) AS voucher_item_uuid, + vi.account_id, vi.debit, vi.credit, + a.number, a.label, + CONCAT(u.first, ' - ', u.last) AS user, + CONCAT(p.abbr, v.reference) AS reference + FROM voucher v + JOIN voucher_item vi ON vi.voucher_uuid = v.uuid + JOIN project p ON p.id = v.project_id + JOIN user u ON u.id = v.user_id + JOIN account a ON a.id = vi.account_id + WHERE v.uuid = ?`; + + db.exec(sql, db.bid(req.params.uuid)) + .then(rows => rm.build(rows, req, reportUrl, reportOptions)) + .spread(function (document, headers) { + res.set(headers).send(document); + }) + .catch(next) + .done(); +} diff --git a/server/lib/ReportManager.js b/server/lib/ReportManager.js new file mode 100644 index 0000000000..177fadb13e --- /dev/null +++ b/server/lib/ReportManager.js @@ -0,0 +1,70 @@ +'use strict'; + +const q = require('q'); +const path = require('path'); +const BadRequest = require('./errors/BadRequest'); +const supportedRender = { + json : require('./renderers/json'), + html : require('./renderers/html'), + pdf : require('./renderers/pdf') +}; + +const defaultRender = 'pdf'; +const defaultOptions = { pageSize : 'A4', orientation: 'landscape' }; +const contentType = { + 'pdf' : '"Content-Type" : "application/pdf"', + 'html' : '"Content-Type" : "application/html"', + 'json' : '"Content-Type" : "application/json"' +}; + +// export the receipt object +exports.build = build; + +/** + * @function build + * @desc build a report for inventory list of metadata + * @param {array} data inventory list of metadata + * @param {object} request the request which contains the renderer type (pdf, json, html, ...) + * @param {string} templateUrl the handlebars template url for the report + * @param {object} options the report page options + * @return {object} document the document blob + * @return {object} headers the document headers + * NOTE: the returned data are get by a spread(document, headers) function + * and the result is sended with res.set(headers).send(document) + */ +function build(data, req, templateUrl, options) { + 'use strict'; + + /** Requirement for report */ + let request = { + query : req.query, + enterprise : req.session.enterprise, + project : req.session.project + }; + + /* + * the template url is like this : + * './server/controllers/stock/inventory/receipts/list.handlebars' + */ + let template = path.normalize(templateUrl); + let queryString = request.query; + let renderTarget = (queryString && queryString.renderer) ? queryString.renderer : defaultRender; + let renderer = supportedRender[renderTarget]; + let pageOptions = options || defaultOptions; + + // header configurations + let headerKey = queryString.renderer || 'pdf'; + let headers = contentType[headerKey]; + + if (!renderer) { + throw new BadRequest('Render target provided is invalid or not supported by this report '.concat(renderTarget)); + } + + let model = { + enterprise : request.enterprise, + project : request.project, + data : data + }; + + return q.all([renderer.render({ model }, template, pageOptions), headers]); +} From 5b8659399419fc59d8a5fb741610ace234d6315f Mon Sep 17 00:00:00 2001 From: Bruce Mbayo Date: Tue, 28 Jun 2016 12:35:34 -0700 Subject: [PATCH 03/11] Fix and enhancement according the review --- client/src/js/services/PrototypeApiService.js | 6 +- client/src/js/services/VoucherService.js | 21 ++++--- .../templates/modals/reports.modal.html | 62 ++++++++++--------- .../templates/modals/reports.modal.js | 2 +- client/src/partials/vouchers/simple.html | 4 +- client/src/partials/vouchers/simple.js | 15 +---- client/test/e2e/shared/FormUtils.js | 4 +- client/test/e2e/vouchers/simple.spec.js | 10 ++- server/controllers/finance/vouchers.js | 40 ++++++------ server/lib/ReportManager.js | 19 +++--- 10 files changed, 98 insertions(+), 85 deletions(-) diff --git a/client/src/js/services/PrototypeApiService.js b/client/src/js/services/PrototypeApiService.js index e0d626ca9b..61b78030c9 100644 --- a/client/src/js/services/PrototypeApiService.js +++ b/client/src/js/services/PrototypeApiService.js @@ -171,14 +171,14 @@ function PrototypeApiService($http, util) { * Sends an HTTP GET request to the url "/route/reports/:id" * to get a document as a report. * - * @param {Number} id - the identifier of the URL route. + * @param {String} param - A parameter for the URL route. * @param {String} filetype - the report file type (pdf, json, html) * @returns {Promise} - the promise */ - function report(id, filetype) { + function report(param, filetype) { // append the id to the base url - var target = this.url.concat('reports/', id); + var target = this.url.concat('reports/', param); // filetype setup var responseType = filetype === 'pdf' ? 'arraybuffer' : null; diff --git a/client/src/js/services/VoucherService.js b/client/src/js/services/VoucherService.js index e101746d31..f86b5038dc 100644 --- a/client/src/js/services/VoucherService.js +++ b/client/src/js/services/VoucherService.js @@ -18,10 +18,22 @@ function VoucherService(PrototypeApiService) { // inherit prototype API service methods angular.extend(service, PrototypeApiService); + // transfer type + service.transferType = [ + { id: 0, text: 'VOUCHERS.SIMPLE.GENERIC_INCOME', incomeExpense: 'income', prefix: 'REC. GEN' }, + { id: 1, text: 'VOUCHERS.SIMPLE.CASH_PAYMENT', incomeExpense: 'income', prefix: 'CASH' }, + { id: 2, text: 'VOUCHERS.SIMPLE.CONVENTION_PAYMENT', incomeExpense: 'income', prefix: 'CONV' }, + { id: 3, text: 'VOUCHERS.SIMPLE.SUPPORT_INCOME', incomeExpense: 'income', prefix: 'PEC' }, + { id: 4, text: 'VOUCHERS.SIMPLE.TRANSFER', incomeExpense: 'income', prefix: 'TRANSF' }, + { id: 5, text: 'VOUCHERS.SIMPLE.GENERIC_EXPENSE', incomeExpense: 'expense', prefix: 'DEP. GEN' }, + { id: 6, text: 'VOUCHERS.SIMPLE.SALARY_PAYMENT', incomeExpense: 'expense', prefix: 'SALAIRE' }, + { id: 7, text: 'VOUCHERS.SIMPLE.CASH_RETURN', incomeExpense: 'expense', prefix: 'PAYBACK' }, + { id: 8, text: 'VOUCHERS.SIMPLE.PURCHASES', incomeExpense: 'expense', prefix: 'ACHAT' } + ]; + service.url = '/vouchers/'; service.createSimple = createSimple; service.create = create; - service.report = report; /** * Wraps the prototype create method. @@ -30,13 +42,6 @@ function VoucherService(PrototypeApiService) { return PrototypeApiService.create.call(service, { voucher : voucher }); } - /** - * Wraps the prototype report method. - */ - function report(id, filetype) { - return PrototypeApiService.report.call(service, id, filetype); - } - /** * @method createSimple * diff --git a/client/src/partials/templates/modals/reports.modal.html b/client/src/partials/templates/modals/reports.modal.html index 2b262c2f9e..445fff9d3f 100644 --- a/client/src/partials/templates/modals/reports.modal.html +++ b/client/src/partials/templates/modals/reports.modal.html @@ -1,35 +1,39 @@ -