Skip to content

Commit

Permalink
feat(bhCurrencySelect): currency selection radios
Browse files Browse the repository at this point in the history
This commit introduces the bhCurrencySelect radio component to select
currencies in forms.  The basic signature looks like this:
```html
 <!-- simple usage -->
 <bh-currency-select
   currency-id="ParentCtrl.model.currencyId"
   validation-trigger="ParentForm.$submitted"
   >
 </bh-currency-select>

 <!--
   complex usage: disable currencies based on ids found in the
   disable-ids array and register an ngChange event.
 -->
 <bh-currency-select
   currency-id="ParentCtrl.model.currencyId"
   on-change="ParentCtrl.currencyChangeEvent()"
   disable-ids="ParentCtrl.disabledIds"
   validation-trigger="ParentForm.$submitted"
   >
 </bh-currency-select>
```
See the source code for implementation details.  The list of supported
bindings follows:
 1. [currency-id] - the model value for the underlying `<input>`s.  This
 is two-way bound to the parent controller.
 2. [validation-trigger] (_optional_) - a boolean that can be passed in
 to show validation messages will only show if this boolean is true.  It
 is useful to bind `ParentForm.$submitted` value to this attribute.
 3. [on-change] (_optional_) - a callback bound the `ng-change` event on
 the `<input>`s.
 4. [disable-ids] (_optional_) - an array of currency ids to be disabled
 as required.

 An implementation of the most complex case is given in the cash module.

 Partially addresses #106.
  • Loading branch information
jniles committed May 23, 2016
1 parent acce2df commit a421129
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 16 deletions.
98 changes: 98 additions & 0 deletions client/src/js/components/bhCurrencySelect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
angular.module('bhima.components')
.component('bhCurrencySelect', {
controller : bhCurrencySelect,
templateUrl : 'partials/templates/bhCurrencySelect.tmpl.html',
bindings : {
validationTrigger : '<',
currencyId : '=',
disableIds : '<',
onChange : '&'
}
});

bhCurrencySelect.$inject = [ '$scope', 'CurrencyService' ];

/**
* Currency Selection Component
*
* This is a radio button currency selection component for choosing currencies
* in a form. If a list of currencies are passed in, these are used instead of
* the application's currencies.
*
* @module components/bhCurrencySelect
*
* @example
* <!-- simple usage -->
* <bh-currency-select
* currency-id="ParentCtrl.model.currencyId"
* validation-trigger="ParentForm.$submitted"
* >
* </bh-currency-select>
*
* <!--
* complex usage: filter the currencies and register an onChange event
* -->
* <bh-currency-select
* currency-id="ParentCtrl.model.currencyId"
* on-change="ParentCtrl.currencyChangeEvent()"
* disable-ids="ParentCtrl.disabledIds"
* validation-trigger="ParentForm.$submitted"
* >
* </bh-currency-select>
*
* BINDINGS
* - [currency-id]
* The model value for the underlying `<input>`s. This
* is two-way bound to the parent controller.
*
* - [validation-trigger]
* a boolean that can be passed in
* to show validation messages will only show if this boolean is true. It
* is useful to bind `ParentForm.$submitted` value to this attribute.
*
* - [on-change]
* a callback bound the `ng-change` event on the `<input>`s.
*
* - [disable-ids]
* an array of currency ids to be disabled as required.
*/
function bhCurrencySelect($scope, Currencies) {
var $ctrl = this;

// bind the currency service to the view
$ctrl.service = Currencies;

// default currencies to an empty list
$ctrl.currencies = [];

// default to noop() if an onChange() method was not passed in
$ctrl.onChange = $ctrl.onChange || angular.noop();

// load all the available currencies
Currencies.read()
.then(function (currencies) {

// cache a label for faster view rendering
currencies.forEach(function (currency) {
currency.label = Currencies.format(currency.id);
});

$ctrl.currencies = currencies;
});

// watch the disabledIds array for changes, and disable the ids in the the
// view based on which ids are present in it
$scope.$watch(function () {
return $ctrl.disableIds;
}, function (array) {

// ensure the array exists and has values
if (!array || !array.length) { return; }

// loop through the currencies, disabling the currencies with ids in the
// disabledIds array.
$ctrl.currencies.forEach(function (currency) {
currency.disabled = array.indexOf(currency.id) > -1;
});
});
}
10 changes: 10 additions & 0 deletions client/src/partials/cash/cash.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
</bh-date-editor>

<!-- currency selection -->
<!--
<div class="radio" ng-class="{ 'has-error' : CashVoucherForm.$submitted && CashVoucherForm.currency.$invalid }">
<p><strong class="control-label">{{ "FORM.LABELS.CURRENCY" | translate }}</strong></p>
<label ng-repeat="currency in CashCtrl.cashbox.currencies track by currency.currency_id" class="radio-inline">
Expand All @@ -83,6 +84,15 @@
</div>
</div>
-->
<bh-currency-select
currency-id="CashCtrl.payment.currency_id"
validation-trigger="CashVoucherForm.$submitted"
on-change="CashCtrl.digestExchangeRate()"
disable-ids="CashCtrl.disabledCurrencyIds"
>
</bh-currency-select>

