mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-20 14:54:15 +01:00
Merge branch 'dev' for release 5.3.9
This commit is contained in:
commit
e66a6adece
@ -1,10 +1,16 @@
|
||||
# Changelog Fab-manager
|
||||
|
||||
## v5.3.9 2022 April 01
|
||||
|
||||
- Optimise sql query, avoid to N+1
|
||||
- Fix a security issue: updated ansi-regex to 4.1.1 to fix [CVE-2021-3807](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-3807)
|
||||
- Fix a bug: unable to show payment modal for stripe
|
||||
|
||||
## v5.3.8 2022 March 29
|
||||
|
||||
- Updated the version of ruby to 2.6.9
|
||||
- Fix a bug: unable to show payment schedules list if active PayZen
|
||||
- Fix a bug: unable to set user's invoicing profile names and email if active address required in create user form
|
||||
- Fix a security issue: updated ruby to 2.6.9 to fix [CVE-2021-31810](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-31810), [CVE-2021-32066](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32066) , [CVE-2021-31799](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41817), [CVE-2021-31799](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41817) and [CVE-2021-41819](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41819)
|
||||
- [TODO DEPLOY] `rails fablab:fix:invoices_without_names_and_email`
|
||||
|
||||
## v5.3.7 2022 March 28
|
||||
@ -28,7 +34,6 @@
|
||||
- Removed unmaintained gem sidekiq-cron and replaced it with sidekiq-scheduler
|
||||
- Removed unmaintained @rails/webpacker v5 and replaced it with shakapacker v6.2.0
|
||||
- Removed dependency to auto-ngtemplate-loader
|
||||
- Removed support for Universal Analytics
|
||||
- Updated deprecated division operators in sass
|
||||
- Fix a bug: prepaid-packs purchases are reported as subscriptions in the statistics
|
||||
- Fix a bug: error Couldn't find the binary git during assets compilation
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { ReactEventHandler, useState } from 'react';
|
||||
import React, { ReactEventHandler, useState, useEffect, ReactElement } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Loader } from '../base/loader';
|
||||
import _ from 'lodash';
|
||||
@ -11,6 +11,8 @@ import {
|
||||
import FormatLib from '../../lib/format';
|
||||
import { PaymentScheduleItemActions, TypeOnce } from './payment-schedule-item-actions';
|
||||
import { StripeElements } from '../payment/stripe/stripe-elements';
|
||||
import SettingAPI from '../../api/setting';
|
||||
import { Setting, SettingName } from '../../models/setting';
|
||||
|
||||
interface PaymentSchedulesTableProps {
|
||||
paymentSchedules: Array<PaymentSchedule>,
|
||||
@ -35,6 +37,13 @@ const PaymentSchedulesTableComponent: React.FC<PaymentSchedulesTableProps> = ({
|
||||
[TypeOnce.CardUpdate, new Map()],
|
||||
[TypeOnce.UpdatePaymentMean, new Map()]
|
||||
]));
|
||||
const [gateway, setGateway] = useState<Setting>(null);
|
||||
|
||||
useEffect(() => {
|
||||
SettingAPI.get(SettingName.PaymentGateway)
|
||||
.then(setting => setGateway(setting))
|
||||
.catch(error => onError(error));
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Check if the requested payment schedule is displayed with its deadlines (PaymentScheduleItem) or without them
|
||||
@ -110,76 +119,98 @@ const PaymentSchedulesTableComponent: React.FC<PaymentSchedulesTableProps> = ({
|
||||
refreshList();
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<StripeElements>
|
||||
<table className="schedules-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="w-35" />
|
||||
<th className="w-200">{t('app.shared.schedules_table.schedule_num')}</th>
|
||||
<th className="w-200">{t('app.shared.schedules_table.date')}</th>
|
||||
<th className="w-120">{t('app.shared.schedules_table.price')}</th>
|
||||
{showCustomer && <th className="w-200">{t('app.shared.schedules_table.customer')}</th>}
|
||||
<th className="w-200"/>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{paymentSchedules.map(p => <tr key={p.id}>
|
||||
<td colSpan={showCustomer ? 6 : 5}>
|
||||
<table className="schedules-table-body">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="w-35 row-header" onClick={togglePaymentScheduleDetails(p.id)}>{expandCollapseIcon(p.id)}</td>
|
||||
<td className="w-200">{p.reference}</td>
|
||||
<td className="w-200">{FormatLib.date(_.minBy(p.items, 'due_date').due_date)}</td>
|
||||
<td className="w-120">{FormatLib.price(p.total)}</td>
|
||||
{showCustomer && <td className="w-200">{p.user.name}</td>}
|
||||
<td className="w-200">{downloadScheduleButton(p.id)}</td>
|
||||
</tr>
|
||||
<tr style={{ display: statusDisplay(p.id) }}>
|
||||
<td className="w-35" />
|
||||
<td colSpan={showCustomer ? 5 : 4}>
|
||||
<div>
|
||||
<table className="schedule-items-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="w-120">{t('app.shared.schedules_table.deadline')}</th>
|
||||
<th className="w-120">{t('app.shared.schedules_table.amount')}</th>
|
||||
<th className="w-200">{t('app.shared.schedules_table.state')}</th>
|
||||
<th className="w-200" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{_.orderBy(p.items, 'due_date').map(item => <tr key={item.id}>
|
||||
<td>{FormatLib.date(item.due_date)}</td>
|
||||
<td>{FormatLib.price(item.amount)}</td>
|
||||
<td>{formatState(item, p)}</td>
|
||||
<td>
|
||||
<PaymentScheduleItemActions paymentScheduleItem={item}
|
||||
paymentSchedule={p}
|
||||
onError={onError}
|
||||
onSuccess={refreshSchedulesTable}
|
||||
onCardUpdateSuccess={onCardUpdateSuccess}
|
||||
operator={operator}
|
||||
displayOnceMap={displayOnceMap}
|
||||
show={isExpanded(p.id)}/>
|
||||
</td>
|
||||
</tr>)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>)}
|
||||
</tbody>
|
||||
</table>
|
||||
</StripeElements>
|
||||
</div>
|
||||
);
|
||||
const renderPaymentSchedulesTable = (): ReactElement => {
|
||||
return (
|
||||
<table className="schedules-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="w-35" />
|
||||
<th className="w-200">{t('app.shared.schedules_table.schedule_num')}</th>
|
||||
<th className="w-200">{t('app.shared.schedules_table.date')}</th>
|
||||
<th className="w-120">{t('app.shared.schedules_table.price')}</th>
|
||||
{showCustomer && <th className="w-200">{t('app.shared.schedules_table.customer')}</th>}
|
||||
<th className="w-200"/>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{paymentSchedules.map(p => <tr key={p.id}>
|
||||
<td colSpan={showCustomer ? 6 : 5}>
|
||||
<table className="schedules-table-body">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="w-35 row-header" onClick={togglePaymentScheduleDetails(p.id)}>{expandCollapseIcon(p.id)}</td>
|
||||
<td className="w-200">{p.reference}</td>
|
||||
<td className="w-200">{FormatLib.date(_.minBy(p.items, 'due_date').due_date)}</td>
|
||||
<td className="w-120">{FormatLib.price(p.total)}</td>
|
||||
{showCustomer && <td className="w-200">{p.user.name}</td>}
|
||||
<td className="w-200">{downloadScheduleButton(p.id)}</td>
|
||||
</tr>
|
||||
<tr style={{ display: statusDisplay(p.id) }}>
|
||||
<td className="w-35" />
|
||||
<td colSpan={showCustomer ? 5 : 4}>
|
||||
<div>
|
||||
<table className="schedule-items-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="w-120">{t('app.shared.schedules_table.deadline')}</th>
|
||||
<th className="w-120">{t('app.shared.schedules_table.amount')}</th>
|
||||
<th className="w-200">{t('app.shared.schedules_table.state')}</th>
|
||||
<th className="w-200" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{_.orderBy(p.items, 'due_date').map(item => <tr key={item.id}>
|
||||
<td>{FormatLib.date(item.due_date)}</td>
|
||||
<td>{FormatLib.price(item.amount)}</td>
|
||||
<td>{formatState(item, p)}</td>
|
||||
<td>
|
||||
<PaymentScheduleItemActions paymentScheduleItem={item}
|
||||
paymentSchedule={p}
|
||||
onError={onError}
|
||||
onSuccess={refreshSchedulesTable}
|
||||
onCardUpdateSuccess={onCardUpdateSuccess}
|
||||
operator={operator}
|
||||
displayOnceMap={displayOnceMap}
|
||||
show={isExpanded(p.id)}/>
|
||||
</td>
|
||||
</tr>)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>)}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine which gateway is enabled and return the appropriate payment schedules
|
||||
*/
|
||||
if (gateway === null) return <div/>;
|
||||
|
||||
switch (gateway.value) {
|
||||
case 'stripe':
|
||||
return (
|
||||
<StripeElements>
|
||||
{renderPaymentSchedulesTable()}
|
||||
</StripeElements>
|
||||
);
|
||||
case 'payzen':
|
||||
return (
|
||||
<div>
|
||||
{renderPaymentSchedulesTable()}
|
||||
</div>
|
||||
);
|
||||
case null:
|
||||
default:
|
||||
console.error(`[PaymentSchedulesTable] Unimplemented gateway: ${gateway.value}`);
|
||||
return <div />;
|
||||
}
|
||||
};
|
||||
PaymentSchedulesTableComponent.defaultProps = { showCustomer: false };
|
||||
|
||||
|
@ -27,7 +27,6 @@ export const StripeElements: React.FC = memo(({ children }) => {
|
||||
{stripe && <Elements stripe={stripe}>
|
||||
{children}
|
||||
</Elements>}
|
||||
{!stripe && children}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -103,14 +103,14 @@ class Availabilities::AvailabilitiesService
|
||||
def availabilities(reservable, type, user)
|
||||
if user.admin? || user.manager?
|
||||
reservable.availabilities
|
||||
.includes(:tags)
|
||||
.includes(:tags, :plans)
|
||||
.where('end_at > ? AND available_type = ?', 1.month.ago, type)
|
||||
.where(lock: false)
|
||||
else
|
||||
end_at = @maximum_visibility[:other]
|
||||
end_at = @maximum_visibility[:year] if subscription_year?(user)
|
||||
reservable.availabilities
|
||||
.includes(:tags)
|
||||
.includes(:tags, :plans)
|
||||
.where('end_at > ? AND end_at < ? AND available_type = ?', DateTime.current, end_at, type)
|
||||
.where('availability_tags.tag_id' => user.tag_ids.concat([nil]))
|
||||
.where(lock: false)
|
||||
@ -127,14 +127,14 @@ class Availabilities::AvailabilitiesService
|
||||
# who made the request?
|
||||
# 1) an admin (he can see all availabilities of 1 month ago and future)
|
||||
if @current_user.admin?
|
||||
availabilities.includes(:tags, :slots, trainings: [:machines])
|
||||
availabilities.includes(:tags, :slots, :plans, trainings: [:machines])
|
||||
.where('availabilities.start_at > ?', 1.month.ago)
|
||||
.where(lock: false)
|
||||
# 2) an user (he cannot see availabilities further than 1 (or 3) months)
|
||||
else
|
||||
end_at = @maximum_visibility[:other]
|
||||
end_at = @maximum_visibility[:year] if show_extended_slots?(user)
|
||||
availabilities.includes(:tags, :slots, :availability_tags, trainings: [:machines])
|
||||
availabilities.includes(:tags, :slots, :availability_tags, :plans, trainings: [:machines])
|
||||
.where('availabilities.start_at > ? AND availabilities.start_at < ?', DateTime.current, end_at)
|
||||
.where('availability_tags.tag_id' => user.tag_ids.concat([nil]))
|
||||
.where(lock: false)
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "fab-manager",
|
||||
"version": "5.3.8",
|
||||
"version": "5.3.9",
|
||||
"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",
|
||||
|
@ -2121,9 +2121,9 @@ ansi-html-community@^0.0.8:
|
||||
integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==
|
||||
|
||||
ansi-regex@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
|
||||
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed"
|
||||
integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==
|
||||
|
||||
ansi-regex@^5.0.0:
|
||||
version "5.0.0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user