'; //dialog div
- //r+= '
'; //reset button and its div
- r += divRowDef;
-
- for (j = 0; j < iLen; j++) {
-
- //if last check close div
- if (j % numRow == 0 && j != 0) {
- r += divClose + divRowDef;
- }
-
- var sLabel = aData[j];
- var sValue = aData[j];
-
- if (typeof (aData[j]) == 'object') {
- sLabel = aData[j].label;
- sValue = aData[j].value;
- }
-
- //check button
- r += '
' + sLabel + '
';
-
- var checkbox = $(r);
- th.html(checkbox);
- th.wrapInner('
');
- //on every checkbox selection
- checkbox.change(function () {
-
- var search = '';
- var or = '|'; //var for select checks in 'or' into the regex
- var resSize = $('input:checkbox[name="' + localLabel + '"]:checked').size();
- $('input:checkbox[name="' + localLabel + '"]:checked').each(function (index) {
-
- //search = search + ' ' + $(this).val();
- //concatenation for selected checks in or
- if ((index == 0 && resSize == 1)
- || (index != 0 && index == resSize - 1)) {
- or = '';
- }
- //trim
- search = search.replace(/^\s+|\s+$/g, "");
- search = search + $(this).val() + or;
- or = '|';
-
- });
-
-
- if (search != "") {
- $('input:checkbox[name="' + localLabel + '"]').removeClass("search_init");
- } else {
- $('input:checkbox[name="' + localLabel + '"]').addClass("search_init");
- }
- /* Old code for setting search_init CSS class on checkboxes if any of them is checked
- for (var jj = 0; jj < iLen; jj++) {
- if (search != "") {
- $('#' + aData[jj]).removeClass("search_init");
- } else {
- $('#' + aData[jj]).addClass("search_init");
- }
- }
- */
-
- //execute search
- oTable.fnFilter(search, index, true, false);
- fnOnFiltered();
- });
- }
-
- //filter button
- $('#' + buttonId).button();
- //dialog
- $('#' + checkToggleDiv).dialog({
- //height: 140,
- autoOpen: false,
- //show: "blind",
- hide: "blind",
- buttons: [{
- text: "Reset",
- click: function () {
- //$('#'+buttonId).removeClass("filter_selected"); //LM remove border if filter selected
- $('input:checkbox[name="' + localLabel + '"]:checked').each(function (index3) {
- $(this).attr('checked', false);
- $(this).addClass("search_init");
- });
- oTable.fnFilter('', index, true, false);
- fnOnFiltered();
- return false;
- }
- },
- {
- text: "Close",
- click: function () { $(this).dialog("close"); }
- }
- ]
- });
-
-
- $('#' + buttonId).click(function () {
-
- $('#' + checkToggleDiv).dialog('open');
- var target = $(this);
- $('#' + checkToggleDiv).dialog("widget").position({ my: 'top',
- at: 'bottom',
- of: target
- });
-
- return false;
- });
-
- var fnOnFilteredCurrent = fnOnFiltered;
-
- fnOnFiltered = function () {
- var target = $('#' + buttonId);
- $('#' + checkToggleDiv).dialog("widget").position({ my: 'top',
- at: 'bottom',
- of: target
- });
- fnOnFilteredCurrent();
- };
- //reset
- /*
- $('#'+buttonId+"Reset").button();
- $('#'+buttonId+"Reset").click(function(){
- $('#'+buttonId).removeClass("filter_selected"); //LM remove border if filter selected
- $('input:checkbox[name="'+localLabel+'"]:checked').each(function(index3) {
- $(this).attr('checked', false);
- $(this).addClass("search_init");
- });
- oTable.fnFilter('', index, true, false);
- return false;
- });
- */
- }
-
-
-
-
- function _fnRangeLabelPart(iPlace) {
- switch (iPlace) {
- case 0:
- return sRangeFormat.substring(0, sRangeFormat.indexOf("{from}"));
- case 1:
- return sRangeFormat.substring(sRangeFormat.indexOf("{from}") + 6, sRangeFormat.indexOf("{to}"));
- default:
- return sRangeFormat.substring(sRangeFormat.indexOf("{to}") + 4);
- }
- }
-
-
-
-
- var oTable = this;
-
- var defaults = {
- sPlaceHolder: "foot",
- sRangeSeparator: "~",
- iFilteringDelay: 500,
- aoColumns: null,
- sRangeFormat: "From {from} to {to}",
- sDateFromToken: "from",
- sDateToToken: "to"
- };
-
- var properties = $.extend(defaults, options);
-
- return this.each(function () {
-
- if (!oTable.fnSettings().oFeatures.bFilter)
- return;
- asInitVals = new Array();
-
- var aoFilterCells = oTable.fnSettings().aoFooter[0];
-
- var oHost = oTable.fnSettings().nTFoot; //Before fix for ColVis
- var sFilterRow = "tr"; //Before fix for ColVis
-
- if (properties.sPlaceHolder == "head:after") {
- var tr = $("tr:first", oTable.fnSettings().nTHead).detach();
- //tr.appendTo($(oTable.fnSettings().nTHead));
- if (oTable.fnSettings().bSortCellsTop) {
- tr.prependTo($(oTable.fnSettings().nTHead));
- //tr.appendTo($("thead", oTable));
- aoFilterCells = oTable.fnSettings().aoHeader[1];
- }
- else {
- tr.appendTo($(oTable.fnSettings().nTHead));
- //tr.prependTo($("thead", oTable));
- aoFilterCells = oTable.fnSettings().aoHeader[0];
- }
-
- sFilterRow = "tr:last";
- oHost = oTable.fnSettings().nTHead;
-
- } else if (properties.sPlaceHolder == "head:before") {
-
- if (oTable.fnSettings().bSortCellsTop) {
- var tr = $("tr:first", oTable.fnSettings().nTHead).detach();
- tr.appendTo($(oTable.fnSettings().nTHead));
- aoFilterCells = oTable.fnSettings().aoHeader[1];
- } else {
- aoFilterCells = oTable.fnSettings().aoHeader[0];
- }
- /*else {
- //tr.prependTo($("thead", oTable));
- sFilterRow = "tr:first";
- }*/
-
- sFilterRow = "tr:first";
-
- oHost = oTable.fnSettings().nTHead;
-
-
- }
-
- //$(sFilterRow + " th", oHost).each(function (index) {//bug with ColVis
- $(aoFilterCells).each(function (index) {//fix for ColVis
- i = index;
- var aoColumn = { type: "text",
- bRegex: false,
- bSmart: true,
- iMaxLenght: -1,
- iFilterLength: 0
- };
- if (properties.aoColumns != null) {
- if (properties.aoColumns.length < i || properties.aoColumns[i] == null)
- return;
- aoColumn = properties.aoColumns[i];
- }
- //label = $(this).text(); //Before fix for ColVis
- label = $($(this)[0].cell).text(); //Fix for ColVis
- if (aoColumn.sSelector == null) {
- //th = $($(this)[0]);//Before fix for ColVis
- th = $($(this)[0].cell); //Fix for ColVis
- }
- else {
- th = $(aoColumn.sSelector);
- if (th.length == 0)
- th = $($(this)[0].cell);
- }
-
- if (aoColumn != null) {
- if (aoColumn.sRangeFormat != null)
- sRangeFormat = aoColumn.sRangeFormat;
- else
- sRangeFormat = properties.sRangeFormat;
- switch (aoColumn.type) {
- case "null":
- break;
- case "number":
- fnCreateInput(oTable, true, false, true, aoColumn.iFilterLength, aoColumn.iMaxLenght);
- break;
- case "select":
- if (aoColumn.bRegex != true)
- aoColumn.bRegex = false;
- fnCreateSelect(oTable, aoColumn.values, aoColumn.bRegex, aoColumn.selected, aoColumn.multiple);
- break;
- case "number-range":
- fnCreateRangeInput(oTable);
- break;
- case "date-range":
- fnCreateDateRangeInput(oTable);
- break;
- case "checkbox":
- fnCreateCheckbox(oTable, aoColumn.values);
- break;
- case "twitter-dropdown":
- case "dropdown":
- fnCreateDropdown(aoColumn.values);
- break;
- case "text":
- default:
- bRegex = (aoColumn.bRegex == null ? false : aoColumn.bRegex);
- bSmart = (aoColumn.bSmart == null ? false : aoColumn.bSmart);
- fnCreateInput(oTable, bRegex, bSmart, false, aoColumn.iFilterLength, aoColumn.iMaxLenght);
- break;
-
- }
- }
- });
-
- for (j = 0; j < aiCustomSearch_Indexes.length; j++) {
- //var index = aiCustomSearch_Indexes[j];
- var fnSearch_ = function () {
- var id = oTable.attr("id");
- return $("#" + id + "_range_from_" + aiCustomSearch_Indexes[j]).val() + properties.sRangeSeparator + $("#" + id + "_range_to_" + aiCustomSearch_Indexes[j]).val()
- }
- afnSearch_.push(fnSearch_);
- }
-
- if (oTable.fnSettings().oFeatures.bServerSide) {
-
- var fnServerDataOriginal = oTable.fnSettings().fnServerData;
-
- oTable.fnSettings().fnServerData = function (sSource, aoData, fnCallback) {
-
- for (j = 0; j < aiCustomSearch_Indexes.length; j++) {
- var index = aiCustomSearch_Indexes[j];
-
- for (k = 0; k < aoData.length; k++) {
- if (aoData[k].name == "sSearch_" + index)
- aoData[k].value = afnSearch_[j]();
- }
- }
- aoData.push({ "name": "sRangeSeparator", "value": properties.sRangeSeparator });
-
- if (fnServerDataOriginal != null) {
- try {
- fnServerDataOriginal(sSource, aoData, fnCallback, oTable.fnSettings()); //TODO: See Issue 18
- } catch (ex) {
- fnServerDataOriginal(sSource, aoData, fnCallback);
- }
- }
- else {
- $.getJSON(sSource, aoData, function (json) {
- fnCallback(json)
- });
- }
- };
-
- }
-
- });
-
- };
-
-
-
-
-})(jQuery);
\ No newline at end of file
diff --git a/app/assets/javascripts/transactions.coffee b/app/assets/javascripts/transactions.coffee
index 24f83d1..9fbbb23 100644
--- a/app/assets/javascripts/transactions.coffee
+++ b/app/assets/javascripts/transactions.coffee
@@ -1,3 +1,17 @@
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
+ready = ->
+ $("#new_transaction").on("ajax:success", (e, data, status, xhr) ->
+ console.log("success")
+ ).on("ajax:error", (e, xhr, status, error) ->
+ console.log(e)
+ console.log(xhr)
+ console.log("failed")
+ )
+$.ajaxSetup({
+ dataType: 'json'
+})
+
+$(document).ready(ready)
+$(document).on('page:load', ready)
diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index 776ece0..bd8d49c 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -11,9 +11,8 @@
* file per style scope.
*
*= require_tree .
- *= require dataTables/extras/dataTables.responsive
- *= require dataTables/jquery.dataTables
*= require select2
+ *= require datagrid
*= require_self
*= require purecss
*/
diff --git a/app/assets/stylesheets/purecss.css b/app/assets/stylesheets/purecss.css.scss
similarity index 66%
rename from app/assets/stylesheets/purecss.css
rename to app/assets/stylesheets/purecss.css.scss
index f6647f5..94150c8 100644
--- a/app/assets/stylesheets/purecss.css
+++ b/app/assets/stylesheets/purecss.css.scss
@@ -43,3 +43,23 @@
background-color: rgb(83, 180, 79);
color: #fff;
}
+
+fieldset.pure-group-inline {
+ display: inline-block;
+ .pure-group-addon {
+ padding: .5em .6em;
+ box-shadow: inset 0 1px 3px #ddd;
+ border: 1px solid #ccc;
+ white-space: nowrap;
+ border-collapse: separate;
+ margin-left: -4px;
+ &:last-child {
+ border-radius: 0 4px 4px 0;
+ }
+ &:first-child {
+ margin-left: 0;
+ border-radius: 4px 0 0 4px;
+ border-right: 0;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/transactions.scss b/app/assets/stylesheets/transactions.scss
index c402390..25d4ef0 100644
--- a/app/assets/stylesheets/transactions.scss
+++ b/app/assets/stylesheets/transactions.scss
@@ -1,3 +1,6 @@
// Place all the styles related to the transactions controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
+.price {
+ width: 100px;
+}
diff --git a/app/assets/stylesheets/users.scss b/app/assets/stylesheets/users.css.scss
similarity index 83%
rename from app/assets/stylesheets/users.scss
rename to app/assets/stylesheets/users.css.scss
index 1efc835..994acd2 100644
--- a/app/assets/stylesheets/users.scss
+++ b/app/assets/stylesheets/users.css.scss
@@ -1,3 +1,4 @@
// Place all the styles related to the users controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
+.euro:before { content:"\20AC"; }
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index c7cc410..47d756c 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -12,12 +12,12 @@ class ApplicationController < ActionController::Base
end
def current_client
- @current_client ||= Client.find_by key: request.headers["X-API-KEY"]
+ @current_client ||= Client.find_by key: (request.headers["X_API_KEY"] || request.headers["HTTP_X_API_KEY"])
end
def current_ability
@current_ability ||=
current_client.try { |c| ClientAbility.new(c) } ||
- Ability.new(current_user)
+ UserAbility.new(current_user)
end
end
diff --git a/app/controllers/transactions_controller.rb b/app/controllers/transactions_controller.rb
index 7b5626d..83f3dd5 100644
--- a/app/controllers/transactions_controller.rb
+++ b/app/controllers/transactions_controller.rb
@@ -1,33 +1,30 @@
class TransactionsController < ApplicationController
- load_and_authorize_resource
skip_before_filter :verify_authenticity_token, only: :create
before_action :authenticate_user!, except: :create
before_action :authenticate_user_or_client!, only: :create
- def index
- @transactions = Transaction.all
- end
+ # This line MUST be placed after authentication
+ load_and_authorize_resource
- def new
- @transaction = Transaction.new
+ def index
+ gridparams = params[:transactions_grid] || Hash.new
+ gridparams = gridparams.merge(
+ order: :created_at,
+ descending: true,
+ current_user: current_user
+ )
+ @grid = TransactionsGrid.new(gridparams) do |scope|
+ scope.page(params[:page])
+ end
end
def create
- @transaction = Transaction.new(transaction_params)
- respond_to do |format|
- format.html do
- if @transaction.save
- flash[:success] = "Transaction created"
- redirect_to new_transaction_path
- else
- render 'new'
- end
- end
-
- format.json do
- head(@transaction.save ? :created : :unprocessable_entity)
- end
+ transaction = Transaction.new(transaction_params)
+ if transaction.save
+ head :created
+ else
+ render json: transaction.errors.full_messages, status: :unprocessable_entity
end
end
@@ -41,8 +38,13 @@ class TransactionsController < ApplicationController
debtor: User.find_by(name: t[:debtor]) || User.zeus,
creditor: User.find_by(name: t[:creditor]) || User.zeus,
issuer: current_client || current_user,
- amount: (t[:euros].to_f*100 + t[:cents].to_f).to_i,
+ amount: (float(t[:euros]) * 100 + float(t[:cents])).to_i,
message: t[:message]
}
end
+
+ def float arg
+ if arg.is_a? String then arg.sub!(',', '.') end
+ arg.to_f
+ end
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index d1e5a0a..80d122d 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -3,6 +3,7 @@ class UsersController < ApplicationController
def show
@user = User.find(params[:id])
+ @transaction = Transaction.new
respond_to do |format|
format.html
format.json do
diff --git a/app/grids/transactions_grid.rb b/app/grids/transactions_grid.rb
new file mode 100644
index 0000000..f7e4ba8
--- /dev/null
+++ b/app/grids/transactions_grid.rb
@@ -0,0 +1,34 @@
+class TransactionsGrid
+
+ include Datagrid
+
+ attr_accessor :current_user
+
+ scope do
+ Transaction
+ end
+
+ self.default_column_options = { order: false }
+
+ column(:created_at, order: true) { |model| model.created_at.to_date }
+ filter(:created_at, :date, range: true)
+
+ column(:amount)
+ filter(:amount, :integer, range: true)
+
+ column(:peer) { |model, grid| model.peer_of(grid.current_user).try(:name) }
+ filter(:peer, :string, header: 'Peer') do |value, scope, grid|
+ scope.joins(debtor: 'id', creditor: 'id')
+ .where("(debtor_id = :user AND creditor.name LIKE :name) OR (creditor_id = :user AND debtor.name LIKE :name)", user: grid.current_user.id, name: "%#{value}%")
+ end
+ # TODO this should probably by a SQL statement
+
+ column(:issuer) { |model| model.issuer.name }
+ filter(:issuer, :string, header: 'Issuer') do |value|
+ self.joins(:issuer).where("issuer.name LIKE :value", value: "%#{value}%")
+ end
+
+ column(:message)
+ filter(:message) { |value| where("message LIKE :value", value: "%#{value}%") }
+
+end
diff --git a/app/grids/transactions_query.rb b/app/grids/transactions_query.rb
new file mode 100644
index 0000000..36d2990
--- /dev/null
+++ b/app/grids/transactions_query.rb
@@ -0,0 +1,64 @@
+class TransactionsQuery
+ def initialize user
+ @user = user
+ @transactions = Arel::Table.new(:transactions)
+ @perspectived = Arel::Table.new(:perspectived_transactions)
+ @peers = Arel::Table.new(:users).alias('peers')
+ end
+
+ def arel
+ Arel::SelectManager.new(ActiveRecord::Base)
+ .from(issued_by(User).union(:all, issued_by(Client)))
+ .project(Arel.star)
+ end
+
+ def issued_by klass
+ issuers = klass.arel_table.alias('issuer')
+ Arel::SelectManager.new(ActiveRecord::Base)
+ .from(transactions)
+ .join(@peers).on(@peers[:id].eq(@perspectived[:peer_id]))
+ .join(issuers).on(issuers[:id].eq(@perspectived[:issuer_id]))
+ .where(@perspectived[:issuer_type].eq(klass.name))
+ .project(
+ @perspectived[:amount],
+ @perspectived[:date],
+ @perspectived[:message],
+ @peers[:name].as('peer'),
+ issuers[:name].as('issuer')
+ )
+ end
+
+ def transactions
+ Arel::Nodes::TableAlias.new(
+ incoming.union(:all, outgoing),
+ @perspectived.name
+ )
+ end
+
+ def outgoing
+ @transactions
+ .where(@transactions[:debtor_id].eq(@user.id))
+ .project(
+ (@transactions[:amount]*Arel::Nodes::SqlLiteral.new("-1")).as('amount'),
+ @transactions[:creditor_id].as('peer_id'),
+ @transactions[:created_at].as('date'),
+ @transactions[:issuer_id],
+ @transactions[:issuer_type],
+ @transactions[:message]
+ )
+ end
+
+ def incoming
+ @user.incoming_transactions.arel
+ @transactions
+ .where(@transactions[:debtor_id].eq(@user.id))
+ .project(
+ @transactions[:amount],
+ @transactions[:debtor_id].as('peer_id'),
+ @transactions[:created_at].as('date'),
+ @transactions[:issuer_id],
+ @transactions[:issuer_type],
+ @transactions[:message]
+ )
+ end
+end
diff --git a/app/helpers/transactions_helper.rb b/app/helpers/transactions_helper.rb
index 36098d1..704ea65 100644
--- a/app/helpers/transactions_helper.rb
+++ b/app/helpers/transactions_helper.rb
@@ -1,2 +1,5 @@
module TransactionsHelper
+ def amount a
+ a.zero? ? nil : number_with_precision(a/100.0, precision: 2)
+ end
end
diff --git a/app/models/client_ability.rb b/app/models/client_ability.rb
index 1f804eb..1a5dd66 100644
--- a/app/models/client_ability.rb
+++ b/app/models/client_ability.rb
@@ -1,4 +1,4 @@
-class Ability
+class ClientAbility
include CanCan::Ability
def initialize(client)
diff --git a/app/models/user.rb b/app/models/user.rb
index c5886cf..6e98824 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -12,6 +12,7 @@
class User < ActiveRecord::Base
devise :timeoutable, :omniauthable, :omniauth_providers => [:zeuswpi]
+
has_many :incoming_transactions,
class_name: 'Transaction', foreign_key: 'creditor_id'
has_many :outgoing_transactions,
diff --git a/app/models/ability.rb b/app/models/user_ability.rb
similarity index 91%
rename from app/models/ability.rb
rename to app/models/user_ability.rb
index c3a4c81..47af0c8 100644
--- a/app/models/ability.rb
+++ b/app/models/user_ability.rb
@@ -1,4 +1,4 @@
-class Ability
+class UserAbility
include CanCan::Ability
def initialize(user)
diff --git a/app/views/datagrid/_enum_checkboxes.html.haml b/app/views/datagrid/_enum_checkboxes.html.haml
new file mode 100644
index 0000000..330234e
--- /dev/null
+++ b/app/views/datagrid/_enum_checkboxes.html.haml
@@ -0,0 +1,5 @@
+- elements.each do |value, text, checked|
+ - id = [form.object_name, filter.name, value].join('_').underscore
+ = form.label filter.name, options.merge(:for => id) do
+ = form.check_box(filter.name, form.datagrid_extra_checkbox_options.merge(:multiple => true, :id => id, :checked => checked), value.to_s, nil)
+ = text
diff --git a/app/views/datagrid/_form.html.haml b/app/views/datagrid/_form.html.haml
new file mode 100644
index 0000000..4c337d1
--- /dev/null
+++ b/app/views/datagrid/_form.html.haml
@@ -0,0 +1,9 @@
+= form_for grid, options do |f|
+ - grid.filters.each do |filter|
+ .datagrid-filter.filter
+ = f.datagrid_label filter
+ = f.datagrid_filter filter, partials: options[:partials]
+ .datagrid-actions
+ = f.submit I18n.t("datagrid.form.search").html_safe, :class => "datagrid-submit"
+ - empty_parameter = Rails.version >= "4.1.0" && Rails.version <= '4.1.2' ? nil : {}
+ = link_to I18n.t('datagrid.form.reset').html_safe, url_for(grid.to_param => empty_parameter), :class => "datagrid-reset"
diff --git a/app/views/datagrid/_head.html.haml b/app/views/datagrid/_head.html.haml
new file mode 100644
index 0000000..8cfe7c6
--- /dev/null
+++ b/app/views/datagrid/_head.html.haml
@@ -0,0 +1,4 @@
+%tr
+ - grid.html_columns(*options[:columns]).each do |column|
+ %th{:class => "#{datagrid_column_classes(grid, column)}"}
+ = column.header
diff --git a/app/views/datagrid/_range_filter.html.haml b/app/views/datagrid/_range_filter.html.haml
new file mode 100644
index 0000000..7ab4337
--- /dev/null
+++ b/app/views/datagrid/_range_filter.html.haml
@@ -0,0 +1,3 @@
+= form.text_field(filter.name, from_options)
+%span{:class => "separator #{filter.type}"} -
+= form.text_field(filter.name, to_options)
diff --git a/app/views/datagrid/_row.html.haml b/app/views/datagrid/_row.html.haml
new file mode 100644
index 0000000..82c9918
--- /dev/null
+++ b/app/views/datagrid/_row.html.haml
@@ -0,0 +1,4 @@
+%tr{class: "#{options[:cycle] && cycle(*options[:cycle])}"}
+ - grid.html_columns(*options[:columns]).each do |column|
+ %td{:class => "#{datagrid_column_classes(grid, column)}"}
+ = datagrid_value(grid, column, asset)
diff --git a/app/views/datagrid/_table.html.haml b/app/views/datagrid/_table.html.haml
new file mode 100644
index 0000000..59f32da
--- /dev/null
+++ b/app/views/datagrid/_table.html.haml
@@ -0,0 +1,9 @@
+= content_tag :table, options[:html] do
+ %thead
+ = datagrid_header(grid, options)
+ %tbody
+ - if assets.any?
+ = datagrid_rows(grid, assets, options)
+ - else
+ %tr
+ %td.noresults{:colspan => "100%"}= I18n.t('datagrid.no_results').html_safe
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 0f335a3..897b33c 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -14,8 +14,6 @@
- if current_user
%li.pure-menu-item
= link_to current_user.name.capitalize, current_user, class: "pure-menu-link"
- %li.pure-menu-item
- = link_to "New Transaction", new_transaction_path, class: "pure-menu-link"
- else
= link_to "Sign in", user_omniauth_authorize_path(:zeuswpi), class: "pure-menu-link" unless current_user
.pure-u-1
diff --git a/app/views/transactions/_new.html.haml b/app/views/transactions/_new.html.haml
new file mode 100644
index 0000000..727d574
--- /dev/null
+++ b/app/views/transactions/_new.html.haml
@@ -0,0 +1,20 @@
+= render 'partials/form_errors', object: @transaction
+= form_for @transaction, remote: true, html: { class: "pure-form" } do |f|
+ - if current_user.penning
+ = f.collection_select :debtor, User.all, :name, :name,
+ { selected: @transaction.debtor.try(:name) || current_user.name }, { class: 'select2-selector'}
+ - else
+ = f.hidden_field :debtor, value: current_user.name
+ = f.select :creditor,
+ options_from_collection_for_select(User.all.order(:name), :name, :name),
+ { include_blank: true },
+ { class: 'select2-selector', size: 50 }
+ -# { class: 'select2-selector', size: 50, required: true }
+ = f.text_field :message, placeholder: "Message", size: 75
+ -# = f.text_field :message, required: true, placeholder: "Message", size: 75
+ %fieldset.pure-group-inline
+ %span.euro.pure-group-addon
+ = f.number_field :euros, value: amount(@transaction.amount),
+ placeholder: "Bedrag", step: 0.01, min: 0.01, class: "pure-group-addon price", size: 20
+ -# placeholder: "Bedrag", step: 0.01, min: 0.01, class: "pure-group-addon price", size: 20, required: true
+ = f.submit "Send it!", class: "pure-button pure-button-primary"
diff --git a/app/views/transactions/index.html.haml b/app/views/transactions/index.html.haml
index e69de29..f8ad708 100644
--- a/app/views/transactions/index.html.haml
+++ b/app/views/transactions/index.html.haml
@@ -0,0 +1,4 @@
+= datagrid_form_for @grid, method: :get, url: transactions_path
+= paginate(@grid.assets)
+= datagrid_table @grid
+= paginate(@grid.assets)
diff --git a/app/views/transactions/new.html.haml b/app/views/transactions/new.html.haml
deleted file mode 100644
index bd46894..0000000
--- a/app/views/transactions/new.html.haml
+++ /dev/null
@@ -1,13 +0,0 @@
-= render 'partials/form_errors', object: @transaction
-= simple_form_for @transaction do |f|
- - if current_user.penning
- = f.collection_select :debtor, User.all, :name, :name,
- { selected: @transaction.debtor.try(:name) || current_user.name }, { class: 'select2-selector'}
- - else
- = f.hidden_field :debtor, value: current_user.name
- = f.collection_select :creditor, User.all, :name, :name,
- { selected: @transaction.creditor.try(:name) || User.zeus.name }, { class: 'select2-selector' }
- = f.input :message, required: true
- = f.input :euros, input_html: { value: @transaction.amount/100}
- .pure-controls
- = f.button :submit, "Send it!"
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 95b8f8f..054c155 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,5 +1,7 @@
%h2= @user.name
+= render 'transactions/new'
+
.panel.panel-default.data-table-filters
.panel-header
%h3.panel-title Filters
diff --git a/config/routes.rb b/config/routes.rb
index 3ac9949..7d6f8fa 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -5,6 +5,6 @@ Rails.application.routes.draw do
root to: 'high_voltage/pages#show', id: "landing"
- resources :transactions, only: [:new, :index, :create]
+ resources :transactions, only: [:index, :create]
resources :users, only: [:show, :index]
end
diff --git a/spec/apis/transactions_controller_spec.rb b/spec/apis/transactions_controller_spec.rb
new file mode 100644
index 0000000..32c7eb2
--- /dev/null
+++ b/spec/apis/transactions_controller_spec.rb
@@ -0,0 +1,58 @@
+describe TransactionsController, type: :api do
+ before :each do
+ @debtor = create :user
+ @creditor = create :user
+ @api_attributes = {
+ debtor: @debtor.name,
+ creditor: @creditor.name,
+ message: Faker::Lorem.sentence,
+ euros: 1,
+ cents: 25
+ }
+ end
+
+ def post_transaction(extra_attributes = {})
+ post '/transactions', { transaction: @api_attributes.merge(extra_attributes) },
+ { 'HTTP_ACCEPT' => "application/json", "X_API_KEY" => @key }
+ end
+
+ before :each do
+ @client = Client.create name: "Tap"
+ @key = @client.key
+ end
+
+ describe "Authentication" do
+ it "should require a client authentication key" do
+ post '/transactions'
+ expect(last_response.status).to eq(401)
+ end
+
+ it "should work with valid key" do
+ post_transaction
+ expect(last_response.status).to eq(201)
+ end
+ end
+
+ describe "successfull creating transaction" do
+ it "should create a transaction" do
+ expect { post_transaction }.to change { Transaction.count }.by(1)
+ end
+
+ it "should set issuer" do
+ post_transaction
+ @transaction = Transaction.last
+ expect(@transaction.issuer).to eq(@client)
+ end
+ end
+
+ describe "failed creating transaction" do
+ it "should create a transaction" do
+ expect { post_transaction(euros: -5) }.to change { Transaction.count }.by(0)
+ end
+
+ it "should give 402 status" do
+ post_transaction(euros: -4)
+ expect(last_response.status).to eq(422)
+ end
+ end
+end
diff --git a/spec/controllers/transactions_controller_spec.rb b/spec/controllers/transactions_controller_spec.rb
index e96cc51..3c6ced4 100644
--- a/spec/controllers/transactions_controller_spec.rb
+++ b/spec/controllers/transactions_controller_spec.rb
@@ -1,7 +1,4 @@
-require 'rails_helper'
-require 'spec_helper'
-
-RSpec.describe TransactionsController, type: :controller do
+describe TransactionsController, type: :controller do
describe "creating transaction" do
before :each do
@debtor = create(:user)
@@ -22,7 +19,7 @@ RSpec.describe TransactionsController, type: :controller do
end
it "should create a new transaction" do
- expect {post :create, @attributes}.to change {Transaction.count}.by(1)
+ expect { post :create, @attributes }.to change { Transaction.count }.by(1)
end
it "should set debtor" do
@@ -58,7 +55,7 @@ RSpec.describe TransactionsController, type: :controller do
it "should be refused" do
expect do
post :create, transaction: attributes_for(:transaction, cents: -20)
- end.not_to change {Transaction.count}
+ end.not_to change { Transaction.count }
end
end
@@ -71,7 +68,7 @@ RSpec.describe TransactionsController, type: :controller do
euros: 10000000,
message: 'DIT IS OVERVAL'
}
- end.not_to change {Transaction.count}
+ end.not_to change { Transaction.count }
end
end
end
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index e2c3d3b..86d0660 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -1,5 +1,36 @@
-require 'rails_helper'
+describe UsersController, type: :controller do
+ before :each do
+ @user = create :penning
+ sign_in @user
+ end
-RSpec.describe UsersController, type: :controller do
+ describe 'GET show' do
+ before :each do
+ get :show, id: @user
+ end
+ it 'should be successful' do
+ expect(response).to render_template(:show)
+ expect(response).to have_http_status(200)
+ end
+
+ it 'should load the correct user' do
+ expect(assigns(:user)).to eq(@user)
+ end
+ end
+
+ describe 'GET index' do
+ before :each do
+ get :index
+ end
+
+ it 'should load an array of all users' do
+ expect(assigns(:users)).to eq([@user])
+ end
+
+ it 'should render the correct template' do
+ expect(response).to have_http_status(200)
+ expect(response).to render_template(:index)
+ end
+ end
end
diff --git a/spec/factories/transactions.rb b/spec/factories/transactions.rb
index 716b419..71e7e00 100644
--- a/spec/factories/transactions.rb
+++ b/spec/factories/transactions.rb
@@ -18,7 +18,7 @@ FactoryGirl.define do
association :debtor, factory: :user
association :creditor, factory: :user
issuer { debtor }
- amount { rand(100) }
+ amount { 1 + rand(100) }
message { Faker::Lorem.sentence }
end
end
diff --git a/spec/helpers/transactions_helper_spec.rb b/spec/helpers/transactions_helper_spec.rb
index 48c6c73..3b3e225 100644
--- a/spec/helpers/transactions_helper_spec.rb
+++ b/spec/helpers/transactions_helper_spec.rb
@@ -1,14 +1,2 @@
-require 'rails_helper'
-
-# Specs in this file have access to a helper object that includes
-# the TransactionsHelper. For example:
-#
-# describe TransactionsHelper do
-# describe "string concat" do
-# it "concats two strings with spaces" do
-# expect(helper.concat_strings("this","that")).to eq("this that")
-# end
-# end
-# end
RSpec.describe TransactionsHelper, type: :helper do
end
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
index 890768c..bb943ca 100644
--- a/spec/helpers/users_helper_spec.rb
+++ b/spec/helpers/users_helper_spec.rb
@@ -1,14 +1,2 @@
-require 'rails_helper'
-
-# Specs in this file have access to a helper object that includes
-# the UsersHelper. For example:
-#
-# describe UsersHelper do
-# describe "string concat" do
-# it "concats two strings with spaces" do
-# expect(helper.concat_strings("this","that")).to eq("this that")
-# end
-# end
-# end
RSpec.describe UsersHelper, type: :helper do
end
diff --git a/spec/models/client_spec.rb b/spec/models/client_spec.rb
index 33e905f..d96c171 100644
--- a/spec/models/client_spec.rb
+++ b/spec/models/client_spec.rb
@@ -9,14 +9,20 @@
# updated_at :datetime not null
#
-require 'rails_helper'
-
-RSpec.describe Client, type: :model do
+describe Client, type: :model do
+ before :each do
+ @client = create :client
+ end
it "should have a valid factory" do
- expect(create(:client)).to be_valid
+ expect(@client).to be_valid
end
it "should generate a key" do
- expect(create(:client).key).to be_present
+ expect(@client.key).to be_present
+ end
+
+ it "should have a unique name" do
+ client = build :client, name: @client.name
+ expect(client).to_not be_valid
end
end
diff --git a/spec/models/transaction_spec.rb b/spec/models/transaction_spec.rb
index d05aaf8..4cd2a8f 100644
--- a/spec/models/transaction_spec.rb
+++ b/spec/models/transaction_spec.rb
@@ -13,9 +13,7 @@
# updated_at :datetime not null
#
-require 'rails_helper'
-
-RSpec.describe Transaction, type: :model do
+describe Transaction, type: :model do
it "has a valid factory" do
expect(create(:transaction)).to be_valid
end
@@ -36,4 +34,20 @@ RSpec.describe Transaction, type: :model do
end
end
+ describe "amount" do
+ it "should be positive" do
+ expect(build :transaction, amount: -5).to_not be_valid
+ end
+
+ it "should not be 0" do
+ expect(build :transaction, amount: 0).to_not be_valid
+ end
+ end
+
+ describe "debtor/creditor" do
+ it "should be different" do
+ @user = create :user
+ expect(build :transaction, debtor: @user, creditor: @user).to_not be_valid
+ end
+ end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index cc98894..e188fc1 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -10,10 +10,17 @@
# updated_at :datetime not null
#
-require 'rails_helper'
+describe User, type: :model do
+ before :each do
+ @user = create :user
+ end
-RSpec.describe User, type: :model do
it "has a valid factory" do
- expect(create(:user)).to be_valid
+ expect(@user).to be_valid
+ end
+
+ it "has a unique name" do
+ user = build :user, name: @user.name
+ expect(user).to_not be_valid
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 831f776..f3829a4 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -22,6 +22,11 @@ Coveralls.wear!('rails')
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
require 'factory_girl'
require 'devise'
+
+Dir[File.join(File.dirname(__FILE__), 'support', '**', '*.rb')].each do |f|
+ require f
+end
+
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
config.include Devise::TestHelpers, type: :controller
diff --git a/spec/support/api_helper.rb b/spec/support/api_helper.rb
new file mode 100644
index 0000000..b67d167
--- /dev/null
+++ b/spec/support/api_helper.rb
@@ -0,0 +1,11 @@
+module ApiHelper
+ include Rack::Test::Methods
+
+ def app
+ Rails.application
+ end
+end
+
+RSpec.configure do |config|
+ config.include ApiHelper, type: :api #apply to all spec for apis folder
+end