Commit d663bb672ef8861404f5d2b1d9b025aeaf7dc3cd

Authored by Braulio Bhavamitra
1 parent 0f263193

Update delivery, orders, orders_cycle, suppliers and shopping_cart to rails4

Showing 113 changed files with 1592 additions and 560 deletions   Show diff stats
plugins/delivery/controllers/myprofile/delivery_plugin/admin_method_controller.rb
... ... @@ -14,7 +14,7 @@ class DeliveryPlugin::AdminMethodController < MyProfileController
14 14  
15 15 def edit
16 16 @delivery_method ||= profile.delivery_methods.find_by_id params[:id]
17   - if params[:delivery_method].present? and @delivery_method.update_attributes params[:delivery_method]
  17 + if params[:delivery_method].present? and @delivery_method.update params[:delivery_method]
18 18 render partial: 'list'
19 19 else
20 20 render partial: 'edit', locals: {delivery_method: @delivery_method}
... ...
plugins/delivery/controllers/myprofile/delivery_plugin/admin_options_controller.rb
... ... @@ -8,7 +8,13 @@ class DeliveryPlugin::AdminOptionsController < DeliveryPlugin::AdminMethodContro
8 8 before_filter :load_owner
9 9  
10 10 def select
  11 + end
11 12  
  13 + def select_all
  14 + missing_methods = profile.delivery_methods - @owner.delivery_methods
  15 + missing_methods.each do |dm|
  16 + DeliveryPlugin::Option.create! owner_id: @owner.id, owner_type: @owner.class.name, delivery_method: dm
  17 + end
12 18 end
13 19  
14 20 def new
... ...
plugins/delivery/db/migrate/20150623125525_add_distribution_margin_to_delivery_plugin_method.rb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +class AddDistributionMarginToDeliveryPluginMethod < ActiveRecord::Migration
  2 +
  3 + def change
  4 + add_column :delivery_plugin_methods, :distribution_margin_fixed, :decimal
  5 + add_column :delivery_plugin_methods, :distribution_margin_percentage, :decimal
  6 + end
  7 +
  8 +end
... ...
plugins/delivery/lib/delivery_plugin/display_helper.rb
... ... @@ -9,15 +9,24 @@ module DeliveryPlugin::DisplayHelper
9 9 methods = options[:methods] || profile.delivery_methods
10 10  
11 11 options = methods.map do |method|
12   - cost = if method.fixed_cost.present? and method.fixed_cost > 0 then float_to_currency_cart(method.fixed_cost, environment) else nil end
  12 + cost = if method.fixed_cost.present? and method.fixed_cost > 0 then method.fixed_cost_as_currency else nil end
13 13 text = if cost.present? then "#{method.name} (#{cost})" else method.name end
14 14  
15 15 content_tag :option, text, value: method.id,
16   - data: {label: method.name, type: method.delivery_type, instructions: method.description.to_s},
17   - selected: if method == selected then 'selected' else nil end
  16 + data: {label: method.name, type: method.delivery_type, instructions: CGI::escapeHTML(method.description.to_s)},
  17 + selected: if method.id == selected then 'selected' else nil end
18 18 end.join
19 19 end
20 20  
  21 + def consumer_delivery_field_value order, field
  22 + # BLACK OR WHITE: do not mix existing delivery data with user's location
  23 + if order.consumer_delivery_data.present?
  24 + order.consumer_delivery_data[field]
  25 + elsif user
  26 + user.send field
  27 + end
  28 + end
  29 +
21 30 def delivery_context
22 31 @delivery_context || 'delivery_plugin/admin_method'
23 32 end
... ...
plugins/delivery/lib/ext/profile.rb
1 1 require_dependency 'profile'
2 2  
3   -class Profile
  3 +([Profile] + Profile.descendants).each do |subclass|
  4 +subclass.class_eval do
4 5  
5   - has_many :delivery_methods, class_name: 'DeliveryPlugin::Method', foreign_key: :profile_id, dependent: :destroy, order: 'id ASC'
  6 + has_many :delivery_methods, -> { order 'id ASC' }, class_name: 'DeliveryPlugin::Method', foreign_key: :profile_id, dependent: :destroy
6 7  
7 8 end
  9 +end
... ...
plugins/delivery/locales/en-US.yml
... ... @@ -12,16 +12,23 @@
12 12 delivery_type: 'Type'
13 13 delivery_type_help: 'Pickup: the products will be delivered in the consumption place.<br>Delivery: the products will be delivered in the address asked by the consumer.'
14 14 name: Name
15   - name_help: Write the name of the Consumption Place or the
16   - fixed_cost: Fixed cost
17   - free_over_price: Order's minimum price for free delivery
  15 + name_help: Write the name of the Consumption Place or the
  16 +
  17 + costs_legend: Delivery's costs
  18 + fixed_cost: Fixed cost of delivery
  19 + free_over_price: Order's minimum value for free delivery
  20 +
  21 + distribution_legend: Distribution's costs
  22 + distribution_margin_percentage: Distribution's margin
  23 + distribution_margin_fixed: Distribution's margin (fixed)
  24 +
18 25 instructions: Description
19 26 instructions_help: "Write the address and other important informations about the place of this consumption place.<br> This text will be available"
20 27 views:
21   - delivery_option:
22   - select:
  28 + delivery_option:
  29 + select:
23 30 add: Add selected
24   - _select_content:
  31 + _select_content:
25 32 add: Add option
26 33 add_new: "add new"
27 34 are_you_sure_you_want: "Are you sure you want to remove?"
... ... @@ -29,14 +36,14 @@
29 36 choose_a_delivery_met: "Choose a delivery method from the list"
30 37 edit_this: "Edit"
31 38 remove_method: "Remove"
32   - _show:
  39 + _show:
33 40 x: X
34   - select:
  41 + select:
35 42 add_a_delivery_method: "Add a delivery method to the Orders' Cycle"
36 43 method:
37 44 index:
38 45 new: "New delivery or pickup"
39   - edit:
  46 + edit:
40 47 add: Add
41 48 back: back
42 49 save: Save
... ...
plugins/delivery/locales/pt-BR.yml
... ... @@ -13,15 +13,21 @@
13 13 delivery_type_help: "Retirada: os produtos serão buscados pela(o) consumidor(a), por exemplo, no empreendimento, numa feira ou num Núcleo de Consumo.<br>Entrega: os produtos serão entregues no endereço solicitado pelo(a) consumidor(a)."
14 14 name: Nome
15 15 name_help: "Para opção Retirada: escreva o nome do local onde os produtos deverão ser retirados pela(o) consumidor(a), por exemplo o nome do Núcleo de Consumo, da feira ou do próprio empreendimento.<br>Para a opção Entrega: escreva o nome do local, (uma cidade ou região) ou a forma de entrega (Correios, transportadora)"
16   - fixed_cost: Custo
17   - free_over_price: "Preço mínimo do pedido para Entrega Grátis"
  16 +
  17 + costs_legend: Custos de entrega
  18 + fixed_cost: Custo de entrega
  19 + free_over_price: "Valor mínimo do pedido para Entrega Grátis"
  20 + distribution_legend: Custos de distribuição
  21 + distribution_margin_percentage: Margem de distribuição
  22 + distribution_margin_fixed: Margem de distribuição (valor fixo)
  23 +
18 24 instructions: Descrição
19 25 instructions_help: "Escreva informações importantes sobre esta opção de Retirada ou Entrega.<br>Por exemplo, o endereço completo do Núcleo de Consumo, a abrangência da região de entrega.<br> Este texto estará disponível no \"Finalizar pedido\" e, ao confirmar a compra, o(a) consumidor(a) receberá um email com o pedido realizado e as informações deste campo."
20 26 views:
21   - delivery_option:
22   - select:
  27 + delivery_option:
  28 + select:
23 29 add: "Adicionar selecionados"
24   - _select_content:
  30 + _select_content:
25 31 add: Adicionar opção
26 32 add_new: "adicionar novo"
27 33 are_you_sure_you_want: "Tem certeza de que quer remover?"
... ... @@ -29,14 +35,14 @@
29 35 choose_a_delivery_met: "Escolha um método de entrega da lista"
30 36 edit_this: Editar
31 37 remove_method: "Remover"
32   - _show:
  38 + _show:
33 39 x: X
34   - select:
  40 + select:
35 41 add_a_delivery_method: "Adicionar um método de entrega ao ciclo de pedidos"
36 42 method:
37 43 index:
38 44 new: "Nova forma de entrega ou retirada"
39   - edit:
  45 + edit:
40 46 add: Adicionar
41 47 back: voltar
42 48 save: Salvar
... ...
plugins/delivery/models/delivery_plugin/method.rb
1 1 class DeliveryPlugin::Method < ActiveRecord::Base
2 2  
3   - extend CurrencyHelper::ClassMethods
4   -
5 3 Types = ['pickup', 'deliver']
6 4  
7 5 # see also: Profile::LOCATION_FIELDS
... ... @@ -10,7 +8,7 @@ class DeliveryPlugin::Method &lt; ActiveRecord::Base
10 8 ].map(&:to_sym)
11 9  
12 10 attr_accessible :profile, :delivery_type, :name, :description,
13   - :fixed_cost, :free_over_price
  11 + :fixed_cost, :free_over_price, :distribution_margin_percentage, :distribution_margin_fixed
14 12  
15 13 belongs_to :profile
16 14  
... ... @@ -20,8 +18,14 @@ class DeliveryPlugin::Method &lt; ActiveRecord::Base
20 18 validates_presence_of :name
21 19 validates_inclusion_of :delivery_type, in: Types
22 20  
23   - scope :pickup, conditions: {delivery_type: 'pickup'}
24   - scope :delivery, conditions: {delivery_type: 'deliver'}
  21 + scope :pickup, -> { where delivery_type: 'pickup' }
  22 + scope :delivery, -> { where delivery_type: 'deliver'}
  23 +
  24 + extend CurrencyHelper::ClassMethods
  25 + has_currency :fixed_cost
  26 + has_currency :free_over_price
  27 + has_currency :distribution_margin_percentage
  28 + has_currency :distribution_margin_fixed
25 29  
26 30 def pickup?
27 31 self.delivery_type == 'pickup'
... ... @@ -30,22 +34,40 @@ class DeliveryPlugin::Method &lt; ActiveRecord::Base
30 34 self.delivery_type == 'deliver'
31 35 end
32 36  
33   - def has_cost? order_price=nil
  37 + def has_distribution_margin?
  38 + (self.distribution_margin_percentage.present? and self.distribution_margin_percentage.nonzero?) or
  39 + (self.distribution_margin_fixed.present? and self.distribution_margin_fixed.nonzero?)
  40 + end
  41 +
  42 + def has_fixed_cost? order_price=nil
34 43 if order_price.present? and order_price.nonzero? and self.free_over_price.present? and self.free_over_price.nonzero?
35 44 order_price <= self.free_over_price
36 45 else
37 46 self.fixed_cost.present? and self.fixed_cost.nonzero?
38 47 end
39 48 end
  49 +
  50 + def distribution_margin order_price
  51 + value = 0
  52 + value += self.distribution_margin_fixed if self.distribution_margin_fixed.present?
  53 + value += order_price * (self.distribution_margin_percentage/100) if self.distribution_margin_percentage.present?
  54 + value
  55 + end
  56 +
  57 + def has_cost? order_price=nil
  58 + has_cost = self.has_distribution_margin?
  59 + has_cost ||= self.has_fixed_cost? order_price
  60 + end
40 61 def free? order_price=nil
41 62 !self.has_cost?
42 63 end
43 64  
44 65 def cost order_price=nil
45   - if self.has_cost?(order_price) then self.fixed_cost.to_f else 0 end
  66 + value = 0
  67 + value += self.fixed_cost if self.has_fixed_cost? order_price
  68 + value += self.distribution_margin order_price if self.has_distribution_margin?
  69 + value
46 70 end
47   - has_currency :fixed_cost
48   - has_currency :free_over_price
49 71 has_currency :cost
50 72  
51 73 protected
... ...
plugins/delivery/public/stylesheets/delivery.scss
... ... @@ -3,7 +3,7 @@
3 3 #delivery-method-edition {
4 4 @extend .container-clean;
5 5  
6   - .field-help {
  6 + .help-block {
7 7 font-size: 10px;
8 8 }
9 9 }
... ...
plugins/delivery/test/test_helper.rb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +require File.dirname(__FILE__) + '/../../../test/test_helper'
... ...
plugins/delivery/test/unit/delivery_plugin/method_test.rb 0 → 100644
... ... @@ -0,0 +1,42 @@
  1 +require "#{File.dirname(__FILE__)}/../../test_helper"
  2 +
  3 +class DeliveryPlugin::MethodTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @profile = build(Profile)
  7 + end
  8 +
  9 + attr_accessor :profile
  10 +
  11 + should 'have a name and a delivery type' do
  12 + dm = DeliveryPlugin::Method.new :name => 'Delivery Deluxe', :delivery_type => 'deliver', :profile => profile
  13 + assert dm.valid?
  14 + dm = DeliveryPlugin::Method.new :profile => profile
  15 + assert !dm.valid?
  16 + end
  17 +
  18 + should 'accept only pickup and deliver as delivery types' do
  19 + dm = build(DeliveryPlugin::Method, :name => 'Delivery Deluxe', :delivery_type => 'unkown', :profile => profile)
  20 + assert !dm.valid?
  21 + dm = build(DeliveryPlugin::Method, :name => 'Delivery Deluxe', :delivery_type => 'pickup', :profile => profile)
  22 + assert dm.valid?
  23 + dm = build(DeliveryPlugin::Method, :name => 'Delivery Deluxe', :delivery_type => 'deliver', :profile => profile)
  24 + assert dm.valid?
  25 + end
  26 +
  27 + should 'filter by delivery types' do
  28 + dm_deliver = create(DeliveryPlugin::Method, :name => 'Delivery Deluxe', :delivery_type => 'deliver', :profile => profile)
  29 + dm_pickup = create(DeliveryPlugin::Method, :name => 'Delivery Deluxe', :delivery_type => 'pickup', :profile => profile)
  30 + assert_equal [dm_deliver], DeliveryPlugin::Method.delivery
  31 + assert_equal [dm_pickup], DeliveryPlugin::Method.pickup
  32 + end
  33 +
  34 + should 'have many delivery options' do
  35 + dm = create(DeliveryPlugin::Method, :name => 'Delivery Deluxe', :delivery_type => 'deliver', :profile => profile)
  36 + cycle = build(OrdersCyclePlugin::Cycle, :name => 'cycle name', :profile => profile)
  37 + option = create(DeliveryPlugin::Option, :cycle => cycle, :delivery_method => dm)
  38 +
  39 + assert_equal [option], dm.reload.delivery_options
  40 + end
  41 +
  42 +end
... ...
plugins/delivery/test/unit/delivery_plugin/option_test.rb 0 → 100644
... ... @@ -0,0 +1,24 @@
  1 +require "#{File.dirname(__FILE__)}/../../test_helper"
  2 +
  3 +class DeliveryPlugin::OptionTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @profile = build(Profile)
  7 + @cycle = build(OrdersCyclePluginCycle, :profile => @profile)
  8 + @delivery_method = build(OrdersCyclePluginMethod, :profile => @profile)
  9 + end
  10 +
  11 + attr_accessor :profile
  12 + attr_accessor :cycle
  13 + attr_accessor :delivery_method
  14 +
  15 + should 'be associated with a cycle and a delivery method' do
  16 + option = OrdersCyclePluginOption.new :cycle => @cycle, :delivery_method => @delivery_method
  17 + assert option.valid?
  18 + option = OrdersCyclePluginOption.new
  19 + :wa
  20 +
  21 + assert !option.valid?
  22 + end
  23 +
  24 +end
... ...
plugins/delivery/views/delivery_plugin/_order_select.html.slim
... ... @@ -2,30 +2,37 @@
2 2 - edition = true if edition.nil?
3 3 - readonly = !edition
4 4  
5   -div.order-delivery-select
  5 +div.order-delivery-select id='order-#{order.id}'
6 6  
7 7 div.supplier-delivery-data
8   - = labelled_form_field _('Option'),
9   - f.select(:supplier_delivery_id, supplier_delivery_options(methods: methods), {}, disabled: readonly,
10   - onchange: 'delivery.order.select.onChange(this)', onkeyup: 'delivery.order.select.onChange(this)')
11   - p.instructions
  8 + - if order.delivery_methods.size > 0
  9 + = labelled_form_field _('Option'),
  10 + f.select(:supplier_delivery_id, supplier_delivery_options(methods: methods, selected: order.supplier_delivery_id), {}, disabled: readonly,
  11 + onchange: 'delivery.order.select.onChange(this)', onkeyup: 'delivery.order.select.onChange(this)') + content_tag('div', '', class: 'help-block instructions')
  12 + - elsif (name = consumer_delivery_field_value order, :name).present?
  13 + = labelled_form_field _('Option'),
  14 + f.text_field(:address, value: name, readonly: true)
  15 + div class='help-block instructions' = h(consumer_delivery_field_value order, :description)
12 16  
13   - div.consumer-delivery-data
14   - = f.fields_for :consumer_delivery_data, order.consumer_delivery_data do |ff|
15   - = labelled_form_field _('Address (street and number)'),
16   - ff.text_field(:address, value: order.consumer_delivery_data[:address], readonly: readonly)
17   - = labelled_form_field _('Address completion'),
18   - ff.text_field(:address_line2, value: order.consumer_delivery_data[:address_line2], readonly: readonly)
19   - = labelled_form_field _('Address reference'),
20   - ff.text_field(:address_reference, value: order.consumer_delivery_data[:address_reference], readonly: readonly)
21   - = labelled_form_field _('District'),
22   - ff.text_field(:district, value: order.consumer_delivery_data[:district], readonly: readonly)
23   - = labelled_form_field _('City'),
24   - ff.text_field(:city, value: order.consumer_delivery_data[:city], readonly: readonly)
25   - = labelled_form_field _('State'),
26   - ff.text_field(:state, value: order.consumer_delivery_data[:state], readonly: readonly)
27   - = labelled_form_field _('ZIP code'),
28   - ff.text_field(:zip_code, value: order.consumer_delivery_data[:zip_code], readonly: readonly)
  17 + / do not render on confirmation email (@view) if supplier_delivery is pickup
  18 + - unless (@view and order.supplier_delivery_data[:delivery_type] == 'pickup') or order.delivery_methods.size == 0
  19 + div.consumer-delivery-data
  20 + = f.fields_for :consumer_delivery_data do |ff|
  21 + = labelled_form_field _('Address (street and number)'),
  22 + ff.text_field(:address, value: consumer_delivery_field_value(order, :address), readonly: readonly)
  23 + = labelled_form_field _('Address completion'),
  24 + ff.text_field(:address_line2, value: consumer_delivery_field_value(order, :address_line2), readonly: readonly)
  25 + = labelled_form_field _('Address reference'),
  26 + ff.text_field(:address_reference, value: consumer_delivery_field_value(order, :address_reference), readonly: readonly)
  27 + = labelled_form_field _('District'),
  28 + ff.text_field(:district, value: consumer_delivery_field_value(order, :district), readonly: readonly)
  29 + = labelled_form_field _('City'),
  30 + ff.text_field(:city, value: consumer_delivery_field_value(order, :city), readonly: readonly)
  31 + = labelled_form_field _('State'),
  32 + ff.text_field(:state, value: consumer_delivery_field_value(order, :state), readonly: readonly)
  33 + = labelled_form_field _('ZIP code'),
  34 + ff.text_field(:zip_code, value: consumer_delivery_field_value(order, :zip_code), readonly: readonly)
  35 +
  36 +javascript:
  37 + delivery.order.select.onChange($('.order-delivery-select#order-#{order.id} #order_supplier_delivery_id'))
29 38  
30   - javascript:
31   - delivery.order.select.onChange($('#order_supplier_delivery_id'))
... ...
plugins/delivery/views/delivery_plugin/admin_method/_edit.html.slim
... ... @@ -12,15 +12,27 @@
12 12 = labelled_field f, :name, t('delivery_plugin.models.method.name'), f.text_field(:name),
13 13 help: t('delivery_plugin.models.method.name_help')
14 14 = labelled_field f, :description, t('delivery_plugin.models.method.instructions'),
15   - f.text_area(:description, rows: 5), help: t('delivery_plugin.models.method.instructions_help')
  15 + f.text_area(:description, rows: 5, class: 'mceEditor'), help: t('delivery_plugin.models.method.instructions_help')
16 16  
17   - = labelled_field f, :fixed_cost, t('delivery_plugin.models.method.fixed_cost'),
18   - input_group_addon(environment.currency_unit){ f.text_field :fixed_cost, type: :number, step: '0.01', value: number_with_precision(delivery_method.fixed_cost, precision: 2, locale: :en)}
  17 + fieldset
  18 + legend= t'delivery_plugin.models.method.costs_legend'
  19 + = labelled_field f, :fixed_cost, t('delivery_plugin.models.method.fixed_cost'),
  20 + input_group_addon(environment.currency_unit){ f.text_field :fixed_cost, type: :number, step: 'any', value: number_with_precision(delivery_method.fixed_cost, precision: 2, locale: :en)}
19 21  
20   - = labelled_field f, :free_over_price, t('delivery_plugin.models.method.free_over_price'),
21   - input_group_addon(environment.currency_unit){ f.text_field :free_over_price, type: :number, step: '0.01', value: number_with_precision(delivery_method.free_over_price, precision: 2, locale: :en)}
  22 + = labelled_field f, :free_over_price, t('delivery_plugin.models.method.free_over_price'),
  23 + input_group_addon(environment.currency_unit){ f.text_field :free_over_price, type: :number, step: 'any', value: number_with_precision(delivery_method.free_over_price, precision: 2, locale: :en)}
  24 +
  25 + fieldset
  26 + legend= t'delivery_plugin.models.method.distribution_legend'
  27 + = labelled_field f, :distribution_margin_percentage, t('delivery_plugin.models.method.distribution_margin_percentage'),
  28 + input_group_addon('%'){ f.text_field :distribution_margin_percentage, type: :number, step: 'any', value: number_with_precision(delivery_method.distribution_margin_percentage, precision: 2, locale: :en)}
  29 +
  30 + = labelled_field f, :distribution_margin_fixed, t('delivery_plugin.models.method.distribution_margin_fixed'),
  31 + input_group_addon(environment.currency_unit){ f.text_field :distribution_margin_fixed, type: :number, step: 'any', value: number_with_precision(delivery_method.distribution_margin_fixed, precision: 2, locale: :en)}
22 32  
23 33 div
24 34 = submit_button :save, if delivery_method.new_record? then t('delivery_plugin.views.method.edit.add') else t('delivery_plugin.views.method.edit.save') end
25 35 = link_to_function t('delivery_plugin.views.method.edit.back'), "delivery.method.view.toggle()"
26 36  
  37 += render file: 'shared/tiny_mce', locals: {mode: 'simple'}
  38 +
... ...
plugins/delivery/views/delivery_plugin/admin_method/_show.html.slim
1   -td.name title="#{method.description}"
  1 +td.name title="#{strip_tags method.description}"
2 2 = method.name
3 3 td.actions
4 4 - if request.GET[:select_ids].present? and not method.id.to_s.in? Array(request.GET[:selected_ids])
... ...
plugins/delivery/views/delivery_plugin/admin_options/_new.js.erb
1   -// FIXME: move to orders_cycle plugin
2   -jQuery('#cycle-delivery-options').html('<%= j render('index', owner: @owner) %>');
  1 +// FIXME: use generic class
  2 +$('#cycle-delivery-options').html('<%= j render('index', owner: @owner) %>');
3 3 noosfero.modal.close()
4 4  
... ...
plugins/delivery/views/delivery_plugin/admin_options/select_all.js.erb 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +// FIXME: use generic class
  2 +$('#cycle-delivery-options').html('<%= j render('index', owner: @owner) %>');
  3 +
... ...
plugins/orders/controllers/myprofile/orders_plugin_admin_controller.rb
... ... @@ -43,8 +43,8 @@ class OrdersPluginAdminController &lt; MyProfileController
43 43 @orders_method = if @actor_name == :supplier then :sales else :purchases end
44 44  
45 45 @order = profile.send(@orders_method).find params[:id]
46   - return render_access_denied unless @order.verify_actor? profile, @actor_name
47   - @order.update_attributes params[:order]
  46 + return render_access_denied unless @user_is_admin or @order.verify_actor? profile, @actor_name
  47 + @order.update params[:order]
