Merge remote-tracking branch 'origin/master' into rector/rules-up-to-php80

Conflicts:
	src/Bundle/ChillActivityBundle/Controller/ActivityController.php
	src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php
	src/Bundle/ChillActivityBundle/Menu/PersonMenuBuilder.php
	src/Bundle/ChillActivityBundle/Repository/ActivityACLAwareRepository.php
	src/Bundle/ChillActivityBundle/Service/DocGenerator/ActivityContext.php
	src/Bundle/ChillCalendarBundle/Command/MapAndSubscribeUserCalendarCommand.php
	src/Bundle/ChillCalendarBundle/RemoteCalendar/Connector/MSGraph/MSGraphUserRepository.php
	src/Bundle/ChillDocStoreBundle/Controller/DocumentAccompanyingCourseController.php
	src/Bundle/ChillDocStoreBundle/Controller/DocumentPersonController.php
	src/Bundle/ChillDocStoreBundle/Repository/PersonDocumentACLAwareRepository.php
	src/Bundle/ChillEventBundle/Search/EventSearch.php
	src/Bundle/ChillMainBundle/Controller/ExportController.php
	src/Bundle/ChillMainBundle/Controller/PermissionsGroupController.php
	src/Bundle/ChillMainBundle/Cron/CronManager.php
	src/Bundle/ChillMainBundle/Entity/CronJobExecution.php
	src/Bundle/ChillMainBundle/Export/ExportManager.php
	src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php
	src/Bundle/ChillMainBundle/Form/Type/Listing/FilterOrderType.php
	src/Bundle/ChillMainBundle/Repository/NotificationRepository.php
	src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php
	src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperBuilder.php
	src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelperFactory.php
	src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php
	src/Bundle/ChillPersonBundle/Controller/SocialWorkSocialActionApiController.php
	src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/AgeAggregator.php
	src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php
	src/Bundle/ChillPersonBundle/Export/Export/ListHouseholdInPeriod.php
	src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodACLAwareRepository.php
	src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php
	src/Bundle/ChillPersonBundle/Service/DocGenerator/AccompanyingPeriodContext.php
	src/Bundle/ChillPersonBundle/Service/DocGenerator/AccompanyingPeriodWorkEvaluationContext.php
	src/Bundle/ChillPersonBundle/Service/DocGenerator/PersonContext.php
	src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReports.php
	src/Bundle/ChillTaskBundle/Controller/SingleTaskController.php
This commit is contained in:
2023-07-17 12:49:13 +02:00
544 changed files with 18622 additions and 2105 deletions

View File

