mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-19 13:54:25 +01:00
Ability to configure reservation slot restrict for plans
This commit is contained in:
parent
767a8cd332
commit
74cc69d1bd
15
CHANGELOG.md
15
CHANGELOG.md
@ -1,5 +1,6 @@
|
||||
# Changelog Fab Manager
|
||||
|
||||
- Ability to configure reservation slot restrict for plans
|
||||
- Ability to create and delete periodic calendar availabilities (recurrence)
|
||||
- Ability to fully customize the home page
|
||||
- Automated setup assistant
|
||||
@ -140,7 +141,7 @@
|
||||
## v4.0.4 2019 August 14
|
||||
|
||||
- Fix a bug: #140 VAT rate is erroneous in invoices.
|
||||
Note: this bug was introduced in v4.0.3 and requires (if you are on v4.0.3) to regenerate the invoices since August 1st (if
|
||||
Note: this bug was introduced in v4.0.3 and requires (if you are on v4.0.3) to regenerate the invoices since August 1st (if
|
||||
- [TODO DEPLOY] `rake fablab:maintenance:regenerate_invoices[2019,8]`
|
||||
|
||||
## v4.0.3 2019 August 01
|
||||
@ -184,7 +185,7 @@
|
||||
- Refactored user's profile to keep invoicing data after an user was deleted
|
||||
- Refactored user's profile to keep statistical data after an user was deleted
|
||||
- Ability to delete an user (fixes #129 and #120)
|
||||
- Ask user acceptance before deposing analytics cookies
|
||||
- Ask user acceptance before deposing analytics cookies
|
||||
- Fix a bug: (spanish) some translations are not loaded correctly
|
||||
- Fix a bug: some users may not appear in the admin's general listing
|
||||
- Fix a bug: Availabilities export report an erroneous number of reservations for machine availabilities (#131)
|
||||
@ -381,8 +382,8 @@
|
||||
- Fix a security issue: sprockets < 2.12.5 has a security vulnerability as described in [CVE-2018-3760](https://nvd.nist.gov/vuln/detail/CVE-2018-3760)
|
||||
- Ensure elasticSearch indices are started with green status on new installations
|
||||
- Refactored User.to_json to remove code duplication
|
||||
- Fixed syntax and typos in README
|
||||
- [TODO DEPLOY] **IMPORTANT** Please read [elastic_upgrade.md](doc/elastic_upgrade.md) for instructions on upgrading ElasticSearch.
|
||||
- Fixed syntax and typos in README
|
||||
- [TODO DEPLOY] **IMPORTANT** Please read [elastic_upgrade.md](doc/elastic_upgrade.md) for instructions on upgrading ElasticSearch.
|
||||
- [TODO DEPLOY] `rake fablab:fix:categories_slugs`
|
||||
- [TODO DEPLOY] -> (only dev) `bundle install`
|
||||
- [TODO DEPLOY] `rake db:seed`
|
||||
@ -395,7 +396,7 @@
|
||||
- Set Stripe API version, all fab-managers has to use this version because codebase relies on it
|
||||
- Fix a security issue: OmniAuth < 1.3.2 has a security vulnerability described in [CVE-2017-18076](https://nvd.nist.gov/vuln/detail/CVE-2017-18076)
|
||||
- Fix a security issue: rack-protection < 1.5.5 has a security vulnerability described in [CVE-2018-1000119](https://nvd.nist.gov/vuln/detail/CVE-2018-1000119)
|
||||
- Fix a security issue: http gem < 0.7.3 has a security vulnerability described in [CVE-2015-1828](https://nvd.nist.gov/vuln/detail/CVE-2015-1828), updates twitter gem as a dependency
|
||||
- Fix a security issue: http gem < 0.7.3 has a security vulnerability described in [CVE-2015-1828](https://nvd.nist.gov/vuln/detail/CVE-2015-1828), updates twitter gem as a dependency
|
||||
|
||||
## v2.6.3 2018 January 2
|
||||
|
||||
@ -451,12 +452,12 @@
|
||||
|
||||
## v2.5.13 2017 September 11
|
||||
|
||||
- Fix a bug: ActiveRecord::RecordNotFound when running rake task fix:recursive_events_over_DST with recursive events which the initial event was deleted
|
||||
- Fix a bug: ActiveRecord::RecordNotFound when running rake task fix:recursive_events_over_DST with recursive events which the initial event was deleted
|
||||
|
||||
## v2.5.12 2017 September 11
|
||||
|
||||
- Fix a bug: Long words overflow from homepage's events blocks
|
||||
- Fix a bug: ActiveRecord::RecordNotFound when running rake task fix:recursive_events_over_DST with non-recursive events
|
||||
- Fix a bug: ActiveRecord::RecordNotFound when running rake task fix:recursive_events_over_DST with non-recursive events
|
||||
|
||||
## v2.5.11 2017 September 7
|
||||
|
||||
|
@ -297,7 +297,9 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
|
||||
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }],
|
||||
trainingsPromise: ['Training', function (Training) { return Training.query().$promise; }],
|
||||
spacesPromise: ['Space', function (Space) { return Space.query().$promise; }],
|
||||
tagsPromise: ['Tag', function (Tag) { return Tag.query().$promise; }]
|
||||
tagsPromise: ['Tag', function (Tag) { return Tag.query().$promise; }],
|
||||
plansPromise: ['Plan', function (Plan) { return Plan.query().$promise; }],
|
||||
groupsPromise: ['Group', function (Group) { return Group.query().$promise; }]
|
||||
} });
|
||||
// when the modal is closed, we send the slot to the server for saving
|
||||
modalInstance.result.then(
|
||||
@ -394,8 +396,8 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
|
||||
/**
|
||||
* Controller used in the slot creation modal window
|
||||
*/
|
||||
Application.Controllers.controller('CreateEventModalController', ['$scope', '$uibModalInstance', '$sce', 'moment', 'start', 'end', 'machinesPromise', 'Availability', 'trainingsPromise', 'spacesPromise', 'tagsPromise', 'growl', '_t',
|
||||
function ($scope, $uibModalInstance, $sce, moment, start, end, machinesPromise, Availability, trainingsPromise, spacesPromise, tagsPromise, growl, _t) {
|
||||
Application.Controllers.controller('CreateEventModalController', ['$scope', '$uibModalInstance', '$sce', 'moment', 'start', 'end', 'machinesPromise', 'Availability', 'trainingsPromise', 'spacesPromise', 'tagsPromise', 'plansPromise', 'groupsPromise', 'growl', '_t',
|
||||
function ($scope, $uibModalInstance, $sce, moment, start, end, machinesPromise, Availability, trainingsPromise, spacesPromise, tagsPromise, plansPromise, groupsPromise, growl, _t) {
|
||||
// $uibModal parameter
|
||||
$scope.start = start;
|
||||
|
||||
@ -414,6 +416,21 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
|
||||
// all tags list
|
||||
$scope.tags = tagsPromise;
|
||||
|
||||
$scope.isOnlySubscriptions = false;
|
||||
$scope.selectedPlans = [];
|
||||
$scope.selectedPlansBinding = {};
|
||||
// list of plans, classified by group
|
||||
$scope.plansClassifiedByGroup = [];
|
||||
for (let group of Array.from(groupsPromise)) {
|
||||
const groupObj = { id: group.id, name: group.name, plans: [] };
|
||||
for (let plan of Array.from(plansPromise)) {
|
||||
if (plan.group_id === group.id) { groupObj.plans.push(plan); }
|
||||
}
|
||||
if (groupObj.plans.length > 0) {
|
||||
$scope.plansClassifiedByGroup.push(groupObj);
|
||||
}
|
||||
}
|
||||
|
||||
// machines associated with the created slot
|
||||
$scope.selectedMachines = [];
|
||||
$scope.selectedMachinesBinding = {};
|
||||
@ -463,6 +480,9 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
|
||||
// localized name(s) of the selected tag(s)
|
||||
$scope.tagsName = '';
|
||||
|
||||
// localized name(s) of the selected plan(s)
|
||||
$scope.plansName = '';
|
||||
|
||||
/**
|
||||
* Adds or removes the provided machine from the current slot
|
||||
* @param machine {Object}
|
||||
@ -491,6 +511,34 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or removes the provided plan from the current slot
|
||||
* @param plan {Object}
|
||||
*/
|
||||
$scope.toggleSelectPlan = function (plan) {
|
||||
const index = $scope.selectedPlans.indexOf(plan);
|
||||
if (index > -1) {
|
||||
return $scope.selectedMachines.splice(index, 1);
|
||||
} else {
|
||||
return $scope.selectedMachines.push(machine);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Select/unselect all the plans
|
||||
*/
|
||||
$scope.toggleAllPlans = function() {
|
||||
const count = $scope.selectedPlans.length;
|
||||
$scope.selectedPlans = [];
|
||||
$scope.selectedPlansBinding = {};
|
||||
if (count == 0) {
|
||||
plansPromise.forEach(function (plan) {
|
||||
$scope.selectedPlans.push(plan);
|
||||
$scope.selectedPlansBinding[plan.id] = true;
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback for the modal window validation: save the slot and closes the modal
|
||||
*/
|
||||
@ -510,6 +558,9 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
|
||||
if ($scope.availability.is_recurrent) {
|
||||
$scope.availability.occurrences = $scope.occurrences;
|
||||
}
|
||||
if ($scope.isOnlySubscriptions && $scope.selectedPlans.length > 0) {
|
||||
$scope.availability.plan_ids = $scope.selectedPlans.map(function (p) { return p.id; });
|
||||
}
|
||||
return Availability.save(
|
||||
{ availability: $scope.availability },
|
||||
function (availability) { $uibModalInstance.close(availability); }
|
||||
@ -560,6 +611,14 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
|
||||
$scope.selectedSpace = $scope.spaces[0];
|
||||
}
|
||||
|
||||
// when disable is only subscriptions option, reset all selected plans
|
||||
$scope.$watch('isOnlySubscriptions', function(value) {
|
||||
if (!value) {
|
||||
$scope.selectedPlans = [];
|
||||
$scope.selectedPlansBinding = {};
|
||||
}
|
||||
});
|
||||
|
||||
// When we configure a machine/space availability, do not let the user change the end time, as the total
|
||||
// time must be dividable by Fablab.slotDuration minutes (base slot duration). For training availabilities, the user
|
||||
// can configure any duration as it does not matters.
|
||||
@ -685,6 +744,9 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
|
||||
return $scope.availability.tag_ids.indexOf(t.id) > -1;
|
||||
})
|
||||
$scope.tagsName = localizedList(tags);
|
||||
if ($scope.isOnlySubscriptions && $scope.selectedPlans.length > 0) {
|
||||
$scope.plansName = localizedList($scope.selectedPlans);
|
||||
}
|
||||
}
|
||||
|
||||
const localizedList = function (items) {
|
||||
|
@ -367,6 +367,8 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
|
||||
$scope.settings = settingsPromise;
|
||||
|
||||
// list of plans, classified by group
|
||||
$scope.groups = groupsPromise;
|
||||
$scope.plans = plansPromise;
|
||||
$scope.plansClassifiedByGroup = [];
|
||||
for (let group of Array.from(groupsPromise)) {
|
||||
const groupObj = { id: group.id, name: group.name, plans: [] };
|
||||
|
@ -23,6 +23,8 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
plan: '=',
|
||||
planSelectionTime: '=',
|
||||
settings: '=',
|
||||
plans: '=',
|
||||
groups: '=',
|
||||
onSlotAddedToCart: '=',
|
||||
onSlotRemovedFromCart: '=',
|
||||
onSlotStartToModify: '=',
|
||||
@ -134,6 +136,27 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
$scope.payCart = function () {
|
||||
// first, we check that a user was selected
|
||||
if (Object.keys($scope.user).length > 0) {
|
||||
|
||||
// check user was selected a plan if slot is restricted for subcriptions
|
||||
const slotHasPlan = [];
|
||||
$scope.events.reserved.forEach(slot => {
|
||||
if (slot.plan_ids.length > 0) {
|
||||
if (
|
||||
($scope.selectedPlan && _.include(slot.plan_ids, $scope.selectedPlan.id)) ||
|
||||
($scope.user.subscribed_plan && _.include(slot.plan_ids, $scope.user.subscribed_plan.id))
|
||||
) {
|
||||
slotHasPlan.push(true);
|
||||
} else {
|
||||
slotHasPlan.push(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
const hasPlanForSlot = slotHasPlan.every(a => a);
|
||||
if (!hasPlanForSlot) {
|
||||
return growl.error(_t('app.shared.cart.slot_restrict_subcriptions_must_select_plan'));
|
||||
}
|
||||
|
||||
|
||||
const reservation = mkReservation($scope.user, $scope.events.reserved, $scope.selectedPlan);
|
||||
|
||||
return Wallet.getWalletByUser({ user_id: $scope.user.id }, function (wallet) {
|
||||
@ -262,6 +285,21 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
*/
|
||||
var slotSelectionChanged = function () {
|
||||
if ($scope.slot) {
|
||||
// build a list of plans if this slot is restricted for subcriptions
|
||||
if ($scope.slot.plan_ids.length > 0) {
|
||||
const _plans = _.filter($scope.plans, p => _.include($scope.slot.plan_ids, p.id));
|
||||
$scope.slot.plansGrouped = [];
|
||||
for (let group of Array.from($scope.groups)) {
|
||||
const groupObj = { id: group.id, name: group.name, plans: [] };
|
||||
for (let plan of Array.from(_plans)) {
|
||||
if (plan.group_id === group.id) { groupObj.plans.push(plan); }
|
||||
}
|
||||
if (groupObj.plans.length > 0) {
|
||||
$scope.slot.plansGrouped.push(groupObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$scope.slot.is_reserved && !$scope.events.modifiable && !$scope.slot.is_completed) {
|
||||
// slot is not reserved and we are not currently modifying a slot
|
||||
// -> can be added to cart or removed if already present
|
||||
|
@ -93,12 +93,41 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-t-sm">
|
||||
<p class="text-center font-sbold" translate>{{ 'app.admin.calendar.restrict_this_slot_for_subcriptions_optional' }}</p>
|
||||
<div class="row">
|
||||
<div class="form-group col-md-12">
|
||||
<label for="is_only_subscriptions" translate>{{ 'app.admin.calendar.enabled' }}</label>
|
||||
<input bs-switch
|
||||
ng-model="isOnlySubscriptions"
|
||||
id="is_only_subscriptions"
|
||||
type="checkbox"
|
||||
class="form-control"
|
||||
switch-on-text="{{ 'app.shared.buttons.yes' | translate }}"
|
||||
switch-off-text="{{ 'app.shared.buttons.no' | translate }}"
|
||||
switch-animate="true"/>
|
||||
</div>
|
||||
<div class="col-md-12" ng-show="isOnlySubscriptions">
|
||||
<p class="font-sbold m-t-sm">{{ 'app.admin.calendar.select_some_plans' | translate }}</p>
|
||||
|
||||
<div class="form-group">
|
||||
<button class="btn btn-default pull-right m-t-n-xl" ng-click="toggleAllPlans()" translate>{{ selectedPlans.length == 0 ? 'app.admin.calendar.select_all' : 'app.admin.calendar.select_none' }}</button>
|
||||
<div ng-repeat="group in plansClassifiedByGroup">
|
||||
<div class="text-center font-sbold">{{::group.name}}</div>
|
||||
<label class="checkbox m-l-md" ng-repeat="plan in group.plans">
|
||||
<input type="checkbox" ng-click="toggleSelectPlan(plan)" ng-model="selectedPlansBinding[plan.id]"> {{::plan.name}}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body m-h" ng-show="step === 4">
|
||||
<div class="m-t-sm">
|
||||
<p class="text-center font-sbold" translate>{{ 'app.admin.calendar.recurrence' }}</p>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<div class="form-group col-md-12">
|
||||
<label for="is_recurrent" translate>{{ 'app.admin.calendar.enabled' }}</label>
|
||||
<input bs-switch
|
||||
ng-model="availability.is_recurrent"
|
||||
@ -167,6 +196,10 @@
|
||||
<span class="underline" translate>{{ 'app.admin.calendar.labels' }}</span>
|
||||
<span ng-bind-html="tagsName"></span>
|
||||
</div>
|
||||
<div class="m-t" ng-show="isOnlySubscriptions">
|
||||
<span class="underline" translate>{{ 'app.admin.calendar.plans' }}</span>
|
||||
<span ng-bind-html="plansName"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -37,6 +37,8 @@
|
||||
plan="selectedPlan"
|
||||
plan-selection-time="planSelectionTime"
|
||||
settings="settings"
|
||||
plans="plans"
|
||||
groups="groups"
|
||||
on-slot-added-to-cart="markSlotAsAdded"
|
||||
on-slot-removed-from-cart="markSlotAsRemoved"
|
||||
on-slot-start-to-modify="markSlotAsModifying"
|
||||
|
@ -28,6 +28,15 @@
|
||||
switch-animate="true"
|
||||
switch-readonly="{{slot.isValid}}"/>
|
||||
</div>
|
||||
<div class="alert alert-warning m-t-sm m-b-none" ng-show="slot.plansGrouped.length">
|
||||
<div class="font-sbold text-u-c" translate>{{ 'app.shared.cart.slot_restrict_plans' }}</div>
|
||||
<div ng-repeat="group in slot.plansGrouped">
|
||||
<div class="font-sbold">{{::group.name}}</div>
|
||||
<ul class="m-n" ng-repeat="plan in group.plans">
|
||||
<li>{{::plan.name}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-valid btn-warning btn-block text-u-c r-b" ng-click="validateSlot(slot)" ng-if="!slot.isValid" translate>{{ 'app.shared.cart.confirm_this_slot' }}</button>
|
||||
|
@ -148,8 +148,8 @@ class API::AvailabilitiesController < API::ApiController
|
||||
def availability_params
|
||||
params.require(:availability).permit(:start_at, :end_at, :available_type, :machine_ids, :training_ids, :nb_total_places,
|
||||
:is_recurrent, :period, :nb_periods, :end_date,
|
||||
machine_ids: [], training_ids: [], space_ids: [], tag_ids: [],
|
||||
machines_attributes: %i[id _destroy])
|
||||
machine_ids: [], training_ids: [], space_ids: [], tag_ids: [], plan_ids: [],
|
||||
machines_attributes: %i[id _destroy], plans_attributes: %i[id _destroy])
|
||||
end
|
||||
|
||||
def lock_params
|
||||
|
@ -4,7 +4,6 @@
|
||||
# Eg. a 3D printer will be reservable on thursday from 9 to 11 pm
|
||||
# Availabilities may be subdivided into Slots (of 1h), for some types of reservables (eg. Machine)
|
||||
class Availability < ActiveRecord::Base
|
||||
|
||||
# elastic initialisations
|
||||
include Elasticsearch::Model
|
||||
index_name 'fablab'
|
||||
@ -29,6 +28,10 @@ class Availability < ActiveRecord::Base
|
||||
has_many :tags, through: :availability_tags
|
||||
accepts_nested_attributes_for :tags, allow_destroy: true
|
||||
|
||||
has_many :plans_availabilities, dependent: :destroy
|
||||
has_many :plans, through: :plans_availabilities
|
||||
accepts_nested_attributes_for :plans, allow_destroy: true
|
||||
|
||||
scope :machines, -> { where(available_type: 'machines') }
|
||||
scope :trainings, -> { includes(:trainings).where(available_type: 'training') }
|
||||
scope :spaces, -> { includes(:spaces).where(available_type: 'space') }
|
||||
@ -86,7 +89,7 @@ class Availability < ActiveRecord::Base
|
||||
def available_space_places
|
||||
return unless available_type == 'space'
|
||||
|
||||
((end_at - start_at)/ApplicationHelper::SLOT_DURATION.minutes).to_i * nb_total_places
|
||||
((end_at - start_at) / ApplicationHelper::SLOT_DURATION.minutes).to_i * nb_total_places
|
||||
end
|
||||
|
||||
def title(filter = {})
|
||||
@ -166,5 +169,4 @@ class Availability < ActiveRecord::Base
|
||||
|
||||
errors.add(:machine_ids, I18n.t('availabilities.must_be_associated_with_at_least_1_machine'))
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -25,7 +25,6 @@ class Plan < ActiveRecord::Base
|
||||
after_create :create_statistic_type
|
||||
after_create :set_name
|
||||
|
||||
|
||||
validates :amount, :group, :base_name, presence: true
|
||||
validates :interval_count, numericality: { only_integer: true, greater_than_or_equal_to: 1 }
|
||||
validates :interval_count, numericality: { less_than: 13 }, if: proc { |plan| plan.interval == 'month' }
|
||||
|
6
app/models/plans_availability.rb
Normal file
6
app/models/plans_availability.rb
Normal file
@ -0,0 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class PlansAvailability < ActiveRecord::Base
|
||||
belongs_to :plan
|
||||
belongs_to :availability
|
||||
end
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.array!(@slots) do |slot|
|
||||
json.id slot.id if slot.id
|
||||
json.can_modify slot.can_modify
|
||||
@ -14,13 +16,16 @@ json.array!(@slots) do |slot|
|
||||
json.name slot.machine.name
|
||||
end
|
||||
# the user who booked the slot ...
|
||||
json.user do
|
||||
json.id slot.reservation.user.id
|
||||
json.name slot.reservation.user.profile.full_name
|
||||
end if @current_user_role == 'admin' and slot.reservation # ... if the slot was reserved
|
||||
if (@current_user_role == 'admin') && slot.reservation
|
||||
json.user do
|
||||
json.id slot.reservation.user.id
|
||||
json.name slot.reservation.user.profile.full_name
|
||||
end
|
||||
end # ... if the slot was reserved
|
||||
json.tag_ids slot.availability.tag_ids
|
||||
json.tags slot.availability.tags do |t|
|
||||
json.id t.id
|
||||
json.name t.name
|
||||
end
|
||||
json.plan_ids slot.availability.plan_ids
|
||||
end
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.array!(@slots) do |slot|
|
||||
json.id slot.id if slot.id
|
||||
json.can_modify slot.can_modify
|
||||
@ -15,13 +17,16 @@ json.array!(@slots) do |slot|
|
||||
json.name slot.space.name
|
||||
end
|
||||
# the user who booked the slot ...
|
||||
json.user do
|
||||
json.id slot.reservation.user.id
|
||||
json.name slot.reservation.user.profile.full_name
|
||||
end if @current_user_role == 'admin' and slot.reservation # ... if the slot was reserved
|
||||
if (@current_user_role == 'admin') && slot.reservation
|
||||
json.user do
|
||||
json.id slot.reservation.user.id
|
||||
json.name slot.reservation.user.profile.full_name
|
||||
end
|
||||
end # ... if the slot was reserved
|
||||
json.tag_ids slot.availability.tag_ids
|
||||
json.tags slot.availability.tags do |t|
|
||||
json.id t.id
|
||||
json.name t.name
|
||||
end
|
||||
json.plan_ids slot.availability.plan_ids
|
||||
end
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.array!(@availabilities) do |a|
|
||||
json.id a.slot_id if a.slot_id
|
||||
if a.is_reserved
|
||||
@ -31,4 +33,5 @@ json.array!(@availabilities) do |a|
|
||||
json.id t.id
|
||||
json.name t.name
|
||||
end
|
||||
json.plan_ids slot.availability.plan_ids
|
||||
end
|
||||
|
@ -46,6 +46,9 @@ en:
|
||||
adjust_the_opening_hours: "Adjust the opening hours"
|
||||
to_time: "to" # context: time. eg. 'from 18:00 to 21:00'
|
||||
restrict_this_slot_with_labels_optional: "Restrict this slot with labels (optional)"
|
||||
restrict_this_slot_for_subcriptions_optional: "Restrict this slot for subscription users (optional)"
|
||||
select_some_plans: "Select some plans"
|
||||
plans: "Plan(s):"
|
||||
recurrence: "Recurrence"
|
||||
enabled: "Enabled"
|
||||
period: "Period"
|
||||
|
@ -44,6 +44,9 @@ es:
|
||||
adjust_the_opening_hours: "Ajustar el horario de apertura"
|
||||
to_time: a
|
||||
restrict_this_slot_with_labels_optional: "Restringir este horario con etiquetas (opcional)"
|
||||
restrict_this_slot_for_subcriptions_optional: "Restrict this slot for subscription users (optional)"
|
||||
select_some_plans: "Select some plans"
|
||||
plans: "Plan(s):"
|
||||
recurrence: "Recurrencia"
|
||||
enabled: "Activa"
|
||||
period: "Período"
|
||||
|
@ -46,6 +46,9 @@ fr:
|
||||
adjust_the_opening_hours: "Ajuster l'horaire"
|
||||
to_time: "à" # context: time. eg. 'from 18:00 to 21:00'
|
||||
restrict_this_slot_with_labels_optional: "Restreindre ce créneau avec des étiquettes (optionnel)"
|
||||
restrict_this_slot_for_subcriptions_optional: "Restreindre ce créneau pour les abonnements (optionnel)"
|
||||
select_some_plans: "Sélectionnez des formules d'abonnement"
|
||||
plans: "Abonnement(s):"
|
||||
recurrence: "Récurrence"
|
||||
enabled: "Activée"
|
||||
period: "Période"
|
||||
|
@ -46,6 +46,9 @@ pt:
|
||||
adjust_the_opening_hours: "Ajustar o horário de funcionamento"
|
||||
to_time: "ás" # context: time. eg. 'from 18:00 to 21:00'
|
||||
restrict_this_slot_with_labels_optional: "Restrinja este slot com etiquetas (opcional)"
|
||||
restrict_this_slot_for_subcriptions_optional: "Restrict this slot for subscription users (optional)"
|
||||
select_some_plans: "Select some plans"
|
||||
plans: "Plan(s):"
|
||||
recurrence: "Recurrence"
|
||||
enabled: "Enabled"
|
||||
period: "Period"
|
||||
|
@ -439,3 +439,5 @@ en:
|
||||
a_problem_occurred_during_the_payment_process_please_try_again_later: "A problem occurred during the payment process. Please try again later."
|
||||
none: "None"
|
||||
online_payment_disabled: "Online payment is not available. Please contact the Fablab reception directly."
|
||||
slot_restrict_plans: "This slot is restricted for the plans below:"
|
||||
slot_restrict_subcriptions_must_select_plan: "The slot is restricted for subscriptions, please select a plan first."
|
||||
|
@ -416,3 +416,5 @@ es:
|
||||
a_problem_occurred_during_the_payment_process_please_try_again_later: "A problem occurred during the payment process. Please try again later."
|
||||
none: "Ninguno"
|
||||
online_payment_disabled: "El pago en línea no está disponible. Póngase en contacto directamente con la recepción de Fablab."
|
||||
slot_restrict_plans: "This slot is restricted for the plans below:"
|
||||
slot_restrict_subcriptions_must_select_plan: "The slot is restricted for subscriptions, please select a plan first."
|
||||
|
@ -439,3 +439,5 @@ fr:
|
||||
a_problem_occurred_during_the_payment_process_please_try_again_later: "Il y a eu un problème lors de la procédure de paiement. Veuillez réessayer plus tard."
|
||||
none: "Aucune"
|
||||
online_payment_disabled: "Le payment par carte bancaire n'est pas disponible. Merci de contacter directement l'accueil du Fablab."
|
||||
slot_restrict_plans: "Ce créneau est restreint pour les formules d'abonnement ci-desous:"
|
||||
slot_restrict_subcriptions_must_select_plan: "Le créneau est restreint pour les abonnements, Veuillez tout d'abord sélectionner un formule d'abonnement"
|
||||
|
@ -439,3 +439,5 @@ pt:
|
||||
a_problem_occurred_during_the_payment_process_please_try_again_later: "Um problema ocorreu durante o processo de pagamento. Por favor tente novamente mais tarde."
|
||||
none: "Vazio"
|
||||
online_payment_disabled: "O pagamento online não está disponível. Entre em contato diretamente com a recepção do Fablab."
|
||||
slot_restrict_plans: "This slot is restricted for the plans below:"
|
||||
slot_restrict_subcriptions_must_select_plan: "The slot is restricted for subscriptions, please select a plan first."
|
||||
|
10
db/migrate/20200206132857_create_plans_availabilities.rb
Normal file
10
db/migrate/20200206132857_create_plans_availabilities.rb
Normal file
@ -0,0 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CreatePlansAvailabilities < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :plans_availabilities do |t|
|
||||
t.belongs_to :plan, index: true
|
||||
t.belongs_to :availability, index: true
|
||||
end
|
||||
end
|
||||
end
|
12
db/schema.rb
12
db/schema.rb
@ -11,12 +11,12 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20200127111404) do
|
||||
ActiveRecord::Schema.define(version: 20200206132857) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
enable_extension "pg_trgm"
|
||||
enable_extension "unaccent"
|
||||
enable_extension "pg_trgm"
|
||||
|
||||
create_table "abuses", force: :cascade do |t|
|
||||
t.integer "signaled_id"
|
||||
@ -465,6 +465,14 @@ ActiveRecord::Schema.define(version: 20200127111404) do
|
||||
|
||||
add_index "plans", ["group_id"], name: "index_plans_on_group_id", using: :btree
|
||||
|
||||
create_table "plans_availabilities", force: :cascade do |t|
|
||||
t.integer "plan_id"
|
||||
t.integer "availability_id"
|
||||
end
|
||||
|
||||
add_index "plans_availabilities", ["availability_id"], name: "index_plans_availabilities_on_availability_id", using: :btree
|
||||
add_index "plans_availabilities", ["plan_id"], name: "index_plans_availabilities_on_plan_id", using: :btree
|
||||
|
||||
create_table "price_categories", force: :cascade do |t|
|
||||
t.string "name"
|
||||
t.text "conditions"
|
||||
|
Loading…
x
Reference in New Issue
Block a user