Merge pull request #2692 from Matt-Yorkley/bi/angular_orders

Angularise orders page
This commit is contained in:
Pau Pérez Fabregat
2018-10-04 21:10:48 +02:00
committed by GitHub
26 changed files with 444 additions and 209 deletions

View File

@@ -0,0 +1,26 @@
angular.module("admin.orders").controller "orderCtrl", ($scope, shops, orderCycles, $compile, $attrs, Orders) ->
$scope.$compile = $compile
$scope.shops = shops
$scope.orderCycles = orderCycles
$scope.distributor_id = parseInt($attrs.ofnDistributorId)
$scope.order_cycle_id = parseInt($attrs.ofnOrderCycleId)
$scope.validOrderCycle = (oc) ->
$scope.orderCycleHasDistributor oc, parseInt($scope.distributor_id)
$scope.distributorHasOrderCycles = (distributor) ->
(oc for oc in $scope.orderCycles when @orderCycleHasDistributor(oc, distributor.id)).length > 0
$scope.orderCycleHasDistributor = (oc, distributor_id) ->
distributor_ids = (d.id for d in oc.distributors)
distributor_ids.indexOf(distributor_id) != -1
$scope.distributionChosen = ->
$scope.distributor_id && $scope.order_cycle_id
for oc in $scope.orderCycles
oc.name_and_status = "#{oc.name} (#{oc.status})"
for shop in $scope.shops
shop.disabled = !$scope.distributorHasOrderCycles(shop)

View File

@@ -1,26 +1,41 @@
angular.module("admin.orders").controller "ordersCtrl", ($scope, $compile, $attrs, shops, orderCycles) ->
$scope.$compile = $compile
$scope.shops = shops
$scope.orderCycles = orderCycles
angular.module("admin.orders").controller "ordersCtrl", ($scope, RequestMonitor, Orders, SortOptions) ->
$scope.RequestMonitor = RequestMonitor
$scope.pagination = Orders.pagination
$scope.orders = Orders.all
$scope.sortOptions = SortOptions
$scope.distributor_id = parseInt($attrs.ofnDistributorId)
$scope.order_cycle_id = parseInt($attrs.ofnOrderCycleId)
$scope.initialise = ->
$scope.q = {
completed_at_not_null: true
}
$scope.fetchResults()
$scope.validOrderCycle = (oc) ->
$scope.orderCycleHasDistributor oc, parseInt($scope.distributor_id)
$scope.fetchResults = (page=1) ->
Orders.index({
'q[created_at_lt]': $scope['q']['created_at_lt'],
'q[created_at_gt]': $scope['q']['created_at_gt'],
'q[state_eq]': $scope['q']['state_eq'],
'q[number_cont]': $scope['q']['number_cont'],
'q[email_cont]': $scope['q']['email_cont'],
'q[bill_address_firstname_start]': $scope['q']['bill_address_firstname_start'],
'q[bill_address_lastname_start]': $scope['q']['bill_address_lastname_start'],
'q[completed_at_not_null]': $scope['q']['completed_at_not_null'],
'q[inventory_units_shipment_id_null]': $scope['q']['inventory_units_shipment_id_null'],
'q[distributor_id_in]': $scope['q']['distributor_id_in'],
'q[order_cycle_id_in]': $scope['q']['order_cycle_id_in'],
'q[order_cycle_id_in]': $scope['q']['order_cycle_id_in'],
'q[s]': $scope.sorting || 'id desc',
per_page: $scope.per_page || 15,
page: page
})
$scope.distributorHasOrderCycles = (distributor) ->
(oc for oc in orderCycles when @orderCycleHasDistributor(oc, distributor.id)).length > 0
$scope.$watch 'sortOptions', (sort) ->
if sort && sort.predicate != ""
$scope.sorting = sort.predicate + ' desc' if sort.reverse
$scope.sorting = sort.predicate + ' asc' if !sort.reverse
$scope.fetchResults()
, true
$scope.orderCycleHasDistributor = (oc, distributor_id) ->
distributor_ids = (d.id for d in oc.distributors)
distributor_ids.indexOf(distributor_id) != -1
$scope.distributionChosen = ->
$scope.distributor_id && $scope.order_cycle_id
for oc in $scope.orderCycles
oc.name_and_status = "#{oc.name} (#{oc.status})"
for shop in $scope.shops
shop.disabled = !$scope.distributorHasOrderCycles(shop)
$scope.changePage = (newPage) ->
$scope.page = newPage
$scope.fetchResults(newPage)

View File

@@ -2,7 +2,6 @@ angular.module("admin.resources").factory 'OrderResource', ($resource) ->
$resource('/admin/orders/:id/:action.json', {}, {
'index':
method: 'GET'
isArray: true
'update':
method: 'PUT'
})

View File

