mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-11-29 10:24:20 +01:00
Merge branch 'dev' for release 6.0.4
This commit is contained in:
commit
f39691d4c4
@ -1,5 +1,13 @@
|
||||
# Changelog Fab-manager
|
||||
|
||||
## v6.0.4 2023 April 25
|
||||
|
||||
- Fix a bug: notification is broken when delete a project
|
||||
- Fix a bug: broken notifications email
|
||||
- Fix a bug: unable to show calendar
|
||||
- Update translations from Crowdin
|
||||
- [TODO DEPLOY] `rails fablab:maintenance:clean_abuse_notifications`
|
||||
|
||||
## v6.0.3 2023 April 12
|
||||
|
||||
- Fix a bug: unable to install Fab-manager by setup.sh
|
||||
|
@ -72,7 +72,7 @@ class API::PayzenController < API::PaymentsController
|
||||
|
||||
cart = shopping_cart
|
||||
|
||||
if order['answer']['transactions'].any? { |transaction| transaction['status'] == 'PAID' }
|
||||
if order['answer']['transactions'].all? { |transaction| transaction['status'] == 'PAID' }
|
||||
render on_payment_success(params[:order_id], cart)
|
||||
else
|
||||
render json: order['answer'], status: :unprocessable_entity
|
||||
@ -86,10 +86,11 @@ class API::PayzenController < API::PaymentsController
|
||||
|
||||
client = PayZen::Transaction.new
|
||||
transaction = client.get(params[:transaction_uuid])
|
||||
order = PayZen::Order.new.get(params[:order_id])
|
||||
|
||||
cart = shopping_cart
|
||||
|
||||
if transaction['answer']['status'] == 'PAID'
|
||||
if transaction['answer']['status'] == 'PAID' && order['answer']['transactions'].all? { |t| t['status'] == 'PAID' }
|
||||
render on_payment_success(params[:order_id], cart)
|
||||
else
|
||||
render json: transaction['answer'], status: :unprocessable_entity
|
||||
|
@ -54,7 +54,7 @@ export const PaymentScheduleItemActions: React.FC<PaymentScheduleItemActionsProp
|
||||
* Check if the current operator has administrative rights or is a normal member
|
||||
*/
|
||||
const isPrivileged = (): boolean => {
|
||||
return (operator.role === 'admin' || operator.role === 'manager');
|
||||
return (operator?.role === 'admin' || operator?.role === 'manager');
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -75,7 +75,7 @@ export const PayzenForm: React.FC<PayzenFormProps> = ({ onSubmit, onSuccess, onE
|
||||
if (updateCard) return onSuccess(null);
|
||||
|
||||
const transaction = event.clientAnswer.transactions[0];
|
||||
if (event.clientAnswer.orderStatus === 'PAID') {
|
||||
if (event.clientAnswer.orderStatus === 'PAID' && transaction?.status === 'PAID') {
|
||||
confirmPayment(event, transaction).then((confirmation) => {
|
||||
PayZenKR.current.removeForms().then(() => {
|
||||
onSuccess(confirmation);
|
||||
|
@ -69,8 +69,8 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
|
||||
snapDuration: BOOKING_SNAP,
|
||||
selectable: true,
|
||||
selectHelper: true,
|
||||
minTime: moment.duration(moment(bookingWindowStart.setting.value).format('HH:mm:ss')),
|
||||
maxTime: moment.duration(moment(bookingWindowEnd.setting.value).format('HH:mm:ss')),
|
||||
minTime: moment.duration(moment.utc(bookingWindowStart.setting.value).format('HH:mm:ss')),
|
||||
maxTime: moment.duration(moment.utc(bookingWindowEnd.setting.value).format('HH:mm:ss')),
|
||||
select (start, end, jsEvent, view) {
|
||||
return calendarSelectCb(start, end, jsEvent, view);
|
||||
},
|
||||
|
@ -71,8 +71,8 @@ Application.Controllers.controller('SettingsController', ['$scope', '$rootScope'
|
||||
$scope.subscriptionExplicationsAlert = { name: 'subscription_explications_alert', value: settingsPromise.subscription_explications_alert };
|
||||
$scope.eventExplicationsAlert = { name: 'event_explications_alert', value: settingsPromise.event_explications_alert };
|
||||
$scope.spaceExplicationsAlert = { name: 'space_explications_alert', value: settingsPromise.space_explications_alert };
|
||||
$scope.windowStart = { name: 'booking_window_start', value: settingsPromise.booking_window_start };
|
||||
$scope.windowEnd = { name: 'booking_window_end', value: settingsPromise.booking_window_end };
|
||||
$scope.windowStart = { name: 'booking_window_start', value: moment.utc(settingsPromise.booking_window_start).format('YYYY-MM-DD HH:mm:ss') };
|
||||
$scope.windowEnd = { name: 'booking_window_end', value: moment.utc(settingsPromise.booking_window_end).format('YYYY-MM-DD HH:mm:ss') };
|
||||
$scope.mainColorSetting = { name: 'main_color', value: settingsPromise.main_color };
|
||||
$scope.secondColorSetting = { name: 'secondary_color', value: settingsPromise.secondary_color };
|
||||
$scope.nameGenre = { name: 'name_genre', value: settingsPromise.name_genre };
|
||||
@ -487,8 +487,12 @@ Application.Controllers.controller('SettingsController', ['$scope', '$rootScope'
|
||||
|
||||
// we prevent the admin from setting the closing time before the opening time
|
||||
$scope.$watch('windowEnd.value', function (newValue, oldValue, scope) {
|
||||
if ($scope.windowStart && moment($scope.windowStart.value).isAfter(newValue)) {
|
||||
return $scope.windowEnd.value = oldValue;
|
||||
if (scope.windowStart) {
|
||||
const startTime = moment($scope.windowStart.value).format('HH:mm:ss');
|
||||
const endTime = moment(newValue).format('HH:mm:ss');
|
||||
if (startTime >= endTime) {
|
||||
scope.windowEnd.value = oldValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -204,8 +204,8 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
|
||||
center: 'title',
|
||||
right: ''
|
||||
},
|
||||
minTime: moment.duration(moment(bookingWindowStart.setting.value).format('HH:mm:ss')),
|
||||
maxTime: moment.duration(moment(bookingWindowEnd.setting.value).format('HH:mm:ss')),
|
||||
minTime: moment.duration(moment.utc(bookingWindowStart.setting.value).format('HH:mm:ss')),
|
||||
maxTime: moment.duration(moment.utc(bookingWindowEnd.setting.value).format('HH:mm:ss')),
|
||||
defaultView: window.innerWidth <= 480 ? 'agendaDay' : 'agendaWeek',
|
||||
eventClick (event, jsEvent, view) {
|
||||
return calendarEventClickCb(event, jsEvent, view);
|
||||
|
@ -447,8 +447,8 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$tran
|
||||
|
||||
// fullCalendar (v2) configuration
|
||||
$scope.calendarConfig = CalendarConfig({
|
||||
minTime: moment.duration(moment(settingsPromise.booking_window_start).format('HH:mm:ss')),
|
||||
maxTime: moment.duration(moment(settingsPromise.booking_window_end).format('HH:mm:ss')),
|
||||
minTime: moment.duration(moment.utc(settingsPromise.booking_window_start).format('HH:mm:ss')),
|
||||
maxTime: moment.duration(moment.utc(settingsPromise.booking_window_end).format('HH:mm:ss')),
|
||||
eventClick (event, jsEvent, view) {
|
||||
return calendarEventClickCb(event, jsEvent, view);
|
||||
},
|
||||
|
@ -385,8 +385,8 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$transi
|
||||
|
||||
// fullCalendar (v2) configuration
|
||||
$scope.calendarConfig = CalendarConfig({
|
||||
minTime: moment.duration(moment(settingsPromise.booking_window_start).format('HH:mm:ss')),
|
||||
maxTime: moment.duration(moment(settingsPromise.booking_window_end).format('HH:mm:ss')),
|
||||
minTime: moment.duration(moment.utc(settingsPromise.booking_window_start).format('HH:mm:ss')),
|
||||
maxTime: moment.duration(moment.utc(settingsPromise.booking_window_end).format('HH:mm:ss')),
|
||||
eventClick (event, jsEvent, view) {
|
||||
return calendarEventClickCb(event, jsEvent, view);
|
||||
},
|
||||
|
@ -155,8 +155,8 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$tra
|
||||
|
||||
// fullCalendar (v2) configuration
|
||||
$scope.calendarConfig = CalendarConfig({
|
||||
minTime: moment.duration(moment(settingsPromise.booking_window_start).format('HH:mm:ss')),
|
||||
maxTime: moment.duration(moment(settingsPromise.booking_window_end).format('HH:mm:ss')),
|
||||
minTime: moment.duration(moment.utc(settingsPromise.booking_window_start).format('HH:mm:ss')),
|
||||
maxTime: moment.duration(moment.utc(settingsPromise.booking_window_end).format('HH:mm:ss')),
|
||||
eventClick (event, jsEvent, view) {
|
||||
return calendarEventClickCb(event, jsEvent, view);
|
||||
},
|
||||
|
@ -40,6 +40,8 @@ class Project < ApplicationRecord
|
||||
has_many :project_steps, dependent: :destroy
|
||||
accepts_nested_attributes_for :project_steps, allow_destroy: true
|
||||
|
||||
has_many :abuses, as: :signaled, dependent: :destroy, class_name: 'Abuse'
|
||||
|
||||
# validations
|
||||
validates :author, :name, presence: true
|
||||
|
||||
|
@ -33,8 +33,8 @@ class EventService
|
||||
end
|
||||
|
||||
def date_range(starting, ending, all_day)
|
||||
start_date = Time.zone.parse(starting[:date])
|
||||
end_date = Time.zone.parse(ending[:date])
|
||||
start_date = Date.parse(starting[:date])
|
||||
end_date = Date.parse(ending[:date])
|
||||
start_time = starting[:time] ? Time.zone.parse(starting[:time]) : nil
|
||||
end_time = ending[:time] ? Time.zone.parse(ending[:time]) : nil
|
||||
if all_day || start_time.nil? || end_time.nil?
|
||||
|
@ -1,10 +1,10 @@
|
||||
<%= render 'notifications_mailer/shared/hello', recipient: @recipient %>
|
||||
|
||||
<p>
|
||||
<%= t('.body.low_stock', { PRODUCT: @attached_object.name }) %>
|
||||
<%= t('.body.low_stock', **{ PRODUCT: @attached_object.name }) %>
|
||||
</p>
|
||||
<p>
|
||||
<%= t('.body.stocks_state_html', { INTERNAL: @attached_object.stock['internal'], EXTERNAL: @attached_object.stock['external'] }) %>
|
||||
<%= t('.body.stocks_state_html', **{ INTERNAL: @attached_object.stock['internal'], EXTERNAL: @attached_object.stock['external'] }) %>
|
||||
</p>
|
||||
<p>
|
||||
<%=link_to( t('.body.manage_stock'), "#{root_url}#!/admin/store/products/#{@attached_object.id}/edit", target: "_blank" )%>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<%= render 'notifications_mailer/shared/hello', recipient: @recipient %>
|
||||
|
||||
<p>
|
||||
<%= t('.body.cancelled_training', {
|
||||
<%= t('.body.cancelled_training', **{
|
||||
TRAINING: @attached_object.trainings.first.name,
|
||||
DATE: I18n.l(@attached_object.start_at.to_date),
|
||||
START: I18n.l(@attached_object.start_at, format: :hour_minute),
|
||||
|
@ -6,7 +6,7 @@
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<%= t('.body.user_of_group_html', { GROUP: @attached_object&.group&.name }) %>
|
||||
<%= t('.body.user_of_group_html', GROUP: @attached_object&.group&.name) %>
|
||||
</p>
|
||||
|
||||
<% if @attached_object.invoicing_profile.organization %>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<%= render 'notifications_mailer/shared/hello', recipient: @recipient %>
|
||||
|
||||
<p><%= t('.body.new_account_imported', ID: @attached_object.id, PROVIDER: provider.name) %><br/>
|
||||
<%= t('.body.provider_uid', UID:@attached_object.uid) %></p>
|
||||
<%= t('.body.provider_uid', UID: @attached_object.uid) %></p>
|
||||
<% if provider.sso_fields.size > 1 %>
|
||||
<p><%= t('.body.known_information') %></p>
|
||||
<ul>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<%= render 'notifications_mailer/shared/hello', recipient: @recipient %>
|
||||
|
||||
<%= t('.body.training_expired_html', {
|
||||
<%= t('.body.training_expired_html', **{
|
||||
TRAINING: @attached_object.name,
|
||||
MACHINES: @attached_object.machines.map(&:name).join(', '),
|
||||
DATE: I18n.l((DateTime.current - @attached_object.authorization_period.months).to_date),
|
||||
|
@ -1,7 +1,7 @@
|
||||
<%= render 'notifications_mailer/shared/hello', recipient: @recipient %>
|
||||
|
||||
<p>
|
||||
<%= t('.body.cancelled_training', {
|
||||
<%= t('.body.cancelled_training', **{
|
||||
TRAINING: @attached_object.reservation.reservable.name,
|
||||
DATE: I18n.l(@attached_object.start_at.to_date),
|
||||
START: I18n.l(@attached_object.start_at, format: :hour_minute),
|
||||
|
@ -1,6 +1,6 @@
|
||||
<%= render 'notifications_mailer/shared/hello', recipient: @recipient %>
|
||||
|
||||
<%= t('.body.training_invalidated_html', {
|
||||
<%= t('.body.training_invalidated_html', **{
|
||||
TRAINING: @attached_object.name,
|
||||
MACHINES: @attached_object.machines.map(&:name).join(', '),
|
||||
DATE: I18n.l((DateTime.current - @attached_object.authorization_period.months).to_date),
|
||||
|
@ -112,7 +112,7 @@ it:
|
||||
create_success: "Lo spazio è stato creato correttamente"
|
||||
update_success: "Lo spazio è stato aggiornato correttamente"
|
||||
event_form:
|
||||
ACTION_title: "{ACTION, select, create{Nuovo} other{Aggiorna l'}} evento"
|
||||
ACTION_title: "{ACTION, select, create{Nuovo} other{Aggiorna}} evento"
|
||||
title: "Titolo"
|
||||
matching_visual: "Corrispondenza illustazione"
|
||||
description: "Descrizione"
|
||||
@ -152,7 +152,7 @@ it:
|
||||
every_month: "Ogni mese"
|
||||
every_year: "Ogni anno"
|
||||
plan_form:
|
||||
ACTION_title: "{ACTION, select, create{Nuovo} other{Aggiorna l'}}evento"
|
||||
ACTION_title: "{ACTION, select, create{Nuovo} other{Aggiorna}}evento"
|
||||
tab_settings: "Impostazioni"
|
||||
tab_usage_limits: "Limiti di utilizzo"
|
||||
description: "Descrizione"
|
||||
@ -285,7 +285,7 @@ it:
|
||||
#manage the trainings & machines slots
|
||||
calendar:
|
||||
calendar_management: "Gestione calendario"
|
||||
trainings: "Certificazioni"
|
||||
trainings: "Abilitazioni"
|
||||
machines: "Macchine"
|
||||
spaces: "Spazi"
|
||||
events: "Eventi"
|
||||
@ -416,7 +416,7 @@ it:
|
||||
themes: "Temi"
|
||||
add_a_new_theme: "Aggiungi un nuovo tema"
|
||||
licences: "Licenze"
|
||||
statuses: "Statuto"
|
||||
statuses: "Status"
|
||||
description: "Descrizione"
|
||||
add_a_new_licence: "Aggiungere una nuova licenza"
|
||||
manage_abuses: "Gestisci i report"
|
||||
@ -1783,7 +1783,7 @@ it:
|
||||
title: "Titolo"
|
||||
fablab_title: "Titolo del FabLab"
|
||||
title_concordance: "Corrispondenza del titolo"
|
||||
male: "Machio."
|
||||
male: "Maschio."
|
||||
female: "Femmina."
|
||||
neutral: "Neutrale."
|
||||
eg: "es:"
|
||||
@ -1804,7 +1804,7 @@ it:
|
||||
public_registrations_allowed: "Registrazioni pubbliche consentite"
|
||||
help: "Aiuto"
|
||||
feature_tour: "Tour delle funzionalità"
|
||||
feature_tour_info_html: "<p>Quando un amministratore o un manager è connesso, un tour di funzionalità verrà attivato la prima volta che visita ogni sezione dell'applicazione. È possibile modificare questo comportamento in uno dei seguenti valori:</p><ul><li>« Una volta » per mantenere il comportamento predefinito.</li><li>« Per sessione » per visualizzare i tour ogni volta che riapri l'applicazione.</li><li>« trigger manuale» per impedire la visualizzazione automatica dei tour. Sarà comunque possibile attivarli premendo il tasto F1 o cliccando su « Aiuto » nel menu dell'utente.</li></ul>"
|
||||
feature_tour_info_html: "<p>Quando un amministratore o un manager è connesso, un tour di funzionalità verrà attivato la prima volta che visita ogni sezione dell'applicazione. È possibile modificare questo comportamento in uno dei seguenti valori:</p><ul><li>« Una volta » per mantenere il comportamento predefinito.</li><li>« Per sessione » per visualizzare i tour ogni volta che riapri l'applicazione.</li><li>« Trigger manuale» per impedire la visualizzazione automatica dei tour. Sarà comunque possibile attivarli premendo il tasto F1 o cliccando su « Aiuto » nel menu dell'utente.</li></ul>"
|
||||
feature_tour_display_mode: "Modalità visualizzazione tour funzionalità"
|
||||
display_mode:
|
||||
once: "Una volta"
|
||||
@ -2373,7 +2373,7 @@ it:
|
||||
filter_clear: "Cancella tutto"
|
||||
filter_apply: "Applica"
|
||||
filter_ref: "Per riferimento"
|
||||
filter_status: "Per stato"
|
||||
filter_status: "Per status"
|
||||
filter_client: "Per cliente"
|
||||
filter_period: "Per periodo"
|
||||
filter_period_from: "Da"
|
||||
|
@ -185,12 +185,12 @@ it:
|
||||
rough_draft: "Bozza preliminare"
|
||||
status_filter:
|
||||
all_statuses: "Tutti gli stati"
|
||||
select_status: "Seleziona uno stato"
|
||||
select_status: "Seleziona uno status"
|
||||
#details of a projet
|
||||
projects_show:
|
||||
rough_draft: "Bozza"
|
||||
project_description: "Descrizione del progetto"
|
||||
by_name: "Per {NAME}"
|
||||
by_name: "di {NAME}"
|
||||
step_N: "Fase {INDEX}"
|
||||
share_on_facebook: "Condividi su Facebook"
|
||||
share_on_twitter: "Condividi su Twitter"
|
||||
@ -237,7 +237,7 @@ it:
|
||||
all_machines: "Tutte le macchine"
|
||||
machine_card:
|
||||
book: "Prenota"
|
||||
consult: "Guarda"
|
||||
consult: "Vedi"
|
||||
#details of a machine
|
||||
machines_show:
|
||||
book_this_machine: "Modifica questa macchina"
|
||||
@ -549,7 +549,7 @@ it:
|
||||
content: "Le macchine sono gli utensili a disposizione degli utenti da prenotare."
|
||||
view:
|
||||
title: "Visualizza"
|
||||
content: "Per modificare o eliminare una macchina, prima clicca qui. Non sarai in grado di eliminare una macchina che è già stata associata a uno slot, ma potrai disattivarla."
|
||||
content: "Per modificare o eliminare una macchina, clicca qui. Non sarai in grado di eliminare una macchina che è già stata associata a uno slot di disponibilità, ma potrai disattivarla."
|
||||
reserve:
|
||||
title: "Prenota"
|
||||
content: "Clicca qui per accedere a un calendario che mostra slot liberi. Questo ti permetterà di prenotare questa macchina per un utente e di gestire le prenotazioni esistenti."
|
||||
|
@ -4,7 +4,7 @@ it:
|
||||
#translations of common buttons
|
||||
buttons:
|
||||
confirm_changes: "Conferma le modifiche"
|
||||
consult: "Guarda"
|
||||
consult: "Vedi"
|
||||
edit: "Modifica"
|
||||
change: "Modifica"
|
||||
delete: "Elimina"
|
||||
|
@ -155,5 +155,12 @@ namespace :fablab do
|
||||
end_date = args.end == 'today' ? Time.current.end_of_day : start_date.next_month
|
||||
[start_date, end_date]
|
||||
end
|
||||
|
||||
desc 'Clean the abuse notifications if signaled object is null'
|
||||
task clean_abuse_notifications: :environment do
|
||||
Abuse.all.each do |abuse|
|
||||
abuse.destroy if abuse.signaled.nil?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "fab-manager",
|
||||
"version": "6.0.3",
|
||||
"version": "6.0.4",
|
||||
"description": "Fab-manager is the FabLab management solution. It provides a comprehensive, web-based, open-source tool to simplify your administrative tasks and your marker's projects.",
|
||||
"keywords": [
|
||||
"fablab",
|
||||
|
@ -46,8 +46,8 @@ class Events::TimezoneTest < ActionDispatch::IntegrationTest
|
||||
e = Event.find_by(id: event[:id])
|
||||
assert_not_nil e, 'Event was not created in database'
|
||||
|
||||
assert_equal '2023-06-15', e.availability.start_at.to_date.iso8601
|
||||
assert_equal '2023-06-15', e.availability.end_at.to_date.iso8601
|
||||
assert_equal '2023-06-14', e.availability.start_at.to_date.iso8601
|
||||
assert_equal '2023-06-14', e.availability.end_at.to_date.iso8601
|
||||
assert_equal '09:48', e.availability.start_at.strftime('%R')
|
||||
assert_equal '11:48', e.availability.end_at.strftime('%R')
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user