add contract pdf generation

This commit is contained in:
2026-02-14 23:59:44 +01:00
parent 7e42fbe106
commit f440cef59e
42 changed files with 1299 additions and 123 deletions

View File

@@ -5,6 +5,9 @@ import type { Shipment, ShipmentCreate, ShipmentEditPayload } from "@/services/r
import type { Productor, ProductorCreate, ProductorEditPayload } from "@/services/resources/productors";
import type { User, UserCreate, UserEditPayload } from "@/services/resources/users";
import type { Product, ProductCreate, ProductEditPayload } from "./resources/products";
import type { ContractCreate } from "./resources/contracts";
import { notifications } from "@mantine/notifications";
import { t } from "@/config/i18n";
export function getShipments(filters?: URLSearchParams): UseQueryResult<Shipment[], Error> {
const queryString = filters?.toString()
@@ -43,7 +46,18 @@ export function createShipment() {
}).then((res) => res.json());
},
onSuccess: async () => {
notifications.show({
title: t("success", {capfirst: true}),
message: t("successfully created shipment", {capfirst: true}),
});
await queryClient.invalidateQueries({ queryKey: ['shipments'] })
},
onError: (error: any) => {
notifications.show({
title: t("error", {capfirst: true}),
message: error?.message || t(`error editing shipment`, {capfirst: true}),
color: "red"
});
}
})
}
@@ -62,7 +76,18 @@ export function editShipment() {
}).then((res) => res.json());
},
onSuccess: async () => {
notifications.show({
title: t("success", {capfirst: true}),
message: t("successfully edited shipment", {capfirst: true}),
});
await queryClient.invalidateQueries({ queryKey: ['shipments'] })
},
onError: (error: any) => {
notifications.show({
title: t("error", {capfirst: true}),
message: error?.message || t(`error editing shipment`, {capfirst: true}),
color: "red"
});
}
})
}
@@ -79,7 +104,18 @@ export function deleteShipment() {
}).then((res) => res.json());
},
onSuccess: async () => {
notifications.show({
title: t("success", {capfirst: true}),
message: t("successfully deleted shipment", {capfirst: true}),
});
await queryClient.invalidateQueries({ queryKey: ['shipments'] })
},
onError: (error: any) => {
notifications.show({
title: t("error", {capfirst: true}),
message: error?.message || t(`error deleting shipment`, {capfirst: true}),
color: "red"
});
}
});
}
@@ -121,7 +157,18 @@ export function createProductor() {
}).then((res) => res.json());
},
onSuccess: async () => {
notifications.show({
title: t("success", {capfirst: true}),
message: t("successfully created productor", {capfirst: true}),
});
await queryClient.invalidateQueries({ queryKey: ['productors'] })
},
onError: (error: any) => {
notifications.show({
title: t("error", {capfirst: true}),
message: error?.message || t(`error editing productor`, {capfirst: true}),
color: "red"
});
}
})
}
@@ -140,7 +187,18 @@ export function editProductor() {
}).then((res) => res.json());
},
onSuccess: async () => {
notifications.show({
title: t("success", {capfirst: true}),
message: t("successfully edited productor", {capfirst: true}),
});
await queryClient.invalidateQueries({ queryKey: ['productors'] })
},
onError: (error: any) => {
notifications.show({
title: t("error", {capfirst: true}),
message: error?.message || t(`error editing productor`, {capfirst: true}),
color: "red"
});
}
})
}
@@ -157,7 +215,18 @@ export function deleteProductor() {
}).then((res) => res.json());
},
onSuccess: async () => {
notifications.show({
title: t("success", {capfirst: true}),
message: t("successfully deleted productor", {capfirst: true}),
});
await queryClient.invalidateQueries({ queryKey: ['productors'] })
},
onError: (error: any) => {
notifications.show({
title: t("error", {capfirst: true}),
message: error?.message || t(`error deleting productor`, {capfirst: true}),
color: "red"
});
}
});
}
@@ -216,7 +285,18 @@ export function deleteForm() {
}).then((res) => res.json());
},
onSuccess: async () => {
notifications.show({
title: t("success", {capfirst: true}),
message: t("successfully deleted form", {capfirst: true}),
});
await queryClient.invalidateQueries({ queryKey: ['forms'] })
},
onError: (error: any) => {
notifications.show({
title: t("error", {capfirst: true}),
message: error?.message || t(`error deleting form`, {capfirst: true}),
color: "red"
});
}
});
}
@@ -235,7 +315,18 @@ export function editForm() {
}).then((res) => res.json());
},
onSuccess: async () => {
notifications.show({
title: t("success", {capfirst: true}),
message: t("successfully edited form", {capfirst: true}),
});
await queryClient.invalidateQueries({ queryKey: ['forms'] })
},
onError: (error: any) => {
notifications.show({
title: t("error", {capfirst: true}),
message: error?.message || t(`error editing form`, {capfirst: true}),
color: "red"
});
}
});
}
@@ -277,7 +368,18 @@ export function createProduct() {
}).then((res) => res.json());
},
onSuccess: async () => {
notifications.show({
title: t("success", {capfirst: true}),
message: t("successfully created product", {capfirst: true}),
});
await queryClient.invalidateQueries({ queryKey: ['products'] })
},
onError: (error: any) => {
notifications.show({
title: t("error", {capfirst: true}),
message: error?.message || t(`error editing product`, {capfirst: true}),
color: "red"
});
}
});
}
@@ -294,7 +396,18 @@ export function deleteProduct() {
}).then((res) => res.json());
},
onSuccess: async () => {
notifications.show({
title: t("success", {capfirst: true}),
message: t("successfully deleted product", {capfirst: true}),
});
await queryClient.invalidateQueries({ queryKey: ['products'] })
},
onError: (error: any) => {
notifications.show({
title: t("error", {capfirst: true}),
message: error?.message || t(`error deleting product`, {capfirst: true}),
color: "red"
});
}
});
}
@@ -313,7 +426,18 @@ export function editProduct() {
}).then((res) => res.json());
},
onSuccess: async () => {
notifications.show({
title: t("success", {capfirst: true}),
message: t("successfully edited product", {capfirst: true}),
});
await queryClient.invalidateQueries({ queryKey: ['products'] })
},
onError: (error: any) => {
notifications.show({
title: t("error", {capfirst: true}),
message: error?.message || t(`error editing product`, {capfirst: true}),
color: "red"
});
}
});
}
@@ -355,7 +479,18 @@ export function createUser() {
}).then((res) => res.json());
},
onSuccess: async () => {
notifications.show({
title: t("success", {capfirst: true}),
message: t("successfully created user", {capfirst: true}),
});
await queryClient.invalidateQueries({ queryKey: ['users'] })
},
onError: (error: any) => {
notifications.show({
title: t("error", {capfirst: true}),
message: error?.message || t(`error editing user`, {capfirst: true}),
color: "red"
});
}
});
}
@@ -372,7 +507,18 @@ export function deleteUser() {
}).then((res) => res.json());
},
onSuccess: async () => {
notifications.show({
title: t("success", {capfirst: true}),
message: t("successfully deleted user", {capfirst: true}),
});
await queryClient.invalidateQueries({ queryKey: ['users'] })
},
onError: (error: any) => {
notifications.show({
title: t("error", {capfirst: true}),
message: error?.message || t(`error deleting user`, {capfirst: true}),
color: "red"
});
}
});
}
@@ -391,7 +537,44 @@ export function editUser() {
}).then((res) => res.json());
},
onSuccess: async () => {
notifications.show({
title: t("success", {capfirst: true}),
message: t("successfully edited user", {capfirst: true}),
});
await queryClient.invalidateQueries({ queryKey: ['users'] })
},
onError: (error: any) => {
notifications.show({
title: t("error", {capfirst: true}),
message: error?.message || t(`error editing user`, {capfirst: true}),
color: "red"
});
}
});
}
export function createContract() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (newContract: ContractCreate) => {
return fetch(`${Config.backend_uri}/contracts`, {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(newContract),
}).then(async (res) => await res.blob());
},
onSuccess: async (pdfBlob) => {
const url = URL.createObjectURL(pdfBlob);
const link = document.createElement("a");
link.href = url;
link.download = `contract.pdf`;
link.click();
URL.revokeObjectURL(url);
await queryClient.invalidateQueries({ queryKey: ["contracts"] });
}
});
}