@@ -0,0 +1,150 @@
<?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\Service\AddressGeographicalUnit;
use Doctrine\DBAL\Connection;
use Psr\Log\LoggerInterface;
final readonly class CollateAddressWithReferenceOrPostalCode implements CollateAddressWithReferenceOrPostalCodeInterface
{
private const LOG_PREFIX = '[collate addresses] ';
/**
* For the address having an "invented" postal code, find the postal code "reference" with the same code,
* and the most similar name. When two reference code match, we add
*
* This query intentionally includes also address with reference, as the reference may be wrong.
*/
private const FORCE_ORIGINAL_POSTAL_CODE = <<<'SQL'
WITH recollate AS (
SELECT * FROM (
SELECT cma.id AS address_id, cmpc.id, cmpc.label, cmpc.code, cmpc_reference.id AS cmpc_reference_id, cmpc_reference.label, cmpc_reference.code,
RANK() OVER (PARTITION BY cma.id ORDER BY SIMILARITY(cmpc.label, cmpc_reference.label) DESC, cmpc_reference.id ASC) AS ranked
FROM
chill_main_address cma JOIN chill_main_postal_code cmpc on cma.postcode_id = cmpc.id,
chill_main_postal_code cmpc_reference
WHERE
-- use only postal code which are reference
cmpc_reference.id != cmpc.id AND cmpc_reference.origin = 0
-- only where cmpc is created manually
AND cmpc.origin != 0
-- only when postal code match
AND TRIM(REPLACE(LOWER(cmpc.code), ' ', '')) = LOWER(cmpc_reference.code)
AND cmpc.country_id = cmpc_reference.country_id
AND cma.id > :since_id -- to set the first id
) sq
WHERE ranked = 1)
UPDATE chill_main_address SET postcode_id = cmpc_reference_id FROM recollate WHERE recollate.address_id = chill_main_address.id;
SQL;
/**
* associate the address with the most similar address reference.
*
* This query intentionally ignores the existing addressreference_id, to let fixing the address match the
* most similar address reference.
*/
private const FORCE_MOST_SIMILAR_ADDRESS_REFERENCE = <<<'SQL'
WITH recollate AS (
SELECT * FROM (
SELECT cma.id AS address_id, cma.streetnumber, cma.street, cmpc.code, cmpc.label, cmar.id AS address_reference_id, cmar.streetnumber, cmar.street, cmpc_reference.code, cmpc_reference.label,
similarity(cma.street, cmar.street),
RANK() OVER (PARTITION BY cma.id ORDER BY SIMILARITY (cma.street, cmar.street) DESC, SIMILARITY (cma.streetnumber, cmar.streetnumber), cmar.id ASC) AS ranked
FROM
chill_main_address cma
JOIN chill_main_postal_code cmpc on cma.postcode_id = cmpc.id,
chill_main_address_reference cmar JOIN chill_main_postal_code cmpc_reference ON cmar.postcode_id = cmpc_reference.id
WHERE
-- only if cmpc is a reference (must be matched before executing this query)
cma.postcode_id = cmar.postcode_id
-- join cmpc to cma
AND SIMILARITY(LOWER(cma.street), LOWER(cmar.street)) > 0.6 AND LOWER(cma.streetnumber) = LOWER(cmar.streetnumber)
-- only addresses which match the address reference - let the user decide if the reference has changed
AND cma.refstatus = 'match'
-- only the most recent
AND cma.id > :since_id
) AS sq
WHERE ranked = 1
)
UPDATE chill_main_address SET addressreference_id = recollate.address_reference_id FROM recollate WHERE chill_main_address.id = recollate.address_id;
SQL;
/**
* Update the point's address with the:
*
* - address reference point, if the address match the reference with sufficient similarity
* - or the postcal code center
*/
private const UPDATE_POINT = <<<'SQL'
WITH address_geom AS (
SELECT cma.id AS address_id, COALESCE(cmar.point, cmpc.center) AS point
FROM chill_main_address cma
LEFT JOIN chill_main_address_reference cmar ON cma.addressreference_id = cmar.id AND similarity(cma.street, cmar.street) > 0.6 AND LOWER(cma.streetnumber) = LOWER(cmar.streetnumber)
LEFT JOIN chill_main_postal_code cmpc ON cma.postcode_id = cmpc.id
WHERE cma.id > :since_id
)
UPDATE chill_main_address SET point = address_geom.point FROM address_geom WHERE address_geom.address_id = chill_main_address.id
SQL;
private const MAX_ADDRESS_ID = <<<'SQL'
SELECT MAX(id) AS max_id FROM chill_main_address;
SQL;
public function __construct(
private Connection $connection,
private LoggerInterface $logger,
) {
}
/**
* @throws \Throwable
*/
public function __invoke(int $sinceId = 0): int
{
try {
[
$postCodeSetReferenceFromMostSimilar,
$addressReferenceMatch,
$pointUpdates,
$lastId,
] = $this->connection->transactional(function () use ($sinceId) {
$postCodeSetReferenceFromMostSimilar = $this->connection->executeStatement(self::FORCE_ORIGINAL_POSTAL_CODE, ['since_id' => $sinceId]);
$addressReferenceMatch = $this->connection->executeStatement(self::FORCE_MOST_SIMILAR_ADDRESS_REFERENCE, ['since_id' => $sinceId]);
$pointUpdates = $this->connection->executeStatement(self::UPDATE_POINT, ['since_id' => $sinceId]);
$lastId = $this->connection->fetchOne(self::MAX_ADDRESS_ID);
return [
$postCodeSetReferenceFromMostSimilar,
$addressReferenceMatch,
$pointUpdates,
$lastId,
];
});
} catch (\Throwable $e) {
$this->logger->error(self::LOG_PREFIX . "error while re-collating addresses", [
'message' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
throw $e;
}
$this->logger->info(self::LOG_PREFIX . "Collate the addresses with reference", [
'set_postcode_from_most_similar' => $postCodeSetReferenceFromMostSimilar,
'address_reference_match' => $addressReferenceMatch,
'point_update' => $pointUpdates,
'since_id' => $sinceId,
'last_id' => $lastId,
]);
return $lastId;
}
}

View File

@@ -0,0 +1,50 @@
<?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\Service\AddressGeographicalUnit;
use Chill\MainBundle\Cron\CronJobInterface;
use Chill\MainBundle\Entity\CronJobExecution;
use Symfony\Component\Clock\ClockInterface;
final readonly class CollateAddressWithReferenceOrPostalCodeCronJob implements CronJobInterface
{
private const LAST_MAX_ID = 'last-max-id';
public function __construct(
private ClockInterface $clock,
private CollateAddressWithReferenceOrPostalCodeInterface $collateAddressWithReferenceOrPostalCode,
) {
}
public function canRun(?CronJobExecution $cronJobExecution): bool
{
if (null === $cronJobExecution) {
return true;
}
$now = $this->clock->now();
return $now->sub(new \DateInterval('PT6H')) > $cronJobExecution->getLastStart();
}
public function getKey(): string
{
return 'collate-address';
}
public function run(array $lastExecutionData): null|array
{
$maxId = ($this->collateAddressWithReferenceOrPostalCode)($lastExecutionData[self::LAST_MAX_ID] ?? 0);
return [self::LAST_MAX_ID => $maxId];
}
}

View File

@@ -0,0 +1,20 @@
<?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\Service\AddressGeographicalUnit;
interface CollateAddressWithReferenceOrPostalCodeInterface
{
/**
* @throws \Throwable
*/
public function __invoke(int $sinceId = 0): int;
}

View File

@@ -46,8 +46,10 @@ class RefreshAddressToGeographicalUnitMaterializedViewCronJob implements CronJob
return 'refresh-materialized-view-address-to-geog-units';
}
public function run(): void
public function run(array $lastExecutionData): null|array
{
$this->connection->executeQuery('REFRESH MATERIALIZED VIEW view_chill_main_address_geographical_unit');
return null;
}
}

