1
0
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:
Du Peng 2023-04-25 16:00:22 +02:00
commit f39691d4c4
25 changed files with 65 additions and 43 deletions

View File

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

View File

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

View File

@ -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');
};
/**

View File

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

View File

@ -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);
},

View File

@ -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;
}
}
});

View File

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

View File

@ -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);
},

View File

@ -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);
},

View File

@ -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);
},

View File

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

View File

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

View File

@ -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" )%>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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