- Add kwaadpepper/serial-int-caster and SerialCasterServiceProvider - Update CodeGenerationService to encode Matricule deterministically with length 6 - Update Code value object to support lengths between 6 and 10 with 53 non-ambiguous characters - Add database migration to sanitize legacy ambiguous codes - Recreate and update unit and integration tests for CodeGenerationService and Code value object
141 lines
4.4 KiB
PHP
141 lines
4.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Contracts\QueryInputEmpty;
|
|
use App\Queries\Settings\GetSettingsQuery;
|
|
use Carbon\CarbonImmutable;
|
|
use Carbon\CarbonInterface;
|
|
use Domain\Models\ValueObjects\EmployeeUpsertData;
|
|
use Domain\Models\ValueObjects\Matricule;
|
|
use Domain\Ports\Services\EmployeeImportStorageService;
|
|
use Illuminate\Contracts\Cache\Repository as CacheRepository;
|
|
use Illuminate\Support\Carbon;
|
|
|
|
class EmployeeSyncService
|
|
{
|
|
private const SYNC_TIMESTAMP_CACHE = 'EMPLOYEE_LAST_SYNC';
|
|
|
|
public function __construct(
|
|
private readonly CacheRepository $cacheRepository,
|
|
private readonly ArcadiaEmployeeService $arcadiaEmployeeService,
|
|
private readonly CodeGenerationService $codeService,
|
|
private readonly EmployeeImportStorageService $employeeImportStorageService,
|
|
private readonly GetSettingsQuery $getSettingsQuery
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* @param array<int, \Domain\Models\ValueObjects\ShopperSiteCode>|null $sites
|
|
*/
|
|
public function synchronize(
|
|
?\DateTimeInterface $since = null,
|
|
?array $sites = null
|
|
): void {
|
|
$sitesToSync = $sites ?? ($this->getSettingsQuery)(new QueryInputEmpty())->getShopperSiteCodes();
|
|
$sinceDate = $since ?? Carbon::createFromTimestamp(0);
|
|
|
|
foreach ($sitesToSync as $siteCode) {
|
|
$employees = $this->arcadiaEmployeeService->getEmployeesOfSiteSince(
|
|
$siteCode,
|
|
$sinceDate
|
|
);
|
|
|
|
if ($employees === false) {
|
|
continue;
|
|
}
|
|
|
|
$this->upsertEmployees($employees);
|
|
}
|
|
|
|
$this->markLastSync();
|
|
}
|
|
|
|
public function getLastSync(): ?CarbonInterface
|
|
{
|
|
$timestamp = $this->cacheRepository->get(self::SYNC_TIMESTAMP_CACHE);
|
|
|
|
if (! is_string($timestamp)) {
|
|
return null;
|
|
}
|
|
|
|
return Carbon::parse($timestamp);
|
|
}
|
|
|
|
private function markLastSync(): void
|
|
{
|
|
$this->cacheRepository->remember(
|
|
self::SYNC_TIMESTAMP_CACHE,
|
|
3600 * 36, // Cache for 36 hours
|
|
fn () => Carbon::now()->format(Carbon::RFC3339)
|
|
);
|
|
}
|
|
|
|
/** @param \App\Services\ArcadiaEmployee[] $employeeList */
|
|
private function upsertEmployees(array $employeeList): EmployeeSyncServiceResult
|
|
{
|
|
$matriculesToInsert = [];
|
|
$errors = [];
|
|
|
|
$upsertData = [];
|
|
$importStartTime = CarbonImmutable::now()->subSecond();
|
|
|
|
foreach ($employeeList as $employeeImport) {
|
|
$matricule = $employeeImport->matricule->value;
|
|
|
|
if (in_array($matricule, $matriculesToInsert, true)) {
|
|
$errors[] = "Duplicate detected in the file: The matricule {$matricule} appears multiple times.";
|
|
|
|
continue;
|
|
}
|
|
|
|
$matriculesToInsert[] = $matricule;
|
|
|
|
// NOTE: We generate a new code for each row, but only insert it for new records.
|
|
$code = $this->codeService->generateFrom(new Matricule($matricule));
|
|
|
|
$upsertData[] = new EmployeeUpsertData(
|
|
matricule: $employeeImport->matricule,
|
|
firstname: $employeeImport->firstname,
|
|
lastname: $employeeImport->lastname,
|
|
etablissement: $employeeImport->etablissement,
|
|
service: $employeeImport->service,
|
|
site: $employeeImport->site,
|
|
startDate: $employeeImport->startDate,
|
|
endDate: $employeeImport->endDate,
|
|
code: $code
|
|
);
|
|
}
|
|
|
|
if (empty($upsertData) && empty($errors)) {
|
|
$errors[] = 'The file contains no valid data.';
|
|
}
|
|
|
|
if (! empty($upsertData)) {
|
|
$this->employeeImportStorageService->upsertEmployees($upsertData);
|
|
|
|
$result = $this->employeeImportStorageService
|
|
->getImportedAfter(
|
|
array_map(
|
|
fn ($m) => new Matricule($m),
|
|
$matriculesToInsert
|
|
),
|
|
$importStartTime
|
|
);
|
|
|
|
$insertedCount = $result->insertedCount;
|
|
$updatedCount = $result->updatedCount;
|
|
} else {
|
|
$insertedCount = 0;
|
|
$updatedCount = 0;
|
|
}
|
|
|
|
return new EmployeeSyncServiceResult(
|
|
insertedCount: $insertedCount,
|
|
updatedCount: $updatedCount,
|
|
errors: $errors,
|
|
);
|
|
}
|
|
}
|