View File

@@ -0,0 +1,4 @@
export type ContractCreate = {
form_id: number;
contract: Record<string, string | number | null>;
}

View File

@@ -1,5 +1,5 @@
import type { Productor } from "@/services/resources/productors";
import type { Shipment, ShipmentInputs } from "@/services/resources/shipments";
import type { Shipment } from "@/services/resources/shipments";
import type { User } from "@/services/resources/users";
export type Form = {
@@ -11,6 +11,7 @@ export type Form = {
productor: Productor;
referer: User;
shipments: Shipment[];
minimum_shipment_value: number | null;
}
export type FormCreate = {
@@ -20,6 +21,7 @@ export type FormCreate = {
end: string;
productor_id: number;
referer_id: number;
minimum_shipment_value: number | null;
}
export type FormEdit = {
@@ -29,6 +31,7 @@ export type FormEdit = {
end?: string | null;
productor_id?: number | null;
referer_id?: number | null;
minimum_shipment_value: number | null;
}
export type FormEditPayload = {
@@ -43,4 +46,5 @@ export type FormInputs = {
end: string | null;
productor_id: string;
referer_id: string;
minimum_shipment_value: number | string | null;
}

View File

@@ -1,10 +1,21 @@
import { t } from "@/config/i18n";
import type { Product } from "./products";
export const PaymentMethods = [
{value: "cheque", label: t("cheque", {capfirst: true})},
{value: "transfer", label: t("transfer", {capfirst: true})},
]
export type PaymentMethod = {
name: string;
details: string;
}
export type Productor = {
id: number;
name: string;
address: string;
payment: string;
payment_methods: PaymentMethod[];
type: string;
products: Product[]
}
@@ -12,22 +23,22 @@ export type Productor = {
export type ProductorCreate = {
name: string;
address: string;
payment: string;
payment_methods: PaymentMethod[];
type: string;
}
export type ProductorEdit = {
name: string | null;
address: string | null;
payment: string | null;
payment_methods: PaymentMethod[];
type: string | null;
}
export type ProductorInputs = {
name: string;
address: string;
payment: string;
type: string;
payment_methods: PaymentMethod[];
}
export type ProductorEditPayload = {

View File

@@ -61,9 +61,9 @@ export type ProductInputs = {
productor_id: string | null;
name: string;
unit: string | null;
price: number | null;
price_kg: number | null;
quantity: number | null;
price: number | string | null;
price_kg: number | string | null;
quantity: number | string | null;
quantity_unit: string | null;
type: string | null;
}
@@ -91,9 +91,9 @@ export function productCreateFromProductInputs(productInput: ProductInputs): Pro
productor_id: Number(productInput.productor_id)!,
name: productInput.name,
unit: productInput.unit!,
price: productInput.price!,
price_kg: productInput.price_kg,
quantity: productInput.quantity,
price: productInput.price === "" || !productInput.price ? null : Number(productInput.price),
price_kg: productInput.price_kg === "" || !productInput.price_kg ? null : Number(productInput.price_kg),
quantity: productInput.quantity === "" || !productInput.quantity ? null : Number(productInput.quantity),
quantity_unit: productInput.quantity_unit,
type: productInput.type!,
}