1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-20 14:54:15 +01:00

(merge) Merge branch 'product-store_stats' into product-store

This commit is contained in:
Sylvain 2022-10-12 14:20:38 +02:00
commit f80aec1ef2
44 changed files with 1715 additions and 156 deletions

View File

@ -36,3 +36,5 @@ Style/FormatString:
EnforcedStyle: sprintf
Style/FormatStringToken:
EnforcedStyle: template
Rails/RedundantPresenceValidationOnBelongsTo:
Enabled: false

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
# API Controller for resources of type Space
# API Controller for various statistical resources (gateway to elasticsearch DB)
class API::StatisticsController < API::ApiController
before_action :authenticate_user!
@ -9,49 +9,25 @@ 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}?
def #{path} # def account
authorize :statistic, :#{path}? # authorize :statistic, :account
render json: Statistics::QueryService.query('#{path}', request) # render json: Statistics::QueryService.query('account', request)
end # end
# remove additional parameters
statistic_type = request.query_parameters.delete('stat-type')
custom_query = request.query_parameters.delete('custom-query')
start_date = request.query_parameters.delete('start-date')
end_date = request.query_parameters.delete('end-date')
def export_#{path} # def export_account
authorize :statistic, :export_#{path}? # authorize :statistic, :export_account?
# run main query in elasticSearch
query = MultiJson.load(request.body.read)
results = Stats::#{path.classify}.search(query, request.query_parameters.symbolize_keys).response
# run additional custom aggregations, if any
CustomAggregationService.new.("#{path}", statistic_type, start_date, end_date, custom_query, results)
# 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}?
export = Export.where(category:'statistics', export_type: '#{path}', query: params[:body], key: params[:type_key]).last
if export.nil? || !FileTest.exist?(export.file)
@export = Export.new(category:'statistics',
export_type: '#{path}',
user: current_user,
query: params[:body],
key: params[:type_key])
@export = Statistics::QueryService.export('#{path}', params) # @export = Statistics::QueryService.export('account', params)
if @export.is_a?(Export)
if @export.save
render json: {export_id: @export.id}, status: :ok
render json: { export_id: @export.id }, status: :ok
else
render json: @export.errors, status: :unprocessable_entity
end
else
send_file File.join(Rails.root, export.file),
send_file @export,
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
disposition: 'attachment'
end
@ -62,16 +38,15 @@ class API::StatisticsController < API::ApiController
def export_global
authorize :statistic, :export_global?
export = Export.where(category: 'statistics', export_type: 'global', query: params[:body]).last
if export.nil? || !FileTest.exist?(export.file)
@export = Export.new(category: 'statistics', export_type: 'global', user: current_user, query: params[:body])
@export = Statistics::QueryService.export(global, params)
if @export.is_a?(Export)
if @export.save
render json: { export_id: @export.id }, status: :ok
else
render json: @export.errors, status: :unprocessable_entity
end
else
send_file File.join(Rails.root, export.file),
send_file @export,
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
disposition: 'attachment'
end

View File

@ -18,7 +18,7 @@ class API::WalletController < API::ApiController
end
def credit
return head 422 unless Setting.get('wallet_module')
return head :unprocessable_entity unless Setting.get('wallet_module')
@wallet = Wallet.find(credit_params[:id])
authorize @wallet
@ -28,7 +28,7 @@ class API::WalletController < API::ApiController
service.create_avoir(transaction, credit_params[:avoir_date], credit_params[:avoir_description]) if credit_params[:avoir]
render :show
else
head 422
head :unprocessable_entity
end
end

View File

