mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-18 07:52:23 +01:00
export machines availabilities
This commit is contained in:
parent
4d09ed37a3
commit
504fe49964
@ -4,8 +4,8 @@
|
||||
# Controller used in the calendar management page
|
||||
##
|
||||
|
||||
Application.Controllers.controller "AdminCalendarController", ["$scope", "$state", "$uibModal", "moment", "Availability", 'Slot', 'Setting', 'growl', 'dialogs', 'bookingWindowStart', 'bookingWindowEnd', 'machinesPromise', '_t', 'uiCalendarConfig', 'CalendarConfig'
|
||||
($scope, $state, $uibModal, moment, Availability, Slot, Setting, growl, dialogs, bookingWindowStart, bookingWindowEnd, machinesPromise, _t, uiCalendarConfig, CalendarConfig) ->
|
||||
Application.Controllers.controller "AdminCalendarController", ["$scope", "$state", "$uibModal", "moment", "Availability", 'Slot', 'Setting', 'Export', 'growl', 'dialogs', 'bookingWindowStart', 'bookingWindowEnd', 'machinesPromise', '_t', 'uiCalendarConfig', 'CalendarConfig'
|
||||
($scope, $state, $uibModal, moment, Availability, Slot, Setting, Export, growl, dialogs, bookingWindowStart, bookingWindowEnd, machinesPromise, _t, uiCalendarConfig, CalendarConfig) ->
|
||||
|
||||
|
||||
|
||||
@ -121,6 +121,17 @@ Application.Controllers.controller "AdminCalendarController", ["$scope", "$state
|
||||
|
||||
|
||||
|
||||
##
|
||||
# Callback to alert the admin that the export request was acknowledged and is
|
||||
# processing right now.
|
||||
##
|
||||
$scope.alertExport = (type) ->
|
||||
Export.status({category: 'availabilities', type: type}).then (res) ->
|
||||
unless (res.data.exists)
|
||||
growl.success _t('admin_calendar.export_is_running_you_ll_be_notified_when_its_ready')
|
||||
|
||||
|
||||
|
||||
### PRIVATE SCOPE ###
|
||||
|
||||
##
|
||||
|
@ -30,6 +30,12 @@
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12 col-lg-3">
|
||||
<div class="m text-center">
|
||||
<a class="btn btn-default" ng-href="api/availabilities/export_index.xlsx" target="export-frame" ng-click="alertExport('index')">
|
||||
<i class="fa fa-file-excel-o"></i> {{ 'admin_calendar.availabilities' | translate }}
|
||||
</a>
|
||||
<iframe name="export-frame" height="0" width="0" class="none"></iframe>
|
||||
</div>
|
||||
<div class="widget panel b-a m m-t-lg">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3 translate>{{ 'admin_calendar.ongoing_reservations' }}</h3>
|
||||
|
@ -6,7 +6,7 @@ class API::AvailabilitiesController < API::ApiController
|
||||
respond_to :json
|
||||
|
||||
## machine/spaces availabilities are divided in multiple slots of 60 minutes
|
||||
SLOT_DURATION = 60
|
||||
SLOT_DURATION = ApplicationHelper::SLOT_DURATION
|
||||
|
||||
def index
|
||||
authorize Availability
|
||||
@ -69,7 +69,6 @@ class API::AvailabilitiesController < API::ApiController
|
||||
|
||||
# request for many days (week or month)
|
||||
else
|
||||
|
||||
@availabilities = Availability.includes(:tags, :machines, :trainings, :spaces, :event, :slots)
|
||||
.where('start_at >= ? AND end_at <= ?', start_date, end_date)
|
||||
@availabilities.each do |a|
|
||||
@ -222,6 +221,22 @@ class API::AvailabilitiesController < API::ApiController
|
||||
@reservation_slots = @availability.slots.includes(reservations: [user: [:profile]]).order('slots.start_at ASC')
|
||||
end
|
||||
|
||||
def export_availabilities
|
||||
authorize :export
|
||||
|
||||
export = Export.where({category:'availabilities', export_type: 'index'}).where('created_at > ?', Availability.maximum('updated_at')).last
|
||||
if export.nil? || !FileTest.exist?(export.file)
|
||||
@export = Export.new({category:'availabilities', export_type: 'index', user: current_user})
|
||||
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), :type => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', :disposition => 'attachment'
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def set_availability
|
||||
@availability = Availability.find(params[:id])
|
||||
|
@ -26,7 +26,14 @@ class API::ExportsController < API::ApiController
|
||||
when 'members'
|
||||
export = export.where('created_at > ?', User.with_role(:member).maximum('updated_at'))
|
||||
else
|
||||
raise ArgumentError, "Unknown type #{params[:type]}"
|
||||
raise ArgumentError, "Unknown export users/#{params[:type]}"
|
||||
end
|
||||
elsif params[:category] === 'availabilities'
|
||||
case params[:type]
|
||||
when 'index'
|
||||
export = export.where('created_at > ?', Availability.maximum('updated_at'))
|
||||
else
|
||||
raise ArgumentError, "Unknown type availabilities/#{params[:type]}"
|
||||
end
|
||||
end
|
||||
export = export.last
|
||||
|
@ -3,6 +3,9 @@ module ApplicationHelper
|
||||
include Twitter::Autolink
|
||||
require 'message_format'
|
||||
|
||||
## machine/spaces availabilities are divided in multiple slots of 60 minutes
|
||||
SLOT_DURATION = 60
|
||||
|
||||
##
|
||||
# Verify if the provided attribute is in the provided attributes array, whatever it exists or not
|
||||
# @param attributes {Array|nil}
|
||||
@ -26,6 +29,10 @@ module ApplicationHelper
|
||||
nil
|
||||
end
|
||||
|
||||
def print_slot(starting, ending)
|
||||
"#{starting.strftime('%H:%M')} - #{ending.strftime('%H:%M')}"
|
||||
end
|
||||
|
||||
def class_exists?(class_name)
|
||||
klass = Module.const_get(class_name)
|
||||
return klass.is_a?(Class)
|
||||
|
@ -28,6 +28,8 @@ class Export < ActiveRecord::Base
|
||||
StatisticsExportWorker.perform_async(self.id)
|
||||
when 'users'
|
||||
UsersExportWorker.perform_async(self.id)
|
||||
when 'availabilities'
|
||||
AvailabilitiesExportWorker.perform_async(self.id)
|
||||
else
|
||||
raise NoMethodError, "Unknown export service for #{category}/#{export_type}"
|
||||
end
|
||||
|
@ -1,5 +1,5 @@
|
||||
class AvailabilityPolicy < ApplicationPolicy
|
||||
%w(index? show? create? update? destroy? reservations?).each do |action|
|
||||
%w(index? show? create? update? destroy? reservations? export?).each do |action|
|
||||
define_method action do
|
||||
user.is_admin?
|
||||
end
|
||||
|
@ -1,5 +1,5 @@
|
||||
class ExportPolicy < Struct.new(:user, :export)
|
||||
%w(export_reservations export_members export_subscriptions download status).each do |action|
|
||||
%w(export_reservations export_members export_subscriptions export_availabilities download status).each do |action|
|
||||
define_method "#{action}?" do
|
||||
user.is_admin?
|
||||
end
|
||||
|
@ -6,58 +6,22 @@ require 'active_record'
|
||||
# require any helpers
|
||||
require './app/helpers/application_helper'
|
||||
|
||||
class UsersExportService
|
||||
class AvailabilitiesExportService
|
||||
|
||||
# export subscriptions
|
||||
def export_subscriptions(export)
|
||||
@subscriptions = Subscription.all.includes(:plan, :user => [:profile])
|
||||
# export all availabilities
|
||||
def export_index(export)
|
||||
@availabilities = Availability.all.includes(:machines, :trainings, :spaces, :event, :slots)
|
||||
|
||||
ActionController::Base.prepend_view_path './app/views/'
|
||||
# place data in view_assigns
|
||||
view_assigns = {subscriptions: @subscriptions}
|
||||
view_assigns = {availabilities: @availabilities}
|
||||
av = ActionView::Base.new(ActionController::Base.view_paths, view_assigns)
|
||||
av.class_eval do
|
||||
# include any needed helpers (for the view)
|
||||
include ApplicationHelper
|
||||
end
|
||||
|
||||
content = av.render template: 'exports/users_subscriptions.xlsx.axlsx'
|
||||
# write content to file
|
||||
File.open(export.file,"w+b") {|f| f.puts content }
|
||||
end
|
||||
|
||||
# export reservations
|
||||
def export_reservations(export)
|
||||
@reservations = Reservation.all.includes(:slots, :reservable, :user => [:profile])
|
||||
|
||||
ActionController::Base.prepend_view_path './app/views/'
|
||||
# place data in view_assigns
|
||||
view_assigns = {reservations: @reservations}
|
||||
av = ActionView::Base.new(ActionController::Base.view_paths, view_assigns)
|
||||
av.class_eval do
|
||||
# include any needed helpers (for the view)
|
||||
include ApplicationHelper
|
||||
end
|
||||
|
||||
content = av.render template: 'exports/users_reservations.xlsx.axlsx'
|
||||
# write content to file
|
||||
File.open(export.file,"w+b") {|f| f.puts content }
|
||||
end
|
||||
|
||||
# export members
|
||||
def export_members(export)
|
||||
@members = User.with_role(:member).includes(:group, :trainings, :tags, :invoices, :projects, :subscriptions => [:plan], :profile => [:address, :organization => [:address]])
|
||||
|
||||
ActionController::Base.prepend_view_path './app/views/'
|
||||
# place data in view_assigns
|
||||
view_assigns = {members: @members}
|
||||
av = ActionView::Base.new(ActionController::Base.view_paths, view_assigns)
|
||||
av.class_eval do
|
||||
# include any needed helpers (for the view)
|
||||
include ApplicationHelper
|
||||
end
|
||||
|
||||
content = av.render template: 'exports/users_members.xlsx.axlsx'
|
||||
content = av.render template: 'exports/availabilities_index.xlsx.axlsx'
|
||||
# write content to file
|
||||
File.open(export.file,"w+b") {|f| f.puts content }
|
||||
end
|
||||
|
@ -3,31 +3,38 @@ wb = xlsx_package.workbook
|
||||
header = wb.styles.add_style :b => true, :bg_color => Stylesheet.primary.upcase.gsub('#', 'FF'), :fg_color => 'FFFFFFFF'
|
||||
date = wb.styles.add_style :format_code => Rails.application.secrets.excel_date_format
|
||||
|
||||
wb.add_worksheet(name: t('export_subscriptions.subscriptions')) do |sheet|
|
||||
## Machines slots
|
||||
wb.add_worksheet(name: t('export_availabilities.machines')) do |sheet|
|
||||
|
||||
## data table
|
||||
# heading labels
|
||||
columns = [t('export_subscriptions.id'), t('export_subscriptions.customer'), t('export_subscriptions.email'),
|
||||
t('export_subscriptions.subscription'), t('export_subscriptions.period'), t('export_subscriptions.start_date'),
|
||||
t('export_subscriptions.expiration_date'), t('export_subscriptions.amount'), t('export_subscriptions.payment_method')]
|
||||
columns = [t('export_availabilities.date'), t('export_availabilities.slot'), t('export_availabilities.machine'),
|
||||
t('export_availabilities.reservations')]
|
||||
sheet.add_row columns, :style => header
|
||||
|
||||
# data rows
|
||||
@subscriptions.each do |sub|
|
||||
data = [
|
||||
sub.user.id,
|
||||
sub.user.profile.full_name,
|
||||
sub.user.email,
|
||||
sub.plan.human_readable_name(group: true),
|
||||
t("duration.#{sub.plan.interval}", count: sub.plan.interval_count),
|
||||
sub.created_at.to_date,
|
||||
sub.expired_at.to_date,
|
||||
number_to_currency(sub.plan.amount / 100),
|
||||
(sub.stp_subscription_id.nil?)? t('export_subscriptions.local_payment') : t('export_subscriptions.online_payment')
|
||||
]
|
||||
styles = [nil, nil, nil, nil, nil, date, date, nil, nil]
|
||||
types = [:integer, :string, :string, :string, :string, :date, :date, :string, :string]
|
||||
@availabilities.where(available_type: 'machines').order(:start_at).each do |a|
|
||||
a.machines.each do |m|
|
||||
((a.end_at - a.start_at) / ApplicationHelper::SLOT_DURATION.minutes).to_i.times do |i|
|
||||
start_at = a.start_at + (i * ApplicationHelper::SLOT_DURATION).minutes
|
||||
end_at = a.start_at + (i * ApplicationHelper::SLOT_DURATION).minutes + ApplicationHelper::SLOT_DURATION.minutes
|
||||
reservations = 0
|
||||
if a.slots and a.slots.map(&:start_at).include? start_at
|
||||
reservations = 1
|
||||
end
|
||||
|
||||
sheet.add_row data, :style => styles, :types => types
|
||||
data = [
|
||||
start_at.to_date,
|
||||
print_slot(start_at, end_at),
|
||||
m.name,
|
||||
reservations
|
||||
]
|
||||
styles = [date, nil, nil, nil]
|
||||
types = [:date, :string, :string, :integer]
|
||||
|
||||
sheet.add_row data, :style => styles, :types => types
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
class UsersExportWorker
|
||||
class AvailabilitiesExportWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(export_id)
|
||||
@ -8,14 +8,14 @@ class UsersExportWorker
|
||||
raise SecurityError, 'Not allowed to export'
|
||||
end
|
||||
|
||||
unless export.category == 'users'
|
||||
unless export.category == 'availabilities'
|
||||
raise KeyError, 'Wrong worker called'
|
||||
end
|
||||
|
||||
service = UsersExportService.new
|
||||
service = AvailabilitiesExportService.new
|
||||
method_name = "export_#{export.export_type}"
|
||||
|
||||
if %w(members subscriptions reservations).include?(export.export_type) and service.respond_to?(method_name)
|
||||
if %w(index).include?(export.export_type) and service.respond_to?(method_name)
|
||||
service.public_send(method_name, export)
|
||||
|
||||
NotificationCenter.call type: :notify_admin_export_complete,
|
||||
|
@ -18,6 +18,7 @@ en:
|
||||
trainings: "Trainings"
|
||||
machines: "Machines"
|
||||
spaces: "Spaces"
|
||||
availabilities: "Availabilities"
|
||||
ongoing_reservations: "Ongoing reservations"
|
||||
no_reservations: "No reservations"
|
||||
confirmation_required: "Confirmation required"
|
||||
@ -45,6 +46,7 @@ en:
|
||||
the_slot_START-END_has_been_successfully_deleted: "The slot {{START}} - {{END}} has been successfully deleted" # angular interpolation
|
||||
unable_to_delete_the_slot_START-END_because_it_s_already_reserved_by_a_member: "Unable to delete the slot {{START}} - {{END}} because it's already reserved by a member" # angular interpolation
|
||||
you_should_select_at_least_a_machine: "You should select at least one machine on this slot."
|
||||
export_is_running_you_ll_be_notified_when_its_ready: "Export is running. You'll be notified when it's ready."
|
||||
|
||||
project_elements:
|
||||
# management of the projects' components
|
||||
|
@ -18,6 +18,7 @@ fr:
|
||||
trainings: "Formations"
|
||||
machines: "Machines"
|
||||
spaces: "Espaces"
|
||||
availabilities: "Disponibilités"
|
||||
ongoing_reservations: "Réservations en cours"
|
||||
no_reservations: "Aucune réservation"
|
||||
confirmation_required: "Confirmation requise"
|
||||
@ -45,6 +46,7 @@ fr:
|
||||
the_slot_START-END_has_been_successfully_deleted: "Le créneau {{START}} - {{END}} a bien été supprimé" # angular interpolation
|
||||
unable_to_delete_the_slot_START-END_because_it_s_already_reserved_by_a_member: "Le créneau {{START}} - {{END}} n'a pu être supprimé car il est déjà réservé par un membre" # angular interpolation
|
||||
you_should_select_at_least_a_machine: "Vous devriez sélectionne au moins une machine pour ce créneau."
|
||||
export_is_running_you_ll_be_notified_when_its_ready: "L'export est en cours. Vous serez notifié lorsqu'il sera prêt."
|
||||
|
||||
project_elements:
|
||||
# gestion des éléments constituant les projets
|
||||
|
@ -186,6 +186,18 @@ en:
|
||||
local_payment: "Payment at the reception"
|
||||
online_payment: "Online payment"
|
||||
|
||||
export_availabilities:
|
||||
# reservation slots export, by type, to EXCEL format
|
||||
machines: "Machines"
|
||||
trainings: "Trainings"
|
||||
spaces: "Spaces"
|
||||
events: "Events"
|
||||
date: "Date"
|
||||
slot: "Slot"
|
||||
machine: "Machine"
|
||||
reservations: "Reservations"
|
||||
|
||||
|
||||
api:
|
||||
notifications:
|
||||
# internal app notifications
|
||||
@ -279,6 +291,7 @@ en:
|
||||
users_members: "of the members' list"
|
||||
users_subscriptions: "of the subscriptions' list"
|
||||
users_reservations: "of the reservations' list"
|
||||
availabilities_index: "of the reservations availabilities"
|
||||
is_over: "is over."
|
||||
download_here: "Download here"
|
||||
notify_member_about_coupon:
|
||||
|
@ -186,6 +186,18 @@ fr:
|
||||
local_payment: "Paiement à l'accueil"
|
||||
online_payment: "Paiement en ligne"
|
||||
|
||||
export_availabilities:
|
||||
# export des listes de créneaux de réservations, par type, au format EXCEL
|
||||
machines: "Machines"
|
||||
trainings: "Formations"
|
||||
spaces: "Espaces"
|
||||
events: "Évènements"
|
||||
date: "Date"
|
||||
slot: "Créneau"
|
||||
machine: "Machine"
|
||||
reservations: "Réservations"
|
||||
|
||||
|
||||
api:
|
||||
notifications:
|
||||
# notifications internes à l'application
|
||||
@ -279,6 +291,7 @@ fr:
|
||||
users_members: "de la liste des membres"
|
||||
users_subscriptions: "de la liste des abonnements"
|
||||
users_reservations: "de la liste des réservations"
|
||||
availabilities_index: "des disponibilités de réservations"
|
||||
is_over: "est terminé."
|
||||
download_here: "Téléchargez ici"
|
||||
notify_member_about_coupon:
|
||||
|
@ -272,6 +272,7 @@ en:
|
||||
users_members: "of the members' list"
|
||||
users_subscriptions: "of the subscriptions' list"
|
||||
users_reservations: "of the reservations' list"
|
||||
availabilities_index: "of the reservations availabilities"
|
||||
click_to_download: "Excel file generated successfully. To download it, click"
|
||||
here: "here"
|
||||
|
||||
|
@ -272,6 +272,7 @@ fr:
|
||||
users_members: "de la liste des membres"
|
||||
users_subscriptions: "de la liste des abonnements"
|
||||
users_reservations: "de la liste des réservations"
|
||||
availabilities_index: "des disponibilités de réservations"
|
||||
click_to_download: "La génération est terminée. Pour télécharger le fichier Excel, cliquez"
|
||||
here: "ici"
|
||||
|
||||
|
@ -84,6 +84,7 @@ Rails.application.routes.draw do
|
||||
get 'spaces/:space_id', action: 'spaces', on: :collection
|
||||
get 'reservations', on: :member
|
||||
get 'public', on: :collection
|
||||
get '/export_index', action: 'export_availabilities', on: :collection
|
||||
end
|
||||
|
||||
resources :groups, only: [:index, :create, :update, :destroy]
|
||||
|
Loading…
x
Reference in New Issue
Block a user