From aa7115dc4c8650cc22e4e109aa5ce0e383408835 Mon Sep 17 00:00:00 2001
From: Lara Carvalho
Date: Tue, 16 Sep 2025 21:14:15 +0000
Subject: [PATCH 01/27] Translated using Weblate (Portuguese)
Currently translated at 100.0% (970 of 970 strings)
Translation: tapir/tapir python translations
Translate-URL: http://weblate.seriousdino.org/projects/tapir/tapir-python-translations/pt/
---
.../locale/pt/LC_MESSAGES/django.po | 23 +++++++++++--------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/tapir/translations/locale/pt/LC_MESSAGES/django.po b/tapir/translations/locale/pt/LC_MESSAGES/django.po
index d991161a3..5fddcd9b1 100644
--- a/tapir/translations/locale/pt/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/pt/LC_MESSAGES/django.po
@@ -3,20 +3,22 @@
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
#
-#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-28 12:56+0200\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: LANGUAGE \n"
-"Language: \n"
+"PO-Revision-Date: 2025-09-17 21:59+0000\n"
+"Last-Translator: Lara Carvalho \n"
+"Language-Team: Portuguese \n"
+"Language: pt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Weblate 5.12.2\n"
+
#: accounts/emails/create_account_reminder_email.py:20
msgid "Create account reminder"
msgstr "Lembrete para criar conta"
@@ -27,7 +29,7 @@ msgstr "Enviado a membros ativos caso não tenham criado a conta 1 mês após se
#: accounts/forms.py:116
msgid "This username is not available."
-msgstr "Este username não está disponível."
+msgstr "Este nome de utilizador não está disponível."
#: accounts/forms.py:126
msgid "Optional Mails"
@@ -97,7 +99,7 @@ msgstr "Idioma preferido"
#: accounts/models.py:113 accounts/models.py:125
#: coop/views/membership_pause.py:74 shifts/views/exemptions.py:214
msgid "None"
-msgstr "Nenhum"
+msgstr "Nenhum(a)"
#: accounts/models.py:267
msgid "This username is already taken."
@@ -225,7 +227,7 @@ msgid ""
" Don't forget to sign up for your first shift at SuperCoop!\n"
msgstr ""
"\n"
-" Não se esqueça de se inscrever para o seu primeiro turno na SuperCoop!\n"
+" Não te esqueças de te inscrever no teu primeiro turno!\n"
#: accounts/templates/accounts/ldap_group_list.html:7
#: accounts/templates/accounts/user_detail.html:110
@@ -1345,7 +1347,7 @@ msgid ""
" updated. You may change working groups as needed.\n"
"
\n"
"
\n"
-" You also have opportunities to get involved at SuperCoop in addition to your regular shift. To make it as easy\n"
+" You also have opportunities to get involved at SuperCoop in addition to your regular shift. To make it as easy\n"
" as\n"
" possible for you to get started, we have summarized all additional\n"
" working groups in the wiki. Please see the Wiki for further information on how to get involved.\n"
@@ -4774,6 +4776,7 @@ msgstr ""
"\n"
" O seu crédito atual é %(credit)s€.\n"
" "
+
#: statistics/templates/statistics/tags/credit_account_card.html:14
msgid "List of last charged credits"
msgstr "Lista dos últimos créditos carregados"
@@ -5850,4 +5853,4 @@ msgstr "%(name)s não é membro da cooperativa. Pode ter transferido as suas aç
#: welcomedesk/services/welcome_desk_warnings_service.py:34
#, python-format
msgid "%(name)s has not attended a welcome session yet. Make sure they plan to do it!"
-msgstr "%(name)s ainda não participou numa sessão de boas-vindas. Certifique-se de que planeiam fazê-lo!"
\ No newline at end of file
+msgstr "%(name)s ainda não participou numa sessão de boas-vindas. Certifique-se de que planeiam fazê-lo!"
From 3728b6ff531c36197aca2c8c1a4acb5c73c77e11 Mon Sep 17 00:00:00 2001
From: Lara Carvalho
Date: Tue, 16 Sep 2025 21:15:20 +0000
Subject: [PATCH 02/27] Translated using Weblate (Portuguese)
Currently translated at 100.0% (43 of 43 strings)
Translation: tapir/tapir javascript translations
Translate-URL: http://weblate.seriousdino.org/projects/tapir/tapir-javascript-translations/pt/
---
.../locale/pt/LC_MESSAGES/djangojs.po | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/tapir/translations/locale/pt/LC_MESSAGES/djangojs.po b/tapir/translations/locale/pt/LC_MESSAGES/djangojs.po
index 91e1319d5..a542896b0 100644
--- a/tapir/translations/locale/pt/LC_MESSAGES/djangojs.po
+++ b/tapir/translations/locale/pt/LC_MESSAGES/djangojs.po
@@ -3,20 +3,22 @@
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
#
-#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-28 12:57+0200\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: LANGUAGE \n"
-"Language: \n"
+"PO-Revision-Date: 2025-09-17 21:59+0000\n"
+"Last-Translator: Lara Carvalho \n"
+"Language-Team: Portuguese \n"
+"Language: pt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Weblate 5.12.2\n"
+
#: dist/assets/fancy_export-RsOcUVRw.js:3
msgid "Fancy export"
msgstr "Exportação avançada"
@@ -175,7 +177,7 @@ msgstr "Co-comprador: "
#: dist/assets/welcome_desk-OgiTR3jj.js:1
msgid "None"
-msgstr "Nenhum"
+msgstr "Nenhum(a)"
#: dist/assets/welcome_desk-OgiTR3jj.js:1
msgid "Whoops! Something went wrong. Please try again. If it keeps happening, please write in the #tapir channel on Slack with your current search: "
@@ -187,4 +189,4 @@ msgstr "Welcome Desk"
#: dist/assets/welcome_desk-OgiTR3jj.js:1
msgid "Name or member ID"
-msgstr "Nome ou ID do membro"
\ No newline at end of file
+msgstr "Nome ou ID do membro"
From d2f1e49fb94bfc4df299e9972c3893904c265ff4 Mon Sep 17 00:00:00 2001
From: Lara Carvalho
Date: Sat, 20 Sep 2025 12:19:14 +0000
Subject: [PATCH 03/27] Translated using Weblate (Portuguese)
Currently translated at 99.4% (965 of 970 strings)
Translation: tapir/tapir python translations
Translate-URL: http://weblate.seriousdino.org/projects/tapir/tapir-python-translations/pt/
---
.../locale/pt/LC_MESSAGES/django.po | 54 +++++--------------
1 file changed, 13 insertions(+), 41 deletions(-)
diff --git a/tapir/translations/locale/pt/LC_MESSAGES/django.po b/tapir/translations/locale/pt/LC_MESSAGES/django.po
index 5fddcd9b1..c5329dd5b 100644
--- a/tapir/translations/locale/pt/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/pt/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-28 12:56+0200\n"
-"PO-Revision-Date: 2025-09-17 21:59+0000\n"
+"PO-Revision-Date: 2025-09-21 12:59+0000\n"
"Last-Translator: Lara Carvalho \n"
"Language-Team: Portuguese \n"
@@ -25,7 +25,9 @@ msgstr "Lembrete para criar conta"
#: accounts/emails/create_account_reminder_email.py:25
msgid "Sent to active member if they haven't created the account 1 month after becoming member."
-msgstr "Enviado a membros ativos caso não tenham criado a conta 1 mês após se tornarem membros."
+msgstr ""
+"Enviado a membros ativos caso não tenham criado conta 1 mês após se tornarem "
+"membros."
#: accounts/forms.py:116
msgid "This username is not available."
@@ -64,7 +66,7 @@ msgstr "Data de nascimento"
#: accounts/models.py:67 coop/models.py:74 coop/models.py:450
msgid "Street and house number"
-msgstr "Rua e número"
+msgstr "Rua/Avenida e número"
#: accounts/models.py:68 coop/models.py:75 coop/models.py:451
msgid "Extra address line"
@@ -106,7 +108,7 @@ msgid "This username is already taken."
msgstr "Este nome de utilizador já está em uso."
#: accounts/templates/accounts/edit_username.default.html:13
-#, python-format
+#, fuzzy, python-format
msgid ""
"\n"
" Edit username: %(display_name_full)s\n"
@@ -117,7 +119,7 @@ msgstr ""
" "
#: accounts/templates/accounts/edit_username.default.html:22
-#, python-format
+#, fuzzy, python-format
msgid ""
"\n"
" Edit username: %(display_name_full)s\n"
@@ -128,7 +130,7 @@ msgstr ""
" "
#: accounts/templates/accounts/edit_username.default.html:28
-#, python-format
+#, fuzzy, python-format
msgid ""
"\n"
"
O seu antigo nome de utilizador é %(old_username)s.
\n"
-"
Se alguma vez atualizou a wiki, por favor contacte a equipa da Wiki no Slack #wiki para manter a sua página de membro e o histórico das suas alterações.
\n"
+"
O teu antigo nome de utilizador é "
+"%(old_username)s.
\n"
@@ -188,40 +190,10 @@ msgid ""
" The Member Office\n"
"
\n"
" "
-msgstr ""
-"\n"
-"
Olá %(display_name_short)s,
\n"
-"
Notámos que já é membro da SuperCoop há um mês/semana, mas ainda não criou uma conta Tapir para se inscrever no seu primeiro turno.
\n"
-"
\n"
-" Como lembrete, para se inscrever no seu primeiro turno (ou indicar que tem direito a uma\n"
-" isenção),\n"
-" visite o Escritório de Membros na nossa loja.\n"
-" Lá será criada uma conta para si no Tapir, o nosso sistema online de gestão de turnos, \n"
-" e será guiado nos passos para escolher o seu turno e grupo de trabalho.\n"
-"
\n"
-"
\n"
-" Para perguntas gerais sobre a sua adesão, contacte o Escritório de Membros por email (mitglied@supercoop.de)\n"
-" ou visite a SuperCoop.\n"
-" Sinta-se à vontade para passar e conhecer outros SuperCoopies em pessoa! O horário de abertura é:\n"
-"
\n"
-"
Segunda - Sexta das 16:30 às 19:30
\n"
-"
Sábados das 11:00 às 14:00
\n"
-"
\n"
-" \n"
-"
\n"
-" Como encontrar a SuperCoop?\n"
-"
\n"
-"
\n"
-" Pode encontrar a nossa loja na Oudenarder Straße 16, 13347 Berlim, na esquina com a Seestraße. \n"
-" Existe também uma entrada traseira, acessível através do antigo Osramhöfe. Esta entrada é acessível a pessoas com mobilidade reduzida.\n"
-"
\n"
-"
\n"
-" Saudações cooperativas, \n"
-" O Escritório de Membros\n"
-"
\n"
-" "
+msgstr " "
#: accounts/templates/accounts/email/create_account_reminder.subject.html:2
+#, fuzzy
msgid ""
"\n"
" Don't forget to sign up for your first shift at SuperCoop!\n"
From a2b3f38e9ecd228f787d740e7611a4ac2d932024 Mon Sep 17 00:00:00 2001
From: Lara Carvalho
Date: Sat, 20 Sep 2025 12:10:33 +0000
Subject: [PATCH 04/27] Translated using Weblate (Portuguese)
Currently translated at 100.0% (43 of 43 strings)
Translation: tapir/tapir javascript translations
Translate-URL: http://weblate.seriousdino.org/projects/tapir/tapir-javascript-translations/pt/
---
tapir/translations/locale/pt/LC_MESSAGES/djangojs.po | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/tapir/translations/locale/pt/LC_MESSAGES/djangojs.po b/tapir/translations/locale/pt/LC_MESSAGES/djangojs.po
index a542896b0..7cc8599e0 100644
--- a/tapir/translations/locale/pt/LC_MESSAGES/djangojs.po
+++ b/tapir/translations/locale/pt/LC_MESSAGES/djangojs.po
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-28 12:57+0200\n"
-"PO-Revision-Date: 2025-09-17 21:59+0000\n"
+"PO-Revision-Date: 2025-09-21 12:59+0000\n"
"Last-Translator: Lara Carvalho \n"
"Language-Team: Portuguese \n"
@@ -41,7 +41,7 @@ msgstr "Adicionar colunas à exportação"
#: dist/assets/fancy_export-RsOcUVRw.js:3
msgid "Click on a selected column to deselect it."
-msgstr "Clique numa coluna selecionada para a desmarcar."
+msgstr "Clica numa coluna selecionada para a desmarcar."
#: dist/assets/fancy_export-RsOcUVRw.js:3
msgid "Build export for "
@@ -49,7 +49,7 @@ msgstr "Gerar exportação para "
#: dist/assets/fancy_export-RsOcUVRw.js:3
msgid "Pick a source dataset"
-msgstr "Escolha um conjunto de dados de origem"
+msgstr "Escolhe um conjunto de dados de origem"
#: dist/assets/fancy_export-RsOcUVRw.js:3
msgid "Download as CSV"
@@ -113,7 +113,7 @@ msgstr "Editar avisos"
#: dist/assets/shift_management-CCdqoXYD.js:1
msgid "Are you sure you want to delete the following qualification: "
-msgstr "Tem a certeza de que deseja eliminar a seguinte qualificação: "
+msgstr "Tens a certeza de que queres eliminar a seguinte qualificação: "
#: dist/assets/shift_management-CCdqoXYD.js:1
msgid "This qualification is not used in any slot."
From 6b11cad0a6094c381f80a5ca0e1216625c6f885f Mon Sep 17 00:00:00 2001
From: Lara Carvalho
Date: Sun, 21 Sep 2025 16:00:58 +0000
Subject: [PATCH 05/27] Translated using Weblate (Portuguese)
Currently translated at 94.1% (913 of 970 strings)
Translation: tapir/tapir python translations
Translate-URL: http://weblate.seriousdino.org/projects/tapir/tapir-python-translations/pt/
---
.../locale/pt/LC_MESSAGES/django.po | 695 ++++++++++++------
1 file changed, 467 insertions(+), 228 deletions(-)
diff --git a/tapir/translations/locale/pt/LC_MESSAGES/django.po b/tapir/translations/locale/pt/LC_MESSAGES/django.po
index c5329dd5b..71c1c0c45 100644
--- a/tapir/translations/locale/pt/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/pt/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-28 12:56+0200\n"
-"PO-Revision-Date: 2025-09-21 12:59+0000\n"
+"PO-Revision-Date: 2025-09-22 15:59+0000\n"
"Last-Translator: Lara Carvalho \n"
"Language-Team: Portuguese \n"
@@ -220,7 +220,7 @@ msgstr "Mais informações"
#: accounts/templates/accounts/purchase_tracking_card.html:25
msgid "The checkout system links your purchase to your member account. The total amount of your purchase is saved each time. However, it does not record which specific products you have purchased. You can also still decide with each purchase whether you want to have your membership card scanned or not. With your general consent here on Tapir, you do not enter into any obligation to scan. You can revoke your consent at any time by deactivating the checkbox above. With your consent, you help Supercoop to better understand the shopping habits of its members. This is important for the further development of our supermarket. More information: https://wiki.supercoop.de/wiki/Mitgliederkarte"
-msgstr "O sistema de caixa associa a sua compra à sua conta de membro. O valor total da sua compra é guardado em cada transação. No entanto, não são registados os produtos específicos que comprou. Pode também decidir, em cada compra, se deseja ou não que o seu cartão de membro seja lido. Ao dar o seu consentimento geral aqui no Tapir, não assume nenhuma obrigação de leitura. Pode revogar o seu consentimento a qualquer momento, desmarcando a caixa acima. Com o seu consentimento, ajuda a Supercoop a compreender melhor os hábitos de compra dos seus membros. Isto é importante para o desenvolvimento futuro do nosso supermercado. Mais informações: https://wiki.supercoop.de/wiki/Mitgliederkarte"
+msgstr ""
#: accounts/templates/accounts/purchase_tracking_card.html:30
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:169
@@ -345,7 +345,7 @@ msgid "Password reset"
msgstr "Redefinição de palavra-passe"
#: accounts/templates/registration/email/password_reset_email.html:14
-#, python-format
+#, fuzzy, python-format
msgid ""
"\n"
"
Hi %(display_name_short)s,
\n"
@@ -360,20 +360,24 @@ msgstr ""
"\n"
"
Olá %(display_name_short)s,
\n"
"
\n"
-" Foi feito um pedido para redefinir a palavra-passe de %(email)s. \n"
-" O seu nome de utilizador é %(username)s \n"
-" Siga este link para redefinir a sua palavra-passe: %(password_reset_url_full)s\n"
-" Se o link não funcionar, tente abri-lo numa janela privada (modo incógnito) ou noutro navegador.\n"
+" Foi feito um pedido para redefinir a palavra-passe "
+"de %(email)s. \n"
+" O teu nome de utilizador é %(username)s \n"
+" Segue este link para redefinir a tua palavra-passe: "
+"%(password_reset_url_full)s\n"
+" Se o link não funcionar, tenta abri-lo numa janela "
+"privada (modo incógnito) ou noutro navegador.\n"
"
\n"
" "
#: accounts/templates/registration/email/password_reset_subject.html:2
+#, fuzzy
msgid ""
"\n"
" Reset your password\n"
msgstr ""
"\n"
-" Redefina a sua palavra-passe\n"
+" Redefine a tua palavra-passe\n"
#: accounts/templates/registration/login.html:7
msgid "Login"
@@ -386,11 +390,11 @@ msgstr "Entrar"
#: accounts/templates/registration/login.html:45
msgid "Click here to show/hide the password"
-msgstr "Clique aqui para mostrar/ocultar a palavra-passe"
+msgstr "Clica aqui para mostrar/ocultar a palavra-passe"
#: accounts/templates/registration/login.html:55
msgid "Forgot your password or your username?"
-msgstr "Esqueceu-se da sua palavra-passe ou do seu nome de utilizador?"
+msgstr "Esqueceste-te da tua palavra-passe ou do teu nome de utilizador?"
#: accounts/templates/registration/password_reset_complete.html:7
msgid "Password set"
@@ -398,11 +402,11 @@ msgstr "Palavra-passe definida"
#: accounts/templates/registration/password_reset_complete.html:9
msgid "Your password has been set. You may go ahead and sign in now."
-msgstr "A sua palavra-passe foi definida. Já pode iniciar sessão."
+msgstr "A tua palavra-passe foi definida. Já podes iniciar sessão."
#: accounts/templates/registration/password_reset_complete.html:11
msgid "Go to login page"
-msgstr "Ir para a página de login"
+msgstr "Ir para a página de iniciar sessão"
#: accounts/templates/registration/password_reset_confirm.html:9
#: accounts/templates/registration/password_update.html:8
@@ -411,7 +415,7 @@ msgstr "Definir uma nova palavra-passe"
#: accounts/templates/registration/password_reset_confirm.html:11
msgid "Please enter a new password"
-msgstr "Por favor, introduza uma nova palavra-passe"
+msgstr "Por favor, introduz uma nova palavra-passe"
#: accounts/templates/registration/password_reset_confirm.html:15
msgid "Change password"
@@ -419,7 +423,9 @@ msgstr "Alterar palavra-passe"
#: accounts/templates/registration/password_reset_confirm.html:21
msgid "The password reset link was invalid, possibly because it has already been used. Please request a new password reset."
-msgstr "O link de redefinição de palavra-passe é inválido, possivelmente porque já foi utilizado. Por favor, solicite uma nova redefinição."
+msgstr ""
+"O link de redefinição de palavra-passe é inválido, possivelmente porque já "
+"foi utilizado. Por favor, solicita uma nova redefinição."
#: accounts/templates/registration/password_reset_done.html:7
msgid "Password reset instructions have been sent."
@@ -427,14 +433,18 @@ msgstr "As instruções para redefinir a palavra-passe foram enviadas."
#: accounts/templates/registration/password_reset_done.html:9
msgid "We have emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly. If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder."
-msgstr "Enviámos por email as instruções para definir a sua palavra-passe, se existir uma conta com o endereço que indicou. Deve recebê-las em breve. Se não receber, verifique se introduziu o email correto e confira a sua pasta de spam."
+msgstr ""
+"Enviámos por email as instruções para definires a tua palavra-passe, se "
+"existir uma conta com o email que indicaste. Deves recebê-las em breve. Se "
+"não receberes, verifica se introduziste o email correto e a tua pasta de "
+"spam."
#: accounts/templates/registration/password_reset_form.html:10
msgid "Problems with logging in?"
msgstr "Problemas ao iniciar sessão?"
#: accounts/templates/registration/password_reset_form.html:16
-#, python-format
+#, fuzzy, python-format
msgid ""
"\n"
" Please enter your email address, we will send you instructions to reset your password. \n"
@@ -443,22 +453,29 @@ msgid ""
" "
msgstr ""
"\n"
-" Por favor, introduza o seu endereço de email e enviaremos instruções para redefinir a sua palavra-passe. \n"
-" O seu nome de utilizador será incluído no email, caso o tenha esquecido. \n"
-" Se se esqueceu tanto do seu email como do seu nome de utilizador, contacte o escritório de membros: %(contact_member_office)s, indicando o seu nome completo e, se souber, o seu número de membro.\n"
+" Por favor, introduz o teu endereço de email e "
+"enviaremos instruções para redefinir a tua palavra-passe. \n"
+" O teu nome de utilizador será incluído no email, "
+"caso o tenhas esquecido. \n"
+" Se te esqueceste tanto do teu email como do teu "
+"nome de utilizador, contacta o GTT-IT ou o GTT-membros: %(contact_member_office)s, "
+"indicando o teu nome completo e número de membro.\n"
" "
#: accounts/templates/registration/password_reset_form.html:24
msgid "Back to login form"
-msgstr "Voltar ao formulário de login"
+msgstr "Voltar ao formulário de início de sessão"
#: accounts/templates/registration/password_reset_form.html:26
msgid "Send me instructions!"
-msgstr "Enviar-me instruções!"
+msgstr "Envia-me instruções!"
#: accounts/validators.py:11
msgid "Enter a valid username. This value may contain only letters, numbers, and ./-/_ characters."
-msgstr "Insira um nome de utilizador válido. Este valor pode conter apenas letras, números e os caracteres ./-/_."
+msgstr ""
+"Insere um nome de utilizador válido. Este apenas pode conter letras, números "
+"e os caracteres ./-/_."
#: accounts/views.py:112 accounts/views.py:117 coop/views/shareowner.py:277
#: coop/views/shareowner.py:282
@@ -471,13 +488,16 @@ msgid "Account welcome email sent."
msgstr "E-mail de boas-vindas da conta enviado."
#: accounts/views.py:202
+#, fuzzy
msgid "You can only look at your own barcode unless you have member office rights"
-msgstr "Só pode ver o seu próprio código de barras, a menos que tenha direitos de administração de membros."
+msgstr ""
+"Só podes ver o teu próprio código de barras se tiveres perfil de "
+"administrador."
#: accounts/views.py:285 accounts/views.py:290
-#, python-format
+#, fuzzy, python-format
msgid "Edit member groups: %(name)s"
-msgstr "Editar grupos do membro: %(name)s"
+msgstr "Editar grupos de membro: %(name)s"
#: accounts/views.py:384 accounts/views.py:389
#, python-format
@@ -493,7 +513,7 @@ msgstr "Membros"
#: coop/templates/coop/draftuser_register_form.default.html:11
#: coop/templates/coop/draftuser_register_form.html:11
msgid "Applicants"
-msgstr "Candidatos"
+msgstr "Candidatos(as)"
#: coop/apps.py:45 coop/templates/coop/member_management.html:7
msgid "Member management"
@@ -518,23 +538,24 @@ msgstr "Enviado a um membro quando um novo co-comprador é registado no seu perf
#: coop/emails/extra_shares_confirmation_email.py:26
msgid "Extra shares bought"
-msgstr "Ações adicionais compradas"
+msgstr "Títulos adicionais comprados"
#: coop/emails/extra_shares_confirmation_email.py:30
msgid "Sent when someone who is already a member buys more shares"
-msgstr "Enviado quando alguém que já é membro compra mais ações"
+msgstr "Enviado quando alguém que já é membro compra mais títulos"
#: coop/emails/membership_confirmation_email_for_active_member.py:26
msgid "Membership confirmation for active users"
msgstr "Confirmação de adesão para membros ativos"
#: coop/emails/membership_confirmation_email_for_investing_member.py:26
+#, fuzzy
msgid "Membership confirmation for investing users"
msgstr "Confirmação de adesão para membros investidores"
#: coop/emails/membershipresignation_confirmation_email.py:22
msgid "Confirmation Email for resigned members."
-msgstr "E-mail de confirmação para membros demissionários."
+msgstr "E-mail de confirmação para membros demissionários (ex-membros)."
#: coop/emails/membershipresignation_confirmation_email.py:26
msgid "Automatically sent after a member has been resigned."
@@ -542,11 +563,13 @@ msgstr "Enviado automaticamente após a saída de um membro."
#: coop/emails/membershipresignation_transferred_shares_confirmation.py:21
msgid "Confirmation Email for transferred shares."
-msgstr "E-mail de confirmação para ações transferidas."
+msgstr "E-mail de confirmação para títulos transferidos."
#: coop/emails/membershipresignation_transferred_shares_confirmation.py:26
msgid "Automatically sent to the member who received shares after a resignation."
-msgstr "Enviado automaticamente ao membro que recebeu ações após uma saída."
+msgstr ""
+"Enviado automaticamente ao membro que foi reembolsado pelo valor dos títulos "
+"após a sua saída."
#: coop/emails/tapir_account_created_email.py:25
msgid "Tapir account created"
@@ -562,8 +585,11 @@ msgid "Start date"
msgstr "Data de início"
#: coop/forms.py:44
+#, fuzzy
msgid "Usually, the date on the membership agreement, or today. In the case of sold or gifted shares, can be set in the future."
-msgstr "Normalmente, a data do contrato de adesão, ou a data de hoje. No caso de ações vendidas ou doadas, pode ser definida no futuro."
+msgstr ""
+"Normalmente, a data do contrato de adesão, ou a data de hoje. No caso de "
+"títulos doados, pode ser definida no futuro."
#: coop/forms.py:49
#: financingcampaign/templates/financingcampaign/general.html:28
@@ -571,43 +597,62 @@ msgid "End date"
msgstr "Data de fim"
#: coop/forms.py:53
+#, fuzzy
msgid "Usually left empty. Can be set to a point in the future if it is already known that the shares will be transferred to another member in the future."
-msgstr "Normalmente deixado em branco. Pode ser definido para uma data futura se já se souber que as ações serão transferidas para outro membro."
+msgstr ""
+"Normalmente deixado em branco. Pode ser definido para uma data futura se já "
+"se souber que as ações serão transferidas para outro membro."
#: coop/forms.py:59
msgid "Number of shares to create"
-msgstr "Número de ações a criar"
+msgstr "Número de títulos a criar"
#: coop/forms.py:67
msgid "The end date must be later than the start date."
-msgstr "A data de fim deve ser posterior à data de início."
+msgstr "A data de saída deve ser posterior à data de início."
#: coop/forms.py:105 coop/models.py:463
msgid "Number of Shares"
-msgstr "Número de ações"
+msgstr "Número de títulos"
#: coop/forms.py:110
-#, python-format
+#, fuzzy, python-format
msgid "Number of shares you would like to purchase. The price of one share is EUR %(share_price)s. You need to purchase at least one share to become member of the cooperative. To support our cooperative even more, you may voluntarily purchase more shares."
-msgstr "Número de ações que gostaria de comprar. O preço de uma ação é EUR %(share_price)s. Deve comprar pelo menos uma ação para se tornar membro da cooperativa. Para apoiar ainda mais a nossa cooperativa, pode comprar voluntariamente mais ações."
+msgstr ""
+"Número de títulos que gostarias de adquirir. O preço de uma título é EUR "
+"%(share_price)s. Deve comprar pelo menos 3 títulos para te tornares membro "
+"da secção consumo. Para apoiares ainda mais a nossa cooperativa, pode "
+"adquirir voluntariamente mais títulos."
#: coop/forms.py:120
+#, fuzzy
msgid "I would like to join the membership list as an investing member (= sponsoring member)"
-msgstr "Gostaria de entrar na lista de membros como membro investidor (= membro patrocinador)"
+msgstr ""
+"Gostaria de entrar na lista de membros como membro investidor (= membro "
+"patrocinador)"
#: coop/forms.py:123
+#, fuzzy
msgid "Note: Investing members are sponsoring members. They have no voting rights in the General Assembly and cannot use the services of the cooperative that are exclusive to ordinary members. "
-msgstr "Nota: Os membros investidores são membros patrocinadores. Não têm direito de voto na Assembleia Geral e não podem utilizar os serviços da cooperativa exclusivos para membros ordinários."
+msgstr ""
+"Nota: Os membros investidores são membros patrocinadores. Não têm "
+"direito de voto na Assembleia Geral e não podem utilizar os serviços da "
+"cooperativa exclusivos para membros ordinários. "
#: coop/forms.py:233
+#, fuzzy
msgid "Usually, the credited member is the same as the paying member. Only if a person if gifting another person a share through the matching program, then the fields can be different."
-msgstr "Normalmente, o membro creditado é o mesmo que o membro pagante. Apenas quando uma pessoa oferece uma ação a outra através do programa de correspondência, os campos podem ser diferentes."
+msgstr ""
+"Normalmente, o membro creditado é o mesmo que o membro pagante. Apenas "
+"quando uma pessoa oferece uma ação a outra através do programa de "
+"correspondência, os campos podem ser diferentes."
#: coop/forms.py:268
msgid "The member stays active"
msgstr "O membro permanece ativo"
#: coop/forms.py:270
+#, fuzzy
msgid "The member becomes investing"
msgstr "O membro torna-se investidor"
@@ -617,11 +662,12 @@ msgstr "Por favor, não mais de 1000 caracteres."
#: coop/forms.py:278
msgid "Member to resign"
-msgstr "Membro a demitir-se"
+msgstr "Membro que vai sair"
#: coop/forms.py:281
+#, fuzzy
msgid "Transferring share(s) to"
-msgstr "Transferindo ação(ões) para"
+msgstr "Transferindo título(s) para"
#: coop/forms.py:287
msgid "Member status"
@@ -629,39 +675,48 @@ msgstr "Estado do membro"
#: coop/forms.py:291
msgid "In the case where the member wants their money back, they stay a member for 3 more years. However, it is very likely that the member doesn't want to be active anymore. If they haven't explicitly mentioned it, please ask them if we can switch them to investing."
-msgstr "No caso de o membro querer o reembolso, permanece como membro por mais 3 anos. No entanto, é muito provável que o membro não queira mais estar ativo. Se não tiverem mencionado explicitamente, pergunte se podemos convertê-lo em membro investidor."
+msgstr ""
#: coop/forms.py:316
msgid "Send confirmation mails"
msgstr "Enviar e-mails de confirmação"
#: coop/forms.py:320
+#, fuzzy
msgid "If enabled, the resigned member and if applicable the member receiving shares will receive confirmation emails."
-msgstr "Se ativado, o membro demissionário e, se aplicável, o membro que receber ações receberão e-mails de confirmação."
+msgstr ""
+"Se ativado, o membro demissionário e, se aplicável, o membro que recebe os "
+"títulos receberão e-mails de confirmação."
#: coop/forms.py:355
msgid "Please pick an option"
-msgstr "Por favor, escolha uma opção"
+msgstr "Por favor, escolhe uma opção"
#: coop/forms.py:367
msgid "This member is already resigned."
-msgstr "Este membro já se demitiu."
+msgstr "Este membro já saiu."
#: coop/forms.py:379
+#, fuzzy
msgid "Please select the member that the shares should be transferred to."
-msgstr "Por favor, selecione o membro para quem as ações devem ser transferidas."
+msgstr ""
+"Por favor, seleciona o membro para quem os títulos devem ser transferidos."
#: coop/forms.py:392
msgid "If the shares don't get transferred to another member, this field should be empty."
-msgstr "Se as ações não forem transferidas para outro membro, este campo deve ficar vazio."
+msgstr ""
+"Se os títulos não forem transferidos para outro membro, este campo deve "
+"ficar vazio."
#: coop/forms.py:405
msgid "Sender and receiver of transferring the share(s) cannot be the same."
-msgstr "O remetente e o destinatário da transferência de ações não podem ser a mesma pessoa."
+msgstr ""
+"O remetente e o destinatário da transferência de títulos não podem ser a "
+"mesma pessoa."
#: coop/forms.py:417
msgid "Cannot pay out, because shares have been gifted."
-msgstr "Não é possível pagar, porque as ações foram doadas."
+msgstr "Não é possível obter reembolso, porque os títulos foram doados."
#: coop/models.py:54
msgid "Is company"
@@ -691,16 +746,17 @@ msgstr "É membro investidor"
#: coop/templates/coop/draftuser_detail.html:176
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:48
#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:168
+#, fuzzy
msgid "Ratenzahlung"
msgstr "Pagamento em prestações"
#: coop/models.py:92 coop/models.py:470
msgid "Attended Welcome Session"
-msgstr "Participou na sessão de boas-vindas"
+msgstr "Participou na sessão informativa"
#: coop/models.py:95
msgid "Is willing to gift a share"
-msgstr "Está disposto a doar uma ação"
+msgstr "Está disposto a doar títulos"
#: coop/models.py:200
msgid "Cannot be a company and have a Tapir account"
@@ -726,17 +782,20 @@ msgstr "Ativo"
#: coop/models.py:371
msgid "Paused"
-msgstr "Pausado"
+msgstr "Em ausência temporária"
#: coop/models.py:466
+#, fuzzy
msgid "Investing member"
msgstr "Membro investidor"
#: coop/models.py:473
+#, fuzzy
msgid "Signed Beteiligungserklärung"
msgstr "Declaração de participação assinada"
#: coop/models.py:475
+#, fuzzy
msgid "Paid Entrance Fee"
msgstr "Taxa de adesão paga"
@@ -753,22 +812,26 @@ msgid "Last name must be set."
msgstr "O apelido deve ser definido."
#: coop/models.py:540
+#, fuzzy
msgid "Membership agreement must be signed."
msgstr "O contrato de adesão deve ser assinado."
#: coop/models.py:542
+#, fuzzy
msgid "Amount of requested shares must be positive."
-msgstr "A quantidade de ações solicitadas deve ser positiva."
+msgstr "O número de títulos solicitados deve ser positivo."
#: coop/models.py:544
msgid "Member already created."
msgstr "Membro já criado."
#: coop/models.py:571
+#, fuzzy
msgid "Paying member"
-msgstr "Membro pagante"
+msgstr "Membro ativo"
#: coop/models.py:579
+#, fuzzy
msgid "Credited member"
msgstr "Membro creditado"
@@ -789,16 +852,18 @@ msgid "Created by"
msgstr "Criado por"
#: coop/models.py:775
+#, fuzzy
msgid "The cooperative buys the shares back from the member"
-msgstr "A cooperativa recompra as ações do membro"
+msgstr "A cooperativa volta a comprar os títulos do membro"
#: coop/models.py:778
msgid "The member gifts the shares to the cooperative"
-msgstr "O membro doa as ações à cooperativa"
+msgstr "O membro doa os títulos à cooperativa"
#: coop/models.py:780
+#, fuzzy
msgid "The shares get transferred to another member"
-msgstr "As ações são transferidas para outro membro"
+msgstr "Os títulos são transferidos para outro membro"
#: coop/models.py:783
msgid "Financial reasons"
@@ -813,20 +878,25 @@ msgid "Distance"
msgstr "Distância"
#: coop/models.py:786
+#, fuzzy
msgid "Strategic orientation of SuperCoop"
-msgstr "Orientação estratégica da SuperCoop"
+msgstr "Orientação estratégica da Cooperativa"
#: coop/models.py:787
msgid "Other"
msgstr "Outro"
#: coop/models.py:792
+#, fuzzy
msgid "Shareowner"
-msgstr "Acionista"
+msgstr "Cooperante"
#: coop/models.py:811
+#, fuzzy
msgid "Leave this empty if the resignation type is not a transfer to another member"
-msgstr "Deixe este campo vazio se o tipo de demissão não for uma transferência para outro membro"
+msgstr ""
+"Deixa este campo vazio se o tipo de demissão não for uma transferência para "
+"outro membro"
#: coop/templates/coop/about.html:5 coop/templates/coop/about.html:11
msgid "About Tapir"
@@ -889,6 +959,7 @@ msgid "Registration confirmed"
msgstr "Registo confirmado"
#: coop/templates/coop/draftuser_confirm_registration.html:8
+#, fuzzy
msgid ""
"\n"
"
Registration successful! Congratulations!
\n"
@@ -896,19 +967,13 @@ msgid ""
"
Once we have received both your Beteiligungserklärung and the corresponding payment, we'll send you the confirmation of your membership.
\n"
"
We will also create for you an account for this website and for the wiki.
\n"
" "
-msgstr ""
-"\n"
-"
Registo bem-sucedido! Parabéns!
\n"
-"
Acabámos de lhe enviar por e-mail a confirmação da sua pré-inscrição com a declaração de participação (Beteiligungserklärung) já preenchida com as suas informações. Só tem de a imprimir e enviar-nos por correio ou digitalizar e enviar por e-mail.
\n"
-"
Assim que recebermos tanto a sua declaração de participação como o pagamento correspondente, enviaremos a confirmação da sua adesão.
\n"
-"
Também criaremos uma conta para si neste site e na wiki.
\n"
-" Foram encontrados membros com informações semelhantes à pessoa que está a tentar criar.\n"
-" Por favor, verifique cuidadosamente que não se trata de um duplicado.\n"
+" Foram encontrados membros com informações "
+"semelhantes à pessoa que está a tentar criar.\n"
+" Por favor, verifica cuidadosamente que não "
+"se trata de um duplicado.\n"
"
\n"
-" Olá equipa de Contabilidade, olá Member Office,\n"
+" Olá equipa GTT-Membros,\n"
"
\n"
"
\n"
" Este é o resumo contabilístico do Tapir para a semana passada.\n"
"
\n"
"
\n"
-" Foram criados %(num_new_members)s novos membros. Para esses novos membros, foram criadas %(total_num_shares_new_members)s ações. \n"
-" Adicionalmente, foram criadas %(total_num_shares_existing_members)s ações para membros existentes.\n"
+" Foram criados %(num_new_members)s novos membros. Para esses "
+"novos membros, foram criadas %(total_num_shares_new_members)s ações. \n"
+" Adicionalmente, foram criados "
+"%(total_num_shares_existing_members)s títulos para membros existentes.\n"
"
\n"
" "
@@ -1073,17 +1144,21 @@ msgstr "Novos membros"
#: coop/templates/coop/email/accounting_recap.body.default.html:26
#: coop/templates/coop/email/accounting_recap.body.default.html:38
msgid "share(s)"
-msgstr "ação(ões)"
+msgstr "título(s)"
#: coop/templates/coop/email/accounting_recap.body.default.html:33
msgid "New shares for existing members upload only after approval in Duo"
-msgstr "Novas ações para membros existentes são carregadas apenas após aprovação no Duo"
+msgstr ""
#: coop/templates/coop/email/accounting_recap.body.default.html:47
+#, fuzzy
msgid "This email is send automatically every Sunday. Contact the Tapir team on Slack if you have any question."
-msgstr "Este e-mail é enviado automaticamente todos os domingos. Contacte a equipa do Tapir no Slack se tiver alguma questão."
+msgstr ""
+"Este e-mail é enviado automaticamente todos os domingos. Contacta a Helpdesk "
+"do Tapir se tiveres alguma questão."
#: coop/templates/coop/email/accounting_recap.subject.default.html:2
+#, fuzzy
msgid ""
"\n"
" Accounting recap\n"
@@ -1092,7 +1167,7 @@ msgstr ""
" Resumo contabilístico\n"
#: coop/templates/coop/email/co_purchaser_updated.body.html:6
-#, python-format
+#, fuzzy, python-format
msgid ""
"\n"
"
Hello %(display_name_short)s,
\n"
@@ -1116,24 +1191,28 @@ msgstr ""
"\n"
"
Olá %(display_name_short)s,
\n"
"
\n"
-" Este é um e-mail automático da SuperCoop. Gostaríamos de informar que um co-comprador foi\n"
+" Este é um e-mail automático da Rizoma. Gostaríamos de informar "
+"que um co-comprador foi\n"
" adicionado à sua conta Tapir.\n"
"
\n"
"
\n"
-" A partir de agora, %(co_purchaser_name)s pode fazer compras na loja usando o seu número de membro.\n"
+" A partir de agora, %(co_purchaser_name)s pode fazer compras na "
+"loja usando o teu número de membro.\n"
"
\n"
"
\n"
-" Se isto for um erro e não corresponder ao seu pedido, envie por favor um\n"
-" e-mail ao Member Office a informar-nos.\n"
+" Se isto for um erro e não corresponder ao seu pedido, envia por "
+"favor um\n"
+" e-mail ao GTT-"
+"membros a informar-nos.\n"
"
\n"
"
\n"
" Saudações cooperativas, \n"
-" O Member Office\n"
+" GTT-membros\n"
"
Encontra em anexo a confirmação de que adquiriu %(num_shares)s ações adicionais.
\n"
-"
Muito obrigado pelo seu apoio contínuo!
\n"
+"
Encontras em anexo a confirmação de que adquiriste %(num_shares)s "
+"títulos adicionais.
\n"
+"
Muito obrigado pelo teu apoio contínuo!
\n"
"
\n"
" Saudações cooperativas \n"
-" O Conselho da SuperCoop (Vorstand) \n"
+" O Conselho da Rizoma (Vorstand) \n"
"
\n"
" "
#: coop/templates/coop/email/extra_shares_bought.subject.html:2
-#, python-format
+#, fuzzy, python-format
msgid ""
"\n"
" Confirmation acquisition of additional shares in %(coop_name)s\n"
msgstr ""
"\n"
-" Confirmação da aquisição de ações adicionais em %(coop_name)s\n"
+" Confirmação da aquisição de títulos adicionais em %(coop_name)s\n"
#: coop/templates/coop/email/membership_confirmation.active.body.html:6
-#, python-format
+#, fuzzy, python-format
msgid ""
"\n"
"
Hello %(display_name)s,
\n"
@@ -1382,25 +1462,40 @@ msgstr ""
"\n"
"
Olá %(display_name)s,
\n"
"
\n"
-" Bem-vindo(a) como membro oficial da nossa cooperativa, %(coop_full_name)s! Em anexo encontra os detalhes da\n"
-" confirmação da sua adesão. Estamos muito felizes por tê-lo(a) connosco e por fazer a SuperCoop crescer consigo!\n"
+" Bem-vindo(a) como membro oficial da nossa cooperativa, "
+"%(coop_full_name)s! Em anexo encontra os detalhes da\n"
+" confirmação da sua adesão. Estamos muito felizes por tê-lo(a) "
+"connosco e por fazer a SuperCoop crescer consigo!\n"
"
\n"
"
\n"
-" Se ainda não o fez, por favor transfira o montante das suas ações juntamente com a taxa de admissão de 10 €\n"
+" Se ainda não o fez, por favor transfira o montante das suas "
+"ações juntamente com a taxa de admissão de 10 €\n"
" para a nossa conta DE98430609671121379000.\n"
-" É também importante concluir a formação online de higiene da METRO antes de iniciar o seu primeiro turno na Coop.\n"
+" É também importante concluir a formação online de higiene da "
+"METRO antes de iniciar o seu primeiro turno na Coop.\n"
" Pode encontrar todas as informações sobre isto no ponto 3.\n"
"
\n"
-"
Veja abaixo os pontos essenciais para arrancar com a sua adesão à SuperCoop:
\n"
+"
Veja abaixo os pontos essenciais para arrancar com a sua adesão à "
+"SuperCoop:
\n"
"
1. Member Office
\n"
-"
O Member Office é o ponto de apoio aos membros para tudo o que é SuperCoop!
\n"
-"
\n"
-" Visite o Member Office para se inscrever no seu primeiro turno (ou indicar que tem direito a uma isenção\n"
-" de trabalho). Criarão uma conta para si no Tapir, o nosso sistema online de gestão de turnos, e orientarão\n"
+"
O Member Office é o ponto de apoio aos membros para tudo o que é "
+"SuperCoop!
\n"
+"
\n"
+" Visite o Member Office para se inscrever no seu primeiro turno ("
+"ou indicar que tem direito a uma "
+"isenção\n"
+" de trabalho). Criarão uma conta para si no Tapir, o nosso "
+"sistema online de gestão de turnos, e orientarão\n"
" os passos para escolher o seu turno e grupo de trabalho.\n"
"
\n"
"
\n"
-" Para questões gerais sobre a sua adesão, contacte o Member Office por e-mail (%(contact_email_address)s) ou visitando a SuperCoop.\n"
+" Para questões gerais sobre a sua adesão, contacte o Member "
+"Office por e-mail (%(contact_email_address)s) ou visitando a SuperCoop.\n"
" Horário de funcionamento:\n"
"
\n"
-" O Tapir é o nosso sistema de gestão de turnos, criado por e para os membros\n"
-" da SuperCoop! Aí pode inscrever-se em turnos e ver informações adicionais sobre a sua adesão, como o número de ações que possui.\n"
+" O Tapir é o nosso "
+"sistema de gestão de turnos, criado por e para os membros\n"
+" da SuperCoop! Aí pode inscrever-se em turnos e ver informações "
+"adicionais sobre a sua adesão, como o número de ações que possui.\n"
"
\n"
"
\n"
" No Tapir encontrará também ligações para:\n"
"
\n"
-"
A semana atual no calendário ABCD (e o ABCD para o resto do ano),
\n"
+"
A semana atual no calendário ABCD (e o ABCD para o resto "
+"do ano),
\n"
"
wiki,
\n"
"
manual do membro,
\n"
"
horários de abertura,
\n"
"
contactos do Member Office
\n"
-"
e estatísticas sobre turnos e a cooperativa em geral.
\n"
+"
e estatísticas sobre turnos e a cooperativa em "
+"geral.
\n"
"
\n"
"
\n"
"
\n"
-" Para criar um perfil no Tapir, visite o Member Office no supermercado (ver horário acima). Não se esqueça de\n"
-" adicionar um co-comprador\n"
+" Para criar um perfil no Tapir, visite o Member Office no "
+"supermercado (ver horário acima). Não se esqueça de\n"
+" adicionar um co-comprador\n"
" à sua conta!\n"
"
\n"
"
\n"
-" Depois de criado o seu perfil, receberá um e-mail de confirmação com uma ligação para definir a palavra-passe.\n"
-" Poderá então usar as mesmas credenciais para aceder à Wiki da SuperCoop.\n"
+" Depois de criado o seu perfil, receberá um e-mail de confirmação "
+"com uma ligação para definir a palavra-passe.\n"
+" Poderá então usar as mesmas credenciais para aceder à Wiki da SuperCoop.\n"
"
\n"
-"
Dica: guarde nos favoritos o link do Tapir para o encontrar facilmente no futuro!
\n"
+"
Dica: guarde nos favoritos o link do Tapir para o encontrar "
+"facilmente no futuro!
\n"
"
3. Formação de Higiene
\n"
"
\n"
-" Os novos membros devem concluir a formação online de higiene da METRO ANTES do primeiro turno:\n"
-" https://kw.my/jEM8PK/#/. A formação demora cerca de 30 minutos e termina com\n"
-" um breve questionário. Se responder corretamente a 80%% das perguntas, recebe um certificado. Mostre-o a um colaborador no seu primeiro turno.\n"
+" Os novos membros devem concluir a formação online de higiene da "
+"METRO ANTES do primeiro turno:\n"
+" https://kw.my/jEM8PK/#/. "
+"A formação demora cerca de 30 minutos e termina com\n"
+" um breve questionário. Se responder corretamente a 80%% das "
+"perguntas, recebe um certificado. Mostre-o a um colaborador no seu primeiro "
+"turno.\n"
"
\n"
"
\n"
-" No primeiro turno, o responsável de equipa ou um colaborador fará uma introdução adicional de 10 minutos e\n"
-" mostrará os pontos e regras mais importantes da loja. Se não conseguir fazer a formação online em casa,\n"
+" No primeiro turno, o responsável de equipa ou um colaborador "
+"fará uma introdução adicional de 10 minutos e\n"
+" mostrará os pontos e regras mais importantes da loja. Se não "
+"conseguir fazer a formação online em casa,\n"
" pode realizá-la no nosso Welcome Desk durante o seu turno.\n"
"
\n"
"
4. Cartão de Membro
\n"
"
\n"
-" O seu Cartão de Membro pode ser ativado através da sua conta Tapir. Os cartões são baseados em consentimento, pois\n"
-" utilizamo-los para recolher dados sobre o valor total das compras e a frequência de compra. Usamos esta\n"
-" informação para apoiar o planeamento financeiro e projetar receitas futuras, e para perceber o impacto de medidas\n"
-" no sortido. Se a regularidade ou o total das compras diminuir, investigamos. Assim, o cartão ajuda-nos a\n"
+" O seu Cartão de Membro pode ser ativado através da sua conta "
+"Tapir. Os cartões são baseados em consentimento, pois\n"
+" utilizamo-los para recolher dados sobre o valor total das "
+"compras e a frequência de compra. Usamos esta\n"
+" informação para apoiar o planeamento financeiro e projetar "
+"receitas futuras, e para perceber o impacto de medidas\n"
+" no sortido. Se a regularidade ou o total das compras diminuir, "
+"investigamos. Assim, o cartão ajuda-nos a\n"
" compreender melhor as necessidades dos membros!\n"
"
Desça a página e dê o seu consentimento (pode remover a qualquer momento com um clique).
\n"
-"
Após consentir, verá um código de barras criado especialmente para si.
\n"
-"
Passo final: lembre-se de o digitalizar na caixa! Pode mostrar no telemóvel, descarregar PDF, capturar ecrã ou imprimir (imprimimos na loja se necessário!).
Desça a página e dê o seu consentimento (pode remover a "
+"qualquer momento com um clique).
\n"
+"
Após consentir, verá um código de barras criado "
+"especialmente para si.
\n"
+"
Passo final: lembre-se de o digitalizar na caixa! Pode "
+"mostrar no telemóvel, descarregar PDF, capturar ecrã ou imprimir (imprimimos "
+"na loja se necessário!).
\n"
" \n"
"
\n"
"
\n"
-" Por fim, pode também carregar saldo no seu cartão de membro e pagar as compras com ele! Evita taxas de transação\n"
-" para si e para a coop e pode fazê-lo diretamente na caixa (Kasse)! 🎉\n"
+" Por fim, pode também carregar saldo no seu cartão de membro e "
+"pagar as compras com ele! Evita taxas de transação\n"
+" para si e para a coop e pode fazê-lo diretamente na caixa (Kasse)"
+"! 🎉\n"
"
\n"
"
5. Grupos de Trabalho
\n"
-"
O seu Grupo de Trabalho indica que tipo de trabalho faz na Coop.
\n"
-"
Nota: o mais importante é escolher um horário de turno que possa cumprir regularmente. Sendo auto-organizados, contamos com a responsabilidade de cada membro.
\n"
+"
O seu Grupo de Trabalho indica que tipo "
+"de trabalho faz na Coop.
\n"
+"
Nota: o mais importante é escolher um horário de "
+"turno que possa cumprir regularmente. Sendo auto-organizados, contamos com a "
+"responsabilidade de cada membro.
\n"
"
\n"
-" Regra geral, os membros devem completar os primeiros seis turnos num dos grupos centrais:\n"
+" Regra geral, os membros devem completar os primeiros seis turnos "
+"num dos grupos centrais:\n"
"
\n"
-"
Receção e Reposição (receber entregas, manter prateleiras e arcas abastecidas, verificar validades – Rote Karte não necessária)
\n"
+"
Receção e Reposição (receber entregas, "
+"manter prateleiras e arcas abastecidas, verificar validades – Rote Karte não "
+"necessária)
\n"
"
Caixa
\n"
-"
Welcome Desk (saudar visitantes, verificar números de membro e responder a perguntas)
\n"
+"
Welcome Desk (saudar visitantes, "
+"verificar números de membro e responder a perguntas)
\n"
"
Manutenção (limpeza após o fecho)
\n"
"
\n"
"
\n"
-"
Os grupos e tarefas adaptam-se dinamicamente; pode mudar de grupo quando necessário.
\n"
-"
Além do turno regular, há muitas formas de se envolver. Reunimos os grupos adicionais na wiki.
\n"
+"
Os grupos e tarefas adaptam-se dinamicamente; pode mudar de grupo "
+"quando necessário.
\n"
+"
Além do turno regular, há muitas formas de se envolver. Reunimos "
+"os grupos "
+"adicionais na wiki.
\n"
"
6. Manual do Membro
\n"
-"
Antes de escrever ao Member Office, consulte o manual do membro com tudo o que precisa de saber.
\n"
+"
Antes de escrever ao Member Office, consulte o manual do membro com tudo o que precisa de saber.
Todos os membros são convidados ao plenário (atualmente online quando há decisões) para votar e debater. Datas e ligações na wiki e newsletter quinzenal.
\n"
+"
Todos os membros são convidados ao plenário (atualmente online "
+"quando há decisões) para votar e debater. Datas e ligações na wiki e "
+"newsletter quinzenal.
\n"
"
9. Newsletter
\n"
-"
Quarta-feira é dia de SuperCoop! De duas em duas semanas recebe novidades no seu e-mail.
\n"
+"
Quarta-feira é dia de SuperCoop! De duas em duas semanas recebe "
+"novidades no seu e-mail.
\n"
"
10. Slack
\n"
-"
Usamos o Slack para comunicação entre grupos, projetos e info geral. Registo gratuito e uso via browser ou app. Junte-se aqui! Se precisar de ajuda, contacte o Member Office.
\n"
+"
Usamos o Slack para comunicação entre grupos, projetos e info "
+"geral. Registo gratuito e uso via browser ou app. Junte-se aqui! Se precisar de ajuda, contacte o "
+"Member Office.
\n"
-" Bem-vindo(a) como membro investidor oficial de %(coop_full_name)s! Ao subscrever %(num_shares)s ação(ões) da cooperativa,\n"
-" decidiu moldar o sistema alimentar connosco e com muitos outros membros!\n"
+" Bem-vindo(a) como membro investidor oficial de %(coop_full_name)s! Ao "
+"subscrever %(num_shares)s ação(ões) da cooperativa,\n"
+" decidiu moldar o sistema alimentar connosco e com muitos outros membros!"
+"\n"
"
\n"
"
\n"
-" Temos muito gosto em confirmar a sua admissão à cooperativa com o número de membro %(owner_id)s. Consulte o PDF em anexo para todos os detalhes.\n"
+" Temos muito gosto em confirmar a sua admissão à cooperativa com o "
+"número de membro %(owner_id)s. Consulte o PDF em anexo para todos os "
+"detalhes.\n"
"
\n"
"
\n"
-" Se tiver perguntas, contacte o nosso Member Office (mitglied@supercoop.de).\n"
+" Se tiver perguntas, contacte o nosso Member Office (mitglied@supercoop.de).\n"
"
\n"
-" Como indicou na sua carta de cancelamento que pretende transferir as suas ações para a SuperCoop,\n"
+" Como na sua carta de cancelamento que pretende transferir as "
+"suas ações para a SuperCoop,\n"
" a sua cessação produz efeitos imediatos.\n"
"
\n"
-" Indicou na sua carta de cancelamento que pretende transferir as suas ações para outro membro. Informámos\n"
+" Indicou na sua carta de cancelamento que pretende transferir "
+"as suas ações para outro membro. Informámos\n"
" %(transferred_member)s separadamente.\n"
" A sua cessação produz efeitos imediatos.\n"
"
\n"
-" Desejamos-lhe tudo de bom para o futuro e, se tiver mais questões, não hesite em contactar o Member Office\n"
+" Desejamos-te tudo de bom para o futuro e, se tiveres mais "
+"questões, não hesites em contactar o GTT-membros\n"
" (%(contact_email_address)s).\n"
"
\n"
-" Boas notícias para si e, infelizmente, más notícias para nós!\n"
+" Boas notícias para ti e, infelizmente, más notícias para nós!\n"
" \n"
-" %(resigning_member)s cancelou a sua adesão à SuperCoop Berlin eG e legou-lhe a(s) sua(s) ação(ões).\n"
-" Não se surpreenda se vir subitamente mais ações na sua conta Tapir.\n"
-" Se isto foi feito sem o seu consentimento, contacte o Member Office\n"
+" %(resigning_member)s cancelou a sua adesão à SuperCoop Berlin eG "
+"e legou-lhe a(s) sua(s) ação(ões).\n"
+" Não se surpreenda se vir subitamente mais ações na sua conta "
+"Tapir.\n"
+" Se isto foi feito sem o seu consentimento, contacte o Member "
+"Office\n"
" (%(contact_email_address)s).\n"
"
\n"
"
Saudações cooperativas,
\n"
@@ -1731,7 +1904,7 @@ msgstr ""
" %(resigned_member_full)s legou-lhe a(s) sua(s) ação(ões) devido à sua demissão\n"
#: coop/templates/coop/email/tapir_account_created.body.html:6
-#, python-format
+#, fuzzy, python-format
msgid ""
"\n"
"
acabámos de criar uma conta para si no sistema de membros da SuperCoop. Aqui pode ver os seus próximos turnos, reservar um turno adicional ou marcar um turno como “à procura de substituto” (por exemplo, quando vai de férias). \n"
-" Consulte a secção III do Manual do Membro para mais informações sobre o sistema de substituições.
\n"
+"
acabámos de criar uma conta para ti no sistema de membros da "
+"secção consumo da Rizoma. Aqui podes ver os teus próximos turnos, reservar "
+"um turno adicional ou cancelar um turno (por exemplo, quando vais de férias)"
+". \n"
+" Consulta o Manual para mais "
+"informações sobre o sistema de turnos.
\n"
" "
#: coop/templates/coop/email/tapir_account_created.body.html:14
@@ -1757,13 +1935,17 @@ msgstr ""
" "
#: coop/templates/coop/email/tapir_account_created.body.html:19
+#, fuzzy
msgid ""
"\n"
" Flying members: Please keep in mind that you must have at least one shift “banked” for each shift cycle. For more information please see the Member Manual .\n"
" "
msgstr ""
"\n"
-" Membros “voadores”: lembrem-se de que devem ter pelo menos um turno “em banco” por ciclo de turnos. Para mais informações, consultem o Manual do Membro.\n"
+" Membros “flex”: lembrem-se de que devem ter pelo menos um "
+"turno “em crédito” por ciclo de turnos. Para mais informações, consultem o "
+"Manual do "
+"Membro.\n"
" "
#: coop/templates/coop/email/tapir_account_created.body.html:27
@@ -2403,7 +2585,7 @@ msgstr "Está totalmente pago"
#: coop/views/shareowner.py:664
msgid "Name or member ID"
-msgstr "Nome ou ID de membro"
+msgstr "Nome ou ID do membro"
#: coop/views/shareowner.py:668
msgid "Is currently exempted from shifts"
@@ -2783,12 +2965,14 @@ msgstr "Calendário anual ABCD, semana atual: {current_week_group_name}"
#: shifts/emails/flying_member_registration_reminder_email.py:17
msgid "Flying member registration reminder"
-msgstr "Lembrete de registo para membro voador"
+msgstr "Lembrete de registo para membro flex"
#: shifts/emails/flying_member_registration_reminder_email.py:22
#, python-format
msgid "Sent to flying members %(nb_days)s days after a cycle has begun, if they haven't registered to a shift for this cycle."
-msgstr "Enviado aos membros voadores %(nb_days)s dias após o início de um ciclo, se não se registaram em nenhum turno para esse ciclo."
+msgstr ""
+"Enviado aos membros flex %(nb_days)s dias após o início de um ciclo, se não "
+"se registaram em nenhum turno para esse ciclo."
#: shifts/emails/freeze_warning_email.py:28
msgid "Freeze warning"
@@ -2796,15 +2980,19 @@ msgstr "Aviso de congelamento"
#: shifts/emails/freeze_warning_email.py:33
msgid "Sent to a member when their shift status is not frozen yet but will be set to frozen if they don't register for make-up shifts."
-msgstr "Enviado a um membro quando o seu estado de turnos ainda não está congelado mas será congelado se não se inscrever em turnos de compensação."
+msgstr ""
+"Enviado a um membro quando o seu estado de turnos ainda não está suspenso "
+"mas será suspenso se não se inscrever em turnos de compensação."
#: shifts/emails/member_frozen_email.py:23
msgid "Shift status set to frozen"
-msgstr "Estado de turnos definido como congelado"
+msgstr "Estado de turnos definido como suspenso"
#: shifts/emails/member_frozen_email.py:28
msgid "Sent to a member when their shift status gets set to frozen. Usually happens if they miss to many shifts."
-msgstr "Enviado a um membro quando o seu estado de turnos passa a congelado. Normalmente acontece se faltar a demasiados turnos."
+msgstr ""
+"Enviado a um membro quando o seu estado de turnos passa a suspenso. "
+"Normalmente acontece se faltar a demasiados turnos."
#: shifts/emails/shift_missed_email.py:22
msgid "Shift missed"
@@ -2837,7 +3025,8 @@ msgstr "Notificação de descongelamento"
#: shifts/emails/unfreeze_notification_email.py:22
msgid "Sent to a member when their shift status gets set from frozen to flying."
-msgstr "Enviado a um membro quando o seu estado de turnos passa de congelado a voador."
+msgstr ""
+"Enviado a um membro quando o seu estado de turnos passa de suspenso a flex."
#: shifts/forms.py:60
msgid "The shift must end after it starts."
@@ -3020,15 +3209,15 @@ msgstr "🏠 ABCD"
#: shifts/models.py:979
msgid "✈ Flying"
-msgstr "✈ Voador"
+msgstr "✈ Flex"
#: shifts/models.py:980
msgid "❄ Frozen"
-msgstr "❄ Congelado"
+msgstr "❄ Suspenso"
#: shifts/models.py:1006
msgid "Is frozen"
-msgstr "Está congelado"
+msgstr "Está suspenso"
#: shifts/models.py:1185
msgid "Cycle start date"
@@ -3055,6 +3244,7 @@ msgstr ""
" "
#: shifts/templates/shifts/cancel_shift.html:24
+#, fuzzy
msgid ""
"\n"
" Cancelling a shift is used for example for holidays. It has the following consequences :\n"
@@ -3066,11 +3256,15 @@ msgid ""
" "
msgstr ""
"\n"
-" Cancelar um turno é usado, por exemplo, em feriados. Tem as seguintes consequências:\n"
+" Cancelar um turno tem as seguintes consequências:"
+"\n"
"
\n"
-"
Deixa de ser possível registar-se no turno.
\n"
-"
Os membros registados via turno ABCD recebem um ponto de turno.
\n"
-"
Os membros registados apenas para este turno não recebem ponto.
\n"
+"
Deixa de ser possível registar-se no "
+"turno.
\n"
+"
Os membros registados via turno ABCD "
+"recebem um ponto de turno.
\n"
+"
Os membros registados apenas para este "
+"turno não recebem ponto.
\n"
-" Um novo ciclo de turnos já decorre há uma semana e reparámos que ainda não se registou num turno adequado. Como membro voador,\n"
-" ao contrário dos membros com turno ABCD regular, tem de tratar disto a cada quatro semanas.\n"
+" Um novo ciclo de turnos já decorre há uma semana e reparámos que "
+"ainda não te registaste num turno. Como membro flex,\n"
+" e ao contrário dos membros com turno ABCD regular, tens de ser "
+"tu a marcar os teus turnos, a cada quatro semanas.\n"
"
\n"
"
\n"
-" Veja o calendário de turnos no Tapir\n"
-" e registe-se num dos turnos assinalados a azul — é onde mais apoio é necessário de momento.\n"
+" Vê o calendário de turnos no Tapir\n"
+" e regista-se num dos turnos assinalados a azul — é onde mais "
+"apoio é necessário de momento.\n"
"
\n"
"
\n"
" Saudações cooperativas, \n"
-" O Member Office\n"
+" GTT-membros\n"
"
\n"
" "
@@ -3152,7 +3351,7 @@ msgid "Sign up for your next SuperCoop shift"
msgstr "Inscreva-se no seu próximo turno da SuperCoop"
#: shifts/templates/shifts/email/freeze_warning.body.html:6
-#, python-format
+#, fuzzy, python-format
msgid ""
"\n"
"
\n"
@@ -3194,26 +3393,32 @@ msgstr ""
"
Olá %(display_name_short)s,
\n"
"\n"
"
\n"
-" Este é um e-mail automático de %(coop_name)s para informar que o seu registo de turnos atingiu %(account_balance)s.\n"
+" Este é um e-mail automático de %(coop_name)s para informar que o "
+"teu registo de turnos atingiu %(account_balance)s.\n"
"
\n"
"\n"
"
\n"
-" Tem agora %(freeze_after_days)s dias para se inscrever nos turnos em atraso como turnos de compensação. Estes turnos\n"
+" Tens agora %(freeze_after_days)s dias para te inscreveres nos "
+"turnos em atraso como turnos de compensação. Estes turnos\n"
" devem ocorrer nas próximas %(nb_weeks_in_the_future)s semanas.\n"
"
\n"
"
\n"
-" Atenção: se não se inscrever nos turnos de compensação nos próximos %(freeze_after_days)s dias,\n"
-" a sua conta será congelada. Isso significa que não poderá fazer compras nem votar nas assembleias gerais. Para\n"
-" descongelar a conta, deve inscrever-se nos turnos em atraso.\n"
+" Atenção: se não te inscreveres nos turnos de compensação nos "
+"próximos %(freeze_after_days)s dias,\n"
+" a tua conta será suspensa. Isso significa que não poderás fazer "
+"compras. Para\n"
+" reativar a conta, deves inscrever-te nos turnos em atraso.\n"
"
\n"
"
\n"
-" Se for membro ABCD e não cumprir os turnos de compensação nem os regulares, será removido do seu turno ABCD.\n"
+" Se fores membro ABCD e não cumprires os turnos de compensação "
+"nem os regulares, serás removido do teu turno ABCD.\n"
"
\n"
"
\n"
-" Inscreva-se nos turnos em atraso o quanto antes. Para mais informações sobre o sistema de turnos,\n"
-" consulte o Manual do Membro.\n"
+" Inscreve-te nos turnos em atraso o quanto antes. Para mais "
+"informações sobre o sistema de turnos,\n"
+" consulta o Manual.\n"
"
\n"
-"
Obrigado!
\n"
+"
Obrigada!
\n"
"
\n"
" Saudações cooperativas, \n"
" O Member Office\n"
@@ -3287,7 +3492,7 @@ msgid ""
" Your Tapir account has been frozen\n"
msgstr ""
"\n"
-" A sua conta Tapir foi congelada\n"
+" A tua conta Tapir foi suspensa\n"
#: shifts/templates/shifts/email/shift_missed.body.html:6
#, python-format
@@ -3511,7 +3716,7 @@ msgid ""
" Your Tapir account has been unfrozen.\n"
msgstr ""
"\n"
-" A sua conta Tapir foi descongelada.\n"
+" A tua conta Tapir foi reativada.\n"
#: shifts/templates/shifts/log/create_exemption_log_entry.html:2
msgid "without end date"
@@ -3591,9 +3796,14 @@ msgid ""
" "
msgstr ""
"\n"
-"
Um membro voador já se registou nos turnos abaixo.
\n"
-"
É ainda possível inscrever alguém neste turno via sistema ABCD. Isso significa que o membro se inscreve para trabalhar este turno de forma recorrente de 4 em 4 semanas (uma vez por ciclo).
\n"
-"
IMPORTANTE: Se um membro voador já se tinha inscrito para o mesmo turno, o membro ABCD terá de se inscrever noutro turno para esse ciclo.
\n"
+"
Um membro flex já se registou nos turnos "
+"abaixo.
\n"
+"
É ainda possível inscrever alguém neste turno via "
+"sistema ABCD. Isso significa que o membro se inscreve para fazer este turno "
+"de forma recorrente de 4 em 4 semanas (uma vez por ciclo).
\n"
+"
IMPORTANTE: Se um membro flex já se tinha "
+"inscrito para o mesmo turno, o membro ABCD terá de se inscrever noutro turno "
+"para esse ciclo.
Para cada membro, verifica se deve ser colocado como congelado, descongelado, ou avisado de que será congelado em breve.
\n"
-"
Este comando é executado automaticamente uma vez por dia. Pode acioná-lo manualmente aqui para testes ou para acelerar uma atualização de estado.
"
+"
Para cada membro, verifica se deve ser "
+"colocado como suspenso, reativado, ou avisado de que será suspenso em "
+"breve.
\n"
+"
Este comando é executado automaticamente uma vez por dia. Pode acioná-lo "
+"manualmente aqui para testes ou para acelerar uma atualização de estado.
"
+"\n"
+" "
#: shifts/templates/shifts/shift_management.html:60
msgid "Generate shifts from ABCD shifts"
@@ -4428,7 +4643,7 @@ msgstr "Criar registo manual na conta de turnos para: %(link)s"
#: shifts/views/views.py:358
msgid "Frozen statuses updated."
-msgstr "Estados congelados atualizados."
+msgstr "Estados suspensos atualizados."
#: statistics/apps.py:18
msgid "Statistics"
@@ -4440,7 +4655,9 @@ msgstr "Membros ABCD"
#: statistics/services/data_providers/data_provider_abcd_members.py:23
msgid "Only members who work are counted: members that are exempted, paused, frozen... are not counted"
-msgstr "Apenas contam os membros que trabalham: isentos, em pausa, congelados… não são contabilizados"
+msgstr ""
+"Apenas contam os membros que trabalham: isentos, em ausência, suspensos… não "
+"são contabilizados"
#: statistics/services/data_providers/data_provider_active_members.py:13
msgid "Active members"
@@ -4448,7 +4665,9 @@ msgstr "Membros ativos"
#: statistics/services/data_providers/data_provider_active_members.py:18
msgid "Active in the sense of their membership: paused and investing members are not active, but frozen members are active"
-msgstr "Ativos no sentido da adesão: membros em pausa e investidores não são ativos, mas membros congelados são considerados ativos"
+msgstr ""
+"Ativos no sentido da adesão: membros em ausência temporária não são ativos, "
+"mas membros suspensos são considerados ativos"
#: statistics/services/data_providers/data_provider_active_members_with_account.py:14
msgid "Active members with Tapir account"
@@ -4481,7 +4700,9 @@ msgstr "Membros isentos"
#: statistics/services/data_providers/data_provider_exempted_members.py:22
msgid "Counting only members that would work if they were not exempted: frozen and investing members with an exemption are not counted."
-msgstr "Conta apenas membros que trabalhariam se não estivessem isentos: membros congelados e investidores com isenção não são contabilizados."
+msgstr ""
+"Conta apenas membros que trabalhariam se não estivessem isentos: membros "
+"suspensos não são contabilizados."
#: statistics/services/data_providers/data_provider_exempted_members_that_work.py:14
msgid "Exempted members that work"
@@ -4489,18 +4710,21 @@ msgstr "Membros isentos que trabalham"
#: statistics/services/data_providers/data_provider_exempted_members_that_work.py:19
msgid "Counting all exempted members (ignoring if they are frozen or investing) that actually did a shift in the past 60 days. Just registering to the shift doesn't count, the attendance must be confirmed."
-msgstr "Conta todos os membros isentos (ignorando se estão congelados ou são investidores) que efetivamente fizeram um turno nos últimos 60 dias. Só registar não conta; a presença tem de ser confirmada."
+msgstr ""
+"Conta todos os membros isentos (ignorando se estão suspensos) que "
+"efetivamente fizeram um turno nos últimos 60 dias. O registo no turno não "
+"conta; a presença tem de ser confirmada."
#: statistics/services/data_providers/data_provider_flying_members.py:21
msgid "Flying members"
-msgstr "Membros voadores"
+msgstr "Membros flex"
#: statistics/services/data_providers/data_provider_frozen_members.py:16
#: statistics/templates/statistics/main_statistics.html:118
#: statistics/templates/statistics/main_statistics.html:129
#: statistics/views/main_view.py:392
msgid "Frozen members"
-msgstr "Membros congelados"
+msgstr "Membros suspensos"
#: statistics/services/data_providers/data_provider_frozen_members.py:21
msgid "Counted out of 'active' members: paused and investing members not counted."
@@ -4508,11 +4732,13 @@ msgstr "Contados dentro dos ‘ativos’: membros em pausa e investidores não s
#: statistics/services/data_providers/data_provider_frozen_members_long_term.py:17
msgid "Long-term frozen members"
-msgstr "Membros congelados de longa duração"
+msgstr "Membros suspensos de longa duração"
#: statistics/services/data_providers/data_provider_frozen_members_long_term.py:22
msgid "Members that are frozen since more than 180 days (roughly 6 month). Long-term frozen members are included in the \"Frozen members\" dataset"
-msgstr "Membros congelados há mais de 180 dias (cerca de 6 meses). Estão incluídos no conjunto “Membros congelados”"
+msgstr ""
+"Membros suspensos há mais de 180 dias (cerca de 6 meses). Estão incluídos no "
+"conjunto “Membros suspensos”"
#: statistics/services/data_providers/data_provider_investing_members.py:13
msgid "Investing members"
@@ -4545,7 +4771,9 @@ msgstr "Membros com direito a compras"
#: statistics/services/data_providers/data_provider_purchasing_members.py:19
msgid "Members who are allowed to shop. To be allowed to shop, a member must be active (see the description for \"Active members\"), have a Tapir account, and not be frozen."
-msgstr "Membros com direito a compras. Para tal, o membro deve ser ativo (ver “Membros ativos”), ter conta Tapir e não estar congelado."
+msgstr ""
+"Membros com direito a compras. Para tal, o membro deve ser ativo (ver “"
+"Membros ativos”), ter conta Tapir e não estar suspenso."
#: statistics/services/data_providers/data_provider_resignations_created.py:13
msgid "Created resignations"
@@ -4569,7 +4797,9 @@ msgstr "Parceiros de turno"
#: statistics/services/data_providers/data_provider_shift_partners.py:23
msgid "Counted out of working members only: a frozen member with a shift partner is not counted"
-msgstr "Contados apenas entre os membros que trabalham: um membro congelado com parceiro de turno não é contabilizado"
+msgstr ""
+"Contados apenas entre os membros que trabalham: um membro suspenso com "
+"parceiro de turno não é contabilizado"
#: statistics/services/data_providers/data_provider_total_members.py:13
msgid "Total members"
@@ -4660,12 +4890,16 @@ msgid ""
msgstr ""
"\n"
"
\n"
-" Todos os membros trabalhadores e todos os com isenção (licença parental,\n"
-" mais de 70, etc.). Membros congelados (que ainda não marcaram turnos de compensação)\n"
-" ou em pausa (ausência de 3 ciclos de turnos ou mais) não têm direito a compras.\n"
+" Todos os membros trabalhadores e todos "
+"os com isenção (licença parental,\n"
+" mais de 65, etc.). Membros suspensos ("
+"que ainda não marcaram turnos de compensação)\n"
+" ou em ausência temporária (ausência de 3 "
+"ciclos de turnos ou mais) não têm direito a compras.\n"
"
\n"
"
\n"
-" Número-alvo de membros com compras para o break-even: %(target_count)s.\n"
+" Número-alvo de membros com compras para "
+"o break-even: %(target_count)s.\n"
"
\n"
" "
@@ -4684,9 +4918,12 @@ msgid ""
" "
msgstr ""
"\n"
-" Membros trabalhadores são membros ativos sem isenção. Exemplos de isenção: um ano de\n"
-" licença parental, doença prolongada ou membros com mais de 70 anos.\n"
-" Número necessário de membros trabalhadores para preencher todos os turnos: %(target_count)s.\n"
+" Membros trabalhadores são membros ativos sem "
+"isenção. Exemplos de isenção: \n"
+" licença parental, doença ou membros com mais "
+"de 65 anos.\n"
+" Número necessário de membros trabalhadores "
+"para preencher todos os turnos: %(target_count)s.\n"
" "
#: statistics/templates/statistics/main_statistics.html:122
@@ -5795,7 +6032,7 @@ msgstr "NOME INDISPONÍVEL"
#: welcomedesk/apps.py:21 welcomedesk/apps.py:24
#: welcomedesk/templates/welcomedesk/welcome_desk_search.html:14
msgid "Welcome Desk"
-msgstr "Welcome Desk"
+msgstr ""
#: welcomedesk/services/welcome_desk_reasons_cannot_shop_service.py:44
#, python-format
@@ -5810,7 +6047,9 @@ msgstr "%(name)s é um membro investidor. Se quiser fazer compras, tem de se tor
#: welcomedesk/services/welcome_desk_reasons_cannot_shop_service.py:51
#, python-format
msgid "%(name)s has been frozen because they missed too many shifts.If they want to shop, they must first be re-activated.Contact a member of the management team."
-msgstr "%(name)s foi colocado em estado congelado por faltar a demasiados turnos. Se quiser fazer compras, tem de ser reativado primeiro. Contacte um membro da equipa de gestão."
+msgstr ""
+"%(name)s foi colocado em estado suspenso por faltar a demasiados turnos. Se "
+"quiser fazer compras, tem de ser reativado primeiro. Contacta o GTT-membros."
#: welcomedesk/services/welcome_desk_reasons_cannot_shop_service.py:56
#, python-format
From 339bac9686106d2e6b922a8f42fb1ffe485aa3e3 Mon Sep 17 00:00:00 2001
From: Lara Carvalho
Date: Sun, 21 Sep 2025 15:59:06 +0000
Subject: [PATCH 06/27] Translated using Weblate (Portuguese)
Currently translated at 95.3% (41 of 43 strings)
Translation: tapir/tapir javascript translations
Translate-URL: http://weblate.seriousdino.org/projects/tapir/tapir-javascript-translations/pt/
---
.../translations/locale/pt/LC_MESSAGES/djangojs.po | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/tapir/translations/locale/pt/LC_MESSAGES/djangojs.po b/tapir/translations/locale/pt/LC_MESSAGES/djangojs.po
index 7cc8599e0..608ff0348 100644
--- a/tapir/translations/locale/pt/LC_MESSAGES/djangojs.po
+++ b/tapir/translations/locale/pt/LC_MESSAGES/djangojs.po
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-28 12:57+0200\n"
-"PO-Revision-Date: 2025-09-21 12:59+0000\n"
+"PO-Revision-Date: 2025-09-22 15:59+0000\n"
"Last-Translator: Lara Carvalho \n"
"Language-Team: Portuguese \n"
@@ -133,7 +133,7 @@ msgstr "Adicionar uma qualificação"
#: dist/assets/welcome_desk-OgiTR3jj.js:1
msgid "Use the search field on the top right."
-msgstr "Use o campo de pesquisa no canto superior direito."
+msgstr "Usa o campo de pesquisa no canto superior direito."
#: dist/assets/welcome_desk-OgiTR3jj.js:1
msgid "Name"
@@ -153,7 +153,7 @@ msgstr "Não"
#: dist/assets/welcome_desk-OgiTR3jj.js:1
msgid "Member details"
-msgstr "Detalhes do membro"
+msgstr "Detalhes de membro"
#: dist/assets/welcome_desk-OgiTR3jj.js:1
msgid "Member"
@@ -180,12 +180,15 @@ msgid "None"
msgstr "Nenhum(a)"
#: dist/assets/welcome_desk-OgiTR3jj.js:1
+#, fuzzy
msgid "Whoops! Something went wrong. Please try again. If it keeps happening, please write in the #tapir channel on Slack with your current search: "
-msgstr "Ups! Algo correu mal. Por favor, tente novamente. Se o problema persistir, escreva no canal #tapir do Slack com a sua pesquisa atual: "
+msgstr ""
+"Ups! Algo correu mal. Por favor, tenta novamente. Se o problema persistir, "
+"escreva no canal #tapir do Slack com a sua pesquisa atual: "
#: dist/assets/welcome_desk-OgiTR3jj.js:1
msgid "Welcome Desk"
-msgstr "Welcome Desk"
+msgstr ""
#: dist/assets/welcome_desk-OgiTR3jj.js:1
msgid "Name or member ID"
From fe0031be23b64501d7fd1ac5fdc4eba104099ffb Mon Sep 17 00:00:00 2001
From: Lara Carvalho
Date: Sun, 28 Sep 2025 11:30:34 +0000
Subject: [PATCH 07/27] Translated using Weblate (Portuguese)
Currently translated at 93.6% (908 of 970 strings)
Translation: tapir/tapir python translations
Translate-URL: http://weblate.seriousdino.org/projects/tapir/tapir-python-translations/pt/
---
.../locale/pt/LC_MESSAGES/django.po | 110 ++++++++++++------
1 file changed, 72 insertions(+), 38 deletions(-)
diff --git a/tapir/translations/locale/pt/LC_MESSAGES/django.po b/tapir/translations/locale/pt/LC_MESSAGES/django.po
index 71c1c0c45..25725ed73 100644
--- a/tapir/translations/locale/pt/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/pt/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-28 12:56+0200\n"
-"PO-Revision-Date: 2025-09-22 15:59+0000\n"
+"PO-Revision-Date: 2025-09-29 08:38+0000\n"
"Last-Translator: Lara Carvalho \n"
"Language-Team: Portuguese \n"
@@ -1944,12 +1944,13 @@ msgstr ""
"\n"
" Membros “flex”: lembrem-se de que devem ter pelo menos um "
"turno “em crédito” por ciclo de turnos. Para mais informações, consultem o "
-"Manual do "
-"Membro.\n"
+"Manual .\n"
" "
#: coop/templates/coop/email/tapir_account_created.body.html:27
-#, python-format
+#, fuzzy, python-format
msgid ""
"\n"
"
Your username is *%(username)s* . In order to login to your account, you first have to set a password : Click here to set your password
Em alternativa, e para quaisquer questões, pode sempre "
+"contactar o GTT-membros\n"
" "
#: coop/templates/coop/email/tapir_account_created.body.html:35
@@ -1974,7 +1982,7 @@ msgid ""
msgstr ""
"\n"
" Cumprimentos cooperativos, \n"
-" A equipa do Member Office da SuperCoop Berlin.\n"
+" A equipa do GTT-membros da Rizoma.\n"
" "
#: coop/templates/coop/email/tapir_account_created.subject.html:2
@@ -2225,7 +2233,7 @@ msgid "Confirmation acquisition of additional shares in %(coop_full_name)s"
msgstr "Confirmação da aquisição de ações adicionais em %(coop_full_name)s"
#: coop/templates/coop/pdf/extra_shares_confirmation_pdf.default.html:66
-#, python-format
+#, fuzzy, python-format
msgid ""
"\n"
"
Dear %(display_name_short)s,
\n"
@@ -2263,19 +2271,23 @@ msgstr ""
"\n"
"
\n"
" Obrigado pelo seu apoio! \n"
-" Este documento confirma que adquiriu %(num_shares)s ações adicionais.\n"
-" Assim que o pagamento for recebido, esta informação será também refletida no seu perfil Tapir.\n"
+" Este documento confirma que adquiriu %(num_shares)s ações "
+"adicionais.\n"
+" Assim que o pagamento for recebido, esta informação será também "
+"refletida no seu perfil Tapir.\n"
"
\n"
"\n"
"
\n"
" Lembrete: O seu número de membro é %(member_number)s. \n"
-" Este número é importante para a sua comunicação connosco e com o Member Office.\n"
+" Este número é importante para a sua comunicação connosco e com o "
+"Member Office.\n"
" Guarde-o, pois irá precisar dele com frequência.\n"
"
\n"
"\n"
@@ -2527,6 +2539,7 @@ msgid "Edit membership pause: %(name)s"
msgstr "Editar pausa de adesão: %(name)s"
#: coop/views/membership_resignation.py:112
+#, fuzzy
msgid "Share(s) gifted {chr(8594)} SuperCoop"
msgstr "Ação(ões) doadas {chr(8594)} SuperCoop"
@@ -2976,7 +2989,7 @@ msgstr ""
#: shifts/emails/freeze_warning_email.py:28
msgid "Freeze warning"
-msgstr "Aviso de congelamento"
+msgstr "Aviso de suspensão"
#: shifts/emails/freeze_warning_email.py:33
msgid "Sent to a member when their shift status is not frozen yet but will be set to frozen if they don't register for make-up shifts."
@@ -3021,7 +3034,7 @@ msgstr "Enviado a um membro que procurava um substituto quando o respetivo slot
#: shifts/emails/unfreeze_notification_email.py:17
msgid "Unfreeze Notification"
-msgstr "Notificação de descongelamento"
+msgstr "Notificação de suspensão"
#: shifts/emails/unfreeze_notification_email.py:22
msgid "Sent to a member when their shift status gets set from frozen to flying."
@@ -3330,9 +3343,9 @@ msgstr ""
" \n"
"
\n"
" Um novo ciclo de turnos já decorre há uma semana e reparámos que "
-"ainda não te registaste num turno. Como membro flex,\n"
-" e ao contrário dos membros com turno ABCD regular, tens de ser "
-"tu a marcar os teus turnos, a cada quatro semanas.\n"
+"ainda não te registaste num turno. Como membro flex, e ao contrário dos "
+"membros com turno ABCD regular, tens de ser tu a marcar os teus turnos, a "
+"cada quatro semanas.\n"
"
\n"
-" Este é um e-mail automático de %(coop_name)s para informar que a sua conta Tapir na SuperCoop foi temporariamente congelada.\n"
-" Infelizmente, o seu registo de turnos esteve abaixo de %(freeze_threshold)s turnos por mais de %(freeze_after_days)s dias.\n"
+" Este é um e-mail automático de %(coop_name)s para informar que a "
+"tua conta Tapir na Rizoma foi temporariamente suspensa.\n"
+" Infelizmente, o teu registo de turnos esteve abaixo de "
+"%(freeze_threshold)s turnos por mais de %(freeze_after_days)s dias.\n"
"
\n"
-"
Enquanto a conta estiver congelada, não pode fazer compras nem votar na Assembleia Geral.
\n"
+"
Enquanto a conta estiver suspensa, não podes fazer compras na "
+"mercearia/rizobar.
\n"
"
\n"
-" Para descongelar, inscreva-se nos turnos em atraso como turnos de compensação. Note que\n"
-" estes turnos devem ocorrer nas próximas %(nb_weeks_in_the_future_for_make_up_shifts)s semanas.\n"
+" Para reactivar a tua conta, inscreve-te nos turnos em atraso "
+"como turnos de compensação. Nota que\n"
+" estes turnos devem ocorrer nas próximas "
+"%(nb_weeks_in_the_future_for_make_up_shifts)s semanas.\n"
"
\n"
"
\n"
-" Inscreva-se o quanto antes. Para mais informações sobre o sistema de turnos,\n"
-" consulte o Manual do Membro.\n"
+" Inscreve-te o quanto antes. Para mais informações sobre o "
+"sistema de turnos,\n"
+" consulta o Manual.\n"
"
\n"
"
Muito obrigado!
\n"
"
\n"
" Saudações cooperativas, \n"
-" O Member Office\n"
+" GTT-membros\n"
"
\n"
" "
@@ -3495,7 +3514,7 @@ msgstr ""
" A tua conta Tapir foi suspensa\n"
#: shifts/templates/shifts/email/shift_missed.body.html:6
-#, python-format
+#, fuzzy, python-format
msgid ""
"\n"
"
Hi %(display_name_short)s,
\n"
@@ -3536,24 +3555,39 @@ msgstr ""
"\n"
"
Olá %(display_name_short)s,
\n"
"\n"
-"
Este é um e-mail automático da SuperCoop.
\n"
+"
Este é um e-mail automático da Rizoma.
\n"
"\n"
"
\n"
-" Foi registado no Tapir que não compareceu ao seguinte turno, apesar de estar registado: %(shift_display_name)s\n"
+" Foi registado no Tapir que não compareceste ao seguinte turno, "
+"apesar de estares registade: %(shift_display_name)s\n"
"
\n"
"
\n"
-" Se isto for um erro (por exemplo, se estava justificado), contacte o Member Office o mais rapidamente possível, respondendo a este e-mail.\n"
+" Se isto for um erro (por exemplo, se estava justificado), "
+"contacta o GTT-membros o mais rapidamente possível, respondendo a este "
+"e-mail.\n"
"
\n"
"
\n"
-" Para que o sistema funcione em caso de ausências, existe o sistema de substituições (interno). Isto alivia tanto a si como à sua equipa. Veja como funciona no\n"
-" Manual do Membro.\n"
-" É sua responsabilidade procurar ativamente um substituto — incluindo marcar no Tapir e procurar via Slack, por exemplo.\n"
+" Quando não podes comparecer num turno com menos de 6 horas de "
+"antecedência, é da tua responsabilidade encontrar alguém para te substituir, "
+"para garantir o bom funcionamento da mercearia, incluindo marcar no Tapir e "
+"procurar via Slack, por exemplo. Podes consultar mais informações no \n"
+" Manual.\n"
+"\n"
"
\n"
"
\n"
-" Regra geral, por cada turno falhado sem substituto, é necessário efetuar dois turnos de compensação. A sua conta Tapir tem agora uma nota sobre isto.\n"
+" Regra geral, por cada turno falhado sem substituto, é necessário "
+"efetuar dois turnos de compensação. A tua conta Tapir tem agora uma nota "
+"sobre isto.\n"
"
\n"
"
\n"
-" Inscreva-se nos turnos de compensação no Tapir dentro das próximas quatro semanas para equilibrar a conta. Caso contrário, os seus direitos de compra e voto poderão ser pausados até retomar o trabalho ativo (ver Manual do Membro (Secção III.F)).\n"
+" Inscreva-se nos turnos de compensação no Tapir dentro das "
+"próximas quatro semanas para equilibrar a conta. Caso contrário, os teus "
+"direitos de compra poderão ser suspensos até retomares o trabalho activo ("
+"ver Manual (Secção III.F)).\n"
"
\n"
"
Obrigado pela compreensão.
\n"
"
\n"
From b21c08649b44ec630ced781fb128969af9716608 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Th=C3=A9ophile=20Madet?=
Date: Mon, 29 Sep 2025 12:37:36 +0200
Subject: [PATCH 08/27] feat: added views to deleted an ABCD slot template.
---
.../tests/test_tapir_user_self_update.py | 7 +
.../tests/test_co_purchaser_updated_mail.py | 7 +
.../coop/tests/test_create_general_account.py | 4 +
tapir/shifts/forms.py | 63 +++++++++
.../0081_shiftslottemplate_deleted.py | 18 +++
tapir/shifts/models.py | 5 +-
.../shifts/templates/shifts/shift_detail.html | 4 +-
.../shift_slot_template_confirm_delete.html | 37 +++++
.../shifts/shift_template_detail.html | 8 ++
tapir/shifts/templatetags/shifts.py | 4 +-
.../test_delete_shift_slot_template_view.py | 130 ++++++++++++++++++
.../test_member_office_registers_other.py | 24 +++-
tapir/shifts/urls.py | 5 +
tapir/shifts/views/attendance.py | 1 +
tapir/shifts/views/management.py | 35 +++++
tapir/shifts/views/views.py | 2 +-
16 files changed, 346 insertions(+), 8 deletions(-)
create mode 100644 tapir/shifts/migrations/0081_shiftslottemplate_deleted.py
create mode 100644 tapir/shifts/templates/shifts/shift_slot_template_confirm_delete.html
create mode 100644 tapir/shifts/tests/test_delete_shift_slot_template_view.py
diff --git a/tapir/accounts/tests/test_tapir_user_self_update.py b/tapir/accounts/tests/test_tapir_user_self_update.py
index 5f0047905..0c057acae 100644
--- a/tapir/accounts/tests/test_tapir_user_self_update.py
+++ b/tapir/accounts/tests/test_tapir_user_self_update.py
@@ -1,3 +1,4 @@
+from django.conf import settings
from django.urls import reverse
from tapir.accounts.models import TapirUser
@@ -7,6 +8,9 @@
class TestTapirUserSelfUpdate(TapirFactoryTestBase):
def test_normal_user_can_self_update(self):
+ if settings.SHIFTS_ONLY:
+ self.skipTest("Can't edit users in shift only mode")
+
actor: TapirUser = TapirUserFactory.create()
response = self.try_update(actor=actor, target=actor)
@@ -17,6 +21,9 @@ def test_normal_user_can_self_update(self):
self.assertEqual("new pronouns", actor.pronouns)
def test_normal_user_cannot_edit_other_user(self):
+ if settings.SHIFTS_ONLY:
+ self.skipTest("Can't edit users in shift only mode")
+
target: TapirUser = TapirUserFactory.create()
actor: TapirUser = TapirUserFactory.create()
diff --git a/tapir/coop/tests/test_co_purchaser_updated_mail.py b/tapir/coop/tests/test_co_purchaser_updated_mail.py
index cba79f422..87ddf596f 100644
--- a/tapir/coop/tests/test_co_purchaser_updated_mail.py
+++ b/tapir/coop/tests/test_co_purchaser_updated_mail.py
@@ -1,3 +1,4 @@
+from django.conf import settings
from django.core import mail
from django.urls import reverse
@@ -8,6 +9,9 @@
class TestCoPurchaserUpdatedMail(TapirFactoryTestBase, TapirEmailTestMixin):
def test_setCoPurchaserToBlank_noEmailSent(self):
+ if settings.SHIFTS_ONLY:
+ self.skipTest("Can't edit users in shift only mode")
+
self.login_as_member_office_user()
tapir_user = TapirUserFactory.create(co_purchaser="A test co-purchaser")
@@ -31,6 +35,9 @@ def test_setCoPurchaserToBlank_noEmailSent(self):
self.assertEqual(0, len(mail.outbox))
def test_setCoPurchaserToNotBlank_emailSent(self):
+ if settings.SHIFTS_ONLY:
+ self.skipTest("Can't edit users in shift only mode")
+
self.login_as_member_office_user()
tapir_user = TapirUserFactory.create(co_purchaser="Old co-purchased")
co_purchaser_name = "A test co-purchaser"
diff --git a/tapir/coop/tests/test_create_general_account.py b/tapir/coop/tests/test_create_general_account.py
index 5d590d7db..be6d8e838 100644
--- a/tapir/coop/tests/test_create_general_account.py
+++ b/tapir/coop/tests/test_create_general_account.py
@@ -1,3 +1,4 @@
+from django.conf import settings
from django.urls import reverse
from tapir.accounts.models import TapirUser
@@ -27,6 +28,9 @@ def test_requires_permissions(self):
)
def test_create_general_account(self):
+ if settings.LOGIN_BACKEND_COOPS_PT:
+ self.skipTest("Can't create user with this login backend")
+
self.login_as_member_office_user()
mock_general_account = GeneralAccountFactory.build()
response = self.visit_view(mock_general_account)
diff --git a/tapir/shifts/forms.py b/tapir/shifts/forms.py
index ddd3a9352..10df14585 100644
--- a/tapir/shifts/forms.py
+++ b/tapir/shifts/forms.py
@@ -8,6 +8,7 @@
BooleanField,
)
from django.forms.widgets import HiddenInput
+from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django_select2.forms import Select2Widget
@@ -133,6 +134,61 @@ def clean(self):
return super().clean()
+class ShiftSlotTemplateDeleteForm(forms.ModelForm):
+ class Meta:
+ model = ShiftSlotTemplate
+ fields = []
+
+ confirm_understood = BooleanField(
+ label=_("I understand the consequences of deleting an ABCD shift slot"),
+ required=True,
+ )
+
+ def __init__(self, *args, **kwargs):
+ self.slot_template: ShiftSlotTemplate = kwargs.pop("slot_template")
+ super().__init__(*args, **kwargs)
+
+ def clean(self):
+ attendances_not_cancelled = ShiftAttendanceTemplate.objects.filter(
+ slot_template=self.slot_template
+ )
+ if attendances_not_cancelled.exists():
+ members = [
+ UserUtils.build_display_name(
+ attendance.user, UserUtils.DISPLAY_NAME_TYPE_FULL
+ )
+ for attendance in attendances_not_cancelled
+ ]
+ raise ValidationError(
+ _(
+ f"In order to delete an ABCD shift slot, no member must be registered to it. "
+ f"The following attendances must be removed: {", ".join(members)}"
+ )
+ )
+
+ future_slots = self.slot_template.generated_slots.filter(
+ shift__start_time__gt=timezone.now()
+ )
+ attendances_not_cancelled = ShiftAttendance.objects.filter(
+ slot__in=future_slots
+ ).exclude(state=ShiftAttendance.State.CANCELLED)
+ if attendances_not_cancelled.exists():
+ members = [
+ f"{UserUtils.build_display_name(
+ attendance.user, UserUtils.DISPLAY_NAME_TYPE_FULL
+ )} {attendance.slot.shift.start_time.strftime("%d.%m.%Y %H:%M")}"
+ for attendance in attendances_not_cancelled
+ ]
+ raise ValidationError(
+ _(
+ f"In order to delete an ABCD shift slot, all attendances on future slots must be cancelled. "
+ f"The following attendances must be cancelled: {", ".join(members)}"
+ )
+ )
+
+ return super().clean()
+
+
class ShiftSlotForm(forms.ModelForm):
class Meta:
model = ShiftSlot
@@ -329,6 +385,13 @@ def get_required_capabilities(self):
def get_shift_object(self) -> Shift | ShiftTemplate:
return self.slot_template.shift_template
+ def clean(self):
+ super().clean()
+ if self.slot_template.deleted:
+ raise ValidationError(
+ _("This ABCD slot is deleted, it is not possible to register to it.")
+ )
+
class RegisterUserToShiftSlotForm(
CustomTimeCleanMixin, MissingCapabilitiesWarningMixin, forms.Form
diff --git a/tapir/shifts/migrations/0081_shiftslottemplate_deleted.py b/tapir/shifts/migrations/0081_shiftslottemplate_deleted.py
new file mode 100644
index 000000000..debb76fab
--- /dev/null
+++ b/tapir/shifts/migrations/0081_shiftslottemplate_deleted.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.1.5 on 2025-09-26 13:37
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("shifts", "0080_shiftslot_deleted"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="shiftslottemplate",
+ name="deleted",
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/tapir/shifts/models.py b/tapir/shifts/models.py
index 6107f39b7..14540c65e 100644
--- a/tapir/shifts/models.py
+++ b/tapir/shifts/models.py
@@ -280,7 +280,7 @@ def create_shift(self, start_date: datetime.date) -> Shift:
generated_shift.save()
shift = generated_shift
- for slot_template in self.slot_templates.all().select_related(
+ for slot_template in self.slot_templates.filter(deleted=False).select_related(
"attendance_template"
):
slot = slot_template.create_slot_from_template(shift)
@@ -293,7 +293,7 @@ def update_future_shift_attendances(self, now=None):
slot_template.update_future_slot_attendances(now)
def add_slot_template_and_update_future_shifts(
- self, slot_name: str, required_capabilities: []
+ self, slot_name: str, required_capabilities: list
):
slot_template = ShiftSlotTemplate.objects.create(
name=slot_name,
@@ -349,6 +349,7 @@ class ShiftSlotTemplate(RequiredCapabilitiesMixin, models.Model):
blank=False,
on_delete=models.CASCADE,
)
+ deleted = models.BooleanField(default=False)
required_capabilities = models.ManyToManyField(ShiftUserCapability)
warnings = models.ManyToManyField(ShiftSlotWarning)
diff --git a/tapir/shifts/templates/shifts/shift_detail.html b/tapir/shifts/templates/shifts/shift_detail.html
index 6cb999ab0..2049505f2 100644
--- a/tapir/shifts/templates/shifts/shift_detail.html
+++ b/tapir/shifts/templates/shifts/shift_detail.html
@@ -351,8 +351,8 @@
Este é o plano de turnos ABCD. Ele repete-se a cada quatro semanas. É por isso que vê apenas os dias da semana (segunda-feira, terça-feira...) e a semana (A, B, C ou D), em vez de datas específicas (ex: 23.8.2021).
\n"
-"
Para ver a data do seu próximo turno, vá ao seu perfil. Se quiser alterar o seu turno ABCD regular, contacte o Escritório de Membros.
"
+"
Este é o plano de turnos ABCD. Ele repete-se a cada "
+"quatro semanas. É por isso que vês apenas os dias da semana (segunda-feira, "
+"terça-feira...) e a semana (A, B, C ou D), em vez de datas específicas (ex: "
+"23.8.2021).
\n"
+"
Para veres a data do teu próximo turno, vai ao seu perfil. Se quiseres "
+"alterar o teu turno ABCD regular, contacta o GTT-membros.
+
+{% endblock content %}
\ No newline at end of file
diff --git a/rizoma/templates/shifts/shift_template_overview.html b/rizoma/templates/shifts/shift_template_overview.html
new file mode 100644
index 000000000..5d59d9602
--- /dev/null
+++ b/rizoma/templates/shifts/shift_template_overview.html
@@ -0,0 +1,79 @@
+{% extends "shifts/base.html" %}
+{% load django_bootstrap5 %}
+{% load static %}
+{% load shifts %}
+{% load i18n %}
+{% block head %}
+ {{ block.super }}
+
+
+{% endblock head %}
+{% block title %}
+ {% translate 'ABCD week-plan' %}
+{% endblock title %}
+{% block content %}
+
+
+
+ {% get_current_week_group as group %}
+ {% translate "ABCD schedule overview, current week: " %}{{ group.name }}
+
+
+ {% blocktranslate %}
+
This is the ABCD shiftplan. It repeats every four weeks. That's why you see only weekdays
+ (Monday, Tuesday...) and the week (A,B,C or D), instead of specific dates (e.g. 23.8.2021).
+ To see the date of your next shift, please go to your profile.
+
If you would like to change your regular ABCD shift, please contact the Member Office.
+ {% endblocktranslate %}
+
+
+ {% shift_filters %}
+
+
+
+
+
+
Time
+ {% for group in shift_template_groups %}
+
{% translate 'Week ' %}{{ group }}
+ {% endfor %}
+
+
+
+ {% for day, day_group in day_groups.items %}
+
+
+
+
{{ day }}
+
+
+ {% for time, time_group in day_group.items %}
+
+
{{ time }}
+ {% for template_group_group in time_group.values %}
+
{% translate "This is the list of all emails that can get sent by Tapir." %}
+
+ {% translate 'The previews in the "Subject" and "Body" columns are fake : they show what the email would look like if it was sent to a random user, using random data.' %}
+
+
+ {% translate 'The files in the "Last sent example" column are real : they have been sent to a user exactly as shown. They can therefore contain private information.' %}
+
+{% endblock content %}
diff --git a/tapir/accounts/apps.py b/tapir/accounts/apps.py
index 98920ceff..93c81b595 100644
--- a/tapir/accounts/apps.py
+++ b/tapir/accounts/apps.py
@@ -1,4 +1,5 @@
from django.apps import AppConfig
+from django.conf import settings
class AccountsConfig(AppConfig):
@@ -9,9 +10,10 @@ def ready(self):
@staticmethod
def register_emails():
- from tapir.core.tapir_email_builder_base import TapirEmailBuilderBase
- from tapir.accounts.emails.create_account_reminder_email import (
- CreateAccountReminderEmailBuilder,
- )
+ if not settings.SHIFTS_ONLY:
+ from tapir.core.tapir_email_builder_base import TapirEmailBuilderBase
+ from tapir.accounts.emails.create_account_reminder_email import (
+ CreateAccountReminderEmailBuilder,
+ )
- TapirEmailBuilderBase.register_email(CreateAccountReminderEmailBuilder)
+ TapirEmailBuilderBase.register_email(CreateAccountReminderEmailBuilder)
diff --git a/tapir/accounts/views.py b/tapir/accounts/views.py
index 5ac9b90bc..7bea7e2a5 100644
--- a/tapir/accounts/views.py
+++ b/tapir/accounts/views.py
@@ -410,12 +410,12 @@ def get_context_data(self, **kwargs):
context_data = super().get_context_data(**kwargs)
context_data["page_title"] = _("Notification settings for %(name)s") % {
"name": UserUtils.build_display_name_for_viewer(
- self.get_tapir_user().share_owner, self.request.user
+ self.get_tapir_user(), self.request.user
)
}
context_data["card_title"] = _("Notification settings for %(name)s") % {
"name": UserUtils.build_display_name_for_viewer(
- self.get_tapir_user().share_owner, self.request.user
+ self.get_tapir_user(), self.request.user
)
}
return context_data
diff --git a/tapir/coop/apps.py b/tapir/coop/apps.py
index 88c7cc34d..c5ae32c14 100644
--- a/tapir/coop/apps.py
+++ b/tapir/coop/apps.py
@@ -61,6 +61,9 @@ def register_sidebar_link_groups():
@staticmethod
def register_emails():
+ if settings.SHIFTS_ONLY:
+ return
+
from tapir.core.tapir_email_builder_base import TapirEmailBuilderBase
from tapir.coop.emails.extra_shares_confirmation_email import (
ExtraSharesConfirmationEmailBuilder,
diff --git a/tapir/shifts/apps.py b/tapir/shifts/apps.py
index 8c2349ec1..edb574ade 100644
--- a/tapir/shifts/apps.py
+++ b/tapir/shifts/apps.py
@@ -95,6 +95,9 @@ def register_emails():
from tapir.shifts.emails.shift_reminder_email import (
ShiftReminderEmailBuilder,
)
+ from tapir.shifts.emails.second_shift_reminder_email import (
+ SecondShiftReminderEmailBuilder,
+ )
from tapir.shifts.emails.stand_in_found_email import (
StandInFoundEmailBuilder,
)
@@ -113,6 +116,7 @@ def register_emails():
TapirEmailBuilderBase.register_email(ShiftMissedEmailBuilder)
TapirEmailBuilderBase.register_email(ShiftReminderEmailBuilder)
+ TapirEmailBuilderBase.register_email(SecondShiftReminderEmailBuilder)
TapirEmailBuilderBase.register_email(StandInFoundEmailBuilder)
TapirEmailBuilderBase.register_email(MemberFrozenEmailBuilder)
TapirEmailBuilderBase.register_email(FreezeWarningEmailBuilder)
diff --git a/tapir/shifts/config.py b/tapir/shifts/config.py
index f1ff24591..8ecc93913 100644
--- a/tapir/shifts/config.py
+++ b/tapir/shifts/config.py
@@ -13,6 +13,11 @@
cycle_start_dates.sort()
REMINDER_EMAIL_DAYS_BEFORE_SHIFT = 9
+SECOND_REMINDER_EMAIL_DAYS_BEFORE_SHIFT = None
+if settings.ENABLE_RIZOMA_CONTENT:
+ REMINDER_EMAIL_DAYS_BEFORE_SHIFT = 7
+ SECOND_REMINDER_EMAIL_DAYS_BEFORE_SHIFT = 2
+
FLYING_MEMBERS_REGISTRATION_REMINDER_DAYS_AFTER_CYCLE_START = 7
FREEZE_THRESHOLD = -4
diff --git a/tapir/shifts/emails/second_shift_reminder_email.py b/tapir/shifts/emails/second_shift_reminder_email.py
new file mode 100644
index 000000000..9146f9ddf
--- /dev/null
+++ b/tapir/shifts/emails/second_shift_reminder_email.py
@@ -0,0 +1,60 @@
+from typing import List
+
+from django.utils.translation import gettext_lazy as _
+
+from tapir.coop.models import ShareOwner
+from tapir.core.mail_option import MailOption
+from tapir.core.tapir_email_builder_base import TapirEmailBuilderBase
+from tapir.shifts import config
+from tapir.shifts.models import Shift
+
+
+class SecondShiftReminderEmailBuilder(TapirEmailBuilderBase):
+ option = MailOption.OPTIONAL_ENABLED
+
+ def __init__(self, shift):
+ super().__init__()
+ self.shift = shift
+
+ @classmethod
+ def get_unique_id(cls) -> str:
+ return "tapir.shifts.shift_reminder_2"
+
+ @classmethod
+ def get_name(cls) -> str:
+ return _("Second shift reminder")
+
+ @classmethod
+ def get_description(cls) -> str:
+ return _(
+ f"Sent to a member {config.SECOND_REMINDER_EMAIL_DAYS_BEFORE_SHIFT} days before their shift"
+ )
+
+ def get_subject_templates(self) -> List:
+ return [
+ "shifts/email/shift_reminder.subject.html",
+ ]
+
+ def get_body_templates(self) -> List:
+ return [
+ "shifts/email/shift_reminder.body.html",
+ ]
+
+ def get_extra_context(self) -> dict:
+ return {"shift": self.shift}
+
+ @classmethod
+ def get_dummy_version(cls) -> TapirEmailBuilderBase | None:
+ share_owner = (
+ ShareOwner.objects.filter(user__isnull=False).order_by("?").first()
+ )
+ shift = Shift.objects.order_by("?").first()
+ if not share_owner or not shift:
+ return None
+ mail = cls(shift=shift)
+ mail.get_full_context(
+ share_owner=share_owner,
+ member_infos=share_owner.get_info(),
+ tapir_user=share_owner.user,
+ )
+ return mail
diff --git a/tapir/shifts/management/commands/send_shift_reminders.py b/tapir/shifts/management/commands/send_shift_reminders.py
index 339ab7849..55ea3f6fd 100644
--- a/tapir/shifts/management/commands/send_shift_reminders.py
+++ b/tapir/shifts/management/commands/send_shift_reminders.py
@@ -7,6 +7,9 @@
from tapir.core.services.send_mail_service import SendMailService
from tapir.shifts import config
+from tapir.shifts.emails.second_shift_reminder_email import (
+ SecondShiftReminderEmailBuilder,
+)
from tapir.shifts.emails.shift_reminder_email import ShiftReminderEmailBuilder
from tapir.shifts.models import ShiftUserData, ShiftAttendance
@@ -16,6 +19,8 @@ class Command(BaseCommand):
def handle(self, *args, **options):
for shift_user_data in ShiftUserData.objects.all():
+ if config.SECOND_REMINDER_EMAIL_DAYS_BEFORE_SHIFT is not None:
+ self.send_second_shift_reminder_for_user(shift_user_data)
self.send_shift_reminder_for_user(shift_user_data)
@staticmethod
@@ -26,6 +31,7 @@ def send_shift_reminder_for_user(shift_user_data: ShiftUserData):
slot__shift__start_time__lte=timezone.now()
+ datetime.timedelta(days=config.REMINDER_EMAIL_DAYS_BEFORE_SHIFT),
reminder_email_sent=False,
+ second_reminder_email_sent=False,
):
with transaction.atomic():
email_builder = ShiftReminderEmailBuilder(shift=attendance.slot.shift)
@@ -36,3 +42,24 @@ def send_shift_reminder_for_user(shift_user_data: ShiftUserData):
attendance.reminder_email_sent = True
attendance.save()
time.sleep(0.1)
+
+ @staticmethod
+ def send_second_shift_reminder_for_user(shift_user_data: ShiftUserData):
+ for attendance in ShiftAttendance.objects.with_valid_state().filter(
+ user=shift_user_data.user,
+ slot__shift__start_time__gte=timezone.now(),
+ slot__shift__start_time__lte=timezone.now()
+ + datetime.timedelta(days=config.SECOND_REMINDER_EMAIL_DAYS_BEFORE_SHIFT),
+ second_reminder_email_sent=False,
+ ):
+ with transaction.atomic():
+ email_builder = SecondShiftReminderEmailBuilder(
+ shift=attendance.slot.shift
+ )
+ SendMailService.send_to_tapir_user(
+ actor=None, recipient=attendance.user, email_builder=email_builder
+ )
+
+ attendance.second_reminder_email_sent = True
+ attendance.save()
+ time.sleep(0.1)
diff --git a/tapir/shifts/migrations/0083_shiftattendance_second_reminder_email_sent.py b/tapir/shifts/migrations/0083_shiftattendance_second_reminder_email_sent.py
new file mode 100644
index 000000000..ea542715b
--- /dev/null
+++ b/tapir/shifts/migrations/0083_shiftattendance_second_reminder_email_sent.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.1.5 on 2025-10-02 14:59
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("shifts", "0082_shifttemplate_deleted"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="shiftattendance",
+ name="second_reminder_email_sent",
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/tapir/shifts/models.py b/tapir/shifts/models.py
index 74048caf0..e455ffadb 100644
--- a/tapir/shifts/models.py
+++ b/tapir/shifts/models.py
@@ -845,6 +845,7 @@ def with_valid_state(self):
ShiftSlot, related_name="attendances", on_delete=models.CASCADE
)
reminder_email_sent = models.BooleanField(default=False)
+ second_reminder_email_sent = models.BooleanField(default=False)
is_solidarity = models.BooleanField(default=False)
custom_time = models.TimeField(
blank=False,
diff --git a/tapir/shifts/tests/test_shift_reminder.py b/tapir/shifts/tests/test_shift_reminder.py
index 9b28b80a3..16ec50cbd 100644
--- a/tapir/shifts/tests/test_shift_reminder.py
+++ b/tapir/shifts/tests/test_shift_reminder.py
@@ -1,12 +1,16 @@
import datetime
+from django.conf import settings
from django.core import mail
+from django.core.management import call_command
from django.utils import timezone
from tapir.accounts.models import TapirUser
from tapir.accounts.tests.factories.factories import TapirUserFactory
+from tapir.shifts.emails.second_shift_reminder_email import (
+ SecondShiftReminderEmailBuilder,
+)
from tapir.shifts.emails.shift_reminder_email import ShiftReminderEmailBuilder
-from tapir.shifts.management.commands.send_shift_reminders import Command
from tapir.shifts.models import ShiftAttendance, Shift
from tapir.shifts.tests.factories import ShiftFactory
from tapir.utils.tests_utils import TapirFactoryTestBase, TapirEmailTestMixin
@@ -24,7 +28,7 @@ def test_shift_in_the_past_does_not_trigger_reminder(self):
user=user, slot=shift_in_the_past.slots.first(), reminder_email_sent=False
)
- Command.send_shift_reminder_for_user(user.shift_user_data)
+ call_command("send_shift_reminders")
self.assertEqual(
0,
@@ -43,7 +47,7 @@ def test_shift_too_far_in_the_future_does_not_trigger_reminder(self):
reminder_email_sent=False,
)
- Command.send_shift_reminder_for_user(user.shift_user_data)
+ call_command("send_shift_reminders")
self.assertEqual(
0,
@@ -64,7 +68,7 @@ def test_shift_in_the_near_future_triggers_a_reminder(self):
reminder_email_sent=False,
)
- Command.send_shift_reminder_for_user(user.shift_user_data)
+ call_command("send_shift_reminders")
self.assertEqual(
1,
@@ -76,3 +80,84 @@ def test_shift_in_the_near_future_triggers_a_reminder(self):
)
attendance.refresh_from_db()
self.assertTrue(attendance.reminder_email_sent)
+
+ def test_shift_in_the_very_near_future_triggers_a_second_reminder(self):
+ if not settings.ENABLE_RIZOMA_CONTENT:
+ self.skipTest("This test is only relevant for rizoma")
+
+ user: TapirUser = TapirUserFactory.create(email=self.USER_EMAIL_ADDRESS)
+ shift_in_the_near_future: Shift = ShiftFactory.create(
+ start_time=timezone.now() + datetime.timedelta(days=1)
+ )
+ attendance = ShiftAttendance.objects.create(
+ user=user,
+ slot=shift_in_the_near_future.slots.first(),
+ reminder_email_sent=True,
+ second_reminder_email_sent=False,
+ )
+
+ call_command("send_shift_reminders")
+
+ self.assertEqual(
+ 1,
+ len(mail.outbox),
+ "A shift that is in the new future should get a reminder.",
+ )
+ self.assertEmailOfClass_GotSentTo(
+ SecondShiftReminderEmailBuilder, self.USER_EMAIL_ADDRESS, mail.outbox[0]
+ )
+ attendance.refresh_from_db()
+ self.assertTrue(attendance.second_reminder_email_sent)
+
+ def test_if_rizoma_content_is_disabled_second_reminders_should_not_be_sent(self):
+ if settings.ENABLE_RIZOMA_CONTENT:
+ self.skipTest("This test is only relevant for supercoop")
+
+ user: TapirUser = TapirUserFactory.create(email=self.USER_EMAIL_ADDRESS)
+ shift_in_the_near_future: Shift = ShiftFactory.create(
+ start_time=timezone.now() + datetime.timedelta(days=1)
+ )
+ attendance = ShiftAttendance.objects.create(
+ user=user,
+ slot=shift_in_the_near_future.slots.first(),
+ reminder_email_sent=True,
+ second_reminder_email_sent=False,
+ )
+
+ call_command("send_shift_reminders")
+
+ self.assertEqual(
+ 0,
+ len(mail.outbox),
+ "A shift that is in the new future should get a reminder.",
+ )
+ self.assertFalse(attendance.second_reminder_email_sent)
+
+ def test_if_both_reminders_could_be_send_only_one_should_be_sent(self):
+ if not settings.ENABLE_RIZOMA_CONTENT:
+ self.skipTest("This test is only relevant for rizoma")
+
+ user: TapirUser = TapirUserFactory.create(email=self.USER_EMAIL_ADDRESS)
+ shift_in_the_near_future: Shift = ShiftFactory.create(
+ start_time=timezone.now() + datetime.timedelta(days=1)
+ )
+ attendance = ShiftAttendance.objects.create(
+ user=user,
+ slot=shift_in_the_near_future.slots.first(),
+ reminder_email_sent=False,
+ second_reminder_email_sent=False,
+ )
+
+ call_command("send_shift_reminders")
+ call_command("send_shift_reminders")
+
+ self.assertEqual(
+ 1,
+ len(mail.outbox),
+ "A shift that is in the new future should get a reminder.",
+ )
+ self.assertEmailOfClass_GotSentTo(
+ SecondShiftReminderEmailBuilder, self.USER_EMAIL_ADDRESS, mail.outbox[0]
+ )
+ attendance.refresh_from_db()
+ self.assertTrue(attendance.second_reminder_email_sent)
From c07900c3ee227506ed4ff37c0be32700ef858fcb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Th=C3=A9ophile=20Madet?=
Date: Thu, 2 Oct 2025 17:18:07 +0200
Subject: [PATCH 17/27] misc: removed file that should not be in git
---
shifts_rizoma.csv | 275 ----------------------------------------------
1 file changed, 275 deletions(-)
delete mode 100644 shifts_rizoma.csv
diff --git a/shifts_rizoma.csv b/shifts_rizoma.csv
deleted file mode 100644
index e5f122380..000000000
--- a/shifts_rizoma.csv
+++ /dev/null
@@ -1,275 +0,0 @@
-SHIFT,Type,Day,Week,Start time,Shift duration,End time,Tipo,Cooperantes,Membros assigned,Membros Max,Availability,Turnos perdidos,Start date,Shift cycle (in days),Next shift
-Organização / ,Organização,,,#ERROR!,,#ERROR!,,"Jessica Roberts,Filipe Ruão Marques Teixeira,Maria Teresa Seixas Valério,Xavier Lacey,Chiara Maria Grilli,Diana Soeiro Maia Vicente",6,20,14 AVAILABLE,,,,#ERROR!
-#ERROR!,,,,#ERROR!,,#ERROR!,,,0,,AVAILABLE,,,,#ERROR!
-Quero ser dispensado/a,Excepção,Flex,,#ERROR!,,#ERROR!,,,0,30,30 AVAILABLE,,,,#ERROR!
-A - Segunda 11:45-14:45 / Caixa,Regular,Segunda,A,11:45,3,14:45,Caixa,,0,1,1 AVAILABLE,,1/5/2023 11:45,28,13/10/2025 11:45
-A - Segunda 11:45-14:45 / Rizobar,Regular,Segunda,A,11:45,3,14:45,Rizobar,André Fontelonga de Lemos,1,1,FULL,,1/5/2023 11:45,28,13/10/2025 11:45
-A - Segunda 11:45-14:45 / Mercearia,Regular,Segunda,A,11:45,3,14:45,Mercearia,,0,1,1 AVAILABLE,,1/5/2023 11:45,28,13/10/2025 11:45
-A - Terça 11:45-14:45 / Caixa,Regular,Terça,A,11:45,3,14:45,Caixa,Miguel José Dias Baião dos Santos Martinho,1,1,FULL,21/1/2022,19/10/2021 11:45,28,16/9/2025 11:45
-A - Terça 11:45-14:45 / Rizobar,Regular,Terça,A,11:45,3,14:45,Rizobar,"Jonathan Howell,Carlotta Defenu",2,2,FULL,,31/5/2022 11:45,,31/5/2022 11:45
-A - Terça 11:45-14:45 / Mercearia,Regular,Terça,A,11:45,3,14:45,Mercearia,Monét Davis,1,2,1 AVAILABLE,5/4/2022,19/10/2021 11:45,28,16/9/2025 11:45
-A - Terça 14:30-17:30 / Caixa,Regular,Terça,A,14:30,3,17:30,Caixa,Tina Magazzini,1,1,FULL,,19/10/2021 14:30,28,16/9/2025 14:30
-A - Terça 14:30-17:30 / Rizobar,Regular,Terça,A,14:30,3,17:30,Rizobar,,0,1,1 AVAILABLE,3/5/2022,19/10/2021 14:30,28,16/9/2025 14:30
-A - Terça 14:30-17:30 / Mercearia,Regular,Terça,A,14:30,3,17:30,Mercearia,"Rúben Filipe Botelho Cordeiro,Emanuel André Pais Saramago",2,2,FULL,,31/5/2022 14:30,28,16/9/2025 14:30
-A - Terça 17:15-20:15 / Caixa,Regular,Terça,A,17:15,3,20:15,Caixa,Catarina Inês Rosa Valencia,1,1,FULL,,19/10/2021 17:15,28,16/9/2025 17:15
-A - Terça 17:15-20:15 / Rizobar,Regular,Terça,A,17:15,3,20:15,Rizobar,Marco Carpentieri,1,1,FULL,10/21/2021,19/10/2021 17:15,28,16/9/2025 17:15
-A - Terça 17:15-20:15 / Mercearia,Regular,Terça,A,17:15,3,20:15,Mercearia,"José Manuel Ferreira Xavier Beirão,Bernini Tiphaine",2,2,FULL,,31/5/2022 17:15,28,16/9/2025 17:15
-A - Terça 17:15-20:15 / Rizochef,Regular,Terça,A,17:15,3,20:15,Rizochef,"Benjamin Kampmann,Pedro Afonso Dias da Silva Fonseca Lopes",2,2,FULL,,19/10/2021 17:15,28,16/9/2025 17:15
-A - Quarta 11:45-14:45 / Caixa,Regular,Quarta,A,11:45,3,14:45,Caixa,Benjamin Tirone Nunes,1,1,FULL,,7/4/2021 11:45,28,17/9/2025 11:45
-A - Quarta 11:45-14:45 / Rizobar,Regular,Quarta,A,11:45,3,14:45,Rizobar,Mari Galicer,1,2,1 AVAILABLE,"5/5/2021, 8/25/2021, 11/17/2021, 1/12/2022, 6/4/2022",7/4/2021 11:45,28,17/9/2025 11:45
-A - Quarta 11:45-14:45 / Mercearia,Regular,Quarta,A,11:45,3,14:45,Mercearia,"Sílvia Maria Valadas Soares,Lisa Adam ",2,2,FULL,"5/5/2021, 8/25/2021, 11/17/2021, 1/12/2022, 6/4/2022",7/4/2021 11:45,28,17/9/2025 11:45
-A - Quarta 14:30-17:30 / Caixa,Regular,Quarta,A,14:30,3,17:30,Caixa,João Carlos de Carvalho Salgado Pereira Bento,1,1,FULL,,7/4/2021 14:30,28,17/9/2025 14:30
-A - Quarta 14:30-17:30 / Rizobar,Regular,Quarta,A,14:30,3,17:30,Rizobar,,0,1,1 AVAILABLE,"30/6/2021, 28/7/2021, 22/9/2021, 15/12/2021, 9/2/2022, 9/2/2022, 6/4/2022, 6/4/2022",7/4/2021 14:30,28,17/9/2025 14:30
-A - Quarta 14:30-17:30 / Mercearia,Regular,Quarta,A,14:30,3,17:30,Mercearia,Gerusa Roberta Martinelli de Almeida ,1,1,FULL,,1/6/2022 14:30,28,17/9/2025 14:30
-A - Quarta 17:15-20:15 / Caixa,Regular,Quarta,A,17:15,3,20:15,Caixa,"Laura Diaz Sufuentes,Inês Correia",2,2,FULL,1/12/2022,2/6/2021 17:15,28,17/9/2025 17:15
-A - Quarta 17:15-20:15 / Rizobar,Regular,Quarta,A,17:15,3,20:15,Rizobar,Martina Yael Altalef,1,1,FULL,12/1/2022,2/6/2021 17:15,28,17/9/2025 17:15
-A - Quarta 17:15-20:15 / Mercearia,Regular,Quarta,A,17:15,3,20:15,Mercearia,"Catarina Martins Ribeiro,Carolina Filipa Fernandes Gonçalves ",2,2,FULL,12/1/2022,2/6/2021 17:15,28,17/9/2025 17:15
-A - Quarta 17:15-20:15 / Rizochef,Regular,Quarta,A,17:15,3,20:15,Rizochef,"Helena Loução ,Adi Dagan",2,2,FULL,,20/10/2021 17:15,28,17/9/2025 17:15
-A - Quinta 11:45-14:45 / Caixa,Regular,Quinta,A,11:45,3,14:45,Caixa,,0,1,1 AVAILABLE,"9/23/2021, 18/11/2021, 1/13/2022",11/2/2021 11:45,28,18/9/2025 11:45
-A - Quinta 11:45-14:45 / Rizobar,Regular,Quinta,A,11:45,3,14:45,Rizobar,Vincent Richeux,1,2,1 AVAILABLE,,2/6/2022 11:45,28,18/9/2025 11:45
-A - Quinta 11:45-14:45 / Mercearia,Regular,Quinta,A,11:45,3,14:45,Mercearia,"Henrique Rodrigues de Freitas ,Carol Getaz",2,2,FULL,7/29/2021,11/2/2021 11:45,28,18/9/2025 11:45
-A - Quinta 14:30-17:30 / Caixa,Regular,Quinta,A,14:30,3,17:30,Caixa,,0,1,1 AVAILABLE,7/4/2022,11/2/2021 14:30,28,18/9/2025 14:30
-A - Quinta 14:30-17:30 / Rizobar,Regular,Quinta,A,14:30,3,17:30,Rizobar,"Tania Barberán Soler,Maria Leonor Rodrigues Rolo Duarte",2,2,FULL,10/2/2022,11/2/2021 14:30,28,18/9/2025 14:30
-A - Quinta 14:30-17:30 / Mercearia,Regular,Quinta,A,14:30,3,17:30,Mercearia,"Maxi Kammbach,Teresa Rox",2,2,FULL,10/2/2022,11/2/2021 14:30,28,18/9/2025 14:30
-A - Quinta 17:15-20:15 / Caixa,Regular,Quinta,A,17:15,3,20:15,Caixa,"Vera Martins Peres de Almeida,Manuel António Banza Rodrigues",2,2,FULL,,6/5/2021 17:15,28,18/9/2025 17:15
-A - Quinta 17:15-20:15 / Rizobar,Regular,Quinta,A,17:15,3,20:15,Rizobar,"Andreia Gonçalves de Sousa,Saskia Hadley ",2,2,FULL,,2/6/2022 17:15,28,18/9/2025 17:15
-A - Quinta 17:15-20:15 / Mercearia,Regular,Quinta,A,17:15,3,20:15,Mercearia,,0,1,1 AVAILABLE,,6/5/2021 17:15,28,18/9/2025 17:15
-A - Quinta 17:15-20:15 / Rizochef,Regular,Quinta,A,17:15,3,20:15,Rizochef,,0,2,2 AVAILABLE,,19/10/2021 17:15,28,16/9/2025 17:15
-A - Quinta 20:00-23:00 / Caixa,Regular,Quinta,A,20:00,3,23:00,Caixa,Luís João Ferreira Vieira,1,1,FULL,,4/5/2023 20:00,28,18/9/2025 20:00
-A - Quinta 20:00-23:00 / Rizobar,Regular,Quinta,A,20:00,3,23:00,Rizobar,Marta Nieto Romero,1,1,FULL,,4/5/2023 20:00,28,18/9/2025 20:00
-A - Quinta 20:00-23:00 / Mercearia,Regular,Quinta,A,20:00,3,23:00,Mercearia,Joana Pinto Simões Figueira Canada,1,1,FULL,,4/5/2023 20:00,28,18/9/2025 20:00
-A - Sexta 11:45-14:45 / Caixa,Regular,Sexta,A,11:45,3,14:45,Caixa,"Laura Gonçalves ,Andreia Barbosa",2,2,FULL,,22/10/2021 11:45,28,19/9/2025 11:45
-A - Sexta 11:45-14:45 / Rizobar,Regular,Sexta,A,11:45,3,14:45,Rizobar,"Zara Quiroga e Gama de Oliveira,João Pedro Soares Fernandes Dias De Brangança Farinha",2,2,FULL,,3/6/2022 11:45,28,19/9/2025 11:45
-A - Sexta 11:45-14:45 / Mercearia,Regular,Sexta,A,11:45,3,14:45,Mercearia,"Carolin Weisser,Aviv Adashi",2,2,FULL,19/11/2021,22/10/2021 11:45,28,19/9/2025 11:45
-A - Sexta 14:30-17:30 / Caixa,Regular,Sexta,A,14:30,3,17:30,Caixa,Astrid Hardeel,1,1,FULL,,12/2/2021 14:30,28,19/9/2025 14:30
-A - Sexta 14:30-17:30 / Rizobar,Regular,Sexta,A,14:30,3,17:30,Rizobar,Teresa Gaspar Mendes Fernandes,1,1,FULL,"17/12/2021, 8/4/2022",12/2/2021 14:30,28,19/9/2025 14:30
-A - Sexta 14:30-17:30 / Mercearia,Regular,Sexta,A,14:30,3,17:30,Mercearia,"Audrey Bideau,Rachel Piner",2,2,FULL,"17/12/2021, 8/4/2022",12/2/2021 14:30,28,19/9/2025 14:30
-A - Sexta 17:15-20:15 / Caixa,Regular,Sexta,A,17:15,3,20:15,Caixa,Claire Harvey,1,1,FULL,,12/2/2021 17:15,28,19/9/2025 17:15
-A - Sexta 17:15-20:15 / Rizobar,Regular,Sexta,A,17:15,3,20:15,Rizobar,"Judit Morello Bullón,Joana Portugal Pereira",2,2,FULL,,3/6/2022 17:15,28,19/9/2025 17:15
-A - Sexta 17:15-20:15 / Mercearia,Regular,Sexta,A,17:15,3,20:15,Mercearia,"Monica Giromini,Sara Dias Faustino Pinho",2,2,FULL,"4/2/2021, 8/27/2021, 8/27/2021, 11/19/2021, 17/12/2021",12/2/2021 17:15,28,19/9/2025 17:15
-A - Sexta 17:15-20:15 / Rizochef,Regular,Sexta,A,17:15,3,20:15,Rizochef,Cyrielle Marchetti,1,2,1 AVAILABLE,,20/10/2023 17:15,,20/10/2023 17:15
-A - Sexta 20:00-23:00 / Caixa,Regular,Sexta,A,20:00,3,23:00,Caixa,"Emilio Fernando Martins e Martin da Fonseca,Gustavo Manuel Lourenço Amaro",2,2,FULL,,22/10/2021 20:00,28,19/9/2025 20:00
-A - Sexta 20:00-23:00 / Rizobar,Regular,Sexta,A,20:00,3,23:00,Rizobar,,0,1,1 AVAILABLE,,22/10/2021 20:00,28,19/9/2025 20:00
-A - Sexta 20:00-23:00 / Mercearia,Regular,Sexta,A,20:00,3,23:00,Mercearia,Maria Cristina Frazão Pissarra Gouveia,1,1,FULL,,22/10/2021 20:00,28,19/9/2025 20:00
-A - Sabado 11:45-14:45 / Caixa,Regular,Sabado,A,11:45,3,14:45,Caixa,Nuno Óscar Gomes Fernandes,1,1,FULL,,13/2/2021 11:45,28,20/9/2025 11:45
-A - Sabado 11:45-14:45 / Rizobar,Regular,Sabado,A,11:45,3,14:45,Rizobar,"Dora Sofia Neves Ribeiro,Maria Teresa Pinto do Amaral Coelho de Mendonça",2,2,FULL,"31/7/2021, 25/9/2021, 15/1/2022, 12/2/2022, 12/2/2022",13/2/2021 11:45,28,20/9/2025 11:45
-A - Sabado 11:45-14:45 / Mercearia,Regular,Sabado,A,11:45,3,14:45,Mercearia,Emília do Nascimento Dias Ribeiro,1,1,FULL,"31/7/2021, 25/9/2021, 15/1/2022, 12/2/2022, 12/2/2022",13/2/2021 11:45,28,20/9/2025 11:45
-A - Sabado 14:30-17:30 / Caixa,Regular,Sabado,A,14:30,3,17:30,Caixa,,0,1,1 AVAILABLE,"23/10/2021, 18/12/2021",10/4/2021 14:30,28,20/9/2025 14:30
-A - Sabado 14:30-17:30 / Rizobar,Regular,Sabado,A,14:30,3,17:30,Rizobar,,0,1,1 AVAILABLE,,4/6/2022 14:30,28,20/9/2025 14:30
-A - Sabado 14:30-17:30 / Mercearia,Regular,Sabado,A,14:30,3,17:30,Mercearia,,0,1,1 AVAILABLE,"6/5/2021, 7/31/2021, 8/28/2021",10/4/2021 14:30,28,20/9/2025 14:30
-A - Sabado 17:15-20:15 / Caixa,Regular,Sabado,A,17:15,3,20:15,Caixa,Laura Vanessa de Freitas da Silva,1,1,FULL,,6/5/2023 17:15,28,20/9/2025 17:15
-A - Sabado 17:15-20:15 / Rizobar,Regular,Sabado,A,17:15,3,20:15,Rizobar,,0,1,1 AVAILABLE,,6/5/2023 17:15,28,20/9/2025 17:15
-A - Sabado 17:15-20:15 / Mercearia,Regular,Sabado,A,17:15,3,20:15,Mercearia,Mara Casimiro Ribeiro,1,1,FULL,,6/5/2023 17:15,28,20/9/2025 17:15
-A - Domingo 11:45-14:45 / Caixa,Regular,Domingo,A,11:45,3,14:45,Caixa,Margarida Maria Valadas Soares,1,1,FULL,4/7/2021,14/3/2021 11:45,28,21/9/2025 11:45
-A - Domingo 11:45-14:45 / Rizobar,Regular,Domingo,A,11:45,3,14:45,Rizobar,Beatriz Berger,1,1,FULL,"16/1/2022, 16/1/2022",14/3/2021 11:45,28,21/9/2025 11:45
-A - Domingo 11:45-14:45 / Mercearia,Regular,Domingo,A,11:45,3,14:45,Mercearia,Catherine Savage,1,1,FULL,"16/1/2022, 16/1/2022",14/3/2021 11:45,28,21/9/2025 11:45
-A - Domingo 14:30-17:30 / Caixa,Regular,Domingo,A,14:30,3,17:30,Caixa,Renata Gonçalves,1,1,FULL,"5/9/2021, 9/25/2021, 16/1/2022",14/3/2021 14:30,28,21/9/2025 14:30
-A - Domingo 14:30-17:30 / Rizobar,Regular,Domingo,A,14:30,3,17:30,Rizobar,Ben Norskov,1,1,FULL,,5/6/2022 14:30,28,21/9/2025 14:30
-A - Domingo 14:30-17:30 / Mercearia,Regular,Domingo,A,14:30,3,17:30,Mercearia,,0,1,1 AVAILABLE,"10/24/2021, 19/12/2021, 16/1/2022, 13/2/2022, 10/4/2022",14/3/2021 14:30,28,21/9/2025 14:30
-A - Domingo 17:15-20:15 / Caixa,Regular,Domingo,A,17:15,3,20:15,Caixa,Francisco Lisboa Outeiro,1,1,FULL,,7/5/2023 17:15,28,21/9/2025 17:15
-A - Domingo 17:15-20:15 / Rizobar,Regular,Domingo,A,17:15,3,20:15,Rizobar,,0,1,1 AVAILABLE,,7/5/2023 17:15,28,21/9/2025 17:15
-A - Domingo 17:15-20:15 / Mercearia,Regular,Domingo,A,17:15,3,20:15,Mercearia,Carolina Custódio Fernandes Miguel,1,1,FULL,,7/5/2023 17:15,28,21/9/2025 17:15
-B - Segunda 11:45-14:45 / Caixa,Regular,Segunda,B,11:45,3,14:45,Caixa,,0,1,1 AVAILABLE,,8/5/2023 11:45,28,22/9/2025 11:45
-B - Segunda 11:45-14:45 / Rizobar,Regular,Segunda,B,11:45,3,14:45,Rizobar,,0,1,1 AVAILABLE,,8/5/2023 11:45,28,22/9/2025 11:45
-B - Segunda 11:45-14:45 / Mercearia,Regular,Segunda,B,11:45,3,14:45,Mercearia,Beatriz Martín-Barbadillo,1,1,FULL,,8/5/2023 11:45,28,22/9/2025 11:45
-B - Terça 11:45-14:45 / Caixa,Regular,Terça,B,11:45,3,14:45,Caixa,Émilie Tran Phong,1,1,FULL,,26/10/2021 11:45,28,23/9/2025 11:45
-B - Terça 11:45-14:45 / Rizobar,Regular,Terça,B,11:45,3,14:45,Rizobar,Maria Teresa Braga Paixao de Almeida Leitao,1,2,1 AVAILABLE,,26/10/2021 11:45,28,23/9/2025 11:45
-B - Terça 11:45-14:45 / Mercearia,Regular,Terça,B,11:45,3,14:45,Mercearia,Maria Teresa Janela Pinto,1,2,1 AVAILABLE,,26/10/2021 11:45,28,23/9/2025 11:45
-B - Terça 14:30-17:30 / Caixa,Regular,Terça,B,14:30,3,17:30,Caixa,Maria Luisa Fernandes,1,1,FULL,,26/10/2021 14:30,28,23/9/2025 14:30
-B - Terça 14:30-17:30 / Rizobar,Regular,Terça,B,14:30,3,17:30,Rizobar,,0,1,1 AVAILABLE,"18/1/2022, 15/2/2022",26/10/2021 14:30,28,23/9/2025 14:30
-B - Terça 14:30-17:30 / Mercearia,Regular,Terça,B,14:30,3,17:30,Mercearia,Omer Ori,1,2,1 AVAILABLE,"18/1/2022, 15/2/2022",26/10/2021 14:30,28,23/9/2025 14:30
-B - Terça 17:15-20:15 / Caixa,Regular,Terça,B,17:15,3,20:15,Caixa,Maria Inês Soares da Costa,1,1,FULL,18/1/2022,26/10/2021 17:15,28,23/9/2025 17:15
-B - Terça 17:15-20:15 / Rizobar,Regular,Terça,B,17:15,3,20:15,Rizobar,Filipe Doutel Pinheiro,1,1,FULL,,26/10/2021 17:15,28,23/9/2025 17:15
-B - Terça 17:15-20:15 / Mercearia,Regular,Terça,B,17:15,3,20:15,Mercearia,,0,1,1 AVAILABLE,,7/6/2022 17:15,28,23/9/2025 17:15
-B - Terça 17:15-20:15 / Rizochef,Regular,Terça,B,17:15,3,20:15,Rizochef,"Aaron Ostrovsky,Agata Ballaun",2,2,FULL,,11/5/2021 17:15,28,23/9/2025 17:15
-B - Quarta 11:45-14:45 / Caixa,Regular,Quarta,B,11:45,3,14:45,Caixa,Mónica Ferreira de Melo Taipina,1,1,FULL,29/9/2021,14/4/2021 11:45,28,24/9/2025 11:45
-B - Quarta 11:45-14:45 / Rizobar,Regular,Quarta,B,11:45,3,14:45,Rizobar,"Jonathan Luyts,Karina Ribeiro de Andrade",2,2,FULL,"19/1/2022, 19/1/2022, 16/2/2022",14/4/2021 11:45,28,24/9/2025 11:45
-B - Quarta 11:45-14:45 / Mercearia,Regular,Quarta,B,11:45,3,14:45,Mercearia,Elliot Elam,1,1,FULL,"19/1/2022, 19/1/2022, 16/2/2022",14/4/2021 11:45,28,24/9/2025 11:45
-B - Quarta 14:30-14:30 / Caixa,,Quarta,B,14:30,,14:30,Caixa,,0,1,1 AVAILABLE,,9/7/2025 14:30,,9/7/2025 14:30
-B - Quarta 14:30-17:30 / Rizobar,Regular,Quarta,B,14:30,3,17:30,Rizobar,Michaela Primus,1,1,FULL,,8/6/2022 14:30,28,24/9/2025 14:30
-B - Quarta 14:30-17:30 / Mercearia,Regular,Quarta,B,14:30,3,17:30,Mercearia,Enrico Leon,1,1,FULL,"11/23/2021, 15/2/2022",14/4/2021 14:30,28,24/9/2025 14:30
-B - Quarta 17:15-20:15 / Caixa,Regular,Quarta,B,17:15,3,20:15,Caixa,João Alexandre dos Santos Martins,1,1,FULL,,12/5/2021 17:15,28,24/9/2025 17:15
-B - Quarta 17:15-20:15 / Rizobar,Regular,Quarta,B,17:15,3,20:15,Rizobar,"Amy Joanne Gardner ,Iona Maria Pereira Heseltine",2,2,FULL,7/7/2021,12/5/2021 17:15,28,24/9/2025 17:15
-B - Quarta 17:15-20:15 / Mercearia,Regular,Quarta,B,17:15,3,20:15,Mercearia,"Carlos Eleazar Agelvis Ibarra,Carlota de Macedo Santos",2,2,FULL,,8/6/2022 17:15,28,24/9/2025 17:15
-B - Quarta 17:15-20:15 / Rizochef,Regular,Quarta,B,17:15,3,20:15,Rizochef,"Aiden Zucker,John Noah Walton",2,2,FULL,,26/10/2022 17:15,28,24/9/2025 17:15
-B - Quinta 11:45-14:45 / Caixa,Regular,Quinta,B,11:45,3,14:45,Caixa,,0,1,1 AVAILABLE,8/7/2021,18/2/2021 11:45,28,25/9/2025 11:45
-B - Quinta 11:45-14:45 / Rizobar,Regular,Quinta,B,11:45,3,14:45,Rizobar,Maria Rita Perdigão Velez,1,2,1 AVAILABLE,9/2/2021,18/2/2021 11:45,28,25/9/2025 11:45
-B - Quinta 11:45-14:45 / Mercearia,Regular,Quinta,B,11:45,3,14:45,Mercearia,"Rita Filipe Saraiva Cabral Caldeira,Sarah Jornsay-Silverberg",2,2,FULL,9/2/2021,18/2/2021 11:45,28,25/9/2025 11:45
-B - Quinta 14:30-17:30 / Caixa,Regular,Quinta,B,14:30,3,17:30,Caixa,,0,1,1 AVAILABLE,,18/2/2021 14:30,28,25/9/2025 14:30
-B - Quinta 14:30-17:30 / Rizobar,Regular,Quinta,B,14:30,3,17:30,Rizobar,,0,1,1 AVAILABLE,,9/6/2022 14:30,28,25/9/2025 14:30
-B - Quinta 14:30-17:30 / Mercearia,Regular,Quinta,B,14:30,3,17:30,Mercearia,Uriel Orlow,1,2,1 AVAILABLE,"8/7/2021, 1/20/2022",18/2/2021 14:30,28,25/9/2025 14:30
-B - Quinta 17:15-20:15 / Caixa,Regular,Quinta,B,17:15,3,20:15,Caixa,"Laurent Grima,Vasco Ferreira da Cruz",2,2,FULL,,13/5/2021 17:15,28,25/9/2025 17:15
-B - Quinta 17:15-20:15 / Rizobar,Regular,Quinta,B,17:15,3,20:15,Rizobar,,0,1,1 AVAILABLE,,9/6/2022 17:15,28,25/9/2025 17:15
-B - Quinta 17:15-20:15 / Mercearia,Regular,Quinta,B,17:15,3,20:15,Mercearia,,0,1,1 AVAILABLE,28/10/2021,13/5/2021 17:15,28,25/9/2025 17:15
-B - Quinta 17:15-20:15 / Rizochef,Regular,Quinta,B,17:15,3,20:15,Rizochef,"Vattani Saray,Carolina Machado Ribeiro",2,2,FULL,,11/5/2021 17:15,28,23/9/2025 17:15
-B - Quinta 20:00-23:00 / Caixa,Regular,Quinta,B,20:00,3,23:00,Caixa,"Tiago Paulo Franco,Catarina Martins Soares",2,2,FULL,,11/5/2023 20:00,28,25/9/2025 20:00
-B - Quinta 20:00-23:00 / Rizobar,Regular,Quinta,B,20:00,3,23:00,Rizobar,Maria Flamino Cabeça,1,1,FULL,,11/5/2023 20:00,28,25/9/2025 20:00
-B - Quinta 20:00-23:00 / Mercearia,Regular,Quinta,B,20:00,3,23:00,Mercearia,Fanny Cloarec ,1,1,FULL,,11/5/2023 20:00,28,25/9/2025 20:00
-B - Sexta 11:45-14:45 / Caixa,Regular,Sexta,B,11:45,3,14:45,Caixa,"Clara Barilani,Inês Queirós Torres Ribeiro",2,2,FULL,,29/10/2021 11:45,28,26/9/2025 11:45
-B - Sexta 11:45-14:45 / Rizobar,Regular,Sexta,B,11:45,3,14:45,Rizobar,Barbara Moreira Del Colletto,1,2,1 AVAILABLE,,29/10/2021 11:45,28,26/9/2025 11:45
-B - Sexta 11:45-14:45 / Mercearia,Regular,Sexta,B,11:45,3,14:45,Mercearia,"Isadora Borges Nolasco Oliveira,Carlota Teixeira Loureiro",2,2,FULL,,29/10/2021 11:45,28,26/9/2025 11:45
-B - Sexta 14:30-17:30 / Caixa,Regular,Sexta,B,14:30,3,17:30,Caixa,João Ricardo Corredoura Fanha,1,1,FULL,"8/13/2021, 10/29/2021",19/2/2021 14:30,28,26/9/2025 14:30
-B - Sexta 14:30-17:30 / Rizobar,Regular,Sexta,B,14:30,3,17:30,Rizobar,,0,1,1 AVAILABLE,,10/6/2022 14:30,28,26/9/2025 14:30
-B - Sexta 14:30-17:30 / Mercearia,Regular,Sexta,B,14:30,3,17:30,Mercearia,Daniela Cabral,1,1,FULL,"21/1/2022, 18/2/2022",19/2/2021 14:30,28,26/9/2025 14:30
-B - Sexta 17:15-20:15 / Caixa,Regular,Sexta,B,17:15,3,20:15,Caixa,"Antoine Clerc,Marta Couto Martins",2,2,FULL,,19/2/2021 17:15,28,26/9/2025 17:15
-B - Sexta 17:15-20:15 / Rizobar,Regular,Sexta,B,17:15,3,20:15,Rizobar,Marion Oberhuber,1,1,FULL,9/7/2021,19/2/2021 17:15,28,26/9/2025 17:15
-B - Sexta 17:15-20:15 / Mercearia,Regular,Sexta,B,17:15,3,20:15,Mercearia,Bernardo Maria Castilho Baptista Fernandes,1,1,FULL,9/7/2021,19/2/2021 17:15,28,26/9/2025 17:15
-B - Sexta 17:15-20:15 / Rizochef,Regular,Sexta,B,17:15,3,20:15,Rizochef,,0,2,2 AVAILABLE,,29/9/2023 17:15,,29/9/2023 17:15
-B - Sexta 20:00-23:00 / Caixa,Regular,Sexta,B,20:00,3,23:00,Caixa,"Jeanne Chomarat,Catarina Farinha",2,2,FULL,,29/10/2021 20:00,28,26/9/2025 20:00
-B - Sexta 20:00-23:00 / Rizobar,Regular,Sexta,B,20:00,3,23:00,Rizobar,Victor Manuel Pinto Lamberto Silva,1,1,FULL,18/2/2022,29/10/2021 20:00,28,26/9/2025 20:00
-B - Sexta 20:00-23:00 / Mercearia,Regular,Sexta,B,20:00,3,23:00,Mercearia,Grégoire Sébastien Lepault,1,1,FULL,18/2/2022,29/10/2021 20:00,28,26/9/2025 20:00
-B - Sabado 11:45-14:45 / Caixa,Regular,Sabado,B,11:45,3,14:45,Caixa,Ana Filipa Soares da Silva Fernandes,1,1,FULL,,20/2/2021 11:45,28,27/9/2025 11:45
-B - Sabado 11:45-14:45 / Rizobar,Regular,Sabado,B,11:45,3,14:45,Rizobar,"Bruno Guimarães,Rita de Sousa Braga e Sá",2,2,FULL,,20/2/2021 11:45,28,27/9/2025 11:45
-B - Sabado 11:45-14:45 / Mercearia,Regular,Sabado,B,11:45,3,14:45,Mercearia,Henrique Belo,1,2,1 AVAILABLE,,20/2/2021 11:45,28,27/9/2025 11:45
-B - Sabado 14:30-17:30 / Caixa,Regular,Sabado,B,14:30,3,17:30,Caixa,,0,1,1 AVAILABLE,2/18/2022,17/4/2021 14:30,28,27/9/2025 14:30
-B - Sabado 14:30-17:30 / Rizobar,Regular,Sabado,B,14:30,3,17:30,Rizobar,Monica Paolizzi,1,1,FULL,"4/9/2021, 4/9/2021, 4/9/2021",17/4/2021 14:30,28,27/9/2025 14:30
-B - Sabado 14:30-17:30 / Mercearia,Regular,Sabado,B,14:30,3,17:30,Mercearia,Teresa Isabel de Freitas Cerdeira,1,1,FULL,"4/9/2021, 4/9/2021, 4/9/2021",17/4/2021 14:30,28,27/9/2025 14:30
-B - Sabado 17:15-20:15 / Caixa,Regular,Sabado,B,17:15,3,20:15,Caixa,,0,1,1 AVAILABLE,,13/5/2023 17:15,28,27/9/2025 17:15
-B - Sabado 17:15-20:15 / Rizobar,Regular,Sabado,B,17:15,3,20:15,Rizobar,,0,1,1 AVAILABLE,,13/5/2023 17:15,28,27/9/2025 17:15
-B - Sabado 17:15-20:15 / Mercearia,Regular,Sabado,B,17:15,3,20:15,Mercearia,Thrinisha Mohandas ,1,1,FULL,,13/5/2023 17:15,28,27/9/2025 17:15
-B - Domingo 11:45-14:45 / Caixa,Regular,Domingo,B,11:45,3,14:45,Caixa,Maria Inês Passos Duarte,1,1,FULL,23/1/2022,21/3/2021 11:45,28,28/9/2025 11:45
-B - Domingo 11:45-14:45 / Rizobar,Regular,Domingo,B,11:45,3,14:45,Rizobar,Filipe Emanuel dos Santos Albuquerque Barroso,1,1,FULL,,12/6/2022 11:45,28,28/9/2025 11:45
-B - Domingo 11:45-14:45 / Mercearia,Regular,Domingo,B,11:45,3,14:45,Mercearia,Maria Angela Gomes Araujo Lacerda Nobre,1,1,FULL,"5/9/2021, 5/9/2021, 10/3/2021",21/3/2021 11:45,28,28/9/2025 11:45
-B - Domingo 14:30-17:30 / Caixa,Regular,Domingo,B,14:30,3,17:30,Caixa,Flávio Rafael Miguel Mocho,1,1,FULL,"5/16/2021, 9/5/2021, 28/11/2021, 23/1/2022",21/3/2021 14:30,28,28/9/2025 14:30
-B - Domingo 14:30-17:30 / Rizobar,Regular,Domingo,B,14:30,3,17:30,Rizobar,Joanne Stilges,1,1,FULL,,12/6/2022 14:30,28,28/9/2025 14:30
-B - Domingo 14:30-17:30 / Mercearia,Regular,Domingo,B,14:30,3,17:30,Mercearia,,0,1,1 AVAILABLE,"11/7/2021, 3/10/2021, 20/2/2022, 20/2/2022",21/3/2021 14:30,28,28/9/2025 14:30
-B - Domingo 17:15-20:15 / Caixa,Regular,Domingo,B,17:15,3,20:15,Caixa,Margarida Beiral,1,1,FULL,,14/5/2023 17:15,28,28/9/2025 17:15
-B - Domingo 17:15-20:15 / Rizobar,Regular,Domingo,B,17:15,3,20:15,Rizobar,Ana Maria Farinha,1,1,FULL,,14/5/2023 17:15,28,28/9/2025 17:15
-B - Domingo 17:15-20:15 / Mercearia,Regular,Domingo,B,17:15,3,20:15,Mercearia,Maria Manuel Martins de Sousa,1,1,FULL,,14/5/2023 17:15,28,28/9/2025 17:15
-C - Segunda 11:45-14:45 / Caixa,Regular,Segunda,C,11:45,3,14:45,Caixa,Clarisse Pineau,1,1,FULL,,17/4/2023 11:45,28,29/9/2025 11:45
-C - Segunda 11:45-14:45 / Rizobar,Regular,Segunda,C,11:45,3,14:45,Rizobar,Fran Scapinello,1,1,FULL,,17/4/2023 11:45,28,29/9/2025 11:45
-C - Segunda 11:45-14:45 / Mercearia,Regular,Segunda,C,11:45,3,14:45,Mercearia,"Freya Dutta,Connor Scott",2,2,FULL,,17/4/2023 11:45,28,29/9/2025 11:45
-C - Terça 11:45-14:45 / Caixa,Regular,Terça,C,11:45,3,14:45,Caixa,Shreya Krishnan,1,1,FULL,,2/11/2021 11:45,28,30/9/2025 11:45
-C - Terça 11:45-14:45 / Rizobar,Regular,Terça,C,11:45,3,14:45,Rizobar,,0,2,2 AVAILABLE,,2/11/2021 11:45,28,30/9/2025 11:45
-C - Terça 11:45-14:45 / Mercearia,Regular,Terça,C,11:45,3,14:45,Mercearia,"Svenja Beller,Millad Khonsorkh",2,2,FULL,,2/11/2021 11:45,28,30/9/2025 11:45
-C - Terça 14:30-17:30 / Caixa,Regular,Terça,C,14:30,3,17:30,Caixa,Mariana Cristina Rydin,1,1,FULL,,2/11/2021 14:30,28,30/9/2025 14:30
-C - Terça 14:30-17:30 / Rizobar,Regular,Terça,C,14:30,3,17:30,Rizobar,,0,1,1 AVAILABLE,,14/6/2022 14:30,28,30/9/2025 14:30
-C - Terça 14:30-17:30 / Mercearia,Regular,Terça,C,14:30,3,17:30,Mercearia,"Karl Wienhold,Graciete Berta de Sousa Nobre ",2,2,FULL,,2/11/2021 14:30,28,30/9/2025 14:30
-C - Terça 17:15-20:15 / Caixa,Regular,Terça,C,17:15,3,20:15,Caixa,Márcia Lopes Sousa da Silva Pereira,1,1,FULL,2/21/2022,2/11/2021 17:15,28,30/9/2025 17:15
-C - Terça 17:15-20:15 / Rizobar,Regular,Terça,C,17:15,3,20:15,Rizobar,Laura Demaude,1,1,FULL,,2/11/2021 17:15,28,30/9/2025 17:15
-C - Terça 17:15-20:15 / Mercearia,Regular,Terça,C,17:15,3,20:15,Mercearia,"Fiona Kinniburgh,Mafalda Maria Cruz Soares de Azevedo",2,2,FULL,,2/11/2021 17:15,28,30/9/2025 17:15
-C - Terça 17:15-20:15 / Rizochef,Regular,Terça,C,17:15,3,20:15,Rizochef,,0,2,2 AVAILABLE,,20/4/2021 17:15,28,30/9/2025 17:15
-C - Quarta 11:45-14:45 / Caixa,Regular,Quarta,C,11:45,3,14:45,Caixa,Cândida Castelino da Silva Regalo ,1,1,FULL,"11/8/2021, 1/12/2021",21/4/2021 11:45,28,1/10/2025 11:45
-C - Quarta 11:45-14:45 / Rizobar,Regular,Quarta,C,11:45,3,14:45,Rizobar,,0,2,2 AVAILABLE,"8/9/2021, 8/9/2021, 6/10/2021",21/4/2021 11:45,28,1/10/2025 11:45
-C - Quarta 11:45-14:45 / Mercearia,Regular,Quarta,C,11:45,3,14:45,Mercearia,"Rui Eduardo Pedroso de Araújo Regalo ,Pierre-Olivier France",2,2,FULL,"8/9/2021, 8/9/2021, 6/10/2021",21/4/2021 11:45,28,1/10/2025 11:45
-C - Quarta 14:30-17:30 / Caixa,Regular,Quarta,C,14:30,3,17:30,Caixa,Ana Teresa Simões Queirós,1,1,FULL,"8/9/2021, 3/11/2021",21/4/2021 14:30,28,1/10/2025 14:30
-C - Quarta 14:30-17:30 / Rizobar,Regular,Quarta,C,14:30,3,17:30,Rizobar,,0,1,1 AVAILABLE,26/1/2022,21/4/2021 14:30,28,1/10/2025 14:30
-C - Quarta 14:30-17:30 / Mercearia,Regular,Quarta,C,14:30,3,17:30,Mercearia,"António Augusto Diniz Veloso,Sílvia Florbela Castro Barros Marques Moutinho Carreira",2,2,FULL,,15/6/2022 14:30,28,1/10/2025 14:30
-C - Quarta 17:15-20:15 / Caixa,Regular,Quarta,C,17:15,3,20:15,Caixa,"Francesco Rocca,Amélie Boudon",2,2,FULL,"8/9/2021, 6/10/2021",19/5/2021 17:15,28,1/10/2025 17:15
-C - Quarta 17:15-20:15 / Rizobar,Regular,Quarta,C,17:15,3,20:15,Rizobar,"Grégoire Devoucoux,Margarida Muralha Schweikert Farinha",2,2,FULL,"6/16/2021, 10/6/2021, 2/23/2022, 20/4/2022",19/5/2021 17:15,28,1/10/2025 17:15
-C - Quarta 17:15-20:15 / Mercearia,Regular,Quarta,C,17:15,3,20:15,Mercearia,Gonçalo da Silva Rosa Loff Sérgio,1,2,1 AVAILABLE,"6/16/2021, 10/6/2021, 2/23/2022, 20/4/2022",19/5/2021 17:15,28,1/10/2025 17:15
-C - Quarta 17:15-20:15 / Rizochef,Regular,Quarta,C,17:15,3,20:15,Rizochef,"Alex Herzog,Melaina Louise Barnes",2,2,FULL,,2/11/2022 17:15,28,1/10/2025 17:15
-C - Quinta 11:45-14:45 / Caixa,Regular,Quinta,C,11:45,3,14:45,Caixa,"Gustavo Garrido da Silva Gonzalez Briz,Fabrizio Cossutta",2,2,FULL,,25/2/2021 11:45,28,2/10/2025 11:45
-C - Quinta 11:45-14:45 / Rizobar,Regular,Quinta,C,11:45,3,14:45,Rizobar,Rosa Clarke,1,2,1 AVAILABLE,,16/6/2022 11:45,28,2/10/2025 11:45
-C - Quinta 11:45-14:45 / Mercearia,Regular,Quinta,C,11:45,3,14:45,Mercearia,,0,2,2 AVAILABLE,,25/2/2021 11:45,28,2/10/2025 11:45
-C - Quinta 14:30-17:30 / Caixa,Regular,Quinta,C,14:30,3,17:30,Caixa,,0,1,1 AVAILABLE,,25/2/2021 14:30,28,2/10/2025 14:30
-C - Quinta 14:30-17:30 / Rizobar,Regular,Quinta,C,14:30,3,17:30,Rizobar,,0,1,1 AVAILABLE,,16/6/2022 14:30,28,2/10/2025 14:30
-C - Quinta 14:30-17:30 / Mercearia,Regular,Quinta,C,14:30,3,17:30,Mercearia,,0,2,2 AVAILABLE,,25/2/2021 14:30,28,2/10/2025 14:30
-C - Quinta 17:15-20:15 / Caixa,Regular,Quinta,C,17:15,3,20:15,Caixa,"Stéphane Badertscher,Lara Cristina de Jesus Carvalho",2,2,FULL,,20/5/2021 17:15,28,2/10/2025 17:15
-C - Quinta 17:15-20:15 / Rizobar,Regular,Quinta,C,17:15,3,20:15,Rizobar,Sylvain Lucien Papyon,1,1,FULL,"7/10/2021, 27/1/2022, 24/2/2022, 24/2/2022",20/5/2021 17:15,28,2/10/2025 17:15
-C - Quinta 17:15-20:15 / Mercearia,Regular,Quinta,C,17:15,3,20:15,Mercearia,,0,1,1 AVAILABLE,,16/6/2022 17:15,28,2/10/2025 17:15
-C - Quinta 17:15-20:15 / Rizochef,Regular,Quinta,C,17:15,3,20:15,Rizochef,,0,2,2 AVAILABLE,,20/4/2021 17:15,28,30/9/2025 17:15
-C - Quinta 20:00-23:00 / Caixa,Regular,Quinta,C,20:00,3,23:00,Caixa,Inês Maria Petiz de Castro Viana,1,1,FULL,,18/5/2023 20:00,28,2/10/2025 20:00
-C - Quinta 20:00-23:00 / Rizobar,Regular,Quinta,C,20:00,3,23:00,Rizobar,João Pedro Faria da Silva Santos,1,1,FULL,,18/5/2023 20:00,28,2/10/2025 20:00
-C - Quinta 20:00-23:00 / Mercearia,Regular,Quinta,C,20:00,3,23:00,Mercearia,Julia Fernandes Hachiya de Azevedo,1,1,FULL,,18/5/2023 20:00,28,2/10/2025 20:00
-C - Sexta 11:45-14:45 / Caixa,Regular,Sexta,C,11:45,3,14:45,Caixa,"Eleanor Atkins,Filipa Soeiro Maia Vicente",2,2,FULL,25/2/2022,3/12/2021 11:45,28,3/10/2025 11:45
-C - Sexta 11:45-14:45 / Rizobar,Regular,Sexta,C,11:45,3,14:45,Rizobar,"Marcela Rocha Aroeira,Juliana Moita Fernandes",2,2,FULL,,17/6/2022 11:45,28,3/10/2025 11:45
-C - Sexta 11:45-14:45 / Mercearia,Regular,Sexta,C,11:45,3,14:45,Mercearia,,0,1,1 AVAILABLE,2/25/2022,15/10/2021 11:45,28,10/10/2025 11:45
-C - Sexta 14:30-17:30 / Caixa,Regular,Sexta,C,14:30,3,17:30,Caixa,Enriqueta Josefina Herrera Bravo de Salazar,1,1,FULL,6/18/2021,26/2/2021 14:30,28,3/10/2025 14:30
-C - Sexta 14:30-17:30 / Rizobar,Regular,Sexta,C,14:30,3,17:30,Rizobar,,0,1,1 AVAILABLE,"5/14/2021, 10/9/2021, 10/8/2021, 12/3/2021, 25/2/2022",26/2/2021 14:30,28,3/10/2025 14:30
-C - Sexta 14:30-17:30 / Mercearia,Regular,Sexta,C,14:30,3,17:30,Mercearia,Chloé Gastine ,1,1,FULL,,17/6/2022 14:30,28,3/10/2025 14:30
-C - Sexta 17:15-20:15 / Caixa,Regular,Sexta,C,17:15,3,20:15,Caixa,"Rui Filipe do Nascimento Gonçalves,Inês Telles de Menezes Marques Tavares",2,2,FULL,,26/2/2021 17:15,28,3/10/2025 17:15
-C - Sexta 17:15-20:15 / Rizobar,Regular,Sexta,C,17:15,3,20:15,Rizobar,Charlotte Seegers,1,1,FULL,8/13/2021,26/2/2021 17:15,28,3/10/2025 17:15
-C - Sexta 17:15-20:15 / Mercearia,Regular,Sexta,C,17:15,3,20:15,Mercearia,Julia Mantwill,1,1,FULL,8/13/2021,26/2/2021 17:15,28,3/10/2025 17:15
-C - Sexta 17:15-20:15 / Rizochef,Regular,Sexta,C,17:15,3,20:15,Rizochef,"Irina Andreia Nunes Lima,João Braga de Oliveira Lopes",2,2,FULL,,6/10/2023 17:15,,6/10/2023 17:15
-C - Sexta 20:00-23:00 / Caixa,Regular,Sexta,C,20:00,3,23:00,Caixa,Paola Guardini,1,1,FULL,,5/11/2021 20:00,28,3/10/2025 20:00
-C - Sexta 20:00-23:00 / Rizobar,Regular,Sexta,C,20:00,3,23:00,Rizobar,Sérgio Filipe Arrais Cassiano Vieira ,1,1,FULL,12/3/2021,28/1/2022 20:00,28,3/10/2025 20:00
-C - Sexta 20:00-23:00 / Mercearia,Regular,Sexta,C,20:00,3,23:00,Mercearia,,0,1,1 AVAILABLE,12/3/2021,28/1/2022 20:00,28,3/10/2025 20:00
-C - Sabado 11:45-14:45 / Caixa,Regular,Sabado,C,11:45,3,14:45,Caixa,Afonso Malheiro,1,1,FULL,"29/1/2022, 2/20/2022",27/2/2021 11:45,28,4/10/2025 11:45
-C - Sabado 11:45-14:45 / Rizobar,Regular,Sabado,C,11:45,3,14:45,Rizobar,Barbara Peric,1,1,FULL,2/26/2022,27/2/2021 11:45,28,4/10/2025 11:45
-C - Sabado 11:45-14:45 / Mercearia,Regular,Sabado,C,11:45,3,14:45,Mercearia,Cinzia Loreti,1,2,1 AVAILABLE,,18/6/2022 11:45,28,4/10/2025 11:45
-C - Sabado 14:30-17:30 / Caixa,Regular,Sabado,C,14:30,3,17:30,Caixa,José Paulo Alves Pinto Barbosa,1,1,FULL,"11/6/2021, 29/1/2022",24/4/2021 14:30,28,4/10/2025 14:30
-C - Sabado 14:30-17:30 / Rizobar,Regular,Sabado,C,14:30,3,17:30,Rizobar,,0,1,1 AVAILABLE,,18/6/2022 14:30,28,4/10/2025 14:30
-C - Sabado 14:30-17:30 / Mercearia,Regular,Sabado,C,14:30,3,17:30,Mercearia,Mónica Almeida Santos,1,1,FULL,12/4/2021,24/4/2021 14:30,28,4/10/2025 14:30
-C - Sabado 17:15-20:15 / Caixa,Regular,Sabado,C,17:15,3,20:15,Caixa,,0,1,1 AVAILABLE,,20/5/2023 17:15,28,4/10/2025 17:15
-C - Sabado 17:15-20:15 / Rizobar,Regular,Sabado,C,17:15,3,20:15,Rizobar,,0,1,1 AVAILABLE,,20/5/2023 17:15,28,4/10/2025 17:15
-C - Sabado 17:15-20:15 / Mercearia,Regular,Sabado,C,17:15,3,20:15,Mercearia,,0,1,1 AVAILABLE,,20/5/2023 17:15,28,4/10/2025 17:15
-C - Domingo 11:45-14:45 / Caixa,Regular,Domingo,C,11:45,3,14:45,Caixa,João Pedro Dionísio Narciso,1,1,FULL,,28/3/2021 11:45,28,5/10/2025 11:45
-C - Domingo 11:45-14:45 / Rizobar,Regular,Domingo,C,11:45,3,14:45,Rizobar,Barbara Gonçalves Arita,1,1,FULL,,28/3/2021 11:45,28,5/10/2025 11:45
-C - Domingo 11:45-14:45 / Mercearia,Regular,Domingo,C,11:45,3,14:45,Mercearia,,0,1,1 AVAILABLE,,28/3/2021 11:45,28,5/10/2025 11:45
-C - Domingo 14:30-17:30 / Caixa,Regular,Domingo,C,14:30,3,17:30,Caixa,António Joaquim Amaral Gomes,1,1,FULL,10/10/2021,28/3/2021 14:30,28,5/10/2025 14:30
-C - Domingo 14:30-17:30 / Rizobar,Regular,Domingo,C,14:30,3,17:30,Rizobar,,0,1,1 AVAILABLE,,19/6/2022 14:30,28,5/10/2025 14:30
-C - Domingo 14:30-17:30 / Mercearia,Regular,Domingo,C,14:30,3,17:30,Mercearia,,0,1,1 AVAILABLE,,28/3/2021 14:30,28,5/10/2025 14:30
-C - Domingo 17:15-20:15 / Caixa,Regular,Domingo,C,17:15,3,20:15,Caixa,João André Viegas Morais,1,1,FULL,,21/5/2023 17:15,28,5/10/2025 17:15
-C - Domingo 17:15-20:15 / Rizobar,Regular,Domingo,C,17:15,3,20:15,Rizobar,,0,1,1 AVAILABLE,,21/5/2023 17:15,28,5/10/2025 17:15
-C - Domingo 17:15-20:15 / Mercearia,Regular,Domingo,C,17:15,3,20:15,Mercearia,Edward Mancey,1,1,FULL,,21/5/2023 17:15,28,5/10/2025 17:15
-D - Segunda 11:45-14:45 / Caixa,Regular,Segunda,D,11:45,3,14:45,Caixa,,0,1,1 AVAILABLE,,24/4/2023 11:45,28,6/10/2025 11:45
-D - Segunda 11:45-14:45 / Rizobar,Regular,Segunda,D,11:45,3,14:45,Rizobar,Diana Guerra,1,1,FULL,,24/4/2023 11:45,28,6/10/2025 11:45
-D - Segunda 11:45-14:45 / Mercearia,Regular,Segunda,D,11:45,3,14:45,Mercearia,,0,1,1 AVAILABLE,,24/4/2023 11:45,28,6/10/2025 11:45
-D - Terça 11:45-14:45 / Caixa,Regular,Terça,D,11:45,3,14:45,Caixa,,0,1,1 AVAILABLE,,12/10/2021 11:45,28,7/10/2025 11:45
-D - Terça 11:45-14:45 / Rizobar,Regular,Terça,D,11:45,3,14:45,Rizobar,Mariana Barbosa Camacho,1,2,1 AVAILABLE,,24/5/2022 11:45,28,7/10/2025 11:45
-D - Terça 11:45-14:45 / Mercearia,Regular,Terça,D,11:45,3,14:45,Mercearia,Hannes Robert Rüter,1,2,1 AVAILABLE,,12/10/2021 11:45,28,7/10/2025 11:45
-D - Terça 14:30-17:30 / Caixa,Regular,Terça,D,14:30,3,17:30,Caixa,"Catarina Isabel Silva Coelho,Irene Tolu",2,2,FULL,,12/10/2021 14:30,28,7/10/2025 14:30
-D - Terça 14:30-17:30 / Rizobar,Regular,Terça,D,14:30,3,17:30,Rizobar,,0,1,1 AVAILABLE,,12/10/2021 14:30,28,7/10/2025 14:30
-D - Terça 14:30-17:30 / Mercearia,Regular,Terça,D,14:30,3,17:30,Mercearia,José Pedro Lira de Mesquita e Sousa,1,2,1 AVAILABLE,,12/10/2021 14:30,28,7/10/2025 14:30
-D - Terça 17:15-20:15 / Caixa,Regular,Terça,D,17:15,3,20:15,Caixa,"Claraluz Lannes Keiser,Joana Vitorino Synek",2,2,FULL,,12/10/2021 17:15,28,7/10/2025 17:15
-D - Terça 17:15-20:15 / Rizobar,Regular,Terça,D,17:15,3,20:15,Rizobar,Maria Magalhães Ressano Garcia,1,1,FULL,,24/5/2022 17:15,28,7/10/2025 17:15
-D - Terça 17:15-20:15 / Mercearia,Regular,Terça,D,17:15,3,20:15,Mercearia,Diogo Carmo dos Lóios Lipari Pinto,1,1,FULL,1/4/2022,12/10/2021 17:15,28,7/10/2025 17:15
-D - Terça 17:15-20:15 / Rizochef,Regular,Terça,D,17:15,3,20:15,Rizochef,"Kanika Singhal,Abia Karim ",2,2,FULL,,27/4/2021 17:15,28,7/10/2025 17:15
-D - Quarta 11:45-14:45 / Caixa,Regular,Quarta,D,11:45,3,14:45,Caixa,,0,1,1 AVAILABLE,7/21/2021,28/4/2021 11:45,28,8/10/2025 11:45
-D - Quarta 11:45-14:45 / Rizobar,Regular,Quarta,D,11:45,3,14:45,Rizobar,Vitor Manuel Peixoto Apolónia,1,2,1 AVAILABLE,1/5/2022,28/4/2021 11:45,28,8/10/2025 11:45
-D - Quarta 11:45-14:45 / Mercearia,Regular,Quarta,D,11:45,3,14:45,Mercearia,"Devina Shah,Takahiro Hosoya",2,2,FULL,1/5/2022,28/4/2021 11:45,28,8/10/2025 11:45
-D - Quarta 14:30-17:30 / Caixa,Regular,Quarta,D,14:30,3,17:30,Caixa,Pilar Do Campo,1,1,FULL,13/10/2021,28/4/2021 14:30,28,8/10/2025 14:30
-D - Quarta 14:30-17:30 / Rizobar,Regular,Quarta,D,14:30,3,17:30,Rizobar,,0,1,1 AVAILABLE,,28/4/2021 14:30,28,8/10/2025 14:30
-D - Quarta 14:30-17:30 / Mercearia,Regular,Quarta,D,14:30,3,17:30,Mercearia,,0,1,1 AVAILABLE,,28/4/2021 14:30,28,8/10/2025 14:30
-D - Quarta 17:15-20:15 / Caixa,Regular,Quarta,D,17:15,3,20:15,Caixa,Hélio Emídio Cerqueira Morais,1,1,FULL,"9/15/2021, 10/11/2021",26/5/2021 17:15,28,8/10/2025 17:15
-D - Quarta 17:15-20:15 / Rizobar,Regular,Quarta,D,17:15,3,20:15,Rizobar,"Isabelle Robinson ,Esmeralda Rosa Soares Prates do Rosário",2,2,FULL,"8/18/2021, 10/8/2021, 9/15/2021, 1/5/2022",26/5/2021 17:15,28,8/10/2025 17:15
-D - Quarta 17:15-20:15 / Mercearia,Regular,Quarta,D,17:15,3,20:15,Mercearia,Rebecca Tawata Tamachiro,1,2,1 AVAILABLE,"8/18/2021, 10/8/2021, 9/15/2 oi 021, 1/5/2022",26/5/2021 17:15,28,8/10/2025 17:15
-D - Quarta 17:15-20:15 / Rizochef,Regular,Quarta,D,17:15,3,20:15,Rizochef,,0,2,2 AVAILABLE,,9/11/2022 17:15,28,8/10/2025 17:15
-D - Quinta 11:45-14:45 / Caixa,Regular,Quinta,D,11:45,3,14:45,Caixa,,0,1,1 AVAILABLE,,4/3/2021 11:45,28,9/10/2025 11:45
-D - Quinta 11:45-14:45 / Rizobar,Regular,Quinta,D,11:45,3,14:45,Rizobar,,0,2,2 AVAILABLE,"10/14/2021, 3/2/2022",4/3/2021 11:45,28,9/10/2025 11:45
-D - Quinta 11:45-14:45 / Mercearia,Regular,Quinta,D,11:45,3,14:45,Mercearia,Sara Marina Franco Finote,1,2,1 AVAILABLE,"10/14/2021, 3/2/2022",4/3/2021 11:45,28,9/10/2025 11:45
-D - Quinta 14:30-17:30 / Caixa,Regular,Quinta,D,14:30,3,17:30,Caixa,Rachel Ben-Menachem,1,1,FULL,,4/3/2021 14:30,28,9/10/2025 14:30
-D - Quinta 14:30-17:30 / Rizobar,Regular,Quinta,D,14:30,3,17:30,Rizobar,Léo Berthe ,1,1,FULL,,26/5/2022 14:30,28,9/10/2025 14:30
-D - Quinta 14:30-17:30 / Mercearia,Regular,Quinta,D,14:30,3,17:30,Mercearia,"Carlo Guadagno,Marion Duval",2,2,FULL,"9/16/2021, 10/14/2021, 9/16/2021",4/3/2021 14:30,28,9/10/2025 14:30
-D - Quinta 17:15-20:15 / Caixa,Regular,Quinta,D,17:15,3,20:15,Caixa,"Anastasia Lukovnikova,André Rosa Alcalde Marques",2,2,FULL,,27/5/2021 17:15,28,9/10/2025 17:15
-D - Quinta 17:15-20:15 / Rizobar,Regular,Quinta,D,17:15,3,20:15,Rizobar,"Véronique Lerch,Lionel Palacin",2,2,FULL,"9/23/2021, 12/9/2021, 6/1/2022, 3/2/2022, 3/2/2022",27/5/2021 17:15,28,9/10/2025 17:15
-D - Quinta 17:15-20:15 / Mercearia,Regular,Quinta,D,17:15,3,20:15,Mercearia,Bernadette Maria Karmasin,1,1,FULL,"9/23/2021, 12/9/2021, 6/1/2022, 3/2/2022, 3/2/2022",27/5/2021 17:15,28,9/10/2025 17:15
-D - Quinta 17:15-20:15 / Rizochef,Regular,Quinta,D,17:15,3,20:15,Rizochef,"Isabella Permanschlager,Nicola Franchini",2,2,FULL,,27/4/2021 17:15,28,7/10/2025 17:15
-D - Quinta 20:00-23:00 / Caixa,Regular,Quinta,D,20:00,3,23:00,Caixa,Anna Matteoli,1,1,FULL,,25/5/2023 20:00,28,9/10/2025 20:00
-D - Quinta 20:00-23:00 / Rizobar,Regular,Quinta,D,20:00,3,23:00,Rizobar,"José Ricardo da Silva Gomes Josué,John Gmerek Huerta",2,2,FULL,,25/5/2023 20:00,28,9/10/2025 20:00
-D - Quinta 20:00-23:00 / Mercearia,Regular,Quinta,D,20:00,3,23:00,Mercearia,Inês Pereira Lopes,1,1,FULL,,25/5/2023 20:00,28,9/10/2025 20:00
-D - Sexta 11:45-14:45 / Caixa,Regular,Sexta,D,11:45,3,14:45,Caixa,Rita Bairrão de Carvalho,1,1,FULL,,15/10/2021 11:45,28,10/10/2025 11:45
-D - Sexta 11:45-14:45 / Rizobar,Regular,Sexta,D,11:45,3,14:45,Rizobar,Vanessa Guerreiro Rodrigues,1,2,1 AVAILABLE,,27/5/2022 11:45,28,10/10/2025 11:45
-D - Sexta 11:45-14:45 / Mercearia,Regular,Sexta,D,11:45,3,14:45,Mercearia,Antonino Faibene,1,1,FULL,4/2/2022,15/10/2021 11:45,28,10/10/2025 11:45
-D - Sexta 14:30-17:30 / Caixa,Regular,Sexta,D,14:30,3,17:30,Caixa,Bertrand Lacoste,1,1,FULL,"23/7/2021, 17/9/2021, 15/10/2021",5/3/2021 14:30,28,10/10/2025 14:30
-D - Sexta 14:30-17:30 / Rizobar,Regular,Sexta,D,14:30,3,17:30,Rizobar,"Mariana Costa Fernandes,Leon Ingelse",2,2,FULL,4/2/2022,5/3/2021 14:30,28,10/10/2025 14:30
-D - Sexta 14:30-17:30 / Mercearia,Regular,Sexta,D,14:30,3,17:30,Mercearia,Anders Chen,1,1,FULL,4/2/2022,5/3/2021 14:30,28,10/10/2025 14:30
-D - Sexta 17:15-20:15 / Caixa,Regular,Sexta,D,17:15,3,20:15,Caixa,"Laura Pereira de Macedo,Eelke André Verhagen",2,2,FULL,8/20/2021,5/3/2021 17:15,28,10/10/2025 17:15
-D - Sexta 17:15-20:15 / Rizobar,Regular,Sexta,D,17:15,3,20:15,Rizobar,"Ana Rita Aguiar Soares Pereira,Elodie Bouny",2,2,FULL,8/20/2021,5/3/2021 17:15,28,10/10/2025 17:15
-D - Sexta 17:15-20:15 / Mercearia,Regular,Sexta,D,17:15,3,20:15,Mercearia,João Ricardo Corredoura Fanha,1,1,FULL,,27/5/2022 17:15,28,10/10/2025 17:15
-D - Sexta 17:15-20:15 / Rizochef,Regular,Sexta,D,17:15,3,20:15,Rizochef,"Magdalena Narewska,Miguel Pinto dos Santos Ferreira ",2,2,FULL,,13/10/2023 17:15,,13/10/2023 17:15
-D - Sexta 20:00-23:00 / Caixa,Regular,Sexta,D,20:00,3,23:00,Caixa,"Alice Carreira de Almeida Figueiredo,Gil Perdigão Gonçalves",2,2,FULL,,12/11/2021 20:00,28,10/10/2025 20:00
-D - Sexta 20:00-23:00 / Rizobar,Regular,Sexta,D,20:00,3,23:00,Rizobar,Afonso Miguel dos Santos Anjos ,1,1,FULL,4/2/2022,12/11/2021 20:00,28,10/10/2025 20:00
-D - Sexta 20:00-23:00 / Mercearia,Regular,Sexta,D,20:00,3,23:00,Mercearia,,0,1,1 AVAILABLE,4/2/2022,12/11/2021 20:00,28,10/10/2025 20:00
-D - Sabado 11:45-14:45 / Caixa,Regular,Sabado,D,11:45,3,14:45,Caixa,Beatriz Matias Vicente,1,1,FULL,13/11/2021,6/3/2021 11:45,28,11/10/2025 11:45
-D - Sabado 11:45-14:45 / Rizobar,Regular,Sabado,D,11:45,3,14:45,Rizobar,,0,1,1 AVAILABLE,"7/24/2021, 9/18/2021, 11/13/2021, 12/11/2021",6/3/2021 11:45,28,11/10/2025 11:45
-D - Sabado 11:45-14:45 / Mercearia,Regular,Sabado,D,11:45,3,14:45,Mercearia,Ana Montanha,1,2,1 AVAILABLE,,28/5/2022 11:45,28,11/10/2025 11:45
-D - Sabado 14:30-17:30 / Caixa,Regular,Sabado,D,14:30,3,17:30,Caixa,Elisabete Esmeralda Martins Serra,1,1,FULL,,1/5/2021 14:30,28,11/10/2025 14:30
-D - Sabado 14:30-17:30 / Rizobar,Regular,Sabado,D,14:30,3,17:30,Rizobar,,0,1,1 AVAILABLE,"8/21/2021, 12/11/2021, 8/1/2022",1/5/2021 14:30,28,11/10/2025 14:30
-D - Sabado 14:30-17:30 / Mercearia,Regular,Sabado,D,14:30,3,17:30,Mercearia,,0,1,1 AVAILABLE,"8/21/2021, 12/11/2021, 8/1/2022",1/5/2021 14:30,28,11/10/2025 14:30
-D - Sabado 17:15-20:15 / Caixa,Regular,Sabado,D,17:15,3,20:15,Caixa,Beatriz Ribeiro dos Santos Franco Afonso,1,1,FULL,,27/5/2023 17:15,28,11/10/2025 17:15
-D - Sabado 17:15-20:15 / Rizobar,Regular,Sabado,D,17:15,3,20:15,Rizobar,,0,1,1 AVAILABLE,,27/5/2023 17:15,28,11/10/2025 17:15
-D - Sabado 17:15-20:15 / Mercearia,Regular,Sabado,D,17:15,3,20:15,Mercearia,Rogério Castelo Castanheira da Silva,1,1,FULL,,27/5/2023 17:15,28,11/10/2025 17:15
-D - Domingo 11:45-14:45 / Caixa,Regular,Domingo,D,11:45,3,14:45,Caixa,Ana Isabel Ribeiro Garcia,1,1,FULL,"17/10/2021, 2/5/2022",4/4/2021 11:45,28,12/10/2025 11:45
-D - Domingo 11:45-14:45 / Rizobar,Regular,Domingo,D,11:45,3,14:45,Rizobar,Beatrice Piletti,1,1,FULL,"12/11/2021, 12/12/2021",4/4/2021 11:45,28,12/10/2025 11:45
-D - Domingo 11:45-14:45 / Mercearia,Regular,Domingo,D,11:45,3,14:45,Mercearia,"Tânia Salvaterra ,Sónia Queiroga",2,2,FULL,"12/11/2021, 12/12/2021",4/4/2021 11:45,28,12/10/2025 11:45
-D - Domingo 14:30-17:30 / Caixa,Regular,Domingo,D,14:30,3,17:30,Caixa,Marta Archibald Cabral,1,1,FULL,"5/30/2021, 1/9/2022",4/4/2021 14:30,28,12/10/2025 14:30
-D - Domingo 14:30-17:30 / Rizobar,Regular,Domingo,D,14:30,3,17:30,Rizobar,,0,1,1 AVAILABLE,,29/5/2022 14:30,28,12/10/2025 14:30
-D - Domingo 14:30-17:30 / Mercearia,Regular,Domingo,D,14:30,3,17:30,Mercearia,Gonçalo Nuno Ferreira Salema de Matos,1,1,FULL,"9/19/2021, 6/2/2022",4/4/2021 14:30,28,12/10/2025 14:30
-D - Domingo 17:15-20:15 / Caixa,Regular,Domingo,D,17:15,3,20:15,Caixa,Helena Alexandra Ribeiro de Carvalho Pinheiro,1,1,FULL,,28/5/2023 17:15,28,12/10/2025 17:15
-D - Domingo 17:15-20:15 / Rizobar,Regular,Domingo,D,17:15,3,20:15,Rizobar,Francisco Gonçalves Dias Cardoso Diogo,1,1,FULL,,28/5/2023 17:15,28,12/10/2025 17:15
-D - Domingo 17:15-20:15 / Mercearia,Regular,Domingo,D,17:15,3,20:15,Mercearia,Mariana Seixas Lopes Nunes Catalão,1,1,FULL,,28/5/2023 17:15,28,12/10/2025 17:15
-Flex / Flex Caixa,Flex,Flex,ABCD,00:00,,00:00,Flex Caixa,"Joke Langens,Anne Fauquet ,Miguel Décio Passos Duarte,Lidia Mello,Bárbara Mendes,Marisa Cesária Ereira Cardoso,Andre Filipe Prazeres Amorim,Ana Catarina Lopes Pinheiro ,Catarina Sofia Domingues Gomes,Sara Filipa Almeida de Andrade,Julie Bauwens,Telma Patrícia Rodrigues Carvalho,Mafalda Sofia Martins Saraiva ,Sara Huberty Nogueira Ramos,Tiago Pereira Martins De Castro Nabais,Aurore Delaunay,João Sousa Fialho,Joana Margarida Gaspar Cação Ribeiro,Filipa Carlota Pereira Fernandes Marques ,Francisca Sottomayor,Kelli Silver,Vanja Phoenix Favetta,Hugo Charbonnier,Márcio Arduin Saraiva",24,30,6 AVAILABLE,,1/11/2021 00:00,,1/11/2021 00:00
-Flex / Flex Mercearia,Flex,Flex,ABCD,00:00,,00:00,Flex Mercearia,"Pia Stoepper,Maria Inês Costa Areosa Rodrigues,Anne Fauquet ,Ana Catarina Lopes Pinheiro ,Joana Luísa Nogueira Amorim,Catarina Sofia Domingues Gomes,Gonçalo Miguel Natário de Lourenço Caiado,Sofia Vieira Paredes,Mafalda Sofia Martins Saraiva ,Sara Huberty Nogueira Ramos,Chris Yeoward,Jean-François Gaudy,Carla Sofia Franco Luis,Inês Alexandra Teixeira Mestre ,Daniele Grosso,Maria de Arcelis Henriques Claudino,Aurore Delaunay,Elvia Vasconcelos,Andrea Sozzi,Eleonor Soares de Albergaria e Silva,Miguel Antunes,Maria João Guerreiro Vaz,Jucimara Araujo Cavalcante Souza,Kitti Baracsi,Catarina das Neves Batista Cerqueira",25,30,5 AVAILABLE,,1/11/2021 00:00,,1/11/2021 00:00
-Flex / Flex Rizobar,Flex,Flex,ABCD,00:00,,00:00,Flex Rizobar,"Ana Margarida Silvestre Botelho,Angela Maria Gallus,Anne Fauquet ,Mariana Stichaner Lacasta Macedo ,Ana Catarina Lopes Pinheiro ,Catarina Sofia Domingues Gomes,Lulu Falcão,Mafalda Sofia Martins Saraiva ,Sara Huberty Nogueira Ramos,Claudia Rita Almeida de Andrade,Lígia Martins Gomes,Aurore Delaunay,Wim Forceville,Francisca Sottomayor,Inês Esteves Ribeiro ,António Silva,Mathilde Gallien,Joana Marques Batista,Miguel Rosal Martins ,Talita Caselato,Peter Joseph,Julia Ribeiro",22,30,8 AVAILABLE,,1/11/2021 00:00,,1/11/2021 00:00
\ No newline at end of file
From 95f7466b58525ffa18b2bba576ac110550799cfc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Th=C3=A9ophile=20Madet?=
Date: Thu, 2 Oct 2025 17:44:17 +0200
Subject: [PATCH 18/27] fix: calendar loads without too many DB queries again
---
tapir/shifts/models.py | 3 +++
tapir/shifts/templatetags/shifts.py | 16 +++++++++++-----
2 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/tapir/shifts/models.py b/tapir/shifts/models.py
index e455ffadb..43c0c51fe 100644
--- a/tapir/shifts/models.py
+++ b/tapir/shifts/models.py
@@ -57,6 +57,9 @@ class ShiftUserCapabilityTranslation(models.Model):
description = models.TextField(_("Description"))
capability = models.ForeignKey(ShiftUserCapability, on_delete=models.CASCADE)
+ def __str__(self):
+ return f"{self.name} - {self.language}"
+
class ShiftSlotWarning(models.Model):
def get_current_translation(self) -> ShiftUserCapabilityTranslation | None:
diff --git a/tapir/shifts/templatetags/shifts.py b/tapir/shifts/templatetags/shifts.py
index 953eaa19f..8e687e980 100644
--- a/tapir/shifts/templatetags/shifts.py
+++ b/tapir/shifts/templatetags/shifts.py
@@ -49,7 +49,9 @@ def shift_name_as_class(shift_name: str) -> str:
def shift_to_block_object(shift: Shift, fill_parent: bool):
attendances = {}
- slots = sort_slots_by_name(list(shift.slots.filter(deleted=False)))
+ slots = shift.slots.all()
+ slots = [slot for slot in slots if not slot.deleted]
+ slots = sort_slots_by_name(slots)
for slot in slots:
slot_name = slot.name
@@ -88,6 +90,7 @@ def shift_to_block_object(shift: Shift, fill_parent: bool):
def get_attendance_state_for_html_icon(slot: ShiftSlot) -> str:
attendance = None
+
for a in slot.attendances.all():
if a.is_valid():
attendance = a
@@ -98,10 +101,11 @@ def get_attendance_state_for_html_icon(slot: ShiftSlot) -> str:
if attendance.state == ShiftAttendance.State.LOOKING_FOR_STAND_IN:
return "standin"
+
if (
slot.slot_template is not None
and hasattr(slot.slot_template, "attendance_template")
- and slot.slot_template.attendance_template.user == attendance.user
+ and slot.slot_template.attendance_template.user_id == attendance.user_id
):
return "regular"
return "single"
@@ -146,9 +150,11 @@ def shift_template_to_block_object(shift_template: ShiftTemplate, fill_parent: b
num_attendances = 0
- slot_templates = sort_slots_by_name(
- list(shift_template.slot_templates.filter(deleted=False))
- )
+ slot_templates = shift_template.slot_templates.all()
+ slot_templates = [
+ slot_template for slot_template in slot_templates if not slot_template.deleted
+ ]
+ slot_templates = sort_slots_by_name(slot_templates)
for slot_template in slot_templates:
slot_name = slot_template.name
From b2b04c4b4bd448c2605a549fada8de5c7d81be23 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Th=C3=A9ophile=20Madet?=
Date: Mon, 6 Oct 2025 16:23:29 +0200
Subject: [PATCH 19/27] misc: small documentation update about syncing user
data with coops.pt
---
README.md | 61 ++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 42 insertions(+), 19 deletions(-)
diff --git a/README.md b/README.md
index 04ff7b08d..1ae5f91a7 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,8 @@
[](https://sonarcloud.io/summary/new_code?id=SuperCoopBerlin_tapir)
[](https://sonarcloud.io/summary/new_code?id=SuperCoopBerlin_tapir)
-Tapir is a member and shift management system for [SuperCoop Berlin](https://supercoop.de) and [Rizoma](https://www.rizomacoop.pt/)
+Tapir is a member and shift management system for [SuperCoop Berlin](https://supercoop.de)
+and [Rizoma](https://www.rizomacoop.pt/)
Our Vorstand and member office uses Tapir to manage shifts and members, for example their personal data, capabilities,
payments and shift statuses. It is also used for automatic mails and evaluate new applicants.
Members can use Tapir to register or unregister for shifts, search for a stand-in and see their shift status as well as
@@ -14,13 +15,16 @@ upcoming shifts.
The Tapir project is developed by SuperCoop members and is licensed under the terms of the [AGPL license](LICENSE.md).
-If you're interested in using Tapir for your own coop, please contact [Théo](https://github.com/Theophile-Madet) or [SuperCoop](https://supercoop.de/en/contact/), we'll be very happy to help!
+If you're interested in using Tapir for your own coop, please contact [Théo](https://github.com/Theophile-Madet)
+or [SuperCoop](https://supercoop.de/en/contact/), we'll be very happy to help!
SuperCoop members can access the system at [https://members.supercoop.de](https://members.supercoop.de).
-Tapir (/ˈteɪpər/) was original inspired by the french coop [L'éléfan](https://github.com/elefan-grenoble/gestion-compte), thanks to them for the inspiration.
-
-A fork for community supported agriculture organization exists [here](https://github.com/FoodCoopX/wirgarten-tapir). While they stil both use the name Tapir, they have diverged a lot and don't share any code anymore.
+Tapir (/ˈteɪpər/) was original inspired by the french
+coop [L'éléfan](https://github.com/elefan-grenoble/gestion-compte), thanks to them for the inspiration.
+
+A fork for community supported agriculture organization exists [here](https://github.com/FoodCoopX/wirgarten-tapir).
+While they stil both use the name Tapir, they have diverged a lot and don't share any code anymore.
## Getting started
@@ -76,13 +80,15 @@ You can do so **without any programming or Python knowledge**! Just choose a tas
## Troubleshooting
* On macOS, in order to set up a local Python `venv`, you might have to install Postgresql to get `psycopg2` working.
- Use `brew install postgresql` for that.
+ Use `brew install postgresql` for that.
## Rizoma version
Currently, two versions of Tapir exist: for SuperCoop on the `master` branch and for Rizoma under the `rizoma` branch.
If you keep the .env file as is, you'll get the SuperCoop version, which has member management and shift management.
-By setting the .env file like this, you'll get the rizoma version, which concentrates on shifts and uses an external identity provider.
+By setting the .env file like this, you'll get the rizoma version, which concentrates on shifts and uses an external
+identity provider.
+
```dotenv
DEBUG=True
DJANGO_VITE_DEBUG=
@@ -95,21 +101,38 @@ COOPS_PT_RSA_PUBLIC_KEY_FILE_PATH=invalid_path
TEMPLATE_SUB_FOLDER=rizoma
```
-### User synchronization
-Every hour, a celery task (`tapir.rizoma.tasks.sync_users_with_coops_pt_backend`) synchronizes the user list and the member list from coops.pt instance (env var `COOPS_PT_API_BASE_URL`) int the Tapir DB.
-Changes done in Tapir are not reflected back to coops.pt.
+### User synchronization with coops.pt
+
+#### Setup
+
+You need to define 2 environment variables for the user sync to work.
-Newly created users can be used to log in right away, there is no need to wait for a sync.
+- The API key `COOPS_PT_API_KEY`, with you should get from [...].coops.pt/developers
+- The path to the RSA public key file `COOPS_PT_RSA_PUBLIC_KEY_FILE_PATH`.
+ This key is used to validate the JWT tokens returned by the coops.pt API when logging in.
+ The key in the file should be in the "PKCS#1 PEM-encoded"-Format.
-You can also run `manage.py sync_users_with_coops_pt_backend` to trigger a sync manually.
+#### Automated sync
+
+- Every hour, a celery task (`tapir.rizoma.tasks.sync_users_with_coops_pt_backend`) synchronizes the user list and the
+ member list from coops.pt instance (env var `COOPS_PT_API_BASE_URL`) in the Tapir DB.
+- Changes done in Tapir are not reflected back to coops.pt.
+- Members deleted from the coops.pt instance will be deleted from the Tapir instance.
+- Newly created users can be used to log in right away, there is no need to wait for a sync.
+- You can also run `manage.py sync_users_with_coops_pt_backend` to trigger a sync manually.
### Google calendar invites
+
When members register to a shift, it is possible to send them an invitation to a Google calendar event by mail.
This requires running local instance and the following setup:
- - get a google secret json file from your Google app (see [the docs](https://developers.google.com/workspace/calendar/api/quickstart/python))
- - on your local machine, set the env var PATH_TO_GOOGLE_CLIENT_SECRET_FILE as path to that file and start your local instance
- - run `manage.py get_google_authorized_user_file`, login if necessary and authorize the app
- - this will create a `google_user_token.json` file
- - copy that file to your server, with the same name and relative path
- - on the feature flag page (.../core/featureflag_list), enable the relevant flag: `feature_flags.shifts.google_calendar_events_for_shifts`
- - done!
\ No newline at end of file
+
+- get a google secret json file from your Google app (
+ see [the docs](https://developers.google.com/workspace/calendar/api/quickstart/python))
+- on your local machine, set the env var PATH_TO_GOOGLE_CLIENT_SECRET_FILE as path to that file and start your local
+ instance
+- run `manage.py get_google_authorized_user_file`, login if necessary and authorize the app
+- this will create a `google_user_token.json` file
+- copy that file to your server, with the same name and relative path
+- on the feature flag page (.../core/featureflag_list), enable the relevant flag:
+ `feature_flags.shifts.google_calendar_events_for_shifts`
+- done!
\ No newline at end of file
From 2dfcc1eb9fcdced1c5c11d3b475a858fa510ee2f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Th=C3=A9ophile=20Madet?=
Date: Mon, 6 Oct 2025 16:24:30 +0200
Subject: [PATCH 20/27] misc: typo fix in the readme
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 1ae5f91a7..778a3fdbf 100644
--- a/README.md
+++ b/README.md
@@ -107,7 +107,7 @@ TEMPLATE_SUB_FOLDER=rizoma
You need to define 2 environment variables for the user sync to work.
-- The API key `COOPS_PT_API_KEY`, with you should get from [...].coops.pt/developers
+- The API key `COOPS_PT_API_KEY`, which you should get from [...].coops.pt/developers
- The path to the RSA public key file `COOPS_PT_RSA_PUBLIC_KEY_FILE_PATH`.
This key is used to validate the JWT tokens returned by the coops.pt API when logging in.
The key in the file should be in the "PKCS#1 PEM-encoded"-Format.
From d84f6bf0e11aa63893c2388f7eaf1b517759d133 Mon Sep 17 00:00:00 2001
From: Lara Carvalho
Date: Tue, 7 Oct 2025 17:26:35 +0000
Subject: [PATCH 21/27] Translated using Weblate (Portuguese)
Currently translated at 93.6% (908 of 970 strings)
Translation: tapir/tapir python translations
Translate-URL: http://weblate.seriousdino.org/projects/tapir/tapir-python-translations/pt/
---
.../locale/pt/LC_MESSAGES/django.po | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/tapir/translations/locale/pt/LC_MESSAGES/django.po b/tapir/translations/locale/pt/LC_MESSAGES/django.po
index ca6a147cd..b8184066a 100644
--- a/tapir/translations/locale/pt/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/pt/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-28 12:56+0200\n"
-"PO-Revision-Date: 2025-09-30 21:59+0000\n"
+"PO-Revision-Date: 2025-10-08 17:59+0000\n"
"Last-Translator: Lara Carvalho \n"
"Language-Team: Portuguese \n"
@@ -2679,7 +2679,7 @@ msgstr "Diversos"
#: core/apps.py:38
msgid "About Rizoma"
-msgstr "Sobre o Rizoma"
+msgstr "Sobre a Rizoma"
#: core/apps.py:46
msgid "Wiki"
@@ -3855,10 +3855,15 @@ msgid ""
" "
msgstr ""
"\n"
-"
Abaixo encontra todos os turnos futuros e quem está registado em cada um.
\n"
-"
Pode inscrever-se em qualquer turno que tenha vagas no TAPIR. Pode também cancelar a inscrição pelo menos %(nb_days_for_self_unregister)s dias antes do turno. \n"
-" Ao inscrever-se num destes turnos, compromete-se apenas com esse dia específico.
\n"
-"
Para se registar num turno ABCD regular, contacte o Member Office.
\n"
+"
Abaixo encontras todos os turnos futuros e quem está registado em "
+"cada um.
\n"
+"
Podes inscrever-te em qualquer turno que tenha vagas no TAPIR. "
+"Podes também cancelar a inscrição pelo menos %(nb_days_for_self_unregister)s "
+"dias antes do turno. \n"
+" Ao te inscreveres num destes turnos, comprometes-te apenas com "
+"esse dia específico.
\n"
+"
Para te registares num turno ABCD regular, contacta o GTT-"
+"membros.
\n"
" "
#: shifts/templates/shifts/shift_calendar_past.html:4
From 5292dc38245b07714b485c477862e0214767e7b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Th=C3=A9ophile=20Madet?=
Date: Thu, 9 Oct 2025 17:09:24 +0200
Subject: [PATCH 22/27] feat: Added a feature flag to block all outgoing
emails.
---
tapir/core/config.py | 1 +
tapir/core/services/send_mail_service.py | 9 ++++++---
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/tapir/core/config.py b/tapir/core/config.py
index 2e3afd3ca..20a2bb946 100644
--- a/tapir/core/config.py
+++ b/tapir/core/config.py
@@ -8,3 +8,4 @@
help_text_displayed_name = "Optional. If filled, will be used instead of the administrative first name wherever possible. Only official documents will show the administrative first name."
feature_flag_solidarity_shifts = "Enable Solidarity Shifts"
+feature_flag_automated_mails = "feature_flags.core.automated_mails"
diff --git a/tapir/core/services/send_mail_service.py b/tapir/core/services/send_mail_service.py
index 166b3e5e7..f50bf72b1 100644
--- a/tapir/core/services/send_mail_service.py
+++ b/tapir/core/services/send_mail_service.py
@@ -3,9 +3,10 @@
from django.contrib.auth.models import User
from django.core.mail import EmailMultiAlternatives
from django.utils import translation
-
from tapir.accounts.models import TapirUser
from tapir.coop.models import ShareOwner
+from tapir.core.config import feature_flag_automated_mails
+from tapir.core.models import FeatureFlag
from tapir.core.services.optional_mails_for_user_service import (
OptionalMailsForUserService,
)
@@ -60,9 +61,11 @@ def __send(
from_email=email_builder.get_from_email(),
attachments=email_builder.get_attachments(),
)
-
email.content_subtype = "html"
- email.send()
+
+ if FeatureFlag.get_flag_value(feature_flag_automated_mails):
+ email.send()
+
cls.create_log_entry(
email=email,
actor=actor,
From fd4b336f41017f6e90c82e9b3a0c56e329ec89ea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Th=C3=A9ophile=20Madet?=
Date: Thu, 9 Oct 2025 17:18:07 +0200
Subject: [PATCH 23/27] feat: Added a command that creates google calendar
events for all future shifts (if they don't have one already)
---
.../commands/create_google_calendar_events.py | 38 +++++++++++++++++++
1 file changed, 38 insertions(+)
create mode 100644 tapir/rizoma/management/commands/create_google_calendar_events.py
diff --git a/tapir/rizoma/management/commands/create_google_calendar_events.py b/tapir/rizoma/management/commands/create_google_calendar_events.py
new file mode 100644
index 000000000..235d216d3
--- /dev/null
+++ b/tapir/rizoma/management/commands/create_google_calendar_events.py
@@ -0,0 +1,38 @@
+import datetime
+
+from django.core.management import BaseCommand
+from django.db import transaction
+
+from tapir.core.models import FeatureFlag
+from tapir.rizoma.config import FEATURE_FLAG_GOOGLE_CALENDAR_EVENTS_FOR_SHIFTS
+from tapir.rizoma.services.google_calendar_event_manager import (
+ GoogleCalendarEventManager,
+)
+from tapir.shifts.models import ShiftAttendance
+
+
+class Command(BaseCommand):
+ @transaction.atomic
+ def handle(self, *args, **options):
+ attendances = ShiftAttendance.objects.filter(
+ slot__shift__start_time__gt=datetime.datetime.now(),
+ external_event_id__isnull=True,
+ )
+ print("Number of events to create: " + str(attendances.count()))
+
+ if not FeatureFlag.get_flag_value(
+ FEATURE_FLAG_GOOGLE_CALENDAR_EVENTS_FOR_SHIFTS
+ ):
+ print(
+ "The calendar event feature is disabled, enable it in the feature flag page if desired."
+ )
+ return
+
+ created = 0
+ for attendance in attendances:
+ GoogleCalendarEventManager.create_calendar_event(attendance)
+ created += 1
+ if created % 30 == 0:
+ print("Created " + str(created) + " events")
+
+ print("All events created")
From 1b127e3bf250b3f0d76e35ab84fd730b0b7a469e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Th=C3=A9ophile=20Madet?=
Date: Sat, 11 Oct 2025 12:09:04 +0200
Subject: [PATCH 24/27] fix: the shift detail pages now checks for capabilities
correctly.
---
tapir/coop/models.py | 20 ++++---
tapir/shifts/forms.py | 2 +-
tapir/shifts/models.py | 59 +++++++++++--------
.../shifts/templates/shifts/shift_detail.html | 8 +--
4 files changed, 50 insertions(+), 39 deletions(-)
diff --git a/tapir/coop/models.py b/tapir/coop/models.py
index 55a12513b..8eaabc4ed 100644
--- a/tapir/coop/models.py
+++ b/tapir/coop/models.py
@@ -1,6 +1,7 @@
import datetime
from typing import Self
+from django.conf import settings
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.db import models
@@ -261,16 +262,17 @@ def get_member_status(self, at_datetime: datetime.datetime | datetime.date = Non
at_datetime = ensure_datetime(at_datetime)
- if (
- not NumberOfSharesService.get_number_of_active_shares(
- self, at_datetime.date()
- )
- > 0
- ):
- return MemberStatus.SOLD
+ if not settings.SHIFTS_ONLY:
+ if (
+ not NumberOfSharesService.get_number_of_active_shares(
+ self, at_datetime.date()
+ )
+ > 0
+ ):
+ return MemberStatus.SOLD
- if InvestingStatusService.is_investing(self, at_datetime):
- return MemberStatus.INVESTING
+ if InvestingStatusService.is_investing(self, at_datetime):
+ return MemberStatus.INVESTING
if MembershipPauseService.has_active_pause(self, at_datetime.date()):
return MemberStatus.PAUSED
diff --git a/tapir/shifts/forms.py b/tapir/shifts/forms.py
index a445cc69f..707968146 100644
--- a/tapir/shifts/forms.py
+++ b/tapir/shifts/forms.py
@@ -339,7 +339,7 @@ def clean(self):
):
user_capabilities = set(user_to_register.shift_user_data.capabilities.all())
missing_capabilities = [
- capability
+ capability.get_current_translation().name
for capability in self.get_required_capabilities()
if capability not in user_capabilities
]
diff --git a/tapir/shifts/models.py b/tapir/shifts/models.py
index 43c0c51fe..aa4bc8261 100644
--- a/tapir/shifts/models.py
+++ b/tapir/shifts/models.py
@@ -343,7 +343,7 @@ def get_required_capabilities_dict(self: ShiftSlotTemplate | ShiftSlot):
returns required capabilities as dictionary with corresponding translation
"""
return {
- capability.id: capability.get_current_translation().name
+ capability: capability.get_current_translation().name
for capability in self.required_capabilities.all().prefetch_related(
"shiftusercapabilitytranslation_set"
)
@@ -698,33 +698,42 @@ def get_valid_attendance(self) -> ShiftAttendance:
return self.valid_attendance
def user_can_attend(self, user):
- return (
+ if (
+ self.get_valid_attendance() is not None
+ and self.get_valid_attendance().state
+ != ShiftAttendance.State.LOOKING_FOR_STAND_IN
+ ):
# Slot must not be attended yet
- (
- not self.get_valid_attendance()
- or self.get_valid_attendance().state
- == ShiftAttendance.State.LOOKING_FOR_STAND_IN
- )
- and
+ return False
+
+ if self.shift.get_attendances().filter(user=user).with_valid_state().exists():
# User isn't already registered for this shift
- not (
- self.shift.get_attendances()
- .filter(user=user)
- .with_valid_state()
- .exists()
- )
- and
+ return False
+
+ if not set(self.required_capabilities.all()).issubset(
+ set(user.shift_user_data.capabilities.all())
+ ):
# User must have all required capabilities
- (
- set(self.required_capabilities.all()).issubset(
- set(user.shift_user_data.capabilities.all())
- )
- )
- and self.shift.is_in_the_future()
- and not self.shift.cancelled
- and hasattr(user, "share_owner")
- and user.share_owner.is_active(self.shift.start_time)
- )
+ return False
+
+ if not self.shift.is_in_the_future():
+ return False
+
+ if self.shift.cancelled:
+ return False
+
+ if not hasattr(user, "share_owner") or user.share_owner is None:
+ return False
+
+ from tapir.coop.models import MemberStatus
+
+ if (
+ user.share_owner.get_member_status(self.shift.start_time)
+ != MemberStatus.ACTIVE
+ ):
+ return False
+
+ return True
def user_can_self_unregister(self, user: TapirUser) -> bool:
user_is_registered_to_slot = (
diff --git a/tapir/shifts/templates/shifts/shift_detail.html b/tapir/shifts/templates/shifts/shift_detail.html
index 2049505f2..e54dd980c 100644
--- a/tapir/shifts/templates/shifts/shift_detail.html
+++ b/tapir/shifts/templates/shifts/shift_detail.html
@@ -241,13 +241,13 @@
#{{ forloop.counter }}
{% endwith %}
- {% for key, value in slot.get_required_capabilities_dict.items %}
- {% if key in request.user.shift_user_data.capabilities %}
+ {% for capability, capability_name in slot.get_required_capabilities_dict.items %}
+ {% if capability in request.user.shift_user_data.capabilities.all %}
check{{ value }}
+ class="material-icons">check{{ capability_name }}
{% else %}
close{{ value }}
+ class="material-icons">close{{ capability_name }}
{% endif %}
{% empty %}
Date: Sat, 11 Oct 2025 12:23:35 +0200
Subject: [PATCH 25/27] feat: thresholds for self unregistering and self
looking for standins are now in hours instead of days and can be overriden
with env vars.
---
tapir/settings.py | 5 +++++
tapir/shifts/models.py | 14 +++++---------
tapir/shifts/templates/shifts/shift_detail.html | 2 +-
.../tests/test_member_self_look_for_stand_in.py | 6 +++---
tapir/shifts/tests/test_member_self_unregisters.py | 8 ++++----
tapir/shifts/views/calendars.py | 5 ++++-
tapir/shifts/views/views.py | 9 ++++++---
7 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/tapir/settings.py b/tapir/settings.py
index db276ddbe..d305f6923 100644
--- a/tapir/settings.py
+++ b/tapir/settings.py
@@ -465,3 +465,8 @@
PATH_TO_GOOGLE_CLIENT_SECRET_FILE = env.str(
"PATH_TO_GOOGLE_CLIENT_SECRET_FILE", default="google_client_secret_desktop.json"
)
+
+NB_HOURS_FOR_SELF_UNREGISTER = env.int("NB_HOURS_FOR_SELF_UNREGISTER", default=7 * 24)
+NB_HOURS_FOR_SELF_LOOK_FOR_STAND_IN = env.int(
+ "NB_HOURS_FOR_SELF_UNREGISTER", default=2 * 24
+)
diff --git a/tapir/shifts/models.py b/tapir/shifts/models.py
index aa4bc8261..252608379 100644
--- a/tapir/shifts/models.py
+++ b/tapir/shifts/models.py
@@ -5,6 +5,7 @@
from functools import cmp_to_key
from locale import strcoll
+from django.conf import settings
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.db import models, transaction
@@ -563,9 +564,6 @@ class Shift(models.Model):
null=True, max_length=1000, verbose_name=_("Cancellation reason")
)
- NB_DAYS_FOR_SELF_UNREGISTER = 7
- NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN = 2
-
def __str__(self):
display_name = "%s: %s %s-%s" % (
self.__class__.__name__,
@@ -745,9 +743,8 @@ def user_can_self_unregister(self, user: TapirUser) -> bool:
or not hasattr(self.slot_template, "attendance_template")
or not self.slot_template.attendance_template.user == user
)
- early_enough = (
- self.shift.start_time.date() - timezone.now().date()
- ).days >= Shift.NB_DAYS_FOR_SELF_UNREGISTER
+ hours = (self.shift.start_time - timezone.now()).total_seconds() / 3600
+ early_enough = hours >= settings.NB_HOURS_FOR_SELF_UNREGISTER
return (
user_is_registered_to_slot
and user_is_not_registered_to_slot_template
@@ -759,9 +756,8 @@ def user_can_look_for_standin(self, user: TapirUser) -> bool:
self.get_valid_attendance() is not None
and self.get_valid_attendance().user == user
)
- early_enough = (
- self.shift.start_time - timezone.now()
- ).days >= Shift.NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN
+ hours = (self.shift.start_time - timezone.now()).total_seconds() / 3600
+ early_enough = hours >= settings.NB_HOURS_FOR_SELF_LOOK_FOR_STAND_IN
return user_is_registered_to_slot and early_enough
def update_attendance_from_template(self):
diff --git a/tapir/shifts/templates/shifts/shift_detail.html b/tapir/shifts/templates/shifts/shift_detail.html
index e54dd980c..0bbd4804d 100644
--- a/tapir/shifts/templates/shifts/shift_detail.html
+++ b/tapir/shifts/templates/shifts/shift_detail.html
@@ -172,7 +172,7 @@
#{{ forloop.counter }}
{% blocktranslate asvar stand_in_tooltip %}You can only look for
a
stand-in
- {{ NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN }} days before the
+ {{ NB_HOURS_FOR_SELF_LOOK_FOR_STAND_IN }} hours before the
shift. If
you can't
attend, contact your shift leader as soon as
diff --git a/tapir/shifts/tests/test_member_self_look_for_stand_in.py b/tapir/shifts/tests/test_member_self_look_for_stand_in.py
index 6993a5010..d7abf82eb 100644
--- a/tapir/shifts/tests/test_member_self_look_for_stand_in.py
+++ b/tapir/shifts/tests/test_member_self_look_for_stand_in.py
@@ -1,5 +1,6 @@
import datetime
+from django.conf import settings
from django.core import mail
from django.urls import reverse
from django.utils import timezone
@@ -9,7 +10,6 @@
from tapir.shifts.models import (
ShiftSlot,
ShiftAttendance,
- Shift,
)
from tapir.shifts.tests.factories import ShiftFactory
from tapir.shifts.tests.utils import register_user_to_shift
@@ -22,7 +22,7 @@ class TestMemberSelfLookForStandIn(TapirFactoryTestBase, TapirEmailTestMixin):
def test_member_self_look_for_stand_in(self):
user = self.login_as_normal_user()
start_time = timezone.now() + datetime.timedelta(
- days=Shift.NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN, hours=1
+ hours=settings.NB_HOURS_FOR_SELF_LOOK_FOR_STAND_IN + 1
)
shift = ShiftFactory.create(start_time=start_time)
@@ -44,7 +44,7 @@ def test_member_self_look_for_stand_in(self):
def test_member_self_look_for_stand_in_threshold(self):
user = self.login_as_normal_user()
start_time = timezone.now() + datetime.timedelta(
- days=Shift.NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN, hours=-1
+ hours=settings.NB_HOURS_FOR_SELF_LOOK_FOR_STAND_IN - 1
)
shift = ShiftFactory.create(start_time=start_time)
diff --git a/tapir/shifts/tests/test_member_self_unregisters.py b/tapir/shifts/tests/test_member_self_unregisters.py
index ef64fb6be..6729bc52a 100644
--- a/tapir/shifts/tests/test_member_self_unregisters.py
+++ b/tapir/shifts/tests/test_member_self_unregisters.py
@@ -1,10 +1,10 @@
import datetime
+from django.conf import settings
from django.urls import reverse
from django.utils import timezone
from tapir.shifts.models import (
- Shift,
ShiftAttendance,
ShiftSlotTemplate,
ShiftAttendanceTemplate,
@@ -18,7 +18,7 @@ class TestMemberSelfUnregisters(TapirFactoryTestBase):
def test_member_self_unregisters(self):
user = self.login_as_normal_user(share_owner__is_investing=False)
start_time = timezone.now() + datetime.timedelta(
- days=Shift.NB_DAYS_FOR_SELF_UNREGISTER, hours=1
+ hours=settings.NB_HOURS_FOR_SELF_UNREGISTER + 1
)
shift = ShiftFactory.create(start_time=start_time)
@@ -45,7 +45,7 @@ def test_member_self_unregisters(self):
def test_member_self_unregisters_threshold(self):
user = self.login_as_normal_user(share_owner__is_investing=False)
start_time = timezone.now() + datetime.timedelta(
- days=Shift.NB_DAYS_FOR_SELF_UNREGISTER - 1
+ hours=settings.NB_HOURS_FOR_SELF_UNREGISTER - 1
)
shift = ShiftFactory.create(start_time=start_time)
@@ -73,7 +73,7 @@ def test_member_self_unregisters_abcd(self):
ShiftAttendanceTemplate.objects.create(slot_template=slot_template, user=user)
shift = shift_template.create_shift(
timezone.now()
- + datetime.timedelta(days=Shift.NB_DAYS_FOR_SELF_UNREGISTER + 1)
+ + datetime.timedelta(hours=settings.NB_HOURS_FOR_SELF_UNREGISTER + 1)
)
attendance = ShiftAttendance.objects.get(slot__shift=shift, user=user)
diff --git a/tapir/shifts/views/calendars.py b/tapir/shifts/views/calendars.py
index 0cdd997db..e6b5573fa 100644
--- a/tapir/shifts/views/calendars.py
+++ b/tapir/shifts/views/calendars.py
@@ -2,6 +2,7 @@
from calendar import MONDAY
from collections import OrderedDict
+from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponse
from django.utils import timezone
@@ -45,7 +46,9 @@ def get_context_data(self, *args, **kwargs):
context_data["date_from"] = date_from.strftime(self.DATE_FORMAT)
context_data["date_to"] = date_to.strftime(self.DATE_FORMAT)
- context_data["nb_days_for_self_unregister"] = Shift.NB_DAYS_FOR_SELF_UNREGISTER
+ context_data["nb_days_for_self_unregister"] = int(
+ settings.NB_HOURS_FOR_SELF_UNREGISTER / 24
+ )
# Because the shift views show a lot of shifts,
# we preload all related objects to avoid doing many database requests.
shifts = (
diff --git a/tapir/shifts/views/views.py b/tapir/shifts/views/views.py
index 2efe53bda..b9a756c96 100644
--- a/tapir/shifts/views/views.py
+++ b/tapir/shifts/views/views.py
@@ -1,6 +1,7 @@
import datetime
import django_tables2
+from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.core.management import call_command
@@ -255,9 +256,11 @@ def get_context_data(self, **kwargs):
context["slots"] = slots
context["attendance_states"] = ShiftAttendance.State
- context["NB_DAYS_FOR_SELF_UNREGISTER"] = Shift.NB_DAYS_FOR_SELF_UNREGISTER
- context["NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN"] = (
- Shift.NB_DAYS_FOR_SELF_LOOK_FOR_STAND_IN
+ context["NB_DAYS_FOR_SELF_UNREGISTER"] = int(
+ settings.NB_HOURS_FOR_SELF_UNREGISTER / 24
+ )
+ context["NB_HOURS_FOR_SELF_LOOK_FOR_STAND_IN"] = (
+ settings.NB_HOURS_FOR_SELF_LOOK_FOR_STAND_IN
)
context["SHIFT_ATTENDANCE_STATES"] = SHIFT_ATTENDANCE_STATES
return context
From b68e5e2348c8d0d47346ef280eb249c54213ffee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Th=C3=A9ophile=20Madet?=
Date: Sat, 11 Oct 2025 12:29:54 +0200
Subject: [PATCH 26/27] feat: ABCD users can unregister from their not-ABCD
shifts but only in Rizoma mode.
---
tapir/shifts/models.py | 26 ++++++++++++++++----------
1 file changed, 16 insertions(+), 10 deletions(-)
diff --git a/tapir/shifts/models.py b/tapir/shifts/models.py
index 252608379..2a6d7a6c7 100644
--- a/tapir/shifts/models.py
+++ b/tapir/shifts/models.py
@@ -738,18 +738,24 @@ def user_can_self_unregister(self, user: TapirUser) -> bool:
self.get_valid_attendance() is not None
and self.get_valid_attendance().user == user
)
- user_is_not_registered_to_slot_template = (
- self.slot_template is None
- or not hasattr(self.slot_template, "attendance_template")
- or not self.slot_template.attendance_template.user == user
- )
+ if not user_is_registered_to_slot:
+ return False
+
+ if not settings.ENABLE_RIZOMA_CONTENT:
+ user_is_registered_to_slot_template = (
+ self.slot_template is not None
+ and hasattr(self.slot_template, "attendance_template")
+ and self.slot_template.attendance_template.user == user
+ )
+ if user_is_registered_to_slot_template:
+ return False
+
hours = (self.shift.start_time - timezone.now()).total_seconds() / 3600
early_enough = hours >= settings.NB_HOURS_FOR_SELF_UNREGISTER
- return (
- user_is_registered_to_slot
- and user_is_not_registered_to_slot_template
- and early_enough
- )
+ if not early_enough:
+ return False
+
+ return True
def user_can_look_for_standin(self, user: TapirUser) -> bool:
user_is_registered_to_slot = (
From cb88f23b0a777fbabe2a87794026c688d06caad7 Mon Sep 17 00:00:00 2001
From: Emanuel Saramago
Date: Fri, 10 Oct 2025 10:20:58 +0000
Subject: [PATCH 27/27] Translated using Weblate (Portuguese)
Currently translated at 93.8% (910 of 970 strings)
Translation: tapir/tapir python translations
Translate-URL: http://weblate.seriousdino.org/projects/tapir/tapir-python-translations/pt/
---
.../locale/pt/LC_MESSAGES/django.po | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/tapir/translations/locale/pt/LC_MESSAGES/django.po b/tapir/translations/locale/pt/LC_MESSAGES/django.po
index b8184066a..2f00e1858 100644
--- a/tapir/translations/locale/pt/LC_MESSAGES/django.po
+++ b/tapir/translations/locale/pt/LC_MESSAGES/django.po
@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-28 12:56+0200\n"
-"PO-Revision-Date: 2025-10-08 17:59+0000\n"
-"Last-Translator: Lara Carvalho \n"
+"PO-Revision-Date: 2025-10-11 10:59+0000\n"
+"Last-Translator: Emanuel Saramago \n"
"Language-Team: Portuguese \n"
"Language: pt\n"
@@ -35,11 +35,11 @@ msgstr "Este nome de utilizador não está disponível."
#: accounts/forms.py:126
msgid "Optional Mails"
-msgstr "Emails opcionais"
+msgstr "E-mails opcionais"
#: accounts/forms.py:133
msgid "Important Mails"
-msgstr "Emails importantes"
+msgstr "E-mails importantes"
#: accounts/models.py:59
msgid "Displayed name"
@@ -90,7 +90,7 @@ msgstr "Co-comprador"
#: accounts/models.py:74
msgid "Allow purchase tracking"
-msgstr "Permitir rastreamento de compras"
+msgstr "Permitir acompanhamento de compras"
#: accounts/models.py:79 accounts/templates/accounts/user_detail.html:105
#: coop/models.py:81 coop/models.py:457
@@ -208,7 +208,7 @@ msgstr "Grupos"
#: accounts/templates/accounts/purchase_tracking_card.html:5
msgid "Purchase tracking"
-msgstr "Rastreamento de compras"
+msgstr "Acompanhamento de compras"
#: accounts/templates/accounts/purchase_tracking_card.html:11
msgid "Get barcode as PDF"
@@ -251,7 +251,7 @@ msgstr "Dados pessoais"
#: accounts/templates/accounts/user_detail.html:32
msgid "Mails"
-msgstr "Emails"
+msgstr "E-mails"
#: accounts/templates/accounts/user_detail.html:37
msgid "Edit groups"
@@ -632,7 +632,6 @@ msgstr ""
"patrocinador)"
#: coop/forms.py:123
-#, fuzzy
msgid "Note: Investing members are sponsoring members. They have no voting rights in the General Assembly and cannot use the services of the cooperative that are exclusive to ordinary members. "
msgstr ""
"Nota: Os membros investidores são membros patrocinadores. Não têm "
@@ -676,6 +675,10 @@ msgstr "Estado do membro"
#: coop/forms.py:291
msgid "In the case where the member wants their money back, they stay a member for 3 more years. However, it is very likely that the member doesn't want to be active anymore. If they haven't explicitly mentioned it, please ask them if we can switch them to investing."
msgstr ""
+"No caso de o membro desejar o seu dinheiro de volta, ele permanecerá como "
+"membro por mais 3 anos. No entanto, é muito provável que o membro não queira "
+"mais ser ativo. Se ele não tiver mencionado isso explicitamente, pergunte se "
+"podemos transferi-lo para a modalidade de investimento."
#: coop/forms.py:316
msgid "Send confirmation mails"