<!-- determine if we are creating a caution or an invoice payment -->
<div class="radio" ng-class="{ 'has-error' : CashVoucherForm.$submitted && CashVoucherForm.type.$invalid }">
<p><strong class="control-label">{{ "FORM.LABELS.TYPE" | translate }}</strong></p>
Expand Down
33 changes: 22 additions & 11 deletions client/src/partials/cash/cash.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,7 @@ function CashController(Cash, Cashboxes, AppCache, Currencies, $stateParams, $lo
/** @const id of the currently select cashbox */
var cashboxId = $stateParams.id;

/** default to dots for currency symbol */
vm.currencyLabel = '...';

// bind methods
vm.currencies = Currencies;
vm.openInvoicesModal = openInvoicesModal;
vm.openTransferModal = openTransferModal;
vm.openSelectCashboxModal = openSelectCashboxModal;
Expand All @@ -51,6 +47,7 @@ function CashController(Cash, Cashboxes, AppCache, Currencies, $stateParams, $lo
vm.toggleVoucherType = toggleVoucherType;
vm.submit = submit;


// temporary error handler until an application-wide method is described
function handler(error) {

Expand Down Expand Up @@ -153,8 +150,27 @@ function CashController(Cash, Cashboxes, AppCache, Currencies, $stateParams, $lo
vm.timestamp = new Date();
vm.enterprise = Session.enterprise;

// load currencies for later templating
Currencies.read();
Currencies.read()
.then(function (currencies) {
vm.currencies = currencies;
return Cashboxes.read(cashboxId);
})
.then(function (cashbox) {
vm.cashbox = cashbox;
cache.cashbox = cashbox;

// collect cashbox ids in an array
var cashboxCurrencyIds = cashbox.currencies.reduce(function (array, currency) {
return array.concat(currency.currency_id);
}, []);

// find all ids that are not cashbox ids, to disable them
vm.disabledCurrencyIds = vm.currencies.reduce(function (array, currency) {
var bool = (cashboxCurrencyIds.indexOf(currency.id) === -1);
return array.concat(bool ? currency.id : []);
}, []);
})
.catch(handler);

// make sure we have exchange
Exchange.read();
Expand Down Expand Up @@ -217,11 +233,6 @@ function CashController(Cash, Cashboxes, AppCache, Currencies, $stateParams, $lo
// exchanges the payment at the bottom of the previous invoice slip.
function digestExchangeRate() {

// this is purely for UI considerations. We want to update the currency
// input's symbol when the currency changes (prompting a
// digestExchangeRate()) call.
vm.currencyLabel = Currencies.symbol(vm.payment.currency_id);

// make sure we have all the required data before attempting to exchange
// any values
if (!vm.slip || !vm.payment.currency_id) { return; }
Expand Down
26 changes: 26 additions & 0 deletions client/src/partials/templates/bhCurrencySelect.tmpl.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<ng-form name="CurrencyForm">
<div class="radio" ng-class="{ 'has-error' : $ctrl.validationTrigger && CurrencyForm.$invalid }" data-bh-currency-select>
<p class="control-label">
<strong>{{ "COLUMNS.CURRENCY" | translate }}</strong>
</p>

<!-- repeat each currency in the application -->
<label ng-repeat="currency in $ctrl.currencies track by currency.id" class="radio-inline" ng-class="{ 'disabled' : currency.disabled }">
<input
name="currency"
type="radio"
ng-model="$ctrl.currencyId"
ng-value="currency.id"
ng-change="$ctrl.onChange"
data-currency-option="{{ currency.id }}"
ng-disabled="currency.disabled"
required>
{{ currency.label }}
</label>

<!-- radio button validation messages -->
<div class="help-block" ng-messages="CurrencyForm.currency.$error" ng-show="$ctrl.validationTrigger">
<div ng-messages-include="partials/templates/messages.tmpl.html"></div>
</div>
</div>
</ng-form>
8 changes: 3 additions & 5 deletions client/test/e2e/cash/cash.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,8 @@ describe('Cash Payments Module', function () {
var cautionOption = element(by.css('[data-caution-option="1"]'));
cautionOption.click();

// select the FC currency from the currency
var FC = element(by.css('[data-currency-option="1"]'));
FC.click();
// select the FC currency from the currency select
components.currencySelect.set(1);

// enter the amount to pay for a caution
components.currencyInput.set(mockCautionPayment.amount, null);
Expand Down Expand Up @@ -202,8 +201,7 @@ describe('Cash Payments Module', function () {
modalSubmit.click();

// select the USD currency from the currency radio buttons
var USD = element(by.css('[data-currency-option="2"]'));
USD.click();
components.currencySelect.set(2);

// enter the amount to pay for an invoice
components.currencyInput.set(mockInvoicesPayment.amount, null);
Expand Down

0 comments on commit a421129

Please sign in to comment.