mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Feature: Allow to filter periods to reassign by postal code
This commit is contained in:
parent
0609fdee14
commit
86cfd87d71
@ -14,7 +14,7 @@ namespace Chill\MainBundle\Controller;
|
|||||||
use Chill\MainBundle\CRUD\Controller\ApiController;
|
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||||
use Chill\MainBundle\Repository\CountryRepository;
|
use Chill\MainBundle\Repository\CountryRepository;
|
||||||
use Chill\MainBundle\Repository\PostalCodeRepository;
|
use Chill\MainBundle\Repository\PostalCodeRepositoryInterface;
|
||||||
use Chill\MainBundle\Serializer\Model\Collection;
|
use Chill\MainBundle\Serializer\Model\Collection;
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
@ -30,11 +30,11 @@ final class PostalCodeAPIController extends ApiController
|
|||||||
|
|
||||||
private PaginatorFactory $paginatorFactory;
|
private PaginatorFactory $paginatorFactory;
|
||||||
|
|
||||||
private PostalCodeRepository $postalCodeRepository;
|
private PostalCodeRepositoryInterface $postalCodeRepository;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
CountryRepository $countryRepository,
|
CountryRepository $countryRepository,
|
||||||
PostalCodeRepository $postalCodeRepository,
|
PostalCodeRepositoryInterface $postalCodeRepository,
|
||||||
PaginatorFactory $paginatorFactory
|
PaginatorFactory $paginatorFactory
|
||||||
) {
|
) {
|
||||||
$this->countryRepository = $countryRepository;
|
$this->countryRepository = $countryRepository;
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Form\Type\DataTransformer;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\PostalCode;
|
||||||
|
use Chill\MainBundle\Repository\PostalCodeRepositoryInterface;
|
||||||
|
use Symfony\Component\Form\DataTransformerInterface;
|
||||||
|
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||||
|
use function gettype;
|
||||||
|
use function is_int;
|
||||||
|
|
||||||
|
class PostalCodeToIdTransformer implements DataTransformerInterface
|
||||||
|
{
|
||||||
|
private PostalCodeRepositoryInterface $postalCodeRepository;
|
||||||
|
|
||||||
|
public function __construct(PostalCodeRepositoryInterface $postalCodeRepository)
|
||||||
|
{
|
||||||
|
$this->postalCodeRepository = $postalCodeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reverseTransform($value)
|
||||||
|
{
|
||||||
|
if (null === $value || trim('') === $value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_int((int) $value)) {
|
||||||
|
throw new TransformationFailedException('Cannot transform ' . gettype($value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->postalCodeRepository->find((int) $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function transform($value)
|
||||||
|
{
|
||||||
|
if (null === $value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value instanceof PostalCode) {
|
||||||
|
return $value->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new TransformationFailedException('Could not reverseTransform ' . gettype($value));
|
||||||
|
}
|
||||||
|
}
|
49
src/Bundle/ChillMainBundle/Form/Type/PickPostalCodeType.php
Normal file
49
src/Bundle/ChillMainBundle/Form/Type/PickPostalCodeType.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Form\Type;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\PostalCode;
|
||||||
|
use Chill\MainBundle\Form\Type\DataTransformer\PostalCodeToIdTransformer;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\Form\FormView;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
class PickPostalCodeType extends AbstractType
|
||||||
|
{
|
||||||
|
private PostalCodeToIdTransformer $postalCodeToIdTransformer;
|
||||||
|
|
||||||
|
public function __construct(PostalCodeToIdTransformer $postalCodeToIdTransformer)
|
||||||
|
{
|
||||||
|
$this->postalCodeToIdTransformer = $postalCodeToIdTransformer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder->addViewTransformer($this->postalCodeToIdTransformer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||||
|
{
|
||||||
|
$view->vars['uniqid'] = $view->vars['attr']['data-input-postal-code'] = uniqid('input_pick_postal_code_');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver
|
||||||
|
->setDefault('class', PostalCode::class)
|
||||||
|
->setDefault('multiple', false)
|
||||||
|
->setAllowedTypes('multiple', ['bool'])
|
||||||
|
->setDefault('compound', false);
|
||||||
|
}
|
||||||
|
}
|
@ -18,10 +18,9 @@ use Doctrine\ORM\EntityManagerInterface;
|
|||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
use Doctrine\ORM\Query\ResultSetMapping;
|
use Doctrine\ORM\Query\ResultSetMapping;
|
||||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||||
use Doctrine\Persistence\ObjectRepository;
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
||||||
final class PostalCodeRepository implements ObjectRepository
|
final class PostalCodeRepository implements PostalCodeRepositoryInterface
|
||||||
{
|
{
|
||||||
private EntityManagerInterface $entityManager;
|
private EntityManagerInterface $entityManager;
|
||||||
|
|
||||||
@ -29,7 +28,7 @@ final class PostalCodeRepository implements ObjectRepository
|
|||||||
|
|
||||||
public function __construct(EntityManagerInterface $entityManager)
|
public function __construct(EntityManagerInterface $entityManager)
|
||||||
{
|
{
|
||||||
$this->repository = $entityManager->getRepository(PostalCode::class);
|
$this->repository = $entityManager->getRepository($this->getClassName());
|
||||||
$this->entityManager = $entityManager;
|
$this->entityManager = $entityManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,20 +50,11 @@ final class PostalCodeRepository implements ObjectRepository
|
|||||||
return $this->repository->find($id, $lockMode, $lockVersion);
|
return $this->repository->find($id, $lockMode, $lockVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return PostalCode[]
|
|
||||||
*/
|
|
||||||
public function findAll(): array
|
public function findAll(): array
|
||||||
{
|
{
|
||||||
return $this->repository->findAll();
|
return $this->repository->findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed|null $limit
|
|
||||||
* @param mixed|null $offset
|
|
||||||
*
|
|
||||||
* @return PostalCode[]
|
|
||||||
*/
|
|
||||||
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
|
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
|
||||||
{
|
{
|
||||||
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||||
@ -95,7 +85,7 @@ final class PostalCodeRepository implements ObjectRepository
|
|||||||
return $this->repository->findOneBy($criteria, $orderBy);
|
return $this->repository->findOneBy($criteria, $orderBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getClassName()
|
public function getClassName(): string
|
||||||
{
|
{
|
||||||
return PostalCode::class;
|
return PostalCode::class;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\Country;
|
||||||
|
use Chill\MainBundle\Entity\PostalCode;
|
||||||
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
|
|
||||||
|
interface PostalCodeRepositoryInterface extends ObjectRepository
|
||||||
|
{
|
||||||
|
public function countByPattern(string $pattern, ?Country $country): int;
|
||||||
|
|
||||||
|
public function find($id, $lockMode = null, $lockVersion = null): ?PostalCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return PostalCode[]
|
||||||
|
*/
|
||||||
|
public function findAll(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed|null $limit
|
||||||
|
* @param mixed|null $offset
|
||||||
|
*
|
||||||
|
* @return PostalCode[]
|
||||||
|
*/
|
||||||
|
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array;
|
||||||
|
|
||||||
|
public function findByPattern(string $pattern, ?Country $country, ?int $start = 0, ?int $limit = 50): array;
|
||||||
|
|
||||||
|
public function findOneBy(array $criteria, ?array $orderBy = null): ?PostalCode;
|
||||||
|
|
||||||
|
public function getClassName(): string;
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
import { createApp } from 'vue';
|
||||||
|
import PickPostalCode from 'ChillMainAssets/vuejs/PickPostalCode/PickPostalCode';
|
||||||
|
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n';
|
||||||
|
import { appMessages } from 'ChillMainAssets/vuejs/PickEntity/i18n';
|
||||||
|
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
|
||||||
|
|
||||||
|
const i18n = _createI18n(appMessages);
|
||||||
|
|
||||||
|
|
||||||
|
function loadOnePicker(el, input, uniqId, city) {
|
||||||
|
const app = createApp({
|
||||||
|
template: '<pick-postal-code @select-city="onCitySelected" @removeCity="onCityRemoved" :picked="city"></pick-postal-code>',
|
||||||
|
components: {
|
||||||
|
PickPostalCode,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
city: city,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onCitySelected(city) {
|
||||||
|
this.city = city;
|
||||||
|
input.value = city.id;
|
||||||
|
},
|
||||||
|
onCityRemoved(city) {
|
||||||
|
this.city = null;
|
||||||
|
input.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.use(i18n)
|
||||||
|
.mount(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadDynamicPickers(element) {
|
||||||
|
|
||||||
|
let apps = element.querySelectorAll('[data-module="pick-postal-code"]');
|
||||||
|
|
||||||
|
apps.forEach(function(el) {
|
||||||
|
|
||||||
|
console.log('el', el);
|
||||||
|
const
|
||||||
|
uniqId = el.dataset.uniqid,
|
||||||
|
input = document.querySelector(`input[data-input-uniqid="${uniqId}"]`),
|
||||||
|
cityIdValue = input.value === '' ? null : input.value
|
||||||
|
;
|
||||||
|
|
||||||
|
console.log('uniqid', uniqId);
|
||||||
|
console.log('input', input);
|
||||||
|
console.log('input value', input.value);
|
||||||
|
console.log('cityIdValue', cityIdValue);
|
||||||
|
|
||||||
|
if (cityIdValue !== null) {
|
||||||
|
makeFetch('GET', `/api/1.0/main/postal-code/${cityIdValue}.json`).then(city => {
|
||||||
|
loadOnePicker(el, input, uniqId, city);
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
loadOnePicker(el, input, uniqId, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function(e) {
|
||||||
|
loadDynamicPickers(document)
|
||||||
|
})
|
@ -0,0 +1,4 @@
|
|||||||
|
# Pickpostalcode
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
<PickPostalCode />
|
@ -0,0 +1,108 @@
|
|||||||
|
<template>
|
||||||
|
<div class="PickPostalCode">
|
||||||
|
<vue-multiselect
|
||||||
|
id="citySelector"
|
||||||
|
@search-change="listenInputSearch"
|
||||||
|
ref="citySelector"
|
||||||
|
v-model="internalPicked"
|
||||||
|
@select="selectCity"
|
||||||
|
@remove="remove"
|
||||||
|
name=""
|
||||||
|
track-by="id"
|
||||||
|
label="value"
|
||||||
|
:custom-label="transName"
|
||||||
|
:placeholder="$t('select_city')"
|
||||||
|
:select-label="$t('multiselect.select_label')"
|
||||||
|
:deselect-label="$t('multiselect.deselect_label')"
|
||||||
|
:selected-label="$t('multiselect.selected_label')"
|
||||||
|
:taggable="true"
|
||||||
|
:multiple="false"
|
||||||
|
:internal-search="false"
|
||||||
|
:loading="isLoading"
|
||||||
|
:options="cities"></vue-multiselect>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="js">
|
||||||
|
|
||||||
|
import VueMultiselect from "vue-multiselect";
|
||||||
|
import {reactive, defineProps, onMounted} from "vue";
|
||||||
|
import {fetchCities, searchCities} from "./api";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
VueMultiselect,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
cities: [],
|
||||||
|
internalPicked: null,
|
||||||
|
isLoading: false,
|
||||||
|
abortControllers: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['pickCity', 'removeCity'],
|
||||||
|
props: {
|
||||||
|
picked: {
|
||||||
|
type: Object,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
country: {
|
||||||
|
type: Object,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.picked !== null) {
|
||||||
|
this.internalPicked = this.picked;
|
||||||
|
this.cities.push(this.picked);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
transName(value) {
|
||||||
|
return (value.code && value.name) ? `${value.name} (${value.code})` : '';
|
||||||
|
},
|
||||||
|
selectCity(city) {
|
||||||
|
console.log('city', city);
|
||||||
|
this.$emit('selectCity', city);
|
||||||
|
},
|
||||||
|
listenInputSearch(query) {
|
||||||
|
if (query.length <= 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let c = this.abortControllers.pop();
|
||||||
|
|
||||||
|
while (typeof c !== 'undefined') {
|
||||||
|
c.abort();
|
||||||
|
c = this.abortControllers.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isLoading = true;
|
||||||
|
let controller = new AbortController();
|
||||||
|
this.abortControllers.push(controller);
|
||||||
|
|
||||||
|
searchCities(query, this.country, controller).then(
|
||||||
|
newCities => {
|
||||||
|
this.cities = this.cities.filter(city => city.id === this.picked);
|
||||||
|
newCities.forEach(item => {
|
||||||
|
this.cities.push(item);
|
||||||
|
})
|
||||||
|
this.isLoading = false;
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error); //TODO better error handling
|
||||||
|
this.isLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
remove(item) {
|
||||||
|
this.$emit('removeCity', item);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
@ -0,0 +1,3 @@
|
|||||||
|
.PickPostalCode {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
import {makeFetch, fetchResults} from 'ChillMainAssets/lib/api/apiMethods';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint chill_api_single_postal_code__index
|
||||||
|
* method GET, get Cities Object
|
||||||
|
* @params {object} a country object
|
||||||
|
* @returns {Promise} a promise containing all Postal Code objects filtered with country
|
||||||
|
*/
|
||||||
|
const fetchCities = (country) => {
|
||||||
|
// warning: do not use fetchResults (in apiMethods): we need only a **part** of the results in the db
|
||||||
|
const params = new URLSearchParams({item_per_page: 100});
|
||||||
|
|
||||||
|
if (country !== null) {
|
||||||
|
params.append('country', country.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeFetch('GET', `/api/1.0/main/postal-code.json?${params.toString()}`).then(r => Promise.resolve(r.results));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint chill_main_postalcodeapi_search
|
||||||
|
* method GET, get Cities Object
|
||||||
|
* @params {string} search a search string
|
||||||
|
* @params {object} country a country object
|
||||||
|
* @params {AbortController} an abort controller
|
||||||
|
* @returns {Promise} a promise containing all Postal Code objects filtered with country and a search string
|
||||||
|
*/
|
||||||
|
const searchCities = (search, country, controller) => {
|
||||||
|
const url = '/api/1.0/main/postal-code/search.json?';
|
||||||
|
const params = new URLSearchParams({q: search});
|
||||||
|
|
||||||
|
if (country !== null) {
|
||||||
|
Object.assign('country', country.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeFetch('GET', url + params, null, {signal: controller.signal})
|
||||||
|
.then(result => Promise.resolve(result.results));
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
fetchCities,
|
||||||
|
searchCities,
|
||||||
|
};
|
@ -238,3 +238,9 @@
|
|||||||
<input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %} data-input-uniqid="{{ form.vars['uniqid'] }}"/>
|
<input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %} data-input-uniqid="{{ form.vars['uniqid'] }}"/>
|
||||||
<div data-module="pick-dynamic" data-types="{{ form.vars['types']|json_encode }}" data-multiple="{{ form.vars['multiple'] }}" data-uniqid="{{ form.vars['uniqid'] }}"></div>
|
<div data-module="pick-dynamic" data-types="{{ form.vars['types']|json_encode }}" data-multiple="{{ form.vars['multiple'] }}" data-uniqid="{{ form.vars['uniqid'] }}"></div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block pick_postal_code_widget %}
|
||||||
|
{{ form_help(form)}}
|
||||||
|
<input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %} data-input-uniqid="{{ form.vars['uniqid'] }}"/>
|
||||||
|
<div data-module="pick-postal-code" data-uniqid="{{ form.vars['uniqid'] }}"></div>
|
||||||
|
{% endblock %}
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Form\Type;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\PostalCode;
|
||||||
|
use Chill\MainBundle\Form\Type\DataTransformer\PostalCodeToIdTransformer;
|
||||||
|
use Chill\MainBundle\Form\Type\PickPostalCodeType;
|
||||||
|
use Chill\MainBundle\Repository\PostalCodeRepositoryInterface;
|
||||||
|
use Prophecy\Argument;
|
||||||
|
use Prophecy\PhpUnit\ProphecyTrait;
|
||||||
|
use ReflectionClass;
|
||||||
|
use Symfony\Component\Form\PreloadedExtension;
|
||||||
|
use Symfony\Component\Form\Test\TypeTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
final class PickPostalCodeTypeTest extends TypeTestCase
|
||||||
|
{
|
||||||
|
use ProphecyTrait;
|
||||||
|
|
||||||
|
public function testSubmitValidData(): void
|
||||||
|
{
|
||||||
|
$form = $this->factory->create(PickPostalCodeType::class, null);
|
||||||
|
|
||||||
|
$form->submit(['1']);
|
||||||
|
|
||||||
|
$this->assertTrue($form->isSynchronized());
|
||||||
|
|
||||||
|
$this->assertEquals(1, $form->getData()->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getExtensions()
|
||||||
|
{
|
||||||
|
$postalCodeRepository = $this->prophesize(PostalCodeRepositoryInterface::class);
|
||||||
|
$postalCodeRepository->find(Argument::type('string'))
|
||||||
|
->will(static function ($args) {
|
||||||
|
$postalCode = new PostalCode();
|
||||||
|
$reflectionClass = new ReflectionClass($postalCode);
|
||||||
|
$id = $reflectionClass->getProperty('id');
|
||||||
|
$id->setAccessible(true);
|
||||||
|
$id->setValue($postalCode, (int) $args[0]);
|
||||||
|
|
||||||
|
return $postalCode;
|
||||||
|
});
|
||||||
|
|
||||||
|
$type = new PickPostalCodeType(
|
||||||
|
new PostalCodeToIdTransformer(
|
||||||
|
$postalCodeRepository->reveal()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
new PreloadedExtension([$type], []),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -70,6 +70,7 @@ module.exports = function(encore, entries)
|
|||||||
encore.addEntry('mod_entity_workflow_subscribe', __dirname + '/Resources/public/module/entity-workflow-subscribe/index.js');
|
encore.addEntry('mod_entity_workflow_subscribe', __dirname + '/Resources/public/module/entity-workflow-subscribe/index.js');
|
||||||
encore.addEntry('mod_entity_workflow_pick', __dirname + '/Resources/public/module/entity-workflow-pick/index.js');
|
encore.addEntry('mod_entity_workflow_pick', __dirname + '/Resources/public/module/entity-workflow-pick/index.js');
|
||||||
encore.addEntry('mod_wopi_link', __dirname + '/Resources/public/module/wopi-link/index.js');
|
encore.addEntry('mod_wopi_link', __dirname + '/Resources/public/module/wopi-link/index.js');
|
||||||
|
encore.addEntry('mod_pick_postal_code', __dirname + '/Resources/public/module/pick-postal-code/index.js');
|
||||||
|
|
||||||
// Vue entrypoints
|
// Vue entrypoints
|
||||||
encore.addEntry('vue_address', __dirname + '/Resources/public/vuejs/Address/index.js');
|
encore.addEntry('vue_address', __dirname + '/Resources/public/vuejs/Address/index.js');
|
||||||
|
@ -11,7 +11,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\PersonBundle\Controller;
|
namespace Chill\PersonBundle\Controller;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\PostalCode;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\MainBundle\Form\Type\PickPostalCodeType;
|
||||||
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
||||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||||
use Chill\MainBundle\Repository\UserRepository;
|
use Chill\MainBundle\Repository\UserRepository;
|
||||||
@ -92,12 +94,14 @@ class ReassignAccompanyingPeriodController extends AbstractController
|
|||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
$userFrom = $form['user']->getData();
|
$userFrom = $form['user']->getData();
|
||||||
|
$postalCodes = $form['postal_code']->getData() instanceof PostalCode ? [$form['postal_code']->getData()] : [];
|
||||||
|
|
||||||
$total = $this->accompanyingPeriodACLAwareRepository->countByUserOpenedAccompanyingPeriod($userFrom);
|
$total = $this->accompanyingPeriodACLAwareRepository->countByUserOpenedAccompanyingPeriod($userFrom);
|
||||||
$paginator = $this->paginatorFactory->create($total);
|
$paginator = $this->paginatorFactory->create($total);
|
||||||
$periods = $this->accompanyingPeriodACLAwareRepository
|
$periods = $this->accompanyingPeriodACLAwareRepository
|
||||||
->findByUserOpenedAccompanyingPeriod(
|
->findByUserAndPostalCodesOpenedAccompanyingPeriod(
|
||||||
$userFrom,
|
$userFrom,
|
||||||
|
$postalCodes,
|
||||||
['openingDate' => 'ASC'],
|
['openingDate' => 'ASC'],
|
||||||
$paginator->getItemsPerPage(),
|
$paginator->getItemsPerPage(),
|
||||||
$paginator->getCurrentPageFirstItemNumber()
|
$paginator->getCurrentPageFirstItemNumber()
|
||||||
@ -148,7 +152,9 @@ class ReassignAccompanyingPeriodController extends AbstractController
|
|||||||
{
|
{
|
||||||
$data = [
|
$data = [
|
||||||
'user' => null,
|
'user' => null,
|
||||||
|
'postal_code' => null,
|
||||||
];
|
];
|
||||||
|
|
||||||
$builder = $this->formFactory->createBuilder(FormType::class, $data, [
|
$builder = $this->formFactory->createBuilder(FormType::class, $data, [
|
||||||
'method' => 'get', 'csrf_protection' => false, ]);
|
'method' => 'get', 'csrf_protection' => false, ]);
|
||||||
|
|
||||||
@ -158,12 +164,17 @@ class ReassignAccompanyingPeriodController extends AbstractController
|
|||||||
'label' => 'reassign.Current user',
|
'label' => 'reassign.Current user',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'help' => 'reassign.Choose a user and click on "Filter" to apply',
|
'help' => 'reassign.Choose a user and click on "Filter" to apply',
|
||||||
|
])
|
||||||
|
->add('postal_code', PickPostalCodeType::class, [
|
||||||
|
'label' => 'reassign.Filter by postal code',
|
||||||
|
'required' => false,
|
||||||
|
'help' => 'reassign.Filter course which are located inside a postal code',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $builder->getForm();
|
return $builder->getForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildReassignForm(array $periodIds, ?User $userFrom): FormInterface
|
private function buildReassignForm(array $periodIds, ?User $userFrom = null): FormInterface
|
||||||
{
|
{
|
||||||
$defaultData = [
|
$defaultData = [
|
||||||
'userFrom' => $userFrom,
|
'userFrom' => $userFrom,
|
||||||
|
@ -11,7 +11,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\PersonBundle\Repository;
|
namespace Chill\PersonBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\Address;
|
||||||
use Chill\MainBundle\Entity\Location;
|
use Chill\MainBundle\Entity\Location;
|
||||||
|
use Chill\MainBundle\Entity\PostalCode;
|
||||||
use Chill\MainBundle\Entity\Scope;
|
use Chill\MainBundle\Entity\Scope;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Entity\UserJob;
|
use Chill\MainBundle\Entity\UserJob;
|
||||||
@ -19,10 +21,14 @@ use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
|
|||||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
|
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
|
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
|
||||||
|
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Doctrine\DBAL\Types\Types;
|
||||||
|
use Doctrine\ORM\Query\Expr\Join;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use Symfony\Component\Security\Core\Security;
|
use Symfony\Component\Security\Core\Security;
|
||||||
use function count;
|
use function count;
|
||||||
@ -49,7 +55,12 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
|
|||||||
$this->centerResolverDispatcher = $centerResolverDispatcher;
|
$this->centerResolverDispatcher = $centerResolverDispatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildQueryOpenedAccompanyingCourseByUser(?User $user)
|
/**
|
||||||
|
* @param array|PostalCode[]
|
||||||
|
*
|
||||||
|
* @return QueryBuilder
|
||||||
|
*/
|
||||||
|
public function buildQueryOpenedAccompanyingCourseByUser(?User $user, array $postalCodes = [])
|
||||||
{
|
{
|
||||||
$qb = $this->accompanyingPeriodRepository->createQueryBuilder('ap');
|
$qb = $this->accompanyingPeriodRepository->createQueryBuilder('ap');
|
||||||
|
|
||||||
@ -65,6 +76,37 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
|
|||||||
->setParameter('now', new DateTime('now'))
|
->setParameter('now', new DateTime('now'))
|
||||||
->setParameter('draft', AccompanyingPeriod::STEP_DRAFT);
|
->setParameter('draft', AccompanyingPeriod::STEP_DRAFT);
|
||||||
|
|
||||||
|
if ([] !== $postalCodes) {
|
||||||
|
$qb->join('ap.locationHistories', 'location_history')
|
||||||
|
->leftJoin(PersonHouseholdAddress::class, 'person_address', Join::WITH, 'IDENTITY(location_history.personLocation) = IDENTITY(person_address.person)')
|
||||||
|
->join(
|
||||||
|
Address::class,
|
||||||
|
'address',
|
||||||
|
Join::WITH,
|
||||||
|
'COALESCE(IDENTITY(location_history.addressLocation), IDENTITY(person_address.address)) = address.id'
|
||||||
|
)
|
||||||
|
->andWhere(
|
||||||
|
$qb->expr()->orX(
|
||||||
|
$qb->expr()->isNull('person_address'),
|
||||||
|
$qb->expr()->andX(
|
||||||
|
$qb->expr()->lte('person_address.validFrom', ':now'),
|
||||||
|
$qb->expr()->orX(
|
||||||
|
$qb->expr()->isNull('person_address.validTo'),
|
||||||
|
$qb->expr()->lt('person_address.validTo', ':now')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->andWhere(
|
||||||
|
$qb->expr()->isNull('location_history.endDate')
|
||||||
|
)
|
||||||
|
->andWhere(
|
||||||
|
$qb->expr()->in('address.postcode', ':postal_codes')
|
||||||
|
)
|
||||||
|
->setParameter('now', new DateTimeImmutable('now'), Types::DATE_IMMUTABLE)
|
||||||
|
->setParameter('postal_codes', $postalCodes);
|
||||||
|
}
|
||||||
|
|
||||||
return $qb;
|
return $qb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,6 +119,18 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
|
|||||||
return $qb->getQuery()->getSingleScalarResult();
|
return $qb->getQuery()->getSingleScalarResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function countByUserAndPostalCodesOpenedAccompanyingPeriod(?User $user, array $postalCodes): int
|
||||||
|
{
|
||||||
|
if (null === $user) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->buildQueryOpenedAccompanyingCourseByUser($user, $postalCodes)
|
||||||
|
->select('COUNT(ap)')
|
||||||
|
->getQuery()
|
||||||
|
->getSingleScalarResult();
|
||||||
|
}
|
||||||
|
|
||||||
public function countByUserOpenedAccompanyingPeriod(?User $user): int
|
public function countByUserOpenedAccompanyingPeriod(?User $user): int
|
||||||
{
|
{
|
||||||
if (null === $user) {
|
if (null === $user) {
|
||||||
@ -158,6 +212,24 @@ final class AccompanyingPeriodACLAwareRepository implements AccompanyingPeriodAC
|
|||||||
return $qb->getQuery()->getResult();
|
return $qb->getQuery()->getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findByUserAndPostalCodesOpenedAccompanyingPeriod(?User $user, array $postalCodes, array $orderBy = [], int $limit = 0, int $offset = 50): array
|
||||||
|
{
|
||||||
|
if (null === $user) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb = $this->buildQueryOpenedAccompanyingCourseByUser($user);
|
||||||
|
|
||||||
|
$qb->setFirstResult($offset)
|
||||||
|
->setMaxResults($limit);
|
||||||
|
|
||||||
|
foreach ($orderBy as $field => $direction) {
|
||||||
|
$qb->addOrderBy('ap.' . $field, $direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $qb->getQuery()->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array|AccompanyingPeriod[]
|
* @return array|AccompanyingPeriod[]
|
||||||
*/
|
*/
|
||||||
|
@ -11,6 +11,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\PersonBundle\Repository;
|
namespace Chill\PersonBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\PostalCode;
|
||||||
use Chill\MainBundle\Entity\Scope;
|
use Chill\MainBundle\Entity\Scope;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Entity\UserJob;
|
use Chill\MainBundle\Entity\UserJob;
|
||||||
@ -25,6 +26,11 @@ interface AccompanyingPeriodACLAwareRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function countByUnDispatched(array $jobs, array $services, array $administrativeLocations): int;
|
public function countByUnDispatched(array $jobs, array $services, array $administrativeLocations): int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array|PostalCode[] $postalCodes
|
||||||
|
*/
|
||||||
|
public function countByUserAndPostalCodesOpenedAccompanyingPeriod(?User $user, array $postalCodes): int;
|
||||||
|
|
||||||
public function countByUserOpenedAccompanyingPeriod(?User $user): int;
|
public function countByUserOpenedAccompanyingPeriod(?User $user): int;
|
||||||
|
|
||||||
public function findByPerson(
|
public function findByPerson(
|
||||||
@ -43,5 +49,10 @@ interface AccompanyingPeriodACLAwareRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function findByUnDispatched(array $jobs, array $services, array $administrativeLocations, ?int $limit = null, ?int $offset = null): array;
|
public function findByUnDispatched(array $jobs, array $services, array $administrativeLocations, ?int $limit = null, ?int $offset = null): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array|PostalCode[] $postalCodes
|
||||||
|
*/
|
||||||
|
public function findByUserAndPostalCodesOpenedAccompanyingPeriod(?User $user, array $postalCodes, array $orderBy = [], int $limit = 0, int $offset = 50): array;
|
||||||
|
|
||||||
public function findByUserOpenedAccompanyingPeriod(?User $user, array $orderBy = [], int $limit = 0, int $offset = 50): array;
|
public function findByUserOpenedAccompanyingPeriod(?User $user, array $orderBy = [], int $limit = 0, int $offset = 50): array;
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,13 @@
|
|||||||
{% block js %}
|
{% block js %}
|
||||||
{{ encore_entry_script_tags('mod_set_referrer') }}
|
{{ encore_entry_script_tags('mod_set_referrer') }}
|
||||||
{{ encore_entry_script_tags('mod_pickentity_type') }}
|
{{ encore_entry_script_tags('mod_pickentity_type') }}
|
||||||
|
{{ encore_entry_script_tags('mod_pick_postal_code') }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
{{ encore_entry_link_tags('mod_set_referrer') }}
|
{{ encore_entry_link_tags('mod_set_referrer') }}
|
||||||
{{ encore_entry_link_tags('mod_pickentity_type') }}
|
{{ encore_entry_link_tags('mod_pickentity_type') }}
|
||||||
|
{{ encore_entry_link_tags('mod_pick_postal_code') }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% macro period_meta(period) %}
|
{% macro period_meta(period) %}
|
||||||
@ -48,6 +50,8 @@
|
|||||||
{{ form_start(form) }}
|
{{ form_start(form) }}
|
||||||
{{ form_label(form.user ) }}
|
{{ form_label(form.user ) }}
|
||||||
{{ form_widget(form.user, {'attr': {'class': 'select2'}}) }}
|
{{ form_widget(form.user, {'attr': {'class': 'select2'}}) }}
|
||||||
|
{{ form_label(form.postal_code) }}
|
||||||
|
{{ form_widget(form.postal_code) }}
|
||||||
<ul class="record_actions">
|
<ul class="record_actions">
|
||||||
<li>
|
<li>
|
||||||
<button type="submit" class="btn btn-misc">
|
<button type="submit" class="btn btn-misc">
|
||||||
|
@ -937,6 +937,8 @@ reassign:
|
|||||||
All periods on this list will be reassigned to this user, excepted the one you manually reassigned before: Tous les parcours visibles sur cette page seront assignés à cet utilisateur, sauf ceux que vous aurez assigné à un utilisateur manuellement.
|
All periods on this list will be reassigned to this user, excepted the one you manually reassigned before: Tous les parcours visibles sur cette page seront assignés à cet utilisateur, sauf ceux que vous aurez assigné à un utilisateur manuellement.
|
||||||
Reassign: Assigner le référent
|
Reassign: Assigner le référent
|
||||||
List periods to be able to reassign them: Choisissez un utilisateur et cliquez sur "Filtrer" pour visualiser ses parcours. Vous pourrez ensuite les réassigner.
|
List periods to be able to reassign them: Choisissez un utilisateur et cliquez sur "Filtrer" pour visualiser ses parcours. Vous pourrez ensuite les réassigner.
|
||||||
|
Filter by postal code: Filtrer par code postal
|
||||||
|
Filter course which are located inside a postal code: Afficher uniquement les parcours localisés auprès de ce code postal (une commune peut comporter plusieurs codes postaux).
|
||||||
|
|
||||||
notification:
|
notification:
|
||||||
Notify referrer: Notifier le référent
|
Notify referrer: Notifier le référent
|
||||||
|
Loading…
x
Reference in New Issue
Block a user