48 48  
49 49 respond_to do |format|
50 50 format.js{ render 'orders_plugin_admin/edit' }
... ...
plugins/orders/controllers/myprofile/orders_plugin_admin_item_controller.rb
... ... @@ -15,7 +15,7 @@ class OrdersPluginAdminItemController &lt; MyProfileController
15 15 @actor_name = params[:actor_name].to_sym
16 16 @order = if @actor_name == :consumer then @item.purchase else @item.sale end
17 17  
18   - @item.update_attributes! params[:item]
  18 + @item.update! params[:item]
19 19 end
20 20  
21 21 def add_search
... ...
plugins/orders/controllers/myprofile/orders_plugin_item_controller.rb
... ... @@ -23,7 +23,7 @@ class OrdersPluginItemController &lt; MyProfileController
23 23  
24 24 if params[:item].present? and set_quantity_consumer_ordered params[:item][:quantity_consumer_ordered]
25 25 params[:item][:quantity_consumer_ordered] = @quantity_consumer_ordered
26   - @item.update_attributes! params[:item]
  26 + @item.update! params[:item]
27 27 end
28 28 end
29 29  
... ...
plugins/orders/controllers/profile/orders_plugin_order_controller.rb
... ... @@ -8,6 +8,7 @@ class OrdersPluginOrderController &lt; ProfileController
8 8 before_filter :load_order, except: [:new]
9 9 before_filter :check_access, only: [:confirm, :remove, :cancel]
10 10 before_filter :set_actor_name
  11 + before_filter :disable_purechat
11 12  
12 13 helper OrdersPlugin::TranslationHelper
13 14 helper OrdersPlugin::DisplayHelper
... ... @@ -19,7 +20,7 @@ class OrdersPluginOrderController &lt; ProfileController
19 20  
20 21 def load_order
21 22 @order = hmvc_orders_context::Sale.find_by_id params[:id]
22   - render_access_denied if @order.present? and not @order.may_view? user
  23 + render_access_denied if @order.present? and (not @user_is_admin or not @order.may_view? user)
23 24 end
24 25  
25 26 def check_access access = 'view'
... ... @@ -40,4 +41,8 @@ class OrdersPluginOrderController &lt; ProfileController
40 41 extend HMVC::ClassMethods
41 42 hmvc OrdersPlugin, orders_context: OrdersPlugin
42 43  
  44 + def disable_purechat
  45 + @disable_purechat = true
  46 + end
  47 +
43 48 end
... ...
plugins/orders/db/migrate/20150627232432_add_status_to_orders_plugin_item.rb 0 → 100644
... ... @@ -0,0 +1,15 @@
  1 +class AddStatusToOrdersPluginItem < ActiveRecord::Migration
  2 +
  3 + def change
  4 + add_column :orders_plugin_items, :status, :string
  5 + add_column :orders_plugin_orders, :building_next_status, :boolean
  6 +
  7 + say_with_time "filling items' statuses..." do
  8 + OrdersPlugin::Item.includes(:order).find_each batch_size: 50 do |item|
  9 + next item.destroy if item.order.nil?
  10 + item.fill_status
  11 + end
  12 + end
  13 + end
  14 +
  15 +end
... ...
plugins/orders/lib/code_numbering.rb 0 → 100644
... ... @@ -0,0 +1,58 @@
  1 +module CodeNumbering
  2 + module ClassMethods
  3 + def code_numbering field, options = {}
  4 + class_attribute :code_numbering_field
  5 + class_attribute :code_numbering_options
  6 +
  7 + self.code_numbering_field = field
  8 + self.code_numbering_options = options
  9 +
  10 + before_create :create_code_numbering
  11 +
  12 + include CodeNumbering::InstanceMethods
  13 + end
  14 + end
  15 +
  16 + module InstanceMethods
  17 +
  18 + def code
  19 + self.attributes[self.code_numbering_field.to_s]
  20 + end
  21 +
  22 + def code_scope
  23 + scope = self.code_numbering_options[:scope]
  24 + case scope
  25 + when Symbol
  26 + self.send scope
  27 + when Proc
  28 + instance_exec &scope
  29 + else
  30 + self.class
  31 + end
  32 + end
  33 +
  34 + def code_maximum
  35 + self.code_scope.maximum(self.code_numbering_field) || 0
  36 + end
  37 +
  38 + def create_code_numbering
  39 + max = self.code_numbering_options[:start].to_i - 1 if self.code_numbering_options[:start]
  40 + max = self.code_maximum
  41 + self.send "#{self.code_numbering_field}=", max+1
  42 + end
  43 +
  44 + def reset_scope_code_numbering
  45 + max = self.code_numbering_options[:start].to_i - 1 if self.code_numbering_options[:start]
  46 + max ||= 1
  47 +
  48 + self.code_scope.order(:created_at).each do |record|
  49 + record.update_column self.code_numbering_field, max
  50 + max += 1
  51 + end
  52 + self.reload
  53 + end
  54 +
  55 + end
  56 +end
  57 +
  58 +ActiveRecord::Base.extend CodeNumbering::ClassMethods
... ...
plugins/orders/lib/ext/profile.rb
1 1 require_dependency 'profile'
  2 +require_dependency 'community'
2 3  
3   -# FIXME move to core
4   -class Profile
5   -
6   - def has_admin? person
7   - return unless person
8   - person.has_permission? 'edit_profile', self
9   - end
10   -
11   -end
12   -
13   -class Profile
  4 +([Profile] + Profile.descendants).each do |subclass|
  5 +subclass.class_eval do
14 6  
15 7 # cannot use :order because of months/years named_scope
16 8 has_many :orders, class_name: 'OrdersPlugin::Sale', foreign_key: :profile_id
17 9 has_many :sales, class_name: 'OrdersPlugin::Sale', foreign_key: :profile_id
18 10 has_many :purchases, class_name: 'OrdersPlugin::Purchase', foreign_key: :consumer_id
19 11  
20   - has_many :ordered_items, through: :orders, source: :items, order: 'name ASC'
  12 + has_many :ordered_items, -> { order 'name ASC' }, through: :orders, source: :items
21 13  
22 14 has_many :sales_consumers, through: :sales, source: :consumer
23   - has_many :purchases_consumers, through: :purchases, source: :consumer
  15 + has_many :purchases_consumers, through: :sales, source: :consumer
24 16  
25 17 has_many :sales_profiles, through: :sales, source: :profile
26   - has_many :purchases_profiles, through: :purchases, source: :profile
  18 + has_many :purchases_profiles, through: :sales, source: :profile
  19 +
  20 +end
  21 +end
  22 +
  23 +class Profile
  24 +
  25 + # FIXME move to core
  26 + def has_admin? person
  27 + return unless person
  28 + person.has_permission? 'edit_profile', self
  29 + end
27 30  
28 31 def sales_all_consumers
29 32 consumers = self.sales_consumers.order 'name ASC'
30   - consumers.concat self.consumers.except_self.order('name ASC') if self.respond_to? :consumers
  33 + consumers.concat self.suppliers.except_self.order('name ASC') if self.respond_to? :suppliers
31 34 consumers.uniq
32 35 end
33   - def purchases_all_suppliers
34   - suppliers = self.purchases_profiles.order 'name ASC'
35   - suppliers.concat self.suppliers.except_self.order('name ASC') if self.respond_to? :suppliers
36   - suppliers.uniq
  36 + def purchases_all_consumers
  37 + consumers = self.purchases_consumers.order 'name ASC'
  38 + consumers.concat self.consumers.except_self.order('name ASC') if self.respond_to? :consumers
  39 + consumers.uniq
37 40 end
38 41  
39 42 def self.create_orders_manager_role env_id
... ...
plugins/orders/lib/ext/session_store.rb
1   -require 'active_record/session_store'
  1 +require_dependency 'session'
2 2  
3   -class ActiveRecord::SessionStore::Session
  3 +class Session
4 4  
5 5 has_many :orders, primary_key: :session_id, foreign_key: :session_id, class_name: 'OrdersPlugin::Order'
6 6  
... ...
plugins/orders/lib/orders_plugin/display_helper.rb
... ... @@ -2,6 +2,8 @@ module OrdersPlugin::DisplayHelper
2 2  
3 3 protected
4 4  
  5 + include HelpHelper
  6 +
5 7 include OrdersPlugin::TranslationHelper
6 8 include OrdersPlugin::PriceHelper
7 9 include OrdersPlugin::DateHelper
... ... @@ -9,7 +11,8 @@ module OrdersPlugin::DisplayHelper
9 11 include OrdersPlugin::TableHelper
10 12 include OrdersPlugin::AdminHelper
11 13 include OrdersPlugin::JavascriptHelper
12   - include HelpHelper
  14 +
  15 + include DeliveryPlugin::DisplayHelper
13 16  
14 17 # come on, you can't replace a rails api method (button_to_function was)!
15 18 def submit_to_function name, function, html_options={}
... ...
plugins/orders/lib/orders_plugin/field_helper.rb
... ... @@ -6,7 +6,7 @@ module OrdersPlugin::FieldHelper
6 6 help = options.delete(:help)
7 7 content_tag :div,
8 8 if form then form.label field, label, class: 'control-label' else label_tag field, label, class: 'control-label' end +
9   - content_tag('div', help, class: 'field-help') +
  9 + content_tag('div', help, class: 'help-block') +
10 10 content_tag('div', field_html, class: 'field-box') +
11 11 content_tag('div', '', style: 'clear: both'),
12 12 options.merge(class: options[:class].to_s + ' field form-group')
... ...
plugins/orders/lib/orders_plugin/mailer.rb
... ... @@ -4,8 +4,6 @@ class OrdersPlugin::Mailer &lt; Noosfero::Plugin::MailerBase
4 4  
5 5 helper ApplicationHelper
6 6 helper OrdersPlugin::DisplayHelper
7   - helper OrdersPlugin::DateHelper
8   - helper OrdersPlugin::TranslationHelper
9 7  
10 8 attr_accessor :environment
11 9 attr_accessor :profile
... ... @@ -82,4 +80,10 @@ class OrdersPlugin::Mailer &lt; Noosfero::Plugin::MailerBase
82 80 end
83 81 end
84 82  
  83 + # for order/show_simple form
  84 + def protect_against_forgery?
  85 + false
  86 + end
  87 + helper_method :protect_against_forgery?
  88 +
85 89 end
... ...
plugins/orders/lib/orders_plugin/report.rb
... ... @@ -142,6 +142,7 @@ module OrdersPlugin::Report
142 142 productsStart = sbs+5
143 143 productsEnd = 0
144 144 selled_sum = 0
  145 + total_price_without_margin = 0
145 146 orders.each do |order|
146 147  
147 148 sheet.add_row [t('lib.report.order_code'), t('lib.report.member_name'), '', t('lib.report.phone'), '', t('lib.report.mail'), ''], style: bluecell_b_top
... ... @@ -188,6 +189,7 @@ module OrdersPlugin::Report
188 189 style: [default,default,default,default,default,currency,currency],
189 190 formula_values: [nil,nil,nil,nil,nil,nil,formula_value_s]
190 191 selled_sum += item.status_quantity * item.price rescue 0
  192 + total_price_without_margin += item.price_without_margins * item.status_quantity
191 193  
192 194 sbe += 1
193 195 sum += formula_value
... ... @@ -200,12 +202,11 @@ module OrdersPlugin::Report
200 202 sbs = sbe + 2
201 203 end
202 204  
203   - sheet.add_row [t('lib.report.selled_total'), '', "=SUM(G#{productsStart}:G#{productsEnd})"],
204   - formula_values: [nil,nil, selled_sum],
205   - style: [redcell,redcell,currency]
206   -
207   - ["A#{sbs}:B#{sbs}"].each{ |c| sheet.merge_cells c }
  205 + sheet.add_row [t('lib.report.selled_total'), '', "=SUM(G#{productsStart}:G#{productsEnd})", t('lib.report.total_price_without_margin'),"","", total_price_without_margin],
  206 + formula_values: [nil, nil, selled_sum, nil, nil, nil, nil],
  207 + style: [redcell, redcell, currency, redcell, redcell, redcell, currency]
208 208  
  209 + ["A#{sbs}:B#{sbs}", "D#{sbs}:F{sbs}"].each{ |c| sheet.merge_cells c }
209 210  
210 211 sheet.column_widths 15,30,30,9,8,10,11
211 212 end # closes spreadsheet
... ...
plugins/orders/locales/en-US.yml
1   -"en-US": &en-US
  1 +en-US: &en-US
  2 +
  3 + number:
  4 + currency:
  5 + format:
  6 + xlsx_currency: "[$$-409]* #.##0,00;[RED]-[$$-409]* #.##0,00"
2 7  
3 8 orders_plugin:
4 9 terms:
... ... @@ -77,6 +82,7 @@
77 82 supplier: "%{terms.supplier.singular.capitalize}"
78 83 total_parcel_value: "Total value using stock"
79 84 total_selled_value: "Total selled value"
  85 + total_price_without_margin: "Total price without margin"
80 86 total_value: "Total Value"
81 87 un: un.
82 88 value: value
... ... @@ -299,8 +305,8 @@
299 305 supplier_product: "%{terms.supplier.singular.capitalize}/Product"
300 306 total: "TOTAL:"
301 307  
302   -'en_US':
  308 +en_US:
303 309 <<: *en-US
304   -'en':
  310 +en:
305 311 <<: *en-US
306 312  
... ...
plugins/orders/locales/pt-BR.yml
1   -"pt-BR": &pt-BR
  1 +pt-BR: &pt-BR
  2 +
  3 + number:
  4 + currency:
  5 + format:
  6 + xlsx_currency: "[$R$-416]* #,##0.00;[RED]-[$R$-416]* #,##0.00"
2 7  
3 8 orders_plugin:
4 9 terms:
... ... @@ -78,6 +83,7 @@
78 83 supplier: "%{terms.supplier.singular.capitalize}"
79 84 total_parcel_value: "Valor total usando estoque"
80 85 total_selled_value: "Valor total vendido"
  86 + total_price_without_margin: "Valor total sem a margem"
81 87 total_value: "total pago"
82 88 un: un.
83 89 value: Valor
... ... @@ -299,8 +305,8 @@
299 305 supplier_product: "%{terms.supplier.singular.capitalize}/Produto"
300 306 total: "TOTAL:"
301 307  
302   -'pt_BR':
  308 +pt_BR:
303 309 <<: *pt-BR
304   -'pt':
  310 +pt:
305 311 <<: *pt-BR
306 312  
... ...
plugins/orders/models/orders_plugin/item.rb
... ... @@ -7,6 +7,13 @@ class OrdersPlugin::Item &lt; ActiveRecord::Base
7 7 # flag used by items to compare them with products
8 8 attr_accessor :product_diff
9 9  
  10 + Statuses = %w[ordered accepted separated delivered received]
  11 + DbStatuses = %w[draft planned cancelled] + Statuses
  12 + UserStatuses = %w[open forgotten planned cancelled] + Statuses
  13 + StatusText = {}; UserStatuses.map do |status|
  14 + StatusText[status] = "orders_plugin.models.order.statuses.#{status}"
  15 + end
  16 +
