Skip to content

Commit

Permalink
feat: inital billing services implementation
Browse files Browse the repository at this point in the history
This implements the intial structure of the billing services page.  As
of this commit, billing service creation works with proper form
validation.  A skeleton UI grid can also be found on the billing
services page.

The struture of billing services looks like this:
 - billing_services/
   - billing_services.html # the abstract state template with title bar)
   - billing_services.list.html # the default grid for the page
   - billing_service.js  # the BillingServicesController
   - create/
     - create.html  # the creation form
     - create.js    # the BillingServicesCreateController
   - update/
     - update.html  # the update form
     - update.js    # the BillingServicesUpdateController

The routes should look like this:
 - `/billing_services` - lists all billing services in a UI grid
 - `/billing_services/create` - brings up a creation form
 - `/billing_services/:id` - brings up an update form

 TODOs
  - implement the `?detailed=1` query string on the server +
    integration tests
  - implement the UPDATE state
  • Loading branch information
jniles committed Mar 31, 2016
1 parent 1c97e63 commit 727ff5d
Show file tree
Hide file tree
Showing 13 changed files with 433 additions and 19 deletions.
4 changes: 4 additions & 0 deletions client/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1470,6 +1470,10 @@
"MISSING" : "Please ensure that all fields are filled in.",
"DATE_OUT_OF_RANGE" : "This date is not in the allowed date range. Please select another date."
},
<<<<<<< HEAD
=======
"ERRORED" : "This form contains errors. Please correct them and resubmit.",
>>>>>>> feat: inital billing services implementation
"INVALID_DATE" : "Invalid date",
"LOGIN" : "Login",
"HAS_ERRORS" : "The form contains errors. Please correct them and resubmit.",
Expand Down
23 changes: 23 additions & 0 deletions client/src/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,29 @@ function bhimaConfig($stateProvider, $urlRouterProvider) {
templateUrl: 'partials/locations/country/country.html'
})

/* billing service */

.state('billingServices', {
url : '/admin/billing_services',
templateUrl : 'partials/billing_services/billing_services.html',
abstract : true
})
.state('billingServices.index', {
url : '', // display along with parent state
templateUrl : '/partials/billing_services/billing_services.list.html',
controller : 'BillingServicesController as BillingServicesCtrl'
})
.state('billingServices.create', {
url : '/create',
templateUrl : '/partials/billing_services/create/create.html',
controller : 'BillingServicesCreateController as BillingServicesCreateCtrl'
})
.state('billingServices.update', {
url : '/:id',
templateUrl : '/partials/billing_services/update/update.html',
controller : 'BillingServicesUpdateController as BillingServiceUpdateCtrl'
})

/* budget routes */

