-
+
diff --git a/frontend/pages/shipment/[[id]].vue b/frontend/pages/shipment/[[id]].vue
new file mode 100644
index 0000000..e4a1ef2
--- /dev/null
+++ b/frontend/pages/shipment/[[id]].vue
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/services/bovin-shipment.ts b/frontend/services/bovin-shipment.ts
new file mode 100644
index 0000000..45871dc
--- /dev/null
+++ b/frontend/services/bovin-shipment.ts
@@ -0,0 +1,58 @@
+import { useApi } from '~/composables/useApi'
+import type { BovinShipmentData } from '~/services/dto/bovin-shipment-data'
+export type BovinShipmentListResponse =
+ | BovinShipmentData[]
+ | { 'hydra:member'?: BovinShipmentData[] }
+
+export type ShipmentBovinePayload = {
+ nbBovinSend: number
+ shipment: string
+ shipmentType: string
+}
+
+export async function getBovinShipmentList(
+ shipmentIri: string
+): Promise
{
+ const api = useApi()
+ const response = await api.get(
+ 'bovin_shipments',
+ { shipment: shipmentIri },
+ {
+ toastErrorKey: 'errors.shipmentBovine.list'
+ }
+ )
+
+ if (Array.isArray(response)) {
+ return response
+ }
+ if (response && typeof response === 'object' && Array.isArray(response['hydra:member'])) {
+ return response['hydra:member']
+ }
+ return []
+}
+
+export async function createShipmentBovine(
+ payload: ShipmentBovinePayload
+): Promise {
+ const api = useApi()
+ return api.post('bovin_shipments', payload, {
+ toastErrorKey: 'errors.shipmentBovine.create'
+ })
+}
+
+export async function deleteShipmentBovine(id: number): Promise {
+ const api = useApi()
+ await api.delete(`bovin_shipments/${id}`, {}, {
+ toastErrorKey: 'errors.shipmentBovine.delete'
+ })
+}
+
+export async function updateShipmentBovine(
+ id: number,
+ payload: Partial
+): Promise {
+ const api = useApi()
+ return api.patch(`bovin_shipments/${id}`, payload, {
+ toastErrorKey: 'errors.shipmentBovine.update'
+ })
+}
diff --git a/frontend/services/customer.ts b/frontend/services/customer.ts
new file mode 100644
index 0000000..04c8faa
--- /dev/null
+++ b/frontend/services/customer.ts
@@ -0,0 +1,23 @@
+import { useApi } from '~/composables/useApi'
+import type { CustomerData } from '~/services/dto/customer-data'
+
+export type CustomerListResponse =
+ | CustomerData[]
+ | { 'hydra:member'?: CustomerData[] }
+
+export async function getCustomerList(): Promise {
+ const api = useApi()
+ const response = await api.get('customers', {}, {
+ toastErrorKey: 'errors.customer.list'
+ })
+
+ if (Array.isArray(response)) {
+ return response
+ }
+
+ if (response && typeof response === 'object' && Array.isArray(response['hydra:member'])) {
+ return response['hydra:member']
+ }
+
+ return []
+}
diff --git a/frontend/services/dto/bovin-shipment-data.ts b/frontend/services/dto/bovin-shipment-data.ts
new file mode 100644
index 0000000..e3bdd42
--- /dev/null
+++ b/frontend/services/dto/bovin-shipment-data.ts
@@ -0,0 +1,8 @@
+import type {ShipmentTypeData} from "~/services/dto/shipment-type-data";
+
+export interface BovinShipmentData {
+ id: number
+ nbBovinSend: number | null
+ shipment?: string | null
+ shipmentType?: ShipmentTypeData | null
+}
diff --git a/frontend/services/dto/customer-data.ts b/frontend/services/dto/customer-data.ts
new file mode 100644
index 0000000..57d6f7d
--- /dev/null
+++ b/frontend/services/dto/customer-data.ts
@@ -0,0 +1,8 @@
+import type { AddressData } from "~/services/dto/address-data"
+
+export interface CustomerData {
+ id: number
+ label: string
+ code?: string | null
+ addresses?: AddressData[] | null
+}
diff --git a/frontend/services/dto/shipment-data.ts b/frontend/services/dto/shipment-data.ts
new file mode 100644
index 0000000..40ec748
--- /dev/null
+++ b/frontend/services/dto/shipment-data.ts
@@ -0,0 +1,51 @@
+import type {CarrierData} from '~/services/dto/carrier-data'
+import type {TruckData} from '~/services/dto/truck-data'
+import type {CustomerData} from '~/services/dto/customer-data'
+
+export interface ShipmentTypeData {
+ id: number
+ label: string
+ code: string
+}
+
+export interface BovinShipmentData {
+ id?: number
+ shipmentType?: ShipmentTypeData | string | null
+ nbBovinSend: number | null
+}
+
+export type ShipmentData = {
+ id: number
+ identificationNumber?: string | null
+ licencePlate: string | null
+ shipmentDate: string
+ currentStep: number
+ isValid: boolean
+ carrier?: CarrierData | null
+ truck?: TruckData | null
+ customer?: CustomerData | null
+ bovinShipments?: BovinShipmentData[] | null
+}
+
+export type ShipmentFormData = {
+ userId: string,
+ shipmentDate: string,
+ customerId: string,
+ addressId: string,
+ truckId: string,
+ carrierId: string,
+ driverId: string,
+ vehicleId: string,
+ licencePlate: string,
+}
+
+export type ShipmentPayload = {
+ licencePlate?: string | null
+ shipmentDate?: string
+ currentStep?: number
+ isValid?: boolean
+ carrier?: string | null
+ truck?: string | null
+ customer?: string | null
+ bovinShipments?: string[] | null
+}
diff --git a/frontend/services/dto/shipment-type-data.ts b/frontend/services/dto/shipment-type-data.ts
new file mode 100644
index 0000000..e1c66db
--- /dev/null
+++ b/frontend/services/dto/shipment-type-data.ts
@@ -0,0 +1,5 @@
+export interface ShipmentTypeData {
+ id: number
+ label: string
+ code: string
+}
diff --git a/frontend/services/shipment-type.ts b/frontend/services/shipment-type.ts
new file mode 100644
index 0000000..a1f8315
--- /dev/null
+++ b/frontend/services/shipment-type.ts
@@ -0,0 +1,24 @@
+import { useApi } from '~/composables/useApi'
+import type {ShipmentTypeData} from "~/services/dto/shipment-type-data";
+
+export type ShipmentTypeListResponse =
+ | ShipmentTypeData[]
+ | { 'hydra:member'?: ShipmentTypeData[] }
+
+
+export async function getShipmentTypeList(): Promise {
+ const api = useApi()
+ const response = await api.get('shipment_types', {}, {
+ toastErrorKey: 'errors.shipment_type.list'
+ })
+
+ if (Array.isArray(response)) {
+ return response
+ }
+
+ if (response && typeof response === 'object' && Array.isArray(response['hydra:member'])) {
+ return response['hydra:member']
+ }
+
+ return []
+}
diff --git a/frontend/services/shipment.ts b/frontend/services/shipment.ts
new file mode 100644
index 0000000..0eecc68
--- /dev/null
+++ b/frontend/services/shipment.ts
@@ -0,0 +1,40 @@
+import {useApi} from '~/composables/useApi'
+import type {ShipmentData, ShipmentPayload} from '~/services/dto/shipment-data'
+import type {WeightData} from '~/services/dto/weight-data'
+
+export async function getShipmentList(isValid: boolean|null = null) {
+ const api = useApi()
+ const query = isValid !== null ? { isValid: isValid} : {}
+ return api.get('shipments', query, {
+ toastErrorKey: 'errors.shipment.list'
+ })
+}
+
+export async function getShipment(id: number) {
+ const api = useApi()
+ return api.get(`shipments/${id}`, {}, {
+ toastErrorKey: 'errors.shipment.fetch'
+ })
+}
+
+export async function createShipment(payload: ShipmentPayload = {}) {
+ const api = useApi()
+ return api.post('shipments', payload, {
+ toastErrorKey: 'errors.shipment.create'
+ })
+}
+
+export async function updateShipment(id: number, payload: ShipmentPayload) {
+ const api = useApi()
+ return api.patch(`shipments/${id}`, payload, {
+ toastErrorKey: 'errors.shipment.update',
+ toastSuccessKey: 'success.shipment.update'
+ })
+}
+
+export async function getWeight(): Promise {
+ const api = useApi()
+ return api.get('shipments/weigh', {}, {
+ toastErrorKey: 'errors.shipment.weigh'
+ })
+}
diff --git a/frontend/stores/shipment.ts b/frontend/stores/shipment.ts
new file mode 100644
index 0000000..a92b919
--- /dev/null
+++ b/frontend/stores/shipment.ts
@@ -0,0 +1,58 @@
+import { defineStore } from 'pinia'
+import type {ShipmentData, ShipmentPayload} from "~/services/dto/shipment-data";
+import {createShipment, getShipment, updateShipment} from "~/services/shipment";
+
+const isShipmentData = (value: unknown): value is ShipmentData => {
+ return Boolean(value && typeof value === 'object' && 'id' in value)
+}
+export const useShipmentStore = defineStore('shipment', {
+ state: () => ({
+ current: null as ShipmentData | null,
+ isLoading: false
+ }),
+ actions: {
+ setCurrent(shipment: ShipmentData | null) {
+ this.current = shipment
+ },
+ clearCurrent() {
+ this.current = null
+ },
+ async loadShipment(id: number) {
+ this.isLoading = true
+ const result = await getShipment(id).finally(() => {
+ this.isLoading = false
+ })
+ if (!isShipmentData(result)) {
+ this.current = null
+ return null
+ }
+
+ this.current = result
+ return result
+ },
+ async createShipment(payload: ShipmentPayload = {}) {
+ this.isLoading = true
+ const result = await createShipment(payload).finally(() => {
+ this.isLoading = false
+ })
+ if (!isShipmentData(result)) {
+ return null
+ }
+
+ this.current = result
+ return result
+ },
+ async updateShipment(id: number, payload: ShipmentPayload) {
+ this.isLoading = true
+ const result = await updateShipment(id, payload).finally(() => {
+ this.isLoading = false
+ })
+ if (!isShipmentData(result)) {
+ return null
+ }
+
+ this.current = result
+ return result
+ }
+ }
+})
diff --git a/src/Entity/Address.php b/src/Entity/Address.php
index e51450c..f8afe1d 100644
--- a/src/Entity/Address.php
+++ b/src/Entity/Address.php
@@ -146,7 +146,7 @@ class Address
return $this;
}
- #[Groups(['address:read', 'supplier:read', 'reception:read'])]
+ #[Groups(['address:read', 'supplier:read', 'reception:read', 'customer:read'])]
public function getFullAddress(): string
{
$parts = array_filter([
diff --git a/src/Entity/BovinShipment.php b/src/Entity/BovinShipment.php
index e538215..6a7bf4e 100644
--- a/src/Entity/BovinShipment.php
+++ b/src/Entity/BovinShipment.php
@@ -4,64 +4,64 @@ declare(strict_types=1);
namespace App\Entity;
+use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
+use ApiPlatform\Metadata\ApiFilter;
+use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
+use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
+use ApiPlatform\Metadata\Patch;
+use ApiPlatform\Metadata\Post;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Attribute\Groups;
#[ORM\Entity]
+#[ApiFilter(SearchFilter::class, properties: ['shipment' => 'exact'])]
+#[ORM\UniqueConstraint(name: 'uniq_bovin_shipment', columns: ['shipment_id', 'shipment_type_id'])]
#[ORM\Table(name: 'bovin_shipment')]
#[ApiResource(
operations: [
new Get(
requirements: ['id' => '\d+'],
- normalizationContext: ['groups' => ['bovin-shipment:read']],
+ normalizationContext: ['groups' => ['shipment-bovine:read']],
),
new GetCollection(
- normalizationContext: ['groups' => ['bovin-shipment:read']],
+ normalizationContext: ['groups' => ['shipment-bovine:read']],
),
- // new Get(
- // uriTemplate: '/receptions/weigh',
- // openapi: new OpenApiOperation(
- // summary: 'Fetch the current weight reading',
- // description: 'Queries the pont-bascule and returns the weight data.',
- // ),
- // normalizationContext: ['groups' => ['reception:weigh:read']],
- // output: PontBasculeReading::class,
- // provider: ReceptionWeighingProvider::class,
- // ),
- // new Get(
- // uriTemplate: '/receptions/{id}/receipt',
- // requirements: ['id' => '\d+'],
- // openapi: new OpenApiOperation(
- // summary: 'Render a reception receipt',
- // description: 'Returns a PDF receipt for the reception.',
- // ),
- // output: false,
- // provider: ReceptionReceiptProvider::class,
- // ),
+ new Post(
+ normalizationContext: ['groups' => ['shipment-bovine:read']],
+ denormalizationContext: ['groups' => ['shipment-bovine:write']],
+ ),
+ new Patch(
+ normalizationContext: ['groups' => ['shipment-bovine:read']],
+ denormalizationContext: ['groups' => ['shipment-bovine:write']],
+ ),
+ new Delete(),
],
+ security: "is_granted('ROLE_USER')",
)]
class BovinShipment
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
- #[Groups(['shipment:read', 'bovine-shipment:read'])]
+ #[Groups(['shipment:read', 'shipment-bovine:read'])]
private ?int $id = null;
- #[ORM\ManyToOne(inversedBy: 'shipment_types')]
- #[Groups(['bovine-shipment:read'])]
+ #[ORM\ManyToOne(inversedBy: 'bovinShipments')]
+ #[Groups(['shipment-bovine:read', 'shipment-bovine:write'])]
+ #[ApiProperty(readableLink: true)]
private ?Shipment $shipment = null;
#[ORM\ManyToOne]
- #[Groups(['shipment:read', 'bovine-shipment:read'])]
+ #[Groups(['shipment:read', 'shipment-bovine:write', 'shipment-bovine:read'])]
+ #[ApiProperty(readableLink: true)]
private ?ShipmentType $shipmentType = null;
#[ORM\Column]
- #[Groups(['shipment:read', 'bovine-shipment:read'])]
+ #[Groups(['shipment:read', 'shipment-bovine:write', 'shipment-bovine:read'])]
private ?int $nbBovinSend = null;
public function getId(): ?int
diff --git a/src/Entity/Shipment.php b/src/Entity/Shipment.php
index 24f41a5..90d5d2f 100644
--- a/src/Entity/Shipment.php
+++ b/src/Entity/Shipment.php
@@ -11,6 +11,7 @@ use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use DateTimeImmutable;
+use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Attribute\Context;
@@ -81,7 +82,7 @@ class Shipment
#[ORM\Column]
#[Groups(['shipment:read', 'shipment:write'])]
- private ?bool $isValid = null;
+ private bool $isValid = false;
#[ORM\Column(name: 'shipment_date', type: 'datetime_immutable')]
#[Groups(['shipment:read', 'shipment:write'])]
@@ -111,9 +112,9 @@ class Shipment
#[Groups(['shipment:read', 'shipment:write'])]
private Collection $bovinShipments;
- public function __construct(Collection $bovinShipments)
+ public function __construct()
{
- $this->bovinShipments = $bovinShipments;
+ $this->bovinShipments = new ArrayCollection();
}
public function getId(): ?int