View File

@@ -0,0 +1,48 @@
<?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\Service\EntityInfo;
use Doctrine\DBAL\Connection;
class ViewEntityInfoManager
{
public function __construct(
/**
* @var ViewEntityInfoProviderInterface[]
*/
private iterable $vienEntityInfoProviders,
private Connection $connection,
) {
}
public function synchronizeOnDB(): void
{
$this->connection->transactional(function (Connection $conn): void {
foreach ($this->vienEntityInfoProviders as $viewProvider) {
foreach ($this->createOrReplaceViewSQL($viewProvider, $viewProvider->getViewName()) as $sql) {
$conn->executeQuery($sql);
}
}
});
}
/**
* @return array<string>
*/
private function createOrReplaceViewSQL(ViewEntityInfoProviderInterface $viewProvider, string $viewName): array
{
return [
"DROP VIEW IF EXISTS {$viewName}",
sprintf("CREATE VIEW {$viewName} AS %s", $viewProvider->getViewQuery())
];
}
}

View File

@@ -0,0 +1,19 @@
<?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\Service\EntityInfo;
interface ViewEntityInfoProviderInterface
{
public function getViewQuery(): string;
public function getViewName(): string;
}

View File

@@ -18,8 +18,12 @@ use UnexpectedValueException;
class RollingDateConverter implements RollingDateConverterInterface
{
public function convert(RollingDate $rollingDate): DateTimeImmutable
public function convert(?RollingDate $rollingDate): ?DateTimeImmutable
{
if (null === $rollingDate) {
return null;
}
switch ($rollingDate->getRoll()) {
case RollingDate::T_MONTH_CURRENT_START:
return $this->toBeginOfMonth($rollingDate->getPivotDate());

View File

@@ -15,5 +15,9 @@ use DateTimeImmutable;
interface RollingDateConverterInterface
{
public function convert(RollingDate $rollingDate): DateTimeImmutable;
/**
* @param RollingDate|null $rollingDate
* @return ($rollingDate is null ? null : DateTimeImmutable)
*/
public function convert(?RollingDate $rollingDate): ?DateTimeImmutable;
}