diff --git a/app/controllers/api/statistics_controller.rb b/app/controllers/api/statistics_controller.rb index 545572c9f..5356c00cd 100644 --- a/app/controllers/api/statistics_controller.rb +++ b/app/controllers/api/statistics_controller.rb @@ -9,7 +9,7 @@ class API::StatisticsController < API::ApiController @statistics = StatisticIndex.all end - %w[account event machine project subscription training user space].each do |path| + %w[account event machine project subscription training user space order].each do |path| class_eval %{ def #{path} authorize :statistic, :#{path}? @@ -30,11 +30,7 @@ class API::StatisticsController < API::ApiController # return result render json: results end - }, __FILE__, __LINE__ - 20 - end - %w[account event machine project subscription training user space].each do |path| - class_eval %{ def export_#{path} authorize :statistic, :export_#{path}? @@ -56,7 +52,7 @@ class API::StatisticsController < API::ApiController disposition: 'attachment' end end - }, __FILE__, __LINE__ - 22 + }, __FILE__, __LINE__ - 42 end def export_global diff --git a/app/models/reservation.rb b/app/models/reservation.rb index 04aeba681..0bc743eed 100644 --- a/app/models/reservation.rb +++ b/app/models/reservation.rb @@ -15,22 +15,23 @@ class Reservation < ApplicationRecord accepts_nested_attributes_for :slots_reservations, allow_destroy: true belongs_to :reservable, polymorphic: true - has_many :tickets + has_many :tickets, dependent: :destroy accepts_nested_attributes_for :tickets, allow_destroy: false has_many :invoice_items, as: :object, dependent: :destroy has_one :payment_schedule_object, as: :object, dependent: :destroy - validates_presence_of :reservable_id, :reservable_type + validates :reservable_id, :reservable_type, presence: true validate :machine_not_already_reserved, if: -> { reservable.is_a?(Machine) } validate :training_not_fully_reserved, if: -> { reservable.is_a?(Training) } validate :slots_not_locked + after_save :update_event_nb_free_places, if: proc { |reservation| reservation.reservable_type == 'Event' } after_commit :notify_member_create_reservation, on: :create after_commit :notify_admin_member_create_reservation, on: :create after_commit :extend_subscription, on: :create - after_save :update_event_nb_free_places, if: proc { |reservation| reservation.reservable_type == 'Event' } + delegate :user, to: :statistic_profile # @param canceled if true, count the number of seats for this reservation, including canceled seats def total_booked_seats(canceled: false) @@ -50,10 +51,6 @@ class Reservation < ApplicationRecord total end - def user - statistic_profile.user - end - def update_event_nb_free_places return unless reservable_type == 'Event' @@ -83,11 +80,11 @@ class Reservation < ApplicationRecord daily_slots[1..].each do |slot| found = false result[date].each do |group_start, group_slots| - if slot[:start_at] === group_slots.last[:end_at] - result[date][group_start].push(slot) - found = true - break - end + next unless slot[:start_at] == group_slots.last[:end_at] + + result[date][group_start].push(slot) + found = true + break end result[date][slot[:start_at]] = [slot] unless found end diff --git a/app/models/stats/account.rb b/app/models/stats/account.rb index 85f6f5671..5292e501f 100644 --- a/app/models/stats/account.rb +++ b/app/models/stats/account.rb @@ -1,6 +1,7 @@ -module Stats - class Account - include Elasticsearch::Persistence::Model - include StatConcern - end +# frozen_string_literal: true + +# This is a statistical data saved in ElasticSearch, about an account creation +class Stats::Account + include Elasticsearch::Persistence::Model + include StatConcern end diff --git a/app/models/stats/event.rb b/app/models/stats/event.rb index 9ad73a6a7..9de4440b2 100644 --- a/app/models/stats/event.rb +++ b/app/models/stats/event.rb @@ -1,12 +1,13 @@ -module Stats - class Event - include Elasticsearch::Persistence::Model - include StatConcern - include StatReservationConcern +# frozen_string_literal: true - attribute :eventId, Integer - attribute :eventDate, String - attribute :ageRange, String - attribute :eventTheme, String - end +# This is a statistical data saved in ElasticSearch, about an event reservation +class Stats::Event + include Elasticsearch::Persistence::Model + include StatConcern + include StatReservationConcern + + attribute :eventId, Integer + attribute :eventDate, String + attribute :ageRange, String + attribute :eventTheme, String end diff --git a/app/models/stats/store_order.rb b/app/models/stats/order.rb similarity index 72% rename from app/models/stats/store_order.rb rename to app/models/stats/order.rb index c29f8abba..9e41e1af7 100644 --- a/app/models/stats/store_order.rb +++ b/app/models/stats/order.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true -class Stats::StoreOrder +# This is a statistical data saved in ElasticSearch, about a store's order +class Stats::Order include Elasticsearch::Persistence::Model include StatConcern diff --git a/app/models/stats/project.rb b/app/models/stats/project.rb index d1ecf9fad..d97668a96 100644 --- a/app/models/stats/project.rb +++ b/app/models/stats/project.rb @@ -1,14 +1,15 @@ -module Stats - class Project - include Elasticsearch::Persistence::Model - include StatConcern +# frozen_string_literal: true - attribute :projectId, Integer - attribute :name, String - attribute :licence, Hash - attribute :themes, Array - attribute :components, Array - attribute :machines, Array - attribute :users, Integer - end +# This is a statistical data saved in ElasticSearch, about a project publication +class Stats::Project + include Elasticsearch::Persistence::Model + include StatConcern + + attribute :projectId, Integer + attribute :name, String + attribute :licence, Hash + attribute :themes, Array + attribute :components, Array + attribute :machines, Array + attribute :users, Integer end diff --git a/app/models/stats/space.rb b/app/models/stats/space.rb index fda08fa18..62b1e6aa7 100644 --- a/app/models/stats/space.rb +++ b/app/models/stats/space.rb @@ -1,9 +1,10 @@ -module Stats - class Space - include Elasticsearch::Persistence::Model - include StatConcern - include StatReservationConcern +# frozen_string_literal: true - attribute :spaceId, Integer - end +# This is a statistical data saved in ElasticSearch, about a space reservation +class Stats::Space + include Elasticsearch::Persistence::Model + include StatConcern + include StatReservationConcern + + attribute :spaceId, Integer end diff --git a/app/models/stats/subscription.rb b/app/models/stats/subscription.rb index 7fef5fd2f..2d60429c3 100644 --- a/app/models/stats/subscription.rb +++ b/app/models/stats/subscription.rb @@ -1,12 +1,13 @@ -module Stats - class Subscription - include Elasticsearch::Persistence::Model - include StatConcern +# frozen_string_literal: true - attribute :ca, Float - attribute :planId, Integer - attribute :subscriptionId, Integer - attribute :invoiceItemId, Integer - attribute :groupName, String - end +# This is a statistical data saved in ElasticSearch, about a subscription to a plan +class Stats::Subscription + include Elasticsearch::Persistence::Model + include StatConcern + + attribute :ca, Float + attribute :planId, Integer + attribute :subscriptionId, Integer + attribute :invoiceItemId, Integer + attribute :groupName, String end diff --git a/app/models/stats/training.rb b/app/models/stats/training.rb index 79854fecb..3c877e9f8 100644 --- a/app/models/stats/training.rb +++ b/app/models/stats/training.rb @@ -1,10 +1,11 @@ -module Stats - class Training - include Elasticsearch::Persistence::Model - include StatConcern - include StatReservationConcern +# frozen_string_literal: true - attribute :trainingId, Integer - attribute :trainingDate, String - end +# This is a statistical data saved in ElasticSearch, about a training reservation +class Stats::Training + include Elasticsearch::Persistence::Model + include StatConcern + include StatReservationConcern + + attribute :trainingId, Integer + attribute :trainingDate, String end diff --git a/app/models/stats/user.rb b/app/models/stats/user.rb index 0720844d0..55c9a42a8 100644 --- a/app/models/stats/user.rb +++ b/app/models/stats/user.rb @@ -1,6 +1,7 @@ -module Stats - class User - include Elasticsearch::Persistence::Model - include StatConcern - end +# frozen_string_literal: true + +# This is a statistical data saved in ElasticSearch, about revenue generated per user +class Stats::User + include Elasticsearch::Persistence::Model + include StatConcern end diff --git a/app/models/subscription.rb b/app/models/subscription.rb index 5c0e40dd1..0754fa515 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -8,11 +8,11 @@ class Subscription < ApplicationRecord belongs_to :statistic_profile has_one :payment_schedule_object, as: :object, dependent: :destroy - has_one :payment_gateway_object, as: :item + has_one :payment_gateway_object, as: :item, dependent: :destroy has_many :invoice_items, as: :object, dependent: :destroy has_many :offer_days, dependent: :destroy - validates_presence_of :plan_id + validates :plan_id, presence: true validates_with SubscriptionGroupValidator # creation @@ -21,18 +21,21 @@ class Subscription < ApplicationRecord after_save :notify_admin_subscribed_plan after_save :notify_partner_subscribed_plan, if: :of_partner_plan? + delegate :user, to: :statistic_profile + def generate_and_save_invoice(operator_profile_id) generate_invoice(operator_profile_id).save end def expire(time) - if !expired? - update_columns(expiration_date: time, canceled_at: time) + if expired? + false + else + # TODO, check if the rubocop:disable directove can be deleted + update_columns(expiration_date: time, canceled_at: time) # rubocop:disable Rails/SkipsModelValidations notify_admin_subscription_canceled notify_member_subscription_canceled true - else - false end end @@ -47,10 +50,6 @@ class Subscription < ApplicationRecord expiration_date end - def user - statistic_profile.user - end - def original_payment_schedule payment_schedule_object&.payment_schedule end diff --git a/app/policies/statistic_policy.rb b/app/policies/statistic_policy.rb index 47814de04..852786a89 100644 --- a/app/policies/statistic_policy.rb +++ b/app/policies/statistic_policy.rb @@ -1,6 +1,9 @@ +# frozen_string_literal: true + +# Check the access policies for API::StatisticsController class StatisticPolicy < ApplicationPolicy - %w(index account event machine project subscription training user space scroll export_subscription export_machine - export_training export_event export_account export_project export_space export_global).each do |action| + %w[index account event machine project subscription training user space order scroll export_subscription export_machine + export_training export_event export_account export_project export_space export_order export_global].each do |action| define_method "#{action}?" do user.admin? end diff --git a/app/services/orders/order_service.rb b/app/services/orders/order_service.rb index 6386ed07d..f1fdf5669 100644 --- a/app/services/orders/order_service.rb +++ b/app/services/orders/order_service.rb @@ -42,6 +42,10 @@ class Orders::OrderService else nil end + + # update in elasticsearch (statistics) + stat_order = Stats::Order.search(query: { term: { orderId: order.id } }) + stat_order.map { |s| s.update(state: state) } end def in_stock?(order, stock_type = 'external') diff --git a/app/services/statistics/builders/store_orders_builder_service.rb b/app/services/statistics/builders/store_orders_builder_service.rb index d57f4c7f8..92d6438e5 100644 --- a/app/services/statistics/builders/store_orders_builder_service.rb +++ b/app/services/statistics/builders/store_orders_builder_service.rb @@ -9,15 +9,15 @@ class Statistics::Builders::StoreOrdersBuilderService def build(options = default_options) # project list Statistics::FetcherService.store_orders_list(options).each do |o| - Stats::StoreOrder.create({ date: format_date(o[:date]), - type: 'order', - subType: 'store', - ca: o[:ca], - products: o[:order_products], - categories: o[:order_categories], - orderId: o[:order_id], - orderState: o[:order_state], - stat: 1 }.merge(user_info_stat(o))) + Stats::Order.create({ date: format_date(o[:date]), + type: 'order', + subType: 'store', + ca: o[:ca], + products: o[:order_products], + categories: o[:order_categories], + orderId: o[:order_id], + state: o[:order_state], + stat: 1 }.merge(user_info_stat(o))) end end end diff --git a/app/services/statistics/cleaner_service.rb b/app/services/statistics/cleaner_service.rb index 671700869..8ed6b50e1 100644 --- a/app/services/statistics/cleaner_service.rb +++ b/app/services/statistics/cleaner_service.rb @@ -7,7 +7,7 @@ class Statistics::CleanerService class << self def clean_stat(options = default_options) client = Elasticsearch::Model.client - %w[Account Event Machine Project Subscription Training User Space].each do |o| + %w[Account Event Machine Project Subscription Training User Space Order].each do |o| model = "Stats::#{o}".constantize client.delete_by_query( index: model.index_name, diff --git a/app/services/statistics/fetcher_service.rb b/app/services/statistics/fetcher_service.rb index d9be86fcb..437715704 100644 --- a/app/services/statistics/fetcher_service.rb +++ b/app/services/statistics/fetcher_service.rb @@ -186,9 +186,9 @@ class Statistics::FetcherService def store_orders_list(options = default_options) result = [] Order.includes(order_items: [:orderable]) - .joins(:order_items) + .joins(:order_items, :order_activities) .where("order_items.orderable_type = 'Product'") - .where('orders.created_at >= :start_date AND orders.created_at <= :end_date', options) + .where('order_activities.created_at >= :start_date AND order_activities.created_at <= :end_date', options) .each do |o| result.push({ date: o.created_at.to_date, ca: calcul_ca(o.invoice) } .merge(user_info(o.statistic_profile)) diff --git a/config/locales/en.yml b/config/locales/en.yml index 2ad1f5e96..bfab612c6 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -430,6 +430,7 @@ en: subscriptions: "Subscriptions" machines_hours: "Machines slots" spaces: "Spaces" + orders: "Orders" trainings: "Trainings" events: "Events" registrations: "Registrations" @@ -453,6 +454,7 @@ en: account_creation: "Account creation" project_publication: "Project publication" duration: "Duration" + store: "Boutique" #statistics exports to the Excel file format export: entries: "Entries" diff --git a/config/routes.rb b/config/routes.rb index 8df19c224..d947cdde7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -282,7 +282,7 @@ Rails.application.routes.draw do end end - %w[account event machine project subscription training user space].each do |path| + %w[account event machine project subscription training user space order].each do |path| post "/stats/#{path}/_search", to: "api/statistics##{path}" post "/stats/#{path}/export", to: "api/statistics#export_#{path}" end diff --git a/db/seeds.rb b/db/seeds.rb index 0234e0a23..856614b5c 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1027,6 +1027,14 @@ unless StatisticIndex.find_by(es_type_key: 'space') ]) end +unless StatisticIndex.find_by(es_type_key: 'order') + index = StatisticIndex.create!(es_type_key: 'order', label: I18n.t('statistics.orders')) + StatisticType.create!([ + { statistic_index_id: index.id, key: 'store', label: I18n.t('statistics.store'), + graph: true, simple: true } + ]) +end + ProfileCustomField.find_or_create_by(label: 'N° SIRET') ProfileCustomField.find_or_create_by(label: 'Code NAF') ProfileCustomField.find_or_create_by(label: 'N° TVA intracommunautaire') diff --git a/test/services/statistics/store_statistic_service_test.rb b/test/services/statistics/store_statistic_service_test.rb index 1233d1326..f94a74529 100644 --- a/test/services/statistics/store_statistic_service_test.rb +++ b/test/services/statistics/store_statistic_service_test.rb @@ -12,11 +12,11 @@ class StoreStatisticServiceTest < ActionDispatch::IntegrationTest ::Statistics::BuilderService.generate_statistic({ start_date: DateTime.current.beginning_of_day, end_date: DateTime.current.end_of_day }) - Stats::StoreOrder.refresh_index! + Stats::Order.refresh_index! # we should find order id 15 (created today) - stat_order = Stats::StoreOrder.search(query: { bool: { must: [{ term: { date: DateTime.current.to_date.iso8601 } }, - { term: { type: 'order' } }] } }).first + stat_order = Stats::Order.search(query: { bool: { must: [{ term: { date: DateTime.current.to_date.iso8601 } }, + { term: { type: 'order' } }] } }).first assert_not_nil stat_order assert_equal @order.id, stat_order['orderId'] check_statistics_on_user(stat_order)