diff --git a/README.md b/README.md index 7d3387b..c3aed71 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ - check products - Publish +## Only show productors / products / forms for referent of type + ## Footer ### Legal diff --git a/backend/alembic/versions/e777ed5729ce_message.py b/backend/alembic/versions/e777ed5729ce_message.py new file mode 100644 index 0000000..d2fa32c --- /dev/null +++ b/backend/alembic/versions/e777ed5729ce_message.py @@ -0,0 +1,33 @@ +"""message + +Revision ID: e777ed5729ce +Revises: 7854064278ce +Create Date: 2026-02-23 13:53:09.999893 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + +import sqlmodel.sql.sqltypes + +# revision identifiers, used by Alembic. +revision: str = 'e777ed5729ce' +down_revision: Union[str, Sequence[str], None] = '7854064278ce' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('form', sa.Column('visible', sa.Boolean(), nullable=False, default=False, server_default="False")) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('form', 'visible') + # ### end Alembic commands ### diff --git a/backend/src/forms/forms.py b/backend/src/forms/forms.py index a30eafc..2254259 100644 --- a/backend/src/forms/forms.py +++ b/backend/src/forms/forms.py @@ -13,10 +13,20 @@ async def get_forms( seasons: list[str] = Query([]), productors: list[str] = Query([]), current_season: bool = False, - session: Session = Depends(get_session) + session: Session = Depends(get_session), ): return service.get_all(session, seasons, productors, current_season) +@router.get('/referents', response_model=list[models.FormPublic]) +async def get_forms_filtered( + seasons: list[str] = Query([]), + productors: list[str] = Query([]), + current_season: bool = False, + session: Session = Depends(get_session), + user: models.User = Depends(get_current_user) +): + return service.get_all(session, seasons, productors, current_season, user) + @router.get('/{id}', response_model=models.FormPublic) async def get_form(id: int, session: Session = Depends(get_session)): result = service.get_one(session, id) diff --git a/backend/src/forms/service.py b/backend/src/forms/service.py index 5156776..6a49f64 100644 --- a/backend/src/forms/service.py +++ b/backend/src/forms/service.py @@ -7,12 +7,20 @@ def get_all( seasons: list[str], productors: list[str], current_season: bool, + user: models.User = None ) -> list[models.FormPublic]: statement = select(models.Form) + if user: + statement = statement\ + .join(models.Productor, models.Form.productor_id == models.Productor.id)\ + .where(models.Productor.type.in_([r.name for r in user.roles]))\ + .distinct() if len(seasons) > 0: statement = statement.where(models.Form.season.in_(seasons)) if len(productors) > 0: statement = statement.join(models.Productor).where(models.Productor.name.in_(productors)) + if not user: + statement = statement.where(models.Form.visible == True) if current_season: subquery = ( select( @@ -29,6 +37,8 @@ def get_all( (models.Productor.type == subquery.c.type) & (models.Form.start == subquery.c.max_start) ) + if not user: + statement = statement.where(models.Form.visible == True) return session.exec(statement.order_by(models.Form.name)).all() return session.exec(statement.order_by(models.Form.name)).all() diff --git a/backend/src/models.py b/backend/src/models.py index 9ce18fd..fa25ee4 100644 --- a/backend/src/models.py +++ b/backend/src/models.py @@ -136,6 +136,7 @@ class FormBase(SQLModel): start: datetime.date end: datetime.date minimum_shipment_value: float | None + visible: bool class FormPublic(FormBase): id: int @@ -167,6 +168,7 @@ class FormUpdate(SQLModel): start: datetime.date | None end: datetime.date | None minimum_shipment_value: float | None + visible: bool | None class FormCreate(FormBase): pass diff --git a/backend/src/productors/productors.py b/backend/src/productors/productors.py index cd24e23..4e89a81 100644 --- a/backend/src/productors/productors.py +++ b/backend/src/productors/productors.py @@ -15,7 +15,7 @@ def get_productors( user: models.User = Depends(get_current_user), session: Session = Depends(get_session) ): - return service.get_all(session, names, types) + return service.get_all(session, user, names, types) @router.get('/{id}', response_model=models.ProductorPublic) def get_productor( diff --git a/backend/src/productors/service.py b/backend/src/productors/service.py index 89a8a0a..dd7a822 100644 --- a/backend/src/productors/service.py +++ b/backend/src/productors/service.py @@ -2,11 +2,14 @@ from sqlmodel import Session, select import src.models as models def get_all( - session: Session, + session: Session, + user: models.User, names: list[str], types: list[str] ) -> list[models.ProductorPublic]: - statement = select(models.Productor) + statement = select(models.Productor)\ + .where(models.Productor.type.in_([r.name for r in user.roles]))\ + .distinct() if len(names) > 0: statement = statement.where(models.Productor.name.in_(names)) if len(types) > 0: diff --git a/backend/src/products/products.py b/backend/src/products/products.py index 7f212b2..b7dced1 100644 --- a/backend/src/products/products.py +++ b/backend/src/products/products.py @@ -16,7 +16,8 @@ def get_products( productors: list[str] = Query([]), ): return service.get_all( - session, + session, + user, names, productors, types, diff --git a/backend/src/products/service.py b/backend/src/products/service.py index ae91f28..b898cda 100644 --- a/backend/src/products/service.py +++ b/backend/src/products/service.py @@ -3,11 +3,15 @@ import src.models as models def get_all( session: Session, + user: models.User, names: list[str], productors: list[str], types: list[str], ) -> list[models.ProductPublic]: - statement = select(models.Product) + statement = select(models.Product)\ + .join(models.Productor, models.Product.productor_id == models.Productor.id)\ + .where(models.Productor.type.in_([r.name for r in user.roles]))\ + .distinct() if len(names) > 0: statement = statement.where(models.Product.name.in_(names)) if len(productors) > 0: diff --git a/backend/src/shipments/service.py b/backend/src/shipments/service.py index e5a8a1f..5158fd4 100644 --- a/backend/src/shipments/service.py +++ b/backend/src/shipments/service.py @@ -3,11 +3,16 @@ import src.models as models def get_all( session: Session, + user: models.User, names: list[str], dates: list[str], forms: list[int] ) -> list[models.ShipmentPublic]: - statement = select(models.Shipment) + statement = select(models.Shipment)\ + .join(models.Form, models.Shipment.form_id == models.Form.id)\ + .join(models.Productor, models.Form.productor_id == models.Productor.id)\ + .where(models.Productor.type.in_([r.name for r in user.roles]))\ + .distinct() if len(names) > 0: statement = statement.where(models.Shipment.name.in_(names)) if len(dates) > 0: diff --git a/backend/src/shipments/shipments.py b/backend/src/shipments/shipments.py index 91f798b..3a4c85d 100644 --- a/backend/src/shipments/shipments.py +++ b/backend/src/shipments/shipments.py @@ -11,12 +11,14 @@ router = APIRouter(prefix='/shipments') @router.get('', response_model=list[models.ShipmentPublic], ) def get_shipments( session: Session = Depends(get_session), + user: models.User = Depends(get_current_user), names: list[str] = Query([]), dates: list[str] = Query([]), forms: list[str] = Query([]), ): return service.get_all( session, + user, names, dates, forms, diff --git a/frontend/locales/en.json b/frontend/locales/en.json index 22bd8f1..02fe189 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -82,7 +82,10 @@ "you can download all contracts for your form using the export all": "you can download all contracts for your form using the export all", "in the same corner you can download a recap by clicking on the button": "in the same corner you can download a recap by clicking on the", "once all contracts downloaded, you can delete the form (to avoid new submissions) and hide it from the home page": "once all contracts downloaded, you can delete the form (to avoid new submissions) and hide it from the home page", + "by checking this option the form will be accessible publicly on the home page, only check it if everything is fine with your form": "by checking this option the form will be accessible publicly on the home page, only check it if everything is fine with your form", "contracts": "contracts", + "hidden": "hidden", + "visible": "visible", "minimum price for this shipment should be at least": "minimum price for this shipment should be at least", "there is": "there is", "for this contract": "for this contract.", diff --git a/frontend/locales/fr.json b/frontend/locales/fr.json index e0ae3f8..72bd885 100644 --- a/frontend/locales/fr.json +++ b/frontend/locales/fr.json @@ -73,7 +73,10 @@ "shipment products is necessary only for occasional products (if all products are recurrent leave empty)": "il est nécessaire de configurer les produits pour la livraison uniquement si il y a des produits occasionnels (laisser vide si tous les produits sont récurents).", "recurrent product is for all shipments, occasional product is for a specific shipment (see shipment form)": "les produits récurrents sont pour toutes les livraisons, les produits occasionnels sont pour une livraison particulière (voir formulaire de création de livraison).", "some contracts require a minimum value per shipment, ignore this field if it's not the case": "certains contrats nécessitent une valeur minimum par livraison. Ce champ peut être ignoré s’il ne s’applique pas à votre contrat.", + "by checking this option the form will be accessible publicly on the home page, only check it if everything is fine with your form": "en cochant cette option le formulaire sera accessible publiquement sur la page d'accueil, cochez cette option uniquement si tout est prêt avec votre formulaire.", "contracts": "contrats", + "hidden": "caché", + "visible": "visible", "minimum price for this shipment should be at least": "le prix minimum d'une livraison doit être au moins de", "there is": "il y a", "for this contract": "pour ce contrat.", @@ -166,7 +169,7 @@ "with cheque and transfer": "avec chèques et virements configuré pour le producteur", "mililiter": "mililitres (ml)", "this field is optionnal a product can have a quantity if configured inside the product it will be shown inside the form": "ce champ est optionnel dans la configuration d'un produit, il représente la quantité d'un produit (poids d'une tranche de foie, poids d'un panier, taille d'un bocal...). Si ce champs est renseigné il sera affiché dans le formulaire à destination des amapiens.", - "this field is also optionnal if a product have a quantity you can select the correct unit (metric system). It will be shown next to product quantity inside the form": "ce champs est optionnel dans la configuation d'un produit, il représente l'unité de mesure associé à la quantité d'un produit (g, kg, ml, L). Si ce champs est renseigné il sera affiché dans le formulaire à destination des amapiens à coté de la quantité du produit.", + "this field is also optionnal if a product have a quantity you can select the correct unit (metric system). It will be shown next to product quantity inside the form": "ce champs est optionnel dans la configuation d'un produit, il représente l'unité de mesure associée à la quantité d'un produit (g, kg, ml, L). Si ce champs est renseigné il sera affiché dans le formulaire à destination des amapiens à coté de la quantité du produit.", "with 150 set as quantity and g as quantity unit in product": "avec \"150\" en quantité de produit et \"grammes\" selectionné dans l'unité de quantité du produit", "all shipments should be recreated for each form creation": "les livraisons étant liées à un formulaire elles doivent être recréés pour chaque nouveau formulaire.", "a productor can be edited if its informations change, it should not be recreated for each contracts": "un(e) producteur·trice peut être édité si ses informations changent, il/elle ne doit pas être recréé pour chaque nouveau contrat.", diff --git a/frontend/src/components/Forms/Modal/index.tsx b/frontend/src/components/Forms/Modal/index.tsx index 36754dd..869fa49 100644 --- a/frontend/src/components/Forms/Modal/index.tsx +++ b/frontend/src/components/Forms/Modal/index.tsx @@ -1,5 +1,6 @@ import { Button, + Checkbox, Group, Modal, NumberInput, @@ -33,6 +34,7 @@ export default function FormModal({ opened, onClose, currentForm, handleSubmit } productor_id: currentForm?.productor?.id.toString() ?? "", referer_id: currentForm?.referer?.id.toString() ?? "", minimum_shipment_value: currentForm?.minimum_shipment_value ?? null, + visible: currentForm?.visible ?? false }, validate: { name: (value) => @@ -136,6 +138,11 @@ export default function FormModal({ opened, onClose, currentForm, handleSubmit } radius="sm" {...form.getInputProps("minimum_shipment_value")} /> +