add(remaining): add a way to generate contracts only with remaining shipments
All checks were successful
Deploy Amap / deploy (push) Successful in 41s

This commit is contained in:
Julien Aldon
2026-04-17 13:33:07 +02:00
parent 4b57b29b34
commit 0c4f499878
5 changed files with 57 additions and 8 deletions

View File

@@ -1,9 +1,11 @@
"""Router for contract resource"""
import datetime
import io
import zipfile
import src.contracts.service as service
import src.forms.service as form_service
import src.shipments.service as shipment_service
from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.responses import StreamingResponse
from sqlmodel import Session
@@ -44,10 +46,15 @@ async def create_contract(
)
)
)
remaining_shipments = shipment_service.get_remaining_shipments(
session,
new_contract.form.id,
datetime.datetime.today()
)
prices = service.generate_products_prices(
occasionals,
recurrents,
new_contract.form.shipments
remaining_shipments
)
recurrent_price = prices['recurrent']
total_price = prices['total']
@@ -111,7 +118,7 @@ async def get_base_contract_template(
cheque_payment_method = list(filter(
lambda x: x.name == "cheque", form.productor.payment_methods))
cheque_number = cheque_payment_method[0].max if len(
cheque_payment_method) > 0 else 3
cheque_payment_method) > 0 and cheque_payment_method[0].max is not None else 3
print(cheque_number, cheque_payment_method)
empty_contract = models.ContractPublic(
firstname='',

View File

@@ -49,6 +49,19 @@ def get_one(session: Session, shipment_id: int) -> models.ShipmentPublic:
return session.get(models.Shipment, shipment_id)
def get_remaining_shipments(session: Session, form_id: int, date: datetime.date) -> list[models.ShipmentPublic]:
statement = (
select(models.Shipment)
.join(
models.Form,
models.Shipment.form_id == models.Form.id
)
.where(models.Form.id == form_id)
.where(models.Shipment.date >= date)
)
return session.exec(statement.order_by(models.Shipment.name)).all()
def create_one(
session: Session,
shipment: models.ShipmentCreate) -> models.ShipmentPublic:

View File

@@ -94,7 +94,7 @@
"filter by productor": "filter by producer",
"filter by season": "filter by season",
"filter by type": "filter by type",
"for this contract": "for this contract.",
"for this contract": "for this contract",
"for transfer method contact your referer or productor": "for bank transfer, contact your referent or producer.",
"form": "contract form",
"form name": "contract form name",
@@ -184,6 +184,8 @@
"shipment products": "shipment products",
"shipment products is necessary only for occasional products (if all products are recurrent leave empty)": "shipment products configuration is only necessary for occasional products (leave empty if all products are recurrent).",
"shipments": "shipments",
"remaining_shipments_one": "remaining shipment",
"remaining_shipments_other": "remaining shipments",
"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.",
"start": "start",
"start date": "start date",

View File

@@ -94,7 +94,7 @@
"filter by productor": "filtrer par producteur·trice",
"filter by season": "filtrer par saisons",
"filter by type": "filtrer par type",
"for this contract": "pour ce contrat.",
"for this contract": "pour ce contrat",
"for transfer method contact your referer or productor": "pour mettre en place le virement automatique, contactez votre référent ou le producteur.",
"form": "formulaire de contrat",
"form name": "nom du formulaire de contrat",
@@ -184,6 +184,8 @@
"shipment products": "produits pour la livraison",
"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).",
"shipments": "livraisons",
"remaining_shipments_one": "livraison restante",
"remaining_shipments_other": "livraisons restantes",
"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é sil ne sapplique pas à votre contrat.",
"start": "début",
"start date": "date de début",

View File

@@ -24,6 +24,7 @@ import { useParams } from "react-router";
import { computePrices } from "./price";
import { ContractCheque } from "@/components/PaymentMethods/Cheque";
import { tranformProducts, type ContractInputs } from "@/services/resources/contracts";
import type { Shipment } from "@/services/resources/shipments";
export function Contract() {
const { id } = useParams();
@@ -71,12 +72,22 @@ export function Contract() {
return form?.productor?.products;
}, [form]);
const remainingShipments = useMemo(() => {
const currentDate = new Date();
return form?.shipments.reduce((acc, shipment) => {
if (new Date(shipment.date) > currentDate) {
return [...acc, shipment];
}
return acc;
}, [] as Shipment[])
}, [form])
const price = useMemo(() => {
if (!allProducts) {
return 0;
}
const productValues = Object.entries(inputForm.getValues().products);
return computePrices(productValues, allProducts, form?.shipments.length);
return computePrices(productValues, allProducts, remainingShipments?.length);
}, [inputForm, allProducts, form?.shipments]);
const inputRefs = useRef<Record<string, HTMLInputElement | null>>({
@@ -229,7 +240,7 @@ export function Contract() {
/>
</Group>
<Title order={3}>{t("shipments", { capfirst: true })}</Title>
<Text>{`${t("there is", { capfirst: true })} ${shipments.length} ${shipments.length > 1 ? t("shipments") : t("shipment")} ${t("for this contract")}`}</Text>
<Text>{`${t("there is", { capfirst: true })} ${shipments.length} ${shipments.length > 1 ? t("shipments") : t("shipment")} ${t("for this contract")} :`}</Text>
<List>
{shipments.map((shipment) => (
<List.Item key={shipment.id}>
@@ -243,6 +254,20 @@ export function Contract() {
</List.Item>
))}
</List>
<Text>{`${t("there is", { capfirst: true })} ${remainingShipments?.length} ${t("remaining_shipments", {count: remainingShipments?.length})} :`}</Text>
<List>
{remainingShipments?.map((shipment) => (
<List.Item key={shipment.id}>
{`${shipment.name} :
${new Date(shipment.date).toLocaleDateString("fr-FR", {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
})}`}
</List.Item>
))}
</List>
{productsRecurent.length > 0 ? (
<>
<Title order={3}>{t("recurrent products", { capfirst: true })}</Title>
@@ -256,12 +281,12 @@ export function Contract() {
))}
</>
) : null}
{shipments.some((shipment) => shipment.products.length > 0) ? (
{remainingShipments?.some((shipment) => shipment.products.length > 0) ? (
<>
<Title order={3}>{t("occasional products", { capfirst: true })}</Title>
<Text>{t("select products per shipment", { capfirst: true })}</Text>
<Accordion defaultValue={"0"}>
{shipments.map((shipment, index) => (
{remainingShipments?.map((shipment, index) => (
<ShipmentForm
minimumPrice={form.minimum_shipment_value}
shipment={shipment}