@@ -1,18 +1,30 @@
angular.module("admin.resources").factory 'Orders', ($q, OrderResource) ->
angular.module("admin.resources").factory 'Orders', ($q, OrderResource, RequestMonitor) ->
new class Orders
all: []
byID: {}
pristineByID: {}
pagination: {}
index: (params={}, callback=null) ->
OrderResource.index params, (data) =>
request = OrderResource.index params, (data) =>
@load(data)
(callback || angular.noop)(data)
RequestMonitor.load(request.$promise)
@all
load: (orders) ->
for order in orders
load: (data) ->
angular.extend(@pagination, data.pagination)
@clearData()
for order in data.orders
@all.push order
@byID[order.id] = order
@pristineByID[order.id] = angular.copy(order)
clearData: ->
@all.length = 0
@byID = {}
@pristineByID = {}
save: (order) ->
deferred = $q.defer()
order.$update({id: order.number})

View File

@@ -0,0 +1,20 @@
@import "admin/variables";
.pagination {
text-align: center;
margin: 2em 0 1em;
button {
margin: 0 0.35em;
&.active {
background-color: darken($spree-blue, 15%);
cursor: default;
}
&.disabled {
background-color: $disabled_button;
cursor: default;
}
}
}

View File

@@ -91,3 +91,15 @@ th.actions {
table.index td.actions {
text-align: left;
}
.orders-loading {
margin-top: 1em;
img {
width: 85px;
}
span {
font-size: 1.2em;
}
}

View File

@@ -9,8 +9,10 @@ $warning-orange: #da7f52;
$bright-orange: #ffa92e;
$medium-grey: #919191;
$pale-blue: #cee1f4;
$light-grey: #ccc;
$admin-table-border: $pale-blue;
$modal-close-button-color: #de6060;
$modal-close-button-hover-color: #bf4545;
$disabled-button: $light-grey;

View File

@@ -61,7 +61,14 @@ Spree::Admin::OrdersController.class_eval do
respond_with(@orders) do |format|
format.html
format.json do
render_as_json @orders
render json: {
orders: ActiveModel::ArraySerializer.new(@orders, each_serializer: Api::Admin::OrderSerializer),
pagination: {
results: @orders.total_count,
pages: @orders.num_pages,
page: params[:page].to_i
}
}
end
end
end
@@ -102,16 +109,14 @@ Spree::Admin::OrdersController.class_eval do
def orders
if json_request?
@search = OpenFoodNetwork::Permissions.new(spree_current_user).editable_orders.ransack(params[:q])
@search.result.reorder('id ASC')
else
@search = Spree::Order.accessible_by(current_ability, :index).ransack(params[:q])
# Replaced this search to filter orders to only show those distributed by current user (or all for admin user)
@search.result.includes([:user, :shipments, :payments]).
distributed_by_user(spree_current_user).
page(params[:page]).
per(params[:per_page] || Spree::Config[:orders_per_page])
@search.result.includes([:user, :shipments, :payments]).distributed_by_user(spree_current_user)
end
@search.result.page(params[:page]).per(params[:per_page] || Spree::Config[:orders_per_page])
end
def require_distributor_abn

View File

@@ -1,5 +1,8 @@
class Api::Admin::OrderSerializer < ActiveModel::Serializer
attributes :id, :number, :full_name, :email, :phone, :completed_at
attributes :id, :number, :full_name, :email, :phone, :completed_at, :display_total
attributes :show_path, :edit_path, :state, :payment_state, :shipment_state
attributes :payments_path, :shipments_path, :ship_path, :ready_to_ship, :created_at
attributes :distributor_name, :special_instructions, :payment_capture_path
has_one :distributor, serializer: Api::Admin::IdSerializer
has_one :order_cycle, serializer: Api::Admin::IdSerializer
@@ -8,6 +11,48 @@ class Api::Admin::OrderSerializer < ActiveModel::Serializer
object.billing_address.nil? ? "" : ( object.billing_address.full_name || "" )
end
def distributor_name
object.distributor.andand.name
end
def show_path
return '' unless object.id
spree_routes_helper.admin_order_path(object)
end
def edit_path
return '' unless object.id
spree_routes_helper.edit_admin_order_path(object)
end
def payments_path
return '' unless object.payment_state
spree_routes_helper.admin_order_payments_path(object)
end
def shipments_path
return '' unless object.shipment_state
spree_routes_helper.admin_order_shipments_path(object)
end
def ship_path
spree_routes_helper.fire_admin_order_path(object, e: 'ship')
end
def payment_capture_path
pending_payment = object.pending_payments.first
return '' unless object.payment_required? && pending_payment
spree_routes_helper.fire_admin_order_payment_path(object, pending_payment.id, e: 'capture')
end
def ready_to_ship
object.ready_to_ship?
end
def display_total
object.display_total.to_html
end
def email
object.email || ""
end
@@ -16,7 +61,17 @@ class Api::Admin::OrderSerializer < ActiveModel::Serializer
object.billing_address.nil? ? "a" : ( object.billing_address.phone || "" )
end
def created_at
object.created_at.blank? ? "" : I18n.l(object.created_at, format: '%B %d, %Y')
end
def completed_at
object.completed_at.blank? ? "" : object.completed_at.strftime("%F %T")
object.completed_at.blank? ? "" : I18n.l(object.completed_at, format: '%B %d, %Y')
end
private
def spree_routes_helper
Spree::Core::Engine.routes_url_helpers
end
end

View File

@@ -0,0 +1,17 @@
.pagination
%button{'ng-click' => 'changePage(1)', 'ng-class' => "{'disabled': pagination.page == 1}", 'ng-disabled' => "pagination.page == 1"}
= "&laquo;".html_safe
= t(:first)
%button{'ng-click' => 'changePage((pagination.page)-1)', 'ng-class' => "{'disabled': pagination.page == 1}", 'ng-disabled' => "pagination.page == 1"}
= t(:previous)
%span{'ng-show' => 'pagination.page > 3'}
= "&hellip;".html_safe
%button{'ng-repeat' => 'i in [].constructor(pagination.pages) track by $index', 'ng-show' =>'($index+1 > pagination.page-3 || (pagination.page > pagination.pages-2 && $index+1 > pagination.pages-5)) && ($index+1 < pagination.page+3 || (pagination.page < 3 && $index+1 < 6))', 'ng-class' => "{'active': pagination.page == $index+1}", 'ng-click' => 'changePage($index+1)', 'ng-disabled' => "pagination.page == $index+1"}
{{$index+1}}
%span{'ng-show' => 'pagination.page < pagination.pages-2'}
= "&hellip;".html_safe
%button{'ng-click' => 'changePage((pagination.page)+1)', 'ng-class' => "{'disabled': pagination.page == pagination.pages}", 'ng-disabled' => "pagination.page == pagination.pages"}
= t(:next)
%button{'ng-click' => 'changePage(pagination.pages)', 'ng-class' => "{'disabled': pagination.page == pagination.pages}", 'ng-disabled' => "pagination.page == pagination.pages"}
= t(:last)
= "&raquo;".html_safe

View File

@@ -1,7 +0,0 @@
- # Get the payment in 'checkout' state if any, and show capture button
- if order.payments.present?
- payment = order.payments.select{|p| p if p.state == 'checkout'}.first
- if !payment.nil?
- payment.actions.grep(/^capture$/).each do |action|
- # copied from backend/app/views/spree/admin/payments/_list.html.erb
= link_to_with_icon "icon-#{action}", t('admin.orders.index.capture'), fire_admin_order_payment_path(order, payment, :e => action), :method => :put, :no_text => true, :data => {:action => action}

View File

@@ -0,0 +1,51 @@
%div{"data-hook" => "admin_orders_index_search"}
= search_form_for [:admin, @search], html: { name: "orders_form", "ng-submit" => "fetchResults()"} do |f|
.field-block.alpha.four.columns
.date-range-filter.field
= label_tag nil, t(:date_range)
.date-range-fields
= f.text_field :created_at_gt, class: 'datepicker', datepicker: 'q.created_at_gt', 'ng-model' => 'q.created_at_gt', :value => params[:q][:created_at_gt], :placeholder => t(:start)
%span.range-divider
%i.icon-arrow-right
= f.text_field :created_at_lt, class: 'datepicker', datepicker: 'q.created_at_lt', 'ng-model' => 'q.created_at_lt', :value => params[:q][:created_at_lt], :placeholder => t(:stop)
.field
= label_tag nil, t(:status)
= f.select :state_eq, Spree::Order.state_machines[:state].states.collect {|s| [t("order_state.#{s.name}"), s.value]}, {:include_blank => true}, :class => 'select2', 'ng-model' => 'q.state_eq'
.four.columns
.field
= label_tag nil, t(:order_number)
= f.text_field :number_cont, 'ng-model' => 'q.number_cont'
.field
= label_tag nil, t(:email)
= f.email_field :email_cont, 'ng-model' => 'q.email_cont'
.four.columns
.field
= label_tag nil, t(:first_name_begins_with)
= f.text_field :bill_address_firstname_start, :size => 25, 'ng-model' => 'q.bill_address_firstname_start'
.field
= label_tag nil, t(:last_name_begins_with)
= f.text_field :bill_address_lastname_start, :size => 25, 'ng-model' => 'q.bill_address_lastname_start'
.omega.four.columns
.field.checkbox
%label
= f.check_box :completed_at_not_null, {:checked => @show_only_completed, 'ng-model' => 'q.completed_at_not_null'}, '1', ''
= t(:show_only_complete_orders)
.field.checkbox
%label
= f.check_box :inventory_units_shipment_id_null, {'ng-model' => 'q.inventory_units_shipment_id_null'}, '1', '0'
= t(:show_only_unfulfilled_orders)
.field-block.alpha.eight.columns
= label_tag nil, t(:distributors)
= select_tag("q[distributor_id_in]",
options_for_select(Enterprise.is_distributor.managed_by(spree_current_user).map {|e| [e.name, e.id]}, params[:distributor_ids]),
{class: "select2 fullwidth", multiple: true, 'ng-model' => 'q.distributor_id_in'})
.field-block.omega.eight.columns
= label_tag nil, t(:order_cycles)
= select_tag("q[order_cycle_id_in]",
options_for_select(OrderCycle.managed_by(spree_current_user).where('order_cycles.orders_close_at is not null').order('order_cycles.orders_close_at DESC').map {|oc| [oc.name, oc.id]}, params[:order_cycle_ids]),
{class: "select2 fullwidth", multiple: true, 'ng-model' => 'q.order_cycle_id_in'})
.clearfix
.actions.filter-actions
%div
%a.button.icon-search{'ng-click' => 'fetchResults()'}
= t(:filter_results)

View File

@@ -0,0 +1,4 @@
%a{'ng-click' => "sortOptions.toggle('#{column_name}')"}
= t(column_name.to_s, scope: 'activerecord.attributes.spree/order')
%span{'ng-show' => "sorting == '#{column_name} asc'"}= "&#x25B2;".html_safe
%span{'ng-show' => "sorting == '#{column_name} desc'"}= "&#x25BC;".html_safe

View File

@@ -13,7 +13,7 @@
%div{"data-hook" => "admin_order_edit_header"}
= render 'spree/shared/error_messages', target: @order
%div{"ng-app" => "admin.orders", "ng-controller" => "ordersCtrl", "ofn-distributor-id" => @order.distributor_id, "ofn-order-cycle-id" => @order.order_cycle_id}
%div{"ng-app" => "admin.orders", "ng-controller" => "orderCtrl", "ofn-distributor-id" => @order.distributor_id, "ofn-order-cycle-id" => @order.order_cycle_id}
= render 'add_product'
%div{"data-hook" => "admin_order_edit_form"}

View File

@@ -1,110 +1,86 @@
- content_for :page_title do
= t(:listing_orders)
= t('.listing_orders')
- content_for :page_actions do
%li
= button_link_to t(:new_order), new_admin_order_url, :icon => 'icon-plus', :id => 'admin_new_order'
= button_link_to t('.new_order'), new_admin_order_url, icon: 'icon-plus', id: 'admin_new_order'
= render partial: 'spree/admin/shared/order_sub_menu'
- content_for :app_wrapper_attrs do
= "ng-app='admin.orders' ng-controller='ordersCtrl'"
- content_for :table_filter_title do
= t(:search)
- content_for :table_filter do
%div{"data-hook" => "admin_orders_index_search"}
= search_form_for [:admin, @search] do |f|
.field-block.alpha.four.columns
.date-range-filter.field
= label_tag nil, t(:date_range)
.date-range-fields
= f.text_field :created_at_gt, :class => 'datepicker datepicker-from', :value => params[:q][:created_at_gt], :placeholder => t(:start)
%span.range-divider
%i.icon-arrow-right
= f.text_field :created_at_lt, :class => 'datepicker datepicker-to', :value => params[:q][:created_at_lt], :placeholder => t(:stop)
.field
= label_tag nil, t(:status)
= f.select :state_eq, Spree::Order.state_machines[:state].states.collect {|s| [t("order_state.#{s.name}"), s.value]}, {:include_blank => true}, :class => 'select2'
.four.columns
.field
= label_tag nil, t(:order_number)
= f.text_field :number_cont
.field
= label_tag nil, t(:email)
= f.email_field :email_cont
.four.columns
.field
= label_tag nil, t(:first_name_begins_with)
= f.text_field :bill_address_firstname_start, :size => 25
.field
= label_tag nil, t(:last_name_begins_with)
= f.text_field :bill_address_lastname_start, :size => 25
.omega.four.columns
.field.checkbox
%label
= f.check_box :completed_at_not_null, {:checked => @show_only_completed}, '1', ''
= t(:show_only_complete_orders)
.field.checkbox
%label
= f.check_box :inventory_units_shipment_id_null, { }, '1', '0'
= t(:show_only_unfulfilled_orders)
.field-block.alpha.eight.columns
= label_tag nil, t(:distributors)
= select_tag("q[distributor_id_in]",
options_for_select(Enterprise.is_distributor.managed_by(spree_current_user).map {|e| [e.name, e.id]}, params[:distributor_ids]),
{class: "select2 fullwidth", multiple: true})
.field-block.omega.eight.columns
= label_tag nil, t(:order_cycles)
= select_tag("q[order_cycle_id_in]",
options_for_select(OrderCycle.managed_by(spree_current_user).where('order_cycles.orders_close_at is not null').order('order_cycles.orders_close_at DESC').map {|oc| [oc.name, oc.id]}, params[:order_cycle_ids]),
{class: "select2 fullwidth", multiple: true})
.clearfix
.actions.filter-actions
%div{"data-hook" => "admin_orders_index_search_buttons"}
= button t(:filter_results), 'icon-search'
- unless @orders.empty?
%table#listing_orders.index.responsive{"data-hook" => "", width: "100%", "ng-app" => "ofn.admin"}
%colgroup
%col{style: "width: 10%"}
%thead
%tr{"data-hook" => "admin_orders_index_headers"}
= render partial: 'filters'
%table#listing_orders.index.responsive{width: "100%", 'ng-init' => 'initialise()', 'ng-show' => "!RequestMonitor.loading && orders.length > 0" }
%colgroup
%col{style: "width: 10%"}
%thead
%tr
%th
= t(:products_distributor)
- if @show_only_completed
%th
= t(:products_distributor)
- if @show_only_completed
%th= sort_link @search, :completed_at, t(:completed_at, :scope => 'activerecord.attributes.spree/order')
- else
%th= sort_link @search, :created_at, t(:created_at, :scope => 'activerecord.attributes.spree/order')
%th= sort_link @search, :number, t(:number, :scope => 'activerecord.attributes.spree/order')
%th= sort_link @search, :state, t(:state, :scope => 'activerecord.attributes.spree/order')
%th= sort_link @search, :payment_state, t(:payment_state, :scope => 'activerecord.attributes.spree/order')
%th= sort_link @search, :shipment_state, t(:shipment_state, :scope => 'activerecord.attributes.spree/order')
%th= sort_link @search, :email, t(:email, :scope => 'activerecord.attributes.spree/order')
%th= sort_link @search, :total, t(:total, :scope => 'activerecord.attributes.spree/order')
%th.actions{"data-hook" => "admin_orders_index_header_actions"}
%tbody
- @orders.each do |order|
%tr{class: "state-#{order.state.downcase} #{cycle('odd', 'even')}", "data-hook" => "admin_orders_index_rows"}
%td.align-center
= order.distributor.andand.name
%td.align-center= l (@show_only_completed ? order.completed_at : order.created_at).to_date
%td
= link_to order.number, admin_order_path(order)
- if order.special_instructions.present?
%br
%span{class: "icon-warning-sign", "ofn-with-tip" => order.special_instructions}
notes
%td.align-center
%span{class: "state #{order.state.downcase}"}= t("order_state.#{order.state.downcase}")
%td.align-center
%span{class: "state #{order.payment_state}"}= link_to t("payment_states.#{order.payment_state}"), admin_order_payments_path(order) if order.payment_state
%td.align-center
%span{class: "state #{order.shipment_state}"}= link_to t("shipment_states.#{order.shipment_state}"), admin_order_shipments_path(order) if order.shipment_state
%td= mail_to order.email
%td.align-center= order.display_total.to_html
%td.actions{"data-hook" => "admin_orders_index_row_actions"}
= link_to_edit_url edit_admin_order_path(order), :title => "admin_edit_#{dom_id(order)}", :no_text => true
- if order.ready_to_ship?
- # copied from backend/app/views/spree/admin/payments/_list.html.erb
= link_to_with_icon "icon-road", t('admin.orders.index.ship'), fire_admin_order_url(order, :e => 'ship'), :method => :put, :no_text => true, :data => {:action => 'ship', :confirm => t(:are_you_sure)}
= render partial: 'spree/admin/orders/capture', locals: {order: order}
- else
.no-objects-found
= t(:no_orders_found)
%a{'ng-click' => "sortOptions.toggle('completed_at')"}
= t(:completed_at, scope: 'activerecord.attributes.spree/order')
%span{'ng-show' => "sorting == 'completed_at asc'"}= "&#x25B2;".html_safe
%span{'ng-show' => "sorting == 'completed_at desc' || sorting === undefined"}= "&#x25BC;".html_safe
- else
%th
= render partial: 'sortable_header', locals: {column_name: 'created_at'}
- ['number', 'state', 'payment_state', 'shipment_state', 'email', 'total'].each do |column_name|
%th
= render partial: 'sortable_header', locals: {column_name: column_name}
%th.actions
%tbody
%tr{ng: {repeat: 'order in orders track by $index', class: {even: "'even'", odd: "'odd'"}}, 'ng-class' => "'state-{{order.state}}'"}
%td.align-center
{{::order.distributor_name}}
%td.align-center
= @show_only_completed ? '{{::order.completed_at}}' : '{{::order.created_at}}'
%td
%a{'ng-href' => '{{::order.show_path}}'}
{{order.number}}
%div{'ng-if' => 'order.special_instructions'}
%br
%span.icon-warning-sign{'ofn-with-tip' => "{{::order.special_instructions}}"}
= t('.note')
%td.align-center
%span.state{'ng-class' => 'order.state'}
{{'order_state.' + order.state | t}}
%td.align-center
%span.state{'ng-class' => 'order.payment_state', 'ng-if' => 'order.payment_state'}
%a{'ng-href' => '{{::order.payments_path}}' }
{{'payment_states.' + order.payment_state | t}}
%td.align-center
%span.state{'ng-class' => 'order.shipment_state', 'ng-if' => 'order.shipment_state'}
%a{'ng-href' => '{{::order.shipments_path}}' }
{{'shipment_states.' + order.shipment_state | t}}
%td
= mail_to "{{::order.email}}"
%td.align-center
%span{'ng-bind-html' => '::order.display_total'}
%td.actions
%a.icon_link.with-tip.icon-edit.no-text{'ng-href' => '{{::order.edit_path}}', 'data-action' => 'edit', 'ofn-with-tip' => t('.edit')}
%div{'ng-if' => 'order.ready_to_ship'}
%a.icon-road.icon_link.with-tip.no-text{'ng-href' => '{{::order.ship_path}}', 'data-action' => 'ship', 'data-confirm' => t(:are_you_sure), 'data-method' => 'put', rel: 'nofollow', 'ofn-with-tip' => t('.ship')}
%div{'ng-if' => 'order.payment_capture_path'}
%a.icon-capture.icon_link.no-text{'ng-href' => '{{::order.payment_capture_path}}', 'data-action' => 'capture', 'data-method' => 'put', rel: 'nofollow', 'ofn-with-tip' => t('.capture')}
= paginate @orders
.orders-loading{'ng-show' => 'RequestMonitor.loading'}
.row
.small-12.columns.fullwidth.text-center
%img.spinner{ src: "/assets/spinning-circles.svg" }
.row
.small-12.columns.fullwidth.text-center
%span= t('.loading')
%div{'ng-show' => "!RequestMonitor.loading && orders.length > 0" }
= render partial: 'admin/shared/angular_pagination'
.no-objects-found{'ng-show' => "!RequestMonitor.loading && orders.length == 0"}
= t('.no_orders_found')

View File

@@ -14,7 +14,7 @@
%div{"data-hook" => "admin_order_new_header"}
= render 'spree/shared/error_messages', :target => @order
%div{"ng-app" => "admin.orders", "ng-controller" => "ordersCtrl"}
%div{"ng-app" => "admin.orders", "ng-controller" => "orderCtrl"}
%div{"ng-show" => "distributionChosen()"}
= render 'add_product'

View File

@@ -14,7 +14,7 @@
%div{"data-hook" => "admin_order_new_header"}
= render 'spree/shared/error_messages', :target => @order
%div{"ng-app" => "admin.orders", "ng-controller" => "ordersCtrl"}
%div{"ng-app" => "admin.orders", "ng-controller" => "orderCtrl"}
= form_for @order, url: admin_order_url(@order), method: :put do |f|
= render 'spree/admin/orders/_form/distribution_fields'
-# This param passed to stop validation error in next page due to no line items in order yet:

View File

@@ -620,9 +620,6 @@ en:
controls:
back_to_my_inventory: Back to my inventory
orders:
index:
capture: "Capture"
ship: "Ship"
invoice_email_sent: 'Invoice email has been sent'
order_email_resent: 'Order email has been resent'
bulk_management:
@@ -2657,6 +2654,19 @@ See the %{link} to find out more about %{sitename}'s features and to start using
index:
inherits_properties_checkbox_hint: "Inherit properties from %{supplier}? (unless overridden above)"
orders:
index:
listing_orders: "Listing Orders"
new_order: "New Order"
capture: "Capture"
ship: "Ship"
edit: "Edit"
note: "Note"
first: "First"
last: "Last"
previous: "Previous"
next: "Next"
loading: "Loading"
no_orders_found: "No Orders Found"
invoice:
issued_on: Issued on
tax_invoice: TAX INVOICE

View File

@@ -66,26 +66,27 @@ describe Spree::Admin::OrdersController, type: :controller do
end
it "retrieves a list of orders with appropriate attributes, including line items with appropriate attributes" do
keys = json_response.first.keys.map{ |key| key.to_sym }
keys = json_response['orders'].first.keys.map{ |key| key.to_sym }
order_attributes.all?{ |attr| keys.include? attr }.should == true
end
it "sorts orders in ascending id order" do
ids = json_response.map{ |order| order['id'] }
ids[0].should < ids[1]
ids[1].should < ids[2]
it "sorts orders in descending id order" do
ids = json_response['orders'].map{ |order| order['id'] }
ids[0].should > ids[1]
ids[1].should > ids[2]
end
it "formats completed_at to 'yyyy-mm-dd hh:mm'" do
json_response.map{ |order| order['completed_at'] }.all?{ |a| a.match("^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$") }.should == true
pp json_response
json_response['orders'].map{ |order| order['completed_at'] }.all?{ |a| a == order1.completed_at.strftime('%B %d, %Y') }.should == true
end
it "returns distributor object with id key" do
json_response.map{ |order| order['distributor'] }.all?{ |d| d.has_key?('id') }.should == true
json_response['orders'].map{ |order| order['distributor'] }.all?{ |d| d.has_key?('id') }.should == true
end
it "retrieves the order number" do
json_response.map{ |order| order['number'] }.all?{ |number| number.match("^R\\d{5,10}$") }.should == true
json_response['orders'].map{ |order| order['number'] }.all?{ |number| number.match("^R\\d{5,10}$") }.should == true
end
end
@@ -120,7 +121,7 @@ describe Spree::Admin::OrdersController, type: :controller do
end
it "retrieves a list of orders" do
keys = json_response.first.keys.map{ |key| key.to_sym }
keys = json_response['orders'].first.keys.map{ |key| key.to_sym }
order_attributes.all?{ |attr| keys.include? attr }.should == true
end
end
@@ -132,7 +133,7 @@ describe Spree::Admin::OrdersController, type: :controller do
end
it "retrieves a list of orders" do
keys = json_response.first.keys.map{ |key| key.to_sym }
keys = json_response['orders'].first.keys.map{ |key| key.to_sym }
order_attributes.all?{ |attr| keys.include? attr }.should == true
end
end

View File

@@ -3,7 +3,7 @@ require "spec_helper"
feature %q{
As an administrator
I want to manage adjustments on orders
} do
}, js: true do
include AuthenticationWorkflow
include WebHelper
@@ -30,7 +30,7 @@ feature %q{
click_link 'New Adjustment'
fill_in 'adjustment_amount', with: 110
fill_in 'adjustment_label', with: 'Late fee'
select 'GST', from: 'tax_rate_id'
select2_select 'GST', from: 'tax_rate_id'
click_button 'Continue'
# Then I should see the adjustment, with the correct tax
@@ -51,11 +51,11 @@ feature %q{
page.find('tr', text: 'Shipping').find('a.icon-edit').click
# Then I should see the uneditable included tax and our tax rate as the default
page.should have_field :adjustment_included_tax, with: '10.00', disabled: true
page.should have_select :tax_rate_id, selected: 'GST'
expect(page).to have_field :adjustment_included_tax, with: '10.00', disabled: true
expect(page).to have_select2 :tax_rate_id, selected: 'GST'
# When I edit the adjustment, removing the tax
select 'Remove tax', from: :tax_rate_id
select2_select 'Remove tax', from: :tax_rate_id
click_button 'Continue'
# Then the adjustment tax should be cleared
@@ -75,11 +75,11 @@ feature %q{
page.find('tr', text: 'Shipping').find('a.icon-edit').click
# Then I should see the uneditable included tax and 'Remove tax' as the default tax rate
page.should have_field :adjustment_included_tax, with: '0.00', disabled: true
page.should have_select :tax_rate_id, selected: []
expect(page).to have_field :adjustment_included_tax, with: '0.00', disabled: true
expect(page).to have_select2 :tax_rate_id, selected: []
# When I edit the adjustment, setting a tax rate
select 'GST', from: :tax_rate_id
select2_select 'GST', from: :tax_rate_id
click_button 'Continue'
# Then the adjustment tax should be recalculated

View File

@@ -54,8 +54,8 @@ feature %q{
it "displays a column for order date" do
expect(page).to have_selector "th.date", text: "ORDER DATE", :visible => true
expect(page).to have_selector "td.date", text: o1.completed_at.strftime("%F %T"), :visible => true
expect(page).to have_selector "td.date", text: o2.completed_at.strftime("%F %T"), :visible => true
expect(page).to have_selector "td.date", text: o1.completed_at.strftime('%B %d, %Y'), :visible => true
expect(page).to have_selector "td.date", text: o2.completed_at.strftime('%B %d, %Y'), :visible => true
end
it "displays a column for producer" do

View File

@@ -109,7 +109,7 @@ feature %q{
quick_login_as_admin
visit '/admin/orders'
uncheck 'Only show complete orders'
click_button 'Filter Results'
page.find('a.icon-search').click
click_edit

View File

@@ -37,7 +37,7 @@ describe "LineItemsCtrl", ->
order = { id: 9, order_cycle: { id: 4 }, distributor: { id: 5 }, number: "R123456" }
lineItem = { id: 7, quantity: 3, order: { id: 9 }, supplier: { id: 1 } }
httpBackend.expectGET("/admin/orders.json?q%5Bcompleted_at_gteq%5D=SomeDate&q%5Bcompleted_at_lt%5D=SomeDate&q%5Bcompleted_at_not_null%5D=true&q%5Bstate_not_eq%5D=canceled").respond [order]
httpBackend.expectGET("/admin/orders.json?q%5Bcompleted_at_gteq%5D=SomeDate&q%5Bcompleted_at_lt%5D=SomeDate&q%5Bcompleted_at_not_null%5D=true&q%5Bstate_not_eq%5D=canceled").respond {orders: [order], pagination: {page: 1, pages: 1, results: 1}}
httpBackend.expectGET("/admin/bulk_line_items.json?q%5Border%5D%5Bcompleted_at_gteq%5D=SomeDate&q%5Border%5D%5Bcompleted_at_lt%5D=SomeDate&q%5Border%5D%5Bcompleted_at_not_null%5D=true&q%5Border%5D%5Bstate_not_eq%5D=canceled").respond [lineItem]
httpBackend.expectGET("/admin/enterprises/visible.json?ams_prefix=basic&q%5Bsells_in%5D%5B%5D=own&q%5Bsells_in%5D%5B%5D=any").respond [distributor]
httpBackend.expectGET("/admin/order_cycles.json?ams_prefix=basic&as=distributor&q%5Borders_close_at_gt%5D=SomeDate").respond [orderCycle]
@@ -68,7 +68,7 @@ describe "LineItemsCtrl", ->
describe "initialisation", ->
it "gets suppliers", ->
expect(scope.suppliers).toDeepEqual [supplier ]
expect(scope.suppliers).toDeepEqual [ supplier ]
it "gets distributors", ->
expect(scope.distributors).toDeepEqual [ distributor ]

View File

@@ -0,0 +1,40 @@
describe "orderCtrl", ->
ctrl = null
scope = {}
attrs = {}
shops = []
orderCycles = [
{id: 10, name: 'Ten', status: 'open', distributors: [{id: 1, name: 'One'}]}
{id: 20, name: 'Twenty', status: 'closed', distributors: [{id: 2, name: 'Two', status: 'closed'}]}
]
beforeEach ->
scope = {}
module 'admin.orders'
inject ($controller) ->
ctrl = $controller 'orderCtrl', {$scope: scope, $attrs: attrs, shops: shops, orderCycles: orderCycles}
it "initialises name_and_status", ->
expect(scope.orderCycles[0].name_and_status).toEqual "Ten (open)"
expect(scope.orderCycles[1].name_and_status).toEqual "Twenty (closed)"
describe "finding valid order cycles for a distributor", ->
order_cycle = {id: 10, distributors: [{id: 1, name: 'One'}]}
it "returns true when the order cycle includes the distributor", ->
scope.distributor_id = '1'
expect(scope.validOrderCycle(order_cycle, 1, [order_cycle])).toBe true
it "returns false otherwise", ->
scope.distributor_id = '2'
expect(scope.validOrderCycle(order_cycle, 1, [order_cycle])).toBe false
describe "checking if a distributor has order cycles", ->
it "returns true when it does", ->
distributor = {id: 1}
expect(scope.distributorHasOrderCycles(distributor)).toBe true
it "returns false otherwise", ->
distributor = {id: 3}
expect(scope.distributorHasOrderCycles(distributor)).toBe false

View File

@@ -1,40 +1,37 @@
describe "ordersCtrl", ->
ctrl = null
scope = {}
attrs = {}
shops = []
orderCycles = [
{id: 10, name: 'Ten', status: 'open', distributors: [{id: 1, name: 'One'}]}
{id: 20, name: 'Twenty', status: 'closed', distributors: [{id: 2, name: 'Two', status: 'closed'}]}
Orders = null
$scope = null
orders = [
{ id: 8, order_cycle: { id: 4 }, distributor: { id: 5 }, number: "R123456" }
{ id: 9, order_cycle: { id: 5 }, distributor: { id: 7 }, number: "R213776" }
]
form = {
q: {
created_at_lt: ''
created_at_gt: ''
completed_at_not_null: true
}
}
beforeEach ->
scope = {}
module 'admin.orders'
inject ($controller, $rootScope, RequestMonitor, SortOptions) ->
$scope = $rootScope.$new()
Orders =
index: jasmine.createSpy('index').and.returnValue(orders)
all: orders
ctrl = $controller 'ordersCtrl', { $scope: $scope, RequestMonitor: RequestMonitor, SortOptions: SortOptions, Orders: Orders }
$scope.q = form.q
module('admin.orders')
inject ($controller) ->
ctrl = $controller 'ordersCtrl', {$scope: scope, $attrs: attrs, shops: shops, orderCycles: orderCycles}
describe "initialising the controller", ->
it "fetches orders", ->
$scope.initialise()
expect(Orders.index).toHaveBeenCalled()
expect($scope.orders).toEqual orders
it "initialises name_and_status", ->
expect(scope.orderCycles[0].name_and_status).toEqual "Ten (open)"
expect(scope.orderCycles[1].name_and_status).toEqual "Twenty (closed)"
describe "finding valid order cycles for a distributor", ->
order_cycle = {id: 10, distributors: [{id: 1, name: 'One'}]}
it "returns true when the order cycle includes the distributor", ->
scope.distributor_id = '1'
expect(scope.validOrderCycle(order_cycle, 1, [order_cycle])).toBe true
it "returns false otherwise", ->
scope.distributor_id = '2'
expect(scope.validOrderCycle(order_cycle, 1, [order_cycle])).toBe false
describe "checking if a distributor has order cycles", ->
it "returns true when it does", ->
distributor = {id: 1}
expect(scope.distributorHasOrderCycles(distributor)).toBe true
it "returns false otherwise", ->
distributor = {id: 3}
expect(scope.distributorHasOrderCycles(distributor)).toBe false
describe "using pagination", ->
it "changes the page", ->
$scope.changePage(2)
expect($scope.page).toEqual 2
expect(Orders.index).toHaveBeenCalled()

View File

@@ -18,20 +18,20 @@ describe "Orders service", ->
result = response = null
beforeEach ->
response = [{ id: 5, name: 'Order 1'}]
response = { orders: [{ id: 5, name: 'Order 1'}], pagination: {page: 1, pages: 1, results: 1} }
$httpBackend.expectGET('/admin/orders.json').respond 200, response
result = Orders.index()
$httpBackend.flush()
it "stores returned data in @byID, with ids as keys", ->
# OrderResource returns instances of Resource rather than raw objects
expect(Orders.byID).toDeepEqual { 5: response[0] }
expect(Orders.byID).toDeepEqual { 5: response.orders[0] }
it "stores returned data in @pristineByID, with ids as keys", ->
expect(Orders.pristineByID).toDeepEqual { 5: response[0] }
expect(Orders.pristineByID).toDeepEqual { 5: response.orders[0] }
it "returns an array of orders", ->
expect(result).toDeepEqual response
expect(result).toDeepEqual response.orders
describe "#save", ->