10 17 # should be Order, but can't reference it here so it would create a cyclic reference
11 18 StatusAccessMap = {
12 19 'ordered' => :consumer,
... ... @@ -28,9 +35,9 @@ class OrdersPlugin::Item &lt; ActiveRecord::Base
28 35  
29 36 serialize :data
30 37  
31   - belongs_to :order, class_name: 'OrdersPlugin::Order', foreign_key: :order_id, touch: true
32   - belongs_to :sale, class_name: 'OrdersPlugin::Sale', foreign_key: :order_id, touch: true
33   - belongs_to :purchase, class_name: 'OrdersPlugin::Purchase', foreign_key: :order_id, touch: true
  38 + belongs_to :order, class_name: '::OrdersPlugin::Order', foreign_key: :order_id, touch: true
  39 + belongs_to :sale, class_name: '::OrdersPlugin::Sale', foreign_key: :order_id, touch: true
  40 + belongs_to :purchase, class_name: '::OrdersPlugin::Purchase', foreign_key: :order_id, touch: true
34 41  
35 42 belongs_to :product
36 43 has_one :supplier, through: :product
... ... @@ -40,28 +47,30 @@ class OrdersPlugin::Item &lt; ActiveRecord::Base
40 47  
41 48 # FIXME: don't work because of load order
42 49 #if defined? SuppliersPlugin
43   - has_many :from_products, through: :product, source: :orders
  50 + has_many :from_products, through: :product
  51 + has_one :from_product, through: :product
44 52 has_many :to_products, through: :product
45   - has_many :sources_supplier_products, through: :product, source: :items
46   - has_many :supplier_products, through: :product, source: :enterprise
  53 + has_one :to_product, through: :product
  54 + has_many :sources_supplier_products, through: :product
  55 + has_one :sources_supplier_product, through: :product
  56 + has_many :supplier_products, through: :product
  57 + has_one :supplier_product, through: :product
47 58 has_many :suppliers, through: :product
  59 + has_one :supplier, through: :product
48 60 #end
49   - def from_product
50   - self.from_products.first
51   - end
52   - def supplier_product
53   - self.supplier_products.first
54   - end
55 61  
56   - scope :ordered, conditions: ['orders_plugin_orders.status = ?', 'ordered'], joins: [:order]
57   - scope :for_product, lambda{ |product| {conditions: {product_id: product.id}} }
  62 + scope :ordered, -> { joins(:order).where 'orders_plugin_orders.status = ?', 'ordered' }
  63 + scope :for_product, -> (product) { where product_id: product.id }
58 64  
59 65 default_scope include: [:product]
60 66  
61 67 validate :has_order
62 68 validates_presence_of :product
  69 + validates_inclusion_of :status, in: DbStatuses
63 70  
  71 + before_validation :set_defaults
64 72 before_save :save_calculated_prices
  73 + before_save :step_status
65 74 before_create :sync_fields
66 75  
67 76 # utility for other classes
... ... @@ -104,6 +113,9 @@ class OrdersPlugin::Item &lt; ActiveRecord::Base
104 113 def price
105 114 self[:price] || (self.product.price_with_discount || 0 rescue nil)
106 115 end
  116 + def price_without_margins
  117 + self.product.price_without_margins rescue self.price
  118 + end
107 119 def unit
108 120 self.product.unit
109 121 end
... ... @@ -121,8 +133,15 @@ class OrdersPlugin::Item &lt; ActiveRecord::Base
121 133 end
122 134 end
123 135  
124   - def status
125   - self.order.status
  136 + def calculated_status
  137 + status = self.order.status
  138 + index = Statuses.index status
  139 + next_status = Statuses[index+1] if index
  140 + next_quantity = "quantity_#{StatusDataMap[next_status]}" if next_status
  141 + if next_status and self.send next_quantity then next_status else status end
  142 + end
  143 + def on_next_status?
  144 + self.order.status != self.calculated_status
126 145 end
127 146  
128 147 # product used for comparizon when repeating an order
... ... @@ -144,13 +163,13 @@ class OrdersPlugin::Item &lt; ActiveRecord::Base
144 163  
145 164 def status_quantity_field
146 165 @status_quantity_field ||= begin
147   - status = StatusDataMap[self.order.status] || 'consumer_ordered'
  166 + status = StatusDataMap[self.status] || 'consumer_ordered'
148 167 "quantity_#{status}"
149 168 end
150 169 end
151 170 def status_price_field
152 171 @status_price_field ||= begin
153   - status = StatusDataMap[self.order.status] || 'consumer_ordered'
  172 + status = StatusDataMap[self.status] || 'consumer_ordered'
154 173 "price_#{status}"
155 174 end
156 175 end
... ... @@ -184,10 +203,10 @@ class OrdersPlugin::Item &lt; ActiveRecord::Base
184 203  
185 204 def quantity_price_data actor_name
186 205 data = {flags: {}}
187   - statuses = OrdersPlugin::Order::Statuses
  206 + statuses = ::OrdersPlugin::Order::Statuses
188 207 statuses_data = data[:statuses] = {}
189 208  
190   - current = statuses.index(self.order.status) || 0
  209 + current = statuses.index(self.status) || 0
191 210 next_status = self.order.next_status actor_name
192 211 next_index = statuses.index(next_status) || current + 1
193 212 goto_next = actor_name == StatusAccessMap[next_status]
... ... @@ -266,8 +285,11 @@ class OrdersPlugin::Item &lt; ActiveRecord::Base
266 285  
267 286 # Set access
268 287 statuses_data.each.with_index do |(status, status_data), i|
269   - status_data[:flags][:editable] = true if StatusAccessMap[status] == actor_name
270   - # code to only allow last status
  288 + #consumer_may_edit = actor_name == :consumer and status == 'ordered' and self.order.open?
  289 + if StatusAccessMap[status] == actor_name
  290 + status_data[:flags][:editable] = true
  291 + end
  292 + # only allow last status
271 293 #status_data[:flags][:editable] = true if status_data[:access] == actor_name and (status_data[:flags][:admin] or self.order.open?)
272 294 end
273 295  
... ... @@ -279,6 +301,14 @@ class OrdersPlugin::Item &lt; ActiveRecord::Base
279 301 self.save_calculated_prices
280 302 end
281 303  
  304 + # used by db/migrate/20150627232432_add_status_to_orders_plugin_item.rb
  305 + def fill_status
  306 + status = self.calculated_status
  307 + return if self.status == status
  308 + self.update_column :status, status
  309 + self.order.update_column :building_next_status, true if self.order.status != status and not self.order.building_next_status
  310 + end
  311 +
282 312 protected
283 313  
284 314 def save_calculated_prices
... ... @@ -288,6 +318,17 @@ class OrdersPlugin::Item &lt; ActiveRecord::Base
288 318 end
289 319 end
290 320  
  321 + def set_defaults
  322 + self.status ||= Statuses.first
  323 + end
  324 +
  325 + def step_status
  326 + status = self.calculated_status
  327 + return if self.status == status
  328 + self.status = status
  329 + self.order.update_column :building_next_status, true if self.order.status != status and not self.order.building_next_status
  330 + end
  331 +
291 332 def has_order
292 333 self.order or self.sale or self.purchase
293 334 end
... ...
plugins/orders/models/orders_plugin/order.rb
1 1 class OrdersPlugin::Order < ActiveRecord::Base
2 2  
3   - Statuses = %w[ordered accepted separated delivered received]
4   - DbStatuses = %w[draft planned cancelled] + Statuses
5   - UserStatuses = %w[open forgotten planned cancelled] + Statuses
6   - StatusText = {}; UserStatuses.map do |status|
7   - StatusText[status] = "orders_plugin.models.order.statuses.#{status}"
8   - end
  3 + # if abstract_class is true then it will trigger https://github.com/rails/rails/issues/20871
  4 + #self.abstract_class = true
  5 +
  6 + Statuses = ::OrdersPlugin::Item::Statuses
  7 + DbStatuses = ::OrdersPlugin::Item::DbStatuses
  8 + UserStatuses = ::OrdersPlugin::Item::UserStatuses
  9 + StatusText = ::OrdersPlugin::Item::StatusText
9 10  
10 11 # oh, we need a payments plugin!
11 12 PaymentMethods = {
... ... @@ -31,18 +32,14 @@ class OrdersPlugin::Order &lt; ActiveRecord::Base
31 32 ]
32 33  
33 34 # copy, for easiness. can't be declared to here to avoid cyclic reference
34   - StatusDataMap = OrdersPlugin::Item::StatusDataMap
35   - StatusAccessMap = OrdersPlugin::Item::StatusAccessMap
  35 + StatusDataMap = ::OrdersPlugin::Item::StatusDataMap
  36 + StatusAccessMap = ::OrdersPlugin::Item::StatusAccessMap
36 37  
37 38 StatusesByActor = {
38 39 consumer: StatusAccessMap.map{ |s, a| s if a == :consumer }.compact,
39 40 supplier: StatusAccessMap.map{ |s, a| s if a == :supplier }.compact,
40 41 }
41 42  
42   - # workaround for STI
43   - self.table_name = :orders_plugin_orders
44   - self.abstract_class = true
45   -
46 43 attr_accessible :status, :consumer, :profile,
47 44 :supplier_delivery_id, :consumer_delivery_id, :supplier_delivery_data, :consumer_delivery_data
48 45  
... ... @@ -51,17 +48,17 @@ class OrdersPlugin::Order &lt; ActiveRecord::Base
51 48 belongs_to :supplier, foreign_key: :profile_id, class_name: 'Profile'
52 49 belongs_to :consumer, class_name: 'Profile'
53 50  
54   - belongs_to :session, primary_key: :session_id, foreign_key: :session_id, class_name: 'ActiveRecord::SessionStore::Session'
  51 + belongs_to :session, primary_key: :session_id, foreign_key: :session_id, class_name: 'Session'
55 52  
56   - has_many :items, class_name: 'OrdersPlugin::Item', foreign_key: :order_id, dependent: :destroy, order: 'name ASC'
  53 + has_many :items, -> { order 'name ASC' }, class_name: 'OrdersPlugin::Item', foreign_key: :order_id, dependent: :destroy
57 54 has_many :products, through: :items
58 55  
59 56 belongs_to :supplier_delivery, class_name: 'DeliveryPlugin::Method'
60 57 belongs_to :consumer_delivery, class_name: 'DeliveryPlugin::Method'
61 58  
62   - scope :alphabetical, -> { joins(:consumer).order 'profiles.name ASC' }
63   - scope :latest, -> { order 'code ASC' }
64   - scope :default_order, -> { order 'code DESC' }
  59 + scope :alphabetical, -> { joins(:consumer).reorder 'profiles.name ASC' }
  60 + scope :latest, -> { reorder 'code ASC' }
  61 + scope :default_order, -> { reorder 'code DESC' }
65 62  
66 63 scope :of_session, -> session_id { where session_id: session_id }
67 64 scope :of_user, -> session_id, consumer_id=nil do
... ... @@ -73,53 +70,50 @@ class OrdersPlugin::Order &lt; ActiveRecord::Base
73 70  
74 71 scope :latest, order: 'created_at DESC'
75 72  
76   - scope :draft, conditions: {status: 'draft'}
77   - scope :planned, conditions: {status: 'planned'}
78   - scope :cancelled, conditions: {status: 'cancelled'}
79   - scope :not_cancelled, conditions: ["status <> 'cancelled'"]
80   - scope :ordered, conditions: ['ordered_at IS NOT NULL']
81   - scope :confirmed, conditions: ['ordered_at IS NOT NULL']
82   - scope :accepted, conditions: ['accepted_at IS NOT NULL']
83   - scope :separated, conditions: ['separated_at IS NOT NULL']
84   - scope :delivered, conditions: ['delivered_at IS NOT NULL']
85   - scope :received, conditions: ['received_at IS NOT NULL']
86   -
87   - scope :for_profile, lambda{ |profile| {conditions: {profile_id: profile.id}} }
88   - scope :for_profile_id, lambda{ |profile_id| {conditions: {profile_id: profile_id}} }
89   - scope :for_supplier, lambda{ |profile| {conditions: {profile_id: profile.id}} }
90   - scope :for_supplier_id, lambda{ |profile_id| {conditions: {profile_id: profile_id}} }
91   - scope :for_consumer, lambda{ |consumer| {conditions: {consumer_id: (consumer.id rescue nil)}} }
92   - scope :for_consumer_id, lambda{ |consumer_id| {conditions: {consumer_id: consumer_id}} }
93   -
94   - scope :months, select: 'DISTINCT(EXTRACT(months FROM orders_plugin_orders.created_at)) as month', order: 'month DESC'
95   - scope :years, select: 'DISTINCT(EXTRACT(YEAR FROM orders_plugin_orders.created_at)) as year', order: 'year DESC'
96   -
97   - scope :by_month, lambda { |month|
98   - where 'EXTRACT(month FROM orders_plugin_orders.created_at) <= :month AND EXTRACT(month FROM orders_plugin_orders.created_at) >= :month',{ month: month }
  73 + scope :draft, -> { where status: 'draft' }
  74 + scope :planned, -> { where status: 'planned' }
  75 + scope :cancelled, -> { where status: 'cancelled' }
  76 + scope :not_cancelled, -> { where "status <> 'cancelled'" }
  77 + scope :ordered, -> { where 'ordered_at IS NOT NULL' }
  78 + scope :confirmed, -> { where 'ordered_at IS NOT NULL' }
  79 + scope :accepted, -> { where 'accepted_at IS NOT NULL' }
  80 + scope :separated, -> { where 'separated_at IS NOT NULL' }
  81 + scope :delivered, -> { where 'delivered_at IS NOT NULL' }
  82 + scope :received, -> { where 'received_at IS NOT NULL' }
  83 +
  84 + scope :for_profile, -> (profile) { where profile_id: profile.id }
  85 + scope :for_profile_id, -> (profile_id) { where profile_id: profile_id }
  86 + scope :for_supplier, -> (profile) { where profile_id: profile.id }
  87 + scope :for_supplier_id, -> (profile_id) { where profile_id: profile_id }
  88 + scope :for_consumer, -> (consumer) { where consumer_id: (consumer.id rescue nil) }
  89 + scope :for_consumer_id, -> (consumer_id) { where consumer_id: consumer_id }
  90 +
  91 + scope :months, -> { select('DISTINCT(EXTRACT(months FROM orders_plugin_orders.created_at)) as month').order('month DESC') }
  92 + scope :years, -> { select('DISTINCT(EXTRACT(YEAR FROM orders_plugin_orders.created_at)) as year').order('year DESC') }
  93 +
  94 + scope :by_month, -> (month) {
  95 + where 'EXTRACT(month FROM orders_plugin_orders.created_at) <= :month AND EXTRACT(month FROM orders_plugin_orders.created_at) >= :month', month: month
99 96 }
100   - scope :by_year, lambda { |year|
101   - where 'EXTRACT(year FROM orders_plugin_orders.created_at) <= :year AND EXTRACT(year FROM orders_plugin_orders.created_at) >= :year', { year: year }
  97 + scope :by_year, -> (year) {
  98 + where 'EXTRACT(year FROM orders_plugin_orders.created_at) <= :year AND EXTRACT(year FROM orders_plugin_orders.created_at) >= :year', year: year
102 99 }
103   - scope :by_range, lambda { |start_time, end_time|
104   - where 'orders_plugin_orders.created_at >= :start AND orders_plugin_orders.created_at <= :end', { start: start_time, end: end_time }
  100 + scope :by_range, -> (start_time, end_time) {
  101 + where 'orders_plugin_orders.created_at >= :start AND orders_plugin_orders.created_at <= :end', start: start_time, end: end_time
105 102 }
106 103  
107   - scope :with_status, lambda { |status|
108   - where status: status
109   - }
110   - scope :with_code, lambda { |code|
111   - where code: code
112   - }
  104 + scope :with_status, -> (status) { where status: status }
  105 + scope :with_code, -> (code) { where code: code }
113 106  
114 107 validates_presence_of :profile
115 108 # consumer is optional, as orders can be made by unlogged users
116 109 validates_inclusion_of :status, in: DbStatuses
117 110  
118 111 before_validation :check_status
  112 + before_validation :change_status
119 113 after_save :send_notifications
120 114  
121 115 extend CodeNumbering::ClassMethods
122   - code_numbering :code, scope: proc{ self.profile.orders }
  116 + code_numbering :code, scope: -> { self.profile.orders }
123 117  
124 118 serialize :data
125 119  
... ... @@ -165,17 +159,17 @@ class OrdersPlugin::Order &lt; ActiveRecord::Base
165 159 # / Items
166 160 # / \ OfferedProduct (column product_id)
167 161 # Order / \ SourceProduct from DistributedProduct (quantity=1, always)
168   - # \ SourceProduct from Product* (quantity be more than 1 if DistributedProduct is an agregate product)
  162 + # \ SourceProduct from Product* (multiple for each if is an aggregate product)
169 163 # for order outside cycle we have
170 164 # / Items
171 165 # / \ SourceProduct from DistributedProduct (quantity=1, always)
172   - # Order / \ SourceProduct from Product* (quantity be more than 1 if DistributedProduct is an agregate product)
  166 + # Order / \ SourceProduct from Product* (multiple for each if is an aggregate product)
173 167 #
174 168 # *suppliers usually don't distribute using cycles, so they only have Product
175 169 #
176 170 def self.supplier_products_by_suppliers orders
177 171 products_by_supplier = {}
178   - items = OrdersPlugin::Item.where(order_id: orders.map(&:id)).includes({sources_supplier_products: [:supplier, :from_product]})
  172 + items = self.parent::Item.where(order_id: orders.map(&:id)).includes(sources_supplier_products: [:supplier, :from_product])
179 173 items.each do |item|
180 174 if item.sources_supplier_products.present?
181 175 item.sources_supplier_products.each do |source_sp|
... ... @@ -358,15 +352,14 @@ class OrdersPlugin::Order &lt; ActiveRecord::Base
358 352  
359 353 # total_price considering last state
360 354 def total_price actor_name = :consumer, admin = false
361   - if not self.pre_order? and admin and status = self.next_status(actor_name)
362   - self.fill_items_data self.status, status
  355 + # for admins, we want the next_status while we concluded the finish status change
  356 + if admin
  357 + price = :status_price
363 358 else
364   - status = self.status
  359 + data = StatusDataMap[self.status] || StatusDataMap[Statuses.first]
  360 + price = "price_#{data}".to_sym
365 361 end
366 362  
367   - data = StatusDataMap[status] || StatusDataMap[Statuses.first]
368   - price = "price_#{data}".to_sym
369   -
370 363 items ||= (self.ordered_items rescue nil) || self.items
371 364 items.collect(&price).inject(0){ |sum, p| sum + p.to_f }
372 365 end
... ... @@ -379,7 +372,8 @@ class OrdersPlugin::Order &lt; ActiveRecord::Base
379 372 end
380 373 has_currency :total
381 374  
382   - def fill_items_data from_status, to_status, save = false
  375 + def fill_items from_status, to_status, save = false
  376 + # check for status advance
383 377 return if (Statuses.index(to_status) <= Statuses.index(from_status) rescue true)
384 378  
385 379 from_data = StatusDataMap[from_status]
... ... @@ -405,15 +399,22 @@ class OrdersPlugin::Order &lt; ActiveRecord::Base
405 399 self.status ||= 'draft'
406 400 # backwards compatibility
407 401 self.status = 'ordered' if self.status == 'confirmed'
  402 + end
  403 +
  404 + def change_status
  405 + return if self.status_was == self.status
408 406  
409   - self.fill_items_data self.status_was, self.status, true
  407 + self.fill_items self.status_was, self.status, true
  408 + self.items.update_all status: self.status
  409 + self.building_next_status = false
410 410  
  411 + # fill dates on status advance
411 412 if self.status_on? 'ordered'
412 413 Statuses.each do |status|
413 414 self.send "#{self.status}_at=", Time.now if self.status_was != status and self.status == status
414 415 end
415 416 else
416   - # for draft, planned, forgotten, cancelled, etc
  417 + # status rewind for draft, planned, forgotten, cancelled, etc
417 418 Statuses.each do |status|
418 419 self.send "#{status}_at=", nil
419 420 end
... ...
plugins/orders/models/orders_plugin/sale.rb
1 1 class OrdersPlugin::Sale < OrdersPlugin::Order
2 2  
  3 + before_validation :fill_default_supplier_delivery
  4 +
3 5 def orders_name
4 6 'sales'
5 7 end
... ... @@ -19,6 +21,17 @@ class OrdersPlugin::Sale &lt; OrdersPlugin::Order
19 21 has_number_with_locale :purchase_quantity_total
20 22 has_currency :purchase_price_total
21 23  
  24 + def supplier_delivery
  25 + super || (self.delivery_methods.first rescue nil)
  26 + end
  27 + def supplier_delivery_id
  28 + self[:supplier_delivery_id] || (self.supplier_delivery.id rescue nil)
  29 + end
  30 +
  31 + def fill_default_supplier_delivery
  32 + self[:supplier_delivery_id] ||= self.supplier_delivery.id if self.supplier_delivery
  33 + end
  34 +
22 35 protected
23 36  
24 37 end
... ...
plugins/orders/orders 0 → 120000
... ... @@ -0,0 +1 @@
  1 +/home/braulio/Projects/noosfero-ecosol/noosfero/plugins/orders
0 2 \ No newline at end of file
... ...
plugins/orders/public/javascripts/orders.js
... ... @@ -139,21 +139,12 @@ orders = {
139 139 },
140 140 },
141 141  
142   - set_orders_container_max_height: function()
  142 + setOrderMaxHeight: function()
143 143 {
144 144 ordersH = $(window).height();
145   - ordersH -= 100
146 145 ordersH -= $('#cirandas-top-bar').outerHeight()
147   - ordersH -= $('.order-status-message').outerHeight()
148   - ordersH -= $('.order-message-title').outerHeight()
149   - ordersH -= $('#order-column .order-items .table-header').last().outerHeight()
150   - ordersH -= $('#order-column .order-total').last().outerHeight()
151   - ordersH -= $('#order-column #actor-data-box').last().outerHeight()
152   - ordersH -= $('#order-column .delivery-box').outerHeight()
153   - ordersH -= $('#order-column .order-message-text').outerHeight()
154   - ordersH -= $('#order-column .order-message-actions').outerHeight()
155   - ordersH -= $('#order-column .actions').outerHeight()
156   - $('.order-items-container .order-items-scroll').css('max-height', ordersH);
  146 + ordersH -= $('.order-view form > .actions').outerHeight(true)
  147 + $('.order-view .order-data').css('max-height', ordersH);
157 148 },
158 149  
159 150 daterangepicker: {
... ... @@ -180,6 +171,7 @@ orders = {
180 171 },
181 172 };
182 173  
183   -$(document).ready(orders.set_orders_container_max_height);
184   -$(window).resize(orders.set_orders_container_max_height);
  174 +$(document).ready(orders.setOrderMaxHeight);
  175 +$(window).resize(orders.setOrderMaxHeight);
  176 +$('#order_supplier_delivery_id').change(orders.setOrderMaxHeight);
185 177  
... ...
plugins/orders/public/public 0 → 120000
... ... @@ -0,0 +1 @@
  1 +/home/braulio/Projects/noosfero-ecosol/noosfero/config/plugins/orders/public
0 2 \ No newline at end of file
... ...
plugins/orders/public/stylesheets/_field.scss
... ... @@ -25,9 +25,8 @@
25 25 float: left;
26 26 width: 100%;
27 27 }
28   - .field-help {
  28 + .help-block {
29 29 clear: both;
30   - margin-bottom: $half-margin;
31 30 font-size: 10px;
32 31 }
33 32  
... ...
plugins/orders/public/stylesheets/_items.scss
... ... @@ -102,9 +102,6 @@
102 102  
103 103 .table-content {
104 104  
105   - .order-items-scroll {
106   - max-height: 350px;
107   - }
108 105 .item {
109 106  
110 107 &.product-unavailable {
... ...
plugins/orders/public/stylesheets/orders.scss
... ... @@ -9,10 +9,10 @@
9 9 @import 'items';
10 10  
11 11 .controller-profile_editor a.control-panel-orders-purchases-sales {
12   - background-image: url("/plugins/orders/images/control-panel/purchases-sales.png")
  12 + background-image: url("/plugins/orders/images/control-panel/purchases-sales.png");
13 13 }
14 14 .controller-profile_editor .msie6 a.control-panel-orders-purchases-sales {
15   - background-image: url("/plugins/orders/images/control-panel/purchases-sales.gif")
  15 + background-image: url("/plugins/orders/images/control-panel/purchases-sales.gif");
16 16 }
17 17  
18 18 .orders-admin-index {
... ... @@ -125,14 +125,13 @@
125 125 }
126 126  
127 127 .order-view {
128   -
129   - &, .order-items-scroll, .order-items, .item {
  128 + &, .order-items, .item {
130 129 width: $order-items-width;
131 130 }
132 131 &.admin {
133 132 width: $order-items-admin-width;
134 133  
135   - .order-items-scroll, .order-items, .item {
  134 + .order-items, .item {
136 135 width: $order-items-admin-width - 2*$border;
137 136 }
138 137 }
... ... @@ -160,16 +159,14 @@
160 159  
161 160 margin: 0;
162 161 border: none;
  162 +
  163 + overflow: hidden;
  164 + overflow-y: auto;
  165 +
163 166 &.admin {
164 167 border: $border solid black;
165 168 }
166 169  
167   - .order-items-scroll {
168   - overflow-y: auto;
169   - padding-right: $intercolumn;
170   - box-sizing: content-box;
171   - }
172   -
173 170 .order-header {
174 171 padding: $half-padding $padding;
175 172 border-bottom: $border solid black;
... ... @@ -223,7 +220,10 @@
223 220 padding-bottom: 200px;
224 221 }
225 222  
226   -.action-orders_cycle_plugin_order-edit .purechat {
227   - left: 40px;
228   - right: none;
  223 +.controller-orders_cycle_plugin_order,
  224 +.controller-consumers_coop_plugin_order {
  225 + .purechat {
  226 + left: 40px;
  227 + right: none;
  228 + }
229 229 }
... ...
plugins/orders/test/test_helper.rb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +require File.dirname(__FILE__) + '/../../../test/test_helper'
... ...
plugins/orders/test/unit/orders_plugin/item_test.rb 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +require File.dirname(__FILE__) + '/../../../../test/test_helper'
  2 +
  3 +class OrdersPlugin::ItemTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @item = build(OrdersPlugin::Item,
  7 + :quantity_shipped => 1.0, :quantity_consumer_ordered => 2.0, :quantity_accepted => 3.0,
  8 + :price_shipped => 10.0, :price_consumer_ordered => 20.0, :price_accepted => 30.0)
  9 + end
  10 +
  11 + should 'calculate prices' do
  12 + end
  13 +
  14 +end
... ...
plugins/orders/test/unit/orders_plugin/order_test.rb 0 → 100644
... ... @@ -0,0 +1,121 @@
  1 +require "test_helper"
  2 +
  3 +class OrdersPlugin::OrderTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @order = build(OrdersPlugin::Order)
  7 + end
  8 +
  9 + should 'report supplier products when distributing aggregate products' do
  10 + env = Environment.create! name: 'megacoop'
  11 + supplier = Enterprise.create! identifier: 'supplier', name: 'supplier', environment: env
  12 + p1 = supplier.products.create! product_category: ProductCategory.create!(name: 'banana', environment: env)
  13 + p2 = supplier.products.create! product_category: ProductCategory.create!(name: 'aipim', environment: env)
  14 +
  15 + coop = Community.create! identifier: 'blah', name: 'blah', environment: env
  16 + coop.suppliers.create! profile: supplier, consumer: coop
  17 + aggregate_product = SuppliersPlugin::DistributedProduct.new profile: coop
  18 + aggregate_product.sources_from_products.build quantity: 1, from_product: p1, to_product: aggregate_product
  19 + aggregate_product.sources_from_products.build quantity: 5, from_product: p2, to_product: aggregate_product
  20 + aggregate_product.save!
  21 +
  22 + # hack
  23 + person = coop
  24 +
  25 + # this also create offered products
  26 + cycle = OrdersCyclePlugin::Cycle.create! name: 'blah', profile: coop, start: Time.now, finish: Time.now+1.day, delivery_start: Time.now+2.days, delivery_finish: Time.now+3.days, status: 'orders'
  27 + sale = cycle.sales.create! profile: person
  28 + sale.items.create! quantity_consumer_ordered: 3, product: aggregate_product
  29 +
  30 + r = OrdersPlugin::Order.supplier_products_by_suppliers [sale]
  31 + quantities = r.first.last.map(&:quantity_ordered).map(&:to_i)
  32 + assert_equal [3*1,3*5], quantities
  33 + end
  34 +
  35 + should 'format code with cycle code' do
  36 + @order.save!
  37 + assert_equal "#{@order.cycle.code}.#{@order.attributes['code']}", @order.code
  38 + end
  39 +
  40 + should 'use as draft default status' do
  41 + @order = create(OrdersPlugin::Order, status: nil)
  42 + assert_equal 'draft', @order.status
  43 + end
  44 +
  45 + ###
  46 + # Status
  47 + ###
  48 +
  49 + should 'define and validate list of statuses' do
  50 + @order.status = 'blah'
  51 + @order.valid?
  52 + assert @order.errors.invalid?('status')
  53 +
  54 + ['draft', 'planned', 'ordered', 'cancelled'].each do |i|
  55 + @order.status = i
  56 + @order.valid?
  57 + assert !@order.errors.invalid?('status')
  58 + end
  59 + end
  60 +
  61 + should 'define status question methods' do
  62 + ['draft', 'planned', 'ordered', 'cancelled'].each do |i|
  63 + @order.status = i
  64 + assert @order.send("#{@order.status}?")
  65 + end
  66 + end
  67 +
  68 + should 'define forgotten and open status' do
  69 + @order.status = 'draft'
  70 + assert @order.draft?
  71 + assert @order.cycle.orders?
  72 + assert @order.open?
  73 + @order.cycle.status = 'closed'
  74 + assert !@order.open?
  75 + assert @order.forgotten?
  76 + end
  77 +
  78 + should 'return current status using forgotten and open too' do
  79 + @order.status = 'draft'
  80 + assert @order.open?
  81 + assert_equal 'open', @order.current_status
  82 + @order.cycle.status = 'closed'
  83 + assert @order.forgotten?
  84 + assert_equal 'forgotten', @order.current_status
  85 + end
  86 +
  87 + should 'define status_message method' do
  88 + assert @order.respond_to?(:status_message)
  89 + end
  90 +
  91 + ###
  92 + # Delivery
  93 + ###
  94 +
  95 + should 'give default value to supplier delivery if not present' do
  96 + @order.save!
  97 + @order.profile.save!
  98 +
  99 + @order.cycle.delivery_methods = []
  100 + @order.supplier_delivery = nil
  101 + assert_nil @order.supplier_delivery
  102 +
  103 + default = @order.cycle.delivery_methods.create! profile: @order.profile, name: 'method', delivery_type: 'deliver'
  104 + assert_equal default, @order.supplier_delivery
  105 + assert_equal default.id, @order.supplier_delivery_id
  106 + end
  107 +
  108 + ###
  109 + # Totals
  110 + ###
  111 +
  112 + should 'give total price and quantity asked' do
  113 + @order.cycle.profile.save!
  114 + product = create(SuppliersPlugin::DistributedProduct, price: 2.0, profile: @order.cycle.profile, supplier: @order.cycle.profile.self_supplier)
  115 + @order.save!
  116 + @order.item.create! product: @order.cycle.products.first, quantity_consumer_ordered: 2.0
  117 + assert_equal 2.0, @order.total_quantity_consumer_ordered
  118 + assert_equal 4.0, @order.total_price_consumer_ordered
  119 + end
  120 +
  121 +end
... ...
plugins/orders/views/orders_plugin/mailer/order_confirmation.html.slim 0 → 100644
... ... @@ -0,0 +1,30 @@
  1 +doctype html
  2 +html
  3 + head
  4 + meta content=("text/html; charset=utf-8") http-equiv="Content-Type"
  5 + = stylesheet_link_tag 'plugins/responsive/bootstrap/all.css' if defined? ResponsivePlugin
  6 + = stylesheet_link_tag 'plugins/orders/style.css'
  7 + body
  8 + p= t'orders_plugin.views.mailer.order_confirmation.dear_name', name: @consumer.name
  9 +
  10 + p= t'orders_plugin.views.mailer.order_confirmation.your_order_made_on_co',
  11 + name: @profile.name, date: day_time(@order.updated_at)
  12 +
  13 + - if @order.respond_to? :cycle
  14 + p= t'orders_plugin.views.mailer.order_confirmation.the_delivery_period_w',
  15 + date: datetime_period_with_from(@order.cycle.delivery_start, @order.cycle.delivery_finish)
  16 +
  17 + p= t'orders_plugin.views.mailer.order_confirmation.order_data_confirmed'
  18 +
  19 + = render "orders_plugin_order/show_simple", order: @order, actor_name: :consumer
  20 +
  21 + p
  22 + = t('orders_plugin.views.mailer.order_confirmation.your_order_can_be_che')
  23 + br
  24 + = link_to t('orders_plugin.views.mailer.order_confirmation.order_code_of_profile') % {code: @order.code, name: @profile.name },
  25 + {controller: :orders_cycle_plugin_order, action: :edit, profile: @profile.identifier, id: @order.id, protocol: "http"}
  26 +
  27 + p
  28 + |--
  29 + br
  30 + = t'orders_plugin.views.mailer.order_confirmation.greetings'
... ...
plugins/orders/views/orders_plugin/shared/daterangepicker/_init.html.slim
1 1 = content_for :head do
2 2 = stylesheet_link_tag '/assets/plugins/orders/stylesheets/daterangepicker-bs3.css'
3 3 / in case it is not loaded for this theme
4   - = stylesheet_link_tag '/assets/designs/icons/awesome/scss/font-awesome.css'
  4 + = stylesheet_link_tag 'designs/icons/awesome/scss/font-awesome.css'
5 5 = javascript_include_tag '/assets/plugins/orders/javascripts/moment-with-locales.js'
6 6 = javascript_include_tag '/assets/plugins/orders/javascripts/daterangepicker.js'
7 7  
... ...
plugins/orders/views/orders_plugin_admin/_filter_fields.html.slim
... ... @@ -3,21 +3,21 @@
3 3 div= render 'orders_plugin/shared/daterangepicker/init'
4 4  
5 5 .field.state
6   - label for="status" = t'views.filter.status'
  6 + label= t'views.filter.status'
7 7 div= select_tag :status,
8 8 options_for_select([[t('views.filter.all'), ""]] + OrdersPlugin::Order::StatusText.map{ |id, name| [t(name), id] }, params[:status])
9 9  
10 10 .field.code
11   - label for="code" = t'views.filter.code'
  11 + label= t'views.filter.code'
12 12 div= text_field_tag :code, params[:code]
13 13  
14 14 div class="field #{actor_name}"
15   - label for="#{actor_name}_id" = t"terms.#{actor_name}.singular", transformation: :capitalize
  15 + label= t"terms.#{actor_name}.singular", transformation: :capitalize
16 16 div= select_tag "#{actor_name}_id",
17 17 options_for_select([[t('views.filter.all'), ""]] + actors.map{ |a| [a.name, a.id] }, params["#{actor_name}_id"])
18 18  
19 19 .field.delivery
20   - label for="delivery_method_id" = t'views.filter.delivery'
  20 + label= t'views.filter.delivery'
21 21 div= select_tag :delivery_method_id,
22 22 options_for_select([[t('views.filter.all'), ""]] + orders_owner.delivery_methods.map{ |dm| [dm.name, dm.id] }, params[:delivery_method_id])
23 23  
... ...
plugins/orders/views/orders_plugin_admin/_index.html.erb
... ... @@ -12,14 +12,14 @@
12 12  
13 13 <% if @purchases.present? %>
14 14 <div id='purchases' class="tab-pane">
15   - <%= render 'orders_plugin_admin/purchases', actors: profile.purchases_all_suppliers,
  15 + <%= render 'orders_plugin_admin/purchases', actors: profile.sales_all_consumers,
16 16 orders_owner: profile, orders: @purchases, month: @purchases_month, year: @purchases_year, wireframe_size: false %>
17 17 </div>
18 18 <% end %>
19 19  
20 20 <% if @sales.present? %>
21 21 <div id='sales' class="tab-pane">
22   - <%= render 'orders_plugin_admin/sales', actors: profile.sales_all_consumers,
  22 + <%= render 'orders_plugin_admin/sales', actors: profile.purchases_all_consumers,
23 23 orders_owner: profile, orders: @sales, month: @sales_month, year: @purchases_year, wireframe_size: false %>
24 24 </div>
25 25 <% end %>
... ...
plugins/orders/views/orders_plugin_admin/_order.html.slim 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +- other_actor_name = if actor_name == :supplier then :consumer else :supplier end
  2 +- edit = true if edit.nil?
  3 +
  4 +div class="order value-row #{order.status}" data-id=order.id id="order-row-#{order.id}" onclick="orders.admin.load_edit(this, '#{url_for action: :edit, id: order.id, actor_name: actor_name}')" toggle-edit="orders.admin.toggle_edit();"
  5 +
  6 + .box-view.with-inner
  7 + .box-field.select toggle-ignore="" = check_box_tag "order_ids[]", order.id
  8 + .box-view-inner
  9 + span.box-field.code= order.code
  10 + span.box-field.actor-name= order.send("#{other_actor_name}_data")[:name]
  11 + span.box-field.order-date= datetime_full order.created_at
  12 + span.box-field.last-update= datetime_full order.updated_at
  13 + span.box-field.total= order.total_price_as_currency_number actor_name
  14 + span.box-field.situation= order_situation order
  15 + = edit_arrow "#order-row-#{order.id}", true, class: 'actions'
  16 + .clean
  17 +
  18 + div class="box-edit #{'loading' if not edit}"
  19 + = render 'orders_plugin_admin/edit', order: order, actor_name: actor_name, other_actor_name: other_actor_name if edit
  20 +
  21 + .clean
... ...
plugins/orders/views/orders_plugin_item/_edit_fields.html.slim
... ... @@ -4,7 +4,7 @@
4 4 .box-field.product title=item.name = item.name
5 5 .box-field.quantity-price-table
6 6 - item_data[:statuses].each do |status, status_data|
7   - - next if status_data[:flags][:not_modified] and not status_data[:flags][:admin]
  7 + - next if status_data[:flags][:not_modified] and not status_data[:flags][:editable]
8 8 = render 'orders_plugin_item/quantity_price', item: item, order: order, status: status, data: item_data, status_data: status_data, actor_name: actor_name
9 9  
10 10 .more
... ... @@ -12,12 +12,15 @@
12 12 / actionmailer doesn't have an user, rescue error
13 13 - if (order.may_edit? user, @admin rescue false)
14 14 - if @admin
15   - = link_to_function t('views.item._edit.remove_from_order'), "orders.item.admin_remove(this, '#{url_for(controller: :orders_plugin_admin_item, action: :edit, id: item.id, actor_name: actor_name)}')", class: 'action-button'
16   - = link_to_function t('views.item._edit.submit'), "orders.item.submit(this, '#{url_for(controller: :orders_plugin_admin_item, action: :edit, id: item.id, actor_name: actor_name)}')", class: 'submit action-button'
  15 + = link_to_function t('views.item._edit.remove_from_order'),
  16 + "orders.item.admin_remove(this, '#{url_for(controller: :orders_plugin_admin_item, action: :edit, id: item.id, actor_name: actor_name)}')"
  17 + = link_to_function t('views.item._edit.submit'),
  18 + "orders.item.submit(this, '#{url_for(controller: :orders_plugin_admin_item, action: :edit, id: item.id, actor_name: actor_name)}')", class: 'submit action-button'
17 19 - else
18 20 = link_to t('views.item._edit.remove_from_order'), {controller: :orders_plugin_item, action: :destroy, id: item.id},
19 21 remote: true, data: {loading: "#item-#{item.id}"}
20 22 = link_to_function t('views.item._edit.submit'), "orders.item.submit(this, '#{url_for(controller: :orders_plugin_item, action: :edit, id: item.id)}')", class: 'submit action-button'
21   - = price_with_unit_span item.price_as_currency_number, (item.product.unit rescue item.price_as_currency_number), nil, class: 'box-field' unless @simple
  23 +
  24 + = price_with_unit_span item.price_as_currency_number, (item.product.unit rescue item.price_as_currency_number), nil, class: 'box-field' unless @view
22 25  
23 26 .clean
... ...
plugins/orders/views/orders_plugin_item/_index.html.slim
... ... @@ -13,12 +13,11 @@ div class=&quot;order-items-container #{&quot;empty&quot; if order.items.size == 0} #{&quot;admin&quot; i
13 13 - if @admin
14 14 .box-field.status= t'views.item._index.status'
15 15 .table-content
16   - .order-items-scroll
17   - - order.items.each do |item|
18   - = render "orders_plugin_item/edit", order: order, item: item, product: item.product, actor_name: actor_name
  16 + - order.items.each do |item|
  17 + = render "orders_plugin_item/edit", order: order, item: item, product: item.product, actor_name: actor_name
19 18 .order-total
20 19 .title= t'views.consumer._total.total'
21   - .value= price_span order.total_price_as_currency_number actor_name
  20 + .value= price_span order.total_price_as_currency_number actor_name, @admin
22 21  
23 22 - else
24 23 = t'views.item._index.empty_order'
... ...
plugins/orders/views/orders_plugin_item/_quantity_price.html.slim
... ... @@ -6,7 +6,7 @@ div class=&quot;quantity-price-row #{status} #{status_data[:flags].keys.join &#39; &#39;}&quot;
6 6 = t'views.item._edit.removed'
7 7 - else
8 8 = quantity_localized
9   - - if not @simple and status_data[:flags][:editable]
  9 + - if not @view and status_data[:flags][:editable]
10 10 = number_field_tag "item[quantity_#{status_data[:field]}]", status_data[:quantity], step: 'any', onkeydown: 'orders.item.quantity_keydown(this, event)'
11 11 - unless status_data[:flags][:removed]
12 12 |&nbsp;
... ...
plugins/orders/views/orders_plugin_order/_data.html.slim 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 += render "orders_plugin_item/index", order: order, actor_name: actor_name, f: f
  2 +
  3 += render 'orders_plugin_order/actor_data', order: order, f: f, actor_name: other_actor_name if @admin
  4 += render 'orders_plugin_order/delivery', order: order, f: f, actor_name: actor_name
  5 += render 'orders_plugin_order/payment', order: order
  6 +
... ...
plugins/orders/views/orders_plugin_order/_delivery.html.slim 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +/ read as: selectable or visualizable
  2 +- if order.delivery_methods.size > 0 or order.supplier_delivery_data.present?
  3 + .delivery-box.order-section
  4 + h3= _'Delivery or pickup method'
  5 +
  6 + - edition = order.may_edit?(user, @admin) && order.delivery_methods.size > 0 if edition.nil?
  7 + = render 'delivery_plugin/order_select', f: f, order: order, methods: order.delivery_methods, edition: edition
  8 +
... ...
plugins/orders/views/orders_plugin_order/_show.html.slim
... ... @@ -30,8 +30,11 @@ div class=&quot;order-view #{&quot;editable&quot; if order.may_edit? user, @admin rescue false}
30 30 - elsif order.open?
31 31 div
32 32 = hidden_field_tag "order[status]", 'ordered'
33   - = f.submit t('views.order._show.confirm_order'), confirm: t('views.order._show.confirming_this_order')
  33 + = f.submit t('views.order._show.confirm_order'), data: {confirm: t('views.order._show.confirming_this_order')}
34 34 |&emsp;
35 35  
36 36 = link_to_function t('views.order._show.cancel_order'), "orders.order.reload(this, '#{url_for controller: :orders_plugin_order, action: :cancel, id: @order.id}')"
37 37  
  38 +javascript:
  39 + orders.setOrderMaxHeight()
  40 +
... ...
plugins/orders/views/orders_plugin_order/_show_simple.html.slim 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +- actor_name ||= :consumer
  2 +- other_actor_name = if actor_name == :supplier then :consumer else :supplier end
  3 +- @view = true
  4 +
  5 +.order-view id="order-#{order.id}"
  6 + = form_for order, as: :order, url: '', html: {onsubmit: 'return false'} do |f|
  7 + .order-data
  8 + = render "orders_plugin_item/index", order: order, actor_name: actor_name
  9 + = render 'orders_plugin_order/delivery', order: order, actor_name: actor_name, edition: false, f: f
  10 +
  11 +javascript:
  12 + orders.setOrderMaxHeight()
... ...
plugins/orders_cycle/controllers/myprofile/orders_cycle_plugin_cycle_controller.rb
... ... @@ -22,23 +22,24 @@ class OrdersCyclePluginCycleController &lt; OrdersPluginAdminController
22 22 end
23 23  
24 24 def new
25   - if request.put?
  25 + if request.patch?
  26 + # can't use profile.orders_cycle here
26 27 @cycle = OrdersCyclePlugin::Cycle.find params[:id]
27 28  
28 29 params[:cycle][:status] = 'orders' if @open = params[:open] == '1'
29   - @success = @cycle.update_attributes params[:cycle]
  30 + @success = @cycle.update params[:cycle]
30 31  
31 32 if @success
32 33 session[:notice] = t('controllers.myprofile.cycle_controller.cycle_created')
33 34 if params[:sendmail]
34 35 OrdersCyclePlugin::Mailer.delay(run_at: @cycle.start).open_cycle(
35   - @cycle.profile, @cycle ,t('controllers.myprofile.cycle_controller.new_open_cycle')+": "+@cycle.name, @cycle.opening_message)
  36 + @cycle.profile, @cycle, "#{t'controllers.myprofile.cycle_controller.new_open_cycle'}: #{@cycle.name}", @cycle.opening_message)
36 37 end
37 38 else
38 39 render action: :edit
39 40 end
40 41 else
41   - count = OrdersCyclePlugin::Cycle.count conditions: {profile_id: profile}
  42 + count = profile.orders_cycles.maximum(:code) || 1
42 43 @cycle = OrdersCyclePlugin::Cycle.create! profile: profile, status: 'new',
43 44 name: t('controllers.myprofile.cycle_controller.cycle_n_n') % {n: count+1}
44 45 end
... ... @@ -48,13 +49,13 @@ class OrdersCyclePluginCycleController &lt; OrdersPluginAdminController
48 49 # editing an order
49 50 return super if params[:actor_name]
50 51  
51   - @cycle = OrdersCyclePlugin::Cycle.find params[:id]
  52 + @cycle = profile.orders_cycles.find params[:id]
52 53 @products = products
53 54  
54 55 if request.xhr?
55 56 if params[:commit]
56 57 params[:cycle][:status] = 'orders' if @open = params[:open] == '1'
57   - @success = @cycle.update_attributes params[:cycle]
  58 + @success = @cycle.update params[:cycle]
58 59  
59 60 if params[:sendmail]
60 61 OrdersCyclePlugin::Mailer.delay(run_at: @cycle.start).open_cycle(@cycle.profile,
... ... @@ -65,7 +66,7 @@ class OrdersCyclePluginCycleController &lt; OrdersPluginAdminController
65 66 end
66 67  
67 68 def products_load
68   - @cycle = OrdersCyclePlugin::Cycle.find params[:id]
  69 + @cycle = profile.orders_cycles.find params[:id]
69 70 @products = products
70 71  
71 72 if @cycle.add_products_job
... ... @@ -76,34 +77,34 @@ class OrdersCyclePluginCycleController &lt; OrdersPluginAdminController
76 77 end
77 78  
78 79 def destroy
79   - @cycle = OrdersCyclePlugin::Cycle.find params[:id]
  80 + @cycle = profile.orders_cycles.find params[:id]
80 81 @cycle.destroy
81 82 redirect_to action: :index
82 83 end
83 84  
84 85 def step
85   - @cycle = OrdersCyclePlugin::Cycle.find params[:id]
  86 + @cycle = profile.orders_cycles.find params[:id]
86 87 @cycle.step
87 88 @cycle.save!
88 89 redirect_to action: :edit, id: @cycle.id
89 90 end
90 91  
91 92 def step_back
92   - @cycle = OrdersCyclePlugin::Cycle.find params[:id]
  93 + @cycle = profile.orders_cycles.find params[:id]
93 94 @cycle.step_back
94 95 @cycle.save!
95 96 redirect_to action: :edit, id: @cycle.id
96 97 end
97 98  
98 99 def add_missing_products
99   - @cycle = OrdersCyclePlugin::Cycle.find params[:id]
  100 + @cycle = profile.orders_cycles.find params[:id]
100 101 @cycle.add_products
101 102 render partial: 'suppliers_plugin/shared/pagereload'
102 103 end
103 104  
104 105 def report_products
105 106 return super if params[:ids].present?
106   - @cycle = OrdersCyclePlugin::Cycle.find params[:id]
  107 + @cycle = profile.orders_cycles.find params[:id]
107 108 report_file = report_products_by_supplier @cycle.supplier_products_by_suppliers(@cycle.sales.ordered)
108 109  
109 110 send_file report_file, type: 'application/xlsx',
... ... @@ -114,7 +115,7 @@ class OrdersCyclePluginCycleController &lt; OrdersPluginAdminController
114 115  
115 116 def report_orders
116 117 return super if params[:ids].present?
117   - @cycle = OrdersCyclePlugin::Cycle.find params[:id]
  118 + @cycle = profile.orders_cycles.find params[:id]
118 119 report_file = report_orders_by_consumer @cycle.sales.ordered
119 120  
120 121 send_file report_file, type: 'application/xlsx',
... ...
plugins/orders_cycle/controllers/myprofile/orders_cycle_plugin_item_controller.rb
... ... @@ -31,12 +31,12 @@ class OrdersCyclePluginItemController &lt; OrdersPluginItemController
31 31 @item.sale = @order
32 32 @item.product = @offered_product
33 33 if set_quantity_consumer_ordered(params[:quantity_consumer_ordered] || 1)
34   - @item.update_attributes! quantity_consumer_ordered: @quantity_consumer_ordered
  34 + @item.update! quantity_consumer_ordered: @quantity_consumer_ordered
35 35 end
36 36 end
37 37  
38 38 def edit
39   - return redirect_to params.merge(action: :admin_edit) if @admin_edit
  39 + return redirect_to url_for(params.merge action: :admin_edit) if @admin_edit
40 40 super
41 41 @offered_product = @item.product
42 42 @cycle = @order.cycle
... ... @@ -50,7 +50,7 @@ class OrdersCyclePluginItemController &lt; OrdersPluginItemController
50 50 #update on association for total
51 51 @order.items.each{ |i| i.attributes = params[:item] if i.id == @item.id }
52 52  
53   - @item.update_attributes = params[:item]
  53 + @item.update params[:item]
54 54 end
55 55  
56 56 def destroy
... ...
plugins/orders_cycle/controllers/myprofile/orders_cycle_plugin_product_controller.rb
... ... @@ -25,7 +25,7 @@ class OrdersCyclePluginProductController &lt; SuppliersPlugin::ProductController
25 25 def cycle_edit
26 26 @product = OrdersCyclePlugin::OfferedProduct.find params[:id]
27 27 if request.xhr?
28   - @product.update_attributes! params[:product]
  28 + @product.update! params[:product]
29 29 respond_to do |format|
30 30 format.js
31 31 end
... ...
plugins/orders_cycle/controllers/profile/orders_cycle_plugin_order_controller.rb
... ... @@ -38,7 +38,7 @@ class OrdersCyclePluginOrderController &lt; OrdersPluginOrderController
38 38 @order.consumer = @consumer
39 39 @order.cycle = @cycle
40 40 @order.save!
41   - redirect_to params.merge(action: :edit, id: @order.id)
  41 + redirect_to url_for(params.merge action: :edit, id: @order.id)
42 42 end
43 43 end
44 44  
... ... @@ -55,7 +55,7 @@ class OrdersCyclePluginOrderController &lt; OrdersPluginOrderController
55 55 end
56 56 @repeat_order.supplier_delivery = @order.supplier_delivery
57 57 @repeat_order.save!
58   - redirect_to params.merge(action: :edit, id: @repeat_order.id)
  58 + redirect_to url_for(params.merge action: :edit, id: @repeat_order.id)
59 59 else
60 60 @orders = @cycle.consumer_previous_orders(@consumer).last(5).reverse
61 61 @orders.each{ |o| o.enable_product_diff }
... ... @@ -72,7 +72,7 @@ class OrdersCyclePluginOrderController &lt; OrdersPluginOrderController
72 72 if status == 'ordered'
73 73 if @order.items.size > 0
74 74 @order.to_yaml # most strange workaround to avoid a crash in the next line
75   - @order.update_attributes! params[:order]
  75 + @order.update! params[:order]
76 76 session[:notice] = t('orders_plugin.controllers.profile.consumer.order_confirmed')
77 77 else
78 78 session[:notice] = t('orders_plugin.controllers.profile.consumer.can_not_confirm_your_')
... ... @@ -117,12 +117,12 @@ class OrdersCyclePluginOrderController &lt; OrdersPluginOrderController
117 117 end
118 118  
119 119 def reopen
120   - @order.update_attributes! status: 'draft'
  120 + @order.update! status: 'draft'
121 121 render 'edit'
122 122 end
123 123  
124 124 def cancel
125   - @order.update_attributes! status: 'cancelled'
  125 + @order.update! status: 'cancelled'
126 126 session[:notice] = t('orders_plugin.controllers.profile.consumer.order_cancelled')
127 127 render 'edit'
128 128 end
... ... @@ -162,7 +162,7 @@ class OrdersCyclePluginOrderController &lt; OrdersPluginOrderController
162 162 end
163 163  
164 164 def supplier_balloon
165   - @supplier = SuppliersPlugin::Supplier.find params[:id]
  165 + @supplier = profile.suppliers.find params[:id]
166 166 end
167 167 def product_balloon
168 168 @product = OrdersCyclePlugin::OfferedProduct.find params[:id]
... ...
plugins/orders_cycle/db/migrate/20140406155248_refactor_orders_cycle_plugin_cycle_order.rb
... ... @@ -4,9 +4,7 @@ class RefactorOrdersCyclePluginCycleOrder &lt; ActiveRecord::Migration
4 4 rename_column :orders_cycle_plugin_cycle_orders, :order_id, :sale_id
5 5 add_column :orders_cycle_plugin_cycle_orders, :purchase_id, :integer
6 6  
7   - add_index :orders_cycle_plugin_cycle_orders, :sale_id
8 7 add_index :orders_cycle_plugin_cycle_orders, :purchase_id
9   - add_index :orders_cycle_plugin_cycle_orders, [:cycle_id, :sale_id]
10 8 add_index :orders_cycle_plugin_cycle_orders, [:cycle_id, :purchase_id], name: :index_orders_cycle_plugin_cycle_orders_cycle_purchase
11 9 end
12 10  
... ...
plugins/orders_cycle/lib/ext/delivery_plugin/option.rb
... ... @@ -2,7 +2,8 @@ require_dependency &#39;delivery_plugin/option&#39;
2 2  
3 3 class DeliveryPlugin::Option
4 4  
5   - belongs_to :cycle, class_name: 'OrdersCyclePlugin::Cycle',
6   - foreign_key: :owner_id, conditions: ["delivery_plugin_options.owner_type = 'OrdersCyclePlugin::Cycle'"]
  5 + belongs_to :cycle, -> {
  6 + where "delivery_plugin_options.owner_type = 'OrdersCyclePlugin::Cycle'"
  7 + }, class_name: 'OrdersCyclePlugin::Cycle', foreign_key: :owner_id
7 8  
8 9 end
... ...
plugins/orders_cycle/lib/ext/profile.rb
1 1 require_dependency 'profile'
  2 +require_dependency 'community'
2 3  
3   -class Profile
  4 +([Profile] + Profile.descendants).each do |subclass|
  5 +subclass.class_eval do
  6 +
  7 + has_many :orders_cycles, -> {
  8 + order('created_at DESC').
  9 + where "orders_cycle_plugin_cycles.status <> 'new'"
  10 + }, foreign_key: :profile_id, class_name: 'OrdersCyclePlugin::Cycle', dependent: :destroy
4 11  
5   - has_many :orders_cycles, class_name: 'OrdersCyclePlugin::Cycle', dependent: :destroy, order: 'created_at DESC',
6   - conditions: ["orders_cycle_plugin_cycles.status <> 'new'"]
7   - has_many :orders_cycles_without_order, class_name: 'OrdersCyclePlugin::Cycle',
8   - conditions: ["orders_cycle_plugin_cycles.status <> 'new'"]
  12 + has_many :orders_cycles_without_order, -> {
  13 + where "orders_cycle_plugin_cycles.status <> 'new'"
  14 + }, foreign_key: :profile_id, class_name: 'OrdersCyclePlugin::Cycle'
9 15  
10 16 has_many :orders_cycles_sales, through: :orders_cycles, source: :sales
11 17 has_many :orders_cycles_purchases, through: :orders_cycles, source: :purchases
12 18  
13   - has_many :offered_products, class_name: 'OrdersCyclePlugin::OfferedProduct', order: 'products.name ASC'
  19 + has_many :offered_products, -> { reorder 'products.name ASC' }, class_name: 'OrdersCyclePlugin::OfferedProduct'
  20 +
  21 +end
  22 +end
  23 +
  24 +class Profile
14 25  
15 26 def orders_cycles_closed_date_range
16   - list = self.orders_cycles.closing.all order: 'start ASC'
  27 + list = self.orders_cycles.closing.order('start ASC').all
17 28 return DateTime.now..DateTime.now if list.blank?
18 29 list.first.start.to_date..list.last.finish.to_date
19 30 end
... ...
plugins/orders_cycle/models/orders_cycle_plugin/cycle.rb
... ... @@ -32,30 +32,32 @@ class OrdersCyclePlugin::Cycle &lt; ActiveRecord::Base
32 32  
33 33 belongs_to :profile
34 34  
35   - has_many :delivery_options, class_name: 'DeliveryPlugin::Option', dependent: :destroy,
36   - as: :owner, conditions: ["delivery_plugin_options.owner_type = 'OrdersCyclePlugin::Cycle'"]
  35 + has_many :delivery_options, -> {
  36 + where "delivery_plugin_options.owner_type = 'OrdersCyclePlugin::Cycle'"
  37 + }, class_name: 'DeliveryPlugin::Option', dependent: :destroy, as: :owner
37 38 has_many :delivery_methods, through: :delivery_options, source: :delivery_method
38 39  
39   - has_many :cycle_orders, class_name: 'OrdersCyclePlugin::CycleOrder', foreign_key: :cycle_id, dependent: :destroy, order: 'id ASC'
  40 + has_many :cycle_orders, -> { order 'id ASC' }, class_name: 'OrdersCyclePlugin::CycleOrder', foreign_key: :cycle_id, dependent: :destroy
40 41  
41 42 # cannot use :order because of months/years named_scope
42 43 has_many :sales, through: :cycle_orders, source: :sale
43 44 has_many :purchases, through: :cycle_orders, source: :purchase
44 45  
45 46 has_many :cycle_products, foreign_key: :cycle_id, class_name: 'OrdersCyclePlugin::CycleProduct', dependent: :destroy
46   - has_many :products, through: :cycle_products, source: :product, order: 'products.name ASC',
47   - include: [ :from_2x_products, :from_products, {profile: :domains}, ]
  47 + has_many :products, -> {
  48 + includes(:from_2x_products, :from_products, {profile: :domains})
  49 + }, through: :cycle_products, class_name: 'OrdersCyclePlugin::OfferedProduct', source: :product
48 50  
49   - has_many :consumers, through: :sales, source: :consumer, order: 'name ASC', uniq: true
50   - has_many :suppliers, through: :products, source: :suppliers, order: 'suppliers_plugin_suppliers.name ASC', uniq: true
51   - has_many :orders_suppliers, through: :sales, source: :profile, order: 'name ASC'
  51 + has_many :consumers, -> { distinct.reorder 'name ASC' }, through: :sales, source: :consumer
  52 + has_many :suppliers, -> { group 'suppliers_plugin_suppliers.id' }, through: :products
  53 + has_many :orders_suppliers, -> { reorder 'name ASC' }, through: :sales, source: :profile
52 54  
53   - has_many :from_products, through: :products, order: 'name ASC', uniq: true
54   - has_many :supplier_products, through: :products, order: 'name ASC', uniq: true
55   - has_many :product_categories, through: :products, order: 'name ASC', uniq: true
  55 + has_many :from_products, -> { distinct.reorder 'name ASC' }, through: :products
  56 + has_many :supplier_products, -> { distinct.reorder 'name ASC' }, through: :products
  57 + has_many :product_categories, -> { distinct.reorder 'name ASC' }, through: :products
56 58  
57   - has_many :orders_confirmed, through: :cycle_orders, source: :sale, order: 'id ASC',
58   - conditions: ['orders_plugin_orders.ordered_at IS NOT NULL']
  59 + has_many :orders_confirmed, -> { reorder('id ASC').where 'orders_plugin_orders.ordered_at IS NOT NULL' },
  60 + through: :cycle_orders, source: :sale
59 61  
60 62 has_many :items_selled, through: :sales, source: :items
61 63 has_many :items_purchased, through: :purchases, source: :items
... ... @@ -64,16 +66,16 @@ class OrdersCyclePlugin::Cycle &lt; ActiveRecord::Base
64 66  
65 67 has_many :ordered_suppliers, through: :orders_confirmed, source: :suppliers
66 68  
67   - has_many :ordered_offered_products, through: :orders_confirmed, source: :offered_products, uniq: true, include: [:suppliers]
68   - has_many :ordered_distributed_products, through: :orders_confirmed, source: :distributed_products, uniq: true, include: [:suppliers]
69   - has_many :ordered_supplier_products, through: :orders_confirmed, source: :supplier_products, uniq: true, include: [:suppliers]
  69 + has_many :ordered_offered_products, -> { distinct.includes :suppliers }, through: :orders_confirmed, source: :offered_products
  70 + has_many :ordered_distributed_products, -> { distinct.includes :suppliers }, through: :orders_confirmed, source: :distributed_products
  71 + has_many :ordered_supplier_products, -> { distinct.includes :suppliers }, through: :orders_confirmed, source: :supplier_products
70 72  
71 73 has_many :volunteers_periods, class_name: 'VolunteersPlugin::Period', as: :owner
72 74 has_many :volunteers, through: :volunteers_periods, source: :profile
73 75 attr_accessible :volunteers_periods_attributes
74 76 accepts_nested_attributes_for :volunteers_periods, allow_destroy: true
75 77  
76   - scope :has_volunteers_periods, -> {uniq.joins [:volunteers_periods]}
  78 + scope :has_volunteers_periods, -> { distinct.joins :volunteers_periods }
77 79  
78 80 # status scopes
79 81 scope :on_edition, -> { where status: 'edition' }
... ... @@ -83,33 +85,29 @@ class OrdersCyclePlugin::Cycle &lt; ActiveRecord::Base
83 85 scope :on_delivery, -> { where status: 'delivery' }
84 86 scope :on_closing, -> { where status: 'closing' }
85 87  
86   - scope :defuncts, conditions: ["status = 'new' AND created_at < ?", 2.days.ago]
87   - scope :not_new, conditions: ["status <> 'new'"]
88   - scope :on_orders, lambda {
89   - {conditions: ["status = 'orders' AND ( (start <= :now AND finish IS NULL) OR (start <= :now AND finish >= :now) )",
90   - {now: DateTime.now}]}
  88 + scope :defuncts, -> { where "status = 'new' AND created_at < ?", 2.days.ago }
  89 + scope :not_new, -> { where "status <> 'new'" }
  90 + scope :on_orders, -> {
  91 + where "status = 'orders' AND ( (start <= :now AND finish IS NULL) OR (start <= :now AND finish >= :now) )", now: DateTime.now
91 92 }
92   - scope :not_on_orders, lambda {
93   - {conditions: ["NOT (status = 'orders' AND ( (start <= :now AND finish IS NULL) OR (start <= :now AND finish >= :now) ) )",
94   - {now: DateTime.now}]}
  93 + scope :not_on_orders, -> {
  94 + where "NOT (status = 'orders' AND ( (start <= :now AND finish IS NULL) OR (start <= :now AND finish >= :now) ) )", now: DateTime.now
95 95 }
96   - scope :opened, conditions: ["status <> 'new' AND status <> 'closing'"]
97   - scope :closing, conditions: ["status = 'closing'"]
98   - scope :by_status, lambda { |status| { conditions: {status: status} } }
  96 + scope :opened, -> { where "status <> 'new' AND status <> 'closing'" }
  97 + scope :closing, -> { where "status = 'closing'" }
  98 + scope :by_status, -> (status) { where status: status }
99 99  
100   - scope :months, select: 'DISTINCT(EXTRACT(months FROM start)) as month', order: 'month DESC'
101   - scope :years, select: 'DISTINCT(EXTRACT(YEAR FROM start)) as year', order: 'year DESC'
  100 + scope :months, -> { order('month DESC').select 'DISTINCT(EXTRACT(months FROM start)) as month' }
  101 + scope :years, -> { order('year DESC').select 'DISTINCT(EXTRACT(YEAR FROM start)) as year' }
102 102  
103   - scope :by_month, lambda { |month| {
104   - conditions: [ 'EXTRACT(month FROM start) <= :month AND EXTRACT(month FROM finish) >= :month', { month: month } ]}
  103 + scope :by_month, -> (month) {
  104 + where 'EXTRACT(month FROM start) <= :month AND EXTRACT(month FROM finish) >= :month', month: month
105 105 }
106   - scope :by_year, lambda { |year| {
107   - conditions: [ 'EXTRACT(year FROM start) <= :year AND EXTRACT(year FROM finish) >= :year', { year: year } ]}
  106 + scope :by_year, -> (year) {
  107 + where 'EXTRACT(year FROM start) <= :year AND EXTRACT(year FROM finish) >= :year', year: year
108 108 }
109   - scope :by_range, lambda { |range| {
110   - conditions: [ 'start BETWEEN :start AND :finish OR finish BETWEEN :start AND :finish',
111   - { start: range.first, finish: range.last }
112   - ]}
  109 + scope :by_range, -> (range) {
  110 + where 'start BETWEEN :start AND :finish OR finish BETWEEN :start AND :finish', start: range.first, finish: range.last
113 111 }
114 112  
115 113 validates_presence_of :profile
... ... @@ -122,7 +120,7 @@ class OrdersCyclePlugin::Cycle &lt; ActiveRecord::Base
122 120 validate :validate_orders_dates, if: :not_new?
123 121 validate :validate_delivery_dates, if: :not_new?
124 122  
125   - before_validation :step_new
  123 + before_save :step_new
126 124 before_validation :update_orders_status
127 125 before_save :add_products_on_edition_state
128 126 after_create :delay_purge_profile_defuncts
... ... @@ -212,9 +210,7 @@ class OrdersCyclePlugin::Cycle &lt; ActiveRecord::Base
212 210 end
213 211  
214 212 def products_for_order
215   - # FIXME name alias conflict
216   - #self.products.unarchived.with_price.order('products.name ASC')
217   - self.products.unarchived.with_price
  213 + self.products.unarchived.alphabetically.with_price
218 214 end
219 215  
220 216 def supplier_products_by_suppliers orders = self.sales.ordered
... ... @@ -261,27 +257,27 @@ class OrdersCyclePlugin::Cycle &lt; ActiveRecord::Base
261 257 end
262 258  
263 259 def step_new
264   - return if new_record?
  260 + return if self.new_record?
265 261 self.step if self.new?
266 262 end
267 263  
268 264 def update_sales_status from, to
269 265 sales = self.sales.where(status: from.to_s)
270 266 sales.each do |sale|
271   - sale.update_attributes status: to.to_s
  267 + sale.update status: to.to_s
272 268 end
273 269 end
274 270  
275 271 def update_purchases_status from, to
276 272 purchases = self.purchases.where(status: from.to_s)
277 273 purchases.each do |purchase|
278   - purchase.update_attributes status: to.to_s
  274 + purchase.update status: to.to_s
279 275 end
280 276 end
281 277  
282 278 def update_orders_status
283   - # step orders to next_status on status change
284   - return if self.new? or self.status_was == "new" or self.status_was == self.status
  279 + return if self.new? or self.status_was == "new"
  280 + return if self.status_was == self.status
285 281  
286 282 # Don't rewind confirmed sales
287 283 unless self.status_was == 'orders' and self.status == 'edition'
... ...
plugins/orders_cycle/models/orders_cycle_plugin/offered_product.rb
1 1 class OrdersCyclePlugin::OfferedProduct < SuppliersPlugin::BaseProduct
2 2  
  3 + # we work use frozen attributes
  4 + self.default_delegate_enable = false
  5 +
3 6 # FIXME: WORKAROUND for https://github.com/rails/rails/issues/6663
4 7 # OrdersCyclePlugin::Sale.find(3697).cycle.suppliers returns empty without this
5 8 def self.finder_needs_type_condition?
... ... @@ -19,10 +22,10 @@ class OrdersCyclePlugin::OfferedProduct &lt; SuppliersPlugin::BaseProduct
19 22 has_one :sources_supplier_product, through: :from_product, source: :sources_from_product
20 23 # necessary only due to the override of sources_supplier_products, as rails somehow caches the old reference
21 24 # copied from suppliers/lib/ext/product
22   - has_many :supplier_products, through: :sources_supplier_products, source: :from_product, order: 'id ASC'
23   - has_one :supplier_product, through: :sources_supplier_product, source: :from_product, order: 'id ASC', autosave: true
24   - has_many :suppliers, through: :sources_supplier_products, uniq: true, order: 'id ASC'
25   - has_one :supplier, through: :sources_supplier_product, order: 'id ASC'
  25 + has_many :supplier_products, -> { order 'id ASC' }, through: :sources_supplier_products, source: :from_product
  26 + has_one :supplier_product, -> { order 'id ASC' }, through: :sources_supplier_product, source: :from_product, autosave: true
  27 + has_many :suppliers, -> { distinct.order 'id ASC' }, through: :sources_supplier_products
  28 + has_one :supplier, -> { order 'id ASC' }, through: :sources_supplier_product
26 29  
27 30 instance_exec &OrdersPlugin::Item::DefineTotals
28 31 extend CurrencyHelper::ClassMethods
... ... @@ -31,9 +34,7 @@ class OrdersCyclePlugin::OfferedProduct &lt; SuppliersPlugin::BaseProduct
31 34 # test this before use!
32 35 #validates_presence_of :cycle
33 36  
34   - # remove on rails4
35   - scope :with_price, conditions: 'products.price > 0'
36   - scope :with_product_category_id, lambda { |id| { conditions: {product_category_id: id} } }
  37 + # override SuppliersPlugin::BaseProduct
37 38 def self.search_scope scope, params
38 39 scope = scope.from_supplier_id params[:supplier_id] if params[:supplier_id].present?
39 40 scope = scope.with_available(if params[:available] == 'true' then true else false end) if params[:available].present?
... ... @@ -50,19 +51,20 @@ class OrdersCyclePlugin::OfferedProduct &lt; SuppliersPlugin::BaseProduct
50 51 op.profile = product.profile
51 52 op.type = self.name
52 53  
53   - op.from_products << product
  54 + op.from_product = product
54 55 cycle.products << op if cycle
55 56  
56 57 op
57 58 end
58 59  
59 60 # always recalculate in case something has changed
  61 + # FIXME: really? it is already copied!
60 62 def margin_percentage
61   - return self['margin_percentage'] if price.nil? or buy_price.nil? or price.zero? or buy_price.zero?
  63 + return super if price.nil? or buy_price.nil? or price.zero? or buy_price.zero?
62 64 ((price / buy_price) - 1) * 100
63 65 end
64 66 def margin_percentage= value
65   - self['margin_percentage'] = value
  67 + super value
66 68 self.price = self.price_with_margins buy_price
67 69 end
68 70  
... ... @@ -71,22 +73,15 @@ class OrdersCyclePlugin::OfferedProduct &lt; SuppliersPlugin::BaseProduct
71 73 end
72 74  
73 75 # reimplement to don't destroy this, keeping history in cycles
74   - # offered products copy attributes
  76 + # as offered products copy attributes.
75 77 def dependent?
76 78 false
77 79 end
78 80  
79   - # cycle products freezes properties and don't use the original
80   - DEFAULT_ATTRIBUTES.each do |a|
81   - define_method "default_#{a}" do
82   - nil
83   - end
84   - end
85   -
86 81 FROOZEN_DEFAULT_ATTRIBUTES = DEFAULT_ATTRIBUTES
87 82 def freeze_default_attributes from_product
88 83 FROOZEN_DEFAULT_ATTRIBUTES.each do |attr|
89   - self[attr] = from_product.send(attr) if from_product[attr] or from_product.respond_to? attr
  84 + self.send "#{attr}=", from_product.send(attr) if from_product[attr] or from_product.respond_to? attr
90 85 end
91 86 end
92 87  
... ...
plugins/orders_cycle/models/orders_cycle_plugin/order_base.rb
... ... @@ -14,26 +14,18 @@ module OrdersCyclePlugin::OrderBase
14 14 self.cycle_sales.includes(:cycle).map(&:cycle) + self.cycle_purchases.includes(:cycle).map(&:cycle)
15 15 end
16 16  
17   - # TODO: test if the has_one defined on Sale/Purchase works and these are not needed
18   - def cycle
19   - self.cycles.first
20   - end
21   - def cycle= cycle
22   - self.cycles = [cycle]
23   - end
24   -
25 17 scope :for_cycle, -> (cycle) {
26 18 where('orders_cycle_plugin_cycles.id = ?', cycle.id).
27 19 joins(:cycles)
28 20 }
29 21  
30   - has_many :items, class_name: 'OrdersCyclePlugin::Item', foreign_key: :order_id, dependent: :destroy, order: 'name ASC'
  22 + has_many :items, -> { order 'name ASC' }, class_name: 'OrdersCyclePlugin::Item', foreign_key: :order_id, dependent: :destroy
31 23  
32   - has_many :offered_products, through: :items, source: :offered_product, uniq: true
33   - has_many :distributed_products, through: :offered_products, source: :from_products, uniq: true
34   - has_many :supplier_products, through: :distributed_products, source: :from_products, uniq: true
  24 + has_many :offered_products, -> { distinct }, through: :items, source: :offered_product
  25 + has_many :distributed_products, -> { distinct }, through: :offered_products, source: :from_products
  26 + has_many :supplier_products, -> { distinct }, through: :distributed_products, source: :from_products
35 27  
36   - has_many :suppliers, through: :supplier_products, uniq: true
  28 + has_many :suppliers, -> { distinct }, through: :supplier_products
37 29  
38 30 extend CodeNumbering::ClassMethods
39 31 code_numbering :code, scope: (proc do
... ...
plugins/orders_cycle/models/orders_cycle_plugin/sale.rb
... ... @@ -24,10 +24,6 @@ class OrdersCyclePlugin::Sale &lt; OrdersPlugin::Sale
24 24 super and self.cycle.orders?
25 25 end
26 26  
27   - def supplier_delivery
28   - super || (self.cycle.delivery_methods.first rescue nil)
29   - end
30   -
31 27 def change_purchases
32 28 return unless self.status_was.present?
33 29 if self.ordered_at_was.nil? and self.ordered_at.present?
... ...
plugins/orders_cycle/test/factories.rb 0 → 100644
... ... @@ -0,0 +1,57 @@
  1 +module OrdersCyclePlugin::Factory
  2 +
  3 + def defaults_for_suppliers_plugin_supplier
  4 + {:profile => build(Profile),
  5 + :consumer => build(Profile)}
  6 + end
  7 +
  8 + def defaults_for_suppliers_plugin_distributed_product attrs = {}
  9 + profile = attrs[:profile] || build(Profile)
  10 + {:profile => profile, :name => "product-#{factory_num_seq}", :price => 2.0,
  11 + :product => build(Product, :enterprise => profile.profile, :price => 2.0),
  12 + :supplier => build(SuppliersPlugin::Supplier, :profile => profile, :consumer => profile)}
  13 + end
  14 +
  15 + def defaults_for_orders_cycle_plugin_offered_product attrs = {}
  16 + hash = defaults_for_orders_cycle_plugin_product(attrs)
  17 + profile = hash[:profile]
  18 + hash.merge({
  19 + :from_products => [build(SuppliersPlugin::DistributedProduct, :profile => profile)]})
  20 + end
  21 +
  22 + def defaults_for_delivery_plugin_method
  23 + {:profile => build(OrdersCyclePlugin::Profile),
  24 + :name => "My delivery #{factory_num_seq.to_s}",
  25 + :delivery_type => 'deliver'}
  26 + end
  27 +
  28 + def defaults_for_delivery_plugin_option
  29 + {:cycle => build(OrdersCyclePlugin::Cycle),
  30 + :delivery_method => build(DeliveryPlugin::Method)}
  31 + end
  32 +
  33 + def defaults_for_orders_plugin_order attrs = {}
  34 + profile = attrs[:profile] || build(OrdersCyclePlugin::Profile)
  35 + {:status => 'ordered',
  36 + :cycle => build(OrdersCyclePlugin::Cycle, :profile => profile),
  37 + :consumer => build(OrdersCyclePlugin::Profile),
  38 + :supplier_delivery => build(DeliveryPlugin::Method, :profile => profile),
  39 + :consumer_delivery => build(DeliveryPlugin::Method, :profile => profile)}
  40 + end
  41 +
  42 + def defaults_for_orders_plugin_items
  43 + {:order => build(OrdersPlugin::Order),
  44 + :product => build(OrdersCyclePlugin::OfferedProduct),
  45 + :quantity_shipped => 1.0, :quantity_ordered => 2.0, :quantity_accepted => 3.0,
  46 + :price_shipped => 10.0, :price_ordered => 20.0, :price_accepted => 30.0}
  47 + end
  48 +
  49 + def defaults_for_orders_cycle_plugin_cycle
  50 + {:profile => build(OrdersCyclePlugin::Profile), :status => 'orders',
  51 + :name => 'weekly', :start => Time.now, :finish => Time.now+1.days}
  52 + end
  53 +
  54 +end
  55 +
  56 +Noosfero::Factory.register_extension OrdersCyclePlugin::Factory
  57 +
... ...
plugins/orders_cycle/test/functional/orders_cycle_plugin/order_controller_test.rb 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +require "#{File.dirname(__FILE__)}/../../test_helper"
  2 +
  3 +class OrdersCyclePlugin::OrderControllerTest < Test::Unit::TestCase
  4 +
  5 + def setup
  6 + @controller = OrdersCyclePluginOrderController.new
  7 + @request = ActionController::TestRequest.new
  8 + @response = ActionController::TestResponse.new
  9 + end
  10 +
  11 +
  12 +end
... ...
plugins/orders_cycle/test/functional/orders_cycle_plugin/session_controller_test.rb 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +require "#{File.dirname(__FILE__)}/../../test_helper"
  2 +
  3 +class OrdersCyclePlugin::CycleControllerTest < Test::Unit::TestCase
  4 +
  5 + def setup
  6 + @controller = OrdersCyclePluginCycleController.new
  7 + @request = ActionController::TestRequest.new
  8 + @response = ActionController::TestResponse.new
  9 + end
  10 +
  11 + should 'create a new cycle' do
  12 + end
  13 +
  14 +end
... ...
plugins/orders_cycle/test/test_helper.rb 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +require File.dirname(__FILE__) + '/../../../test/test_helper'
  2 +require 'spec'
  3 +
  4 +class ActiveRecord::TestCase < ActiveSupport::TestCase
  5 + include OrdersCyclePluginFactory
  6 +end
... ...
plugins/orders_cycle/test/unit/orders_cycle_plugin/cycle_test.rb 0 → 100644
... ... @@ -0,0 +1,27 @@
  1 +require "#{File.dirname(__FILE__)}/../../test_helper"
  2 +
  3 +class OrdersCyclePlugin::CycleTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @profile = Enterprise.create!(:name => "trocas verdes", :identifier => "trocas-verdes")
  7 + @pc = ProductCategory.create!(:name => 'frutas', :environment_id => 1)
  8 + @profile.products = [Product.create!(:name => 'banana', :product_category => @pc),
  9 + Product.new(:name => 'mandioca', :product_category => @pc), Product.new(:name => 'alface', :product_category => @pc)]
  10 +
  11 + profile.offered_products = @profile.products.map{ |p| OrdersCyclePlugin::OfferedProduct.create!(:product => p) }
  12 + DeliveryPlugin::Method.create! :name => 'at home', :delivery_type => 'pickup', :profile => @profile
  13 + @cycle = OrdersCyclePlugin::Cycle.create!(:profile => @profile)
  14 + end
  15 +
  16 + should 'add products from profile after create' do
  17 + assert_equal @cycle.products.collect(&:product_id), @profile.products.collect(&:id)
  18 + end
  19 +
  20 + should 'have at least one delivery method unless in edition status' do
  21 + cycle = OrdersCyclePlugin::Cycle.create! :profile => @profile, :name => "Testes batidos", :start => DateTime.now, :status => 'edition'
  22 + assert cycle
  23 + cycle.status = 'orders'
  24 + assert_nil cycle.save!
  25 + end
  26 +
  27 +end
... ...
plugins/orders_cycle/test/unit/orders_cycle_plugin/offered_product_test.rb 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +require "#{File.dirname(__FILE__)}/../../test_helper"
  2 +
  3 +class OrdersCyclePlugin::OfferedProductTest < ActiveSupport::TestCase
  4 +
  5 +
  6 +end
... ...
plugins/orders_cycle/test/unit/profile_test.rb 0 → 100644
... ... @@ -0,0 +1,127 @@
  1 +require "#{File.dirname(__FILE__)}/../../test_helper"
  2 +
  3 +class OrdersCyclePlugin::ProfileTest < ActiveRecord::TestCase
  4 +
  5 + def setup
  6 + @profile = build(Profile)
  7 + @invisible_profile = build(Enterprise, :visible => false)
  8 + @other_profile = build(Enterprise)
  9 + @profile = build(OrdersCyclePlugin::profile, :profile => @profile)
  10 + @self_supplier = build(OrdersCyclePlugin::Supplier, :consumer => @profile, :profile => @profile)
  11 + @dummy_supplier = build(OrdersCyclePlugin::Supplier, :consumer => @profile, :profile => @dummy_profile)
  12 + @other_supplier = build(OrdersCyclePlugin::Supplier, :consumer => @profile, :profile => @other_profile)
  13 + end
  14 +
  15 + attr_accessor :profile, :invisible_profile, :other_profile,
  16 + :self_supplier, :dummy_supplier, :other_supplier
  17 +
  18 + should 'respond to name methods' do
  19 + profile.expects(:name).returns('name')
  20 + assert_equal 'name', profile.name
  21 + end
  22 +
  23 + should 'respond to dummy methods' do
  24 + profile.dummy = true
  25 + assert_equal true, profile.dummy?
  26 + profile.dummy = false
  27 + assert_equal false, profile.dummy
  28 + end
  29 +
  30 + should "return closed cycles' date range" do
  31 + DateTime.expects(:now).returns(1).at_least_once
  32 + assert_equal 1..1, profile.orders_cycles_closed_date_range
  33 + s1 = create(OrdersCyclePlugin::Cycle, :profile => profile, :start => Time.now-1.days, :finish => nil)
  34 + s2 = create(OrdersCyclePlugin::Cycle, :profile => profile, :finish => Time.now+1.days, :start => Time.now)
  35 + assert_equal (s1.start.to_date..s2.finish.to_date), profile.orders_cycles_closed_date_range
  36 + end
  37 +
  38 + should 'return abbreviation or the name' do
  39 + profile.name_abbreviation = 'coll.'
  40 + profile.profile.name = 'collective'
  41 + assert_equal 'coll.', profile.abbreviation_or_name
  42 + profile.name_abbreviation = nil
  43 + assert_equal 'collective', profile.abbreviation_or_name
  44 + end
  45 +
  46 + ###
  47 + # Products
  48 + ###
  49 +
  50 + should "default products's margins when asked" do
  51 + profile.update! :margin_percentage => 10
  52 + product = create(SuppliersPlugin::DistributedProduct, :profile => profile, :supplier => profile.self_supplier,
  53 + :price => 10, :default_margin_percentage => false)
  54 + cycle = create(OrdersCyclePlugin::Cycle, :profile => profile)
  55 + sproduct = cycle.products.first
  56 + sproduct.update! :margin_percentage => 5
  57 + cycleclosed = create(OrdersCyclePlugin::Cycle, :profile => profile, :status => 'closed')
  58 +
  59 + profile.orders_cycles_products_default_margins
  60 + product.reload
  61 + sproduct.reload
  62 + assert_equal true, product.default_margin_percentage
  63 + assert_equal sproduct.margin_percentage, profile.margin_percentage
  64 + end
  65 +
  66 + should 'return not yet distributed products' do
  67 + profile.save!
  68 + other_profile.save!
  69 + other_supplier.save!
  70 + product = create(SuppliersPlugin::DistributedProduct, :profile => other_profile, :supplier => other_profile.self_supplier)
  71 + profile.add_supplier_products other_supplier
  72 + product2 = create(SuppliersPlugin::DistributedProduct, :profile => other_profile, :supplier => other_profile.self_supplier)
  73 + assert_equal [product2], profile.not_distributed_products(other_supplier)
  74 + end
  75 +
  76 + ###
  77 + # Suppliers
  78 + ###
  79 +
  80 + should 'add supplier' do
  81 + @profile.save!
  82 + @other_profile.save!
  83 +
  84 + assert_difference OrdersCyclePlugin::Supplier, :count do
  85 + @profile.add_supplier @other_profile
  86 + end
  87 + assert @profile.suppliers_profiles.include?(@other_profile)
  88 + assert @other_profile.consumers_profiles.include?(@profile)
  89 + end
  90 +
  91 + should "add all supplier's products when supplier is added" do
  92 + @profile.save!
  93 + @other_profile.save!
  94 + product = create(SuppliersPlugin::DistributedProduct, :profile => @other_profile)
  95 + @profile.add_supplier @other_profile
  96 + assert_equal [product], @profile.from_products
  97 + end
  98 +
  99 + should 'remove supplier' do
  100 + @profile.save!
  101 + @other_profile.save!
  102 +
  103 + @profile.add_supplier @other_profile
  104 + assert_difference OrdersCyclePlugin::Supplier, :count, -1 do
  105 + assert_difference RoleAssignment, :count, -1 do
  106 + @profile.remove_supplier @other_profile
  107 + end
  108 + end
  109 + assert !@profile.suppliers_profiles.include?(@other_profile)
  110 + end
  111 +
  112 + should "archive supplier's products when supplier is removed" do
  113 + @profile.save!
  114 + @other_profile.save!
  115 + product = create(SuppliersPlugin::DistributedProduct, :profile => @other_profile)
  116 + @profile.add_supplier @other_profile
  117 + @profile.remove_supplier @other_profile
  118 + assert_equal [product], @profile.from_products
  119 + assert_equal 1, @profile.distributed_products.archived.count
  120 + end
  121 +
  122 + should 'create self supplier automatically' do
  123 + profile = create(OrdersCyclePlugin::profile, :profile => @profile)
  124 + assert_equal 1, profile.suppliers.count
  125 + end
  126 +
  127 +end
... ...
plugins/orders_cycle/views/orders_cycle_plugin_cycle/_cycle_sales.html.slim
1 1 = render 'orders_plugin_admin/sales', actors: @cycle.consumers, orders_owner: @cycle, owner_id: @cycle.id,
2 2 orders: @cycle.sales.default_order.paginate(per_page: 30, page: params[:page])
  3 +
... ...
plugins/orders_cycle/views/orders_cycle_plugin_cycle/_edit_fields.html.slim
... ... @@ -53,6 +53,5 @@ h3= t(&#39;views.cycle._edit_fields.general_settings&#39;)
53 53 = link_to t('views.cycle._edit_fields.cancel_changes'), @cycle.new? ? {action: :index} : params
54 54 - unless @cycle.new?
55 55 |&nbsp;
56   - = link_to t('views.cycle._edit_fields.remove'), {action: :destroy, id: @cycle.id}, confirm: t('views.cycle._edit_fields.confirm_remove')
57   -
  56 + = link_to t('views.cycle._edit_fields.remove'), {action: :destroy, id: @cycle.id}, data: {confirm: t('views.cycle._edit_fields.confirm_remove')}
58 57  
... ...
plugins/orders_cycle/views/orders_cycle_plugin_cycle/_product_lines.html.erb
1 1 <div class="cycle-products-table sortable-table">
2 2 <%= pagination_links @products %>
3 3 <div id="result-count">
4   - <small><%= t('views.cycle._product_lines.showing_pcount_produc') % {:pcount => @products.length, :allpcount => @cycle.products.count} %></small>
  4 + <small><%= t('views.cycle._product_lines.showing_pcount_produc') % {pcount: @products.length, allpcount: @cycle.products.count} %></small>
5 5 </div>
6 6 <div class='table-header'>
7 7 <span class='box-field category'><%= t('views.cycle._product_lines.category') %></span>
... ... @@ -17,7 +17,7 @@
17 17 <div class='table-content'>
18 18 <% @products.each do |p| %>
19 19 <div id="cycle-product-<%=p.id%>" class='cycle-product value-row' toggle-edit="orders_cycle.offered_product.edit();">
20   - <%= render 'orders_cycle_plugin_product/cycle_edit', :p => p %>
  20 + <%= render 'orders_cycle_plugin_product/cycle_edit', p: p %>
21 21 </div>
22 22 <% end %>
23 23 <%= javascript_tag do %>
... ...
plugins/orders_cycle/views/orders_cycle_plugin_cycle/_products_loading.html.erb
... ... @@ -3,7 +3,7 @@
3 3 <%= t('views.cycle._products_loading') %>
4 4  
5 5 <%= javascript_tag do %>
6   - orders_cycle.cycle.products.load_url = <%= url_for(:action => :products_load, :id => @cycle.id).to_json %>
  6 + orders_cycle.cycle.products.load_url = <%= url_for(action: :products_load, id: @cycle.id).to_json %>
7 7 orders_cycle.cycle.products.load()
8 8 <% end %>
9 9 </div>
... ...
plugins/orders_cycle/views/orders_cycle_plugin_cycle/_timeline.html.slim 0 → 100644
... ... @@ -0,0 +1,30 @@
  1 +- actions = true if actions.nil? and @admin
  2 +- listing = false if listing.nil?
  3 +- view_status = if OrdersCyclePlugin::Cycle::Statuses.index(params[:view]) then params[:view] else cycle.status end
  4 +- status_text = t"models.cycle.statuses.#{view_status}"
  5 +
  6 +.cycle-timeline
  7 + - OrdersCyclePlugin::Cycle::UserStatuses.each do |status|
  8 + - klass = "cycle-timeline-item #{timeline_class cycle, status, view_status}"
  9 + - name = t("models.cycle.statuses.#{status}")
  10 + = link_to name, params.merge(action: :edit, id: cycle.id, view: status), class: klass
  11 +
  12 +- if listing
  13 + .dates-brief
  14 + .date
  15 + span.field-title= t'views.cycle._brief.orders'
  16 + |&emsp;
  17 + span= datetime_period_short cycle.start, cycle.finish
  18 + .date
  19 + span.field-title= t'views.cycle._brief.delivery'
  20 + |&emsp;
  21 + span= datetime_period_short cycle.delivery_start, cycle.delivery_finish
  22 +
  23 +- if actions
  24 + .actions-bar
  25 + - if cycle.status == view_status and cycle.status != 'closing'
  26 + = link_to t('views.cycle._timeline.close_status') % {status: status_text},
  27 + {action: :step, id: cycle.id, method: :post},
  28 + {confirm: t('views.cycle._timeline.are_you_sure_you_want_to_close') % {status: status_text}, class: 'action-button'}
  29 + = render "orders_cycle_plugin_cycle/actions/#{view_status}", cycle: cycle rescue nil
  30 +
... ...
plugins/orders_cycle/views/orders_cycle_plugin_cycle/actions/_closed.html.slim 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +- if cycle.status == 'closing'
  2 + = link_to t('views.cycle._timeline.reopen_orders_period'), {action: :step_back, id: cycle.id, method: :post},
  3 + id: 'cycle-open-cycle', class: 'action-button', data: {confirm: t('views.cycle._timeline.are_you_sure_you_want_to_reopen')}
  4 +
... ...
plugins/orders_cycle/views/orders_cycle_plugin_cycle/edit.js.erb
1 1 <% if params[:commit] %>
2 2 <% if @success and @open %>
3   - window.location = <%= url_for(:action => :edit, :id => @cycle.id).to_json %>
  3 + window.location = <%= url_for(action: :edit, id: @cycle.id).to_json %>
4 4 <% else %>
5   - noosfero.modal.html(<%= render('edit_popin', :cycle => @cycle).to_json %>)
  5 + noosfero.modal.html(<%= render('edit_popin', cycle: @cycle).to_json %>)
6 6 <% end %>
7 7 <% else %>
8 8 jQuery("#cycle-products .table").replaceWith('<%= escape_javascript render('product_lines') %>')
... ...
plugins/orders_cycle/views/orders_cycle_plugin_gadgets/cycles.html.erb
... ... @@ -2,11 +2,11 @@
2 2  
3 3 <div id="cycles-gadget">
4 4 <% profile.orders_cycles.on_orders.each do |cycle| %>
5   - <%= render :partial => 'orders_cycle_plugin_gadgets/cycle', :locals => {:cycle => cycle} %>
  5 + <%= render 'orders_cycle_plugin_gadgets/cycle', cycle: cycle %>
6 6 <% end %>
7 7  
8 8 <div>
9   - <%= link_to t('views.gadgets.cycles.all_cycles'), {:controller => :orders_cycle_plugin_order, :profile => profile.identifier, :action => :index}, :id => "all-cycles" %>
  9 + <%= link_to t('views.gadgets.cycles.all_cycles'), {controller: :orders_cycle_plugin_order, profile: profile.identifier, action: :index}, id: "all-cycles" %>
10 10 <div class='clean'></div>
11 11 </div>
12 12 </div>
... ...
plugins/orders_cycle/views/orders_cycle_plugin_order/cycle_edit.rjs
1   -page.replace_html "order-#{@order.id}", :partial => 'cycle_edit', :locals => {:order => @order}
2   -page.replace_html "cycle-products-sums", :partial => 'orders_cycle_plugin_cycle/orders_suppliers', :locals => {:cycle => @order.cycle}
  1 +page.replace_html "order-#{@order.id}", partial: 'cycle_edit', locals: {order: @order}
  2 +page.replace_html "cycle-products-sums", partial: 'orders_cycle_plugin_cycle/orders_suppliers', locals: {cycle: @order.cycle}
3 3 page << "toggle_edit.value_row.reload();"
... ...
plugins/shopping_cart/controllers/shopping_cart_plugin_controller.rb
... ... @@ -119,7 +119,8 @@ class ShoppingCartPluginController &lt; OrdersPluginController
119 119 @settings = cart_profile.shopping_cart_settings
120 120 @cart = cart
121 121 @profile = cart_profile
122   - @order = profile.sales.build consumer: user
  122 + supplier_delivery = profile.delivery_methods.first
  123 + @order = build_order self.cart[:items], supplier_delivery
123 124  
124 125 last_delivery_option_id = session[:cart][:last_delivery_option_id] if session[:cart]
125 126 @order.supplier_delivery = profile.delivery_methods.where(id: last_delivery_option_id).first if last_delivery_option_id
... ... @@ -192,7 +193,8 @@ class ShoppingCartPluginController &lt; OrdersPluginController
192 193 @profile = cart_profile
193 194 supplier_delivery = @profile.delivery_methods.where(id: params[:order][:supplier_delivery_id]).first
194 195 order = build_order self.cart[:items], supplier_delivery
195   - total_price = order.total_price
  196 + order.supplier_delivery = supplier_delivery
  197 + total_price = order.total
196 198 render json: {
197 199 ok: true,
198 200 delivery_price: float_to_currency_cart(supplier_delivery.cost(total_price), environment, unit: ''),
... ...
plugins/shopping_cart/db/migrate/20121022190819_move_fields_included_on_profiles_table_to_settings.rb
1 1 class MoveFieldsIncludedOnProfilesTableToSettings < ActiveRecord::Migration
2 2 def self.up
3   - Profile.find_each do |profile|
  3 + Enterprise.find_each do |profile|
4 4 settings = profile.shopping_cart_settings
5 5 settings.enabled = profile.shopping_cart
6 6 settings.delivery = profile.shopping_cart_delivery
... ...
plugins/shopping_cart/db/migrate/20131226125124_move_shopping_cart_purchase_order_to_orders_plugin_order.rb
... ... @@ -2,7 +2,7 @@ OrdersPlugin.send :remove_const, :Item if defined? OrdersPlugin::Item
2 2 OrdersPlugin.send :remove_const, :Order if defined? OrdersPlugin::Order
3 3  
4 4 class ShoppingCartPlugin::PurchaseOrder < ActiveRecord::Base
5   - acts_as_having_settings :field => :data
  5 + acts_as_having_settings field: :data
6 6  
7 7 module Status
8 8 OPENED = 0
... ... @@ -13,17 +13,17 @@ class ShoppingCartPlugin::PurchaseOrder &lt; ActiveRecord::Base
13 13 end
14 14  
15 15 class Profile
16   - has_many :orders, :class_name => 'OrdersPlugin::Order'
  16 + has_many :orders, class_name: 'OrdersPlugin::Order'
17 17 end
18 18  
19 19 class OrdersPlugin::Item < ActiveRecord::Base
20   - belongs_to :order, :class_name => 'OrdersPlugin::Order'
  20 + belongs_to :order, class_name: 'OrdersPlugin::Order'
21 21 end
22 22 class OrdersPlugin::Order < ActiveRecord::Base
23   - has_many :items, :class_name => 'OrdersPlugin::Item', :foreign_key => :order_id
  23 + has_many :items, class_name: 'OrdersPlugin::Item', foreign_key: :order_id
24 24  
25 25 extend CodeNumbering::ClassMethods
26   - code_numbering :code, :scope => proc{ self.profile.orders }
  26 + code_numbering :code, scope: proc{ self.profile.orders }
27 27 end
28 28  
29 29 StatusTransform = {
... ... @@ -37,10 +37,10 @@ class MoveShoppingCartPurchaseOrderToOrdersPluginOrder &lt; ActiveRecord::Migration
37 37 def self.up
38 38 OrdersPlugin::Order.record_timestamps = false
39 39  
40   - ShoppingCartPlugin::PurchaseOrder.all(:order => 'created_at ASC').each do |purchase_order|
  40 + ShoppingCartPlugin::PurchaseOrder.order('created_at ASC').find_each do |purchase_order|
41 41 data = purchase_order.data
42 42  
43   - order = OrdersPlugin::Order.new :profile_id => purchase_order.seller_id, :consumer_id => purchase_order.customer_id
  43 + order = OrdersPlugin::Order.new profile_id: purchase_order.seller_id, consumer_id: purchase_order.customer_id
44 44  
45 45 order.consumer_data = {}
46 46 ['contact_phone','name','email'].each do |prop|
... ... @@ -58,7 +58,7 @@ class MoveShoppingCartPurchaseOrderToOrdersPluginOrder &lt; ActiveRecord::Migration
58 58 order.supplier_delivery_data = {}
59 59  
60 60 data[:products_list].each do |id, data|
61   - item = order.items.build :product_id => id, :name => data[:name], :quantity_consumer_ordered => data[:quantity], :price => data[:price]
  61 + item = order.items.build product_id: id, name: data[:name], quantity_consumer_ordered: data[:quantity], price: data[:price]
62 62 item.order = order
63 63 end
64 64  
... ...
plugins/shopping_cart/features/delivery_admin.feature
... ... @@ -36,7 +36,6 @@ Feature: delivery administration
36 36 And I follow "New delivery or pickup"
37 37 And I select "Deliver" from "Type"
38 38 And I fill in "Name" with "Bike"
39   - And I fill in "Description" with "Beers delivered with my old bike."
40 39 And I fill in "Fixed cost" with "8.00"
41 40 And I fill in "Order's minimum price for free delivery" with "35.50"
42 41 When I press "Add"
... ... @@ -49,7 +48,6 @@ Feature: delivery administration
49 48 And I follow "New delivery or pickup"
50 49 And I select "Pickup" from "Type"
51 50 And I fill in "Name" with "Bar"
52   - And I fill in "Description" with "Come to my bar and pick it yourself."
53 51 And I fill in "Fixed cost" with "0.00"
54 52 When I press "Add"
55 53 Then I should see "Bar"
... ...
plugins/shopping_cart/features/features 0 → 120000
... ... @@ -0,0 +1 @@
  1 +/home/braulio/Projects/noosfero-ecosol/noosfero/config/plugins/shopping_cart/features
0 2 \ No newline at end of file
... ...
plugins/shopping_cart/lib/shopping_cart_plugin.rb
1 1 class ShoppingCartPlugin < Noosfero::Plugin
2   - include ModalHelper
3   - include ActionView::Helpers::UrlHelper
4 2  
5 3 def self.plugin_name
6 4 "Shopping Basket"
... ... @@ -34,10 +32,6 @@ class ShoppingCartPlugin &lt; Noosfero::Plugin
34 32 buttons
35 33 end
36 34  
37   - def controller
38   - context
39   - end
40   -
41 35 def add_to_cart_button item, options = {}
42 36 profile = item.profile
43 37 return unless profile.shopping_cart_enabled and item.available
... ...
plugins/shopping_cart/lib/shopping_cart_plugin/cart_helper.rb
... ... @@ -61,7 +61,8 @@ module ShoppingCartPlugin::CartHelper
61 61  
62 62 def items_table(items, delivery_method = nil, by_mail = false)
63 63 # partial key needed in mailer context
64   - render partial: 'shopping_cart_plugin/items', locals: {order: build_order(items, delivery_method), by_mail: by_mail}
  64 + order = @order || build_order(items, delivery_method)
  65 + render partial: 'shopping_cart_plugin/items', locals: {order: order, by_mail: by_mail}
65 66 end
66 67  
67 68 def float_to_currency_cart value, environment, _options = {}
... ...
plugins/shopping_cart/po/pt/shopping_cart.po
... ... @@ -13,7 +13,7 @@ msgid &quot;&quot;
13 13 msgstr ""
14 14 "Project-Id-Version: 1.3~rc2-1-ga15645d\n"
15 15 "POT-Creation-Date: 2015-10-30 16:34-0300\n"
16   -"PO-Revision-Date: 2015-09-29 20:23-0300\n"
  16 +"PO-Revision-Date: 2015-11-15 18:17-0300\n"
17 17 "Last-Translator: Michal Čihař <michal@cihar.com>\n"
18 18 "Language-Team: Portuguese <https://hosted.weblate.org/projects/noosfero/"
19 19 "plugin-shopping-cart/pt/>\n"
... ... @@ -32,6 +32,48 @@ msgstr &quot;Funcionalidade de cesto de compras para empreendimentos&quot;
32 32 msgid "Shopping basket"
33 33 msgstr "Cesto de compras"
34 34  
  35 +#: plugins/shopping_cart/lib/shopping_cart_plugin.rb:63
  36 +msgid "Purchase reports"
  37 +msgstr "Relatórios de compras"
  38 +
  39 +#: plugins/shopping_cart/lib/shopping_cart_plugin/purchase_order.rb:40
  40 +msgid "Opened"
  41 +msgstr "Aberto"
  42 +
  43 +#: plugins/shopping_cart/lib/shopping_cart_plugin/purchase_order.rb:40
  44 +msgid "Confirmed"
  45 +msgstr "Confirmado"
  46 +
  47 +#: plugins/shopping_cart/lib/shopping_cart_plugin/purchase_order.rb:40
  48 +msgid "Shipped"
  49 +msgstr "Enviado"
  50 +
  51 +#: plugins/shopping_cart/lib/shopping_cart_plugin/cart_helper.rb:41
  52 +msgid "Free delivery"
  53 +msgstr "Frete grátis"
  54 +
  55 +#: plugins/shopping_cart/lib/shopping_cart_plugin/cart_helper.rb:43
  56 +#: plugins/shopping_cart/views/shopping_cart_plugin_myprofile/edit.html.erb:7
  57 +msgid "Delivery"
  58 +msgstr "Entrega"
  59 +
  60 +#: plugins/shopping_cart/views/shopping_cart_plugin_myprofile/edit.html.erb:7
  61 +msgid "Delivery or pickup"
  62 +msgstr "Forma de retirada ou entrega"
  63 +
  64 +#: plugins/delivery/views/delivery_plugin/_order_select.html.slim
  65 +msgid "Instructions"
  66 +msgstr "Instruções"
  67 +
  68 +#: plugins/shopping_cart/lib/shopping_cart_plugin/cart_helper.rb:53
  69 +msgid "Item"
  70 +msgstr "Item"
  71 +
  72 +#: plugins/shopping_cart/lib/shopping_cart_plugin/cart_helper.rb:76
  73 +#: plugins/shopping_cart/views/cart.html.erb:7
  74 +msgid "Total:"
  75 +msgstr "Total:"
  76 +
35 77 #: plugins/shopping_cart/lib/shopping_cart_plugin/cart_helper.rb:11
36 78 msgid "Add to basket"
37 79 msgstr "Adicionar ao cesto"
... ... @@ -112,9 +154,8 @@ msgid &quot;Shopping checkout&quot;
112 154 msgstr "Finalizar pedido"
113 155  
114 156 #: plugins/shopping_cart/views/public/_cart.html.erb:8
115   -#, fuzzy
116 157 msgid "Basket is empty"
117   -msgstr "Cesto exibido."
  158 +msgstr "Cesto vazio."
118 159  
119 160 #: plugins/shopping_cart/views/public/_cart.html.erb:14
120 161 msgid "Basket"
... ... @@ -124,11 +165,6 @@ msgstr &quot;Cesto&quot;
124 165 msgid "Clean basket"
125 166 msgstr "Limpar cesto"
126 167  
127   -#: plugins/shopping_cart/views/public/_cart.html.erb:20
128   -#: plugins/shopping_cart/views/shopping_cart_plugin/_items.html.erb:42
129   -msgid "Total:"
130   -msgstr "Total:"
131   -
132 168 #: plugins/shopping_cart/views/public/_cart.html.erb:23
133 169 msgid "Show basket"
134 170 msgstr "Mostrar cesto"
... ... @@ -163,7 +199,8 @@ msgstr &quot;Tem certeza que quer limpar seu cesto?&quot;
163 199  
164 200 #: plugins/shopping_cart/views/public/_cart.html.erb:54
165 201 msgid "repeat order"
166   -msgstr ""
  202 +msgstr "repetir pedido"
  203 +
167 204  
168 205 #: plugins/shopping_cart/views/shopping_cart_plugin/_items.html.erb:7
169 206 msgid "Item name"
... ... @@ -179,39 +216,37 @@ msgid &quot;Hi %s!&quot;
179 216 msgstr "Olá %s!"
180 217  
181 218 #: plugins/shopping_cart/views/shopping_cart_plugin/mailer/customer_notification.html.erb:10
182   -#, fuzzy
183   -msgid ""
184   -"This is a notification e-mail about your buy request on the enterprise %s."
185   -msgstr "Esse é um email de notificação sobre o seu pedido de compra em %s."
  219 +msgid "This is a notification e-mail about your buy request on the enterprise %s."
  220 +msgstr "Esse é um email de notificação sobre o seu pedido de compra no empreendimento %s."
186 221  
187 222 #: plugins/shopping_cart/views/shopping_cart_plugin/mailer/customer_notification.html.erb:11
188   -#, fuzzy
189 223 msgid ""
190 224 "The enterprise already received your buy request and will contact you for "
191 225 "confirmation."
192 226 msgstr ""
193   -"O fornecedor já recebeu o seu pedido de compra e deve te contactar para "
  227 +"O empreendimento já recebeu o seu pedido de compra e entrará em contato para "
  228 +"confirmação."
  229 +
194 230 "confirmação."
195 231  
196 232 #: plugins/shopping_cart/views/shopping_cart_plugin/mailer/customer_notification.html.erb:12
197   -#, fuzzy
198 233 msgid "If you have any doubts about your order, write to us at: %s."
199   -msgstr "Se você tem alguma dúvida, nos contacte em: %s"
  234 +msgstr "Se você tem alguma dúvida sobre o seu pedido, nos escreva: %s."
200 235  
201 236 #: plugins/shopping_cart/views/shopping_cart_plugin/mailer/customer_notification.html.erb:13
202 237 msgid "Review below the informations of your order:"
203   -msgstr ""
  238 +msgstr "Abaixo, revise as informações do seu pedido:"
204 239  
205 240 #: plugins/shopping_cart/views/shopping_cart_plugin/mailer/customer_notification.html.erb:19
206 241 #: plugins/shopping_cart/views/shopping_cart_plugin/mailer/supplier_notification.html.erb:17
207 242 msgid "Phone number"
208 243 msgstr ""
209 244  
  245 +#: plugins/shopping_cart/views/shopping_cart_plugin_myprofile/_orders_list.html.erb:35
210 246 #: plugins/shopping_cart/views/shopping_cart_plugin/mailer/customer_notification.html.erb:22
211 247 #: plugins/shopping_cart/views/shopping_cart_plugin/buy.html.erb:22
212   -#, fuzzy
213 248 msgid "Payment's method"
214   -msgstr "Pagamento"
  249 +msgstr "Forma de pagamento"
215 250  
216 251 #: plugins/shopping_cart/views/shopping_cart_plugin/mailer/customer_notification.html.erb:24
217 252 #: plugins/shopping_cart/views/shopping_cart_plugin/mailer/supplier_notification.html.erb:22
... ... @@ -219,31 +254,30 @@ msgstr &quot;Pagamento&quot;
219 254 msgid "shopping_cart|Change"
220 255 msgstr "Troco para"
221 256  
222   -#: plugins/shopping_cart/views/shopping_cart_plugin/mailer/customer_notification.html.erb:28
223   -msgid "Delivery or pickup"
224   -msgstr "Forma de retirada ou entrega"
  257 +#: plugins/shopping_cart/views/shopping_cart_plugin_myprofile/_products_list.html.erb:7
  258 +msgid "Quantity"
  259 +msgstr "Quantidade"
225 260  
226   -#: plugins/shopping_cart/views/shopping_cart_plugin/mailer/customer_notification.html.erb:63
  261 +#: plugins/shopping_cart/views/shopping_cart_plugin/mailer/customer_notification.html.erb:47
227 262 msgid "Here are the products you bought:"
228   -msgstr ""
  263 +msgstr "Aqui estão os produtos que você pediu:"
229 264  
230   -#: plugins/shopping_cart/views/shopping_cart_plugin/mailer/customer_notification.html.erb:67
  265 +#: plugins/shopping_cart/views/shopping_cart_plugin/mailer/customer_notification.html.erb:51
231 266 msgid "Thanks for buying with us!"
232   -msgstr ""
  267 +msgstr "Obrigado por comprar conosco!"
233 268  
234   -#: plugins/shopping_cart/views/shopping_cart_plugin/mailer/customer_notification.html.erb:70
235   -#: plugins/shopping_cart/views/shopping_cart_plugin/mailer/supplier_notification.html.erb:61
  269 +#: plugins/shopping_cart/views/shopping_cart_plugin/mailer/customer_notification.html.erb:54
  270 +#: plugins/shopping_cart/views/shopping_cart_plugin/mailer/supplier_notification.html.erb:51
236 271 msgid "A service of %s."
237   -msgstr ""
  272 +msgstr "Um serviço de %s."
238 273  
239 274 #: plugins/shopping_cart/views/shopping_cart_plugin/mailer/supplier_notification.html.erb:10
240   -#, fuzzy
241 275 msgid "This is a buy request made by %s."
242   -msgstr "[%s] Você tem um novo pedido de compra de %s."
  276 +msgstr "Esse é um pedido de compra feito por %s."
243 277  
244 278 #: plugins/shopping_cart/views/shopping_cart_plugin/mailer/supplier_notification.html.erb:11
245 279 msgid "Below follows the customer informations:"
246   -msgstr ""
  280 +msgstr "Abaixo seguem as informações do consumidor:"
247 281  
248 282 #: plugins/shopping_cart/views/shopping_cart_plugin/mailer/supplier_notification.html.erb:20
249 283 #: plugins/shopping_cart/views/shopping_cart_plugin/buy.html.erb:25
... ... @@ -251,20 +285,18 @@ msgid &quot;Payment&quot;
251 285 msgstr "Pagamento"
252 286  
253 287 #: plugins/shopping_cart/views/shopping_cart_plugin/mailer/supplier_notification.html.erb:55
  288 +#: plugins/shopping_cart/views/shopping_cart_plugin/mailer/supplier_notification.html.erb:45
254 289 msgid "And here are the items bought by this customer:"
255   -msgstr ""
  290 +msgstr "E aqui estão os itens pedidos por esse consumidor:"
256 291  
257   -#: plugins/shopping_cart/views/shopping_cart_plugin/mailer/supplier_notification.html.erb:59
  292 +#: plugins/shopping_cart/views/shopping_cart_plugin/mailer/supplier_notification.html.erb:49
258 293 msgid "If there are any problems with this email contact the admin of %s."
259 294 msgstr ""
  295 +"Se houver algum problema com esse email contacte o administrador de %s."
260 296  
261   -#: plugins/shopping_cart/views/shopping_cart_plugin/buy.html.erb:4
262   -msgid "haven't finished yet: back to shopping"
263   -msgstr ""
264   -
265   -#: plugins/shopping_cart/views/shopping_cart_plugin/buy.html.erb:12
  297 +#: plugins/shopping_cart/views/shopping_cart_plugin/buy.html.erb:9
266 298 msgid "Personal identification"
267   -msgstr ""
  299 +msgstr "Identificação pessoal"
268 300  
269 301 #: plugins/shopping_cart/views/shopping_cart_plugin/buy.html.erb:15
270 302 msgid "Name"
... ... @@ -290,36 +322,21 @@ msgstr &quot;Seu Pedido&quot;
290 322 msgid "Send buy request"
291 323 msgstr "Enviar pedido de compra"
292 324  
293   -#~ msgid "Purchase reports"
294   -#~ msgstr "Relatórios de compras"
295   -
296   -#~ msgid "Opened"
297   -#~ msgstr "Aberto"
298   -
299   -#~ msgid "Canceled"
300   -#~ msgstr "Cancelado"
301   -
302   -#~ msgid "Confirmed"
303   -#~ msgstr "Confirmado"
304   -
305   -#~ msgid "Shipped"
306   -#~ msgstr "Enviado"
307   -
308   -#~ msgid "Free delivery"
309   -#~ msgstr "Frete grátis"
310   -
311   -#~ msgid "Delivery"
312   -#~ msgstr "Entrega"
313   -
314   -#~ msgid "Instructions"
315   -#~ msgstr "Instruções"
  325 +#: plugins/shopping_cart/views/shopping_cart_plugin/buy.html.erb:15
  326 +msgid "Address completion"
  327 +msgstr "Complemento (p.ex. apto)"
316 328  
317   -#~ msgid "Option wasn't updated successfully."
318   -#~ msgstr "Opção não foi atualizada com sucesso."
  329 +#: plugins/shopping_cart/views/shopping_cart_plugin/buy.html.erb
  330 +msgid "haven't finished yet: back to shopping"
  331 +msgstr "ainda não concluí: voltar às compras"
319 332  
320   -#~ msgid "Request sent successfully. Check your email."
321   -#~ msgstr "Requisição enviada com sucesso. Cheque seu email."
  333 +#: plugins/shopping_cart/views/shopping_cart_plugin/_items.html.erb
  334 +msgid "Qtty"
  335 +msgstr "Qtde"
322 336  
  337 +#: plugins/shopping_cart/views/shopping_cart_plugin/_items.html.erb
  338 +msgid "Unit price"
  339 +msgstr "Preço unitário"
323 340 #~ msgid "Can't join items from different enterprises."
324 341 #~ msgstr "Não é possível juntar itens de empreendimentos diferentes."
325 342  
... ...
plugins/shopping_cart/public/cart.js
... ... @@ -63,12 +63,6 @@ function Cart(config) {
63 63 var input = $("input", li)[0];
64 64 input.lastValue = input.value;
65 65 input.productId = item.id;
66   - input.ajustSize = function() {
67   - var len = this.value.toString().length;
68   - if(len > 2) len--;
69   - this.style.width = len+"em";
70   - };
71   - input.ajustSize();
72 66 input.onchange = function() {
73 67 me.updateQuantity(this, this.productId, this.value);
74 68 };
... ...
plugins/shopping_cart/public/public 0 → 120000
... ... @@ -0,0 +1 @@
  1 +/home/braulio/Projects/noosfero-ecosol/noosfero/config/plugins/shopping_cart/public
0 2 \ No newline at end of file
... ...
plugins/shopping_cart/shopping_cart 0 → 120000
... ... @@ -0,0 +1 @@
  1 +/home/braulio/Projects/noosfero-ecosol/noosfero/plugins/shopping_cart
0 2 \ No newline at end of file
... ...
plugins/shopping_cart/views/public/_cart.html.erb
... ... @@ -16,7 +16,7 @@
16 16 <a href="cart:clean" onclick="cart.clean(); return false" class="cart-clean"><%=_('Clean basket')%></a>
17 17 <ul class="cart-items">
18 18 </ul>
19   - <%= button 'cart', _('Shopping checkout'), "/plugin/shopping_cart/buy", :class => 'cart-buy modal-toggle' %>
  19 + <%= button 'cart', _('Shopping checkout'), "/plugin/shopping_cart/buy", class: 'cart-buy' %>
20 20 <div class="cart-total"><%=_('Total:')%> <b></b></div>
21 21 </div>
22 22 <a href="#" onclick="cart.toggle(); return false" class="cart-toggle">
... ... @@ -31,7 +31,7 @@
31 31 <div class="picture" style="background-image:url({{item.picture}})"></div>
32 32 <span class="item-name">{{item.name}}</span>
33 33 <div class="item-price">
34   - <input size="1" value="{{item.quantity}}" />{{item.priceTxt}}
  34 + <input value="{{item.quantity}}" />{{item.priceTxt}}
35 35 </div>
36 36 </li>
37 37 </script>
... ...
plugins/shopping_cart/views/shopping_cart_plugin/_items.html.erb
... ... @@ -3,45 +3,51 @@
3 3 style="<%= 'border-collapse: collapse' if by_mail %>">
4 4  
5 5 <tr>
6   - <th>
7   - <%= _('Item name') %>
  6 + <th class='cart-table-item' <%= "align='center'" if by_mail %>>
  7 + <%= _('Item') %>
8 8 </th>
9   - <th>
10   - <%= if by_mail then '&nbsp;#&nbsp;' else '#' end %>
  9 + <th class='cart-table-quantity' <%= "align='center'" if by_mail %>>
  10 + <%= if by_mail then '&nbsp;'+_('Qtty')+'&nbsp;' else _('Qtty') end %>
11 11 </th>
12   - <th>
13   - <%= _('Price') + " (#{@environment.currency_unit})" %>
  12 + <th class='cart-table-price' <%= "align='center'" if by_mail %>>
  13 + <%= _('Unit price') + " (#{@environment.currency_unit})" %>
  14 + </th>
  15 + <th class='cart-table-price' <%= "align='center'" if by_mail %>>
  16 + <%= _('Total') + " (#{@environment.currency_unit})" %>
14 17 </th>
15 18 </tr>
16 19  
17 20 <% order.items.each do |item| %>
18 21 <tr>
19   - <td>
  22 + <td class='cart-table-item' <%= "align='left'" if by_mail %>>
20 23 <%= item.name %>
21 24 </td>
22   - <td class='cart-table-quantity' align="<%= 'center' if by_mail %>">
23   - <%= item.quantity_consumer_ordered %>
  25 + <td class='cart-table-quantity' <%= "align='center'" if by_mail %>>
  26 + <%= "%03d" % item.quantity_consumer_ordered %>
  27 + </td>
  28 + <td class='cart-table-price' <%= "align='right'" if by_mail %>>
  29 + <%= get_price item.product, @environment, 1, unit: '' %>
24 30 </td>
25   - <td class='cart-table-price' align="<%= 'right' if by_mail %>">
  31 + <td class='cart-table-price' <%= "align='right'" if by_mail %>>
26 32 <%= get_price item.product, @environment, item.quantity_consumer_ordered, unit: '' %>
27 33 </td>
28 34  
29 35 </tr>
30 36 <% end %>
31 37  
32   - <tr>
33   - <td colspan=2 id='delivery-name'>
  38 + <tr class='delivery-costs'>
  39 + <td colspan=3 id='delivery-name'>
34 40 <%= order.supplier_delivery.name if order.supplier_delivery %>
35 41 </td>
36   - <td id='delivery-price' class="cart-table-price">
  42 + <td id='delivery-price' class="cart-table-price" <%= "align='right'" if by_mail %>>
37 43 <%= float_to_currency_cart order.supplier_delivery.cost(order.total_price), @environment, unit: '' if order.supplier_delivery %>
38 44 </td>
39 45 </tr>
40 46  
41   - <th colspan=2 class="cart-table-total-label">
  47 + <th colspan=3 class="cart-table-total-label" <%= "align='right'" if by_mail %>>
42 48 <%= _('Total:') %>
43 49 </th>
44   - <th class="cart-table-total-value">
  50 + <th class="cart-table-total-value" <%= "align='right'" if by_mail %>>
45 51 <%= float_to_currency_cart order.total, @environment %>
46 52 </th>
47 53 </table>
... ...
plugins/shopping_cart/views/shopping_cart_plugin/buy.html.erb
1 1 <div id='cart-request-box' class="row">
2   - <h1>
3   - <%= _('Shopping checkout') %>
4   - <%= button :back, _("haven't finished yet: back to shopping"), 'javascript:history.back()', class: 'cart-go-back' %>
5   - </h1>
  2 + <h1><%= _('Shopping checkout') %></h1>
  3 + <%= button :back, _("haven't finished yet: back to shopping"), 'javascript:history.back()', class: 'cart-go-back' %>
6 4  
7 5 <%= form_for :order, url: {action: :send_request},
8 6 html: {onsubmit: "return Cart.send_request(this)", id: 'cart-request-form'} do |f| %>
9 7  
10   - <div class="col-lg-6 col-md-6 col-sm-6">
  8 + <div class="col-lg-6 col-md-6 col-sm-6 col-lg-push-6 col-md-push-6 col-sm-push-6">
  9 + <fieldset>
  10 + <legend><%=s_('Your Order')%></legend>
  11 + <% supplier_delivery = @order.supplier_delivery || profile.delivery_methods.first %>
  12 + <%= items_table @cart[:items], @profile, supplier_delivery %>
  13 + </fieldset>
  14 + </div>
  15 +
  16 + <div class="col-lg-6 col-md-6 col-sm-6 col-lg-pull-6 col-md-pull-6 col-sm-pull-6">
11 17 <fieldset>
12 18 <legend><%=_('Personal identification')%></legend>
13 19  
... ... @@ -30,21 +36,15 @@
30 36 <% if profile.delivery_methods.size > 0 %>
31 37 <fieldset>
32 38 <legend><%=_('Delivery or pickup method')%></legend>
  39 +
33 40 <%= render 'delivery_plugin/order_select', f: f, order: @order %>
34 41 </fieldset>
35 42 <% end %>
36 43 </div>
37 44  
38   - <div class="col-lg-6 col-md-6 col-sm-6">
39   - <fieldset>
40   - <legend><%=s_('Your Order')%></legend>
41   - <% supplier_delivery = @order.supplier_delivery || profile.delivery_methods.first %>
42   - <%= items_table @cart[:items], supplier_delivery %>
43   - </fieldset>
44   - </div>
45   -
46 45 <div id="cart-form-actions" class="row">
47   - <%= submit_button(:send, _('Send buy request')) %>
  46 + <%= button :back, _("haven't finished yet: back to shopping"), 'javascript:history.back()' %>
  47 + <%= submit_button(:send, _('Send buy request'), option:"success") %>
48 48 </div>
49 49 <% end %>
50 50 </div>
... ...
plugins/suppliers/controllers/myprofile/suppliers_plugin/basket_controller.rb
... ... @@ -52,10 +52,12 @@ class SuppliersPlugin::BasketController &lt; MyProfileController
52 52 extend HMVC::ClassMethods
53 53 hmvc SuppliersPlugin
54 54  
55   - def default_url_options
56   - # avoid rails' use_relative_controller!
57   - {use_route: '/'}
  55 + # inherit routes from core skipping use_relative_controller!
  56 + def url_for options
  57 + options[:controller] = "/#{options[:controller]}" if options.is_a? Hash and options[:controller] and not options[:controller].to_s.starts_with? '/'
  58 + super options
58 59 end
  60 + helper_method :url_for
59 61  
60 62 def set_allowed_user
61 63 @allowed_user = true
... ...
plugins/suppliers/controllers/myprofile/suppliers_plugin/product_controller.rb
... ... @@ -32,7 +32,7 @@ class SuppliersPlugin::ProductController &lt; MyProfileController
32 32  
33 33 def edit
34 34 @product = profile.products.supplied.find params[:id]
35   - @product.update_attributes params["product_#{@product.id}"]
  35 + @product.update params["product_#{@product.id}"]
36 36 end
37 37  
38 38 def import
... ... @@ -79,6 +79,7 @@ class SuppliersPlugin::ProductController &lt; MyProfileController
79 79  
80 80 @supplier = SuppliersPlugin::Supplier.where(id: params[:supplier_id]).first if params[:supplier_id].present?
81 81  
  82 + # FIXME: joins(:from_products) is hiding own products (except baskets)
82 83 @scope = profile.products.unarchived.joins :from_products, :suppliers
83 84 @scope = SuppliersPlugin::BaseProduct.search_scope @scope, params
84 85 @products_count = @scope.supplied_for_count.count
... ... @@ -95,9 +96,11 @@ class SuppliersPlugin::ProductController &lt; MyProfileController
95 96 extend HMVC::ClassMethods
96 97 hmvc SuppliersPlugin
97 98  
98   - def default_url_options
99   - # avoid rails' use_relative_controller!
100   - {use_route: '/'}
  99 + # inherit routes from core skipping use_relative_controller!
  100 + def url_for options
  101 + options[:controller] = "/#{options[:controller]}" if options.is_a? Hash and options[:controller] and not options[:controller].to_s.starts_with? '/'
  102 + super options
101 103 end
  104 + helper_method :url_for
102 105  
103 106 end
... ...
plugins/suppliers/controllers/myprofile/suppliers_plugin_myprofile_controller.rb
... ... @@ -21,7 +21,7 @@ class SuppliersPluginMyprofileController &lt; MyProfileController
21 21 end
22 22  
23 23 def new
24   - @new_supplier.update_attributes! params[:supplier]
  24 + @new_supplier.update! params[:supplier]
25 25 @supplier = @new_supplier
26 26 session[:notice] = t('controllers.myprofile.supplier_created')
27 27 end
... ... @@ -33,7 +33,7 @@ class SuppliersPluginMyprofileController &lt; MyProfileController
33 33  
34 34 def edit
35 35 @supplier = profile.suppliers.find params[:id]
36   - @supplier.update_attributes params[:supplier]
  36 + @supplier.update params[:supplier]
37 37 end
38 38  
39 39 def margin_change
... ... @@ -58,8 +58,8 @@ class SuppliersPluginMyprofileController &lt; MyProfileController
58 58  
59 59 def search
60 60 @query = params[:query].downcase
61   - @enterprises = environment.enterprises.enabled.public.all limit: 12, order: 'name ASC',
62   - conditions: ['LOWER(name) LIKE ? OR LOWER(name) LIKE ? OR identifier LIKE ?', "#{@query}%", "% #{@query}%", "#{@query}%"]
  61 + @enterprises = environment.enterprises.enabled.is_public.limit(12).order('name ASC').
  62 + where('name ILIKE ? OR name ILIKE ? OR identifier LIKE ?', "#{@query}%", "% #{@query}%", "#{@query}%")
63 63 @enterprises -= profile.suppliers.collect(&:profile)
64 64 end
65 65  
... ...
plugins/suppliers/lib/default_delegate.rb
... ... @@ -15,6 +15,9 @@ module DefaultDelegate
15 15  
16 16 # TODO: add some documentation about the methods being added
17 17 def default_delegate field, options = {}
  18 + class_attribute :default_delegate_enable unless respond_to? :default_delegate_enable
  19 + self.default_delegate_enable = true if self.default_delegate_enable.nil?
  20 +
18 21 # rake db:migrate run?
19 22 return unless self.table_exists?
20 23  
... ... @@ -91,6 +94,8 @@ module DefaultDelegate
91 94 end
92 95  
93 96 define_method "#{field}_with_default" do
  97 + return self.send own_field unless self.default_delegate_enable
  98 +
94 99 if self.send default_setting
95 100 # delegated_field may return nil, so use own instead
96 101 # this is the case with some associations (e.g. Product#product_qualifiers)
... ... @@ -101,6 +106,8 @@ module DefaultDelegate
101 106 end
102 107 end
103 108 define_method "#{field}_with_default=" do |*args|
  109 + return self.send "#{own_field}=", *args unless self.default_delegate_enable
  110 +
104 111 own = self.send "#{own_field}=", *args
105 112 # break/set the default setting automatically, used for interfaces
106 113 # that don't have the default setting (e.g. manage_products)
... ...
plugins/suppliers/lib/ext/product.rb
... ... @@ -7,20 +7,22 @@ class Product
7 7 has_currency :price
8 8 has_currency :discount
9 9  
10   - scope :available, conditions: {available: true}
11   - scope :unavailable, conditions: ['products.available <> true']
12   - scope :archived, conditions: {archived: true}
13   - scope :unarchived, conditions: ['products.archived <> true']
  10 + scope :alphabetically, -> { order 'products.name ASC' }
14 11  
15   - scope :with_available, lambda { |available| where available: available }
16   - scope :with_price, conditions: 'products.price > 0'
17   - scope :with_product_category_id, lambda { |id| { conditions: {product_category_id: id} } }
  12 + scope :available, -> { where available: true }
  13 + scope :unavailable, -> { where 'products.available <> true' }
  14 + scope :archived, -> { where archived: true }
  15 + scope :unarchived, -> { where 'products.archived <> true' }
  16 +
  17 + scope :with_available, -> (available) { where available: available }
  18 + scope :with_price, -> { where 'products.price > 0' }
18 19  
19 20 # FIXME: transliterate input and name column
20   - scope :name_like, lambda { |name| { conditions: ["LOWER(products.name) LIKE ?", "%#{name}%"] } }
  21 + scope :name_like, -> (name) { where "name ILIKE ?", "%#{name}%" }
  22 + scope :with_product_category_id, -> (id) { where product_category_id: id }
21 23  
22   - scope :by_profile, lambda { |profile| { conditions: {profile_id: profile.id} } }
23   - scope :by_profile_id, lambda { |profile_id| { conditions: {profile_id: profile_id} } }
  24 + scope :by_profile, -> (profile) { where profile_id: profile.id }
  25 + scope :by_profile_id, -> (profile_id) { where profile_id: profile_id }
24 26  
25 27 def self.product_categories_of products
26 28 ProductCategory.find products.collect(&:product_category_id).compact.select{ |id| not id.zero? }
... ... @@ -36,16 +38,16 @@ end
36 38  
37 39 class Product
38 40  
39   - attr_accessible :from_products, :supplier_id, :supplier
  41 + attr_accessible :from_products, :from_product, :supplier_id, :supplier
40 42  
41 43 has_many :sources_from_products, foreign_key: :to_product_id, class_name: 'SuppliersPlugin::SourceProduct', dependent: :destroy
42 44 has_one :sources_from_product, foreign_key: :to_product_id, class_name: 'SuppliersPlugin::SourceProduct'
43 45 has_many :sources_to_products, foreign_key: :from_product_id, class_name: 'SuppliersPlugin::SourceProduct', dependent: :destroy
44 46 has_one :sources_to_product, foreign_key: :from_product_id, class_name: 'SuppliersPlugin::SourceProduct'
45   - has_many :to_products, through: :sources_to_products, order: 'id ASC'
46   - has_one :to_product, through: :sources_to_product, order: 'id ASC', autosave: true
47   - has_many :from_products, through: :sources_from_products, order: 'id ASC'
48   - has_one :from_product, through: :sources_from_product, order: 'id ASC', autosave: true
  47 + has_many :to_products, -> { order 'id ASC' }, through: :sources_to_products
  48 + has_one :to_product, -> { order 'id ASC' }, through: :sources_to_product, autosave: true
  49 + has_many :from_products, -> { order 'id ASC' }, through: :sources_from_products
  50 + has_one :from_product, -> { order 'id ASC' }, through: :sources_from_product, autosave: true
49 51  
50 52 has_many :sources_from_2x_products, through: :from_products, source: :sources_from_products
51 53 has_one :sources_from_2x_product, through: :from_product, source: :sources_from_product
... ... @@ -59,15 +61,15 @@ class Product
59 61 # semantic alias for supplier_from_product(s)
60 62 has_many :sources_supplier_products, foreign_key: :to_product_id, class_name: 'SuppliersPlugin::SourceProduct'
61 63 has_one :sources_supplier_product, foreign_key: :to_product_id, class_name: 'SuppliersPlugin::SourceProduct'
62   - has_many :supplier_products, through: :sources_supplier_products, source: :from_product, order: 'id ASC'
63   - has_one :supplier_product, through: :sources_supplier_product, source: :from_product, order: 'id ASC', autosave: true
64   - has_many :suppliers, through: :sources_supplier_products, uniq: true, order: 'id ASC'
65   - has_one :supplier, through: :sources_supplier_product, order: 'id ASC'
  64 + has_many :supplier_products, -> { order 'id ASC' }, through: :sources_supplier_products, source: :from_product
  65 + has_one :supplier_product, -> { order 'id ASC' }, through: :sources_supplier_product, source: :from_product, autosave: true
  66 + has_many :suppliers, -> { distinct.order 'id ASC' }, through: :sources_supplier_products
  67 + has_one :supplier, -> { order 'id ASC' }, through: :sources_supplier_product
66 68  
67   - has_many :consumers, through: :to_products, source: :profile, uniq: true, order: 'id ASC'
68   - has_one :consumer, through: :to_product, source: :profile, order: 'id ASC'
  69 + has_many :consumers, -> { distinct.order 'id ASC' }, through: :to_products, source: :profile
  70 + has_one :consumer, -> { order 'id ASC' }, through: :to_product, source: :profile
69 71  
70   - # overhide original
  72 + # overhide original, FIXME: rename to available_and_supplier_active
71 73 scope :available, -> {
72 74 joins(:suppliers).
73 75 where 'products.available = ? AND suppliers_plugin_suppliers.active = ?', true, true
... ... @@ -81,23 +83,24 @@ class Product
81 83 where "products.available #{op} ? #{cond} suppliers_plugin_suppliers.active #{op} ?", true, true
82 84 }
83 85  
84   - scope :name_like, lambda { |name| where "from_products_products.name ILIKE ?", "%#{name}%" }
85   - scope :with_product_category_id, lambda { |id| where 'from_products_products.product_category_id = ?', id }
  86 + scope :fp_name_like, -> (name) { where "from_products_products.name ILIKE ?", "%#{name}%" }
  87 + scope :fp_with_product_category_id, -> (id) { where 'from_products_products.product_category_id = ?', id }
86 88  
87 89 # prefer distributed_products has_many to use DistributedProduct scopes and eager loading
88 90 scope :distributed, -> { where type: 'SuppliersPlugin::DistributedProduct'}
89 91 scope :own, -> { where type: nil }
90 92 scope :supplied, -> {
91   - where(type: [nil, 'SuppliersPlugin::DistributedProduct']).
92   - # this allow duplicates and sorting on the fields
93   - group('products.id')
  93 + # this remove duplicates and allow sorting on the fields, unlike distinct
  94 + group('products.id').
  95 + where type: [nil, 'SuppliersPlugin::DistributedProduct']
94 96 }
95 97 scope :supplied_for_count, -> {
96   - where(type: [nil, 'SuppliersPlugin::DistributedProduct']).uniq
  98 + distinct.
  99 + where type: [nil, 'SuppliersPlugin::DistributedProduct']
97 100 }
98 101  
99   - scope :from_supplier, lambda { |supplier| { conditions: ['suppliers_plugin_suppliers.id = ?', supplier.id] } }
100   - scope :from_supplier_id, lambda { |supplier_id| { conditions: ['suppliers_plugin_suppliers.id = ?', supplier_id] } }
  102 + scope :from_supplier, -> (supplier) { joins(:suppliers).where 'suppliers_plugin_suppliers.id = ?', supplier.id }
  103 + scope :from_supplier_id, -> (supplier_id) { joins(:suppliers).where 'suppliers_plugin_suppliers.id = ?', supplier_id }
101 104  
102 105 after_create :distribute_to_consumers
103 106  
... ... @@ -132,8 +135,8 @@ class Product
132 135  
133 136 def distribute_to_consumer consumer, attrs = {}
134 137 distributed_product = consumer.distributed_products.where(profile_id: consumer.id, from_products_products: {id: self.id}).first
135   - distributed_product ||= SuppliersPlugin::DistributedProduct.create! profile: consumer, from_products: [self]
136   - distributed_product.update_attributes! attrs if attrs.present?
  138 + distributed_product ||= SuppliersPlugin::DistributedProduct.create! profile: consumer, from_product: self
  139 + distributed_product.update! attrs if attrs.present?
137 140 distributed_product
138 141 end
139 142  
... ...
plugins/suppliers/lib/ext/profile.rb
1 1 require_dependency 'profile'
  2 +require_dependency 'community'
2 3  
3   -# FIXME: The lines bellow should be on the core
4   -class Profile
  4 +# FIXME: should be on the core
  5 +([Profile] + Profile.descendants).each do |subclass|
  6 +subclass.class_eval do
5 7  
6   - has_many :products
  8 + has_many :products, foreign_key: :profile_id
7 9  
  10 +end
  11 +end
  12 +class Profile
8 13 def create_product?
9 14 true
10 15 end
11   -
12 16 end
13 17  
14   -class Profile
  18 +([Profile] + Profile.descendants).each do |subclass|
  19 +subclass.class_eval do
15 20  
16 21 # use profile.products.supplied to include own products
17   - has_many :distributed_products, class_name: 'SuppliersPlugin::DistributedProduct'
  22 + has_many :distributed_products, class_name: 'SuppliersPlugin::DistributedProduct', foreign_key: :profile_id
18 23  
19 24 has_many :from_products, through: :products
20 25 has_many :to_products, through: :products
... ... @@ -24,6 +29,11 @@ class Profile
24 29 has_many :consumers, class_name: 'SuppliersPlugin::Consumer', foreign_key: :profile_id, dependent: :destroy,
25 30 include: [{profile: [:domains], consumer: [:domains]}], order: 'name ASC'
26 31  
  32 +end
  33 +end
  34 +
  35 +class Profile
  36 +
27 37 def supplier_settings
28 38 @supplier_settings ||= Noosfero::Plugin::Settings.new self, SuppliersPlugin
29 39 end
... ...
plugins/suppliers/lib/suppliers_plugin/import.rb
... ... @@ -123,11 +123,11 @@ class SuppliersPlugin::Import
123 123 product ||= supplier.profile.products.build attrs
124 124 # let update happen only on dummy suppliers
125 125 if product.persisted? and supplier.dummy?
126   - product.update_attributes! attrs
  126 + product.update! attrs
127 127 elsif product.new_record?
128 128 # create products as not available
129 129 attrs[:available] = false if not supplier.dummy?
130   - product.update_attributes! attrs
  130 + product.update! attrs
131 131 end
132 132  
133 133 distributed_product = product.distribute_to_consumer consumer, distributed_attrs
... ...
plugins/suppliers/models/suppliers_plugin/base_product.rb
... ... @@ -19,7 +19,8 @@ class SuppliersPlugin::BaseProduct &lt; Product
19 19 }
20 20 ]
21 21  
22   - self.abstract_class = true
  22 + # if abstract_class is true then it will trigger https://github.com/rails/rails/issues/20871
  23 + #self.abstract_class = true
23 24  
24 25 settings_items :minimum_selleable, type: Float, default: nil
25 26 settings_items :margin_percentage, type: Float, default: nil
... ... @@ -75,11 +76,12 @@ class SuppliersPlugin::BaseProduct &lt; Product
75 76 Unit.new(singular: I18n.t('suppliers_plugin.models.product.unit'), plural: I18n.t('suppliers_plugin.models.product.units'))
76 77 end
77 78  
  79 + # override SuppliersPlugin::BaseProduct
78 80 def self.search_scope scope, params
79 81 scope = scope.from_supplier_id params[:supplier_id] if params[:supplier_id].present?
80 82 scope = scope.with_available(if params[:available] == 'true' then true else false end) if params[:available].present?
81   - scope = scope.name_like params[:name] if params[:name].present?
82   - scope = scope.with_product_category_id params[:category_id] if params[:category_id].present?
  83 + scope = scope.fp_name_like params[:name] if params[:name].present?
  84 + scope = scope.fp_with_product_category_id params[:category_id] if params[:category_id].present?
83 85 scope
84 86 end
85 87  
... ... @@ -98,7 +100,7 @@ SQL
98 100 def self.archive_orphans
99 101 self.where(id: self.orphans_ids).find_each batch_size: 50 do |product|
100 102 # need full save to trigger search index
101   - product.update_attributes archived: true
  103 + product.update archived: true
102 104 end
103 105 end
104 106  
... ... @@ -173,10 +175,10 @@ SQL
173 175  
174 176 # FIXME: move to core
175 177 def archive
176   - self.update_attributes! archived: true
  178 + self.update! archived: true
177 179 end
178 180 def unarchive
179   - self.update_attributes! archived: false
  181 + self.update! archived: false
180 182 end
181 183  
182 184 protected
... ...
plugins/suppliers/models/suppliers_plugin/supplier.rb
... ... @@ -16,21 +16,21 @@ class SuppliersPlugin::Supplier &lt; ActiveRecord::Base
16 16  
17 17 scope :alphabetical, -> { order 'name ASC' }
18 18  
19   - scope :active, conditions: {active: true}
  19 + scope :active, -> { where active: true }
20 20 scope :dummy, -> { joins(:profile).where profiles: {visible: false} }
21 21  
22   - scope :of_profile, lambda { |p| { conditions: {profile_id: p.id} } }
23   - scope :of_profile_id, lambda { |id| { conditions: {profile_id: id} } }
24   - scope :of_consumer, lambda { |c| { conditions: {consumer_id: c.id} } }
25   - scope :of_consumer_id, lambda { |id| { conditions: {consumer_id: id} } }
  22 + scope :of_profile, -> (p) { where profile_id: p.id }
  23 + scope :of_profile_id, -> (id) { where profile_id: id }
  24 + scope :of_consumer, -> (c) { where consumer_id: c.id }
  25 + scope :of_consumer_id, -> (id) { where consumer_id: id }
26 26  
27   - scope :from_supplier_id, lambda { |supplier_id| { conditions: ['suppliers_plugin_suppliers.id = ?', supplier_id] } }
  27 + scope :from_supplier_id, -> (supplier_id) { where 'suppliers_plugin_suppliers.id = ?', supplier_id }
28 28  
29   - scope :with_name, lambda { |name| if name then {conditions: ["LOWER(suppliers_plugin_suppliers.name) LIKE ?","%#{name.downcase}%"]} else {} end }
30   - scope :by_active, lambda { |active| if active then {conditions: {active: active}} else {} end }
  29 + scope :with_name, -> (name) { where "suppliers_plugin_suppliers.name ILIKE ?", "%#{name.downcase}%" if name }
  30 + scope :by_active, -> (active) { where active: active if active }
31 31  
32   - scope :except_people, { conditions: ['profiles.type <> ?', Person.name], joins: [:consumer] }
33   - scope :except_self, { conditions: 'profile_id <> consumer_id' }
  32 + scope :except_people, -> { joins(:consumer).where 'profiles.type <> ?', Person.name }
  33 + scope :except_self, -> { where 'profile_id <> consumer_id' }
34 34  
35 35 after_create :add_admins, if: :dummy?
36 36 after_create :save_profile, if: :dummy?
... ...
plugins/suppliers/test/test_helper.rb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +require File.dirname(__FILE__) + '/../../../test/test_helper'
... ...
plugins/suppliers/test/unit/code_numbering_test.rb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +require File.dirname(__FILE__) + '/../../../../test/test_helper'
  2 +
  3 +class CodeNumberingTest < ActiveSupport::TestCase
  4 +
  5 +
  6 +end
  7 +
... ...
plugins/suppliers/test/unit/default_delegate_test.rb 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +require 'test_helper'
  2 +
  3 +class DefaultDelegateTest < ActiveSupport::TestCase
  4 +
  5 +
  6 +end
... ...
plugins/suppliers/test/unit/suppliers_plugin/distributed_product_test.rb 0 → 100644
... ... @@ -0,0 +1,108 @@
  1 +require "#{File.dirname(__FILE__)}/../../test_helper"
  2 +
  3 +class SuppliersPlugin::DistributedProductTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @product_category = create(ProductCategory, :name => 'parent')
  7 + @profile = build(Enterprise)
  8 + @invisible_profile = build(Enterprise, :visible => false)
  9 + @other_profile = build(Enterprise)
  10 + @self_supplier = build(SuppliersPlugin::Supplier, :consumer => @profile, :profile => @profile)
  11 + @dummy_supplier = build(SuppliersPlugin::Supplier, :consumer => @profile, :profile => @dummy_profile)
  12 + @other_supplier = build(SuppliersPlugin::Supplier, :consumer => @profile, :profile => @other_profile)
  13 + end
  14 +
  15 + attr_accessor :product_category,
  16 + :profile, :invisible_profile, :other_profile,
  17 + :profile, :dummy_profile, :other_profile, :self_supplier, :dummy_supplier, :other_supplier
  18 +
  19 + should 'return default settings considering dummy supplier' do
  20 + product = build(SuppliersPlugin::DistributedProduct, :profile => @profile, :supplier => @dummy_supplier)
  21 + assert_equal nil, product.default_name
  22 + assert_equal nil, product.default_description
  23 + product = build(SuppliersPlugin::DistributedProduct, :profile => @profile, :supplier => @other_supplier)
  24 + assert_equal true, product.default_name
  25 + assert_equal true, product.default_description
  26 + end
  27 +
  28 + should 'return price without margins if it is own product' do
  29 + product = build(SuppliersPlugin::DistributedProduct, :price => 10, :margin_percentage => 10, :profile => @profile, :supplier => @self_supplier)
  30 + assert_equal 10.0, product.price.to_f
  31 + end
  32 +
  33 + should 'return price without margins if supplier product has no price' do
  34 + supplier_product = build(SuppliersPlugin::DistributedProduct, :profile => @other_profile, :supplier => @other_profile.self_supplier)
  35 + product = build(SuppliersPlugin::DistributedProduct, :price => 10, :margin_percentage => 10, :profile => @profile, :supplier => @other_supplier)
  36 + assert_equal 10.0, product.price.to_f
  37 + end
  38 +
  39 + should 'return price with margins' do
  40 + supplier_product = build(SuppliersPlugin::DistributedProduct, :price => 10, :margin_percentage => 10, :profile => @other_profile, :supplier => @other_profile.self_supplier)
  41 + product = build(SuppliersPlugin::DistributedProduct, :price => 10, :margin_percentage => 10, :supplier_product => supplier_product, :profile => @profile, :supplier => @other_supplier)
  42 +
  43 + product.default_margin_percentage = false
  44 + assert_equal 11.0, product.price.to_f
  45 + profile.margin_percentage = 20
  46 + product.default_margin_percentage = true
  47 + assert_equal 12.0, product.price.to_f
  48 + end
  49 +
  50 + should 'allow set of supplier product' do
  51 + product = build(SuppliersPlugin::DistributedProduct, :price => 10, :margin_percentage => 10, :profile => @profile, :supplier => @self_supplier)
  52 +
  53 + product.from_product = build(SuppliersPlugin::DistributedProduct, :profile => @profile, :supplier => @self_supplier)
  54 + assert_nothing_raised do
  55 + product.supplier_product = {:price => 10, :margin_percentage => 10}
  56 + product.supplier_product = SuppliersPlugin::DistributedProduct.new :price => 5
  57 + end
  58 + end
  59 +
  60 + should 'create a supplier product for a dummy supplier' do
  61 + product = build(SuppliersPlugin::DistributedProduct, :profile => @profile, :supplier => @dummy_supplier)
  62 + assert product.supplier_product
  63 + # negative assertion
  64 + product = build(SuppliersPlugin::DistributedProduct, :profile => @profile, :supplier => @other_supplier)
  65 + assert !product.supplier_product
  66 + end
  67 +
  68 + should 'respond to supplier_product_id setter and getter' do
  69 + product = create(SuppliersPlugin::DistributedProduct, :profile => @profile, :supplier => @dummy_supplier)
  70 + assert_equal product.supplier_product.id, product.supplier_product_id
  71 + product.expects(:distribute_from)
  72 + product.supplier_product_id = 1
  73 + end
  74 +
  75 + should 'respond to distribute_from' do
  76 + product = create(SuppliersPlugin::DistributedProduct, :profile => @profile, :supplier => @profile.self_supplier)
  77 +
  78 + assert_raise RuntimeError do
  79 + supplier_product = build(SuppliersPlugin::DistributedProduct, :profile => @other_profile)
  80 + product.distribute_from(supplier_product)
  81 + end
  82 +
  83 + supplier_product = create(SuppliersPlugin::DistributedProduct, :profile => @other_profile)
  84 + product.profile.add_supplier @other_profile
  85 + product.distribute_from supplier_product
  86 + assert_equal product.supplier.profile, supplier_product.profile
  87 + assert_equal product.supplier.profile, supplier_product.profile
  88 + end
  89 +
  90 + should 'return json for category hierarchy' do
  91 + grandparent = create(ProductCategory, :name => 'grand parent')
  92 + parent = create(ProductCategory, :name => 'parent')
  93 + child = product_category
  94 +
  95 + product = SuppliersPlugin::DistributedProduct.new :category => parent
  96 + hash = {:own_name => "parent", :id => "2", :subcats => [], :name => "parent",
  97 + :hierarchy => [{:own_name => "parent", :subcats => [], :name => "parent", :id => "2"}]}
  98 + assert_equal hash, product.json_for_category
  99 + end
  100 +
  101 + should 'block own product distribution' do
  102 + product = Product.create :enterprise => @profile, :product_category => product_category
  103 + distributed = SuppliersPlugin::DistributedProduct.new :enterprise => @profile, :from_products => [product]
  104 +
  105 + assert distributed.invalid?
  106 + end
  107 +
  108 +end
... ...
plugins/suppliers/test/unit/suppliers_plugin/product_test.rb 0 → 100644
... ... @@ -0,0 +1,49 @@
  1 +require "test_helper"
  2 +
  3 +class SuppliersPlugin::ProductTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @product = build(SuppliersPlugin::BaseProduct)
  7 + end
  8 +
  9 + should 'return first from product as supplier product' do
  10 + fp = build(SuppliersPlugin::BaseProduct, :profile => @product.profile)
  11 + @product.from_products = [fp]
  12 + assert_equal fp, @product.from_product
  13 + assert_equal fp, @product.supplier_product
  14 + end
  15 +
  16 + should 'respond to dummy and own' do
  17 + assert !@product.dummy?
  18 + assert @product.own?
  19 + end
  20 +
  21 + should 'return price with margins' do
  22 + supplier_product = build(SuppliersPlugin::DistributedProduct, :price => 10, :margin_percentage => 10, :profile => @product.profile, :supplier => @product.profile.self_supplier)
  23 + product = build(SuppliersPlugin::DistributedProduct, :price => 10, :margin_percentage => 10, :supplier_product => supplier_product, :profile => @product.profile, :supplier => @product.profile.self_supplier)
  24 +
  25 + product.default_margin_percentage = false
  26 + assert_equal 11.0, product.price_with_margins
  27 + @product.profile.margin_percentage = 20
  28 + product.default_margin_percentage = true
  29 + assert_equal 12.0, product.price_with_margins
  30 + end
  31 +
  32 + should 'build default unit if none exists' do
  33 + assert_equal 0, Unit.count
  34 + assert 'unit', @product.unit.singular
  35 + end
  36 +
  37 + should 'avoid destroy by raising an exception' do
  38 + assert_raise RuntimeError do
  39 + @product.destroy
  40 + end
  41 + end
  42 +
  43 + should 'accept price in different formats' do
  44 + @product.price = '2,45'
  45 + assert_equal 2.45, @product.price
  46 + end
  47 +
  48 +
  49 +end
... ...
plugins/suppliers/test/unit/suppliers_plugin/source_product_test.rb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +require "#{File.dirname(__FILE__)}/../../test_helper"
  2 +
  3 +class SuppliersPlugin::SourceProductTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + end
  7 +
  8 +end
... ...
plugins/suppliers/test/unit/suppliers_plugin/supplier_test.rb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +require "#{File.dirname(__FILE__)}/../../test_helper"
  2 +
  3 +class SuppliersPlugin::SupplierTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + end
  7 +
  8 +end
... ...
plugins/suppliers/views/suppliers_plugin_myprofile/margin_change.html.slim 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +#suppliers-plugin-margin-change.popin
  2 + h1= t'views.myprofile.margin_change.change_default_margin'
  3 +
  4 + = form_for @profile, as: :profile_data, remote: true, url: {controller: :suppliers_plugin_myprofile, action: :margin_change} do |f|
  5 + div= t'views.myprofile.margin_change.notice'
  6 +
  7 + = labelled_field f, :margin_percentage, t('views.myprofile.margin_change.new_margin'),
  8 + input_group_addon(t'views.myprofile.margin_change.%'){ f.number_field :margin_percentage, step: 'any' }
  9 +
  10 + .checkbox
  11 + label name='apply_to_all'
  12 + = check_box_tag :apply_to_all, 1, false
  13 + = t'views.myprofile.margin_change.apply_to_all'
  14 + .clean
  15 +
  16 + = submit_button :save, t('views.myprofile.margin_change.confirm')
  17 + = modal_close_link t('views.myprofile.margin_change.cancel')
... ...