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) => {