[#256] Créer une nouvelle réception (étape 3 - bovin) #11
4
.idea/dataSources.xml
generated
4
.idea/dataSources.xml
generated
@@ -5,7 +5,7 @@
|
|||||||
<driver-ref>postgresql</driver-ref>
|
<driver-ref>postgresql</driver-ref>
|
||||||
<synchronize>true</synchronize>
|
<synchronize>true</synchronize>
|
||||||
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
||||||
<jdbc-url>jdbc:postgresql://localhost:5432/ferme</jdbc-url>
|
<jdbc-url>jdbc:postgresql://localhost:5433/ferme</jdbc-url>
|
||||||
<working-dir>$ProjectFileDir$</working-dir>
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
</data-source>
|
</data-source>
|
||||||
<data-source source="LOCAL" name="Ferme recette" uuid="ae622167-c834-4e7b-87a5-c1721036f5dc">
|
<data-source source="LOCAL" name="Ferme recette" uuid="ae622167-c834-4e7b-87a5-c1721036f5dc">
|
||||||
@@ -16,4 +16,4 @@
|
|||||||
<working-dir>$ProjectFileDir$</working-dir>
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
</data-source>
|
</data-source>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
8
.idea/workspace.xml
generated
8
.idea/workspace.xml
generated
@@ -5,14 +5,18 @@
|
|||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="7c107abe-5995-4428-8429-b146aaca8386" name="Changes" comment="feat : Ajout de la sélection des bovins étape 3 d'une réception (WIP)">
|
<list default="true" id="7c107abe-5995-4428-8429-b146aaca8386" name="Changes" comment="feat : Ajout de la sélection des bovins étape 3 d'une réception (WIP)">
|
||||||
|
<change beforePath="$PROJECT_DIR$/.idea/dataSources.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/config/reference.php" beforeDir="false" afterPath="$PROJECT_DIR$/config/reference.php" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/config/reference.php" beforeDir="false" afterPath="$PROJECT_DIR$/config/reference.php" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/frontend/components/reception/reception-bovine-received.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/reception/reception-bovine-received.vue" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/frontend/components/reception/reception-bovine-received.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/reception/reception-bovine-received.vue" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/frontend/components/reception/reception-form.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/reception/reception-form.vue" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/frontend/components/reception/reception-form.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/reception/reception-form.vue" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/frontend/components/reception/reception-product-received.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/reception/reception-product-received.vue" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/frontend/components/reception/reception-product-received.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/reception/reception-product-received.vue" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/frontend/services/dto/reception-data.ts" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/services/dto/reception-data.ts" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/frontend/components/ui/UiNumberInput.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/ui/UiNumberInput.vue" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/frontend/services/reception.ts" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/services/reception.ts" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/Entity/Reception.php" beforeDir="false" afterPath="$PROJECT_DIR$/src/Entity/Reception.php" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/Entity/Reception.php" beforeDir="false" afterPath="$PROJECT_DIR$/src/Entity/Reception.php" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/Entity/ReceptionBovine.php" beforeDir="false" afterPath="$PROJECT_DIR$/src/Entity/ReceptionBovine.php" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/Repository/.gitignore" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/Repository/BovineTypeRepository.php" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/Repository/ReceptionBovineRepository.php" beforeDir="false" />
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type {BovineTypeData} from "~/services/dto/bovine-type-data";
|
import type {BovineTypeData} from "~/services/dto/bovine-type-data";
|
||||||
import {getBovineTypeList} from "~/services/bovine-type";
|
import {getBovineTypeList} from "~/services/bovine-type";
|
||||||
import {MERCHANDISE_TYPE_CODES, RECEPTION_TYPE_CODES} from "~/utils/constants";
|
import {RECEPTION_TYPE_CODES} from "~/utils/constants";
|
||||||
import {useReceptionStore} from '~/stores/reception'
|
import {useReceptionStore} from '~/stores/reception'
|
||||||
import {
|
import {
|
||||||
createReceptionBovine,
|
createReceptionBovine,
|
||||||
@@ -46,6 +46,7 @@ import {
|
|||||||
} from "~/services/reception-bovine";
|
} from "~/services/reception-bovine";
|
||||||
import {computed, onMounted, reactive, ref, watch} from "vue";
|
import {computed, onMounted, reactive, ref, watch} from "vue";
|
||||||
|
|
||||||
|
const toast = useToast()
|
||||||
const isLoadingBovineType = ref(false)
|
const isLoadingBovineType = ref(false)
|
||||||
const bovineType = ref<BovineTypeData[]>([])
|
const bovineType = ref<BovineTypeData[]>([])
|
||||||
const receptionStore = useReceptionStore()
|
const receptionStore = useReceptionStore()
|
||||||
@@ -55,19 +56,13 @@ const receptionId = computed(() => receptionStore.current?.id ?? null)
|
|||||||
const receptionIri = computed(() =>
|
const receptionIri = computed(() =>
|
||||||
receptionId.value ? `/api/receptions/${receptionId.value}` : null
|
receptionId.value ? `/api/receptions/${receptionId.value}` : null
|
||||||
)
|
)
|
||||||
const toast = useToast()
|
const totalBovines = computed(() => {
|
||||||
const nuxtApp = useNuxtApp()
|
const base = Object.values(bovineQuantities).reduce((sum, value) => {
|
||||||
const i18n = nuxtApp.$i18n as { t: (key: string) => string } |
|
return sum + (value ?? 0)
|
||||||
undefined
|
|
||||||
const t = (key: string) => (i18n?.t ? String(i18n.t(key)) : key)
|
|
||||||
const totalBovineQuantity = computed(() => {
|
|
||||||
const baseTotal = Object.values(bovineQuantities).reduce((sum, value) => {
|
|
||||||
const n = typeof value === 'number' ? value : 0
|
|
||||||
return sum + n
|
|
||||||
}, 0)
|
}, 0)
|
||||||
const other = typeof otherQuantity.value === 'number' ? otherQuantity.value : 0
|
return base + (otherQuantity.value ?? 0)
|
||||||
return baseTotal + other
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const loadBovineType = async () => {
|
const loadBovineType = async () => {
|
||||||
isLoadingBovineType.value = true
|
isLoadingBovineType.value = true
|
||||||
try {
|
try {
|
||||||
@@ -117,6 +112,7 @@ watch(
|
|||||||
async function syncBovineSelections(receptionIri: string) {
|
async function syncBovineSelections(receptionIri: string) {
|
||||||
const existing = await getReceptionBovineList(receptionIri)
|
const existing = await getReceptionBovineList(receptionIri)
|
||||||
const existingMap = new Map<string, { id: number; quantity: number | null }>()
|
const existingMap = new Map<string, { id: number; quantity: number | null }>()
|
||||||
|
|
||||||
for (const selection of existing) {
|
for (const selection of existing) {
|
||||||
const bovineTypeId = String(selection.bovineType.id)
|
const bovineTypeId = String(selection.bovineType.id)
|
||||||
existingMap.set(bovineTypeId, {
|
existingMap.set(bovineTypeId, {
|
||||||
@@ -159,37 +155,21 @@ async function syncBovineSelections(receptionIri: string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const hasNegativeQuantity = computed(() => {
|
|
||||||
const anyNegativeInTypes =
|
|
||||||
Object.values(bovineQuantities).some((value) => {
|
|
||||||
return typeof value === 'number' && value < 0
|
|
||||||
})
|
|
||||||
|
|
||||||
const otherNegative =
|
|
||||||
typeof otherQuantity.value === 'number' &&
|
|
||||||
otherQuantity.value < 0
|
|
||||||
|
|
||||||
return anyNegativeInTypes || otherNegative
|
|
||||||
})
|
|
||||||
async function goNext() {
|
async function goNext() {
|
||||||
if (!receptionStore.current || !receptionIri.value) {
|
if (!receptionStore.current || !receptionIri.value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (hasNegativeQuantity.value) {
|
|
||||||
toast.error({
|
// @TODO Ajouter un composable pour le toaster qui gère les key i18n
|
||||||
title: 'Erreur',
|
if (totalBovines.value > 52) {
|
||||||
message: ("La quantité de bovins ne peut pas être négative.")
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Le 52 à vérifier
|
|
||||||
if (totalBovineQuantity.value > 52) {
|
|
||||||
toast.error({
|
toast.error({
|
||||||
title: 'Erreur',
|
title: 'Erreur',
|
||||||
message: ('Le total des bovins ne peut pas dépasser 52.')
|
message: ('Le total des bovins ne peut pas dépasser 52.')
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextStep = receptionStore.current.currentStep + 1
|
const nextStep = receptionStore.current.currentStep + 1
|
||||||
await syncBovineSelections(receptionIri.value)
|
await syncBovineSelections(receptionIri.value)
|
||||||
|
|
||||||
|
|||||||
@@ -222,10 +222,12 @@ const filteredVehicles = computed<VehicleData[]>(() => {
|
|||||||
(!form.truckId || String(vehicle.truck?.id) === form.truckId)
|
(!form.truckId || String(vehicle.truck?.id) === form.truckId)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const selectedReceptionType = computed(() =>
|
const selectedReceptionType = computed(() =>
|
||||||
receptionTypes.value.find((type) => String(type.id) === form.receptionTypeId) ?? null
|
receptionTypes.value.find((type) => String(type.id) === form.receptionTypeId) ?? null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Supprime les données bovines si on change de type de réception
|
||||||
const clearReceptionBovines = async (receptionIri: string) => {
|
const clearReceptionBovines = async (receptionIri: string) => {
|
||||||
const existing = await getReceptionBovineList(receptionIri)
|
const existing = await getReceptionBovineList(receptionIri)
|
||||||
for (const selection of existing) {
|
for (const selection of existing) {
|
||||||
@@ -471,9 +473,6 @@ async function validate() {
|
|||||||
const normalizedTruckId = form.truckId.trim()
|
const normalizedTruckId = form.truckId.trim()
|
||||||
const normalizedCarrierId = form.carrierId.trim()
|
const normalizedCarrierId = form.carrierId.trim()
|
||||||
const normalizedDriverId = form.driverId.trim()
|
const normalizedDriverId = form.driverId.trim()
|
||||||
const previousTypeCode = receptionStore.current.receptionType?.code ?? null
|
|
||||||
const nextTypeCode = selectedReceptionType.value?.code ?? null
|
|
||||||
const receptionIri = `/api/receptions/${receptionStore.current.id}`
|
|
||||||
const receptionTypeIri = normalizedReceptionTypeId
|
const receptionTypeIri = normalizedReceptionTypeId
|
||||||
? `/api/reception_types/${normalizedReceptionTypeId}`
|
? `/api/reception_types/${normalizedReceptionTypeId}`
|
||||||
: null
|
: null
|
||||||
@@ -522,6 +521,11 @@ async function validate() {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const previousTypeCode = receptionStore.current.receptionType?.code ?? null
|
||||||
|
const nextTypeCode = selectedReceptionType.value?.code ?? null
|
||||||
|
const receptionIri = `/api/receptions/${receptionStore.current.id}`
|
||||||
|
|
||||||
if (
|
if (
|
||||||
previousTypeCode === RECEPTION_TYPE_CODES.BOVINS &&
|
previousTypeCode === RECEPTION_TYPE_CODES.BOVINS &&
|
||||||
nextTypeCode === RECEPTION_TYPE_CODES.MERCHANDISES
|
nextTypeCode === RECEPTION_TYPE_CODES.MERCHANDISES
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col items-center gap-16">
|
<div class="flex flex-col items-center gap-16">
|
||||||
<!-- @TODO voir pour séparer dans un composant au moment de l'implémentation des Bovins -->
|
|
||||||
<div
|
<div
|
||||||
v-if="receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.MERCHANDISES"
|
v-if="receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.MERCHANDISES"
|
||||||
class="flex flex-col gap-16 items-center w-full">
|
class="flex flex-col gap-16 items-center w-full">
|
||||||
@@ -99,7 +98,6 @@ const selectedBuildingIds = ref<string[]>([])
|
|||||||
const selectedPelletBuildingIds = ref<Record<string, string[]>>({})
|
const selectedPelletBuildingIds = ref<Record<string, string[]>>({})
|
||||||
const merchandiseDetail = ref('')
|
const merchandiseDetail = ref('')
|
||||||
|
|
||||||
|
|
||||||
// Extrait l'ID d'une relation depuis un IRI ou un objet complet.
|
// Extrait l'ID d'une relation depuis un IRI ou un objet complet.
|
||||||
const getRelationId = (value: unknown): string | null => {
|
const getRelationId = (value: unknown): string | null => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
disabled ? 'cursor-not-allowed' : 'cursor-text',
|
disabled ? 'cursor-not-allowed' : 'cursor-text',
|
||||||
inputClass
|
inputClass
|
||||||
]"
|
]"
|
||||||
|
@keydown="onKeydown"
|
||||||
@input="onInput"
|
@input="onInput"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@@ -78,7 +79,13 @@ const onInput = (event: Event) => {
|
|||||||
emit('update:modelValue', null)
|
emit('update:modelValue', null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const numeric = Number(target.value)
|
const numeric = Math.max(0, Number(target.value))
|
||||||
emit('update:modelValue', Number.isNaN(numeric) ? null : numeric)
|
emit('update:modelValue', Number.isNaN(numeric) ? null : numeric)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onKeydown = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === '-' || event.key === 'e' || event.key === 'E') {
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ import {useApi} from '~/composables/useApi'
|
|||||||
import type {ReceptionData, ReceptionPayload} from '~/services/dto/reception-data'
|
import type {ReceptionData, ReceptionPayload} from '~/services/dto/reception-data'
|
||||||
import type {WeightData} from '~/services/dto/weight-data'
|
import type {WeightData} from '~/services/dto/weight-data'
|
||||||
|
|
||||||
export async function getReceptionList() {
|
export async function getReceptionList(isValid: boolean|null = null) {
|
||||||
const api = useApi()
|
const api = useApi()
|
||||||
return api.get<ReceptionData>(`receptions`, {}, {
|
const query = isValid !== null ? { isValid: isValid} : {}
|
||||||
|
return api.get<ReceptionData[]>('receptions', query, {
|
||||||
toastErrorKey: 'errors.reception.list'
|
toastErrorKey: 'errors.reception.list'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
|
||||||
|
use ApiPlatform\Metadata\ApiFilter;
|
||||||
use ApiPlatform\Metadata\ApiProperty;
|
use ApiPlatform\Metadata\ApiProperty;
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
@@ -26,6 +28,7 @@ use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
|
|||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
#[ORM\HasLifecycleCallbacks]
|
#[ORM\HasLifecycleCallbacks]
|
||||||
#[ORM\Table(name: 'reception')]
|
#[ORM\Table(name: 'reception')]
|
||||||
|
#[ApiFilter(BooleanFilter::class, properties: ['isValid'])]
|
||||||
#[ApiResource(
|
#[ApiResource(
|
||||||
operations: [
|
operations: [
|
||||||
new Get(
|
new Get(
|
||||||
|
|||||||
0
src/Repository/.gitignore
vendored
0
src/Repository/.gitignore
vendored
@@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Repository;
|
|
||||||
|
|
||||||
use App\Entity\BovineType;
|
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @extends ServiceEntityRepository<BovineType>
|
|
||||||
*/
|
|
||||||
class BovineTypeRepository extends ServiceEntityRepository
|
|
||||||
{
|
|
||||||
public function __construct(ManagerRegistry $registry)
|
|
||||||
{
|
|
||||||
parent::__construct($registry, BovineType::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @return BovineType[] Returns an array of BovineType objects
|
|
||||||
// */
|
|
||||||
// public function findByExampleField($value): array
|
|
||||||
// {
|
|
||||||
// return $this->createQueryBuilder('b')
|
|
||||||
// ->andWhere('b.exampleField = :val')
|
|
||||||
// ->setParameter('val', $value)
|
|
||||||
// ->orderBy('b.id', 'ASC')
|
|
||||||
// ->setMaxResults(10)
|
|
||||||
// ->getQuery()
|
|
||||||
// ->getResult()
|
|
||||||
// ;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public function findOneBySomeField($value): ?BovineType
|
|
||||||
// {
|
|
||||||
// return $this->createQueryBuilder('b')
|
|
||||||
// ->andWhere('b.exampleField = :val')
|
|
||||||
// ->setParameter('val', $value)
|
|
||||||
// ->getQuery()
|
|
||||||
// ->getOneOrNullResult()
|
|
||||||
// ;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Repository;
|
|
||||||
|
|
||||||
use App\Entity\ReceptionBovine;
|
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @extends ServiceEntityRepository<ReceptionBovine>
|
|
||||||
*/
|
|
||||||
class ReceptionBovineRepository extends ServiceEntityRepository
|
|
||||||
{
|
|
||||||
public function __construct(ManagerRegistry $registry)
|
|
||||||
{
|
|
||||||
parent::__construct($registry, ReceptionBovine::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @return ReceptionBovine[] Returns an array of ReceptionBovine objects
|
|
||||||
// */
|
|
||||||
// public function findByExampleField($value): array
|
|
||||||
// {
|
|
||||||
// return $this->createQueryBuilder('r')
|
|
||||||
// ->andWhere('r.exampleField = :val')
|
|
||||||
// ->setParameter('val', $value)
|
|
||||||
// ->orderBy('r.id', 'ASC')
|
|
||||||
// ->setMaxResults(10)
|
|
||||||
// ->getQuery()
|
|
||||||
// ->getResult()
|
|
||||||
// ;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public function findOneBySomeField($value): ?ReceptionBovine
|
|
||||||
// {
|
|
||||||
// return $this->createQueryBuilder('r')
|
|
||||||
// ->andWhere('r.exampleField = :val')
|
|
||||||
// ->setParameter('val', $value)
|
|
||||||
// ->getQuery()
|
|
||||||
// ->getOneOrNullResult()
|
|
||||||
// ;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user