Replace Maternity with Women's Health (OB/GYN) and configurable service lines.
Deploy Ladill Care / deploy (push) Successful in 26s
Deploy Ladill Care / deploy (push) Successful in 26s
Keeps Fertility as a separate specialty, adds OB/GYN and fertility staff roles, and migrates live org settings from maternity → womens_health. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -45,13 +45,160 @@ class SpecialtyModuleService
|
||||
return config('care.specialty_modules', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy maternity module key → womens_health.
|
||||
*/
|
||||
public function normalizeKey(string $key): string
|
||||
{
|
||||
return $key === 'maternity' ? 'womens_health' : $key;
|
||||
}
|
||||
|
||||
public function definition(string $key): ?array
|
||||
{
|
||||
$key = $this->normalizeKey($key);
|
||||
$catalog = $this->catalog();
|
||||
|
||||
return $catalog[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate org settings from maternity → womens_health (idempotent).
|
||||
*/
|
||||
public function migrateMaternityToWomensHealth(Organization $organization): Organization
|
||||
{
|
||||
$settings = is_array($organization->settings) ? $organization->settings : [];
|
||||
$modules = is_array($settings['specialty_modules'] ?? null) ? $settings['specialty_modules'] : [];
|
||||
$provisioning = is_array($settings['specialty_module_provisioning'] ?? null)
|
||||
? $settings['specialty_module_provisioning']
|
||||
: [];
|
||||
|
||||
$changed = false;
|
||||
if (! empty($modules['maternity']) && empty($modules['womens_health'])) {
|
||||
$modules['womens_health'] = true;
|
||||
$modules['maternity'] = false;
|
||||
$changed = true;
|
||||
}
|
||||
if (isset($provisioning['maternity']) && ! isset($provisioning['womens_health'])) {
|
||||
$provisioning['womens_health'] = $provisioning['maternity'];
|
||||
unset($provisioning['maternity']);
|
||||
$changed = true;
|
||||
} elseif (isset($provisioning['maternity']) && isset($provisioning['womens_health'])) {
|
||||
unset($provisioning['maternity']);
|
||||
$changed = true;
|
||||
}
|
||||
|
||||
if (! $changed) {
|
||||
return $organization;
|
||||
}
|
||||
|
||||
$settings['specialty_modules'] = $modules;
|
||||
$settings['specialty_module_provisioning'] = $provisioning;
|
||||
$organization->update(['settings' => $settings]);
|
||||
|
||||
// Point legacy maternity departments at the new type when still labeled maternity.
|
||||
Department::query()
|
||||
->where('organization_id', $organization->id)
|
||||
->where('type', 'maternity')
|
||||
->update(['type' => 'womens_health']);
|
||||
|
||||
return $organization->fresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array{label: string, default_on: bool}>
|
||||
*/
|
||||
public function serviceLines(string $moduleKey): array
|
||||
{
|
||||
$definition = $this->definition($moduleKey);
|
||||
$lines = $definition['service_lines'] ?? [];
|
||||
if (! is_array($lines)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$out = [];
|
||||
foreach ($lines as $key => $line) {
|
||||
if (! is_string($key) || ! is_array($line)) {
|
||||
continue;
|
||||
}
|
||||
$out[$key] = [
|
||||
'label' => (string) ($line['label'] ?? $key),
|
||||
'default_on' => (bool) ($line['default_on'] ?? true),
|
||||
];
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enabled service lines for an org module (defaults when unset).
|
||||
*
|
||||
* @return array<string, bool>
|
||||
*/
|
||||
public function enabledServiceLines(Organization $organization, string $moduleKey): array
|
||||
{
|
||||
$moduleKey = $this->normalizeKey($moduleKey);
|
||||
$catalog = $this->serviceLines($moduleKey);
|
||||
if ($catalog === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$stored = data_get(
|
||||
$organization->settings,
|
||||
"specialty_module_provisioning.{$moduleKey}.service_lines",
|
||||
[],
|
||||
);
|
||||
$stored = is_array($stored) ? $stored : [];
|
||||
|
||||
$out = [];
|
||||
foreach ($catalog as $key => $line) {
|
||||
$out[$key] = array_key_exists($key, $stored)
|
||||
? (bool) $stored[$key]
|
||||
: $line['default_on'];
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, bool> $desired
|
||||
*/
|
||||
public function syncServiceLines(Organization $organization, string $moduleKey, array $desired): void
|
||||
{
|
||||
$moduleKey = $this->normalizeKey($moduleKey);
|
||||
$catalog = $this->serviceLines($moduleKey);
|
||||
abort_unless($catalog !== [], 404);
|
||||
|
||||
$settings = is_array($organization->settings) ? $organization->settings : [];
|
||||
$provisioning = is_array($settings['specialty_module_provisioning'] ?? null)
|
||||
? $settings['specialty_module_provisioning']
|
||||
: [];
|
||||
$record = is_array($provisioning[$moduleKey] ?? null) ? $provisioning[$moduleKey] : [];
|
||||
|
||||
$lines = [];
|
||||
foreach (array_keys($catalog) as $key) {
|
||||
$lines[$key] = (bool) ($desired[$key] ?? false);
|
||||
}
|
||||
// Keep at least one line on so the module remains usable.
|
||||
if (! in_array(true, $lines, true)) {
|
||||
$first = array_key_first($catalog);
|
||||
if ($first !== null) {
|
||||
$lines[$first] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$record['service_lines'] = $lines;
|
||||
$provisioning[$moduleKey] = $record;
|
||||
$settings['specialty_module_provisioning'] = $provisioning;
|
||||
$organization->update(['settings' => $settings]);
|
||||
}
|
||||
|
||||
public function isServiceLineEnabled(Organization $organization, string $moduleKey, string $lineKey): bool
|
||||
{
|
||||
$enabled = $this->enabledServiceLines($organization, $moduleKey);
|
||||
|
||||
return (bool) ($enabled[$lineKey] ?? false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Heroicon-style icon identifier from the specialty catalog (e.g. bolt, heart).
|
||||
*/
|
||||
@@ -86,10 +233,18 @@ class SpecialtyModuleService
|
||||
|
||||
public function isEnabled(Organization $organization, string $key): bool
|
||||
{
|
||||
$key = $this->normalizeKey($key);
|
||||
|
||||
if (! $this->definition($key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// One-time maternity → womens_health settings migration.
|
||||
if ($key === 'womens_health'
|
||||
&& data_get($organization->settings, 'specialty_modules.maternity')) {
|
||||
$organization = $this->migrateMaternityToWomensHealth($organization);
|
||||
}
|
||||
|
||||
if ($this->isDefaultOnPaidPlans($key) && $this->plans->hasPaidPlan($organization)) {
|
||||
return true;
|
||||
}
|
||||
@@ -245,6 +400,7 @@ class SpecialtyModuleService
|
||||
*/
|
||||
public function memberAccessLevel(Organization $organization, ?Member $member, string $key): string
|
||||
{
|
||||
$key = $this->normalizeKey($key);
|
||||
$cacheKey = $organization->id.'|'.($member?->id ?? 'guest').'|'.$key;
|
||||
if (isset($this->memberAccessLevelCache[$cacheKey])) {
|
||||
return $this->memberAccessLevelCache[$cacheKey];
|
||||
@@ -294,6 +450,7 @@ class SpecialtyModuleService
|
||||
*/
|
||||
public function memberCanManage(Organization $organization, ?Member $member, string $key): bool
|
||||
{
|
||||
$key = $this->normalizeKey($key);
|
||||
if (! $this->isEnabled($organization, $key) || ! $member) {
|
||||
return false;
|
||||
}
|
||||
@@ -358,6 +515,7 @@ class SpecialtyModuleService
|
||||
*/
|
||||
public function memberCanView(Organization $organization, ?Member $member, string $key): bool
|
||||
{
|
||||
$key = $this->normalizeKey($key);
|
||||
if ($this->memberCanManage($organization, $member, $key)
|
||||
|| $this->memberCanRefer($organization, $member, $key)) {
|
||||
return true;
|
||||
@@ -413,6 +571,7 @@ class SpecialtyModuleService
|
||||
*/
|
||||
public function memberCanRefer(Organization $organization, ?Member $member, string $key): bool
|
||||
{
|
||||
$key = $this->normalizeKey($key);
|
||||
if ($this->memberCanManage($organization, $member, $key)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user