diff --git a/CHANGELOG.md b/CHANGELOG.md index 953eb15ea..0dffb3842 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Add extra_authorize_params to OpenIdConnect config - Improvement : add a notification to remind users to upload their supporting documents - Cancel payment schedule subscription after update the payment mean +- admin can see reservations of a member ## v6.0.14 2023 September 6 diff --git a/app/frontend/src/javascript/components/dashboard/reservations/credits-panel.tsx b/app/frontend/src/javascript/components/dashboard/reservations/credits-panel.tsx index 2790485b3..46ed36cef 100644 --- a/app/frontend/src/javascript/components/dashboard/reservations/credits-panel.tsx +++ b/app/frontend/src/javascript/components/dashboard/reservations/credits-panel.tsx @@ -6,9 +6,11 @@ import { useTranslation } from 'react-i18next'; import { Credit, CreditableType } from '../../../models/credit'; import CreditAPI from '../../../api/credit'; import { HtmlTranslate } from '../../base/html-translate'; +import { User } from '../../../models/user'; interface CreditsPanelProps { userId: number, + currentUser?: User, onError: (message: string) => void, reservableType: CreditableType } @@ -16,7 +18,7 @@ interface CreditsPanelProps { /** * List all available credits for the given user and the given resource */ -const CreditsPanel: React.FC = ({ userId, onError, reservableType }) => { +const CreditsPanel: React.FC = ({ userId, currentUser = null, onError, reservableType }) => { const { t } = useTranslation('logged'); const [credits, setCredits] = useState>([]); @@ -37,16 +39,30 @@ const CreditsPanel: React.FC = ({ userId, onError, reservable /** * Display a placeholder when there's no credits to display */ - const noCredits = (): ReactNode => { + const noCredits = (currentUser: User): ReactNode => { return ( -
{t('app.logged.dashboard.reservations_dashboard.credits_panel.no_credits')}
+
{t(`app.logged.dashboard.reservations_dashboard.${translationKeyPrefix(currentUser)}.no_credits`) /* eslint-disable-line fabmanager/scoped-translation */ }
); }; + /** + * returns true if there is a currentUser and current user is manager or admin + */ + const currentUserIsAdminOrManager = (currentUser: User): boolean => { + return currentUser && (currentUser.role === 'admin' || currentUser.role === 'manager'); + }; + + /** + * returns translation key prefix + */ + const translationKeyPrefix = (currentUser: User): string => { + return currentUserIsAdminOrManager(currentUser) ? 'credits_panel_as_admin' : 'credits_panel'; + }; + return ( -

{t('app.logged.dashboard.reservations_dashboard.credits_panel.title')}

- {credits.length !== 0 && +

{t(`app.logged.dashboard.reservations_dashboard.${translationKeyPrefix(currentUser)}.title`) /* eslint-disable-line fabmanager/scoped-translation */}

+ {credits.length !== 0 && !currentUserIsAdminOrManager(currentUser) &&
{t('app.logged.dashboard.reservations_dashboard.credits_panel.info')}
@@ -56,14 +72,14 @@ const CreditsPanel: React.FC = ({ userId, onError, reservable {credits.map(c =>

{c.creditable.name}

-
+
{(c.hours_used && c.hours_used > 0) && - + }

)} - {credits.length === 0 && noCredits()} + {credits.length === 0 && noCredits(currentUser)}
); }; diff --git a/app/frontend/src/javascript/components/dashboard/reservations/prepaid-packs-panel.tsx b/app/frontend/src/javascript/components/dashboard/reservations/prepaid-packs-panel.tsx index c01deefcd..9051023a9 100644 --- a/app/frontend/src/javascript/components/dashboard/reservations/prepaid-packs-panel.tsx +++ b/app/frontend/src/javascript/components/dashboard/reservations/prepaid-packs-panel.tsx @@ -21,13 +21,14 @@ import { HtmlTranslate } from '../../base/html-translate'; interface PrepaidPacksPanelProps { user: User, + currentUser?: User, onError: (message: string) => void } /** * List all available prepaid packs for the given user */ -const PrepaidPacksPanel: React.FC = ({ user, onError }) => { +const PrepaidPacksPanel: React.FC = ({ user, currentUser = null, onError }) => { const { t } = useTranslation('logged'); const [machines, setMachines] = useState>([]); @@ -101,6 +102,20 @@ const PrepaidPacksPanel: React.FC = ({ user, onError }) return (packs.length > 0 && (!packsForSubscribers || (packsForSubscribers && user.subscribed_plan != null))); }; + /** + * returns true if there is a currentUser and current user is manager or admin + */ + const currentUserIsAdminOrManager = (currentUser: User): boolean => { + return currentUser && (currentUser.role === 'admin' || currentUser.role === 'manager'); + }; + + /** + * returns translation key prefix + */ + const translationKeyPrefix = (currentUser: User): string => { + return currentUserIsAdminOrManager(currentUser) ? 'prepaid_packs_panel_as_admin' : 'prepaid_packs_panel'; + }; + /** * Callback triggered when a prepaid pack was successfully bought: refresh the list of packs for the user */ @@ -113,7 +128,7 @@ const PrepaidPacksPanel: React.FC = ({ user, onError }) return ( -

{t('app.logged.dashboard.reservations_dashboard.prepaid_packs_panel.title')}

+

{t(`app.logged.dashboard.reservations_dashboard.${translationKeyPrefix(currentUser)}.title`) /* eslint-disable-line fabmanager/scoped-translation */}

{userPacks.map(pack => (
@@ -124,7 +139,7 @@ const PrepaidPacksPanel: React.FC = ({ user, onError })

{pack.prepaid_pack.priceable.name}

- {FormatLib.date(pack.expires_at) &&

{FormatLib.date(pack.expires_at)}

} + {pack.expires_at && FormatLib.date(pack.expires_at) &&

{FormatLib.date(pack.expires_at)}

}

{(pack.prepaid_pack.minutes - pack.minutes_used) / 60}H / {pack.prepaid_pack.minutes / 60}H

@@ -143,7 +158,7 @@ const PrepaidPacksPanel: React.FC = ({ user, onError }) ))} - {canBuyPacks() &&
+ {canBuyPacks() && !currentUserIsAdminOrManager(currentUser) &&

{t('app.logged.dashboard.reservations_dashboard.prepaid_packs_panel.cta_info')}

@@ -163,7 +178,7 @@ const PrepaidPacksPanel: React.FC = ({ user, onError }) onSuccess={onPackBoughtSuccess} />}
} {packs.length === 0 &&

{t('app.logged.dashboard.reservations_dashboard.prepaid_packs_panel.no_packs')}

} - {(packsForSubscribers && user.subscribed_plan == null && packs.length > 0) && + {(packsForSubscribers && user.subscribed_plan == null && packs.length > 0 && !currentUserIsAdminOrManager(currentUser)) && } diff --git a/app/frontend/src/javascript/components/dashboard/reservations/reservations-dashboard.tsx b/app/frontend/src/javascript/components/dashboard/reservations/reservations-dashboard.tsx index f37f399cb..5f9ccbea4 100644 --- a/app/frontend/src/javascript/components/dashboard/reservations/reservations-dashboard.tsx +++ b/app/frontend/src/javascript/components/dashboard/reservations/reservations-dashboard.tsx @@ -14,13 +14,14 @@ declare const Application: IApplication; interface ReservationsDashboardProps { onError: (message: string) => void, - user: User + user: User, + currentUser?: User } /** * User dashboard showing everything about his spaces/machine reservations and also remaining credits */ -const ReservationsDashboard: React.FC = ({ onError, user }) => { +const ReservationsDashboard: React.FC = ({ onError, user, currentUser = null }) => { const { t } = useTranslation('logged'); const [modules, setModules] = useState>(); @@ -34,17 +35,17 @@ const ReservationsDashboard: React.FC = ({ onError,
{modules?.get('machines_module') !== 'false' &&

{t('app.logged.dashboard.reservations_dashboard.machine_section_title')}

- - - + + +
} {modules?.get('spaces_module') !== 'false' &&

{t('app.logged.dashboard.reservations_dashboard.space_section_title')}

- - + +
}
); }; -Application.Components.component('reservationsDashboard', react2angular(ReservationsDashboard, ['onError', 'user'])); +Application.Components.component('reservationsDashboard', react2angular(ReservationsDashboard, ['onError', 'user', 'currentUser'])); diff --git a/app/frontend/src/javascript/components/dashboard/reservations/reservations-panel.tsx b/app/frontend/src/javascript/components/dashboard/reservations/reservations-panel.tsx index 680a17b17..aad840fa4 100644 --- a/app/frontend/src/javascript/components/dashboard/reservations/reservations-panel.tsx +++ b/app/frontend/src/javascript/components/dashboard/reservations/reservations-panel.tsx @@ -9,9 +9,11 @@ import { Loader } from '../../base/loader'; import FormatLib from '../../../lib/format'; import _ from 'lodash'; import { FabButton } from '../../base/fab-button'; +import { User } from '../../../models/user'; interface SpaceReservationsProps { userId: number, + currentUser?: User, onError: (message: string) => void, reservableType: 'Machine' | 'Space' } @@ -19,7 +21,7 @@ interface SpaceReservationsProps { /** * List all reservations for the given user and the given type */ -const ReservationsPanel: React.FC = ({ userId, onError, reservableType }) => { +const ReservationsPanel: React.FC = ({ userId, currentUser = null, onError, reservableType }) => { const { t } = useTranslation('logged'); const [reservations, setReservations] = useState>([]); @@ -71,6 +73,20 @@ const ReservationsPanel: React.FC = ({ userId, onError, return reservation.slots_reservations_attributes.map(sr => sr.canceled_at).every(ca => ca != null); }; + /** + * returns true if there is a currentUser and current user is manager or admin + */ + const currentUserIsAdminOrManager = (currentUser: User): boolean => { + return currentUser && (currentUser.role === 'admin' || currentUser.role === 'manager'); + }; + + /** + * returns translation key prefix + */ + const translationKeyPrefix = (currentUser: User): string => { + return currentUserIsAdminOrManager(currentUser) ? 'reservations_panel_as_admin' : 'reservations_panel'; + }; + /** * Render the reservation in a user-friendly way */ @@ -95,7 +111,7 @@ const ReservationsPanel: React.FC = ({ userId, onError, return ( -

{t('app.logged.dashboard.reservations_dashboard.reservations_panel.title')}

+

{t(`app.logged.dashboard.reservations_dashboard.${translationKeyPrefix(currentUser)}.title`) /* eslint-disable-line fabmanager/scoped-translation */}

{futur.length === 0 ? noReservations() diff --git a/app/frontend/templates/admin/members/edit.html b/app/frontend/templates/admin/members/edit.html index 287a61911..c3a01fbb6 100644 --- a/app/frontend/templates/admin/members/edit.html +++ b/app/frontend/templates/admin/members/edit.html @@ -66,6 +66,10 @@ + + + + {USED} {USED, plural, =0{credit} one{credit} other{credits}}." no_credits: "You don't have any credits yet. Some subscriptions may allow you to book some slots for free." + credits_panel_as_admin: + title: "Credits" + remaining_credits_html: "{REMAINING} {REMAINING, plural, one{slot} other{slots}} can be booked for free." + used_credits_html: " {USED} {USED, plural, =0{credit} one{credit} other{credits}} already used." + no_credits: "No credits yet." prepaid_packs_panel: title: "My prepaid packs" name: "Prepaid pack name" @@ -172,6 +179,8 @@ de: cta_button: "Buy a pack" no_packs: "No prepaid packs available for sale" reserved_for_subscribers_html: 'The purchase of prepaid packs is reserved for subscribers. Subscribe now to benefit.' + prepaid_packs_panel_as_admin: + title: "Prepaid packs" #public profil of a member members_show: members_list: "Mitgliederliste" diff --git a/config/locales/app.logged.en.yml b/config/locales/app.logged.en.yml index 202b90b56..7c4dfc71b 100644 --- a/config/locales/app.logged.en.yml +++ b/config/locales/app.logged.en.yml @@ -169,12 +169,19 @@ en: no_reservation: "No reservation" show_more: "Show more" cancelled_slot: "Cancelled" + reservations_panel_as_admin: + title: "Reservations" credits_panel: title: "My credits" info: "Your subscription comes with free credits you can use when reserving" remaining_credits_html: "You can book {REMAINING} {REMAINING, plural, one{slot} other{slots}} for free." used_credits_html: "You have already used {USED} {USED, plural, =0{credit} one{credit} other{credits}}." no_credits: "You don't have any credits yet. Some subscriptions may allow you to book some slots for free." + credits_panel_as_admin: + title: "Credits" + remaining_credits_html: "{REMAINING} {REMAINING, plural, one{slot} other{slots}} can be booked for free." + used_credits_html: " {USED} {USED, plural, =0{credit} one{credit} other{credits}} already used." + no_credits: "No credits yet." prepaid_packs_panel: title: "My prepaid packs" name: "Prepaid pack name" @@ -187,6 +194,8 @@ en: cta_button: "Buy a pack" no_packs: "No prepaid packs available for sale" reserved_for_subscribers_html: 'The purchase of prepaid packs is reserved for subscribers. Subscribe now to benefit.' + prepaid_packs_panel_as_admin: + title: "Prepaid packs" #public profil of a member members_show: members_list: "Members list" diff --git a/config/locales/app.logged.es.yml b/config/locales/app.logged.es.yml index 4e80d5a2a..b022bb6d5 100644 --- a/config/locales/app.logged.es.yml +++ b/config/locales/app.logged.es.yml @@ -154,12 +154,19 @@ es: no_reservation: "Sin reserva" show_more: "Mostrar más" cancelled_slot: "Cancelado" + reservations_panel_as_admin: + title: "Reservations" credits_panel: title: "Mis créditos" info: "Su suscripción incluye créditos gratuitos que puede utilizar al reservar" remaining_credits_html: "Puede reservar {REMAINING} {REMAINING, plural, one{franja horaria} other{franjas horarias}} gratis." used_credits_html: "Ya has utilizado {USED} {USED, plural, =0{crédito} one{crédito} other{créditos}}." no_credits: "Aún no tienes créditos. Algunos abonos te permiten reservar franjas horarias gratuitamente." + credits_panel_as_admin: + title: "Credits" + remaining_credits_html: "{REMAINING} {REMAINING, plural, one{slot} other{slots}} can be booked for free." + used_credits_html: " {USED} {USED, plural, =0{credit} one{credit} other{credits}} already used." + no_credits: "No credits yet." prepaid_packs_panel: title: "Mis paquetes de prepago" name: "Nombre del paquete prepago" @@ -172,6 +179,8 @@ es: cta_button: "Comprar un paquete" no_packs: "No hay paquetes de prepago disponibles para la venta" reserved_for_subscribers_html: 'La compra de paquetes de prepago está reservada a los abonados. Suscríbete ahora para beneficiarte.' + prepaid_packs_panel_as_admin: + title: "Prepaid packs" #public profil of a member members_show: members_list: "Lista de miembros" diff --git a/config/locales/app.logged.fr.yml b/config/locales/app.logged.fr.yml index 8a861f2fe..6a67e6365 100644 --- a/config/locales/app.logged.fr.yml +++ b/config/locales/app.logged.fr.yml @@ -169,12 +169,19 @@ fr: no_reservation: "Aucune réservation" show_more: "Afficher plus" cancelled_slot: "Annulé" + reservations_panel_as_admin: + title: "Réservations" credits_panel: title: "Mes crédits" info: "Avec votre abonnement, vous bénéficiez de crédits gratuits que vous pouvez utiliser lors de vos réservations" remaining_credits_html: "Vous pouvez réserver {REMAINING} {REMAINING, plural, one{créneau} other{créneaux}} gratuitement." used_credits_html: "Vous avez déjà utilisé {USED} {USED, plural, =0{crédit} one{crédit} other{crédits}}." no_credits: "Vous n'avez pas encore de crédits. Certains abonnements peuvent vous permettre de réserver des créneaux gratuitement." + credits_panel_as_admin: + title: "Crédits" + remaining_credits_html: "{REMAINING} {REMAINING, plural, one{slot} other{slots}} peut être réservé gratuitement." + used_credits_html: " {USED} {USED, plural, =0{crédit} one{crédit} other{crédits}} déjà utilisé." + no_credits: "Pas encore de crédits." prepaid_packs_panel: title: "Mes packs d'heures prépayées" name: "Nom du pack d'heures prépayées" @@ -187,6 +194,8 @@ fr: cta_button: "Acheter un pack" no_packs: "Aucun pack prépayé disponible à la vente" reserved_for_subscribers_html: 'L''achat de packs prépayés est réservé aux abonnés. Abonnez-vous maintenant pour en bénéficier.' + prepaid_packs_panel_as_admin: + title: "Packs d'heures prépayées" #public profil of a member members_show: members_list: "Liste des membres" diff --git a/config/locales/app.logged.it.yml b/config/locales/app.logged.it.yml index 6762c774a..b63175227 100644 --- a/config/locales/app.logged.it.yml +++ b/config/locales/app.logged.it.yml @@ -154,12 +154,19 @@ it: no_reservation: "Nessuna prenotazione" show_more: "Mostra altro" cancelled_slot: "Annullato" + reservations_panel_as_admin: + title: "Reservations" credits_panel: title: "I miei crediti" info: "Il tuo abbonamento viene fornito con crediti gratuiti che puoi utilizzare durante le prenotazioni" remaining_credits_html: "Puoi prenotare {REMAINING} {REMAINING, plural, one{slot} other{slot}} gratis." used_credits_html: "Hai già usato {USED} {USED, plural, =0{credito} one{credito} other{crediti}}." no_credits: "Non hai ancora alcun credito. Alcuni abbonamenti potrebbero permetterti di prenotare alcuni slot gratis." + credits_panel_as_admin: + title: "Credits" + remaining_credits_html: "{REMAINING} {REMAINING, plural, one{slot} other{slots}} can be booked for free." + used_credits_html: " {USED} {USED, plural, =0{credit} one{credit} other{credits}} already used." + no_credits: "No credits yet." prepaid_packs_panel: title: "I miei pacchetti prepagati" name: "Nome pacchetto prepagato" @@ -172,6 +179,8 @@ it: cta_button: "Acquista un pacchetto" no_packs: "Nessun pacchetto prepagato disponibile per la vendita" reserved_for_subscribers_html: 'L''acquisto di pacchetti prepagati è riservato agli abbonati. Iscriviti ora per ottenerne.' + prepaid_packs_panel_as_admin: + title: "Prepaid packs" #public profil of a member members_show: members_list: "Lista dei membri" diff --git a/config/locales/app.logged.no.yml b/config/locales/app.logged.no.yml index 5d9868952..cb998a383 100644 --- a/config/locales/app.logged.no.yml +++ b/config/locales/app.logged.no.yml @@ -154,12 +154,19 @@ no_reservation: "No reservation" show_more: "Show more" cancelled_slot: "Cancelled" + reservations_panel_as_admin: + title: "Reservations" credits_panel: title: "My credits" info: "Your subscription comes with free credits you can use when reserving" remaining_credits_html: "You can book {REMAINING} {REMAINING, plural, one{slot} other{slots}} for free." used_credits_html: "You have already used {USED} {USED, plural, =0{credit} one{credit} other{credits}}." no_credits: "You don't have any credits yet. Some subscriptions may allow you to book some slots for free." + credits_panel_as_admin: + title: "Credits" + remaining_credits_html: "{REMAINING} {REMAINING, plural, one{slot} other{slots}} can be booked for free." + used_credits_html: " {USED} {USED, plural, =0{credit} one{credit} other{credits}} already used." + no_credits: "No credits yet." prepaid_packs_panel: title: "My prepaid packs" name: "Prepaid pack name" @@ -172,6 +179,8 @@ cta_button: "Buy a pack" no_packs: "No prepaid packs available for sale" reserved_for_subscribers_html: 'The purchase of prepaid packs is reserved for subscribers. Subscribe now to benefit.' + prepaid_packs_panel_as_admin: + title: "Prepaid packs" #public profil of a member members_show: members_list: "Medlemsliste" diff --git a/config/locales/app.logged.pt.yml b/config/locales/app.logged.pt.yml index d3b3be3b0..3e53ecabd 100644 --- a/config/locales/app.logged.pt.yml +++ b/config/locales/app.logged.pt.yml @@ -154,12 +154,19 @@ pt: no_reservation: "Nenhuma reserva" show_more: "Exibir mais" cancelled_slot: "Cancelado" + reservations_panel_as_admin: + title: "Reservations" credits_panel: title: "Meus créditos" info: "Sua assinatura vem com créditos gratuitos que você pode usar na reserva" remaining_credits_html: "Você pode reservar {REMAINING} {REMAINING, plural, one{slot} other{slots}} de graça." used_credits_html: "Você já usou {USED} {USED, plural, =0{crédito} one{crédito} other{créditos}}." no_credits: "Você não tem nenhum crédito ainda. Algumas assinaturas podem permitir que você reserve alguns slots gratuitamente." + credits_panel_as_admin: + title: "Credits" + remaining_credits_html: "{REMAINING} {REMAINING, plural, one{slot} other{slots}} can be booked for free." + used_credits_html: " {USED} {USED, plural, =0{credit} one{credit} other{credits}} already used." + no_credits: "No credits yet." prepaid_packs_panel: title: "Meus pacotes pré-pagos" name: "Nome do pacote pré-pago" @@ -172,6 +179,8 @@ pt: cta_button: "Comprar um pacote" no_packs: "Não há pacotes pré-pagos disponíveis para venda" reserved_for_subscribers_html: 'A compra de pacotes pré-pagos é reservada para os inscritos. Inscreva-se agora para beneficiar.' + prepaid_packs_panel_as_admin: + title: "Prepaid packs" #public profil of a member members_show: members_list: "Lista de membros" diff --git a/config/locales/app.logged.zu.yml b/config/locales/app.logged.zu.yml index cac3ea55c..291f7f367 100644 --- a/config/locales/app.logged.zu.yml +++ b/config/locales/app.logged.zu.yml @@ -154,12 +154,19 @@ zu: no_reservation: "crwdns37037:0crwdne37037:0" show_more: "crwdns37039:0crwdne37039:0" cancelled_slot: "crwdns37041:0crwdne37041:0" + reservations_panel_as_admin: + title: "crwdns37749:0crwdne37749:0" credits_panel: title: "crwdns37043:0crwdne37043:0" info: "crwdns37045:0crwdne37045:0" remaining_credits_html: "crwdns37047:0REMAINING={REMAINING}crwdnd37047:0REMAINING={REMAINING}crwdne37047:0" used_credits_html: "crwdns37049:0USED={USED}crwdnd37049:0USED={USED}crwdne37049:0" no_credits: "crwdns37051:0crwdne37051:0" + credits_panel_as_admin: + title: "crwdns37751:0crwdne37751:0" + remaining_credits_html: "crwdns37753:0REMAINING={REMAINING}crwdnd37753:0REMAINING={REMAINING}crwdne37753:0" + used_credits_html: "crwdns37755:0USED={USED}crwdnd37755:0USED={USED}crwdne37755:0" + no_credits: "crwdns37757:0crwdne37757:0" prepaid_packs_panel: title: "crwdns37053:0crwdne37053:0" name: "crwdns37055:0crwdne37055:0" @@ -172,6 +179,8 @@ zu: cta_button: "crwdns37069:0crwdne37069:0" no_packs: "crwdns37389:0crwdne37389:0" reserved_for_subscribers_html: 'crwdns37391:0{LINK}crwdne37391:0' + prepaid_packs_panel_as_admin: + title: "crwdns37759:0crwdne37759:0" #public profil of a member members_show: members_list: "crwdns27656:0crwdne27656:0" diff --git a/db/structure.sql b/db/structure.sql index 81acd320f..b6a29fb8b 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -3390,9 +3390,7 @@ CREATE TABLE public.spaces ( updated_at timestamp without time zone NOT NULL, characteristics text, disabled boolean, - deleted_at timestamp without time zone, - ancestry character varying NOT NULL COLLATE pg_catalog."C", - ancestry_depth integer DEFAULT 0 + deleted_at timestamp without time zone ); @@ -7470,13 +7468,6 @@ CREATE INDEX index_spaces_availabilities_on_availability_id ON public.spaces_ava CREATE INDEX index_spaces_availabilities_on_space_id ON public.spaces_availabilities USING btree (space_id); --- --- Name: index_spaces_on_ancestry; Type: INDEX; Schema: public; Owner: - --- - -CREATE INDEX index_spaces_on_ancestry ON public.spaces USING btree (ancestry); - - -- -- Name: index_spaces_on_deleted_at; Type: INDEX; Schema: public; Owner: - -- @@ -9266,5 +9257,3 @@ INSERT INTO "schema_migrations" (version) VALUES ('20230831103208'), ('20230901090637'), ('20230907124230'); - -