diff --git a/backend/src/contracts/contracts.py b/backend/src/contracts/contracts.py index 1f53ae8..940fd5e 100644 --- a/backend/src/contracts/contracts.py +++ b/backend/src/contracts/contracts.py @@ -105,6 +105,46 @@ async def create_contract( } ) +@router.get('/{form_id}/base') +async def get_base_contract_template( + form_id: int, + session: Session = Depends(get_session), +): + form = form_service.get_one(session, form_id) + recurrents = [pr for pr in form.productor.products if pr.type == models.ProductType.RECCURENT] + occasionals = [{ + 'shipment': sh, + 'price': None, + 'products': [{'product': pr, 'quantity': None} for pr in sh.products] + } for sh in form.shipments] + empty_contract = models.Contract( + firstname="", + form=form, + lastname="", + email="", + phone="", + payment_method="cheque" + ) + cheques = [{"name": None, "value": None}, {"name": None, "value": None}, {"name": None, "value": None}] + try: + pdf_bytes = generate_html_contract( + empty_contract, + cheques, + occasionals, + recurrents, + ) + pdf_file = io.BytesIO(pdf_bytes) + contract_id = f'{empty_contract.form.productor.type}_{empty_contract.form.season}' + except Exception: + raise HTTPException(status_code=400, detail=messages.pdferror) + return StreamingResponse( + pdf_file, + media_type='application/pdf', + headers={ + 'Content-Disposition': f'attachment; filename=contract_{contract_id}.pdf' + } + ) + @router.get('', response_model=list[models.ContractPublic]) def get_contracts( forms: list[str] = Query([]), diff --git a/backend/src/contracts/generate_contract.py b/backend/src/contracts/generate_contract.py index 6adbd48..735b405 100644 --- a/backend/src/contracts/generate_contract.py +++ b/backend/src/contracts/generate_contract.py @@ -12,8 +12,8 @@ def generate_html_contract( cheques: list[dict], occasionals: list[dict], reccurents: list[dict], - recurrent_price: float, - total_price: float + recurrent_price: float | None = None, + total_price: float | None = None ): template_dir = pathlib.Path("./src/contracts/templates").resolve() template_loader = jinja2.FileSystemLoader(searchpath=template_dir) diff --git a/backend/src/contracts/templates/layout.html b/backend/src/contracts/templates/layout.html index 2585dfd..79cafff 100644 --- a/backend/src/contracts/templates/layout.html +++ b/backend/src/contracts/templates/layout.html @@ -281,7 +281,7 @@ {% endfor %} Total - {{recurrent_price}}€ + {{recurrent_price if recurrent_price else ""}}€ @@ -317,14 +317,15 @@ product.product.quantity_unit != None else ""}} - {{product.quantity}}{{"g" if product.product.unit == "1" else + {{product.product.quantity if product.product.quantity != None + else ""}}{{"g" if product.product.unit == "1" else "kg" if product.product.unit == "2" else "p" }} {% endfor%} Total - {{occasional.price}}€ + {{occasional.price if occasional.price else ""}}€ @@ -333,7 +334,7 @@ {% endif %}
Prix Total :
-
{{total_price}}€
+
{{total_price if total_price else ""}}€

Paiement par {{contract_payment_method}}

{% if contract_payment_method == "chèque" %} @@ -342,14 +343,14 @@ {% for cheque in cheques %} - Cheque n°{{cheque.name}} + Cheque n°{{cheque.name if cheque.name else ""}} {% endfor %} {% for cheque in cheques %} - {{cheque.value}}€ + {{cheque.value if cheque.value else ""}}€ {% endfor %} diff --git a/frontend/locales/en.json b/frontend/locales/en.json index 99c9744..5e00fa2 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -75,6 +75,8 @@ "some contracts require a minimum value per shipment, ignore this field if it's not the case": "some contracts require a minimum value per shipment. Ignore this field if it does not apply to your contract.", "export contracts": "export contracts", "download recap": "download recap", + "fill contract online": "fill contract online", + "download base template to print": "download base template to print", "to export contracts submissions before sending to the productor go to the contracts section": "to export contracts submissions before sending to the productor go to the contracts section.", "in this page you can view all contracts submissions, you can remove duplicates submission or download a specific contract": "in this page you can view all contracts submissions, you can remove duplicates submission or download a specific contract", "you can download all contracts for your form using the export all": "you can download all contracts for your form using the export all", diff --git a/frontend/locales/fr.json b/frontend/locales/fr.json index cc60ae3..d4ee378 100644 --- a/frontend/locales/fr.json +++ b/frontend/locales/fr.json @@ -89,6 +89,8 @@ "all contracts": "tous les contrats", "remove contract": "supprimer le contrat", "download contract": "télécharger le contrat", + "fill contract online": "remplir le contrat en ligne", + "download base template to print": "télécharger le contrat à remplir sur papier", "by selecting a form here you can download all contracts of your form": "en selectionnant un formulaire, vous téléchargez tous les contrats pour un formulaire donné.", "edit user": "modifier l'utilisateur·trice", "remove user": "supprimer l'utilisateur·trice", diff --git a/frontend/src/components/Forms/Card/index.tsx b/frontend/src/components/Forms/Card/index.tsx index 511c55c..cbeda7a 100644 --- a/frontend/src/components/Forms/Card/index.tsx +++ b/frontend/src/components/Forms/Card/index.tsx @@ -1,17 +1,53 @@ -import { Badge, Box, Group, Paper, Text, Title } from "@mantine/core"; +import { ActionIcon, Badge, Box, Group, Paper, Text, Title, Tooltip } from "@mantine/core"; import { Link } from "react-router"; import type { Form } from "@/services/resources/forms"; +import { IconDownload, IconExternalLink, IconLink } from "@tabler/icons-react"; +import { t } from "@/config/i18n"; +import { useGetContractFileTemplate } from "@/services/api"; export type FormCardProps = { form: Form; }; export function FormCard({ form }: FormCardProps) { + const contractBaseTemplate = useGetContractFileTemplate() return ( + + + { + await contractBaseTemplate.mutateAsync(form.id) + }} + > + + + + + + + + + + diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts index 819eb6e..8dea5f0 100644 --- a/frontend/src/services/api.ts +++ b/frontend/src/services/api.ts @@ -711,6 +711,34 @@ export function useGetContractFile() { }); } + +export function useGetContractFileTemplate() { + return useMutation({ + mutationFn: async (form_id: number) => { + const res = await fetchWithAuth(`${Config.backend_uri}/contracts/${form_id}/base`) + .then((res) => res); + + if (!res.ok) throw new Error(); + const blob = await res.blob(); + const disposition = res.headers.get("Content-Disposition"); + const filename = + disposition && disposition?.includes("filename=") + ? disposition.split("filename=")[1].replace(/"/g, "") + : `contract_${form_id}.pdf`; + return { blob, filename }; + }, + onSuccess: ({ blob, filename }) => { + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = filename; + link.click(); + URL.revokeObjectURL(url); + }, + }); +} + + export function useGetRecap() { return useMutation({ mutationFn: async (form_id: number) => {