mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-17 06:52:27 +01:00
Merge branch 'dev' into l10n_dev
This commit is contained in:
commit
ad52f825fa
15
CHANGELOG.md
15
CHANGELOG.md
@ -2,13 +2,22 @@
|
||||
|
||||
- Interface to manage partners
|
||||
- Ability to define, per availability, a custom duration for the reservation slots
|
||||
- Corrected the documentation about BOOK_SLOT_AT_SAME_TIME
|
||||
- Fix a bug: unable to change group if the previous was deactivated
|
||||
- Fix a bug: unable to create events or trainings that are not multiples of SLOT_DURATION
|
||||
- Fix a bug: unable to delete an unreserved event
|
||||
- Fix a bug: "Free entry" label for events without reservation
|
||||
- Fix a bug: updating a setting without any changes triggers an error
|
||||
- Fix a bug: plan edition does not show the associated group
|
||||
- Fix a bug: subscription page shows the groups without any active plans
|
||||
- Fix a bug: cart price inconsistently updated after a subscription
|
||||
|
||||
## v4.3.4 2020 April 14
|
||||
|
||||
- Improved version check
|
||||
- Improved setup script for installations without nginx
|
||||
- Changed some default values for new installations
|
||||
- Compatible database with Fab-manager v1, to allow upgrades
|
||||
- Database is now compatible with Fab-manager v1, to allow upgrades
|
||||
- Updated documentation
|
||||
- Changed In-Context pseudo-language to Zulu instead of Acholi
|
||||
- Allow removing contacts from the about page
|
||||
@ -28,7 +37,7 @@
|
||||
## v4.3.3 2020 April 1st
|
||||
|
||||
- Docker build will no longer embed development dependencies
|
||||
- Updated instructions to setup a development environment
|
||||
- Updated instructions to set up a development environment
|
||||
- Updated translations
|
||||
- Removed `MESSAGEFORMAT_LOCALE` as it is now handled by make-plural
|
||||
- Updated rails framework to v5.2
|
||||
@ -64,7 +73,7 @@
|
||||
## v4.3.1 2020 March 04
|
||||
|
||||
- Updated user's manual for v4.3 (fr)
|
||||
- Display user's manual when help is asked, if no tour is available
|
||||
- Display user's manual when asking for help, if no tour is available
|
||||
- Change style and pluralize the text of the slot division alert in new availability assistant
|
||||
- Fix a bug: in feature tours, next and previous arrows may be broken on some systems
|
||||
- Fix a bug: in the user's menu, two links to the personal wallet
|
||||
|
@ -64,8 +64,6 @@ angular.module('application', ['ngCookies', 'ngResource', 'ngSanitize', 'ui.rout
|
||||
$translateProvider.useMessageFormatInterpolation();
|
||||
// Set the language of the instance (from ruby configuration)
|
||||
$translateProvider.preferredLanguage(Fablab.locale);
|
||||
// In any cases, fallback to english
|
||||
$translateProvider.fallbackLanguage('en');
|
||||
// End the tour when the user clicks the forward or back buttons of the browser
|
||||
TourConfigProvider.enableNavigationInterceptors();
|
||||
}]).run(['$rootScope', '$log', 'AuthService', 'Auth', 'amMoment', '$state', 'editableOptions', 'Analytics',
|
||||
|
@ -18,8 +18,8 @@
|
||||
* Controller used in the calendar management page
|
||||
*/
|
||||
|
||||
Application.Controllers.controller('AdminCalendarController', ['$scope', '$state', '$uibModal', 'moment', 'Availability', 'Slot', 'Setting', 'Export', 'growl', 'dialogs', 'bookingWindowStart', 'bookingWindowEnd', 'machinesPromise', 'plansPromise', 'groupsPromise', '_t', 'uiCalendarConfig', 'CalendarConfig', 'Member', 'uiTourService',
|
||||
function ($scope, $state, $uibModal, moment, Availability, Slot, Setting, Export, growl, dialogs, bookingWindowStart, bookingWindowEnd, machinesPromise, plansPromise, groupsPromise, _t, uiCalendarConfig, CalendarConfig, Member, uiTourService) {
|
||||
Application.Controllers.controller('AdminCalendarController', ['$scope', '$state', '$uibModal', 'moment', 'AuthService', 'Availability', 'Slot', 'Setting', 'Export', 'growl', 'dialogs', 'bookingWindowStart', 'bookingWindowEnd', 'machinesPromise', 'plansPromise', 'groupsPromise', '_t', 'uiCalendarConfig', 'CalendarConfig', 'Member', 'uiTourService',
|
||||
function ($scope, $state, $uibModal, moment, AuthService, Availability, Slot, Setting, Export, growl, dialogs, bookingWindowStart, bookingWindowEnd, machinesPromise, plansPromise, groupsPromise, _t, uiCalendarConfig, CalendarConfig, Member, uiTourService) {
|
||||
/* PRIVATE STATIC CONSTANTS */
|
||||
|
||||
// The calendar is divided in slots of 30 minutes
|
||||
@ -313,14 +313,16 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
|
||||
placement: 'right',
|
||||
popupClass: 'width-350'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.admin-calendar .export-xls-button',
|
||||
stepId: 'export',
|
||||
order: 2,
|
||||
title: _t('app.admin.tour.calendar.export.title'),
|
||||
content: _t('app.admin.tour.calendar.export.content'),
|
||||
placement: 'left'
|
||||
});
|
||||
if (AuthService.isAuthorized('admin')) {
|
||||
uitour.createStep({
|
||||
selector: '.admin-calendar .export-xls-button',
|
||||
stepId: 'export',
|
||||
order: 2,
|
||||
title: _t('app.admin.tour.calendar.export.title'),
|
||||
content: _t('app.admin.tour.calendar.export.content'),
|
||||
placement: 'left'
|
||||
});
|
||||
}
|
||||
uitour.createStep({
|
||||
selector: '.heading .import-ics-button',
|
||||
stepId: 'import',
|
||||
|
@ -153,8 +153,8 @@ class EventsController {
|
||||
/**
|
||||
* Controller used in the events listing page (admin view)
|
||||
*/
|
||||
Application.Controllers.controller('AdminEventsController', ['$scope', '$state', 'dialogs', '$uibModal', 'growl', 'Event', 'Category', 'EventTheme', 'AgeRange', 'PriceCategory', 'eventsPromise', 'categoriesPromise', 'themesPromise', 'ageRangesPromise', 'priceCategoriesPromise', '_t', 'Member', 'uiTourService',
|
||||
function ($scope, $state, dialogs, $uibModal, growl, Event, Category, EventTheme, AgeRange, PriceCategory, eventsPromise, categoriesPromise, themesPromise, ageRangesPromise, priceCategoriesPromise, _t, Member, uiTourService) {
|
||||
Application.Controllers.controller('AdminEventsController', ['$scope', '$state', 'dialogs', '$uibModal', 'growl', 'AuthService', 'Event', 'Category', 'EventTheme', 'AgeRange', 'PriceCategory', 'eventsPromise', 'categoriesPromise', 'themesPromise', 'ageRangesPromise', 'priceCategoriesPromise', '_t', 'Member', 'uiTourService',
|
||||
function ($scope, $state, dialogs, $uibModal, growl, AuthService, Event, Category, EventTheme, AgeRange, PriceCategory, eventsPromise, categoriesPromise, themesPromise, ageRangesPromise, priceCategoriesPromise, _t, Member, uiTourService) {
|
||||
/* PUBLIC SCOPE */
|
||||
|
||||
// By default, the pagination mode is activated to limit the page size
|
||||
@ -407,38 +407,40 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
|
||||
content: _t('app.admin.tour.events.filter.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.events-management .events-categories',
|
||||
stepId: 'categories',
|
||||
order: 3,
|
||||
title: _t('app.admin.tour.events.categories.title'),
|
||||
content: _t('app.admin.tour.events.categories.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.events-management .events-themes',
|
||||
stepId: 'themes',
|
||||
order: 4,
|
||||
title: _t('app.admin.tour.events.themes.title'),
|
||||
content: _t('app.admin.tour.events.themes.content'),
|
||||
placement: 'top'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.events-management .events-age-ranges',
|
||||
stepId: 'ages',
|
||||
order: 5,
|
||||
title: _t('app.admin.tour.events.ages.title'),
|
||||
content: _t('app.admin.tour.events.ages.content'),
|
||||
placement: 'top'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.events-management .prices-tab',
|
||||
stepId: 'prices',
|
||||
order: 6,
|
||||
title: _t('app.admin.tour.events.prices.title'),
|
||||
content: _t('app.admin.tour.events.prices.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
if (AuthService.isAuthorized('admin')) {
|
||||
uitour.createStep({
|
||||
selector: '.events-management .events-categories',
|
||||
stepId: 'categories',
|
||||
order: 3,
|
||||
title: _t('app.admin.tour.events.categories.title'),
|
||||
content: _t('app.admin.tour.events.categories.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.events-management .events-themes',
|
||||
stepId: 'themes',
|
||||
order: 4,
|
||||
title: _t('app.admin.tour.events.themes.title'),
|
||||
content: _t('app.admin.tour.events.themes.content'),
|
||||
placement: 'top'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.events-management .events-age-ranges',
|
||||
stepId: 'ages',
|
||||
order: 5,
|
||||
title: _t('app.admin.tour.events.ages.title'),
|
||||
content: _t('app.admin.tour.events.ages.content'),
|
||||
placement: 'top'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.events-management .prices-tab',
|
||||
stepId: 'prices',
|
||||
order: 6,
|
||||
title: _t('app.admin.tour.events.prices.title'),
|
||||
content: _t('app.admin.tour.events.prices.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
}
|
||||
uitour.createStep({
|
||||
selector: 'body',
|
||||
stepId: 'conclusion',
|
||||
|
@ -17,8 +17,8 @@
|
||||
/**
|
||||
* Controller used in the admin invoices listing page
|
||||
*/
|
||||
Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'Invoice', 'AccountingPeriod', 'invoices', 'closedPeriods', '$uibModal', 'growl', '$filter', 'Setting', 'settings', '_t', 'Member', 'uiTourService',
|
||||
function ($scope, $state, Invoice, AccountingPeriod, invoices, closedPeriods, $uibModal, growl, $filter, Setting, settings, _t, Member, uiTourService) {
|
||||
Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'Invoice', 'AccountingPeriod', 'AuthService', 'invoices', 'closedPeriods', '$uibModal', 'growl', '$filter', 'Setting', 'settings', '_t', 'Member', 'uiTourService',
|
||||
function ($scope, $state, Invoice, AccountingPeriod, AuthService, invoices, closedPeriods, $uibModal, growl, $filter, Setting, settings, _t, Member, uiTourService) {
|
||||
/* PRIVATE STATIC CONSTANTS */
|
||||
|
||||
// number of invoices loaded each time we click on 'load more...'
|
||||
@ -291,8 +291,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
||||
growl.success(_t('app.admin.invoices.invoice_reference_successfully_saved'));
|
||||
}
|
||||
, function (error) {
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_invoice_reference'));
|
||||
console.error(error);
|
||||
if (error.status === 304) return;
|
||||
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_invoice_reference'));
|
||||
console.error(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -330,8 +332,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
||||
}
|
||||
}
|
||||
, function (error) {
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_invoicing_code'));
|
||||
return console.error(error);
|
||||
if (error.status === 304) return;
|
||||
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_invoicing_code'));
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
return Setting.update({ name: 'invoice_code-active' }, { value: result.active ? 'true' : 'false' }, function (data) {
|
||||
@ -343,8 +347,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
||||
}
|
||||
}
|
||||
, function (error) {
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_activating_the_invoicing_code'));
|
||||
return console.error(error);
|
||||
if (error.status === 304) return;
|
||||
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_activating_the_invoicing_code'));
|
||||
console.error(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -375,8 +381,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
||||
return growl.success(_t('app.admin.invoices.order_number_successfully_saved'));
|
||||
}
|
||||
, function (error) {
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_order_number'));
|
||||
return console.error(error);
|
||||
if (error.status === 304) return;
|
||||
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_order_number'));
|
||||
console.error(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -434,8 +442,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
||||
}
|
||||
}
|
||||
, function (error) {
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_VAT_rate'));
|
||||
return console.error(error);
|
||||
if (error.status === 304) return;
|
||||
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_VAT_rate'));
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
return Setting.update({ name: 'invoice_VAT-active' }, { value: result.active ? 'true' : 'false' }, function (data) {
|
||||
@ -447,8 +457,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
||||
}
|
||||
}
|
||||
, function (error) {
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_activating_the_VAT'));
|
||||
return console.error(error);
|
||||
if (error.status === 304) return;
|
||||
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_activating_the_VAT'));
|
||||
console.error(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -463,8 +475,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
||||
return growl.success(_t('app.admin.invoices.text_successfully_saved'));
|
||||
}
|
||||
, function (error) {
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_text'));
|
||||
return console.error(error);
|
||||
if (error.status === 304) return;
|
||||
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_text'));
|
||||
console.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
@ -478,8 +492,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
||||
return growl.success(_t('app.admin.invoices.address_and_legal_information_successfully_saved'));
|
||||
}
|
||||
, function (error) {
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_address_and_the_legal_information'));
|
||||
return console.error(error);
|
||||
if (error.status === 304) return;
|
||||
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_address_and_the_legal_information'));
|
||||
console.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
@ -566,15 +582,27 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
||||
$scope.setupInvoicesTour = function () {
|
||||
// get the tour defined by the ui-tour directive
|
||||
const uitour = uiTourService.getTourByName('invoices');
|
||||
uitour.createStep({
|
||||
selector: 'body',
|
||||
stepId: 'welcome',
|
||||
order: 0,
|
||||
title: _t('app.admin.tour.invoices.welcome.title'),
|
||||
content: _t('app.admin.tour.invoices.welcome.content'),
|
||||
placement: 'bottom',
|
||||
orphan: true
|
||||
});
|
||||
if (AuthService.isAuthorized('admin')) {
|
||||
uitour.createStep({
|
||||
selector: 'body',
|
||||
stepId: 'welcome',
|
||||
order: 0,
|
||||
title: _t('app.admin.tour.invoices.welcome.title'),
|
||||
content: _t('app.admin.tour.invoices.welcome.content'),
|
||||
placement: 'bottom',
|
||||
orphan: true
|
||||
});
|
||||
} else {
|
||||
uitour.createStep({
|
||||
selector: 'body',
|
||||
stepId: 'welcome_manager',
|
||||
order: 0,
|
||||
title: _t('app.admin.tour.invoices.welcome_manager.title'),
|
||||
content: _t('app.admin.tour.invoices.welcome_manager.content'),
|
||||
placement: 'bottom',
|
||||
orphan: true
|
||||
});
|
||||
}
|
||||
if (!Fablab.withoutInvoices && $scope.invoices.length > 0) {
|
||||
uitour.createStep({
|
||||
selector: '.invoices-management .invoices-list',
|
||||
@ -609,39 +637,41 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
||||
placement: 'left'
|
||||
});
|
||||
}
|
||||
uitour.createStep({
|
||||
selector: '.invoices-management .invoices-settings',
|
||||
stepId: 'settings',
|
||||
order: 5,
|
||||
title: _t('app.admin.tour.invoices.settings.title'),
|
||||
content: _t('app.admin.tour.invoices.settings.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.invoices-management .accounting-codes-tab',
|
||||
stepId: 'codes',
|
||||
order: 6,
|
||||
title: _t('app.admin.tour.invoices.codes.title'),
|
||||
content: _t('app.admin.tour.invoices.codes.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.heading .export-accounting-button',
|
||||
stepId: 'export',
|
||||
order: 7,
|
||||
title: _t('app.admin.tour.invoices.export.title'),
|
||||
content: _t('app.admin.tour.invoices.export.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.heading .close-accounting-periods-button',
|
||||
stepId: 'periods',
|
||||
order: 8,
|
||||
title: _t('app.admin.tour.invoices.periods.title'),
|
||||
content: _t('app.admin.tour.invoices.periods.content'),
|
||||
placement: 'bottom',
|
||||
popupClass: 'shift-left-50'
|
||||
});
|
||||
if (AuthService.isAuthorized('admin')) {
|
||||
uitour.createStep({
|
||||
selector: '.invoices-management .invoices-settings',
|
||||
stepId: 'settings',
|
||||
order: 5,
|
||||
title: _t('app.admin.tour.invoices.settings.title'),
|
||||
content: _t('app.admin.tour.invoices.settings.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.invoices-management .accounting-codes-tab',
|
||||
stepId: 'codes',
|
||||
order: 6,
|
||||
title: _t('app.admin.tour.invoices.codes.title'),
|
||||
content: _t('app.admin.tour.invoices.codes.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.heading .export-accounting-button',
|
||||
stepId: 'export',
|
||||
order: 7,
|
||||
title: _t('app.admin.tour.invoices.export.title'),
|
||||
content: _t('app.admin.tour.invoices.export.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.heading .close-accounting-periods-button',
|
||||
stepId: 'periods',
|
||||
order: 8,
|
||||
title: _t('app.admin.tour.invoices.periods.title'),
|
||||
content: _t('app.admin.tour.invoices.periods.content'),
|
||||
placement: 'bottom',
|
||||
popupClass: 'shift-left-50'
|
||||
});
|
||||
}
|
||||
uitour.createStep({
|
||||
selector: 'body',
|
||||
stepId: 'conclusion',
|
||||
@ -710,8 +740,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
||||
{ value: $scope.invoice.logo.base64 },
|
||||
function (data) { growl.success(_t('app.admin.invoices.logo_successfully_saved')); },
|
||||
function (error) {
|
||||
if (error.status === 304) return;
|
||||
|
||||
growl.error(_t('app.admin.invoices.an_error_occurred_while_saving_the_logo'));
|
||||
return console.error(error);
|
||||
console.error(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -126,8 +126,8 @@ class MembersController {
|
||||
/**
|
||||
* Controller used in the members/groups management page
|
||||
*/
|
||||
Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', '$uibModal', 'membersPromise', 'adminsPromise', 'partnersPromise', 'managersPromise', 'growl', 'Admin', 'dialogs', '_t', 'Member', 'Export', 'User', 'uiTourService',
|
||||
function ($scope, $sce, $uibModal, membersPromise, adminsPromise, partnersPromise, managersPromise, growl, Admin, dialogs, _t, Member, Export, User, uiTourService) {
|
||||
Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', '$uibModal', 'membersPromise', 'adminsPromise', 'partnersPromise', 'managersPromise', 'growl', 'Admin', 'AuthService', 'dialogs', '_t', 'Member', 'Export', 'User', 'uiTourService',
|
||||
function ($scope, $sce, $uibModal, membersPromise, adminsPromise, partnersPromise, managersPromise, growl, Admin, AuthService, dialogs, _t, Member, Export, User, uiTourService) {
|
||||
/* PRIVATE STATIC CONSTANTS */
|
||||
|
||||
// number of users loaded each time we click on 'load more...'
|
||||
@ -217,6 +217,18 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the managers ordering criterion to the one provided
|
||||
* @param orderManager {string} ordering criterion
|
||||
*/
|
||||
$scope.setOrderManager = function (orderManager) {
|
||||
if ($scope.orderManager === orderManager) {
|
||||
return $scope.orderManager = `-${orderManager}`;
|
||||
} else {
|
||||
return $scope.orderManager = orderManager;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Open a modal dialog allowing the admin to create a new partner user
|
||||
@ -343,6 +355,36 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask for confirmation then delete the specified manager
|
||||
* @param managers {Array} full list of managers
|
||||
* @param manager {Object} manager to delete
|
||||
*/
|
||||
$scope.destroyManager = function (managers, manager) {
|
||||
dialogs.confirm(
|
||||
{
|
||||
resolve: {
|
||||
object () {
|
||||
return {
|
||||
title: _t('app.admin.members.confirmation_required'),
|
||||
msg: $sce.trustAsHtml(_t('app.admin.members.delete_this_manager') + '<br/><br/>' + _t('app.admin.members.this_may_take_a_while_please_wait'))
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
function () { // cancel confirmed
|
||||
User.delete(
|
||||
{ id: manager.id },
|
||||
function () {
|
||||
managers.splice(findItemIdxById(managers, manager.id), 1);
|
||||
return growl.success(_t('app.admin.members.manager_successfully_deleted'));
|
||||
},
|
||||
function (error) { growl.error(_t('app.admin.members.unable_to_delete_the_manager')); }
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the 'load more' button.
|
||||
* Will load the next results of the current search, if any
|
||||
@ -434,22 +476,24 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
placement: 'left'
|
||||
});
|
||||
}
|
||||
uitour.createStep({
|
||||
selector: '.members-management .exports-buttons',
|
||||
stepId: 'exports',
|
||||
order: 5,
|
||||
title: _t('app.admin.tour.members.exports.title'),
|
||||
content: _t('app.admin.tour.members.exports.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.heading .import-members',
|
||||
stepId: 'import',
|
||||
order: 6,
|
||||
title: _t('app.admin.tour.members.import.title'),
|
||||
content: _t('app.admin.tour.members.import.content'),
|
||||
placement: 'left'
|
||||
});
|
||||
if (AuthService.isAuthorized('admin')) {
|
||||
uitour.createStep({
|
||||
selector: '.members-management .exports-buttons',
|
||||
stepId: 'exports',
|
||||
order: 5,
|
||||
title: _t('app.admin.tour.members.exports.title'),
|
||||
content: _t('app.admin.tour.members.exports.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.heading .import-members',
|
||||
stepId: 'import',
|
||||
order: 6,
|
||||
title: _t('app.admin.tour.members.import.title'),
|
||||
content: _t('app.admin.tour.members.import.content'),
|
||||
placement: 'left'
|
||||
});
|
||||
}
|
||||
uitour.createStep({
|
||||
selector: '.members-management .admins-tab',
|
||||
stepId: 'admins',
|
||||
@ -458,31 +502,33 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
|
||||
content: _t('app.admin.tour.members.admins.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.members-management .groups-tab',
|
||||
stepId: 'groups',
|
||||
order: 8,
|
||||
title: _t('app.admin.tour.members.groups.title'),
|
||||
content: _t('app.admin.tour.members.groups.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.members-management .labels-tab',
|
||||
stepId: 'labels',
|
||||
order: 9,
|
||||
title: _t('app.admin.tour.members.labels.title'),
|
||||
content: _t('app.admin.tour.members.labels.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.members-management .sso-tab',
|
||||
stepId: 'sso',
|
||||
order: 10,
|
||||
title: _t('app.admin.tour.members.sso.title'),
|
||||
content: _t('app.admin.tour.members.sso.content'),
|
||||
placement: 'bottom',
|
||||
popupClass: 'shift-left-50'
|
||||
});
|
||||
if (AuthService.isAuthorized('admin')) {
|
||||
uitour.createStep({
|
||||
selector: '.members-management .groups-tab',
|
||||
stepId: 'groups',
|
||||
order: 8,
|
||||
title: _t('app.admin.tour.members.groups.title'),
|
||||
content: _t('app.admin.tour.members.groups.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.members-management .labels-tab',
|
||||
stepId: 'labels',
|
||||
order: 9,
|
||||
title: _t('app.admin.tour.members.labels.title'),
|
||||
content: _t('app.admin.tour.members.labels.content'),
|
||||
placement: 'bottom'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.members-management .sso-tab',
|
||||
stepId: 'sso',
|
||||
order: 10,
|
||||
title: _t('app.admin.tour.members.sso.title'),
|
||||
content: _t('app.admin.tour.members.sso.content'),
|
||||
placement: 'bottom',
|
||||
popupClass: 'shift-left-50'
|
||||
});
|
||||
}
|
||||
uitour.createStep({
|
||||
selector: 'body',
|
||||
stepId: 'conclusion',
|
||||
@ -1029,3 +1075,72 @@ Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'A
|
||||
}
|
||||
|
||||
]);
|
||||
|
||||
/**
|
||||
* Controller used in the manager's creation page (admin view)
|
||||
*/
|
||||
Application.Controllers.controller('NewManagerController', ['$state', '$scope', 'User', 'groupsPromise', 'tagsPromise', 'growl', '_t',
|
||||
function ($state, $scope, User, groupsPromise, tagsPromise, growl, _t) {
|
||||
// default admin profile
|
||||
$scope.manager = {
|
||||
statistic_profile_attributes: {
|
||||
gender: true
|
||||
},
|
||||
profile_attributes: {},
|
||||
invoicing_profile_attributes: {}
|
||||
};
|
||||
|
||||
// Default parameters for AngularUI-Bootstrap datepicker
|
||||
$scope.datePicker = {
|
||||
format: Fablab.uibDateFormat,
|
||||
opened: false,
|
||||
options: {
|
||||
startingDay: Fablab.weekStartingDay
|
||||
}
|
||||
};
|
||||
|
||||
// list of all groups
|
||||
$scope.groups = groupsPromise.filter(function (g) { return (g.slug !== 'admins') && !g.disabled; });
|
||||
|
||||
// list of all tags
|
||||
$scope.tags = tagsPromise;
|
||||
|
||||
/**
|
||||
* Shows the birth day datepicker
|
||||
* @param $event {Object} jQuery event object
|
||||
*/
|
||||
$scope.openDatePicker = function ($event) { $scope.datePicker.opened = true; };
|
||||
|
||||
/**
|
||||
* Send the new manager, currently stored in $scope.manager, to the server for database saving
|
||||
*/
|
||||
$scope.saveManager = function () {
|
||||
User.save(
|
||||
{},
|
||||
{ manager: $scope.manager },
|
||||
function () {
|
||||
growl.success(_t('app.admin.manager_new.manager_successfully_created', { GENDER: getGender($scope.manager) }));
|
||||
return $state.go('app.admin.members');
|
||||
}
|
||||
, function (error) {
|
||||
growl.error(_t('app.admin.admins_new.failed_to_create_manager') + JSON.stringify(error.data ? error.data : error));
|
||||
console.error(error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/* PRIVATE SCOPE */
|
||||
|
||||
/**
|
||||
* Return an enumerable meaningful string for the gender of the provider user
|
||||
* @param user {Object} Database user record
|
||||
* @return {string} 'male' or 'female'
|
||||
*/
|
||||
const getGender = function (user) {
|
||||
if (user.statistic_profile_attributes) {
|
||||
if (user.statistic_profile_attributes.gender) { return 'male'; } else { return 'female'; }
|
||||
} else { return 'other'; }
|
||||
};
|
||||
}
|
||||
|
||||
]);
|
||||
|
@ -209,7 +209,12 @@ Application.Controllers.controller('SettingsController', ['$scope', '$rootScope'
|
||||
{ name: setting.name },
|
||||
{ value },
|
||||
function () { growl.success(_t('app.admin.settings.customization_of_SETTING_successfully_saved', { SETTING: _t(`app.admin.settings.${setting.name}`) })); },
|
||||
function (error) { console.log(error); }
|
||||
function (error) {
|
||||
if (error.status === 304) return;
|
||||
|
||||
growl.error(_t('app.admin.settings.an_error_occurred_saving_the_setting'));
|
||||
console.log(error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -126,8 +126,8 @@ Application.Controllers.controller('EventsController', ['$scope', '$state', 'Eve
|
||||
}
|
||||
]);
|
||||
|
||||
Application.Controllers.controller('ShowEventController', ['$scope', '$state', '$stateParams', '$rootScope', 'Event', '$uibModal', 'Member', 'Reservation', 'Price', 'CustomAsset', 'Slot', 'eventPromise', 'growl', '_t', 'Wallet', 'helpers', 'dialogs', 'priceCategoriesPromise', 'settingsPromise',
|
||||
function ($scope, $state, $stateParams, $rootScope, Event, $uibModal, Member, Reservation, Price, CustomAsset, Slot, eventPromise, growl, _t, Wallet, helpers, dialogs, priceCategoriesPromise, settingsPromise) {
|
||||
Application.Controllers.controller('ShowEventController', ['$scope', '$state', '$stateParams', '$rootScope', 'Event', '$uibModal', 'Member', 'Reservation', 'Price', 'CustomAsset', 'Slot', 'eventPromise', 'growl', '_t', 'Wallet', 'AuthService', 'helpers', 'dialogs', 'priceCategoriesPromise', 'settingsPromise',
|
||||
function ($scope, $state, $stateParams, $rootScope, Event, $uibModal, Member, Reservation, Price, CustomAsset, Slot, eventPromise, growl, _t, Wallet, AuthService, helpers, dialogs, priceCategoriesPromise, settingsPromise) {
|
||||
/* PUBLIC SCOPE */
|
||||
|
||||
// reservations for the currently shown event
|
||||
@ -245,32 +245,32 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
if ($scope.event.nb_total_places > 0) {
|
||||
$scope.reserveSuccess = false;
|
||||
if (!$scope.isAuthenticated()) {
|
||||
return $scope.login(null, function (user) {
|
||||
if (user.role !== 'admin') {
|
||||
return $scope.ctrl.member = user;
|
||||
$scope.login(null, function (user) {
|
||||
if (user.role !== 'admin' || user.role !== 'manager') {
|
||||
$scope.ctrl.member = user;
|
||||
}
|
||||
const sameTimeReservations = findReservationsAtSameTime();
|
||||
if (sameTimeReservations.length > 0) {
|
||||
showReserveSlotSameTimeModal(sameTimeReservations, function(res) {
|
||||
return $scope.reserve.toReserve = !$scope.reserve.toReserve;
|
||||
$scope.reserve.toReserve = !$scope.reserve.toReserve;
|
||||
});
|
||||
} else {
|
||||
return $scope.reserve.toReserve = !$scope.reserve.toReserve;
|
||||
$scope.reserve.toReserve = !$scope.reserve.toReserve;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if ($scope.currentUser.role === 'admin') {
|
||||
return $scope.reserve.toReserve = !$scope.reserve.toReserve;
|
||||
if (AuthService.isAuthorized(['admin', 'manager'])) {
|
||||
$scope.reserve.toReserve = !$scope.reserve.toReserve;
|
||||
} else {
|
||||
Member.get({ id: $scope.currentUser.id }, function (member) {
|
||||
$scope.ctrl.member = member;
|
||||
const sameTimeReservations = findReservationsAtSameTime();
|
||||
if (sameTimeReservations.length > 0) {
|
||||
showReserveSlotSameTimeModal(sameTimeReservations, function(res) {
|
||||
return $scope.reserve.toReserve = !$scope.reserve.toReserve;
|
||||
$scope.reserve.toReserve = !$scope.reserve.toReserve;
|
||||
});
|
||||
} else {
|
||||
return $scope.reserve.toReserve = !$scope.reserve.toReserve;
|
||||
$scope.reserve.toReserve = !$scope.reserve.toReserve;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -286,9 +286,9 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
resetEventReserve();
|
||||
$scope.reserveSuccess = false;
|
||||
if ($scope.ctrl.member) {
|
||||
return Member.get({ id: $scope.ctrl.member.id }, function (member) {
|
||||
Member.get({ id: $scope.ctrl.member.id }, function (member) {
|
||||
$scope.ctrl.member = member;
|
||||
return getReservations($scope.event.id, 'Event', $scope.ctrl.member.id);
|
||||
getReservations($scope.event.id, 'Event', $scope.ctrl.member.id);
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -303,14 +303,17 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
|
||||
return Wallet.getWalletByUser({ user_id: $scope.ctrl.member.id }, function (wallet) {
|
||||
const amountToPay = helpers.getAmountToPay($scope.reserve.amountTotal, wallet.amount);
|
||||
if (($scope.currentUser.role !== 'admin') && (amountToPay > 0)) {
|
||||
if ((AuthService.isAuthorized(['member']) && amountToPay > 0)
|
||||
|| (AuthService.isAuthorized('manager') && $scope.ctrl.member.id === $rootScope.currentUser.id && amountToPay > 0)) {
|
||||
if ($rootScope.fablabWithoutOnlinePayment) {
|
||||
growl.error(_t('app.public.events_show.online_payment_disabled'));
|
||||
} else {
|
||||
return payByStripe(reservation);
|
||||
}
|
||||
} else {
|
||||
if (($scope.currentUser.role === 'admin') || (amountToPay === 0)) {
|
||||
if (AuthService.isAuthorized('admin')
|
||||
|| (AuthService.isAuthorized('manager') && $scope.ctrl.member.id !== $rootScope.currentUser.id)
|
||||
|| amountToPay === 0) {
|
||||
return payOnSite(reservation);
|
||||
}
|
||||
}
|
||||
@ -564,7 +567,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
}
|
||||
|
||||
// watch when a coupon is applied to re-compute the total price
|
||||
return $scope.$watch('coupon.applied', function (newValue, oldValue) {
|
||||
$scope.$watch('coupon.applied', function (newValue, oldValue) {
|
||||
if ((newValue !== null) || (oldValue !== null)) {
|
||||
return $scope.computeEventAmount();
|
||||
}
|
||||
@ -577,7 +580,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
* @param reservable_type {string} 'Event'
|
||||
* @param user_id {number} the user's id (current or managed)
|
||||
*/
|
||||
var getReservations = function (reservable_id, reservable_type, user_id) {
|
||||
const getReservations = function (reservable_id, reservable_type, user_id) {
|
||||
Reservation.query({
|
||||
reservable_id,
|
||||
reservable_type,
|
||||
@ -592,7 +595,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
* @param event {Object} Current event
|
||||
* @return {{user_id:number, reservable_id:number, reservable_type:string, slots_attributes:Array<Object>, nb_reserve_places:number}}
|
||||
*/
|
||||
var mkReservation = function (member, reserve, event) {
|
||||
const mkReservation = function (member, reserve, event) {
|
||||
const reservation = {
|
||||
user_id: member.id,
|
||||
reservable_id: event.id,
|
||||
@ -628,7 +631,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
* @param coupon {Object} Coupon as returned from the API
|
||||
* @return {{reservation:Object, coupon_code:string}}
|
||||
*/
|
||||
var mkRequestParams = function (reservation, coupon) {
|
||||
const mkRequestParams = function (reservation, coupon) {
|
||||
const params = {
|
||||
reservation,
|
||||
coupon_code: ((coupon ? coupon.code : undefined))
|
||||
@ -640,7 +643,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
/**
|
||||
* Set the current reservation to the default values. This implies the reservation form to be hidden.
|
||||
*/
|
||||
var resetEventReserve = function () {
|
||||
const resetEventReserve = function () {
|
||||
if ($scope.event) {
|
||||
$scope.reserve = {
|
||||
nbPlaces: {
|
||||
@ -666,7 +669,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
* Open a modal window which trigger the stripe payment process
|
||||
* @param reservation {Object} to book
|
||||
*/
|
||||
var payByStripe = function (reservation) {
|
||||
const payByStripe = function (reservation) {
|
||||
$uibModal.open({
|
||||
templateUrl: '<%= asset_path "stripe/payment_modal.html" %>',
|
||||
size: 'md',
|
||||
@ -730,7 +733,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
* Open a modal window which trigger the local payment process
|
||||
* @param reservation {Object} to book
|
||||
*/
|
||||
var payOnSite = function (reservation) {
|
||||
const payOnSite = function (reservation) {
|
||||
$uibModal.open({
|
||||
templateUrl: '<%= asset_path "shared/valid_reservation_modal.html" %>',
|
||||
size: 'sm',
|
||||
@ -808,7 +811,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
* What to do after the payment was successful
|
||||
* @param reservation {Object} booked reservation
|
||||
*/
|
||||
var afterPayment = function (reservation) {
|
||||
const afterPayment = function (reservation) {
|
||||
$scope.event.nb_free_places = $scope.event.nb_free_places - reservation.total_booked_seats;
|
||||
resetEventReserve();
|
||||
$scope.reserveSuccess = true;
|
||||
@ -822,7 +825,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
/**
|
||||
* Find user's reservations, the same date at the same time, with event
|
||||
*/
|
||||
var findReservationsAtSameTime = function () {
|
||||
const findReservationsAtSameTime = function () {
|
||||
let sameTimeReservations = [
|
||||
'training_reservations',
|
||||
'machine_reservations',
|
||||
@ -848,7 +851,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
* @param sameTimeReservations {Array} reservations the same date at the same time
|
||||
* @param callback {function} callback will invoke when user confirm
|
||||
*/
|
||||
var showReserveSlotSameTimeModal = function(sameTimeReservations, callback) {
|
||||
const showReserveSlotSameTimeModal = function(sameTimeReservations, callback) {
|
||||
const modalInstance = $uibModal.open({
|
||||
animation: true,
|
||||
templateUrl: '<%= asset_path "shared/_reserve_slot_same_time.html" %>',
|
||||
|
@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
Application.Controllers.controller('HomeController', ['$scope', '$stateParams', 'settingsPromise', 'Member', 'uiTourService', '_t', 'Help',
|
||||
function ($scope, $stateParams, settingsPromise, Member, uiTourService, _t, Help) {
|
||||
Application.Controllers.controller('HomeController', ['$scope', '$stateParams', '$translatePartialLoader', 'AuthService', 'settingsPromise', 'Member', 'uiTourService', '_t', 'Help',
|
||||
function ($scope, $stateParams, $translatePartialLoader, AuthService, settingsPromise, Member, uiTourService, _t, Help) {
|
||||
/* PUBLIC SCOPE */
|
||||
|
||||
// Home page HTML content
|
||||
@ -21,8 +21,12 @@ Application.Controllers.controller('HomeController', ['$scope', '$stateParams',
|
||||
* This is intended as a contextual help (when pressing F1)
|
||||
*/
|
||||
$scope.setupHomeTour = function () {
|
||||
if ($scope.currentUser && $scope.currentUser.role === 'admin') {
|
||||
setupWelcomeTour();
|
||||
if (AuthService.isAuthorized(['admin', 'manager'])) {
|
||||
// Workaround for the following bug: sometimes, when the feature tour is shown, the translations keys are not
|
||||
// interpreted. This is an ugly hack, but we can't do better for now because angular-ui-tour does not support
|
||||
// removing steps (this would allow us to recreate the steps when the translations are loaded), and we can't use
|
||||
// promises with _t's translations (this would be a very big refactoring)
|
||||
setTimeout(setupWelcomeTour, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
@ -182,7 +186,7 @@ Application.Controllers.controller('HomeController', ['$scope', '$stateParams',
|
||||
selector: '.nav-primary .admin-section',
|
||||
stepId: 'admin',
|
||||
order: 9,
|
||||
title: _t('app.public.tour.welcome.admin.title'),
|
||||
title: _t('app.public.tour.welcome.admin.title', { ROLE: _t(`app.public.common.${$scope.currentUser.role}`) }),
|
||||
content: _t('app.public.tour.welcome.admin.content'),
|
||||
placement: 'right'
|
||||
});
|
||||
@ -271,14 +275,16 @@ Application.Controllers.controller('HomeController', ['$scope', '$stateParams',
|
||||
placement: 'bottom',
|
||||
orphan: 'true'
|
||||
});
|
||||
uitour.createStep({
|
||||
selector: '.app-generator .app-version',
|
||||
stepId: 'version',
|
||||
order: 19,
|
||||
title: _t('app.public.tour.welcome.version.title'),
|
||||
content: _t('app.public.tour.welcome.version.content'),
|
||||
placement: 'top'
|
||||
});
|
||||
if (AuthService.isAuthorized('admin')) {
|
||||
uitour.createStep({
|
||||
selector: '.app-generator .app-version',
|
||||
stepId: 'version',
|
||||
order: 19,
|
||||
title: _t('app.public.tour.welcome.version.title'),
|
||||
content: _t('app.public.tour.welcome.version.content'),
|
||||
placement: 'top'
|
||||
});
|
||||
}
|
||||
uitour.createStep({
|
||||
selector: 'body',
|
||||
stepId: 'conclusion',
|
||||
|
@ -180,8 +180,8 @@ const _reserveMachine = function (machine, e) {
|
||||
/**
|
||||
* Controller used in the public listing page, allowing everyone to see the list of machines
|
||||
*/
|
||||
Application.Controllers.controller('MachinesController', ['$scope', '$state', '_t', 'Machine', '$uibModal', 'machinesPromise', 'Member', 'uiTourService',
|
||||
function ($scope, $state, _t, Machine, $uibModal, machinesPromise, Member, uiTourService) {
|
||||
Application.Controllers.controller('MachinesController', ['$scope', '$state', '_t', 'AuthService', 'Machine', '$uibModal', 'machinesPromise', 'Member', 'uiTourService',
|
||||
function ($scope, $state, _t, AuthService, Machine, $uibModal, machinesPromise, Member, uiTourService) {
|
||||
/* PUBLIC SCOPE */
|
||||
|
||||
// Retrieve the list of machines
|
||||
@ -219,32 +219,54 @@ Application.Controllers.controller('MachinesController', ['$scope', '$state', '_
|
||||
*/
|
||||
$scope.setupMachinesTour = function () {
|
||||
// setup the tour for admins only
|
||||
if ($scope.currentUser && $scope.currentUser.role === 'admin') {
|
||||
if (AuthService.isAuthorized(['admin', 'manager'])) {
|
||||
// get the tour defined by the ui-tour directive
|
||||
const uitour = uiTourService.getTourByName('machines');
|
||||
uitour.createStep({
|
||||
selector: 'body',
|
||||
stepId: 'welcome',
|
||||
order: 0,
|
||||
title: _t('app.public.tour.machines.welcome.title'),
|
||||
content: _t('app.public.tour.machines.welcome.content'),
|
||||
placement: 'bottom',
|
||||
orphan: true
|
||||
});
|
||||
if (AuthService.isAuthorized('admin')) {
|
||||
uitour.createStep({
|
||||
selector: 'body',
|
||||
stepId: 'welcome',
|
||||
order: 0,
|
||||
title: _t('app.public.tour.machines.welcome.title'),
|
||||
content: _t('app.public.tour.machines.welcome.content'),
|
||||
placement: 'bottom',
|
||||
orphan: true
|
||||
});
|
||||
if ($scope.machines.length > 0) {
|
||||
uitour.createStep({
|
||||
selector: '.machines-list .show-button',
|
||||
stepId: 'view',
|
||||
order: 1,
|
||||
title: _t('app.public.tour.machines.view.title'),
|
||||
content: _t('app.public.tour.machines.view.content'),
|
||||
placement: 'top'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
uitour.createStep({
|
||||
selector: 'body',
|
||||
stepId: 'welcome_manager',
|
||||
order: 0,
|
||||
title: _t('app.public.tour.machines.welcome_manager.title'),
|
||||
content: _t('app.public.tour.machines.welcome_manager.content'),
|
||||
placement: 'bottom',
|
||||
orphan: true
|
||||
});
|
||||
}
|
||||
if ($scope.machines.length > 0) {
|
||||
uitour.createStep({
|
||||
selector: '.machines-list .show-button',
|
||||
stepId: 'view',
|
||||
order: 1,
|
||||
title: _t('app.public.tour.machines.view.title'),
|
||||
content: _t('app.public.tour.machines.view.content'),
|
||||
selector: '.machines-list .reserve-button',
|
||||
stepId: 'reserve',
|
||||
order: 2,
|
||||
title: _t('app.public.tour.machines.reserve.title'),
|
||||
content: _t('app.public.tour.machines.reserve.content'),
|
||||
placement: 'top'
|
||||
});
|
||||
}
|
||||
uitour.createStep({
|
||||
selector: 'body',
|
||||
stepId: 'conclusion',
|
||||
order: 2,
|
||||
order: 3,
|
||||
title: _t('app.public.tour.conclusion.title'),
|
||||
content: _t('app.public.tour.conclusion.content'),
|
||||
placement: 'bottom',
|
||||
@ -524,12 +546,13 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
|
||||
* When modifying an already booked reservation, callback when the modification was successfully done.
|
||||
*/
|
||||
$scope.modifyMachineSlot = function () {
|
||||
$scope.events.placable.title = $scope.currentUser.role !== 'admin' ? _t('app.logged.machines_reserve.i_ve_reserved') : _t('app.logged.machines_reserve.not_available');
|
||||
$scope.events.placable.title = $scope.currentUser.id === $scope.events.modifiable.user.id ? _t('app.logged.machines_reserve.i_ve_reserved') : _t('app.logged.machines_reserve.not_available');
|
||||
$scope.events.placable.backgroundColor = 'white';
|
||||
$scope.events.placable.borderColor = $scope.events.modifiable.borderColor;
|
||||
$scope.events.placable.id = $scope.events.modifiable.id;
|
||||
$scope.events.placable.is_reserved = true;
|
||||
$scope.events.placable.can_modify = true;
|
||||
$scope.events.placable.user = angular.copy($scope.events.modifiable.user);
|
||||
|
||||
$scope.events.modifiable.backgroundColor = 'white';
|
||||
$scope.events.modifiable.title = '';
|
||||
@ -549,7 +572,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
|
||||
$scope.events.placable.backgroundColor = 'white';
|
||||
$scope.events.placable.title = '';
|
||||
}
|
||||
$scope.events.modifiable.title = $scope.currentUser.role !== 'admin' ? _t('app.logged.machines_reserve.i_ve_reserved') : _t('app.logged.machines_reserve.not_available');
|
||||
$scope.events.modifiable.title = $scope.currentUser.id === $scope.events.modifiable.user.id ? _t('app.logged.machines_reserve.i_ve_reserved') : _t('app.logged.machines_reserve.not_available');
|
||||
$scope.events.modifiable.backgroundColor = 'white';
|
||||
|
||||
return updateCalendar();
|
||||
@ -604,16 +627,18 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
|
||||
angular.forEach($scope.events.reserved, function (machineSlot, key) {
|
||||
machineSlot.is_reserved = true;
|
||||
machineSlot.can_modify = true;
|
||||
if ($scope.currentUser.role !== 'admin') {
|
||||
machineSlot.title = _t('app.logged.machines_reserve.i_ve_reserved');
|
||||
machineSlot.borderColor = BOOKED_SLOT_BORDER_COLOR;
|
||||
updateMachineSlot(machineSlot, reservation, $scope.currentUser);
|
||||
} else {
|
||||
if ($scope.currentUser.role === 'admin' || ($scope.currentUser.role === 'manager' && reservation.user_id !== $scope.currentUser.id)) {
|
||||
// an admin or a manager booked for someone else
|
||||
machineSlot.title = _t('app.logged.machines_reserve.not_available');
|
||||
machineSlot.borderColor = UNAVAILABLE_SLOT_BORDER_COLOR;
|
||||
updateMachineSlot(machineSlot, reservation, $scope.ctrl.member);
|
||||
} else {
|
||||
// booked for "myself"
|
||||
machineSlot.title = _t('app.logged.machines_reserve.i_ve_reserved');
|
||||
machineSlot.borderColor = BOOKED_SLOT_BORDER_COLOR;
|
||||
updateMachineSlot(machineSlot, reservation, $scope.currentUser);
|
||||
}
|
||||
return machineSlot.backgroundColor = 'white';
|
||||
machineSlot.backgroundColor = 'white';
|
||||
});
|
||||
|
||||
if ($scope.selectedPlan) {
|
||||
|
@ -81,59 +81,73 @@ Application.Controllers.controller('MainNavController', ['$scope', function ($sc
|
||||
{
|
||||
state: 'app.admin.calendar',
|
||||
linkText: 'app.public.common.manage_the_calendar',
|
||||
linkIcon: 'calendar'
|
||||
linkIcon: 'calendar',
|
||||
authorizedRoles: ['admin', 'manager']
|
||||
},
|
||||
{
|
||||
state: 'app.public.machines_list',
|
||||
linkText: 'app.public.common.manage_the_machines',
|
||||
linkIcon: 'cogs'
|
||||
linkIcon: 'cogs',
|
||||
authorizedRoles: ['admin', 'manager']
|
||||
},
|
||||
{
|
||||
state: 'app.admin.trainings',
|
||||
linkText: 'app.public.common.trainings_monitoring',
|
||||
linkIcon: 'graduation-cap'
|
||||
linkIcon: 'graduation-cap',
|
||||
authorizedRoles: ['admin', 'manager']
|
||||
},
|
||||
{
|
||||
state: 'app.admin.events',
|
||||
linkText: 'app.public.common.manage_the_events',
|
||||
linkIcon: 'tags'
|
||||
linkIcon: 'tags',
|
||||
authorizedRoles: ['admin', 'manager']
|
||||
},
|
||||
{ class: 'menu-spacer' },
|
||||
{
|
||||
state: 'app.admin.members',
|
||||
linkText: 'app.public.common.manage_the_users',
|
||||
linkIcon: 'users'
|
||||
linkIcon: 'users',
|
||||
authorizedRoles: ['admin', 'manager']
|
||||
},
|
||||
{
|
||||
state: 'app.admin.pricing',
|
||||
linkText: 'app.public.common.subscriptions_and_prices',
|
||||
linkIcon: 'money'
|
||||
linkIcon: 'money',
|
||||
authorizedRoles: ['admin']
|
||||
},
|
||||
{
|
||||
state: 'app.admin.invoices',
|
||||
linkText: 'app.public.common.manage_the_invoices',
|
||||
linkIcon: 'file-pdf-o'
|
||||
linkIcon: 'file-pdf-o',
|
||||
authorizedRoles: ['admin', 'manager']
|
||||
},
|
||||
{
|
||||
state: 'app.admin.statistics',
|
||||
linkText: 'app.public.common.statistics',
|
||||
linkIcon: 'bar-chart-o'
|
||||
linkIcon: 'bar-chart-o',
|
||||
authorizedRoles: ['admin']
|
||||
},
|
||||
{
|
||||
class: 'menu-spacer',
|
||||
authorizedRoles: ['admin']
|
||||
},
|
||||
{ class: 'menu-spacer' },
|
||||
{
|
||||
state: 'app.admin.settings',
|
||||
linkText: 'app.public.common.customization',
|
||||
linkIcon: 'gear'
|
||||
linkIcon: 'gear',
|
||||
authorizedRoles: ['admin']
|
||||
},
|
||||
{
|
||||
state: 'app.admin.project_elements',
|
||||
linkText: 'app.public.common.manage_the_projects_elements',
|
||||
linkIcon: 'tasks'
|
||||
linkIcon: 'tasks',
|
||||
authorizedRoles: ['admin']
|
||||
},
|
||||
{
|
||||
state: 'app.admin.open_api_clients',
|
||||
linkText: 'app.public.common.open_api_clients',
|
||||
linkIcon: 'cloud'
|
||||
linkIcon: 'cloud',
|
||||
authorizedRoles: ['admin']
|
||||
}
|
||||
].concat(Fablab.adminNavLinks);
|
||||
|
||||
|
@ -12,8 +12,8 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScope', '$state', '$uibModal', 'Auth', 'dialogs', 'growl', 'plansPromise', 'groupsPromise', 'Subscription', 'Member', 'subscriptionExplicationsPromise', '_t', 'Wallet', 'helpers',
|
||||
function ($scope, $rootScope, $state, $uibModal, Auth, dialogs, growl, plansPromise, groupsPromise, Subscription, Member, subscriptionExplicationsPromise, _t, Wallet, helpers) {
|
||||
Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScope', '$state', '$uibModal', 'Auth', 'AuthService', 'dialogs', 'growl', 'plansPromise', 'groupsPromise', 'Subscription', 'Member', 'subscriptionExplicationsPromise', '_t', 'Wallet', 'helpers',
|
||||
function ($scope, $rootScope, $state, $uibModal, Auth, AuthService, dialogs, growl, plansPromise, groupsPromise, Subscription, Member, subscriptionExplicationsPromise, _t, Wallet, helpers) {
|
||||
/* PUBLIC SCOPE */
|
||||
|
||||
// list of groups
|
||||
@ -28,13 +28,6 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
|
||||
|
||||
// list of plans, classified by group
|
||||
$scope.plansClassifiedByGroup = [];
|
||||
for (var group of Array.from($scope.groups)) {
|
||||
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); }
|
||||
}
|
||||
$scope.plansClassifiedByGroup.push(groupObj);
|
||||
}
|
||||
|
||||
// user to deal with
|
||||
$scope.ctrl = {
|
||||
@ -62,15 +55,15 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
|
||||
|
||||
/**
|
||||
* Callback to deal with the subscription of the user selected in the dropdown list instead of the current user's
|
||||
* subscription. (admins only)
|
||||
* subscription. (admins and managers only)
|
||||
*/
|
||||
$scope.updateMember = function () {
|
||||
$scope.selectedPlan = null;
|
||||
$scope.paid.plan = null;
|
||||
$scope.group.change = false;
|
||||
return Member.get({ id: $scope.ctrl.member.id }, function (member) {
|
||||
Member.get({ id: $scope.ctrl.member.id }, function (member) {
|
||||
$scope.ctrl.member = member;
|
||||
return $scope.group.id = $scope.ctrl.member.group_id;
|
||||
$scope.group.id = $scope.ctrl.member.group_id;
|
||||
});
|
||||
};
|
||||
|
||||
@ -97,14 +90,17 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
|
||||
$scope.openSubscribePlanModal = function () {
|
||||
Wallet.getWalletByUser({ user_id: $scope.ctrl.member.id }, function (wallet) {
|
||||
const amountToPay = helpers.getAmountToPay($scope.cart.total, wallet.amount);
|
||||
if (($scope.currentUser.role !== 'admin') && (amountToPay > 0)) {
|
||||
if ((AuthService.isAuthorized('member') && amountToPay > 0)
|
||||
|| (AuthService.isAuthorized('manager') && $scope.ctrl.member.id === $rootScope.currentUser.id && amountToPay > 0)) {
|
||||
if ($rootScope.fablabWithoutOnlinePayment) {
|
||||
growl.error(_t('app.public.plans.online_payment_disabled'));
|
||||
} else {
|
||||
return payByStripe();
|
||||
}
|
||||
} else {
|
||||
if (($scope.currentUser.role === 'admin') || (amountToPay === 0)) {
|
||||
if (AuthService.isAuthorized('admin')
|
||||
|| (AuthService.isAuthorized('manager') && $scope.ctrl.member.id !== $rootScope.currentUser.id)
|
||||
|| amountToPay === 0) {
|
||||
return payOnSite();
|
||||
}
|
||||
}
|
||||
@ -115,7 +111,7 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
|
||||
* Return the group object, identified by the ID set in $scope.group.id
|
||||
*/
|
||||
$scope.getUserGroup = function () {
|
||||
for (group of Array.from($scope.groups)) {
|
||||
for (const group of Array.from($scope.groups)) {
|
||||
if (group.id === $scope.group.id) {
|
||||
return group;
|
||||
}
|
||||
@ -130,7 +126,8 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
|
||||
$scope.ctrl.member = user;
|
||||
$scope.group.change = false;
|
||||
$scope.selectedPlan = null;
|
||||
if ($scope.currentUser.role !== 'admin') {
|
||||
if (AuthService.isAuthorized('member') ||
|
||||
(AuthService.isAuthorized('manager') && $scope.currentUser.id !== $scope.ctrl.member.id)) {
|
||||
$rootScope.currentUser = user;
|
||||
Auth._currentUser.group_id = user.group_id;
|
||||
growl.success(_t('app.public.plans.your_group_was_successfully_changed'));
|
||||
@ -139,7 +136,8 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
|
||||
}
|
||||
}
|
||||
, function (err) {
|
||||
if ($scope.currentUser.role !== 'admin') {
|
||||
if (AuthService.isAuthorized('member') ||
|
||||
(AuthService.isAuthorized('manager') && $scope.currentUser.id !== $scope.ctrl.member.id)) {
|
||||
growl.error(_t('app.public.plans.an_error_prevented_your_group_from_being_changed'));
|
||||
} else {
|
||||
growl.error(_t('app.public.plans.an_error_prevented_to_change_the_user_s_group'));
|
||||
@ -179,8 +177,20 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
|
||||
* Kind of constructor: these actions will be realized first when the controller is loaded
|
||||
*/
|
||||
const initialize = function () {
|
||||
// group all plans by Group
|
||||
for (const group of $scope.groups) {
|
||||
const groupObj = { id: group.id, name: group.name, plans: [], actives: 0 };
|
||||
for (let plan of plansPromise) {
|
||||
if (plan.group_id === group.id) {
|
||||
groupObj.plans.push(plan);
|
||||
if (!plan.disabled) { groupObj.actives++; }
|
||||
}
|
||||
}
|
||||
$scope.plansClassifiedByGroup.push(groupObj);
|
||||
}
|
||||
|
||||
if ($scope.currentUser) {
|
||||
if ($scope.currentUser.role !== 'admin') {
|
||||
if (!AuthService.isAuthorized('admin')) {
|
||||
$scope.ctrl.member = $scope.currentUser;
|
||||
$scope.paid.plan = $scope.currentUser.subscribed_plan;
|
||||
$scope.group.id = $scope.currentUser.group_id;
|
||||
@ -201,9 +211,9 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
|
||||
* Compute the total amount for the current reservation according to the previously set parameters
|
||||
* and assign the result in $scope.reserve.amountTotal
|
||||
*/
|
||||
var updateCartPrice = function () {
|
||||
// first we check that a user was selected
|
||||
if (Object.keys($scope.ctrl.member).length > 0) {
|
||||
const updateCartPrice = function () {
|
||||
// first we check the selection of a user
|
||||
if (Object.keys($scope.ctrl.member).length > 0 && $scope.selectedPlan) {
|
||||
$scope.cart.total = $scope.selectedPlan.amount;
|
||||
// apply the coupon if any
|
||||
if ($scope.coupon.applied) {
|
||||
@ -223,7 +233,7 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
|
||||
/**
|
||||
* Open a modal window which trigger the stripe payment process
|
||||
*/
|
||||
var payByStripe = function () {
|
||||
const payByStripe = function () {
|
||||
$uibModal.open({
|
||||
templateUrl: '<%= asset_path "stripe/payment_modal.html" %>',
|
||||
size: 'md',
|
||||
@ -262,7 +272,7 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
|
||||
CustomAsset.get({ name: 'cgv-file' }, function (cgv) { $scope.cgv = cgv.custom_asset; });
|
||||
|
||||
/**
|
||||
* Callback for click on the 'proceed' button.
|
||||
* Callback for a click on the 'proceed' button.
|
||||
* Handle the stripe's card tokenization process response and save the subscription to the API with the
|
||||
* card token just created.
|
||||
*/
|
||||
@ -283,7 +293,7 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
|
||||
/**
|
||||
* Open a modal window which trigger the local payment process
|
||||
*/
|
||||
var payOnSite = function () {
|
||||
const payOnSite = function () {
|
||||
$uibModal.open({
|
||||
templateUrl: '<%= asset_path "plans/payment_modal.html" %>',
|
||||
size: 'sm',
|
||||
@ -301,7 +311,7 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
|
||||
// user wallet amount
|
||||
$scope.walletAmount = wallet.amount;
|
||||
|
||||
// subcription price, coupon subtracted if any
|
||||
// subscription price, coupon subtracted if any
|
||||
$scope.price = price;
|
||||
|
||||
// price to pay
|
||||
|
@ -98,8 +98,8 @@ class SpacesController {
|
||||
/**
|
||||
* Controller used in the public listing page, allowing everyone to see the list of spaces
|
||||
*/
|
||||
Application.Controllers.controller('SpacesController', ['$scope', '$state', 'spacesPromise', '_t', 'Member', 'uiTourService',
|
||||
function ($scope, $state, spacesPromise, _t, Member, uiTourService) {
|
||||
Application.Controllers.controller('SpacesController', ['$scope', '$state', 'spacesPromise', 'AuthService', '_t', 'Member', 'uiTourService',
|
||||
function ($scope, $state, spacesPromise, AuthService, _t, Member, uiTourService) {
|
||||
/* PUBLIC SCOPE */
|
||||
|
||||
// Retrieve the list of spaces
|
||||
@ -131,9 +131,10 @@ Application.Controllers.controller('SpacesController', ['$scope', '$state', 'spa
|
||||
*/
|
||||
$scope.setupSpacesTour = function () {
|
||||
// setup the tour for admins only
|
||||
if ($scope.currentUser && $scope.currentUser.role === 'admin') {
|
||||
if (AuthService.isAuthorized(['admin', 'manager'])) {
|
||||
// get the tour defined by the ui-tour directive
|
||||
const uitour = uiTourService.getTourByName('spaces');
|
||||
if (AuthService.isAuthorized('admin')) {
|
||||
uitour.createStep({
|
||||
selector: 'body',
|
||||
stepId: 'welcome',
|
||||
@ -153,10 +154,31 @@ Application.Controllers.controller('SpacesController', ['$scope', '$state', 'spa
|
||||
placement: 'top'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
uitour.createStep({
|
||||
selector: 'body',
|
||||
stepId: 'welcome_manager',
|
||||
order: 0,
|
||||
title: _t('app.public.tour.spaces.welcome_manager.title'),
|
||||
content: _t('app.public.tour.spaces.welcome_manager.content'),
|
||||
placement: 'bottom',
|
||||
orphan: true
|
||||
});
|
||||
}
|
||||
if ($scope.spaces.length > 0) {
|
||||
uitour.createStep({
|
||||
selector: '.spaces-list .reserve-button',
|
||||
stepId: 'reserve',
|
||||
order: 2,
|
||||
title: _t('app.public.tour.spaces.reserve.title'),
|
||||
content: _t('app.public.tour.spaces.reserve.content'),
|
||||
placement: 'top'
|
||||
});
|
||||
}
|
||||
uitour.createStep({
|
||||
selector: 'body',
|
||||
stepId: 'conclusion',
|
||||
order: 2,
|
||||
order: 3,
|
||||
title: _t('app.public.tour.conclusion.title'),
|
||||
content: _t('app.public.tour.conclusion.content'),
|
||||
placement: 'bottom',
|
||||
|
@ -10,8 +10,8 @@
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs', 'growl', 'Auth', 'Price', 'Wallet', 'CustomAsset', 'Slot', 'helpers', '_t', '$uibModal',
|
||||
function ($rootScope, $uibModal, dialogs, growl, Auth, Price, Wallet, CustomAsset, Slot, helpers, _t, $uibModal) {
|
||||
Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs', 'growl', 'Auth', 'Price', 'Wallet', 'CustomAsset', 'Slot', 'AuthService', 'helpers', '_t',
|
||||
function ($rootScope, $uibModal, dialogs, growl, Auth, Price, Wallet, CustomAsset, Slot, AuthService, helpers, _t) {
|
||||
return ({
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
@ -167,7 +167,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
// 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 subscriptions
|
||||
// check selected user has a subscription, if any slot is restricted for subscriptions
|
||||
const slotValidations = [];
|
||||
let slotNotValid;
|
||||
let slotNotValidError;
|
||||
@ -195,7 +195,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
});
|
||||
const hasPlanForSlot = slotValidations.every(function (a) { return a; });
|
||||
if (!hasPlanForSlot) {
|
||||
if (!$scope.isAdmin()) {
|
||||
if (!AuthService.isAuthorized(['admin', 'manager'])) {
|
||||
return growl.error(_t('app.shared.cart.slot_restrict_subscriptions_must_select_plan'));
|
||||
} else {
|
||||
const modalInstance = $uibModal.open({
|
||||
@ -216,7 +216,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
return paySlots();
|
||||
}
|
||||
} else {
|
||||
// otherwise we alert, this error musn't occur when the current user is not admin
|
||||
// otherwise we alert, this error musn't occur when the current user is not admin or manager
|
||||
return growl.error(_t('app.shared.cart.please_select_a_member_first'));
|
||||
}
|
||||
};
|
||||
@ -285,12 +285,6 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the currently logged user has teh 'admin' role?
|
||||
* @returns {boolean}
|
||||
*/
|
||||
$scope.isAdmin = function () { return $rootScope.currentUser && ($rootScope.currentUser.role === 'admin'); };
|
||||
|
||||
/* PRIVATE SCOPE */
|
||||
|
||||
/**
|
||||
@ -325,11 +319,13 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
/**
|
||||
* Callback triggered when the selected slot changed
|
||||
*/
|
||||
var slotSelectionChanged = function () {
|
||||
const slotSelectionChanged = function () {
|
||||
if ($scope.slot) {
|
||||
// build a list of plans if this slot is restricted for subscriptions
|
||||
// if this slot is restricted for subscribers...
|
||||
if ($scope.slot.plan_ids.length > 0) {
|
||||
// ... we select all the plans matching these restrictions...
|
||||
const _plans = _.filter($scope.plans, function (p) { return _.include($scope.slot.plan_ids, p.id) });
|
||||
// ... and we group these plans, by Group...
|
||||
$scope.slot.plansGrouped = [];
|
||||
$scope.slot.group_ids = [];
|
||||
for (let group of Array.from($scope.groups)) {
|
||||
@ -338,7 +334,9 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
if (plan.group_id === group.id) { groupObj.plans.push(plan); }
|
||||
}
|
||||
if (groupObj.plans.length > 0) {
|
||||
if ($scope.isAdmin()) {
|
||||
// ... Finally, we only keep the plans matching the group of the current user
|
||||
// OR all plans if the current user is admin or manager
|
||||
if (AuthService.isAuthorized(['admin', 'manager'])) {
|
||||
$scope.slot.plansGrouped.push(groupObj);
|
||||
} else if ($scope.user.group_id === groupObj.id) {
|
||||
$scope.slot.plansGrouped.push(groupObj);
|
||||
@ -398,7 +396,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
}
|
||||
}
|
||||
, function (type) {
|
||||
// the user has choosen an action, so we proceed
|
||||
// the user has chosen an action, so we proceed
|
||||
if (type === 'move') {
|
||||
if (typeof $scope.onSlotStartToModify === 'function') { $scope.onSlotStartToModify(); }
|
||||
return $scope.events.modifiable = $scope.slot;
|
||||
@ -433,7 +431,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
/**
|
||||
* Reset the parameters that may lead to a wrong price but leave the content (events added to cart)
|
||||
*/
|
||||
var resetCartState = function () {
|
||||
const resetCartState = function () {
|
||||
$scope.selectedPlan = null;
|
||||
$scope.coupon.applied = null;
|
||||
$scope.events.moved = null;
|
||||
@ -446,8 +444,8 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
* Determines if the provided booked slot is able to be modified by the user.
|
||||
* @param slot {Object} fullCalendar event object
|
||||
*/
|
||||
var slotCanBeModified = function (slot) {
|
||||
if ($scope.isAdmin()) { return true; }
|
||||
const slotCanBeModified = function (slot) {
|
||||
if (AuthService.isAuthorized(['admin', 'manager'])) { return true; }
|
||||
const slotStart = moment(slot.start);
|
||||
const now = moment();
|
||||
return (slot.can_modify && $scope.enableBookingMove && (slotStart.diff(now, 'hours') >= $scope.moveBookingDelay));
|
||||
@ -457,8 +455,8 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
* Determines if the provided booked slot is able to be canceled by the user.
|
||||
* @param slot {Object} fullCalendar event object
|
||||
*/
|
||||
var slotCanBeCanceled = function (slot) {
|
||||
if ($scope.isAdmin()) { return true; }
|
||||
const slotCanBeCanceled = function (slot) {
|
||||
if (AuthService.isAuthorized(['admin', 'manager'])) { return true; }
|
||||
const slotStart = moment(slot.start);
|
||||
const now = moment();
|
||||
return (slot.can_modify && $scope.enableBookingCancel && (slotStart.diff(now, 'hours') >= $scope.cancelBookingDelay));
|
||||
@ -467,7 +465,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
/**
|
||||
* Callback triggered when the selected slot changed
|
||||
*/
|
||||
var planSelectionChanged = function () {
|
||||
const planSelectionChanged = function () {
|
||||
if (Auth.isAuthenticated()) {
|
||||
if ($scope.selectedPlan !== $scope.plan) {
|
||||
$scope.selectedPlan = $scope.plan;
|
||||
@ -486,7 +484,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
/**
|
||||
* Update the total price of the current selection/reservation
|
||||
*/
|
||||
var updateCartPrice = function () {
|
||||
const updateCartPrice = function () {
|
||||
if (Object.keys($scope.user).length > 0) {
|
||||
const r = mkReservation($scope.user, $scope.events.reserved, $scope.selectedPlan);
|
||||
return Price.compute(mkRequestParams(r, $scope.coupon.applied), function (res) {
|
||||
@ -501,7 +499,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
}
|
||||
};
|
||||
|
||||
var setSlotsDetails = function (details) {
|
||||
const setSlotsDetails = function (details) {
|
||||
angular.forEach($scope.events.reserved, function (slot) {
|
||||
angular.forEach(details.slots, function (s) {
|
||||
if (moment(s.start_at).isSame(slot.start)) {
|
||||
@ -518,7 +516,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
* @param coupon {Object} Coupon as returned from the API
|
||||
* @return {{reservation:Object, coupon_code:string}}
|
||||
*/
|
||||
var mkRequestParams = function (reservation, coupon) {
|
||||
const mkRequestParams = function (reservation, coupon) {
|
||||
return {
|
||||
reservation,
|
||||
coupon_code: ((coupon ? coupon.code : undefined))
|
||||
@ -532,7 +530,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
* @param [plan] {Object} Plan as retrieved from the API: plan to buy with the current reservation
|
||||
* @return {{user_id:Number, reservable_id:Number, reservable_type:String, slots_attributes:Array<Object>, plan_id:Number|null}}
|
||||
*/
|
||||
var mkReservation = function (member, slots, plan) {
|
||||
const mkReservation = function (member, slots, plan) {
|
||||
const reservation = {
|
||||
user_id: member.id,
|
||||
reservable_id: $scope.reservableId,
|
||||
@ -555,7 +553,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
/**
|
||||
* Open a modal window that allows the user to process a credit card payment for his current shopping cart.
|
||||
*/
|
||||
var payByStripe = function (reservation) {
|
||||
const payByStripe = function (reservation) {
|
||||
$uibModal.open({
|
||||
templateUrl: '<%= asset_path "stripe/payment_modal.html" %>',
|
||||
size: 'md',
|
||||
@ -612,7 +610,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
/**
|
||||
* Open a modal window that allows the user to process a local payment for his current shopping cart (admin only).
|
||||
*/
|
||||
var payOnSite = function (reservation) {
|
||||
const payOnSite = function (reservation) {
|
||||
$uibModal.open({
|
||||
templateUrl: '<%= asset_path "shared/valid_reservation_modal.html" %>',
|
||||
size: 'sm',
|
||||
@ -681,7 +679,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
/**
|
||||
* Actions to run after the payment was successful
|
||||
*/
|
||||
var afterPayment = function (reservation) {
|
||||
const afterPayment = function (reservation) {
|
||||
// we set the cart content as 'paid' to display a summary of the transaction
|
||||
$scope.events.paid = $scope.events.reserved;
|
||||
$scope.amountPaid = $scope.amountTotal;
|
||||
@ -697,19 +695,22 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
/**
|
||||
* Actions to pay slots
|
||||
*/
|
||||
var paySlots = function() {
|
||||
const paySlots = function() {
|
||||
const reservation = mkReservation($scope.user, $scope.events.reserved, $scope.selectedPlan);
|
||||
|
||||
return Wallet.getWalletByUser({ user_id: $scope.user.id }, function (wallet) {
|
||||
const amountToPay = helpers.getAmountToPay($scope.amountTotal, wallet.amount);
|
||||
if (!$scope.isAdmin() && (amountToPay > 0)) {
|
||||
if ((AuthService.isAuthorized(['member']) && amountToPay > 0)
|
||||
|| (AuthService.isAuthorized('manager') && $scope.user.id === $rootScope.currentUser.id && amountToPay > 0)) {
|
||||
if ($rootScope.fablabWithoutOnlinePayment) {
|
||||
growl.error(_t('app.shared.cart.online_payment_disabled'));
|
||||
} else {
|
||||
return payByStripe(reservation);
|
||||
}
|
||||
} else {
|
||||
if ($scope.isAdmin() || (amountToPay === 0)) {
|
||||
if (AuthService.isAuthorized(['admin'])
|
||||
|| (AuthService.isAuthorized('manager') && $scope.user.id !== $rootScope.currentUser.id)
|
||||
|| amountToPay === 0) {
|
||||
return payOnSite(reservation);
|
||||
}
|
||||
}
|
||||
@ -726,10 +727,11 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
||||
/**
|
||||
* Controller of modal for show reservations the same date at the same time
|
||||
*/
|
||||
Application.Controllers.controller('ReserveSlotSameTimeController', ['$scope', '$uibModalInstance', 'sameTimeReservations', 'growl', '_t',
|
||||
function ($scope, $uibModalInstance, sameTimeReservations, growl, _t) {
|
||||
Application.Controllers.controller('ReserveSlotSameTimeController', ['$scope', '$uibModalInstance', 'AuthService', 'sameTimeReservations', 'growl', '_t',
|
||||
function ($scope, $uibModalInstance, AuthService, sameTimeReservations, growl, _t) {
|
||||
$scope.sameTimeReservations = sameTimeReservations;
|
||||
$scope.bookSlotAtSameTime = Fablab.bookSlotAtSameTime;
|
||||
$scope.isAuthorized = AuthService.isAuthorized;
|
||||
/**
|
||||
* Confirmation callback
|
||||
*/
|
||||
|
@ -55,7 +55,7 @@ angular.module('application.router', ['ui.router'])
|
||||
.state('app.logged', {
|
||||
abstract: true,
|
||||
data: {
|
||||
authorizedRoles: ['member', 'admin']
|
||||
authorizedRoles: ['member', 'admin', 'manager']
|
||||
},
|
||||
resolve: {
|
||||
currentUser: ['Auth', function (Auth) { return Auth.currentUser(); }],
|
||||
@ -68,7 +68,7 @@ angular.module('application.router', ['ui.router'])
|
||||
.state('app.admin', {
|
||||
abstract: true,
|
||||
data: {
|
||||
authorizedRoles: ['admin']
|
||||
authorizedRoles: ['admin', 'manager']
|
||||
},
|
||||
resolve: {
|
||||
currentUser: ['Auth', function (Auth) { return Auth.currentUser(); }],
|
||||
@ -931,8 +931,21 @@ angular.module('application.router', ['ui.router'])
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('app.admin.managers_new', {
|
||||
url: '/admin/managers/new',
|
||||
views: {
|
||||
'main@': {
|
||||
templateUrl: '<%= asset_path "admin/managers/new.html" %>',
|
||||
controller: 'NewManagerController'
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
groupsPromise: ['Group', function (Group) { return Group.query().$promise; }],
|
||||
tagsPromise: ['Tag', function (Tag) { return Tag.query().$promise; }]
|
||||
}
|
||||
})
|
||||
|
||||
// authentification providers
|
||||
// authentication providers
|
||||
.state('app.admin.authentication_new', {
|
||||
url: '/admin/authentications/new',
|
||||
views: {
|
||||
|
@ -1,9 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
Application.Services.factory('_t', ['$filter', function ($filter) {
|
||||
return function (key, interpolation, options) {
|
||||
if (interpolation == null) { interpolation = undefined; }
|
||||
if (options == null) { options = undefined; }
|
||||
return $filter('translate')(key, interpolation, options);
|
||||
Application.Services.factory('_t', ['$translate', function ($translate) {
|
||||
return function (key, interpolations) {
|
||||
if (interpolations == null) { interpolations = undefined; }
|
||||
return $translate.instant(key, interpolations);
|
||||
};
|
||||
}]);
|
||||
|
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
Application.Services.factory('Help', ['$rootScope', '$uibModal', '$state', function ($rootScope, $uibModal, $state) {
|
||||
Application.Services.factory('Help', ['$rootScope', '$uibModal', '$state', 'AuthService',
|
||||
function ($rootScope, $uibModal, $state, AuthService) {
|
||||
const TOURS = {
|
||||
'app.public.home': 'welcome',
|
||||
'app.public.machines_list': 'machines',
|
||||
@ -19,7 +20,7 @@ Application.Services.factory('Help', ['$rootScope', '$uibModal', '$state', funct
|
||||
|
||||
|
||||
return function (e) {
|
||||
if (!$rootScope.currentUser || $rootScope.currentUser.role !== 'admin') return;
|
||||
if (!AuthService.isAuthorized(['admin', 'manager'])) return;
|
||||
|
||||
if (e.key === 'F1') {
|
||||
e.preventDefault();
|
||||
|
@ -537,7 +537,6 @@
|
||||
}
|
||||
|
||||
li.level-2-tab > a {
|
||||
background: #eee;
|
||||
line-height: 14px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12 col-lg-3">
|
||||
<div class="m text-center">
|
||||
<div class="m text-center" ng-show="isAuthorized('admin')">
|
||||
<a class="btn btn-default export-xls-button"
|
||||
ng-href="api/availabilities/export_index.xlsx"
|
||||
target="export-frame"
|
||||
@ -71,7 +71,7 @@
|
||||
<span class="btn btn-warning btn-xs" ng-click="cancelBooking(r)" ng-if="!r.canceled_at"><i class="fa fa-times red"></i></span>
|
||||
</li>
|
||||
</ul>
|
||||
<div ng-show="reservations.length == 0" translate>{{ 'app.admin.calendar.no_reservations' }}</div>
|
||||
<div ng-show="reservations.length == 0" translate>{{ 'app.admin.calendar.without_reservation' }}</div>
|
||||
<div class="m-t" ng-show="availability.lock"><i class="fa fa-ban"></i> <span class="m-l-xs" translate>{{ 'app.admin.calendar.reservations_locked' }}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -11,7 +11,7 @@
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md" ng-if="isAuthorized(['admin'])">
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md" ng-if="isAuthorized(['admin', 'manager'])">
|
||||
<section class="heading-actions wrapper">
|
||||
<a class="btn btn-lg btn-warning bg-white b-2x rounded m-t-sm upper text-sm" ui-sref="app.admin.events_new" role="button" translate>{{ 'app.admin.events.add_an_event' }}</a>
|
||||
</section>
|
||||
@ -26,7 +26,7 @@
|
||||
ui-tour-scroll-parent-id="content-main"
|
||||
post-render="setupEventsTour">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="col-md-12" ng-if="isAuthorized('admin')">
|
||||
<uib-tabset justified="true" active="tabs.active">
|
||||
<uib-tab heading="{{ 'app.admin.events.events_monitoring' | translate }}" index="0">
|
||||
<ng-include src="'<%= asset_path "admin/events/monitoring.html" %>'"></ng-include>
|
||||
@ -41,6 +41,9 @@
|
||||
</uib-tab>
|
||||
</uib-tabset>
|
||||
</div>
|
||||
<div class="col-md-12" ng-if="isAuthorized('manager')">
|
||||
<ng-include src="'<%= asset_path "admin/events/monitoring.html" %>'"></ng-include>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
@ -51,7 +51,7 @@
|
||||
<td style="vertical-align:middle">
|
||||
<span class="ng-binding" ng-if="event.nb_total_places > 0">{{ event.nb_total_places - event.nb_free_places }} / {{ event.nb_total_places }}</span>
|
||||
<span class="badge font-sbold cancelled" ng-if="event.nb_total_places == -1" translate>{{ 'app.admin.events.cancelled' }}</span>
|
||||
<span class="badge font-sbold" ng-if="!event.nb_total_places" translate>{{ 'app.admin.events.free_entry' }}</span>
|
||||
<span class="badge font-sbold" ng-if="!event.nb_total_places" translate>{{ 'app.admin.events.without_reservation' }}</span>
|
||||
</td>
|
||||
|
||||
<td style="vertical-align:middle">
|
||||
|
111
app/assets/templates/admin/invoices/codes.html.erb
Normal file
111
app/assets/templates/admin/invoices/codes.html.erb
Normal file
@ -0,0 +1,111 @@
|
||||
<div class="panel panel-default m-t-md accounting-codes">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="journalCode" translate>{{ 'app.admin.invoices.accounting_journal_code' }}</label>
|
||||
<input type="text" id="journalCode" ng-model="settings.journalCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_journal_code' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="cardClientCode" translate>{{ 'app.admin.invoices.accounting_card_client_code' }}</label>
|
||||
<input type="text" id="cardClientCode" ng-model="settings.cardClientCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.card_client_code' | translate }}" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="cardClientLabel" translate>{{ 'app.admin.invoices.accounting_card_client_label' }}</label>
|
||||
<input type="text" id="cardClientLabel" ng-model="settings.cardClientLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.card_client_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="walletClientCode" translate>{{ 'app.admin.invoices.accounting_wallet_client_code' }}</label>
|
||||
<input type="text" id="walletClientCode" ng-model="settings.walletClientCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.wallet_client_code' | translate }}" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="walletClientLabel" translate>{{ 'app.admin.invoices.accounting_wallet_client_label' }}</label>
|
||||
<input type="text" id="walletClientLabel" ng-model="settings.walletClientLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.wallet_client_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="otherClientCode" translate>{{ 'app.admin.invoices.accounting_other_client_code' }}</label>
|
||||
<input type="text" id="otherClientCode" ng-model="settings.otherClientCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.other_client_code' | translate }}" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="otherClientLabel" translate>{{ 'app.admin.invoices.accounting_other_client_label' }}</label>
|
||||
<input type="text" id="otherClientLabel" ng-model="settings.otherClientLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.other_client_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="walletCode" translate>{{ 'app.admin.invoices.accounting_wallet_code' }}</label>
|
||||
<input type="text" id="walletCode" ng-model="settings.walletCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_wallet_code' | translate }}" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="walletLabel" translate>{{ 'app.admin.invoices.accounting_wallet_label' }}</label>
|
||||
<input type="text" id="walletLabel" ng-model="settings.walletLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_wallet_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="vatCode" translate>{{ 'app.admin.invoices.accounting_vat_code' }}</label>
|
||||
<input type="text" id="vatCode" ng-model="settings.vatCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_vat_code' | translate }}"/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="vatLabel" translate>{{ 'app.admin.invoices.accounting_vat_label' }}</label>
|
||||
<input type="text" id="vatLabel" ng-model="settings.vatLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_vat_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="subscriptionCode" translate>{{ 'app.admin.invoices.accounting_subscription_code' }}</label>
|
||||
<input type="text" id="subscriptionCode" ng-model="settings.subscriptionCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_subscription_code' | translate }}" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="subscriptionLabel" translate>{{ 'app.admin.invoices.accounting_subscription_label' }}</label>
|
||||
<input type="text" id="subscriptionLabel" ng-model="settings.subscriptionLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_subscription_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="machineCode" translate>{{ 'app.admin.invoices.accounting_Machine_code' }}</label>
|
||||
<input type="text" id="machineCode" ng-model="settings.machineCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_machine_code' | translate }}"/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="machineLabel" translate>{{ 'app.admin.invoices.accounting_Machine_label' }}</label>
|
||||
<input type="text" id="machineLabel" ng-model="settings.machineLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_machine_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="trainingCode" translate>{{ 'app.admin.invoices.accounting_Training_code' }}</label>
|
||||
<input type="text" id="trainingCode" ng-model="settings.trainingCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_training_code' | translate }}" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="trainingLabel" translate>{{ 'app.admin.invoices.accounting_Training_label' }}</label>
|
||||
<input type="text" id="trainingLabel" ng-model="settings.trainingLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_training_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="eventCode" translate>{{ 'app.admin.invoices.accounting_Event_code' }}</label>
|
||||
<input type="text" id="eventCode" ng-model="settings.eventCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_event_code' | translate }}"/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="eventLabel" translate>{{ 'app.admin.invoices.accounting_Event_label' }}</label>
|
||||
<input type="text" id="eventLabel" ng-model="settings.eventLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_event_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="spaceCode" translate>{{ 'app.admin.invoices.accounting_Space_code' }}</label>
|
||||
<input type="text" id="spaceCode" ng-model="settings.spaceCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_space_code' | translate }}" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="spaceLabel" translate>{{ 'app.admin.invoices.accounting_Space_label' }}</label>
|
||||
<input type="text" id="spaceLabel" ng-model="settings.spaceLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_space_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<button name="button" class="btn btn-warning m-t-lg" ng-click="save()" translate>{{ 'app.shared.buttons.save' }}</button>
|
||||
</div>
|
||||
</div>
|
@ -11,7 +11,7 @@
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-12 col-md-3">
|
||||
<section class="heading-actions wrapper">
|
||||
<section class="heading-actions wrapper" ng-show="isAuthorized('admin')">
|
||||
<a class="btn btn-default rounded m-t-sm export-accounting-button" ng-click="toggleExportModal()"><i class="fa fa-book"></i></a>
|
||||
<iframe name="export-frame" height="0" width="0" class="none" id="accounting-export-frame"></iframe>
|
||||
<a class="btn btn-lg btn-default rounded m-t-sm text-sm close-accounting-periods-button" ng-click="closeAnAccountingPeriod()"><i class="fa fa-calendar-check-o"></i> {{ 'app.admin.invoices.accounting_periods' | translate }}</a>
|
||||
@ -28,530 +28,25 @@
|
||||
ui-tour-scroll-parent-id="content-main"
|
||||
post-render="setupInvoicesTour">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="col-md-12" ng-if="isAuthorized('admin')">
|
||||
<uib-tabset justified="true" active="tabs.active">
|
||||
<uib-tab heading="{{ 'app.admin.invoices.invoices_list' | translate }}" ng-hide="fablabWithoutInvoices" index="0">
|
||||
<h3 class="m-t-xs"><i class="fa fa-filter"></i> {{ 'app.admin.invoices.filter_invoices' | translate }}</h3>
|
||||
<ng-include src="'<%= asset_path "admin/invoices/list.html" %>'"></ng-include>
|
||||
</uib-tab>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon" translate>{{ 'app.admin.invoices.invoice_num_' }}</span>
|
||||
<input type="text" ng-model="searchInvoice.reference" class="form-control" placeholder="" ng-change="handleFilterChange()">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<uib-tab heading="{{ 'app.admin.invoices.invoicing_settings' | translate }}" index="1" class="invoices-settings">
|
||||
<ng-include src="'<%= asset_path "admin/invoices/settings.html" %>'"></ng-include>
|
||||
</uib-tab>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon" translate>{{ 'app.admin.invoices.customer_' }}</span>
|
||||
<input type="text" ng-model="searchInvoice.name" class="form-control" placeholder="" ng-change="handleFilterChange()">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<uib-tab heading="{{ 'app.admin.invoices.accounting_codes' | translate }}" index="2" class="accounting-codes-tab">
|
||||
<ng-include src="'<%= asset_path "admin/invoices/codes.html" %>'"></ng-include>
|
||||
</uib-tab>
|
||||
</uib-tabset>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">{{ "app.admin.invoices.date_" | translate }}</span>
|
||||
<input type="date" ng-model="searchInvoice.date" class="form-control" ng-change="handleFilterChange()">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
||||
<table class="table invoices-list" ng-if="invoices.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:5%"></th>
|
||||
<th style="width:15%"><a href="" ng-click="setOrderInvoice('reference')">{{ 'app.admin.invoices.invoice_num' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderInvoice=='reference', 'fa fa-sort-numeric-desc': orderInvoice=='-reference', 'fa fa-arrows-v': orderInvoice }"></i></a></th>
|
||||
|
||||
<th style="width:20%"><a href="" ng-click="setOrderInvoice('date')">{{ 'app.admin.invoices.date' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderInvoice=='date', 'fa fa-sort-numeric-desc': orderInvoice=='-date', 'fa fa-arrows-v': orderInvoice }"></i></a></th>
|
||||
|
||||
<th style="width:10%"><a href="" ng-click="setOrderInvoice('total')"> {{ 'app.admin.invoices.price' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderInvoice=='total', 'fa fa-sort-numeric-desc': orderInvoice=='-total', 'fa fa-arrows-v': orderInvoice }"></i></a></th>
|
||||
|
||||
<th style="width:20%"><a href="" ng-click="setOrderInvoice('name')">{{ 'app.admin.invoices.customer' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderInvoice=='name', 'fa fa-sort-alpha-desc': orderInvoice=='-name', 'fa fa-arrows-v': orderInvoice }"></i></a></th>
|
||||
|
||||
<th style="width:30%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="invoice in invoices">
|
||||
<td class="chained-indicator">
|
||||
<i class="fa fa-link chained" ng-show="invoice.chained_footprint"/>
|
||||
<i class="fa fa-chain-broken broken" ng-hide="invoice.chained_footprint"/>
|
||||
</td>
|
||||
<td>{{ invoice.reference }}</td>
|
||||
<td ng-if="!invoice.is_avoir">{{ invoice.date | amDateFormat:'L LTS' }}</td>
|
||||
<td ng-if="invoice.is_avoir">{{ invoice.date | amDateFormat:'L' }}</td>
|
||||
<td>{{ invoice.total | currency}}</td>
|
||||
<td>
|
||||
<a href="" ui-sref="app.admin.members_edit({id: invoice.user_id})" ng-show="invoice.user_id">{{ invoice.name }}</a>
|
||||
<span ng-hide="invoice.user_id">{{ invoice.name }}</span>
|
||||
<td>
|
||||
<div class="buttons">
|
||||
<a class="btn btn-default download-button" ng-href="api/invoices/{{invoice.id}}/download" target="_blank" ng-if="!invoice.is_avoir">
|
||||
<i class="fa fa-file-pdf-o"></i> {{ 'app.admin.invoices.download_the_invoice' | translate }}
|
||||
</a>
|
||||
<a class="btn btn-default" ng-href="api/invoices/{{invoice.id}}/download" target="_blank" ng-if="invoice.is_avoir">
|
||||
<i class="fa fa-file-pdf-o"></i> {{ 'app.admin.invoices.download_the_credit_note' | translate }}
|
||||
</a>
|
||||
<a class="btn btn-default refund-button" ng-click="generateAvoirForInvoice(invoice)" ng-if="(!invoice.has_avoir || invoice.has_avoir == 'partial') && !invoice.is_avoir && !invoice.prevent_refund && !isDateClosed(invoice.created_at)">
|
||||
<i class="fa fa-reply"></i> {{ 'app.admin.invoices.credit_note' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="text-center">
|
||||
<button class="btn btn-warning" ng-click="showNextInvoices()" ng-hide="noMoreResults"><i class="fa fa-search-plus" aria-hidden="true"></i> {{ 'app.admin.invoices.display_more_invoices' | translate }}</button>
|
||||
</div>
|
||||
<p ng-if="invoices.length == 0" translate>{{ 'app.admin.invoices.no_invoices_for_now' }}</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</uib-tab>
|
||||
|
||||
|
||||
|
||||
|
||||
<uib-tab heading="{{ 'app.admin.invoices.invoicing_settings' | translate }}" index="1" class="invoices-settings">
|
||||
<div class="alert alert-warning p-md m-t" role="alert" ng-show="fablabWithoutInvoices">
|
||||
<i class="fa fa-warning m-r"></i>
|
||||
<span translate>{{ 'app.admin.invoices.warning_invoices_disabled' }}</span>
|
||||
</div>
|
||||
<form class="invoice-placeholder">
|
||||
<div class="invoice-logo">
|
||||
<img src="data:image/png;base64," data-src="holder.js/100%x100%/text:/font:FontAwesome/icon" bs-holder ng-if="!invoice.logo" class="img-responsive">
|
||||
<img base-sixty-four-image="invoice.logo" ng-if="invoice.logo && invoice.logo.base64">
|
||||
<div class="tools-box">
|
||||
<div class="btn-group">
|
||||
<div class="btn btn-default btn-file">
|
||||
<i class="fa fa-edit"></i> {{ 'app.admin.invoices.change_logo' | translate }}
|
||||
<input type="file" accept="image/png,image/jpeg,image/x-png,image/pjpeg" name="invoice[logo][attachment]" ng-model="invoice.logo" base-sixty-four-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="invoice-buyer-infos">
|
||||
<strong translate>{{ 'app.admin.invoices.john_smith' }}</strong>
|
||||
<div translate>{{ 'app.admin.invoices.john_smith_at_example_com' }}</div>
|
||||
</div>
|
||||
<div class="invoice-reference invoice-editable" ng-click="openEditReference()">{{ 'app.admin.invoices.invoice_reference_' | translate }} {{mkReference()}}</div>
|
||||
<div class="invoice-code invoice-editable" ng-show="invoice.code.active" ng-click="openEditCode()">{{ 'app.admin.invoices.code_' | translate }} {{invoice.code.model}}</div>
|
||||
<div class="invoice-code invoice-activable" ng-show="!invoice.code.active" ng-click="openEditCode()" translate>{{ 'app.admin.invoices.code_disabled' }}</div>
|
||||
<div class="invoice-order invoice-editable" ng-click="openEditInvoiceNb()"> {{ 'app.admin.invoices.order_num' | translate }} {{mkNumber()}}</div>
|
||||
<div class="invoice-date">{{ 'app.admin.invoices.invoice_issued_on_DATE_at_TIME' | translate:{DATE:(today | amDateFormat:'L'), TIME:(today | amDateFormat:'LT')} }}</div>
|
||||
<div class="invoice-object">
|
||||
{{ 'app.admin.invoices.object_reservation_of_john_smith_on_DATE_at_TIME' | translate:{DATE:(inOneWeek | amDateFormat:'L'), TIME:(inOneWeek | amDateFormat:'LT')} }}
|
||||
</div>
|
||||
<div class="invoice-data">
|
||||
{{ 'app.admin.invoices.order_summary' | translate }}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th translate>{{ 'app.admin.invoices.details' }}</th>
|
||||
<th class="right" translate>{{ 'app.admin.invoices.amount' }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ 'app.admin.invoices.machine_booking-3D_printer' | translate }} {{inOneWeek | amDateFormat:'LLL'}} - {{inOneWeekAndOneHour | amDateFormat:'LT'}}</td>
|
||||
<td class="right">{{30.0 | currency}}</td>
|
||||
</tr>
|
||||
|
||||
<tr class="invoice-total" ng-class="{'bold vat-line':invoice.VAT.active}">
|
||||
<td ng-show="!invoice.VAT.active" translate>{{ 'app.admin.invoices.total_amount' }}</td>
|
||||
<td ng-show="invoice.VAT.active" translate>{{ 'app.admin.invoices.total_including_all_taxes' }}</td>
|
||||
<td class="right">{{30.0 | currency}}</td>
|
||||
</tr>
|
||||
|
||||
<tr class="invoice-vat invoice-activable" ng-click="openEditVAT()" ng-show="!invoice.VAT.active">
|
||||
<td translate>{{ 'app.admin.invoices.VAT_disabled' }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
|
||||
<tr class="invoice-vat invoice-editable vat-line italic" ng-click="openEditVAT()" ng-show="invoice.VAT.active">
|
||||
<td>{{ 'app.admin.invoices.including_VAT' | translate }} {{invoice.VAT.rate}} %</td>
|
||||
<td>{{30-(30/(invoice.VAT.rate/100+1)) | currency}}</td>
|
||||
</tr>
|
||||
<tr class="invoice-ht vat-line italic" ng-show="invoice.VAT.active">
|
||||
<td translate>{{ 'app.admin.invoices.including_total_excluding_taxes' }}</td>
|
||||
<td>{{30/(invoice.VAT.rate/100+1) | currency}}</td>
|
||||
</tr>
|
||||
<tr class="invoice-payed vat-line bold" ng-show="invoice.VAT.active">
|
||||
<td translate>{{ 'app.admin.invoices.including_amount_payed_on_ordering' }}</td>
|
||||
<td>{{30.0 | currency}}</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="invoice-payment" translate translate-values="{DATE:(today | amDateFormat:'L'), TIME:(today | amDateFormat:'LT'), AMOUNT:(30.0 | currency)}">
|
||||
{{ 'app.admin.invoices.settlement_by_debit_card_on_DATE_at_TIME_for_an_amount_of_AMOUNT' }}
|
||||
</p>
|
||||
</div>
|
||||
<div medium-editor class="invoice-text invoice-editable" ng-model="invoice.text.content"
|
||||
options='{
|
||||
"placeholder": "{{ "app.admin.invoices.important_notes" | translate }}",
|
||||
"buttons": ["underline"]
|
||||
}'
|
||||
ng-blur="textEditEnd($event)">
|
||||
</div>
|
||||
<div medium-editor class="invoice-legals invoice-editable" ng-model="invoice.legals.content"
|
||||
options='{
|
||||
"placeholder": "{{ "app.admin.invoices.address_and_legal_information" | translate }}",
|
||||
"buttons": ["bold", "underline"]
|
||||
}'
|
||||
ng-blur="legalsEditEnd($event)">
|
||||
</div>
|
||||
</form>
|
||||
</uib-tab>
|
||||
|
||||
|
||||
|
||||
|
||||
<uib-tab heading="{{ 'app.admin.invoices.accounting_codes' | translate }}" index="2" class="accounting-codes-tab">
|
||||
<div class="panel panel-default m-t-md accounting-codes">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="journalCode" translate>{{ 'app.admin.invoices.accounting_journal_code' }}</label>
|
||||
<input type="text" id="journalCode" ng-model="settings.journalCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_journal_code' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="cardClientCode" translate>{{ 'app.admin.invoices.accounting_card_client_code' }}</label>
|
||||
<input type="text" id="cardClientCode" ng-model="settings.cardClientCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.card_client_code' | translate }}" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="cardClientLabel" translate>{{ 'app.admin.invoices.accounting_card_client_label' }}</label>
|
||||
<input type="text" id="cardClientLabel" ng-model="settings.cardClientLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.card_client_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="walletClientCode" translate>{{ 'app.admin.invoices.accounting_wallet_client_code' }}</label>
|
||||
<input type="text" id="walletClientCode" ng-model="settings.walletClientCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.wallet_client_code' | translate }}" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="walletClientLabel" translate>{{ 'app.admin.invoices.accounting_wallet_client_label' }}</label>
|
||||
<input type="text" id="walletClientLabel" ng-model="settings.walletClientLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.wallet_client_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="otherClientCode" translate>{{ 'app.admin.invoices.accounting_other_client_code' }}</label>
|
||||
<input type="text" id="otherClientCode" ng-model="settings.otherClientCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.other_client_code' | translate }}" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="otherClientLabel" translate>{{ 'app.admin.invoices.accounting_other_client_label' }}</label>
|
||||
<input type="text" id="otherClientLabel" ng-model="settings.otherClientLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.other_client_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="walletCode" translate>{{ 'app.admin.invoices.accounting_wallet_code' }}</label>
|
||||
<input type="text" id="walletCode" ng-model="settings.walletCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_wallet_code' | translate }}" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="walletLabel" translate>{{ 'app.admin.invoices.accounting_wallet_label' }}</label>
|
||||
<input type="text" id="walletLabel" ng-model="settings.walletLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_wallet_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="vatCode" translate>{{ 'app.admin.invoices.accounting_vat_code' }}</label>
|
||||
<input type="text" id="vatCode" ng-model="settings.vatCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_vat_code' | translate }}"/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="vatLabel" translate>{{ 'app.admin.invoices.accounting_vat_label' }}</label>
|
||||
<input type="text" id="vatLabel" ng-model="settings.vatLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_vat_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="subscriptionCode" translate>{{ 'app.admin.invoices.accounting_subscription_code' }}</label>
|
||||
<input type="text" id="subscriptionCode" ng-model="settings.subscriptionCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_subscription_code' | translate }}" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="subscriptionLabel" translate>{{ 'app.admin.invoices.accounting_subscription_label' }}</label>
|
||||
<input type="text" id="subscriptionLabel" ng-model="settings.subscriptionLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_subscription_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="machineCode" translate>{{ 'app.admin.invoices.accounting_Machine_code' }}</label>
|
||||
<input type="text" id="machineCode" ng-model="settings.machineCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_machine_code' | translate }}"/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="machineLabel" translate>{{ 'app.admin.invoices.accounting_Machine_label' }}</label>
|
||||
<input type="text" id="machineLabel" ng-model="settings.machineLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_machine_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="trainingCode" translate>{{ 'app.admin.invoices.accounting_Training_code' }}</label>
|
||||
<input type="text" id="trainingCode" ng-model="settings.trainingCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_training_code' | translate }}" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="trainingLabel" translate>{{ 'app.admin.invoices.accounting_Training_label' }}</label>
|
||||
<input type="text" id="trainingLabel" ng-model="settings.trainingLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_training_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="eventCode" translate>{{ 'app.admin.invoices.accounting_Event_code' }}</label>
|
||||
<input type="text" id="eventCode" ng-model="settings.eventCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_event_code' | translate }}"/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="eventLabel" translate>{{ 'app.admin.invoices.accounting_Event_label' }}</label>
|
||||
<input type="text" id="eventLabel" ng-model="settings.eventLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_event_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="spaceCode" translate>{{ 'app.admin.invoices.accounting_Space_code' }}</label>
|
||||
<input type="text" id="spaceCode" ng-model="settings.spaceCode.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_space_code' | translate }}" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="spaceLabel" translate>{{ 'app.admin.invoices.accounting_Space_label' }}</label>
|
||||
<input type="text" id="spaceLabel" ng-model="settings.spaceLabel.value" class="form-control" placeholder="{{ 'app.admin.invoices.general_space_label' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<button name="button" class="btn btn-warning m-t-lg" ng-click="save()" translate>{{ 'app.shared.buttons.save' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</uib-tab>
|
||||
</uib-tabset>
|
||||
</div>
|
||||
<div class="col-md-12" ng-if="isAuthorized('manager')">
|
||||
<ng-include src="'<%= asset_path "admin/invoices/list.html" %>'"></ng-include>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<script type="text/ng-template" id="editReference.html">
|
||||
<div class="custom-invoice">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title" translate>{{ 'app.admin.invoices.invoice_reference' }}</h3>
|
||||
</div>
|
||||
<div class="modal-body row">
|
||||
<div class="elements col-md-4">
|
||||
<h4>Éléments</h4>
|
||||
<ul>
|
||||
<li ng-click="invoice.reference.help = 'addYear.html'">{{ 'app.admin.invoices.year' | translate }}</li>
|
||||
<li ng-click="invoice.reference.help = 'addMonth.html'">{{ 'app.admin.invoices.month' | translate }}</li>
|
||||
<li ng-click="invoice.reference.help = 'addDay.html'">{{ 'app.admin.invoices.day' | translate }}</li>
|
||||
<li ng-click="invoice.reference.help = 'addInvoiceNumber.html'">{{ 'app.admin.invoices.num_of_invoice' | translate }}</li>
|
||||
<li ng-click="invoice.reference.help = 'addOnlineInfo.html'">{{ 'app.admin.invoices.online_sales' | translate }}</li>
|
||||
<%# <li ng-click="invoice.reference.help = 'addWalletInfo.html'">{{ 'app.admin.invoices.wallet' | translate }}</li> %>
|
||||
<li ng-click="invoice.reference.help = 'addRefundInfo.html'">{{ 'app.admin.invoices.refund' | translate }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="model">
|
||||
<h4 translate>{{ 'app.admin.invoices.model' }}</h4>
|
||||
<input type="text" class="form-control" ng-model="model">
|
||||
</div>
|
||||
<div class="help">
|
||||
<h4 translate>{{ 'app.admin.invoices.documentation' }}</h4>
|
||||
<ng-include src="invoice.reference.help" autoscroll="true">
|
||||
</ng-include>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
|
||||
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addYear.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>YY</strong></td><td translate>{{ 'app.admin.invoices.2_digits_year' }}</td></tr>
|
||||
<tr><td><strong>YYYY</strong></td><td translate>{{ 'app.admin.invoices.4_digits_year' }}</td></tr>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addMonth.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>M</strong></td><td translate>{{ 'app.admin.invoices.month_number' }}</td></tr>
|
||||
<tr><td><strong>MM</strong></td><td translate>{{ 'app.admin.invoices.2_digits_month_number' }}</td></tr>
|
||||
<tr><td><strong>MMM</strong></td><td translate>{{ 'app.admin.invoices.3_characters_month_name' }}</td></tr>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addDay.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>D</strong></td><td translate>{{ 'app.admin.invoices.day_in_the_month' }}</td></tr>
|
||||
<tr><td><strong>DD</strong></td><td translate>{{ 'app.admin.invoices.2_digits_day_in_the_month' }}</td></tr>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addInvoiceNumber.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>dd...dd</strong></td><td translate>{{ 'app.admin.invoices.n_digits_daily_count_of_invoices' }}</td></tr>
|
||||
<tr><td><strong>mm...mm</strong></td><td translate>{{ 'app.admin.invoices.n_digits_monthly_count_of_invoices' }}</td></tr>
|
||||
<tr><td><strong>yy...yy</strong></td><td translate>{{ 'app.admin.invoices.n_digits_annual_amount_of_invoices' }}</td></tr>
|
||||
</table>
|
||||
<span class="bottom-notes" translate>{{ 'app.admin.invoices.beware_if_the_number_exceed_the_specified_length_it_will_be_truncated_by_the_left' }}</span>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addOrderNumber.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>nn...nn</strong></td><td translate>{{ 'app.admin.invoices.n_digits_count_of_orders' }}</td></tr>
|
||||
<tr><td><strong>dd...dd</strong></td><td translate>{{ 'app.admin.invoices.n_digits_daily_count_of_orders' }}</td></tr>
|
||||
<tr><td><strong>mm...mm</strong></td><td translate>{{ 'app.admin.invoices.n_digits_monthly_count_of_orders' }}</td></tr>
|
||||
<tr><td><strong>yy...yy</strong></td><td translate>{{ 'app.admin.invoices.n_digits_annual_amount_of_orders' }}</td></tr>
|
||||
</table>
|
||||
<span class="bottom-notes" translate>{{ 'app.admin.invoices.beware_if_the_number_exceed_the_specified_length_it_will_be_truncated_by_the_left' }}</span>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addOnlineInfo.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>X[texte]</strong></td><td>{{ 'app.admin.invoices.add_a_notice_regarding_the_online_sales_only_if_the_invoice_is_concerned' | translate }} <mark translate>{{ 'app.admin.invoices.this_will_never_be_added_when_a_refund_notice_is_present' }}</mark> {{ 'app.admin.invoices.eg_XVL_will_add_VL_to_the_invoices_settled_with_stripe' | translate }}</td></tr>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addWalletInfo.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>W[texte]</strong></td><td>{{ 'app.admin.invoices.add_a_notice_regarding_the_wallet_only_if_the_invoice_is_concerned' | translate }} <mark translate>{{ 'app.admin.invoices.this_will_never_be_added_when_a_refund_notice_is_present' }}</mark> {{ 'app.admin.invoices.eg_WPM_will_add_PM_to_the_invoices_settled_with_wallet' | translate }}</td></tr>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addRefundInfo.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>R[texte]</strong></td><td>{{ 'app.admin.invoices.add_a_notice_regarding_refunds_only_if_the_invoice_is_concerned' | translate }}<mark translate>{{ 'app.admin.invoices.this_will_never_be_added_when_an_online_sales_notice_is_present' }}</mark> {{ 'app.admin.invoices.eg_RA_will_add_A_to_the_refund_invoices' | translate }}</td></tr>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="editCode.html">
|
||||
<div class="custom-invoice">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title" translate>{{ 'app.admin.invoices.code' }}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="enableCode" class="control-label" translate>{{ 'app.admin.invoices.enable_the_code' }}</label>
|
||||
<input bs-switch
|
||||
ng-model="isSelected"
|
||||
id="enableCode"
|
||||
type="checkbox"
|
||||
class="form-control m-l-sm"
|
||||
switch-on-text="{{ 'app.admin.invoices.enabled' | translate }}"
|
||||
switch-off-text="{{ 'app.admin.invoices.disabled' | translate }}"
|
||||
switch-animate="true"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="isSelected">
|
||||
<label for="codeModel" class="control-label" translate>{{ 'app.admin.invoices.code' }}</label>
|
||||
<input id="codeModel" type="text" ng-model="codeModel" class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
|
||||
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<script type="text/ng-template" id="editNumber.html">
|
||||
<div class="custom-invoice">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title" translate>{{ 'app.admin.invoices.order_number' }}</h3>
|
||||
</div>
|
||||
<div class="modal-body row">
|
||||
<div class="elements col-md-4">
|
||||
<h4 translate>{{ 'app.admin.invoices.elements' }}</h4>
|
||||
<ul>
|
||||
<li ng-click="invoice.number.help = 'addYear.html'">{{ 'app.admin.invoices.year' | translate }}</li>
|
||||
<li ng-click="invoice.number.help = 'addMonth.html'">{{ 'app.admin.invoices.month' | translate }}</li>
|
||||
<li ng-click="invoice.number.help = 'addDay.html'">{{ 'app.admin.invoices.day' | translate }}</li>
|
||||
<li ng-click="invoice.number.help = 'addOrderNumber.html'">{{ 'app.admin.invoices.order_num' | translate }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="model">
|
||||
<h4 translate>{{ 'app.admin.invoices.model' }}</h4>
|
||||
<input type="text" class="form-control" ng-model="model">
|
||||
</div>
|
||||
<div class="help">
|
||||
<h4 translate>{{ 'app.admin.invoices.documentation' }}</h4>
|
||||
<ng-include src="invoice.number.help" autoscroll="true">
|
||||
</ng-include>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
|
||||
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/ng-template" id="editVAT.html">
|
||||
<div class="custom-invoice">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title" translate>{{ 'app.admin.invoices.VAT' }}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="enableVAT" class="control-label" translate>{{ 'app.admin.invoices.enable_VAT' }}</label>
|
||||
<input bs-switch
|
||||
ng-model="isSelected"
|
||||
id="enableVAT"
|
||||
type="checkbox"
|
||||
class="form-control m-l-sm"
|
||||
switch-on-text="{{ 'app.admin.invoices.enabled' | translate }}"
|
||||
switch-off-text="{{ 'app.admin.invoices.disabled' | translate }}"
|
||||
switch-animate="true"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="isSelected">
|
||||
<label for="vatRate" class="control-label" translate>{{ 'app.admin.invoices.VAT_rate' }}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">% </span>
|
||||
<input id="vatRate" type="number" ng-model="rate" class="form-control" min="0" max="100"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="m-t-lg">
|
||||
<h4 translate>{{ 'app.admin.invoices.VAT_history' }}</h4>
|
||||
<table class="table scrollable-3-cols">
|
||||
<thead>
|
||||
<tr>
|
||||
<th translate>{{ 'app.admin.invoices.VAT_rate' }}</th>
|
||||
<th translate>{{ 'app.admin.invoices.changed_at' }}</th>
|
||||
<th translate>{{ 'app.admin.invoices.changed_by' }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="value in history | orderBy:'-date'">
|
||||
<td>
|
||||
<span class="no-user-label" ng-show="value.enabled === false" translate>{{'app.admin.invoices.VAT_disabled'}}</span>
|
||||
<span class="no-user-label" ng-show="value.enabled === true" translate>{{'app.admin.invoices.VAT_enabled'}}</span>
|
||||
<span ng-show="value.rate">{{value.rate}}</span>
|
||||
</td>
|
||||
<td>{{value.date | amDateFormat:'L LT'}}</td>
|
||||
<td>{{value.user.name}}<span class="no-user-label" ng-hide="value.user" translate>{{ 'app.admin.invoices.deleted_user' }}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
|
||||
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
86
app/assets/templates/admin/invoices/list.html.erb
Normal file
86
app/assets/templates/admin/invoices/list.html.erb
Normal file
@ -0,0 +1,86 @@
|
||||
<h3 class="m-t-xs"><i class="fa fa-filter"></i> {{ 'app.admin.invoices.filter_invoices' | translate }}</h3>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon" translate>{{ 'app.admin.invoices.invoice_num_' }}</span>
|
||||
<input type="text" ng-model="searchInvoice.reference" class="form-control" placeholder="" ng-change="handleFilterChange()">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon" translate>{{ 'app.admin.invoices.customer_' }}</span>
|
||||
<input type="text" ng-model="searchInvoice.name" class="form-control" placeholder="" ng-change="handleFilterChange()">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">{{ "app.admin.invoices.date_" | translate }}</span>
|
||||
<input type="date" ng-model="searchInvoice.date" class="form-control" ng-change="handleFilterChange()">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
||||
<table class="table invoices-list" ng-if="invoices.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:5%"></th>
|
||||
<th style="width:15%"><a href="" ng-click="setOrderInvoice('reference')">{{ 'app.admin.invoices.invoice_num' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderInvoice=='reference', 'fa fa-sort-numeric-desc': orderInvoice=='-reference', 'fa fa-arrows-v': orderInvoice }"></i></a></th>
|
||||
|
||||
<th style="width:20%"><a href="" ng-click="setOrderInvoice('date')">{{ 'app.admin.invoices.date' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderInvoice=='date', 'fa fa-sort-numeric-desc': orderInvoice=='-date', 'fa fa-arrows-v': orderInvoice }"></i></a></th>
|
||||
|
||||
<th style="width:10%"><a href="" ng-click="setOrderInvoice('total')"> {{ 'app.admin.invoices.price' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderInvoice=='total', 'fa fa-sort-numeric-desc': orderInvoice=='-total', 'fa fa-arrows-v': orderInvoice }"></i></a></th>
|
||||
|
||||
<th style="width:20%"><a href="" ng-click="setOrderInvoice('name')">{{ 'app.admin.invoices.customer' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderInvoice=='name', 'fa fa-sort-alpha-desc': orderInvoice=='-name', 'fa fa-arrows-v': orderInvoice }"></i></a></th>
|
||||
|
||||
<th style="width:30%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="invoice in invoices">
|
||||
<td class="chained-indicator">
|
||||
<i class="fa fa-link chained" ng-show="invoice.chained_footprint"/>
|
||||
<i class="fa fa-chain-broken broken" ng-hide="invoice.chained_footprint"/>
|
||||
</td>
|
||||
<td>{{ invoice.reference }}</td>
|
||||
<td ng-if="!invoice.is_avoir">{{ invoice.date | amDateFormat:'L LTS' }}</td>
|
||||
<td ng-if="invoice.is_avoir">{{ invoice.date | amDateFormat:'L' }}</td>
|
||||
<td>{{ invoice.total | currency}}</td>
|
||||
<td>
|
||||
<a href="" ui-sref="app.admin.members_edit({id: invoice.user_id})" ng-show="invoice.user_id">{{ invoice.name }}</a>
|
||||
<span ng-hide="invoice.user_id">{{ invoice.name }}</span>
|
||||
<td>
|
||||
<div class="buttons">
|
||||
<a class="btn btn-default download-button" ng-href="api/invoices/{{invoice.id}}/download" target="_blank" ng-if="!invoice.is_avoir">
|
||||
<i class="fa fa-file-pdf-o"></i> {{ 'app.admin.invoices.download_the_invoice' | translate }}
|
||||
</a>
|
||||
<a class="btn btn-default" ng-href="api/invoices/{{invoice.id}}/download" target="_blank" ng-if="invoice.is_avoir">
|
||||
<i class="fa fa-file-pdf-o"></i> {{ 'app.admin.invoices.download_the_credit_note' | translate }}
|
||||
</a>
|
||||
<a class="btn btn-default refund-button" ng-click="generateAvoirForInvoice(invoice)" ng-if="(!invoice.has_avoir || invoice.has_avoir == 'partial') && !invoice.is_avoir && !invoice.prevent_refund && !isDateClosed(invoice.created_at)">
|
||||
<i class="fa fa-reply"></i> {{ 'app.admin.invoices.credit_note' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="text-center">
|
||||
<button class="btn btn-warning" ng-click="showNextInvoices()" ng-hide="noMoreResults"><i class="fa fa-search-plus" aria-hidden="true"></i> {{ 'app.admin.invoices.display_more_invoices' | translate }}</button>
|
||||
</div>
|
||||
<p ng-if="invoices.length == 0" translate>{{ 'app.admin.invoices.no_invoices_for_now' }}</p>
|
||||
|
||||
</div>
|
||||
</div>
|
310
app/assets/templates/admin/invoices/settings.html.erb
Normal file
310
app/assets/templates/admin/invoices/settings.html.erb
Normal file
@ -0,0 +1,310 @@
|
||||
<div class="alert alert-warning p-md m-t" role="alert" ng-show="fablabWithoutInvoices">
|
||||
<i class="fa fa-warning m-r"></i>
|
||||
<span translate>{{ 'app.admin.invoices.warning_invoices_disabled' }}</span>
|
||||
</div>
|
||||
<form class="invoice-placeholder">
|
||||
<div class="invoice-logo">
|
||||
<img src="data:image/png;base64," data-src="holder.js/100%x100%/text:/font:FontAwesome/icon" bs-holder ng-if="!invoice.logo" class="img-responsive">
|
||||
<img base-sixty-four-image="invoice.logo" ng-if="invoice.logo && invoice.logo.base64">
|
||||
<div class="tools-box">
|
||||
<div class="btn-group">
|
||||
<div class="btn btn-default btn-file">
|
||||
<i class="fa fa-edit"></i> {{ 'app.admin.invoices.change_logo' | translate }}
|
||||
<input type="file" accept="image/png,image/jpeg,image/x-png,image/pjpeg" name="invoice[logo][attachment]" ng-model="invoice.logo" base-sixty-four-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="invoice-buyer-infos">
|
||||
<strong translate>{{ 'app.admin.invoices.john_smith' }}</strong>
|
||||
<div translate>{{ 'app.admin.invoices.john_smith_at_example_com' }}</div>
|
||||
</div>
|
||||
<div class="invoice-reference invoice-editable" ng-click="openEditReference()">{{ 'app.admin.invoices.invoice_reference_' | translate }} {{mkReference()}}</div>
|
||||
<div class="invoice-code invoice-editable" ng-show="invoice.code.active" ng-click="openEditCode()">{{ 'app.admin.invoices.code_' | translate }} {{invoice.code.model}}</div>
|
||||
<div class="invoice-code invoice-activable" ng-show="!invoice.code.active" ng-click="openEditCode()" translate>{{ 'app.admin.invoices.code_disabled' }}</div>
|
||||
<div class="invoice-order invoice-editable" ng-click="openEditInvoiceNb()"> {{ 'app.admin.invoices.order_num' | translate }} {{mkNumber()}}</div>
|
||||
<div class="invoice-date">{{ 'app.admin.invoices.invoice_issued_on_DATE_at_TIME' | translate:{DATE:(today | amDateFormat:'L'), TIME:(today | amDateFormat:'LT')} }}</div>
|
||||
<div class="invoice-object">
|
||||
{{ 'app.admin.invoices.object_reservation_of_john_smith_on_DATE_at_TIME' | translate:{DATE:(inOneWeek | amDateFormat:'L'), TIME:(inOneWeek | amDateFormat:'LT')} }}
|
||||
</div>
|
||||
<div class="invoice-data">
|
||||
{{ 'app.admin.invoices.order_summary' | translate }}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th translate>{{ 'app.admin.invoices.details' }}</th>
|
||||
<th class="right" translate>{{ 'app.admin.invoices.amount' }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ 'app.admin.invoices.machine_booking-3D_printer' | translate }} {{inOneWeek | amDateFormat:'LLL'}} - {{inOneWeekAndOneHour | amDateFormat:'LT'}}</td>
|
||||
<td class="right">{{30.0 | currency}}</td>
|
||||
</tr>
|
||||
|
||||
<tr class="invoice-total" ng-class="{'bold vat-line':invoice.VAT.active}">
|
||||
<td ng-show="!invoice.VAT.active" translate>{{ 'app.admin.invoices.total_amount' }}</td>
|
||||
<td ng-show="invoice.VAT.active" translate>{{ 'app.admin.invoices.total_including_all_taxes' }}</td>
|
||||
<td class="right">{{30.0 | currency}}</td>
|
||||
</tr>
|
||||
|
||||
<tr class="invoice-vat invoice-activable" ng-click="openEditVAT()" ng-show="!invoice.VAT.active">
|
||||
<td translate>{{ 'app.admin.invoices.VAT_disabled' }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
|
||||
<tr class="invoice-vat invoice-editable vat-line italic" ng-click="openEditVAT()" ng-show="invoice.VAT.active">
|
||||
<td>{{ 'app.admin.invoices.including_VAT' | translate }} {{invoice.VAT.rate}} %</td>
|
||||
<td>{{30-(30/(invoice.VAT.rate/100+1)) | currency}}</td>
|
||||
</tr>
|
||||
<tr class="invoice-ht vat-line italic" ng-show="invoice.VAT.active">
|
||||
<td translate>{{ 'app.admin.invoices.including_total_excluding_taxes' }}</td>
|
||||
<td>{{30/(invoice.VAT.rate/100+1) | currency}}</td>
|
||||
</tr>
|
||||
<tr class="invoice-payed vat-line bold" ng-show="invoice.VAT.active">
|
||||
<td translate>{{ 'app.admin.invoices.including_amount_payed_on_ordering' }}</td>
|
||||
<td>{{30.0 | currency}}</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="invoice-payment" translate translate-values="{DATE:(today | amDateFormat:'L'), TIME:(today | amDateFormat:'LT'), AMOUNT:(30.0 | currency)}">
|
||||
{{ 'app.admin.invoices.settlement_by_debit_card_on_DATE_at_TIME_for_an_amount_of_AMOUNT' }}
|
||||
</p>
|
||||
</div>
|
||||
<div medium-editor class="invoice-text invoice-editable" ng-model="invoice.text.content"
|
||||
options='{
|
||||
"placeholder": "{{ "app.admin.invoices.important_notes" | translate }}",
|
||||
"buttons": ["underline"]
|
||||
}'
|
||||
ng-blur="textEditEnd($event)">
|
||||
</div>
|
||||
<div medium-editor class="invoice-legals invoice-editable" ng-model="invoice.legals.content"
|
||||
options='{
|
||||
"placeholder": "{{ "app.admin.invoices.address_and_legal_information" | translate }}",
|
||||
"buttons": ["bold", "underline"]
|
||||
}'
|
||||
ng-blur="legalsEditEnd($event)">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<script type="text/ng-template" id="editReference.html">
|
||||
<div class="custom-invoice">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title" translate>{{ 'app.admin.invoices.invoice_reference' }}</h3>
|
||||
</div>
|
||||
<div class="modal-body row">
|
||||
<div class="elements col-md-4">
|
||||
<h4>Éléments</h4>
|
||||
<ul>
|
||||
<li ng-click="invoice.reference.help = 'addYear.html'">{{ 'app.admin.invoices.year' | translate }}</li>
|
||||
<li ng-click="invoice.reference.help = 'addMonth.html'">{{ 'app.admin.invoices.month' | translate }}</li>
|
||||
<li ng-click="invoice.reference.help = 'addDay.html'">{{ 'app.admin.invoices.day' | translate }}</li>
|
||||
<li ng-click="invoice.reference.help = 'addInvoiceNumber.html'">{{ 'app.admin.invoices.num_of_invoice' | translate }}</li>
|
||||
<li ng-click="invoice.reference.help = 'addOnlineInfo.html'">{{ 'app.admin.invoices.online_sales' | translate }}</li>
|
||||
<%# <li ng-click="invoice.reference.help = 'addWalletInfo.html'">{{ 'app.admin.invoices.wallet' | translate }}</li> %>
|
||||
<li ng-click="invoice.reference.help = 'addRefundInfo.html'">{{ 'app.admin.invoices.refund' | translate }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="model">
|
||||
<h4 translate>{{ 'app.admin.invoices.model' }}</h4>
|
||||
<input type="text" class="form-control" ng-model="model">
|
||||
</div>
|
||||
<div class="help">
|
||||
<h4 translate>{{ 'app.admin.invoices.documentation' }}</h4>
|
||||
<ng-include src="invoice.reference.help" autoscroll="true">
|
||||
</ng-include>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
|
||||
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addYear.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>YY</strong></td><td translate>{{ 'app.admin.invoices.2_digits_year' }}</td></tr>
|
||||
<tr><td><strong>YYYY</strong></td><td translate>{{ 'app.admin.invoices.4_digits_year' }}</td></tr>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addMonth.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>M</strong></td><td translate>{{ 'app.admin.invoices.month_number' }}</td></tr>
|
||||
<tr><td><strong>MM</strong></td><td translate>{{ 'app.admin.invoices.2_digits_month_number' }}</td></tr>
|
||||
<tr><td><strong>MMM</strong></td><td translate>{{ 'app.admin.invoices.3_characters_month_name' }}</td></tr>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addDay.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>D</strong></td><td translate>{{ 'app.admin.invoices.day_in_the_month' }}</td></tr>
|
||||
<tr><td><strong>DD</strong></td><td translate>{{ 'app.admin.invoices.2_digits_day_in_the_month' }}</td></tr>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addInvoiceNumber.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>dd...dd</strong></td><td translate>{{ 'app.admin.invoices.n_digits_daily_count_of_invoices' }}</td></tr>
|
||||
<tr><td><strong>mm...mm</strong></td><td translate>{{ 'app.admin.invoices.n_digits_monthly_count_of_invoices' }}</td></tr>
|
||||
<tr><td><strong>yy...yy</strong></td><td translate>{{ 'app.admin.invoices.n_digits_annual_amount_of_invoices' }}</td></tr>
|
||||
</table>
|
||||
<span class="bottom-notes" translate>{{ 'app.admin.invoices.beware_if_the_number_exceed_the_specified_length_it_will_be_truncated_by_the_left' }}</span>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addOrderNumber.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>nn...nn</strong></td><td translate>{{ 'app.admin.invoices.n_digits_count_of_orders' }}</td></tr>
|
||||
<tr><td><strong>dd...dd</strong></td><td translate>{{ 'app.admin.invoices.n_digits_daily_count_of_orders' }}</td></tr>
|
||||
<tr><td><strong>mm...mm</strong></td><td translate>{{ 'app.admin.invoices.n_digits_monthly_count_of_orders' }}</td></tr>
|
||||
<tr><td><strong>yy...yy</strong></td><td translate>{{ 'app.admin.invoices.n_digits_annual_amount_of_orders' }}</td></tr>
|
||||
</table>
|
||||
<span class="bottom-notes" translate>{{ 'app.admin.invoices.beware_if_the_number_exceed_the_specified_length_it_will_be_truncated_by_the_left' }}</span>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addOnlineInfo.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>X[texte]</strong></td><td>{{ 'app.admin.invoices.add_a_notice_regarding_the_online_sales_only_if_the_invoice_is_concerned' | translate }} <mark translate>{{ 'app.admin.invoices.this_will_never_be_added_when_a_refund_notice_is_present' }}</mark> {{ 'app.admin.invoices.eg_XVL_will_add_VL_to_the_invoices_settled_with_stripe' | translate }}</td></tr>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addWalletInfo.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>W[texte]</strong></td><td>{{ 'app.admin.invoices.add_a_notice_regarding_the_wallet_only_if_the_invoice_is_concerned' | translate }} <mark translate>{{ 'app.admin.invoices.this_will_never_be_added_when_a_refund_notice_is_present' }}</mark> {{ 'app.admin.invoices.eg_WPM_will_add_PM_to_the_invoices_settled_with_wallet' | translate }}</td></tr>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addRefundInfo.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>R[texte]</strong></td><td>{{ 'app.admin.invoices.add_a_notice_regarding_refunds_only_if_the_invoice_is_concerned' | translate }}<mark translate>{{ 'app.admin.invoices.this_will_never_be_added_when_an_online_sales_notice_is_present' }}</mark> {{ 'app.admin.invoices.eg_RA_will_add_A_to_the_refund_invoices' | translate }}</td></tr>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="editCode.html">
|
||||
<div class="custom-invoice">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title" translate>{{ 'app.admin.invoices.code' }}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="enableCode" class="control-label" translate>{{ 'app.admin.invoices.enable_the_code' }}</label>
|
||||
<input bs-switch
|
||||
ng-model="isSelected"
|
||||
id="enableCode"
|
||||
type="checkbox"
|
||||
class="form-control m-l-sm"
|
||||
switch-on-text="{{ 'app.admin.invoices.enabled' | translate }}"
|
||||
switch-off-text="{{ 'app.admin.invoices.disabled' | translate }}"
|
||||
switch-animate="true"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="isSelected">
|
||||
<label for="codeModel" class="control-label" translate>{{ 'app.admin.invoices.code' }}</label>
|
||||
<input id="codeModel" type="text" ng-model="codeModel" class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
|
||||
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<script type="text/ng-template" id="editNumber.html">
|
||||
<div class="custom-invoice">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title" translate>{{ 'app.admin.invoices.order_number' }}</h3>
|
||||
</div>
|
||||
<div class="modal-body row">
|
||||
<div class="elements col-md-4">
|
||||
<h4 translate>{{ 'app.admin.invoices.elements' }}</h4>
|
||||
<ul>
|
||||
<li ng-click="invoice.number.help = 'addYear.html'">{{ 'app.admin.invoices.year' | translate }}</li>
|
||||
<li ng-click="invoice.number.help = 'addMonth.html'">{{ 'app.admin.invoices.month' | translate }}</li>
|
||||
<li ng-click="invoice.number.help = 'addDay.html'">{{ 'app.admin.invoices.day' | translate }}</li>
|
||||
<li ng-click="invoice.number.help = 'addOrderNumber.html'">{{ 'app.admin.invoices.order_num' | translate }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="model">
|
||||
<h4 translate>{{ 'app.admin.invoices.model' }}</h4>
|
||||
<input type="text" class="form-control" ng-model="model">
|
||||
</div>
|
||||
<div class="help">
|
||||
<h4 translate>{{ 'app.admin.invoices.documentation' }}</h4>
|
||||
<ng-include src="invoice.number.help" autoscroll="true">
|
||||
</ng-include>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
|
||||
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/ng-template" id="editVAT.html">
|
||||
<div class="custom-invoice">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title" translate>{{ 'app.admin.invoices.VAT' }}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="enableVAT" class="control-label" translate>{{ 'app.admin.invoices.enable_VAT' }}</label>
|
||||
<input bs-switch
|
||||
ng-model="isSelected"
|
||||
id="enableVAT"
|
||||
type="checkbox"
|
||||
class="form-control m-l-sm"
|
||||
switch-on-text="{{ 'app.admin.invoices.enabled' | translate }}"
|
||||
switch-off-text="{{ 'app.admin.invoices.disabled' | translate }}"
|
||||
switch-animate="true"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="isSelected">
|
||||
<label for="vatRate" class="control-label" translate>{{ 'app.admin.invoices.VAT_rate' }}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">% </span>
|
||||
<input id="vatRate" type="number" ng-model="rate" class="form-control" min="0" max="100"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="m-t-lg">
|
||||
<h4 translate>{{ 'app.admin.invoices.VAT_history' }}</h4>
|
||||
<table class="table scrollable-3-cols">
|
||||
<thead>
|
||||
<tr>
|
||||
<th translate>{{ 'app.admin.invoices.VAT_rate' }}</th>
|
||||
<th translate>{{ 'app.admin.invoices.changed_at' }}</th>
|
||||
<th translate>{{ 'app.admin.invoices.changed_by' }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="value in history | orderBy:'-date'">
|
||||
<td>
|
||||
<span class="no-user-label" ng-show="value.enabled === false" translate>{{'app.admin.invoices.VAT_disabled'}}</span>
|
||||
<span class="no-user-label" ng-show="value.enabled === true" translate>{{'app.admin.invoices.VAT_enabled'}}</span>
|
||||
<span ng-show="value.rate">{{value.rate}}</span>
|
||||
</td>
|
||||
<td>{{value.date | amDateFormat:'L LT'}}</td>
|
||||
<td>{{value.user.name}}<span class="no-user-label" ng-hide="value.user" translate>{{ 'app.admin.invoices.deleted_user' }}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-warning" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
|
||||
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
34
app/assets/templates/admin/managers/new.html.erb
Normal file
34
app/assets/templates/admin/managers/new.html.erb
Normal file
@ -0,0 +1,34 @@
|
||||
<section class="heading b-b">
|
||||
<div class="row no-gutter">
|
||||
<div class="col-xs-2 col-sm-2 col-md-1">
|
||||
<section class="heading-btn">
|
||||
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
|
||||
<section class="heading-title">
|
||||
<h1 translate>{{ 'app.admin.manager_new.add_a_manager' }}</h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="row no-gutter">
|
||||
<div class=" col-sm-12 col-md-9 b-r nopadding">
|
||||
|
||||
<form role="form" name="managerForm" class="form-horizontal" novalidate>
|
||||
<section class="panel panel-default bg-light m-lg">
|
||||
<div class="panel-body m-r">
|
||||
<ng-include src="'<%= asset_path "shared/_manager_form.html" %>'"></ng-include>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="panel-footer no-padder">
|
||||
<input type="submit" value="{{ 'app.shared.buttons.save' | translate}}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-click="saveManager()" ng-disabled="managerForm.$invalid"/>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
@ -7,7 +7,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.admins_new" translate>{{ 'app.admin.members.add_a_new_administrator' }}</button>
|
||||
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.admins_new" ng-show="isAuthorized('admin')" translate>
|
||||
{{ 'app.admin.members.add_a_new_administrator' }}
|
||||
</button>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
@ -29,7 +31,7 @@
|
||||
<td>{{ admin.email }}</td>
|
||||
<td>{{ admin.profile_attributes.phone }}</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" ng-if="admin.id != currentUser.id" ng-click="destroyAdmin(admins, admin)">
|
||||
<button class="btn btn-danger" ng-if="isAuthorized('admin') && admin.id != currentUser.id" ng-click="destroyAdmin(admins, admin)">
|
||||
<i class="fa fa-trash-o"></i>
|
||||
</button>
|
||||
</td>
|
||||
|
@ -76,8 +76,13 @@
|
||||
<p>
|
||||
{{ 'app.admin.members_edit.price_' | translate }} {{ subscription.plan.amount | currency}}
|
||||
</p>
|
||||
<button class="btn btn-default" ng-click="updateSubscriptionModal(subscription, true)" translate>{{ 'app.admin.members_edit.offer_free_days' }}</button>
|
||||
<button class="btn btn-default" ng-click="updateSubscriptionModal(subscription, false)" translate>{{ 'app.admin.members_edit.extend_subscription' }}</button>
|
||||
<div ng-hide="user.id === currentUser.id">
|
||||
<button class="btn btn-default" ng-click="updateSubscriptionModal(subscription, true)" translate>{{ 'app.admin.members_edit.offer_free_days' }}</button>
|
||||
<button class="btn btn-default" ng-click="updateSubscriptionModal(subscription, false)" translate>{{ 'app.admin.members_edit.extend_subscription' }}</button>
|
||||
</div>
|
||||
<p class="alert alert-info" ng-show="user.id === currentUser.id" translate>
|
||||
{{ 'app.admin.members_edit.cannot_extend_own_subscription' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@ -229,9 +234,12 @@
|
||||
<ng-include src="'<%= asset_path "wallet/show.html" %>'"></ng-include>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
<div class="col-sm-4 text-center">
|
||||
<div class="col-sm-4 text-center" ng-hide="user.id === currentUser.id">
|
||||
<button type="button" class="btn btn-warning m-t m-b" ng-click="createWalletCreditModal(user, wallet)" translate>{{ 'app.admin.members_edit.to_credit' }}</button>
|
||||
</div>
|
||||
<p class="col-sm-4 alert alert-info" ng-show="user.id === currentUser.id" translate>
|
||||
{{ 'app.admin.members_edit.cannot_credit_own_wallet' }}
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-xs-1 col-xs-offset-1 col-md-offset-2 b-l">
|
||||
<section class="heading-actions wrapper">
|
||||
<section class="heading-actions wrapper" ng-show="isAuthorized('admin')">
|
||||
<a role="button" class="btn btn-default b-2x rounded m-t-sm import-members" ui-sref="app.admin.members_import">
|
||||
<i class="fa fa-cloud-upload"></i>
|
||||
</a>
|
||||
@ -30,7 +30,7 @@
|
||||
post-render="setupMembersTour">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<uib-tabset justified="true" active="tabs.active">
|
||||
<uib-tabset justified="true" active="tabs.active" ng-if="isAuthorized('admin')">
|
||||
|
||||
<uib-tab heading="{{ 'app.admin.members.users' | translate }}" index="0">
|
||||
<ng-include src="'<%= asset_path "admin/members/users.html" %>'"></ng-include>
|
||||
@ -48,6 +48,10 @@
|
||||
<div ui-view="authentification"></div>
|
||||
</uib-tab>
|
||||
</uib-tabset>
|
||||
|
||||
<div ng-if="isAuthorized('manager')">
|
||||
<ng-include src="'<%= asset_path "admin/members/users.html" %>'"></ng-include>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -1,35 +1,44 @@
|
||||
<p class="alert alert-info m-t-lg" translate>
|
||||
{{ 'app.admin.members.managers_info' }}
|
||||
</p>
|
||||
|
||||
<div class="col-md-5 m-t-lg">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-filter"></i></span>
|
||||
<input type="text" ng-model="searchFilter" class="form-control" placeholder="{{ 'app.admin.members.search_for_an_administrator' | translate }}">
|
||||
<input type="text" ng-model="searchFilter" class="form-control" placeholder="{{ 'app.admin.members.search_for_a_manager' | translate }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.admins_new" translate>{{ 'app.admin.members.add_a_new_administrator' }}</button>
|
||||
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.managers_new" ng-show="isAuthorized('admin')" translate>
|
||||
{{ 'app.admin.members.add_a_new_manager' }}
|
||||
</button>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:15%"><a href="" ng-click="setOrderAdmin('profile_attributes.last_name')">{{ 'app.admin.members.surname' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='profile_attributes.last_name', 'fa fa-sort-alpha-desc': orderAdmin =='-profile_attributes.last_name', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
|
||||
<th style="width:15%"><a href="" ng-click="setOrderManager('last_name')">{{ 'app.admin.members.surname' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='last_name', 'fa fa-sort-alpha-desc': orderAdmin =='-last_name', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
|
||||
|
||||
<th style="width:15%"><a href="" ng-click="setOrderAdmin('profile_attributes.first_name')">{{ 'app.admin.members.first_name' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='profile_attributes.first_name', 'fa fa-sort-alpha-desc': orderAdmin =='-profile_attributes.first_name', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
|
||||
<th style="width:15%"><a href="" ng-click="setOrderManager('first_name')">{{ 'app.admin.members.first_name' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='first_name', 'fa fa-sort-alpha-desc': orderAdmin =='-first_name', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
|
||||
|
||||
<th style="width:15%"><a href="" ng-click="setOrderAdmin('email')">{{ 'app.admin.members.email' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='email', 'fa fa-sort-alpha-desc': orderAdmin =='-email', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
|
||||
<th style="width:15%"><a href="" ng-click="setOrderManager('email')">{{ 'app.admin.members.email' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='email', 'fa fa-sort-alpha-desc': orderAdmin =='-email', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
|
||||
|
||||
<th style="width:10%"><a href="" ng-click="setOrderAdmin('profile_attributes.phone')">{{ 'app.admin.members.phone' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderAdmin =='profile_attributes.phone', 'fa fa-sort-numeric-desc': orderAdmin =='-profile_attributes.phone', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
|
||||
<th style="width:10%"><a href="" ng-click="setOrderManager('phone')">{{ 'app.admin.members.phone' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderAdmin =='profile_attributes.phone', 'fa fa-sort-numeric-desc': orderAdmin =='-profile_attributes.phone', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
|
||||
<th style="width:10%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="admin in admins | filter:searchFilter | orderBy: orderAdmin">
|
||||
<td class="text-c">{{ admin.profile_attributes.last_name }}</td>
|
||||
<td class="text-c">{{ admin.profile_attributes.first_name }}</td>
|
||||
<td>{{ admin.email }}</td>
|
||||
<td>{{ admin.profile_attributes.phone }}</td>
|
||||
<tr ng-repeat="manager in managers | filter:searchFilter | orderBy: orderManager">
|
||||
<td class="text-c">{{ manager.last_name }}</td>
|
||||
<td class="text-c">{{ manager.first_name }}</td>
|
||||
<td>{{ manager.email }}</td>
|
||||
<td>{{ manager.phone }}</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" ng-if="admin.id != currentUser.id" ng-click="destroyAdmin(admins, admin)">
|
||||
<button class="btn btn-default edit-member" ui-sref="app.admin.members_edit({id: manager.id})">
|
||||
<i class="fa fa-edit"></i>
|
||||
</button>
|
||||
<button class="btn btn-danger" ng-if="isAuthorized('admin')" ng-click="destroyManager(managers, manager)">
|
||||
<i class="fa fa-trash-o"></i>
|
||||
</button>
|
||||
</td>
|
||||
|
@ -18,8 +18,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.members_new" translate>{{ 'app.admin.members.add_a_new_member' }}</button>
|
||||
<div class="pull-right exports-buttons">
|
||||
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.members_new" translate>
|
||||
{{ 'app.admin.members.add_a_new_member' }}
|
||||
</button>
|
||||
<div class="pull-right exports-buttons" ng-show="isAuthorized('admin')">
|
||||
<a class="btn btn-default" ng-href="api/members/export_members.xlsx" target="export-frame" ng-click="alertExport('members')">
|
||||
<i class="fa fa-file-excel-o"></i> {{ 'app.admin.members.members' | translate }}
|
||||
</a>
|
||||
@ -57,7 +59,7 @@
|
||||
<button class="btn btn-default edit-member" ui-sref="app.admin.members_edit({id: m.id})">
|
||||
<i class="fa fa-edit"></i>
|
||||
</button>
|
||||
<button class="btn btn-danger delete-member" ng-click="deleteMember(m.id)">
|
||||
<button class="btn btn-danger delete-member" ng-click="deleteMember(m.id)" ng-show="isAuthorized('admin')">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
<span class="label label-danger text-white" ng-show="m.need_completion" translate>{{ 'app.shared.user_admin.incomplete_profile' }}</span>
|
||||
|
@ -33,7 +33,7 @@
|
||||
<td>{{ partner.email }}</td>
|
||||
<td><a ui-sref="app.admin.plans.edit({id:partner.resource.id})">{{ partner.resource ? partner.resource.base_name : '' }}</a></td>
|
||||
<td>
|
||||
<button class="btn btn-danger" ng-if="partner.id != currentUser.id" ng-click="destroyPartner(partners, partner)">
|
||||
<button class="btn btn-danger" ng-if="!partner.resource" ng-click="destroyPartner(partners, partner)">
|
||||
<i class="fa fa-trash-o"></i>
|
||||
</button>
|
||||
</td>
|
||||
|
@ -1,18 +1,18 @@
|
||||
<uib-tabset justified="true" active="tabs.sub" class="m-t">
|
||||
|
||||
<uib-tab classes="level-2-tab" heading="{{ 'app.admin.members.members' | translate }}" index="0">
|
||||
<uib-tab classes="{{isAuthorized('admin') ? 'level-2-tab' : ''}}" heading="{{ 'app.admin.members.members' | translate }}" index="0">
|
||||
<ng-include src="'<%= asset_path "admin/members/members.html" %>'"></ng-include>
|
||||
</uib-tab>
|
||||
|
||||
<uib-tab classes="level-2-tab" heading="{{ 'app.admin.members.administrators' | translate }}" class="admins-tab" index="1">
|
||||
<uib-tab classes="{{isAuthorized('admin') ? 'level-2-tab' : ''}}" heading="{{ 'app.admin.members.administrators' | translate }}" class="admins-tab" index="1">
|
||||
<ng-include src="'<%= asset_path "admin/members/administrators.html" %>'"></ng-include>
|
||||
</uib-tab>
|
||||
|
||||
<!--<uib-tab classes="level-2-tab" heading="{{ 'app.admin.members.managers' | translate }}" class="admins-tab" index="2">
|
||||
<uib-tab classes="{{isAuthorized('admin') ? 'level-2-tab' : ''}}" heading="{{ 'app.admin.members.managers' | translate }}" class="admins-tab" index="2">
|
||||
<ng-include src="'<%= asset_path "admin/members/managers.html" %>'"></ng-include>
|
||||
</uib-tab>-->
|
||||
</uib-tab>
|
||||
|
||||
<uib-tab classes="level-2-tab" heading="{{ 'app.admin.members.partners' | translate }}" class="admins-tab" index="3">
|
||||
<uib-tab classes="{{isAuthorized('admin') ? 'level-2-tab' : ''}}" heading="{{ 'app.admin.members.partners' | translate }}" class="admins-tab" index="3" ng-show="isAuthorized('admin')">
|
||||
<ng-include src="'<%= asset_path "admin/members/partners.html" %>'"></ng-include>
|
||||
</uib-tab>
|
||||
|
||||
|
@ -35,7 +35,7 @@
|
||||
ng-disabled="method == 'PATCH'">
|
||||
<option value="all" translate>{{ 'app.shared.plan.transversal_all_groups' }}</option>
|
||||
<optgroup label="Groupes">
|
||||
<option ng-repeat="group in groups" value="{{group.id}}" ng-selected="plan.group_id == group.id">{{group.name}}</option>
|
||||
<option ng-repeat="group in groups" ng-value="group.id" ng-selected="plan.group_id == group.id">{{group.name}}</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<span class="help-block" ng-show="planForm['plan[group_id]'].$dirty && planForm['plan[group_id]'].$error.required" translate>{{ 'app.shared.plan.group_is_required' }}</span>
|
||||
|
@ -17,8 +17,6 @@
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
@ -35,7 +35,7 @@
|
||||
<uib-tabset justified="true" active="tabs.active">
|
||||
<uib-tab heading="{{ 'app.admin.trainings.trainings' | translate }}" index="0" class="manage-trainings">
|
||||
<div class="m-t m-b">
|
||||
<button type="button" class="btn btn-warning" ui-sref="app.admin.trainings_new">
|
||||
<button type="button" class="btn btn-warning" ui-sref="app.admin.trainings_new" ng-show="isAuthorized('admin')">
|
||||
<i class="fa fa-plus m-r"></i>
|
||||
<span translate>{{ 'app.admin.trainings.add_a_new_training' }}</span>
|
||||
</button>
|
||||
@ -64,7 +64,7 @@
|
||||
<td>{{ showMachines(training) }}</td>
|
||||
<td>{{ training.nb_total_places }}</td>
|
||||
<td>
|
||||
<div class="buttons">
|
||||
<div class="buttons" ng-show="isAuthorized('admin')">
|
||||
<button class="btn btn-default" ui-sref="app.admin.trainings_edit({id:training.id})">
|
||||
<i class="fa fa-edit"></i> {{ 'app.shared.buttons.edit' | translate }}
|
||||
</button>
|
||||
|
@ -1,5 +1,4 @@
|
||||
<div>
|
||||
|
||||
<section class="heading">
|
||||
<div class="row no-gutter">
|
||||
<ng-include src="'<%= asset_path "dashboard/nav.html" %>'"></ng-include>
|
||||
@ -7,7 +6,6 @@
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<div class="row no-gutter">
|
||||
<div class="col-md-12 m m-t-lg">
|
||||
<ng-include src="'<%= asset_path "wallet/show.html" %>'"></ng-include>
|
||||
@ -17,5 +15,4 @@
|
||||
<ng-include src="'<%= asset_path "wallet/transactions.html" %>'"></ng-include>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -67,7 +67,7 @@
|
||||
<span class="text-black-light text-xs" ng-if="event.nb_free_places > 0">{{event.nb_free_places}} {{ 'app.public.events_list.still_available' | translate }}</span>
|
||||
<span class="text-black-light text-xs" ng-if="event.nb_total_places > 0 && event.nb_free_places <= 0" translate>{{ 'app.public.events_list.sold_out' }}</span>
|
||||
<span class="text-black-light text-xs" ng-if="event.nb_total_places == -1" translate>{{ 'app.public.events_list.cancelled' }}</span>
|
||||
<span class="text-black-light text-xs" ng-if="!event.nb_total_places" translate>{{ 'app.public.events_list.free_entry' }}</span>
|
||||
<span class="text-black-light text-xs" ng-if="!event.nb_total_places" translate>{{ 'app.public.events_list.without_reservation' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -14,11 +14,11 @@
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md" ng-if="isAuthorized('admin')">
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md" ng-if="isAuthorized(['admin', 'manager'])">
|
||||
<section class="heading-actions wrapper">
|
||||
|
||||
<a ui-sref="app.admin.events_edit({id: event.id})" ng-if="isAuthorized('admin')" class="btn btn-lg btn-warning bg-white b-2x rounded m-t-xs text-u-c text-sm"><i class="fa fa-edit"></i> {{ 'app.shared.buttons.edit' | translate }}</a>
|
||||
<a ng-click="deleteEvent(event)" ng-if="isAuthorized('admin')" class="btn btn-lg btn-danger b-2x rounded no-b m-t-xs"><i class="fa fa-trash-o"></i></a>
|
||||
<a ui-sref="app.admin.events_edit({id: event.id})" class="btn btn-lg btn-warning bg-white b-2x rounded m-t-xs text-u-c text-sm"><i class="fa fa-edit"></i> {{ 'app.shared.buttons.edit' | translate }}</a>
|
||||
<a ng-click="deleteEvent(event)" class="btn btn-lg btn-danger b-2x rounded no-b m-t-xs"><i class="fa fa-trash-o"></i></a>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
@ -62,7 +62,7 @@
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<div ng-if="currentUser.role === 'admin'">
|
||||
<div ng-if="isAuthorized(['admin', 'manager'])">
|
||||
<select-member></select-member>
|
||||
</div>
|
||||
|
||||
@ -102,7 +102,7 @@
|
||||
<div ng-if="event.nb_total_places == -1"><span class="badge font-sbold" translate>{{ 'app.public.events_show.cancelled' }}</span></div>
|
||||
</div>
|
||||
<div class="text-sm m-b" ng-if="!event.nb_total_places">
|
||||
<div><span class="badge font-sbold" translate>{{ 'app.public.events_show.free_entry' }}</span></div>
|
||||
<div><span class="badge font-sbold" translate>{{ 'app.public.events_show.without_reservation' }}</span></div>
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -42,7 +42,7 @@
|
||||
<i class="fa fa-user red col-xs-3 padder-icon"></i>
|
||||
<h6 class="m-n col-xs-9 ">
|
||||
<span ng-if="event.nb_free_places > 0">{{ 'app.public.home.still_available' | translate }} {{event.nb_free_places}}</span>
|
||||
<span ng-if="!event.nb_total_places" translate>{{ 'app.public.home.free_entry' }}</span>
|
||||
<span ng-if="!event.nb_total_places" translate>{{ 'app.public.home.without_reservation' }}</span>
|
||||
<span ng-if="event.nb_total_places > 0 && event.nb_free_places <= 0" translate>{{ 'app.public.home.event_full' }}</span>
|
||||
</h6>
|
||||
</div>
|
||||
|
@ -37,7 +37,7 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-md-offset-6 m-t-n row-centered" ng-if="isAuthorized('admin')">
|
||||
<div class="col-md-3 col-md-offset-6 m-t-n row-centered" ng-if="isAuthorized(['admin', 'manager'])">
|
||||
<a role="button" ui-sref="app.admin.calendar" class="btn btn-lg btn-default rounded m-t-sm text-sm">
|
||||
<i class="fa fa-calendar-check-o m-r" aria-hidden="true"></i><span translate>{{ 'app.public.machines_list.new_availability' }}</span>
|
||||
</a>
|
||||
@ -59,7 +59,7 @@
|
||||
|
||||
<div class="text-center clearfix">
|
||||
<div class="col-sm-6 b-r no-padder">
|
||||
<div class="btn btn-default btn-block no-b padder-v red" ng-click="reserveMachine(machine, $event)" ng-hide="machine.disabled">
|
||||
<div class="btn btn-default btn-block no-b padder-v red reserve-button" ng-click="reserveMachine(machine, $event)" ng-hide="machine.disabled">
|
||||
<i class="fa fa-bookmark m-r-xs"></i>
|
||||
<span class="hidden-sm" translate>{{ 'app.public.machines_list.book' }}</span>
|
||||
</div>
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
<div class="col-sm-12 col-md-12 col-lg-3">
|
||||
|
||||
<div ng-if="currentUser.role === 'admin'">
|
||||
<div ng-if="isAuthorized(['admin', 'manager'])">
|
||||
<select-member></select-member>
|
||||
</div>
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
<div class="row no-gutter">
|
||||
<div class="col-sm-12 col-md-9 b-r">
|
||||
|
||||
<div class="row m-t m-b padder" ng-repeat="plansGroup in plansClassifiedByGroup | groupFilter:ctrl.member">
|
||||
<div class="row m-t m-b padder" ng-repeat="plansGroup in plansClassifiedByGroup | groupFilter:ctrl.member" ng-show="plansGroup.actives > 0">
|
||||
|
||||
<div class="col-md-12 text-center">
|
||||
<h2 class="text-u-c">{{plansGroup.name}}</h2>
|
||||
@ -36,25 +36,24 @@
|
||||
<div class="wrap">
|
||||
<div class="price">
|
||||
<div class="amount" data-fittext>{{plan.amount | currency}}</div>
|
||||
<span class="period">{{ plan.interval | planIntervalFilter: plan.interval_count }}</span>
|
||||
<span class="period">{{ plan.interval | planIntervalFilter: plan.interval_count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cta-button" ng-if="!currentUser || currentUser.role == 'member'">
|
||||
<button class="btn btn-default rounded" ng-click="selectPlan(plan)" ng-if="currentUser.subscribed_plan.id != plan.id" ng-disabled="currentUser.subscribed_plan" ng-class="{ 'bg-yellow': selectedPlan==plan }">
|
||||
|
||||
<span ng-if="currentUser" translate>{{ 'app.public.plans.i_choose_that_plan' }}</span>
|
||||
|
||||
<span ng-if="!currentUser" translate>{{ 'app.public.plans.i_subscribe_online' }}</span>
|
||||
|
||||
<div class="cta-button" ng-if="!ctrl.member || ctrl.member.role == 'member' || (ctrl.member.role == 'manager' && ctrl.member.id === currentUser.id)">
|
||||
<button class="btn btn-default rounded" ng-click="selectPlan(plan)" ng-if="ctrl.member.subscribed_plan.id != plan.id" ng-disabled="ctrl.member.subscribed_plan" ng-class="{ 'bg-yellow': selectedPlan==plan }">
|
||||
<span ng-if="ctrl.member" translate>{{ 'app.public.plans.i_choose_that_plan' }}</span>
|
||||
<span ng-if="!ctrl.member" translate>{{ 'app.public.plans.i_subscribe_online' }}</span>
|
||||
</button>
|
||||
|
||||
<button class="btn btn-warning bg-yellow rounded" ng-if="currentUser.subscribed_plan.id == plan.id" ng-disabled="currentUser.subscribed_plan.id == plan.id" translate>{{ 'app.public.plans.i_already_subscribed' }}</button>
|
||||
<button class="btn btn-warning bg-yellow rounded" ng-if="ctrl.member.subscribed_plan.id == plan.id" ng-disabled="ctrl.member.subscribed_plan.id == plan.id" translate>
|
||||
{{ 'app.public.plans.i_already_subscribed' }}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="cta-button" ng-if="currentUser.role == 'admin'">
|
||||
<div class="cta-button" ng-if="isAuthorized('admin') || (ctrl.member.role == 'manager' && ctrl.member.id != currentUser.id)">
|
||||
<button class="btn btn-default rounded" ng-click="selectPlan(plan)" ng-class="{ 'bg-yellow': selectedPlan==plan }" ng-disabled="!ctrl.member">
|
||||
<span translate>{{ 'app.public.plans.i_choose_that_plan' }}</span>
|
||||
</button>
|
||||
@ -67,9 +66,9 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-xs-12 col-md-12 col-lg-10 col-centered no-gutter" ng-if="currentUser.subscription && isInPast(currentUser.subscription.expired_at)">
|
||||
<div class="col-xs-12 col-md-12 col-lg-10 col-centered no-gutter" ng-if="ctrl.member.subscription && isInPast(ctrl.member.subscription.expired_at)">
|
||||
<uib-alert type="info">
|
||||
{{ 'app.public.plans.your_subscription_expires_on_the_DATE' | translate:{DATE:(currentUser.subscription.expired_at | amDateFormat:'L' )} }}
|
||||
{{ 'app.public.plans.your_subscription_expires_on_the_DATE' | translate:{DATE:(ctrl.member.subscription.expired_at | amDateFormat:'L' )} }}
|
||||
</uib-alert>
|
||||
</div>
|
||||
|
||||
@ -81,14 +80,14 @@
|
||||
|
||||
<div class="col-sm-12 col-md-12 col-lg-3">
|
||||
|
||||
<div ng-if="currentUser.role === 'admin'">
|
||||
<div ng-if="isAuthorized(['admin', 'manager'])">
|
||||
<select-member subscription="false"></select-member>
|
||||
</div>
|
||||
|
||||
<section class="widget panel b-a m m-t-lg" ng-show="ctrl.member">
|
||||
<div class="panel-heading b-b">
|
||||
<h3 ng-show="currentUser.role != 'admin'" translate>{{ 'app.public.plans.my_group' }}</h3>
|
||||
<h3 ng-show="currentUser.role === 'admin'" translate translate-values="{GENDER:getGender(currentUser)}">{{ 'app.public.plans.his_group' }}</h3>
|
||||
<h3 ng-show="isAuthorized('member')" translate>{{ 'app.public.plans.my_group' }}</h3>
|
||||
<h3 ng-show="isAuthorized(['admin', 'manager'])" translate translate-values="{GENDER:getGender(ctrl.member)}">{{ 'app.public.plans.his_group' }}</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg auto wrapper">
|
||||
<div ng-show="!group.change">
|
||||
@ -99,14 +98,14 @@
|
||||
ng-click="group.change = !group.change"
|
||||
ng-show="(!selectedPlan && ctrl.member && !ctrl.member.subscribed_plan && ctrl.member.subscription) || (!paid.plan)"
|
||||
translate
|
||||
translate-values="{ROLE:currentUser.role}">{{ 'app.public.plans.he_wants_to_change_group' }}</button>
|
||||
translate-values="{ROLE:ctrl.member.role}">{{ 'app.public.plans.he_wants_to_change_group' }}</button>
|
||||
</div>
|
||||
<div ng-show="group.change">
|
||||
<select class="form-control" ng-options="g.id as g.name for g in groups" ng-model="group.id"></select>
|
||||
<button class="btn btn-success m-t"
|
||||
ng-click="selectGroup()"
|
||||
translate
|
||||
translate-values="{ROLE:currentUser.role, GENDER:getGender(currentUser)}">{{ 'app.public.plans.change_my_group' }}</button>
|
||||
translate-values="{ROLE:ctrl.member.role, GENDER:getGender(ctrl.member)}">{{ 'app.public.plans.change_my_group' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<div class="panel-body">
|
||||
<div class="font-sbold text-u-c">{{ 'app.shared.cart.datetime_to_time' | translate:{START_DATETIME:(slot.start | amDateFormat:'LLLL'), END_TIME:(slot.end | amDateFormat:'LT') } }}</div>
|
||||
<div class="text-base">{{ 'app.shared.cart.cost_of_TYPE' | translate:{TYPE:reservableType} }} <span ng-class="{'text-blue': !slot.promo, 'red': slot.promo}">{{slot.price | currency}}</span></div>
|
||||
<div ng-show="isAdmin()" class="m-t">
|
||||
<div ng-show="isAuthorized(['admin', 'manager'])" class="m-t">
|
||||
<label for="offerSlot" class="control-label m-r" translate>{{ 'app.shared.cart.offer_this_slot' }}</label>
|
||||
<input bs-switch
|
||||
ng-model="slot.offered"
|
||||
|
157
app/assets/templates/shared/_manager_form.html
Normal file
157
app/assets/templates/shared/_manager_form.html
Normal file
@ -0,0 +1,157 @@
|
||||
<div class="row m-t">
|
||||
<div class="col-sm-offset-3 col-sm-6">
|
||||
<div class="form-group" ng-class="{'has-error': managerForm['manager[statistic_profile_attributes][gender]'].$dirty && managerForm['manager[statistic_profile_attributes][gender]'].$invalid}">
|
||||
<label class="checkbox-inline btn btn-default">
|
||||
<input type="radio"
|
||||
name="manager[statistic_profile_attributes][gender]"
|
||||
ng-model="manager.statistic_profile_attributes.gender"
|
||||
ng-value="true"
|
||||
required/>
|
||||
<i class="fa fa-male m-l-sm"></i> {{ 'app.admin.manager_new.man' | translate }}
|
||||
</label>
|
||||
<label class="checkbox-inline btn btn-default">
|
||||
<input type="radio"
|
||||
name="manager[statistic_profile_attributes][gender]"
|
||||
ng-model="manager.statistic_profile_attributes.gender"
|
||||
ng-value="false"/>
|
||||
<i class="fa fa-female m-l-sm"></i> {{ 'app.admin.manager_new.woman' | translate }}
|
||||
</label>
|
||||
<span class="exponent m-l-xs help-cursor"><i class="fa fa-asterisk" aria-hidden="true"></i></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': managerForm['manager[username]'].$dirty && managerForm['manager[username]'].$invalid}">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-user"></i> <span class="exponent"><i class="fa fa-asterisk" aria-hidden="true"></i></span></span>
|
||||
<input ng-model="manager.username"
|
||||
type="text" name="manager[username]"
|
||||
class="form-control"
|
||||
id="user_username"
|
||||
placeholder="{{ 'app.admin.manager_new.pseudonym' | translate }}"
|
||||
required>
|
||||
</div>
|
||||
<span class="help-block" ng-show="managerForm['manager[username]'].$dirty && managerForm['manager[username]'].$error.required" translate>{{ 'app.admin.manager_new.pseudonym_is_required' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': managerForm['manager[profile_attributes][last_name]'].$dirty && managerForm['manager[profile_attributes][last_name]'].$invalid}">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-user"></i> <span class="exponent"><i class="fa fa-asterisk" aria-hidden="true"></i></span></span>
|
||||
<input ng-model="manager.profile_attributes.last_name"
|
||||
type="text"
|
||||
name="manager[profile_attributes][last_name]"
|
||||
class="form-control"
|
||||
id="user_last_name"
|
||||
placeholder="{{ 'app.admin.manager_new.surname' | translate }}"
|
||||
required>
|
||||
</div>
|
||||
<span class="help-block" ng-show="managerForm['manager[profile_attributes][last_name]'].$dirty && managerForm['manager[profile_attributes][last_name]'].$error.required" translate>{{ 'app.admin.manager_new.surname_is_required' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': managerForm['manager[profile_attributes][first_name]'].$dirty && managerForm['manager[profile_attributes][first_name]'].$invalid}">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-user"></i> <span class="exponent"><i class="fa fa-asterisk" aria-hidden="true"></i></span></span>
|
||||
<input ng-model="manager.profile_attributes.first_name"
|
||||
type="text"
|
||||
name="manager[profile_attributes][first_name]"
|
||||
class="form-control"
|
||||
id="user_first_name"
|
||||
placeholder="{{ 'app.admin.manager_new.first_name' | translate }}"
|
||||
required>
|
||||
</div>
|
||||
<span class="help-block" ng-show="managerForm['manager[profile_attributes][first_name]'].$dirty && managerForm['manager[profile_attributes][first_name]'].$error.required" translate>{{ 'app.admin.manager_new.first_name_is_required' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': managerForm['manager[email]'].$dirty && managerForm['manager[email]'].$invalid}">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-envelope"></i> <span class="exponent"><i class="fa fa-asterisk" aria-hidden="true"></i></span></span>
|
||||
<input ng-model="manager.email"
|
||||
type="email"
|
||||
name="manager[email]"
|
||||
class="form-control"
|
||||
id="user_email"
|
||||
placeholder="{{ 'app.admin.manager_new.email_address' | translate }}"
|
||||
required>
|
||||
</div>
|
||||
<span class="help-block" ng-show="managerForm['manager[email]'].$dirty && managerForm['manager[email]'].$error.required" translate>{{ 'app.admin.manager_new.email_is_required' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': managerForm['manager[statistic_profile_attributes][birthday]'].$dirty && managerForm['manager[statistic_profile_attributes][birthday]'].$invalid}">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-calendar-o"></i> </span>
|
||||
<input type="text"
|
||||
id="user_birthday"
|
||||
class="form-control"
|
||||
ng-model="manager.statistic_profile_attributes.birthday"
|
||||
uib-datepicker-popup="{{datePicker.format}}"
|
||||
datepicker-options="datePicker.options"
|
||||
is-open="datePicker.opened"
|
||||
placeholder="{{ 'app.admin.manager_new.birth_date' | translate }}"
|
||||
ng-click="openDatePicker($event)"
|
||||
/>
|
||||
<input type="hidden"
|
||||
name="manager[statistic_profile_attributes][birthday]"
|
||||
value="{{manager.statistic_profile_attributes.birthday | toIsoDate}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-map-marker"></i> </span>
|
||||
<input type="hidden"
|
||||
name="manager[invoicing_profile_attributes][address_attributes][id]"
|
||||
ng-value="manager.invoicing_profile_attributes.address.id" />
|
||||
<input ng-model="manager.invoicing_profile_attributes.address_attributes.address"
|
||||
type="text"
|
||||
name="manager[invoicing_profile_attributes][address_attributes][address]"
|
||||
class="form-control"
|
||||
id="user_address"
|
||||
placeholder="{{ 'app.admin.manager_new.address' | translate }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="{'has-error': managerForm['manager[profile_attributes][phone]'].$dirty && managerForm['manager[profile_attributes][phone]'].$invalid}">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-phone"></i> </span>
|
||||
<input ng-model="manager.profile_attributes.phone"
|
||||
type="text"
|
||||
name="manager[profile_attributes][phone]"
|
||||
class="form-control" id="user_phone"
|
||||
placeholder="{{ 'app.admin.manager_new.phone_number' | translate }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="form-group" ng-class="{'has-error': managerForm['manager[group_id]'].$dirty && managerForm['manager[group_id]'].$invalid}">
|
||||
<label for="manager_group_id" class="col-sm-3 control-label">
|
||||
<span translate>{{ 'app.shared.user_admin.group' }}</span>
|
||||
<span class="exponent"><i class="fa fa-asterisk" aria-hidden="true"></i></span>
|
||||
</label>
|
||||
<div class="col-sm-6">
|
||||
<select ng-model="manager.group_id" class="form-control" name="manager[group_id]" id="manager_group_id"
|
||||
ng-options="g.id as g.name for g in groups" required>
|
||||
</select>
|
||||
<input type="hidden" name="manager[group_id]" ng-value="manager.group_id" />
|
||||
<span class="help-block" ng-show="managerForm['manager[group_id]'].$dirty && managerForm['manager[group_id]'].$error.required" translate>{{ 'app.shared.user_admin.group_is_required' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label" translate>{{ 'app.shared.user_admin.tags' }}</label>
|
||||
<div class="col-sm-6 nopadding">
|
||||
<input type="hidden" name="manager[tag_ids][]" value="" />
|
||||
<ui-select multiple ng-model="manager.tag_ids" name="user[tag_ids][]" class="form-control">
|
||||
<ui-select-match>
|
||||
<span ng-bind="$item.name"></span>
|
||||
<input type="hidden" name="manager[tag_ids][]" value="{{$item.id}}" />
|
||||
</ui-select-match>
|
||||
<ui-select-choices repeat="t.id as t in (tags | filter: $select.search)">
|
||||
<span ng-bind-html="t.name | highlight: $select.search"></span>
|
||||
</ui-select-choices>
|
||||
</ui-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
@ -3,16 +3,16 @@
|
||||
<h1 translate>{{ 'app.shared.cart.slot_at_same_time' }}</h1>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p ng-if="bookSlotAtSameTime || currentUser.role === 'admin'" translate>{{ 'app.shared.cart.do_you_really_want_to_book_slot_at_same_time' }}</p>
|
||||
<p ng-if="!bookSlotAtSameTime && currentUser.role !== 'admin'" translate>{{ 'app.shared.cart.unable_to_book_slot_because_really_have_reservation_at_same_time' }}</p>
|
||||
<p ng-if="bookSlotAtSameTime || isAuthorized(['admin', 'manager'])" translate>{{ 'app.shared.cart.do_you_really_want_to_book_slot_at_same_time' }}</p>
|
||||
<p ng-if="!bookSlotAtSameTime && !isAuthorized(['admin', 'manager'])" translate>{{ 'app.shared.cart.unable_to_book_slot_because_really_have_reservation_at_same_time' }}</p>
|
||||
<ul>
|
||||
<li ng-repeat="r in sameTimeReservations">
|
||||
<span>{{::r.reservable.name}}{{::r.reservable.title}}</span>
|
||||
<div class="font-sbold text-u-c">{{ 'app.shared.cart.datetime_to_time' | translate:{START_DATETIME:(r.start_at | amDateFormat:'LLLL'), END_TIME:(r.end_at | amDateFormat:'LT') } }}</div>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button ng-if="bookSlotAtSameTime || currentUser.role === 'admin'" class="btn btn-info" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
|
||||
<button ng-if="bookSlotAtSameTime || isAuthorized(['admin', 'manager'])" class="btn btn-info" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
|
||||
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
|
||||
</div>
|
||||
|
@ -3,8 +3,8 @@
|
||||
<h1 translate>{{ 'app.shared.confirm_modify_slot_modal.change_the_slot' }}</h1>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p ng-show="currentUser.role != 'admin'" translate>{{ 'app.shared.confirm_modify_slot_modal.do_you_want_to_change_your_booking_slot_initially_planned_at' }} </p>
|
||||
<p ng-show="currentUser.role == 'admin'" translate translate-values="{NAME: object.user.name}">{{ 'app.shared.confirm_modify_slot_modal.do_you_want_to_change_NAME_s_booking_slot_initially_planned_at' }}</p>
|
||||
<p ng-show="currentUser.id === object.user.id" translate>{{ 'app.shared.confirm_modify_slot_modal.do_you_want_to_change_your_booking_slot_initially_planned_at' }} </p>
|
||||
<p ng-show="currentUser.id !== object.user.id" translate translate-values="{NAME: object.user.name}">{{ 'app.shared.confirm_modify_slot_modal.do_you_want_to_change_NAME_s_booking_slot_initially_planned_at' }}</p>
|
||||
<p><strong>{{object.start | amDateFormat: 'LL'}} : {{object.start | amDateFormat:'LT'}} - {{object.end | amDateFormat:'LT'}}</strong></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
@ -40,8 +40,8 @@
|
||||
<li><a ui-sref="app.logged.dashboard.events" translate>{{ 'app.public.common.my_events' }}</a></li>
|
||||
<li><a ui-sref="app.logged.dashboard.invoices" ng-hide="fablabWithoutInvoices" translate>{{ 'app.public.common.my_invoices' }}</a></li>
|
||||
<li ng-hide="fablabWithoutWallet"><a ui-sref="app.logged.dashboard.wallet" translate>{{ 'app.public.common.my_wallet' }}</a></li>
|
||||
<li class="divider" ng-if="currentUser.role === 'admin'"></li>
|
||||
<li><a class="text-black pointer" ng-click="help($event)" ng-if="currentUser.role === 'admin'"><i class="fa fa-question-circle"></i> <span translate>{{ 'app.public.common.help' }}</span> </a></li>
|
||||
<li class="divider" ng-if="isAuthorized(['admin', 'manager'])"></li>
|
||||
<li><a class="text-black pointer" ng-click="help($event)" ng-if="isAuthorized(['admin', 'manager'])"><i class="fa fa-question-circle"></i> <span translate>{{ 'app.public.common.help' }}</span> </a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a class="text-black pointer" ng-click="logout($event)"><i class="fa fa-power-off"></i> {{ 'app.public.common.sign_out' | translate }}</a></li>
|
||||
</ul>
|
||||
|
@ -71,10 +71,11 @@
|
||||
</ul>
|
||||
|
||||
<!-- Admin entries -->
|
||||
<div class="line-s bg-red-dark dk " ng-if="isAuthorized('admin')"></div>
|
||||
<div class="line-s bg-red-dark dk " ng-if="isAuthorized(['admin', 'manager'])"></div>
|
||||
<div class="text-xs font-bold text-bordeau hidden-nav-xs padder m-t-lg m-b-sm admin-section" ng-if="isAuthorized('admin')" translate>{{ 'app.public.common.admin' }}</div>
|
||||
<ul class="nav" ng-if="isAuthorized('admin')">
|
||||
<li class="{{navLink.class}}" ng-repeat="navLink in adminNavLinks">
|
||||
<div class="text-xs font-bold text-bordeau hidden-nav-xs padder m-t-lg m-b-sm admin-section" ng-if="isAuthorized('manager')" translate>{{ 'app.public.common.manager' }}</div>
|
||||
<ul class="nav" ng-if="isAuthorized(['admin', 'manager'])">
|
||||
<li class="{{navLink.class}}" ng-repeat="navLink in adminNavLinks" ng-if="!navLink.authorizedRoles || isAuthorized(navLink.authorizedRoles)">
|
||||
<a ng-click="toggleNavSize($event)" ga ui-sref="{{navLink.state}}" ui-sref-active="active" class="auto" data-toggle="class:nav-off-screen" data-target="#nav" ng-if="navLink.state">
|
||||
<i class="fa fa-{{navLink.linkIcon}} fa-lg"></i>
|
||||
<span>{{navLink.linkText | translate}}</span>
|
||||
|
@ -37,7 +37,7 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-md-offset-6 m-t-n row-centered" ng-if="isAuthorized('admin')">
|
||||
<div class="col-md-3 col-md-offset-6 m-t-n row-centered" ng-if="isAuthorized(['admin', 'manager'])">
|
||||
<a role="button" ui-sref="app.admin.calendar" class="btn btn-lg btn-default rounded m-t-sm text-sm">
|
||||
<i class="fa fa-calendar-check-o m-r" aria-hidden="true"></i><span translate>{{ 'app.public.spaces_list.new_availability' }}</span>
|
||||
</a>
|
||||
@ -60,7 +60,7 @@
|
||||
|
||||
<div class="text-center clearfix">
|
||||
<div class="col-sm-6 b-r no-padder">
|
||||
<div class="btn btn-default btn-block no-b padder-v red" ng-click="reserveSpace(space, $event)" ng-hide="space.disabled">
|
||||
<div class="btn btn-default btn-block no-b padder-v red reserve-button" ng-click="reserveSpace(space, $event)" ng-hide="space.disabled">
|
||||
<i class="fa fa-bookmark m-r-xs"></i>
|
||||
<span class="hidden-sm" translate>{{ 'app.public.spaces_list.book' }}</span>
|
||||
</div>
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
<div class="col-sm-12 col-md-12 col-lg-3">
|
||||
|
||||
<div ng-if="currentUser.role === 'admin'">
|
||||
<div ng-if="isAuthorized(['admin', 'manager'])">
|
||||
<select-member></select-member>
|
||||
</div>
|
||||
|
||||
|
@ -80,7 +80,7 @@ class API::AvailabilitiesController < API::ApiController
|
||||
end
|
||||
|
||||
def machine
|
||||
@current_user_role = current_user.admin? ? 'admin' : 'user'
|
||||
@current_user_role = current_user.role
|
||||
|
||||
service = Availabilities::AvailabilitiesService.new(current_user, other: @visi_max_other, year: @visi_max_year)
|
||||
@slots = service.machines(params[:machine_id], user)
|
||||
@ -92,7 +92,7 @@ class API::AvailabilitiesController < API::ApiController
|
||||
end
|
||||
|
||||
def spaces
|
||||
@current_user_role = current_user.admin? ? 'admin' : 'user'
|
||||
@current_user_role = current_user.role
|
||||
|
||||
service = Availabilities::AvailabilitiesService.new(current_user, other: @visi_max_other, year: @visi_max_year)
|
||||
@slots = service.spaces(params[:space_id], user)
|
||||
|
@ -38,7 +38,7 @@ class API::MembersController < API::ApiController
|
||||
end
|
||||
|
||||
def create
|
||||
authorize User
|
||||
authorize :user, :create_member?
|
||||
|
||||
@member = User.new(user_params.permit!)
|
||||
members_service = Members::MembersService.new(@member)
|
||||
@ -222,7 +222,7 @@ class API::MembersController < API::ApiController
|
||||
],
|
||||
statistic_profile_attributes: %i[id gender birthday])
|
||||
|
||||
elsif current_user.admin?
|
||||
elsif current_user.admin? || current_user.manager?
|
||||
params.require(:user).permit(:username, :email, :password, :password_confirmation, :is_allow_contact, :is_allow_newsletter, :group_id,
|
||||
tag_ids: [],
|
||||
profile_attributes: [:id, :first_name, :last_name, :phone, :interest, :software_mastered, :website, :job,
|
||||
|
@ -9,13 +9,13 @@ class API::ReservationsController < API::ApiController
|
||||
|
||||
def index
|
||||
if params[:reservable_id] && params[:reservable_type] && params[:user_id]
|
||||
params[:user_id] = current_user.id unless current_user.admin?
|
||||
params[:user_id] = current_user.id unless current_user.admin? || current_user.manager?
|
||||
|
||||
where_clause = params.permit(:reservable_id, :reservable_type).to_h
|
||||
where_clause[:statistic_profile_id] = StatisticProfile.find_by!(user_id: params[:user_id])
|
||||
|
||||
@reservations = Reservation.where(where_clause)
|
||||
elsif params[:reservable_id] && params[:reservable_type] && current_user.admin?
|
||||
elsif params[:reservable_id] && params[:reservable_type] && (current_user.admin? || current_user.manager?)
|
||||
@reservations = Reservation.where(params.permit(:reservable_id, :reservable_type))
|
||||
else
|
||||
@reservations = []
|
||||
@ -25,12 +25,13 @@ class API::ReservationsController < API::ApiController
|
||||
def show; end
|
||||
|
||||
# Admins can create any reservations. Members can directly create reservations if total = 0,
|
||||
# otherwise, they must use payments_controller#confirm_payment
|
||||
# otherwise, they must use payments_controller#confirm_payment.
|
||||
# Managers can create reservations for other users
|
||||
def create
|
||||
user_id = current_user.admin? ? params[:reservation][:user_id] : current_user.id
|
||||
user_id = current_user.admin? || current_user.manager? ? params[:reservation][:user_id] : current_user.id
|
||||
amount = transaction_amount(current_user.admin?, user_id)
|
||||
|
||||
authorize ReservationContext.new(Reservation, amount)
|
||||
authorize ReservationContext.new(Reservation, amount, user_id)
|
||||
|
||||
@reservation = Reservation.new(reservation_params)
|
||||
is_reserve = Reservations::Reserve.new(user_id, current_user.invoicing_profile.id)
|
||||
|
@ -10,12 +10,13 @@ class API::SubscriptionsController < API::ApiController
|
||||
end
|
||||
|
||||
# Admins can create any subscriptions. Members can directly create subscriptions if total = 0,
|
||||
# otherwise, they must use payments_controller#confirm_payment
|
||||
# otherwise, they must use payments_controller#confirm_payment.
|
||||
# Managers can create subscriptions for other users
|
||||
def create
|
||||
user_id = current_user.admin? ? params[:subscription][:user_id] : current_user.id
|
||||
user_id = current_user.admin? || current_user.manager? ? params[:subscription][:user_id] : current_user.id
|
||||
amount = transaction_amount(current_user.admin?, user_id)
|
||||
|
||||
authorize SubscriptionContext.new(Subscription, amount)
|
||||
authorize SubscriptionContext.new(Subscription, amount, user_id)
|
||||
|
||||
@subscription = Subscription.new(subscription_params)
|
||||
is_subscribe = Subscriptions::Subscribe.new(current_user.invoicing_profile.id, user_id)
|
||||
|
@ -41,7 +41,8 @@ class API::TrainingsController < API::ApiController
|
||||
end
|
||||
|
||||
head :no_content
|
||||
elsif @training.update(training_params)
|
||||
elsif current_user.admin? && @training.update(training_params)
|
||||
# only admins can fully update a training, not managers
|
||||
render :show, status: :ok, location: @training
|
||||
else
|
||||
render json: @training.errors, status: :unprocessable_entity
|
||||
|
@ -6,7 +6,9 @@ class API::UsersController < API::ApiController
|
||||
before_action :set_user, only: %i[destroy]
|
||||
|
||||
def index
|
||||
if current_user.admin? && %w[partner manager].include?(params[:role])
|
||||
authorize User
|
||||
|
||||
if %w[partner manager].include?(params[:role])
|
||||
@users = User.with_role(params[:role].to_sym).includes(:profile)
|
||||
else
|
||||
head 403
|
||||
@ -15,12 +17,18 @@ class API::UsersController < API::ApiController
|
||||
|
||||
def create
|
||||
authorize User
|
||||
res = UserService.create_partner(partner_params)
|
||||
res = if !params[:user].empty?
|
||||
UserService.create_partner(partner_params)
|
||||
elsif !params[:manager].empty?
|
||||
UserService.create_manager(manager_params)
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
||||
if res[:saved]
|
||||
@user = res[:user]
|
||||
render status: :created
|
||||
else²
|
||||
else
|
||||
render json: res[:user].errors.full_messages, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
@ -40,4 +48,14 @@ class API::UsersController < API::ApiController
|
||||
def partner_params
|
||||
params.require(:user).permit(:email, :first_name, :last_name)
|
||||
end
|
||||
|
||||
def manager_params
|
||||
params.require(:manager).permit(
|
||||
:username, :email, :group_id,
|
||||
tag_ids: [],
|
||||
profile_attributes: %i[first_name last_name phone],
|
||||
invoicing_profile_attributes: [address_attributes: [:address]],
|
||||
statistic_profile_attributes: %i[gender birthday]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -1,3 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Devise controller to handle validation of email addresses
|
||||
class ConfirmationsController < Devise::ConfirmationsController
|
||||
# The path used after confirmation.
|
||||
def after_confirmation_path_for(resource_name, resource)
|
||||
|
@ -1,5 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Devise controller used for the "forgotten password" feature
|
||||
class PasswordsController < Devise::PasswordsController
|
||||
# POST /resource/password
|
||||
# POST /users/password.json
|
||||
def create
|
||||
self.resource = resource_class.send_reset_password_instructions(resource_params)
|
||||
yield resource if block_given?
|
||||
|
@ -1,17 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Handle requests originated by indexer bots of social networks
|
||||
class SocialBotController < ActionController::Base
|
||||
def share
|
||||
case request.original_fullpath
|
||||
when /(=%2F|\/)projects(%2F|\/)([\-0-9a-z_]+)/
|
||||
@project = Project.friendly.find("#{$3}")
|
||||
render :project, status: :ok
|
||||
when /(=%2F|\/)events(%2F|\/)([0-9]+)/
|
||||
@event = Event.find("#{$3}".to_i)
|
||||
render :event, status: :ok
|
||||
when /(=%2F|\/)trainings(%2F|\/)([\-0-9a-z_]+)/
|
||||
@training = Training.friendly.find("#{$3}")
|
||||
when %r{(=%2F|/)projects(%2F|/)([\-0-9a-z_]+)}
|
||||
@project = Project.friendly.find(Regexp.last_match(3).to_s)
|
||||
render :project, status: :ok
|
||||
when %r{(=%2F|/)events(%2F|/)([0-9]+)}
|
||||
@event = Event.find(Regexp.last_match(3).to_s.to_i)
|
||||
render :event, status: :ok
|
||||
when %r{(=%2F|/)trainings(%2F|/)([\-0-9a-z_]+)}
|
||||
@training = Training.friendly.find(Regexp.last_match(3).to_s)
|
||||
render :training, status: :ok
|
||||
else
|
||||
puts "unknown bot request : #{request.original_url}"
|
||||
else
|
||||
puts "unknown bot request : #{request.original_url}"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -160,6 +160,8 @@ class Availability < ApplicationRecord
|
||||
private
|
||||
|
||||
def length_must_be_slot_multiple
|
||||
return unless available_type == 'machines' || available_type == 'space'
|
||||
|
||||
duration = slot_duration || ApplicationHelper::SLOT_DURATION
|
||||
return unless end_at < (start_at + duration.minutes)
|
||||
|
||||
|
@ -23,7 +23,7 @@ class Avoir < Invoice
|
||||
|
||||
def notify_admins_refund_created
|
||||
NotificationCenter.call type: 'notify_admin_refund_created',
|
||||
receiver: User.admins,
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: self
|
||||
end
|
||||
end
|
||||
|
@ -15,7 +15,7 @@ class EventPriceCategory < ApplicationRecord
|
||||
protected
|
||||
|
||||
def verify_no_associated_tickets
|
||||
throw(:abort) if tickets.count.zero?
|
||||
throw(:abort) unless tickets.count.zero?
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -144,7 +144,7 @@ class Project < ApplicationRecord
|
||||
|
||||
def notify_admin_when_project_published
|
||||
NotificationCenter.call type: 'notify_admin_when_project_published',
|
||||
receiver: User.admins,
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: self
|
||||
end
|
||||
|
||||
|
@ -306,7 +306,7 @@ class Reservation < ApplicationRecord
|
||||
|
||||
def notify_admin_member_create_reservation
|
||||
NotificationCenter.call type: 'notify_admin_member_create_reservation',
|
||||
receiver: User.admins,
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: self
|
||||
end
|
||||
|
||||
|
@ -40,7 +40,7 @@ class Slot < ApplicationRecord
|
||||
receiver: reservation.user,
|
||||
attached_object: self
|
||||
NotificationCenter.call type: 'notify_admin_slot_is_modified',
|
||||
receiver: User.admins,
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: self
|
||||
end
|
||||
|
||||
@ -49,7 +49,7 @@ class Slot < ApplicationRecord
|
||||
receiver: reservation.user,
|
||||
attached_object: self
|
||||
NotificationCenter.call type: 'notify_admin_slot_is_canceled',
|
||||
receiver: User.admins,
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: self
|
||||
end
|
||||
|
||||
|
@ -148,7 +148,7 @@ class Subscription < ApplicationRecord
|
||||
|
||||
def notify_admin_subscription_canceled
|
||||
NotificationCenter.call type: 'notify_admin_subscription_canceled',
|
||||
receiver: User.admins,
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: self
|
||||
end
|
||||
|
||||
@ -173,7 +173,7 @@ class Subscription < ApplicationRecord
|
||||
meta_data: meta_data
|
||||
|
||||
NotificationCenter.call type: :notify_admin_subscription_extended,
|
||||
receiver: User.admins,
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: self,
|
||||
meta_data: meta_data
|
||||
end
|
||||
|
@ -97,6 +97,22 @@ class User < ApplicationRecord
|
||||
User.with_role(:member)
|
||||
end
|
||||
|
||||
def self.partners
|
||||
User.with_role(:partner)
|
||||
end
|
||||
|
||||
def self.managers
|
||||
User.with_role(:manager)
|
||||
end
|
||||
|
||||
def self.admins_and_managers
|
||||
User.with_any_role(:admin, :manager)
|
||||
end
|
||||
|
||||
def self.online_payers
|
||||
User.with_any_role(:manager, :member)
|
||||
end
|
||||
|
||||
def self.superadmin
|
||||
return unless Rails.application.secrets.superadmin_email.present?
|
||||
|
||||
@ -104,7 +120,7 @@ class User < ApplicationRecord
|
||||
end
|
||||
|
||||
def training_machine?(machine)
|
||||
return true if admin?
|
||||
return true if admin? || manager?
|
||||
|
||||
trainings.map(&:machines).flatten.uniq.include?(machine)
|
||||
end
|
||||
@ -131,6 +147,24 @@ class User < ApplicationRecord
|
||||
has_role? :member
|
||||
end
|
||||
|
||||
def manager?
|
||||
has_role? :manager
|
||||
end
|
||||
|
||||
def partner?
|
||||
has_role? :partner
|
||||
end
|
||||
|
||||
def role
|
||||
if admin?
|
||||
'admin'
|
||||
elsif manager?
|
||||
'manager'
|
||||
else
|
||||
'other'
|
||||
end
|
||||
end
|
||||
|
||||
def all_projects
|
||||
my_projects.to_a.concat projects
|
||||
end
|
||||
@ -285,7 +319,7 @@ class User < ApplicationRecord
|
||||
|
||||
protected
|
||||
|
||||
# remove projets drafts that are not linked to another user
|
||||
# remove projects drafts that are not linked to another user
|
||||
def remove_orphan_drafts
|
||||
orphans = my_projects
|
||||
.joins('LEFT JOIN project_users ON projects.id = project_users.project_id')
|
||||
@ -332,19 +366,19 @@ class User < ApplicationRecord
|
||||
attached_object: self
|
||||
else
|
||||
NotificationCenter.call type: 'notify_admin_when_user_is_created',
|
||||
receiver: User.admins,
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: self
|
||||
end
|
||||
end
|
||||
|
||||
def notify_group_changed
|
||||
return if changes[:group_id].first.nil?
|
||||
return unless changes[:group_id]&.first
|
||||
|
||||
ex_group = Group.find(changes[:group_id].first)
|
||||
meta_data = { ex_group_name: ex_group.name }
|
||||
|
||||
NotificationCenter.call type: :notify_admin_user_group_changed,
|
||||
receiver: User.admins,
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: self,
|
||||
meta_data: meta_data
|
||||
|
||||
|
@ -2,9 +2,13 @@
|
||||
|
||||
# Check the access policies for API::AccountingPeriodsController
|
||||
class AccountingPeriodPolicy < ApplicationPolicy
|
||||
%w[index show create last_closing_end download_archive].each do |action|
|
||||
%w[index show create download_archive].each do |action|
|
||||
define_method "#{action}?" do
|
||||
user.admin?
|
||||
end
|
||||
end
|
||||
|
||||
def last_closing_end?
|
||||
user.admin? || user.manager?
|
||||
end
|
||||
end
|
||||
|
@ -1,6 +1,6 @@
|
||||
class AdminPolicy < ApplicationPolicy
|
||||
def index?
|
||||
user.admin?
|
||||
user.admin? || user.manager?
|
||||
end
|
||||
|
||||
def create?
|
||||
|
@ -1,7 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Check the access policies for API::AvailabilitiesController
|
||||
class AvailabilityPolicy < ApplicationPolicy
|
||||
%w(index? show? create? update? destroy? reservations? export? lock?).each do |action|
|
||||
%w[index? show? create? update? destroy? reservations? lock?].each do |action|
|
||||
define_method action do
|
||||
user.admin?
|
||||
user.admin? || user.manager?
|
||||
end
|
||||
end
|
||||
|
||||
def export?
|
||||
user.admin?
|
||||
end
|
||||
end
|
||||
|
@ -18,7 +18,7 @@ class EventPolicy < ApplicationPolicy
|
||||
end
|
||||
|
||||
def create?
|
||||
user.admin?
|
||||
user.admin? || user.manager?
|
||||
end
|
||||
|
||||
def update?
|
||||
|
@ -3,10 +3,10 @@
|
||||
# Check the access policies for API::ICalendarController
|
||||
class ICalendarPolicy < ApplicationPolicy
|
||||
def create?
|
||||
user.admin?
|
||||
user.admin? || user.manager?
|
||||
end
|
||||
|
||||
def destroy?
|
||||
user.admin?
|
||||
user.admin? || user.manager?
|
||||
end
|
||||
end
|
||||
|
@ -1,18 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Check the access policies for API::InvoicesController
|
||||
class InvoicePolicy < ApplicationPolicy
|
||||
def index?
|
||||
user.admin?
|
||||
end
|
||||
|
||||
def download?
|
||||
user.admin? or (record.invoicing_profile.user_id == user.id)
|
||||
user.admin? || user.manager? || (record.invoicing_profile.user_id == user.id)
|
||||
end
|
||||
|
||||
def create?
|
||||
user.admin?
|
||||
user.admin? || user.manager?
|
||||
end
|
||||
|
||||
def list?
|
||||
user.admin?
|
||||
user.admin? || user.manager?
|
||||
end
|
||||
|
||||
def first?
|
||||
|
@ -2,11 +2,12 @@
|
||||
|
||||
# Pundit Additional context to validate the price of a reservation
|
||||
class ReservationContext
|
||||
attr_reader :reservation, :price
|
||||
attr_reader :reservation, :price, :user_id
|
||||
|
||||
def initialize(reservation, price)
|
||||
def initialize(reservation, price, user_id)
|
||||
@reservation = reservation
|
||||
@price = price
|
||||
@user_id = user_id
|
||||
end
|
||||
|
||||
def policy_class
|
||||
|
@ -3,10 +3,10 @@
|
||||
# Check the access policies for API::ReservationsController
|
||||
class ReservationPolicy < ApplicationPolicy
|
||||
def create?
|
||||
user.admin? || record.price.zero?
|
||||
user.admin? || (user.manager? && record.user_id != user.id) || record.price.zero?
|
||||
end
|
||||
|
||||
def update?
|
||||
user.admin? || record.user == user
|
||||
user.admin? || user.manager? || record.user == user
|
||||
end
|
||||
end
|
||||
|
@ -1,15 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Check the access policies for API::SlotsController
|
||||
class SlotPolicy < ApplicationPolicy
|
||||
def update?
|
||||
# check that the update is allowed and the prevention delay has not expired
|
||||
delay = Setting.find_by( name: 'booking_move_delay').value.to_i
|
||||
enabled = (Setting.find_by( name: 'booking_move_enable').value == 'true')
|
||||
delay = Setting.find_by(name: 'booking_move_delay').value.to_i
|
||||
enabled = (Setting.find_by(name: 'booking_move_enable').value == 'true')
|
||||
|
||||
# these condition does not apply to admins
|
||||
user.admin? or
|
||||
(record.reservation.user == user and enabled and ((record.start_at - DateTime.current).to_i / 3600 >= delay))
|
||||
user.admin? || user.manager? ||
|
||||
(record.reservation.user == user && enabled && ((record.start_at - DateTime.current).to_i / 3600 >= delay))
|
||||
end
|
||||
|
||||
def cancel?
|
||||
user.admin? or record.reservation.user == user
|
||||
user.admin? || user.manager? || record.reservation.user == user
|
||||
end
|
||||
end
|
||||
|
@ -2,11 +2,12 @@
|
||||
|
||||
# Pundit Additional context to validate the price of a subscription
|
||||
class SubscriptionContext
|
||||
attr_reader :subscription, :price
|
||||
attr_reader :subscription, :price, :user_id
|
||||
|
||||
def initialize(subscription, price)
|
||||
def initialize(subscription, price, user_id)
|
||||
@subscription = subscription
|
||||
@price = price
|
||||
@user_id = user_id
|
||||
end
|
||||
|
||||
def policy_class
|
||||
|
@ -4,7 +4,7 @@
|
||||
class SubscriptionPolicy < ApplicationPolicy
|
||||
include FablabConfiguration
|
||||
def create?
|
||||
!fablab_plans_deactivated? && (user.admin? || record.price.zero?)
|
||||
!fablab_plans_deactivated? && (user.admin? || (user.manager? && record.user_id != user.id) || record.price.zero?)
|
||||
end
|
||||
|
||||
def show?
|
||||
@ -12,6 +12,6 @@ class SubscriptionPolicy < ApplicationPolicy
|
||||
end
|
||||
|
||||
def update?
|
||||
user.admin?
|
||||
user.admin? || (user.manager? && record.user.id != user.id)
|
||||
end
|
||||
end
|
||||
|
@ -1,3 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Check the access policies for API::TrainingsController
|
||||
class TrainingPolicy < ApplicationPolicy
|
||||
class Scope < Scope
|
||||
def resolve
|
||||
@ -5,17 +8,19 @@ class TrainingPolicy < ApplicationPolicy
|
||||
end
|
||||
end
|
||||
|
||||
%w(create update).each do |action|
|
||||
define_method "#{action}?" do
|
||||
user.admin?
|
||||
end
|
||||
def create
|
||||
user.admin?
|
||||
end
|
||||
|
||||
def update?
|
||||
user.admin? || user.manager?
|
||||
end
|
||||
|
||||
def destroy?
|
||||
user.admin? and record.destroyable?
|
||||
user.admin? && record.destroyable?
|
||||
end
|
||||
|
||||
def availabilities?
|
||||
user.admin?
|
||||
user.admin? || user.manager?
|
||||
end
|
||||
end
|
||||
|
@ -16,26 +16,30 @@ class UserPolicy < ApplicationPolicy
|
||||
end
|
||||
|
||||
def show?
|
||||
user.admin? || (record.is_allow_contact && record.member?) || (user.id == record.id)
|
||||
user.admin? || user.manager? || (record.is_allow_contact && record.member?) || (user.id == record.id)
|
||||
end
|
||||
|
||||
def update?
|
||||
user.admin? || (user.id == record.id)
|
||||
user.admin? || user.manager? || (user.id == record.id)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
user.admin? || (user.id == record.id)
|
||||
end
|
||||
|
||||
def merge?
|
||||
user.id == record.id
|
||||
%w[merge complete_tour].each do |action|
|
||||
define_method "#{action}?" do
|
||||
user.id == record.id
|
||||
end
|
||||
end
|
||||
|
||||
def complete_tour?
|
||||
user.id == record.id
|
||||
%w[list index create_member].each do |action|
|
||||
define_method "#{action}?" do
|
||||
user.admin? || user.manager?
|
||||
end
|
||||
end
|
||||
|
||||
%w[list create mapping].each do |action|
|
||||
%w[create mapping].each do |action|
|
||||
define_method "#{action}?" do
|
||||
user.admin?
|
||||
end
|
||||
|
@ -1,13 +1,14 @@
|
||||
class WalletPolicy < ApplicationPolicy
|
||||
def by_user?
|
||||
user.admin? or user == record.user
|
||||
end
|
||||
# frozen_string_literal: true
|
||||
|
||||
def transactions?
|
||||
user.admin? or user == record.user
|
||||
# Check the access policies for API::WalletController
|
||||
class WalletPolicy < ApplicationPolicy
|
||||
%w[by_user transactions].each do |action|
|
||||
define_method "#{action}?" do
|
||||
user.admin? || user.manager? || user == record.user
|
||||
end
|
||||
end
|
||||
|
||||
def credit?
|
||||
user.admin?
|
||||
user.admin? || (user.manager? && user != record.user)
|
||||
end
|
||||
end
|
||||
|
@ -50,7 +50,7 @@ class Members::ListService
|
||||
'SELECT max("created_at") ' \
|
||||
'FROM "subscriptions" ' \
|
||||
'WHERE "statistic_profile_id" = "statistic_profiles"."id")')
|
||||
.where("users.is_active = 'true' AND roles.name = 'member'")
|
||||
.where("users.is_active = 'true' AND (roles.name = 'member' OR roles.name = 'manager')")
|
||||
.limit(50)
|
||||
query.downcase.split(' ').each do |word|
|
||||
members = members.where('lower(f_unaccent(profiles.first_name)) ~ :search OR ' \
|
||||
|
@ -50,4 +50,19 @@ class UserService
|
||||
end
|
||||
{ saved: saved, user: admin }
|
||||
end
|
||||
|
||||
def self.create_manager(params)
|
||||
generated_password = Devise.friendly_token.first(8)
|
||||
manager = User.new(params.merge(password: generated_password))
|
||||
manager.send :set_slug
|
||||
|
||||
saved = manager.save
|
||||
if saved
|
||||
manager.send_confirmation_instructions
|
||||
manager.add_role(:manager)
|
||||
manager.remove_role(:member)
|
||||
UsersMailer.delay.notify_user_account_created(manager, generated_password)
|
||||
end
|
||||
{ saved: saved, user: manager }
|
||||
end
|
||||
end
|
||||
|
@ -22,7 +22,7 @@ class WalletService
|
||||
receiver: @wallet.user,
|
||||
attached_object: transaction
|
||||
NotificationCenter.call type: 'notify_admin_user_wallet_is_credited',
|
||||
receiver: User.admins,
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: transaction
|
||||
return transaction
|
||||
end
|
||||
|
@ -15,13 +15,13 @@ json.array!(@slots) do |slot|
|
||||
json.id slot.machine.id
|
||||
json.name slot.machine.name
|
||||
end
|
||||
# the user who booked the slot ...
|
||||
if (@current_user_role == 'admin') && slot.reservation
|
||||
# the user who booked the slot, if the slot was reserved
|
||||
if (%w[admin manager].include? @current_user_role) && 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
|
||||
end
|
||||
json.tag_ids slot.availability.tag_ids
|
||||
json.tags slot.availability.tags do |t|
|
||||
json.id t.id
|
||||
|
@ -16,13 +16,13 @@ json.array!(@slots) do |slot|
|
||||
json.id slot.space.id
|
||||
json.name slot.space.name
|
||||
end
|
||||
# the user who booked the slot ...
|
||||
if (@current_user_role == 'admin') && slot.reservation
|
||||
# the user who booked the slot, if the slot was reserved
|
||||
if (%w[admin manager].include? @current_user_role) && 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
|
||||
end
|
||||
json.tag_ids slot.availability.tag_ids
|
||||
json.tags slot.availability.tags do |t|
|
||||
json.id t.id
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.extract! @machine, :id, :name, :description, :spec, :disabled, :created_at, :updated_at, :slug
|
||||
json.machine_image @machine.machine_image.attachment.large.url if @machine.machine_image
|
||||
json.machine_files_attributes @machine.machine_files do |f|
|
||||
@ -7,9 +9,11 @@ json.machine_files_attributes @machine.machine_files do |f|
|
||||
end
|
||||
json.trainings @machine.trainings.each, :id, :name, :disabled
|
||||
json.current_user_is_training current_user.training_machine?(@machine) if current_user
|
||||
json.current_user_training_reservation do
|
||||
json.partial! 'api/reservations/reservation', reservation: current_user.training_reservation_by_machine(@machine)
|
||||
end if current_user and !current_user.training_machine?(@machine) and current_user.training_reservation_by_machine(@machine)
|
||||
if current_user && !current_user.training_machine?(@machine) && current_user.training_reservation_by_machine(@machine)
|
||||
json.current_user_training_reservation do
|
||||
json.partial! 'api/reservations/reservation', reservation: current_user.training_reservation_by_machine(@machine)
|
||||
end
|
||||
end
|
||||
|
||||
json.machine_projects @machine.projects.published.last(10) do |p|
|
||||
json.id p.id
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
json.users @users do |user|
|
||||
json.extract! user, :id, :email, :first_name, :last_name
|
||||
json.phone user.profile.phone
|
||||
json.name user.profile.full_name
|
||||
json.resource user.roles.last.resource
|
||||
end
|
||||
|
@ -9,14 +9,14 @@ class SubscriptionExpireWorker
|
||||
receiver: s.user,
|
||||
attached_object: s
|
||||
NotificationCenter.call type: 'notify_admin_subscription_will_expire_in_7_days',
|
||||
receiver: User.admins,
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: s
|
||||
else
|
||||
NotificationCenter.call type: 'notify_member_subscription_is_expired',
|
||||
receiver: s.user,
|
||||
attached_object: s
|
||||
NotificationCenter.call type: 'notify_admin_subscription_is_expired',
|
||||
receiver: User.admins,
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: s
|
||||
end
|
||||
end
|
||||
|
@ -19,7 +19,7 @@ en:
|
||||
availabilities: "Availabilities"
|
||||
availabilities_notice: "Export to an Excel workbook every slots available for reservation, and their occupancy rate."
|
||||
ongoing_reservations: "Ongoing reservations"
|
||||
no_reservations: "No reservations"
|
||||
without_reservation: "Without reservation"
|
||||
confirmation_required: "Confirmation required"
|
||||
do_you_really_want_to_cancel_the_USER_s_reservation_the_DATE_at_TIME_concerning_RESERVATION: "Do you really {GENDER, select, other {want}} to cancel the {USER}'s reservation, the {DATE} at {TIME}, concerning {RESERVATION}?"
|
||||
reservation_was_successfully_cancelled: "Reservation was successfully cancelled."
|
||||
@ -193,7 +193,7 @@ en:
|
||||
booking: "Booking"
|
||||
sold_out: "Sold out"
|
||||
cancelled: "Cancelled"
|
||||
free_entry: "Free entry"
|
||||
without_reservation: "Without reservation"
|
||||
free_admission: "Free admission"
|
||||
view_reservations: "View reservations"
|
||||
load_the_next_events: "Load the next events..."
|
||||
@ -604,15 +604,21 @@ en:
|
||||
administrators: "Administrators"
|
||||
search_for_an_administrator: "Search for an administrator"
|
||||
add_a_new_administrator: "Add a new administrator"
|
||||
managers: "Managers"
|
||||
managers_info: "A manager is a restricted administrator that cannot modify the settings of the application. However, he will be able to take reservations for any members and for all managers, including himself, and to process payments and refunds."
|
||||
search_for_a_manager: "Search for a manager"
|
||||
add_a_new_manager: "Add a new manager"
|
||||
delete_this_manager: "Do you really want to delete this manager? This cannot be undone."
|
||||
manager_successfully_deleted: "Manager successfully deleted."
|
||||
unable_to_delete_the_manager: "Unable to delete the manager."
|
||||
partners: "Partners"
|
||||
partners_info: "A partner is a special user that can be associated with the «Partner» plans. These users will only receive notifications about subscriptions to these plans."
|
||||
partners_info: "A partner is a special user that can be associated with the «Partner» plans. These users won't be able to connect and will just receive notifications about subscriptions to their associated plan."
|
||||
search_for_a_partner: "Search for a partner"
|
||||
add_a_new_partner: "Add a new partner"
|
||||
delete_this_partner: "Do you really want to delete this partner? This cannot be undone."
|
||||
partner_successfully_deleted: "Partner successfully deleted."
|
||||
unable_to_delete_the_partner: "Unable to delete the partner."
|
||||
associated_plan: "Associated plan"
|
||||
managers: "Managers"
|
||||
groups: "Groups"
|
||||
tags: "Tags"
|
||||
authentication: "Authentication"
|
||||
@ -757,6 +763,8 @@ en:
|
||||
a_problem_occurred_while_taking_the_subscription: "A problem occurred while taking the subscription"
|
||||
wallet: "Wallet"
|
||||
to_credit: 'Credit'
|
||||
cannot_credit_own_wallet: "You cannot credit your own wallet. Please ask another manager or an administrator to credit your wallet."
|
||||
cannot_extend_own_subscription: "You cannot extend your own subscription. Please ask another manager or an administrator to extend your subscription."
|
||||
#add a new administrator to the platform
|
||||
admins_new:
|
||||
add_an_administrator: "Add an administrator"
|
||||
@ -775,6 +783,24 @@ en:
|
||||
birth_date: "Date of birth"
|
||||
address: "Address"
|
||||
phone_number: "Phone number"
|
||||
#add a new manager to the platform
|
||||
manager_new:
|
||||
add_a_manager: "Add a manager"
|
||||
manager_successfully_created: "Manager successfully created. {GENDER, select, female{She} other{He}} receive {GENDER, select, female{her} other{his}} connection directives by e-mail."
|
||||
failed_to_create_manager: "Unable to create the manager:"
|
||||
man: "Man"
|
||||
woman: "Woman"
|
||||
pseudonym: "Pseudonym"
|
||||
pseudonym_is_required: "Pseudonym is required."
|
||||
first_name: "First name"
|
||||
first_name_is_required: "First name is required."
|
||||
surname: "Last name"
|
||||
surname_is_required: "Last name is required."
|
||||
email_address: "Email address"
|
||||
email_is_required: "Email address is required."
|
||||
birth_date: "Date of birth"
|
||||
address: "Address"
|
||||
phone_number: "Phone number"
|
||||
#add a new authentication provider (SSO)
|
||||
authentication_new:
|
||||
local_database: "Local Database"
|
||||
@ -987,6 +1013,7 @@ en:
|
||||
advanced: "Advanced settings"
|
||||
customize_home_page_css: "Customise the stylesheet og the home page"
|
||||
home_css_notice_html: "You can customize the stylesheet which will apply to the home page, using the <a href=\"https://sass-lang.com/documentation\" target=\"_blank\">SASS</a> syntax. These styles will be automatically subordinated to the <code>.home-page</code> selector to prevent any risk of breaking the application. Meanwhile please be careful, any changes in the home page editor at the top of the page may broke your styles, always refer to the HTML code."
|
||||
an_error_occurred_saving_the_setting: "An error occurred while saving the setting. Please try again later."
|
||||
sort_by:
|
||||
default: "Default"
|
||||
name: "Name"
|
||||
@ -1064,9 +1091,12 @@ en:
|
||||
welcome:
|
||||
title: "Trainings"
|
||||
content: "Here you can create, modify and delete trainings. It is also the place where you can validate the training courses followed by your members."
|
||||
welcome_manager:
|
||||
title: "Trainings"
|
||||
content: "This is the place where you can view the trainings and their associations with the machines. It is also the place where you can validate the training courses followed by your members."
|
||||
trainings:
|
||||
title: "Manage trainings"
|
||||
content: "<p>When creating a training, you can define a default number of places. However, the number of actual places may be modified for each session.</p><p>The training sessions are scheduled from the administrator tab « Calendar ».</p><p>Another thing: it is possible to associate one or more machines with a training. This makes it a prerequisite for the reservation of these machines.</p>"
|
||||
content: "<p>With each training, a default number of places is associated. However, the number of actual places may be modified for each session.</p><p>The training sessions are scheduled from the administrator tab « Calendar ».</p><p>Furthermore, a training may be associated with one or more machines. This makes it a prerequisite for the reservation of these machines.</p>"
|
||||
filter:
|
||||
title: "Filter"
|
||||
content: "By default, only active courses are displayed here. Display the others by choosing another filter here."
|
||||
@ -1124,6 +1154,9 @@ en:
|
||||
welcome:
|
||||
title: "Invoices"
|
||||
content: "<p>Here you will be able to download invoices and credit notes issued, as well as manage everything related to accounting and invoicing.</p><p>If you use third-party software to manage your invoices, it is possible to deactivate the billing module. For this, contact your system administrator.</p>"
|
||||
welcome_manager:
|
||||
title: "Invoices"
|
||||
content: "Here you will be able to download invoices and create credit notes."
|
||||
list:
|
||||
title: "Invoices list"
|
||||
content: "By default, this table lists all the invoices and credit notes issued by Fab-manager. You can sort the list in a different order by clicking on the header of each column."
|
||||
|
@ -193,8 +193,8 @@ pt:
|
||||
booking: "Reserva"
|
||||
sold_out: "Esgotado"
|
||||
cancelled: "Cancelado"
|
||||
without_reservation: "Sem reservas"
|
||||
free_admission: "Admissão gratuita"
|
||||
no_reservations: "Sem reservas"
|
||||
free_admission: "Entrada gratuita"
|
||||
view_reservations: "Ver reservas"
|
||||
load_the_next_events: "Load the next events..."
|
||||
categories: "Categorias"
|
||||
|
@ -29,6 +29,7 @@ en:
|
||||
#left menu
|
||||
notifications: "Notifications"
|
||||
admin: "Admin"
|
||||
manager: "Manager"
|
||||
reduce_panel: "Reduce panel"
|
||||
#left menu (public)
|
||||
home: "Home"
|
||||
@ -146,7 +147,7 @@ en:
|
||||
from_date_to_date: "From {START} to {END}"
|
||||
on_the_date: "On the {DATE}"
|
||||
from_time_to_time: "From {START} to {END}"
|
||||
free_entry: "Free entry"
|
||||
without_reservation: "Without reservation"
|
||||
free_admission: "Free admission"
|
||||
full_price: "Full price: "
|
||||
event_full: "Event full"
|
||||
@ -241,8 +242,8 @@ en:
|
||||
your_subscription_expires_on_the_DATE: "Your subscription expires on the {DATE}"
|
||||
my_group: "My group"
|
||||
his_group: "{GENDER, select, male{His} female{Her} other{Its}} group"
|
||||
he_wants_to_change_group: "{ROLE, select, admin{The user wants} other{I want}} to change group"
|
||||
change_my_group: "Change {ROLE, select, admin{{GENDER, select, male{his} female{her} other{its}}} other{my}} group"
|
||||
he_wants_to_change_group: "{ROLE, select, member{I want} other{The user wants}} to change group"
|
||||
change_my_group: "Change {ROLE, select, member{my} other{{GENDER, select, male{his} female{her} other{its}}}} group"
|
||||
summary: "Summary"
|
||||
your_subscription_has_expired_on_the_DATE: "Your subscription has expired on the {DATE}"
|
||||
subscription_price: "Subscription price"
|
||||
@ -269,7 +270,7 @@ en:
|
||||
cancelled: "Cancelled"
|
||||
free_admission: "Free admission"
|
||||
still_available: "available place(s)"
|
||||
free_entry: "Free entry"
|
||||
without_reservation: "Without reservation"
|
||||
add_an_event: "Add an event"
|
||||
load_the_next_events: "Load the next events..."
|
||||
full_price_: "Full price:"
|
||||
@ -290,7 +291,7 @@ en:
|
||||
full_price_: "Full price:"
|
||||
tickets_still_availables: "Tickets still available:"
|
||||
sold_out: "Sold out."
|
||||
free_entry: "Free entry"
|
||||
without_reservation: "Without reservation"
|
||||
cancelled: "Cancelled"
|
||||
ticket: "{NUMBER, plural, one{ticket} other{tickets}}"
|
||||
make_a_gift_of_this_reservation: "Make a gift of this reservation"
|
||||
@ -387,8 +388,8 @@ en:
|
||||
title: "Subscriptions"
|
||||
content: "Subscriptions provide a way to segment your prices and provide benefits to regular users."
|
||||
admin:
|
||||
title: "Administrator section"
|
||||
content: "<p>All of the elements below are only accessible to administrators. They allow you to manage and configure Fab-manager.</p><p>At the end of this visit, click on one of them to find out more.</p>"
|
||||
title: "{ROLE} section"
|
||||
content: "<p>All of the elements below are only accessible to administrators and managers. They allow you to manage and configure Fab-manager.</p><p>At the end of this visit, click on one of them to find out more.</p>"
|
||||
about:
|
||||
title: "About"
|
||||
content: "A page that you can fully customize, to present your activity and your structure."
|
||||
@ -423,13 +424,25 @@ en:
|
||||
welcome:
|
||||
title: "Machines"
|
||||
content: "<p>Machines are the tools available for your users. You must create here the machines which can then be reserved by the members.</p><p>You can also create entries for non-bookable or free access machines, then you just need to not associate availability slots with them.</p>"
|
||||
welcome_manager:
|
||||
title: "Machines"
|
||||
content: "Machines are the tools available for the users to reserve."
|
||||
view:
|
||||
title: "View"
|
||||
content: "To modify or delete a machine, click here first. You will not be able to delete a machine that has already been associated with availability slots, but you can deactivate it."
|
||||
reserve:
|
||||
title: "Reserve"
|
||||
content: "Click here to access an agenda showing free slots. This will let you book this machine for an user and manage existing reservations."
|
||||
spaces:
|
||||
welcome:
|
||||
title: "Spaces"
|
||||
content: "<p>Spaces are places available for your users. For example, a meeting room or a woodshop. You must create here the spaces which can then be reserved by members.</p><p>The specificity of the spaces is that they can be reserved by several users at the same time.</p>"
|
||||
welcome_manager:
|
||||
title: "Spaces"
|
||||
content: "<p>Spaces are places available to users, by reservation. For example, a meeting room or a woodshop.</p><p>The specificity of the spaces is that they can be reserved by several users at the same time.</p>"
|
||||
view:
|
||||
title: "View"
|
||||
content: "To modify or delete a space, click here first. You will not be able to delete a space that has already been associated with availability slots, but you can deactivate it."
|
||||
reserve:
|
||||
title: "Reserve"
|
||||
content: "Click here to access an agenda showing free slots. This will let you book this space for an user and manage existing reservations."
|
@ -148,7 +148,7 @@ es:
|
||||
on_the_date: "El {DATE}"
|
||||
from_time_to_time: "Desde {START} hasta {END}"
|
||||
without_reservation: "Sin reserva"
|
||||
free_admission: "Admisión gratuita"
|
||||
free_admission: "Entrada gratuita"
|
||||
full_price: "Precio completo: "
|
||||
event_full: "Evento lleno"
|
||||
still_available: "Asiento(s) disponible(s): "
|
||||
|
@ -148,7 +148,7 @@ pt:
|
||||
on_the_date: "Em {DATE}"
|
||||
from_time_to_time: "Das {START} até {END}"
|
||||
without_reservation: "Sem reservas"
|
||||
free_admission: "Admissão grátis"
|
||||
free_admission: "Entrada grátis"
|
||||
full_price: "Valor inteira: "
|
||||
event_full: "Evento lotado"
|
||||
still_available: "Locais disponíveis: "
|
||||
|
@ -121,7 +121,7 @@ If set to 'false' the phone number won't be required to register a new user on t
|
||||
|
||||
BOOK_SLOT_AT_SAME_TIME
|
||||
|
||||
If set to 'false', users won't be able to book a machine/formation/event slot if they already have a reservation the same day at the same time.
|
||||
If set to 'true', users will be able to book a machine/formation/event slot, even if they already have a reservation the same day at the same time.
|
||||
<a name="USER_CONFIRMATION_NEEDED_TO_SIGN_IN"></a>
|
||||
|
||||
USER_CONFIRMATION_NEEDED_TO_SIGN_IN
|
||||
|
@ -48,7 +48,10 @@ namespace :fablab do
|
||||
|
||||
desc 'sync users to the stripe database'
|
||||
task sync_members: :environment do
|
||||
User.members.each do |member|
|
||||
puts 'We create all non-existing customers on stripe. This may take a while, please wait...'
|
||||
total = User.online_payers.count
|
||||
User.online_payers.each_with_index do |member, index|
|
||||
print_on_line "#{index} / #{total}"
|
||||
begin
|
||||
stp_customer = Stripe::Customer.retrieve member.stp_customer_id
|
||||
StripeWorker.perform_async(:create_stripe_customer, member.id) if stp_customer.nil? || stp_customer[:deleted]
|
||||
@ -56,6 +59,12 @@ namespace :fablab do
|
||||
StripeWorker.perform_async(:create_stripe_customer, member.id)
|
||||
end
|
||||
end
|
||||
puts 'Done'
|
||||
end
|
||||
|
||||
def print_on_line(str)
|
||||
print "#{str}\r"
|
||||
$stdout.flush
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user