@ -106,6 +106,7 @@ Application.Controllers.controller('HomeController', ['$scope', '$transition$',
const setupWelcomeTour = function () {
// get the tour defined by the ui-tour directive
const uitour = uiTourService.getTourByName('welcome');
if (!uitour) return;
// add the steps
uitour.createStep({
selector: 'body',

View File

@ -7,7 +7,7 @@ class Order < PaymentDocument
belongs_to :coupon
belongs_to :invoice
has_many :order_items, dependent: :destroy
has_one :payment_gateway_object, as: :item
has_one :payment_gateway_object, as: :item, dependent: :destroy
has_many :order_activities, dependent: :destroy
ALL_STATES = %w[cart paid payment_failed refunded in_progress ready canceled delivered].freeze

View File

@ -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

View File

@ -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

View File

@ -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

13
app/models/stats/order.rb Normal file
View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
# This is a statistical data saved in ElasticSearch, about a store's order
class Stats::Order
include Elasticsearch::Persistence::Model
include StatConcern
attribute :orderId, Integer
attribute :state, String
attribute :products, Array
attribute :categories, Array
attribute :ca, Float
end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -11,6 +11,8 @@ class Wallet < ApplicationRecord
validates :invoicing_profile, presence: true
delegate :user, to: :invoicing_profile
def credit(amount)
if amount.is_a?(Numeric) && amount >= 0
self.amount += amount
@ -26,8 +28,4 @@ class Wallet < ApplicationRecord
end
false
end
def user
invoicing_profile.user
end
end

View File

@ -8,17 +8,15 @@ class WalletTransaction < ApplicationRecord
belongs_to :wallet
belongs_to :reservation
# what was paid with the wallet
has_one :invoice
has_one :payment_schedule
has_one :invoice, dependent: :nullify
has_one :payment_schedule, dependent: :nullify
# how the wallet was credited
has_one :invoice_item, as: :object, dependent: :destroy
validates_inclusion_of :transaction_type, in: %w[credit debit]
validates :transaction_type, inclusion: { in: %w[credit debit] }
validates :invoicing_profile, :wallet, presence: true
def user
invoicing_profile.user
end
delegate :user, to: :invoicing_profile
def original_invoice
invoice_item.invoice

View File

@ -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

View File

@ -12,6 +12,8 @@ class ExportService
last_export_reservations
when 'users/subscription'
last_export_subscriptions
when %r{statistics/.*}
last_export_statistics(type.split('/')[1])
else
raise TypeError "unknown export type: #{type}"
end
@ -44,5 +46,10 @@ class ExportService
.where('created_at > ?', last_update)
.last
end
def last_export_statistics(type)
Export.where(category: 'statistics', export_type: type, query: params[:body], key: params[:type_key])
.last
end
end
end

View File

@ -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')

View File

@ -11,6 +11,7 @@ class Statistics::BuilderService
Statistics::Builders::ReservationsBuilderService.build(options)
Statistics::Builders::MembersBuilderService.build(options)
Statistics::Builders::ProjectsBuilderService.build(options)
Statistics::Builders::StoreOrdersBuilderService.build(options)
end
private

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
# Generate statistics indicators about store's orders
class Statistics::Builders::StoreOrdersBuilderService
include Statistics::Concerns::HelpersConcern
include Statistics::Concerns::StoreOrdersConcern
class << self
def build(options = default_options)
states = {
'paid-processed': %w[paid in_progress ready delivered],
aborted: %w[payment_failed refunded canceled]
}
# orders list
states.each do |sub_type, order_states|
Statistics::FetcherService.store_orders_list(order_states, options).each do |o|
Stats::Order.create({ date: format_date(o[:date]),
type: 'store',
subType: sub_type,
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
end
end

View File

@ -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,

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
# Provides methods to consolidate data from Store Orders to use in statistics
module Statistics::Concerns::StoreOrdersConcern
extend ActiveSupport::Concern
class_methods do
def get_order_products(order)
order.order_items.where(orderable_type: 'Product').map do |item|
{ id: item.orderable_id, name: item.orderable.name }
end
end
def get_order_categories(order)
order.order_items
.where(orderable_type: 'Product')
.map(&:orderable)
.map(&:product_category)
.map { |cat| { id: cat.id, name: cat.name } }
.uniq
end
def store_order_info(order)
{
order_id: order.id,
order_state: order.state,
order_products: get_order_products(order),
order_categories: get_order_categories(order)
}
end
end
end

View File

@ -6,6 +6,7 @@ class Statistics::FetcherService
include Statistics::Concerns::HelpersConcern
include Statistics::Concerns::ComputeConcern
include Statistics::Concerns::ProjectsConcern
include Statistics::Concerns::StoreOrdersConcern
class << self
def subscriptions_list(options = default_options)
@ -182,6 +183,22 @@ class Statistics::FetcherService
result
end
def store_orders_list(states, options = default_options)
result = []
Order.includes(order_items: [:orderable])
.joins(:order_items, :order_activities)
.where(order_items: { orderable_type: 'Product' })
.where(orders: { state: states })
.where('order_activities.created_at >= :start_date AND order_activities.created_at <= :end_date', options)
.group('orders.id')
.each do |o|
result.push({ date: o.created_at.to_date, ca: calcul_ca(o.invoice) }
.merge(user_info(o.statistic_profile))
.merge(store_order_info(o)))
end
result
end
private
def add_ca(profile, new_ca, users_list)

View File

@ -0,0 +1,37 @@
# frozen_string_literal: true
# Query the elasticsearch database of statistics and format the result
class Statistics::QueryService
class << self
def query(statistic_index, request)
# remove additional parameters
statistic_type = request.query_parameters.delete('stat-type')
custom_query = request.query_parameters.delete('custom-query')
start_date = request.query_parameters.delete('start-date')
end_date = request.query_parameters.delete('end-date')
# run main query in elasticSearch
query = MultiJson.load(request.body.read)
model = "Stats::#{statistic_index}".constantize
results = model.search(query, request.query_parameters.symbolize_keys).response
# run additional custom aggregations, if any
CustomAggregationService.new.call(statistic_index, statistic_type, start_date, end_date, custom_query, results)
results
end
def export(statistic_index, params)
export = ExportService.last_export("statistics/#{statistic_index}")
if export.nil? || !FileTest.exist?(export.file)
Export.new(category: 'statistics',
export_type: statistic_index,
user: current_user,
query: params[:body],
key: params[:type_key])
else
File.root.join(export.file)
end
end
end
end

View File

@ -9,33 +9,33 @@ class WalletService
## credit an amount to wallet, if credit success then return a wallet transaction and notify to admin
def credit(amount)
transaction = nil
ActiveRecord::Base.transaction do
if @wallet.credit(amount)
if @wallet&.credit(amount)
transaction = WalletTransaction.new(
invoicing_profile: @user.invoicing_profile,
invoicing_profile: @user&.invoicing_profile,
wallet: @wallet,
transaction_type: 'credit',
amount: amount
)
if transaction.save
NotificationCenter.call type: 'notify_user_wallet_is_credited',
receiver: @wallet.user,
attached_object: transaction
NotificationCenter.call type: 'notify_admin_user_wallet_is_credited',
receiver: User.admins_and_managers,
attached_object: transaction
transaction
end
raise ActiveRecord::Rollback unless transaction.save
NotificationCenter.call type: 'notify_user_wallet_is_credited',
receiver: @wallet&.user,
attached_object: transaction
NotificationCenter.call type: 'notify_admin_user_wallet_is_credited',
receiver: User.admins_and_managers,
attached_object: transaction
end
raise ActiveRecord::Rollback
end
false
transaction
end
## debit an amount to wallet, if debit success then return a wallet transaction
def debit(amount)
transaction = nil
ActiveRecord::Base.transaction do
if @wallet.debit(amount)
if @wallet&.debit(amount)
transaction = WalletTransaction.new(
invoicing_profile: @user&.invoicing_profile,
wallet: @wallet,
@ -43,11 +43,10 @@ class WalletService
amount: amount
)
transaction if transaction.save
raise ActiveRecord::Rollback unless transaction.save
end
raise ActiveRecord::Rollback
end
false
transaction
end
## create a refund invoice associated with the given wallet transaction

View File

@ -1258,6 +1258,7 @@ en:
export_is_running_you_ll_be_notified_when_its_ready: "Export is running. You'll be notified when it's ready."
create_plans_to_start: "Start by creating new subscription plans."
click_here: "Click here to create your first one."
average_cart: "Average cart:"
#statistics graphs
stats_graphs:
statistics: "Statistics"

View File

@ -430,6 +430,7 @@ en:
subscriptions: "Subscriptions"
machines_hours: "Machines slots"
spaces: "Spaces"
orders: "Orders"
trainings: "Trainings"
events: "Events"
registrations: "Registrations"
@ -453,6 +454,9 @@ en:
account_creation: "Account creation"
project_publication: "Project publication"
duration: "Duration"
store: "Boutique"
paid-processed: "Paid and/or processed"
aborted: "Aborted"
#statistics exports to the Excel file format
export:
entries: "Entries"

View File

@ -283,7 +283,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

View File

@ -1027,6 +1027,30 @@ 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'))
type = StatisticType.create!([
{ statistic_index_id: index.id, key: 'store', label: I18n.t('statistics.store'),
graph: true, simple: true }
])
StatisticSubType.create!([
{ key: 'paid-processed', label: I18n.t('statistics.paid-processed'), statistic_types: [type] },
{ key: 'aborted', label: I18n.t('statistics.aborted'), statistic_types: [type] }
])
# average cart price for orders
average_cart = StatisticCustomAggregation.new(
statistic_type_id: type.id,
es_index: 'stats',
es_type: 'order',
field: 'average_cart',
query: '{"size":0, "aggregations":{"%{aggs_name}":{"avg":{"field":"ca", ' \
'"script":"BigDecimal.valueOf(_value).setScale(1, RoundingMode.HALF_UP)", "missing": 0}}}, ' \
'"query":{"bool":{"must":[{"range": {"date":{"gte":"%{start_date}", "lte":"%{end_date}"}}}]}}}'
)
average_cart.save!
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')

View File

@ -70,3 +70,103 @@ asset_8:
type: UserAvatar
created_at: 2018-12-27 14:48:52.141382000 Z
updated_at: 2018-12-27 14:48:52.141382000 Z
asset_2707:
id: 2707
viewable_id: 3
viewable_type: Product
attachment: appset-icon.png
type: ProductImage
created_at: '2022-09-13 10:05:07.212923'
updated_at: '2022-09-27 10:17:51.976313'
is_main: true
asset_2729:
id: 2729
viewable_id: 16
viewable_type: Product
attachment: osb.jpeg
type: ProductImage
created_at: '2022-09-14 13:50:12.588896'
updated_at: '2022-09-27 10:13:39.414052'
is_main: true
asset_2727:
id: 2727
viewable_id: 15
viewable_type: Product
attachment: cp.jpeg
type: ProductImage
created_at: '2022-09-14 13:49:08.237136'
updated_at: '2022-09-27 10:13:51.282923'
is_main: true
asset_2721:
id: 2721
viewable_id: 12
viewable_type: Product
attachment: casqueapointe.jpg
type: ProductImage
created_at: '2022-09-14 13:42:36.731315'
updated_at: '2022-09-27 10:14:23.371827'
is_main: true
asset_2731:
id: 2731
viewable_id: 17
viewable_type: Product
attachment: 3plis.jpeg
type: ProductImage
created_at: '2022-09-14 13:51:12.634244'
updated_at: '2022-09-27 10:13:28.657539'
is_main: true
asset_2725:
id: 2725
viewable_id: 14
viewable_type: Product
attachment: melamine.jpeg
type: ProductImage
created_at: '2022-09-14 13:48:05.033284'
updated_at: '2022-09-27 10:14:00.569077'
is_main: true
asset_2723:
id: 2723
viewable_id: 13
viewable_type: Product
attachment: mdf.jpeg
type: ProductImage
created_at: '2022-09-14 13:47:18.129274'
updated_at: '2022-09-27 10:14:10.137542'
is_main: true
asset_2733:
id: 2733
viewable_id: 18
viewable_type: Product
attachment: lamelle-colle.jpeg
type: ProductImage
created_at: '2022-09-14 13:52:08.666869'
updated_at: '2022-09-27 10:13:10.979122'
is_main: true
asset_2719:
id: 2719
viewable_id: 11
viewable_type: Product
attachment: bulldozer.png
type: ProductImage
created_at: '2022-09-14 13:41:16.209363'
updated_at: '2022-09-27 10:14:33.153221'
is_main: true
asset_2743:
id: 2743
viewable_id: 22
viewable_type: Product
attachment: filament_pla_1_75_bleu.jpeg
type: ProductImage
created_at: '2022-10-03 14:00:50.082820'
updated_at: '2022-10-03 14:00:50.082820'
is_main: true
asset_2744:
id: 2744
viewable_id: 23
viewable_type: Product
attachment: filament-pla_blanc.webp
type: ProductImage
created_at: '2022-10-03 14:01:43.711513'
updated_at: '2022-10-03 14:01:43.711513'
is_main: true

View File

@ -72,3 +72,159 @@ invoice_item_6:
object_type: Subscription
object_id: 4
main: true
invoice_item_11702:
id: 11702
invoice_id: 5811
amount: 4000
created_at: '2022-09-20 15:14:23.015625'
updated_at: '2022-09-20 15:14:23.180166'
description: Tablette lamellé-collé x 1
invoice_item_id:
footprint: 1d112a787e89d444f2817b909d7857a3a26fc2a3cd4c4404fb50bec282988a02
object_type: OrderItem
object_id: 1
main: true
invoice_item_11703:
id: 11703
invoice_id: 5811
amount: 500
created_at: '2022-09-20 15:14:23.287522'
updated_at: '2022-09-20 15:14:23.289957'
description: Panneaux de MDF x 1
invoice_item_id:
footprint: 67f143564abfeab7cb93988a02987a5a2b04a2632d0f9cfde61fa39c398cd02b
object_type: OrderItem
object_id: 2
main: false
invoice_item_11704:
id: 11704
invoice_id: 5812
amount: 6000
created_at: '2022-09-20 15:14:48.367843'
updated_at: '2022-09-20 15:14:48.385139'
description: Panneau de contre-plaqué x 4
invoice_item_id:
footprint: ea51c614632c018f0fae53c2bf8503c71d18c082ae3246155971e19ac2db143a
object_type: OrderItem
object_id: 3
main: true
invoice_item_11712:
id: 11712
invoice_id: 5816
amount: 119
created_at: '2022-10-04 12:36:03.103560'
updated_at: '2022-10-04 12:36:03.282529'
description: Filament PLA blanc x 1
invoice_item_id:
footprint: 4c5c8a1d7884502ea7791aeed1701cf3761562855d51ed19396b344c665c308b
object_type: OrderItem
object_id: 16
main: true
invoice_item_11713:
id: 11713
invoice_id: 5816
amount: 200
created_at: '2022-10-04 12:36:03.286776'
updated_at: '2022-10-04 12:36:03.290235'
description: Filament PLA bleu x 1
invoice_item_id:
footprint: 12c1a2c7fdb0f4b35d0c24137e6e0bdbbb39fa5c6500aa7837c67f92084d5071
object_type: OrderItem
object_id: 17
main: false
invoice_item_11714:
id: 11714
invoice_id: 5817
amount: 119
created_at: '2022-10-04 13:54:42.977460'
updated_at: '2022-10-04 13:54:42.992564'
description: Filament PLA blanc x 1
invoice_item_id:
footprint: cdb07fe1f0b986b9a53b6ec71f91a96e8b8d438592e88fd0041133b2ab46c8ca
object_type: OrderItem
object_id: 18
main: true
invoice_item_11715:
id: 11715
invoice_id: 5817
amount: 1500
created_at: '2022-10-04 13:54:43.021426'
updated_at: '2022-10-04 13:54:43.024326'
description: Panneau de contre-plaqué x 1
invoice_item_id:
footprint: 3ac08745b737bc370424ee9f1b68b421000eb98e0f3e65d7395d905d75de05c6
object_type: OrderItem
object_id: 19
main: false
invoice_item_11716:
id: 11716
invoice_id: 5818
amount: 1000
created_at: '2022-10-04 14:04:12.745449'
updated_at: '2022-10-04 14:04:12.749266'
description: Bulldozer x 1
invoice_item_id:
footprint: 1f4b6489579d7f045c846b97bc8ed402ec91f6fd7a5c2fa93b74eb7d21c7de39
object_type: OrderItem
object_id: 20
main: true
invoice_item_11717:
id: 11717
invoice_id: 5819
amount: 2
created_at: '2022-10-04 14:17:52.856695'
updated_at: '2022-10-04 14:17:52.871262'
description: Sticker Hello x 2
invoice_item_id:
footprint: 0fbecff886d9c2ffc332def13fa2c8869be8a40d2be7a3126a77974bd920c563
object_type: OrderItem
object_id: 21
main: true
invoice_item_11718:
id: 11718
invoice_id: 5819
amount: 4000
created_at: '2022-10-04 14:17:52.875854'
updated_at: '2022-10-04 14:17:52.878780'
description: Tablette lamellé-collé x 1
invoice_item_id:
footprint: ad160b9357518b31f63661fe38998b8c1d97fda61bc744876826ae638a8142a0
object_type: OrderItem
object_id: 22
main: false
invoice_item_11719:
id: 11719
invoice_id: 5820
amount: 12000
created_at: '2022-10-04 14:25:37.319701'
updated_at: '2022-10-04 14:25:37.322782'
description: Tablette lamellé-collé x 3
invoice_item_id:
footprint: 409a6f0b9f67510038a8b9a407db6d09b97f12d7169d6f6f121eedb2941d1bfc
object_type: OrderItem
object_id: 11
main: true
invoice_item_11720:
id: 11720
invoice_id: 5821
amount: 12000
created_at: '2022-10-04 14:32:28.209257'
updated_at: '2022-10-04 14:32:28.226556'
description: Tablette lamellé-collé x 3
invoice_item_id:
footprint: 733610b9c8e33f6a63f497d867d40386c623f12a526814d09d88a96f53741b7b
object_type: OrderItem
object_id: 23
main: true
invoice_item_11721:
id: 11721
invoice_id: 5822
amount: 3000
created_at: '2022-10-04 14:35:40.603969'
updated_at: '2022-10-04 14:35:40.608505'
description: Panneau de 3 plis mélèze x 1
invoice_item_id:
footprint: c1ad8c20d080ebc8267ff0d1b668feb8c989c2561be75d311f29f49a03405895
object_type: OrderItem
object_id: 24
main: true

View File

@ -124,3 +124,183 @@ invoice_6:
invoicing_profile_id: 8
operator_profile_id: 1
statistic_profile_id: 8
invoice_5811:
id: 5811
total: 4500
created_at: '2022-09-20 15:14:22.873707'
updated_at: '2022-09-20 15:14:23.496793'
reference: '2209002'
payment_method: local
avoir_date:
invoice_id:
type:
subscription_to_expire:
description:
wallet_amount:
wallet_transaction_id:
coupon_id:
footprint: '094590df6330de6a2b5d2ce7230673c7178f2639ca8ceb51ba272795349fff95'
environment: development
invoicing_profile_id: 3
operator_profile_id: 1
statistic_profile_id: 3
invoice_5812:
id: 5812
total: 6000
created_at: '2022-09-20 15:14:48.345927'
updated_at: '2022-09-20 15:14:48.409110'
reference: '2209004'
payment_method: local
avoir_date:
invoice_id:
type:
subscription_to_expire:
description:
wallet_amount:
wallet_transaction_id:
coupon_id:
footprint: ea5cc57a8af956f9a3e66ec1f9bd7fe5fe51e1220955cacb11a3cc99d2e6aa54
environment: development
invoicing_profile_id: 7
operator_profile_id: 1
statistic_profile_id: 7
invoice_5816:
id: 5816
total: 319
created_at: '2022-10-04 12:36:03.060832'
updated_at: '2022-10-04 12:36:03.445507'
reference: 2210002/VL
payment_method: card
avoir_date:
invoice_id:
type:
subscription_to_expire:
description:
wallet_amount:
wallet_transaction_id:
coupon_id:
footprint: ca8879f07cf1bd29500a9df29ce110843fb204ab0da8140bb4c6e8e908a65e0b
environment: development
invoicing_profile_id: 4
operator_profile_id: 4
statistic_profile_id: 4
invoice_5817:
id: 5817
total: 1296
created_at: '2022-10-04 13:54:42.975196'
updated_at: '2022-10-04 13:54:43.070098'
reference: 2210004/VL
payment_method: card
avoir_date:
invoice_id:
type:
subscription_to_expire:
description:
wallet_amount:
wallet_transaction_id:
coupon_id: 30
footprint: 838ae505dfba54f6a69cd341abf8f97fd9370c2acb81e37c6e70d98049d80646
environment: development
invoicing_profile_id: 4
operator_profile_id: 4
statistic_profile_id: 4
invoice_5818:
id: 5818
total: 1000
created_at: '2022-10-04 14:04:12.742685'
updated_at: '2022-10-04 14:04:12.774844'
reference: 2210006/VL
payment_method: card
avoir_date:
invoice_id:
type:
subscription_to_expire:
description:
wallet_amount:
wallet_transaction_id:
coupon_id:
footprint: 93db8c6ded7066a71927b6950cf5a9ea69aff25036df3a5a4259615347f2c8b2
environment: development
invoicing_profile_id: 4
operator_profile_id: 4
statistic_profile_id: 4
invoice_5819:
id: 5819
total: 4002
created_at: '2022-10-04 14:17:52.854636'
updated_at: '2022-10-04 14:17:52.898044'
reference: 2210008/VL
payment_method: card
avoir_date:
invoice_id:
type:
subscription_to_expire:
description:
wallet_amount:
wallet_transaction_id:
coupon_id:
footprint: bfd2f78a99aadd137f3f28fe8258a46ff8afa69b458f0a6f2dce1205fab22784
environment: development
invoicing_profile_id: 4
operator_profile_id: 4
statistic_profile_id: 4
invoice_5820:
id: 5820
total: 12000
created_at: '2022-10-04 14:25:37.291945'
updated_at: '2022-10-04 14:25:37.341401'
reference: '2210010'
payment_method: local
avoir_date:
invoice_id:
type:
subscription_to_expire:
description:
wallet_amount:
wallet_transaction_id:
coupon_id:
footprint: db6a663a25e19229ee93868765e1463ec7bd7856b173c5dd326d70d98369cfc0
environment: development
invoicing_profile_id: 3
operator_profile_id: 1
statistic_profile_id: 3
invoice_5821:
id: 5821
total: 12000
created_at: '2022-10-04 14:32:28.204985'
updated_at: '2022-10-04 14:32:28.292591'
reference: '2210012'
payment_method: local
avoir_date:
invoice_id:
type:
subscription_to_expire:
description:
wallet_amount:
wallet_transaction_id:
coupon_id:
footprint: 3ed9a13e5c7155bc3b186f3215b90345cc5520822728796e7c1c00ad5128ed01
environment: development
invoicing_profile_id: 2
operator_profile_id: 1
statistic_profile_id: 2
invoice_5822:
id: 5822
total: 3000
created_at: '2022-10-04 14:35:40.584472'
updated_at: '2022-10-04 14:35:40.637091'
reference: '2210014'
payment_method: local
avoir_date:
invoice_id:
type:
subscription_to_expire:
description:
wallet_amount:
wallet_transaction_id:
coupon_id:
footprint: 63d069e72b4992244861e9450b0c3ba3b9b30638af972a631b81578091d2925d
environment: development
invoicing_profile_id: 2
operator_profile_id: 1
statistic_profile_id: 2

129
test/fixtures/order_activities.yml vendored Normal file
View File

@ -0,0 +1,129 @@
order_activity_1:
id: 1
order_id: 1
operator_profile_id:
activity_type: paid
note:
created_at: '2022-09-20 15:14:22.596166'
updated_at: '2022-09-20 15:14:22.596166'
order_activity_2:
id: 2
order_id: 2
operator_profile_id:
activity_type: paid
note:
created_at: '2022-09-20 15:14:48.328689'
updated_at: '2022-09-20 15:14:48.328689'
order_activity_6:
id: 6
order_id: 9
operator_profile_id:
activity_type: paid
note:
created_at: '2022-10-04 12:36:02.719677'
updated_at: '2022-10-04 12:36:02.719677'
order_activity_7:
id: 7
order_id: 2
operator_profile_id: 2437
activity_type: ready
note: Merci de venir retirer la commande
created_at: '2022-10-04 13:37:49.932146'
updated_at: '2022-10-04 13:37:49.932146'
order_activity_11:
id: 11
order_id: 9
operator_profile_id: 2437
activity_type: ready
note: ''
created_at: '2022-10-04 13:50:30.609274'
updated_at: '2022-10-04 13:50:30.609274'
order_activity_12:
id: 12
order_id: 10
operator_profile_id:
activity_type: paid
note:
created_at: '2022-10-04 13:54:42.749983'
updated_at: '2022-10-04 13:54:42.749983'
order_activity_13:
id: 13
order_id: 10
operator_profile_id: 2437
activity_type: ready
note: ''
created_at: '2022-10-04 13:54:56.790702'
updated_at: '2022-10-04 13:54:56.790702'
order_activity_14:
id: 14
order_id: 11
operator_profile_id:
activity_type: paid
note:
created_at: '2022-10-04 14:04:12.721800'
updated_at: '2022-10-04 14:04:12.721800'
order_activity_15:
id: 15
order_id: 11
operator_profile_id: 2437
activity_type: ready
note: ''
created_at: '2022-10-04 14:04:28.489152'
updated_at: '2022-10-04 14:04:28.489152'
order_activity_16:
id: 16
order_id: 12
operator_profile_id:
activity_type: paid
note:
created_at: '2022-10-04 14:17:52.835587'
updated_at: '2022-10-04 14:17:52.835587'
order_activity_17:
id: 17
order_id: 12
operator_profile_id: 2437
activity_type: ready
note: "<p>je ne sais pas que te dire <u>mon ami</u></p>"
created_at: '2022-10-04 14:22:54.560670'
updated_at: '2022-10-04 14:22:54.560670'
order_activity_18:
id: 18
order_id: 5
operator_profile_id:
activity_type: paid
note:
created_at: '2022-10-04 14:25:37.278540'
updated_at: '2022-10-04 14:25:37.278540'
order_activity_19:
id: 19
order_id: 14
operator_profile_id:
activity_type: paid
note:
created_at: '2022-10-04 14:32:27.965847'
updated_at: '2022-10-04 14:32:27.965847'
order_activity_20:
id: 20
order_id: 14
operator_profile_id: 2437
activity_type: ready
note: "<p><strong>Lorem ipsum</strong> dolor sit amet</p>"
created_at: '2022-10-04 14:33:17.067080'
updated_at: '2022-10-04 14:33:17.067080'
order_activity_21:
id: 21
order_id: 15
operator_profile_id:
activity_type: paid
note:
created_at: <%= DateTime.current.utc.change({:hour => 10}).strftime('%Y-%m-%d %H:%M:%S.%9N Z') %>
updated_at: <%= DateTime.current.utc.change({:hour => 10}).strftime('%Y-%m-%d %H:%M:%S.%9N Z') %>
order_activity_22:
id: 22
order_id: 15
operator_profile_id: 2437
activity_type: ready
note: "<p>Votre <em>commande</em> est prête, merci de venir la récupérer <strong>jeudi</strong>
<u>après 14 h</u>.</p>"
created_at: <%= DateTime.current.utc.change({:hour => 15}).strftime('%Y-%m-%d %H:%M:%S.%9N Z') %>
updated_at: <%= DateTime.current.utc.change({:hour => 15}).strftime('%Y-%m-%d %H:%M:%S.%9N Z') %>

130
test/fixtures/order_items.yml vendored Normal file
View File

@ -0,0 +1,130 @@
order_item_1:
id: 1
order_id: 1
orderable_type: Product
orderable_id: 18
amount: 4000
quantity: 1
is_offered:
created_at: '2022-09-20 15:14:05.605885'
updated_at: '2022-09-20 15:14:05.605885'
order_item_2:
id: 2
order_id: 1
orderable_type: Product
orderable_id: 13
amount: 500
quantity: 1
is_offered:
created_at: '2022-09-20 15:14:09.022448'
updated_at: '2022-09-20 15:14:09.022448'
order_item_3:
id: 3
order_id: 2
orderable_type: Product
orderable_id: 15
amount: 1500
quantity: 4
is_offered:
created_at: '2022-09-20 15:14:34.899658'
updated_at: '2022-09-20 15:14:34.899658'
order_item_11:
id: 11
order_id: 5
orderable_type: Product
orderable_id: 18
amount: 4000
quantity: 3
is_offered:
created_at: '2022-09-28 08:31:30.377083'
updated_at: '2022-10-04 08:34:25.130457'
order_item_16:
id: 16
order_id: 9
orderable_type: Product
orderable_id: 23
amount: 119
quantity: 1
is_offered:
created_at: '2022-10-04 12:35:39.730968'
updated_at: '2022-10-04 12:35:39.730968'
order_item_17:
id: 17
order_id: 9
orderable_type: Product
orderable_id: 22
amount: 200
quantity: 1
is_offered:
created_at: '2022-10-04 12:35:40.562076'
updated_at: '2022-10-04 12:35:40.562076'
order_item_18:
id: 18
order_id: 10
orderable_type: Product
orderable_id: 23
amount: 119
quantity: 1
is_offered:
created_at: '2022-10-04 13:54:19.042858'
updated_at: '2022-10-04 13:54:19.042858'
order_item_19:
id: 19
order_id: 10
orderable_type: Product
orderable_id: 15
amount: 1500
quantity: 1
is_offered:
created_at: '2022-10-04 13:54:22.649153'
updated_at: '2022-10-04 13:54:22.649153'
order_item_20:
id: 20
order_id: 11
orderable_type: Product
orderable_id: 11
amount: 1000
quantity: 1
is_offered:
created_at: '2022-10-04 14:03:59.721736'
updated_at: '2022-10-04 14:03:59.721736'
order_item_21:
id: 21
order_id: 12
orderable_type: Product
orderable_id: 20
amount: 1
quantity: 2
is_offered:
created_at: '2022-10-04 14:05:23.583721'
updated_at: '2022-10-04 14:17:41.866526'
order_item_22:
id: 22
order_id: 12
orderable_type: Product
orderable_id: 18
amount: 4000
quantity: 1
is_offered:
created_at: '2022-10-04 14:17:33.257776'
updated_at: '2022-10-04 14:17:33.257776'
order_item_23:
id: 23
order_id: 14
orderable_type: Product
orderable_id: 18
amount: 4000
quantity: 3
is_offered:
created_at: '2022-10-04 14:31:51.106853'
updated_at: '2022-10-04 14:31:54.194063'
order_item_24:
id: 24
order_id: 15
orderable_type: Product
orderable_id: 17
amount: 3000
quantity: 1
is_offered:
created_at: <%= DateTime.current.utc.change({:hour => 10}).strftime('%Y-%m-%d %H:%M:%S.%9N Z') %>
updated_at: <%= DateTime.current.utc.change({:hour => 10}).strftime('%Y-%m-%d %H:%M:%S.%9N Z') %>

162
test/fixtures/orders.yml vendored Normal file
View File

@ -0,0 +1,162 @@
order_1:
id: 1
statistic_profile_id: 3
operator_profile_id: 1
token: 3R9wtsPjyYMHKqy-I2V5Cg1661868752184
reference:
state: paid
total: 4500
created_at: '2022-08-30 14:12:32.213832'
updated_at: '2022-09-20 15:14:23.501369'
wallet_amount:
wallet_transaction_id:
payment_method: local
footprint:
environment: test
coupon_id:
paid_total: 4500
invoice_id: 5811
order_2:
id: 2
statistic_profile_id: 7
operator_profile_id: 1
token: TDKq6p4f3WgDRMIvplEqpg1663686863871
reference: '005877-09-22'
state: ready
total: 6000
created_at: '2022-09-20 15:14:23.887098'
updated_at: '2022-10-04 13:37:49.935380'
wallet_amount:
wallet_transaction_id:
payment_method: local
footprint:
environment: test
coupon_id:
paid_total: 6000
invoice_id: 5812
order_5:
id: 5
statistic_profile_id: 3
operator_profile_id: 1
token: UbTOaTePhajDJPPH42e3yw1664353619348
reference: '005882-09-22'
state: paid
total: 12000
created_at: '2022-09-28 08:26:59.368029'
updated_at: '2022-10-04 14:25:37.345897'
wallet_amount:
wallet_transaction_id:
payment_method: local
footprint:
environment: test
coupon_id:
paid_total: 12000
invoice_id: 5820
order_9:
id: 9
statistic_profile_id: 4
operator_profile_id: 4
token: 8jOZwd2vkE86x26cg167iQ1664886886040
reference: '005888-10-22'
state: ready
total: 319
created_at: '2022-10-04 12:34:46.054976'
updated_at: '2022-10-04 13:50:30.611456'
wallet_amount:
wallet_transaction_id:
payment_method: card
footprint:
environment: test
coupon_id:
paid_total: 319
invoice_id: 5816
order_10:
id: 10
statistic_profile_id: 4
operator_profile_id: 4
token: ryrsm4yj21DTJv1qIgMuGA1664886964105
reference: '005890-10-22'
state: ready
total: 1619
created_at: '2022-10-04 12:36:04.117389'
updated_at: '2022-10-04 13:54:56.792550'
wallet_amount:
wallet_transaction_id:
payment_method: card
footprint:
environment: test
coupon_id: 30
paid_total: 1296
invoice_id: 5817
order_11:
id: 11
statistic_profile_id: 4
operator_profile_id: 4
token: ogvv31XzLE0zFLc_c8k4Sw1664891683573
reference: '005892-10-22'
state: ready
total: 1000
created_at: '2022-10-04 13:54:43.617309'
updated_at: '2022-10-04 14:04:28.490690'
wallet_amount:
wallet_transaction_id:
payment_method: card
footprint:
environment: test
coupon_id:
paid_total: 1000
invoice_id: 5818
order_12:
id: 12
statistic_profile_id: 4
operator_profile_id: 4
token: ttG9U892Bu0gbu8OnJkwTw1664892253183
reference: '005894-10-22'
state: ready
total: 4002
created_at: '2022-10-04 14:04:13.206078'
updated_at: '2022-10-04 14:22:54.562186'
wallet_amount:
wallet_transaction_id:
payment_method: card
footprint:
environment: test
coupon_id:
paid_total: 4002
invoice_id: 5819
order_14:
id: 14
statistic_profile_id: 2
operator_profile_id: 1
token: QsDdf_8YHL6mxUSMno-deg1664893537749
reference: '005898-10-22'
state: ready
total: 12000
created_at: '2022-10-04 14:25:37.773536'
updated_at: '2022-10-04 14:33:17.068764'
wallet_amount:
wallet_transaction_id:
payment_method: local
footprint:
environment: test
coupon_id:
paid_total: 12000
invoice_id: 5821
order_15:
id: 15
statistic_profile_id: 2
operator_profile_id: 1
token: JOLm4fAZjFSkmWzAX3KObw1664893948693
reference: '005900-10-22'
state: ready
total: 3000
created_at: <%= DateTime.current.utc.change({:hour => 10}).strftime('%Y-%m-%d %H:%M:%S.%9N Z') %>
updated_at: <%= DateTime.current.utc.change({:hour => 15}).strftime('%Y-%m-%d %H:%M:%S.%9N Z') %>
wallet_amount:
wallet_transaction_id:
payment_method: local
footprint:
environment: test
coupon_id:
paid_total: 3000
invoice_id: 5822

49
test/fixtures/product_categories.yml vendored Normal file
View File

@ -0,0 +1,49 @@
product_category_1:
id: 1
name: Consommables
slug: consommables
parent_id:
position: 0
created_at: '2022-08-23 07:55:35.573968'
updated_at: '2022-08-23 07:55:35.573968'
product_category_2:
id: 2
name: Fournitures
slug: fournitures
parent_id:
position: 1
created_at: '2022-08-23 07:55:49.380035'
updated_at: '2022-10-05 11:42:52.965226'
product_category_3:
id: 3
name: Filament imprimante 3D
slug: filament-imprimante-3d
parent_id: 1
position: 0
created_at: '2022-08-23 07:56:05.254962'
updated_at: '2022-08-23 07:56:05.254962'
product_category_4:
id: 4
name: Objets créés au fablab
slug: objets-crees-au-fablab
parent_id:
position: 2
created_at: '2022-09-14 13:39:03.170717'
updated_at: '2022-10-05 11:42:52.965226'
product_category_5:
id: 5
name: Bois
slug: bois
parent_id: 1
position: 1
created_at: '2022-09-14 14:57:58.136610'
updated_at: '2022-09-14 14:57:58.136610'
product_category_6:
id: 6
name: Stickers
slug: stickers
parent_id: 4
position: 0
created_at: '2022-09-28 08:12:35.823487'
updated_at: '2022-09-28 08:12:35.823487'

View File

@ -0,0 +1,275 @@
product_stock_movement_26:
id: 26
product_id: 3
quantity: 1
reason: inward_stock
stock_type: internal
remaining_stock: 1
date: '2022-09-13 12:52:52.656974'
created_at: '2022-09-13 12:52:52.669247'
updated_at: '2022-09-13 12:52:52.669247'
order_item_id:
product_stock_movement_33:
id: 33
product_id: 11
quantity: 10
reason: inward_stock
stock_type: external
remaining_stock: 10
date: '2022-09-14 13:41:16.204940'
created_at: '2022-09-14 13:41:16.212290'
updated_at: '2022-09-14 13:41:16.212290'
order_item_id:
product_stock_movement_34:
id: 34
product_id: 12
quantity: 1
reason: returned
stock_type: internal
remaining_stock: 1
date: '2022-09-14 13:42:53.702489'
created_at: '2022-09-14 13:42:53.706634'
updated_at: '2022-09-14 13:42:53.706634'
order_item_id:
product_stock_movement_35:
id: 35
product_id: 13
quantity: 1000
reason: inward_stock
stock_type: external
remaining_stock: 1000
date: '2022-09-14 13:47:18.120615'
created_at: '2022-09-14 13:47:18.131919'
updated_at: '2022-09-14 13:47:18.131919'
order_item_id:
product_stock_movement_36:
id: 36
product_id: 14
quantity: 800
reason: inward_stock
stock_type: external
remaining_stock: 800
date: '2022-09-14 13:48:05.025884'
created_at: '2022-09-14 13:48:05.035926'
updated_at: '2022-09-14 13:48:05.035926'
order_item_id:
product_stock_movement_37:
id: 37
product_id: 15
quantity: 600
reason: inward_stock
stock_type: external
remaining_stock: 600
date: '2022-09-14 13:49:08.203980'
created_at: '2022-09-14 13:49:08.240896'
updated_at: '2022-09-14 13:49:08.240896'
order_item_id:
product_stock_movement_38:
id: 38
product_id: 16
quantity: 100
reason: inward_stock
stock_type: external
remaining_stock: 100
date: '2022-09-14 13:50:12.581463'
created_at: '2022-09-14 13:50:12.591335'
updated_at: '2022-09-14 13:50:12.591335'
order_item_id:
product_stock_movement_39:
id: 39
product_id: 17
quantity: 100
reason: inward_stock
stock_type: external
remaining_stock: 100
date: '2022-09-14 13:51:12.627746'
created_at: '2022-09-14 13:51:12.636692'
updated_at: '2022-09-14 13:51:12.636692'
order_item_id:
product_stock_movement_40:
id: 40
product_id: 18
quantity: 20
reason: inward_stock
stock_type: external
remaining_stock: 20
date: '2022-09-14 13:52:08.655537'
created_at: '2022-09-14 13:52:08.672419'
updated_at: '2022-09-14 13:52:08.672419'
order_item_id:
product_stock_movement_41:
id: 41
product_id: 18
quantity: -1
reason: sold
stock_type: external
remaining_stock: 19
date: '2022-09-20 15:14:22.629666'
created_at: '2022-09-20 15:14:22.651877'
updated_at: '2022-09-20 15:14:22.651877'
order_item_id: 1
product_stock_movement_42:
id: 42
product_id: 13
quantity: -1
reason: sold
stock_type: external
remaining_stock: 999
date: '2022-09-20 15:14:22.688402'
created_at: '2022-09-20 15:14:22.692461'
updated_at: '2022-09-20 15:14:22.692461'
order_item_id: 2
product_stock_movement_43:
id: 43
product_id: 15
quantity: -4
reason: sold
stock_type: external
remaining_stock: 596
date: '2022-09-20 15:14:48.330513'
created_at: '2022-09-20 15:14:48.334360'
updated_at: '2022-09-20 15:14:48.334360'
order_item_id: 3
product_stock_movement_56:
id: 56
product_id: 22
quantity: 1000
reason: inward_stock
stock_type: external
remaining_stock: 1000
date: '2022-10-03 14:00:49.950506'
created_at: '2022-10-03 14:00:50.107429'
updated_at: '2022-10-03 14:00:50.107429'
order_item_id:
product_stock_movement_57:
id: 57
product_id: 22
quantity: 1000
reason: inward_stock
stock_type: internal
remaining_stock: 1000
date: '2022-10-03 14:00:49.950569'
created_at: '2022-10-03 14:00:50.136314'
updated_at: '2022-10-03 14:00:50.136314'
order_item_id:
product_stock_movement_58:
id: 58
product_id: 23
quantity: 1000
reason: inward_stock
stock_type: internal
remaining_stock: 1000
date: '2022-10-03 14:02:00.518281'
created_at: '2022-10-03 14:02:00.524640'
updated_at: '2022-10-03 14:02:00.524640'
order_item_id:
product_stock_movement_59:
id: 59
product_id: 23
quantity: 1000
reason: inward_stock
stock_type: external
remaining_stock: 1000
date: '2022-10-03 14:02:00.518337'
created_at: '2022-10-03 14:02:00.526611'
updated_at: '2022-10-03 14:02:00.526611'
order_item_id:
product_stock_movement_61:
id: 61
product_id: 23
quantity: -1
reason: sold
stock_type: external
remaining_stock: 999
date: '2022-10-04 12:36:02.765304'
created_at: '2022-10-04 12:36:02.788575'
updated_at: '2022-10-04 12:36:02.788575'
order_item_id: 16
product_stock_movement_62:
id: 62
product_id: 22
quantity: -1
reason: sold
stock_type: external
remaining_stock: 999
date: '2022-10-04 12:36:02.827107'
created_at: '2022-10-04 12:36:02.832197'
updated_at: '2022-10-04 12:36:02.832197'
order_item_id: 17
product_stock_movement_63:
id: 63
product_id: 23
quantity: -1
reason: sold
stock_type: external
remaining_stock: 998
date: '2022-10-04 13:54:42.768616'
created_at: '2022-10-04 13:54:42.783563'
updated_at: '2022-10-04 13:54:42.783563'
order_item_id: 18
product_stock_movement_64:
id: 64
product_id: 15
quantity: -1
reason: sold
stock_type: external
remaining_stock: 595
date: '2022-10-04 13:54:42.785294'
created_at: '2022-10-04 13:54:42.788610'
updated_at: '2022-10-04 13:54:42.788610'
order_item_id: 19
product_stock_movement_65:
id: 65
product_id: 11
quantity: -1
reason: sold
stock_type: external
remaining_stock: 9
date: '2022-10-04 14:04:12.723429'
created_at: '2022-10-04 14:04:12.729897'
updated_at: '2022-10-04 14:04:12.729897'
order_item_id: 20
product_stock_movement_67:
id: 67
product_id: 18
quantity: -1
reason: sold
stock_type: external
remaining_stock: 18
date: '2022-10-04 14:17:52.842942'
created_at: '2022-10-04 14:17:52.846168'
updated_at: '2022-10-04 14:17:52.846168'
order_item_id: 22
product_stock_movement_68:
id: 68
product_id: 18
quantity: -3
reason: sold
stock_type: external
remaining_stock: 15
date: '2022-10-04 14:25:37.280032'
created_at: '2022-10-04 14:25:37.283491'
updated_at: '2022-10-04 14:25:37.283491'
order_item_id: 11
product_stock_movement_69:
id: 69
product_id: 18
quantity: -3
reason: sold
stock_type: external
remaining_stock: 12
date: '2022-10-04 14:32:28.002830'
created_at: '2022-10-04 14:32:28.023296'
updated_at: '2022-10-04 14:32:28.023296'
order_item_id: 23
product_stock_movement_70:
id: 70
product_id: 17
quantity: -1
reason: sold
stock_type: external
remaining_stock: 99
date: '2022-10-04 14:35:40.556113'
created_at: '2022-10-04 14:35:40.560251'
updated_at: '2022-10-04 14:35:40.560251'
order_item_id: 24

182
test/fixtures/products.yml vendored Normal file
View File

@ -0,0 +1,182 @@
product_3:
id: 3
name: Caisse en bois
slug: caisse-en-bois
sku: '1694813216840'
description: "<p>C'est un superbe caisse en bois massif</p>"
is_active: true
product_category_id: 4
amount: 52300
quantity_min: 10
stock: '{"external": 0, "internal": 1}'
low_stock_alert: true
low_stock_threshold: 10
created_at: '2022-09-13 10:05:07.010322'
updated_at: '2022-09-14 13:39:55.309956'
product_11:
id: 11
name: Bulldozer
slug: bulldozer
sku: '816516515'
description: "<p>Jouet pour enfants</p>"
is_active: true
product_category_id: 4
amount: 1000
quantity_min: 1
stock: '{"external": 9, "internal": 0}'
low_stock_alert: false
low_stock_threshold:
created_at: '2022-09-14 13:41:16.207154'
updated_at: '2022-10-04 14:04:12.727452'
product_12:
id: 12
name: Casque à pointe
slug: casque-a-pointe
sku: '10210706'
description: "<p>Authentique casque à pointe allemand de la 1ère guerre mondiale</p>"
is_active: true
product_category_id: 4
amount: 142300
quantity_min: 1
stock: '{"external": 0, "internal": 1}'
low_stock_alert: false
low_stock_threshold:
created_at: '2022-09-14 13:42:36.729625'
updated_at: '2022-09-14 13:42:53.704455'
product_13:
id: 13
name: Panneaux de MDF
slug: panneaux-de-mdf
sku: 3-8612
description: ''
is_active: true
product_category_id: 5
amount: 500
quantity_min: 1
stock: '{"external": 999, "internal": 0}'
low_stock_alert: false
low_stock_threshold:
created_at: '2022-09-14 13:47:18.123531'
updated_at: '2022-09-20 15:14:22.690742'
product_14:
id: 14
name: Panneau de mélaminé
slug: panneau-de-melamine
sku: 12-4512
description: ''
is_active: true
product_category_id: 5
amount: 1000
quantity_min: 1
stock: '{"external": 800, "internal": 0}'
low_stock_alert: false
low_stock_threshold:
created_at: '2022-09-14 13:48:05.028266'
updated_at: '2022-09-14 14:58:28.577253'
product_15:
id: 15
name: Panneau de contre-plaqué
slug: panneau-de-contre-plaque
sku: 12-4613
description: ''
is_active: true
product_category_id: 5
amount: 1500
quantity_min: 1
stock: '{"external": 595, "internal": 0}'
low_stock_alert: false
low_stock_threshold:
created_at: '2022-09-14 13:49:08.233064'
updated_at: '2022-10-04 13:54:42.787290'
product_16:
id: 16
name: Panneau d'OSB
slug: panneau-osb
sku: 14-14488
description: ''
is_active: true
product_category_id: 5
amount: 1300
quantity_min: 1
stock: '{"external": 100, "internal": 0}'
low_stock_alert: false
low_stock_threshold:
created_at: '2022-09-14 13:50:12.584041'
updated_at: '2022-09-14 14:58:38.374112'
product_17:
id: 17
name: Panneau de 3 plis mélèze
slug: panneau-de-3-plis-meleze
sku: 14-47887
description: ''
is_active: true
product_category_id: 5
amount: 3000
quantity_min: 1
stock: '{"external": 99, "internal": 0}'
low_stock_alert: false
low_stock_threshold:
created_at: '2022-09-14 13:51:12.630109'
updated_at: '2022-10-04 14:35:40.558485'
product_18:
id: 18
name: Tablette lamellé-collé
slug: tablette-lamelle-colle
sku: 14-879895
description: ''
is_active: true
product_category_id: 5
amount: 4000
quantity_min: 1
stock: '{"external": 12, "internal": 0}'
low_stock_alert: false
low_stock_threshold:
created_at: '2022-09-14 13:52:08.659248'
updated_at: '2022-10-04 14:32:28.021046'
product_20:
id: 20
name: Sticker Hello
slug: sticker-hello
sku: 63-44801
description: <p>un joli sticker en forme de <a target="_blank" rel="noopener noreferrer nofollow"
href="https://www.example.com/hello">smiley</a> qui dit bonjour</p>
is_active: true
product_category_id: 6
amount: 1
quantity_min: 1
stock: '{"external": 15, "internal": 0}'
low_stock_alert: false
low_stock_threshold:
created_at: '2022-09-28 08:16:32.348880'
updated_at: '2022-10-04 14:17:52.839249'
product_22:
id: 22
name: Filament PLA bleu
slug: filament-pla-bleu
sku: 984-777
description: <p><a target="_blank" rel="noopener noreferrer nofollow" href="https://www.filimprimante3d.fr/content/80-le-pla-c-est-quoi-comment-choisir-son-filament-pla">Filament
PLA</a> 1,75 mm de couleur bleue</p>
is_active: true
product_category_id: 3
amount: 200
quantity_min: 1
stock: '{"external": 999, "internal": 1000}'
low_stock_alert: false
low_stock_threshold:
created_at: '2022-10-03 14:00:49.967342'
updated_at: '2022-10-04 12:36:02.830248'
product_23:
id: 23
name: Filament PLA blanc
slug: filament-pla-blanc
sku: 178-774
description: "<p>Filament PLA 1,75 mm blanc</p>"
is_active: true
product_category_id: 3
amount: 119
quantity_min: 1
stock: '{"external": 998, "internal": 1000}'
low_stock_alert: false
low_stock_threshold:
created_at: '2022-10-03 14:01:43.706366'
updated_at: '2022-10-04 13:54:42.782174'

View File

@ -27,3 +27,18 @@ users_role_6:
user_id: 6
role_id: 4
users_role_7:
user_id: 7
role_id: 2
users_role_8:
user_id: 8
role_id: 2
users_role_9:
user_id: 9
role_id: 2
users_role_10:
user_id: 10
role_id: 2

View File

@ -2,7 +2,7 @@
require 'test_helper'
class StatisticServiceTest < ActionDispatch::IntegrationTest
class ReservationSubscriptionStatisticServiceTest < ActionDispatch::IntegrationTest
setup do
@user = User.members.without_subscription.first
@admin = User.with_role(:admin).first

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
require 'test_helper'
class StoreStatisticServiceTest < ActionDispatch::IntegrationTest
setup do
@order = Order.find(15)
end
test 'build stats about orders' do
# Build the stats for the last 3 days, we expect the above invoices (reservations+subscription) to appear in the resulting stats
::Statistics::BuilderService.generate_statistic({ start_date: DateTime.current.beginning_of_day,
end_date: DateTime.current.end_of_day })
Stats::Order.refresh_index!
# we should find order id 15 (created today)
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)
end
def check_statistics_on_user(stat)
assert_equal @order.statistic_profile.str_gender, stat['gender']
assert_equal @order.statistic_profile.age.to_i, stat['age']
assert_equal @order.statistic_profile.group.slug, stat['group']
end
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class WalletServiceTest < ActiveSupport::TestCase
@ -54,9 +56,9 @@ class WalletServiceTest < ActiveSupport::TestCase
test 'rollback debited amount if has an error when create wallet transaction' do
service = WalletService.new(wallet: @vlonchamp_wallet)
expected_amount = @vlonchamp_wallet.amount
transaction = service.debit(5)
transaction = service.debit('error')
@vlonchamp_wallet.reload
assert_equal @vlonchamp_wallet.amount, expected_amount
assert_equal expected_amount, @vlonchamp_wallet.amount
assert_not transaction
end
end