.state('/budgets/create', {
Expand Down
67 changes: 53 additions & 14 deletions client/src/js/services/AccountService.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,63 @@ AccountService.$inject = ['$http', 'util', 'SessionService'];
/**
* Account Service
*
* A service wrapper for the /accounts HTTP endpoint.
* A service wrapper for the /accounts HTTP endpoint.
*/
function AccountService($http, util, sessionService) {
var service = this;
var baseUrl = '/accounts/';

service.read = read;

/** @deprecated */
service.list = list;

service.getBalance = getBalance;
service.getChildren = getChildren;

service.flatten = flatten;
service.order = order;
service.create = create;
service.update = update;

/**
* The read() method loads data from the api endpoint. If an id is provided,
* the $http promise is resolved with a single JSON object, otherwise an array
* of objects should be expected.
*
* @param {Number} id - the id of the account to fetch (optional).
* @param {Object} options - options to be passed as query strings (optional).
* @return {Promise} promise - resolves to either a JSON (if id provided) or
* an array of JSONs.
*/
function read(id, options) {
var url = baseUrl.concat(id || '');
return $http.get(url, { params : options })
.then(util.unwrapHttpResponse)
.then(function (accounts) {

// label the accounts with a nice human readable label
accounts.forEach(function (account) {
account.label = label(account);
});

/** @todo make this ordering work */
// return order(accounts);
return accounts;
});
}

// return a list of accounts
function list() {
return $http.get('/accounts?full=1') // TODO - this should only be /accounts
.then(util.unwrapHttpResponse)
.then(function (accounts) {

// hack to make sure accounts are properly rendered on cashboxes page
// FIXME - make /accounts return the account type w/o query string
// and preformat the numberLabel elsewhere
accounts.forEach(function (account) {
account.numberLabel = account.number + ' - ' + account.label;
account.label = label(account);
});

return accounts;
Expand All @@ -38,15 +71,21 @@ function AccountService($http, util, sessionService) {
}

function getBalance(account_id, opt){
var url = baseUrl + '/' + account_id + '/balance';
var url = baseUrl + account_id + '/balance';
return $http.get(url, opt)
.then(util.unwrapHttpResponse);
}

/** @helper
*This method builds a tree data structure of
*accounts and children of a specified parentId.
**/
function label(account) {
return account.account_number + ' - ' + account.account_txt;
}

/**
* This method builds a tree data structure of accounts and children of a
* specified parentId.
*
* @method
*/
function getChildren(accounts, parentId) {
var children;

Expand All @@ -69,9 +108,9 @@ function AccountService($http, util, sessionService) {
}

/**
*@helper
*flattens a tree data structure (must have children property) in place.
**/
* flattens a tree data structure (must have children property) in place.
* @method
*/
function flatten(tree) {
return tree.reduce(function (array, node) {
var items = [node].concat(node.children ? flatten(node.children) : []);
Expand All @@ -80,9 +119,9 @@ function AccountService($http, util, sessionService) {
}

/**
*creates a proper account ordering by first creating an account tree and then
* flattening in place.
**/
* creates a proper account ordering by first creating an account tree and then
* flattening in place.
*/
function order(accounts) {

// NOTE
Expand Down Expand Up @@ -154,4 +193,4 @@ function AccountService($http, util, sessionService) {
}

return service;
}
}
95 changes: 95 additions & 0 deletions client/src/js/services/BillingServicesService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
angular.module('bhima.services')
.service('BillingServicesService', BillingServicesService);

BillingServicesService.$inject = [
'$http', 'util'
];

/**
* Billing Services Service
*
* This function wraps the /billing_services API endpoint and exposes CRUD
* methods to controllers.
*
* @constructor
*/
function BillingServicesService($http, util) {
var service = this;
var url = '/billing_services/';

/* service methods */
service.read = read;
service.create = create;
service.update = update;
service.delete = del;

/* ------------------------------------------------------------------------ */

/**
* The create() method creates a new billing service in the database via a
* POST requests to the HTTP API endpoint.
*
* @param {Object} data - billing service properties to be submitted to the
* service.
* @return {Promise} promise - resolves with the id of the created billing
* service entity or is rejected with an HTTP error.
*/
function create(data) {

// copy the data not do disrupt the view
data = angular.copy(data);

// remove view-specific values
if (data.account) {
data.account_id = data.account.id;
delete data.account;
}

return $http.post(url, { billingService : data })
.then(util.unwrapHttpResponse);
}

/**
* The read() method loads data from the api endpoint. If an id is provided,
* the $http promise is resolved with a single JSON object, otherwise an array
* of objects should be expected.
*
* @param {Number} id - the id of the billing service (optional).
* @param {Object} options - options to be passed as query strings (optional).
* @return {Promise} promise - resolves to either a JSON (if id provided) or
* an array of JSONs.
*/
function read(id, options) {
var target = url.concat(id || '');
return $http.get(target, { params : options })
.then(util.unwrapHttpResponse);
}

/**
* The update() method updates a billing service in the database via a PUT
* request to the HTTP API endpoint.
*
* @param {Number} id - the id of the billing service to be modified.
* @param {Object} data - billing service properties to be updated with new
* values.
* @return {Promise} promise - resolves with the id of the created billing
* service entity or is rejected with an HTTP error.
*/
function update(id, data) {
var target = url.concat(id);
return $http.put(target, data)
.then(util.unwrapHttpResponse);
}

/**
* The delete() method deletes data from the database using the API endpoint.
*
* @param {Number} id - the id of the billing service.
* @return {Promise} promise - a promise resolving to an empty object.
*/
function del(id) {
var target = url.concat(id);
return $http.delete(target)
.then(util.unwrapHttpResponse);
}
}
19 changes: 19 additions & 0 deletions client/src/partials/billing_services/billing_services.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<div class="flex-header">
<div class="bhima-title">
<ol class="headercrumb">
<li class="static">{{ "TREE.ADMIN" | translate }}</li>
<li class="title">{{ "BILLING_SERVICES.TITLE" | translate }}</li>
</ol>
</div>
</div>

<div class="flex-util">
<button class="btn btn-default" ui-sref="billingServices.create" data-method="create">
<span class="glyphicon glyphicon-plus"></span> {{ "BILLING_SERVICES.CREATE" | translate }}
</button>
</div>

<!-- template in child views -->
<div class="flex-content">
<div class="container-fluid" ui-view></div>
</div>
66 changes: 66 additions & 0 deletions client/src/partials/billing_services/billing_services.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
angular.module('bhima.controllers')
.controller('BillingServicesController', BillingServicesController);

BillingServicesController.$inject = [
'$state', '$translate', 'BillingServicesService'
];

/**
* The Billing Services Controller
*
* This is the default controller for the billing services URL endpoint.
*/
function BillingServicesController($state, $translate, BillingServices) {
var vm = this;

// these options are for the ui-grid
vm.options = {
appScopeProvider: vm,
enableSorting : true,
enableHiding : false,
columnDefs : [
{ field : 'id', name : $translate.instant('COLUMNS.ID') },
{ field : 'account_id', name : $translate.instant('COLUMNS.ACCOUNT') },
{ field : 'label', name : $translate.instant('COLUMNS.LABEL') },
{ field : 'description', name : $translate.instant('COLUMNS.DESCRIPTION') },
{ field : 'value', name : $translate.instant('COLUMNS.VALUE') },
{ field : 'date', name : $translate.instant('COLUMNS.DATE'), cellFilter:'date' },
]
};

// default loading state - false;
vm.loading = false;

vm.update = update;

// fired on state init
function startup() {

// turn the loading indicator on
toggleLoadingIndicator();

// retrieve a detailed list of the billing services in the application
BillingServices.read(null, { detailed : 1 })
.then(function (billingServices) {
console.log('loaded:', billingServices);
vm.options.data = billingServices;
})
.finally(function () {

// turn loading indicator off after HTTP request is finished
toggleLoadingIndicator();
});
}

function toggleLoadingIndicator() {
vm.loading = !vm.loading;
}

// update the clicked billing service
function update(id) {
console.log('clicked:', id);
//$state.go('billingServices.update');
}

startup();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<div class="grid" id="BillingServicesGrid" ui-grid="BillingServicesCtrl.options">
</div>
Loading

0 comments on commit 727ff5d

Please sign in to comment.