diff --git a/app/Actions/ExportReport.php b/app/Actions/ExportReport.php index f212633b..827edfba 100644 --- a/app/Actions/ExportReport.php +++ b/app/Actions/ExportReport.php @@ -21,6 +21,8 @@ class ExportReport extends Action protected bool | null $showMissingValues = false; + protected bool | null $addCasesInMonitoring = false; + protected function setUp(): void { parent::setUp(); @@ -58,6 +60,13 @@ public function setShowMissingValues(?bool $showMissingValues): self return $this; } + public function setAddCasesInMonitoring(?bool $addCasesInMonitoring): self + { + $this->addCasesInMonitoring = $addCasesInMonitoring; + + return $this; + } + public function generateExport(): BinaryFileResponse { $service = new BeneficiariesV2(); @@ -65,6 +74,7 @@ public function generateExport(): BinaryFileResponse ->setStartDate($this->startDate) ->setEndDate($this->endDate) ->setShowMissingValue($this->showMissingValues) + ->setAddCasesInMonitoring($this->addCasesInMonitoring) ->composeReport(); $fileName = \sprintf('%s_%s_%s.xlsx', $this->startDate, $this->endDate, $this->reportType->value); diff --git a/app/Concerns/HasPermissions.php b/app/Concerns/HasPermissions.php index a9c356d4..504962fa 100644 --- a/app/Concerns/HasPermissions.php +++ b/app/Concerns/HasPermissions.php @@ -78,4 +78,22 @@ public function hasAccessToCommunity() return (bool) $this->permissions?->admin_permissions->contains(AdminPermission::CAN_CHANGE_ORGANISATION_PROFILE); } + + public function canSearchBeneficiary(): bool + { + if ($this->isNgoAdmin()) { + return true; + } + + return $this->permissions?->case_permissions->contains(CasePermission::CAN_SEARCH_AND_COPY_CASES_IN_ALL_CENTERS); + } + + public function hasAccessToReports(): bool + { + if ($this->isNgoAdmin()) { + return true; + } + + return (bool) $this->permissions?->case_permissions->contains(CasePermission::HAS_ACCESS_TO_STATISTICS); + } } diff --git a/app/Concerns/LogsActivityOptions.php b/app/Concerns/LogsActivityOptions.php index be13ad7e..5ebaa60f 100644 --- a/app/Concerns/LogsActivityOptions.php +++ b/app/Concerns/LogsActivityOptions.php @@ -5,6 +5,7 @@ namespace App\Concerns; use App\Models\Activity; +use Illuminate\Database\Eloquent\Relations\HasMany; use Spatie\Activitylog\LogOptions; use Spatie\Activitylog\Traits\LogsActivity; @@ -31,4 +32,10 @@ public function tapActivity(Activity $activity, string $eventName) $activity->event = $activity->subject_type; $activity->subject()->associate($this->beneficiary); } + + public function activity(): HasMany + { + return $this->hasMany(Activity::class, 'subject_id') + ->where('subject_type', 'beneficiary'); + } } diff --git a/app/Concerns/PreventMultipleSubmit.php b/app/Concerns/PreventMultipleSubmit.php new file mode 100644 index 00000000..720fee73 --- /dev/null +++ b/app/Concerns/PreventMultipleSubmit.php @@ -0,0 +1,29 @@ +id()); + $lock = Cache::lock($cacheKey, 5); + + if (! $lock->get()) { + throw new Halt(); + } + } + + public function beforeSave(): void + { + $this->beforeCreate(); + } +} diff --git a/app/Filament/Admin/Resources/BenefitResource/Pages/CreateBenefit.php b/app/Filament/Admin/Resources/BenefitResource/Pages/CreateBenefit.php index 1c41f7a9..0a5bf179 100644 --- a/app/Filament/Admin/Resources/BenefitResource/Pages/CreateBenefit.php +++ b/app/Filament/Admin/Resources/BenefitResource/Pages/CreateBenefit.php @@ -4,12 +4,15 @@ namespace App\Filament\Admin\Resources\BenefitResource\Pages; +use App\Concerns\PreventMultipleSubmit; use App\Filament\Admin\Resources\BenefitResource; use Filament\Resources\Pages\CreateRecord; use Illuminate\Contracts\Support\Htmlable; class CreateBenefit extends CreateRecord { + use PreventMultipleSubmit; + protected static string $resource = BenefitResource::class; protected static bool $canCreateAnother = false; diff --git a/app/Filament/Admin/Resources/InstitutionResource/Pages/CreateInstitution.php b/app/Filament/Admin/Resources/InstitutionResource/Pages/CreateInstitution.php index 95568f40..680b8b23 100644 --- a/app/Filament/Admin/Resources/InstitutionResource/Pages/CreateInstitution.php +++ b/app/Filament/Admin/Resources/InstitutionResource/Pages/CreateInstitution.php @@ -4,6 +4,7 @@ namespace App\Filament\Admin\Resources\InstitutionResource\Pages; +use App\Concerns\PreventMultipleSubmit; use App\Filament\Admin\Resources\InstitutionResource; use App\Filament\Admin\Resources\UserInstitutionResource\Pages\EditUserInstitution; use App\Forms\Components\Repeater; @@ -17,6 +18,7 @@ class CreateInstitution extends CreateRecord { use HasWizard; + use PreventMultipleSubmit; protected static string $resource = InstitutionResource::class; diff --git a/app/Filament/Admin/Resources/OrganizationResource/Pages/CreateOrganization.php b/app/Filament/Admin/Resources/OrganizationResource/Pages/CreateOrganization.php index e70abbc9..e3a9aec3 100644 --- a/app/Filament/Admin/Resources/OrganizationResource/Pages/CreateOrganization.php +++ b/app/Filament/Admin/Resources/OrganizationResource/Pages/CreateOrganization.php @@ -4,11 +4,14 @@ namespace App\Filament\Admin\Resources\OrganizationResource\Pages; +use App\Concerns\PreventMultipleSubmit; use App\Filament\Admin\Resources\OrganizationResource; use Filament\Resources\Pages\CreateRecord; class CreateOrganization extends CreateRecord { + use PreventMultipleSubmit; + protected static string $resource = OrganizationResource::class; protected static bool $canCreateAnother = false; diff --git a/app/Filament/Admin/Resources/ResultResource/Pages/CreateResult.php b/app/Filament/Admin/Resources/ResultResource/Pages/CreateResult.php index 56a0b92f..35f8cf2e 100644 --- a/app/Filament/Admin/Resources/ResultResource/Pages/CreateResult.php +++ b/app/Filament/Admin/Resources/ResultResource/Pages/CreateResult.php @@ -4,12 +4,15 @@ namespace App\Filament\Admin\Resources\ResultResource\Pages; +use App\Concerns\PreventMultipleSubmit; use App\Filament\Admin\Resources\ResultResource; use Filament\Resources\Pages\CreateRecord; use Illuminate\Contracts\Support\Htmlable; class CreateResult extends CreateRecord { + use PreventMultipleSubmit; + protected static string $resource = ResultResource::class; protected static bool $canCreateAnother = false; diff --git a/app/Filament/Admin/Resources/RoleResource/Pages/CreateRole.php b/app/Filament/Admin/Resources/RoleResource/Pages/CreateRole.php index c7d958fd..bb0b48af 100644 --- a/app/Filament/Admin/Resources/RoleResource/Pages/CreateRole.php +++ b/app/Filament/Admin/Resources/RoleResource/Pages/CreateRole.php @@ -4,12 +4,15 @@ namespace App\Filament\Admin\Resources\RoleResource\Pages; +use App\Concerns\PreventMultipleSubmit; use App\Filament\Admin\Resources\RoleResource; use Filament\Resources\Pages\CreateRecord; use Illuminate\Contracts\Support\Htmlable; class CreateRole extends CreateRecord { + use PreventMultipleSubmit; + protected static string $resource = RoleResource::class; protected static bool $canCreateAnother = false; diff --git a/app/Filament/Admin/Resources/ServiceResource/Pages/CreateService.php b/app/Filament/Admin/Resources/ServiceResource/Pages/CreateService.php index fa64447d..a9eb84ec 100644 --- a/app/Filament/Admin/Resources/ServiceResource/Pages/CreateService.php +++ b/app/Filament/Admin/Resources/ServiceResource/Pages/CreateService.php @@ -4,12 +4,15 @@ namespace App\Filament\Admin\Resources\ServiceResource\Pages; +use App\Concerns\PreventMultipleSubmit; use App\Filament\Admin\Resources\ServiceResource; use Filament\Resources\Pages\CreateRecord; use Illuminate\Contracts\Support\Htmlable; class CreateService extends CreateRecord { + use PreventMultipleSubmit; + protected static string $resource = ServiceResource::class; protected static bool $canCreateAnother = false; diff --git a/app/Filament/Admin/Resources/UserResource/Pages/CreateUser.php b/app/Filament/Admin/Resources/UserResource/Pages/CreateUser.php index d69f1e44..4f3016ee 100644 --- a/app/Filament/Admin/Resources/UserResource/Pages/CreateUser.php +++ b/app/Filament/Admin/Resources/UserResource/Pages/CreateUser.php @@ -4,11 +4,14 @@ namespace App\Filament\Admin\Resources\UserResource\Pages; +use App\Concerns\PreventMultipleSubmit; use App\Filament\Admin\Resources\UserResource; use Filament\Resources\Pages\CreateRecord; class CreateUser extends CreateRecord { + use PreventMultipleSubmit; + protected static string $resource = UserResource::class; protected static bool $canCreateAnother = false; diff --git a/app/Filament/Organizations/Pages/ReportsPage.php b/app/Filament/Organizations/Pages/ReportsPage.php index 998fd995..f759c997 100644 --- a/app/Filament/Organizations/Pages/ReportsPage.php +++ b/app/Filament/Organizations/Pages/ReportsPage.php @@ -8,8 +8,12 @@ use App\Enums\ReportType; use App\Forms\Components\ReportTable; use Filament\Forms; +use Filament\Forms\Components\Checkbox; +use Filament\Forms\Components\DatePicker; +use Filament\Forms\Components\Select; use Filament\Forms\Concerns\InteractsWithForms; use Filament\Forms\Form; +use Filament\Forms\Get; use Filament\Infolists\Components\Section; use Filament\Infolists\Contracts\HasInfolists; use Filament\Infolists\Infolist; @@ -17,6 +21,7 @@ use Filament\Pages\Page; use Illuminate\Contracts\Support\Htmlable; use Illuminate\Contracts\View\View; +use Illuminate\Support\HtmlString; class ReportsPage extends Page implements Forms\Contracts\HasForms, HasInfolists { @@ -37,6 +42,13 @@ class ReportsPage extends Page implements Forms\Contracts\HasForms, HasInfolists public $show_missing_values; + public $add_cases_in_monitoring; + + public static function canAccess(): bool + { + return auth()->user()->hasAccessToReports(); + } + public static function getNavigationGroup(): ?string { return __('navigation.statistics._group'); @@ -69,22 +81,39 @@ protected function getFormSchema(): array Forms\Components\Section::make() ->columns(4) ->schema([ - Forms\Components\Select::make('report_type') + Select::make('report_type') ->key('report_type') ->label(__('report.labels.report_type')) ->columnSpan(2) ->options(ReportType::options()) ->searchable(), - Forms\Components\DatePicker::make('start_date') + DatePicker::make('start_date') ->label(__('report.labels.start_date')) - ->default(now()->startOfMonth()), + ->default(now()->startOfMonth()) + ->maxDate(fn (Get $get) => $get('end_date') ? debug($get('end_date')) : now()) + ->live(), - Forms\Components\DatePicker::make('end_date') + DatePicker::make('end_date') ->label(__('report.labels.end_date')) - ->default(now()), + ->default(now()) + ->minDate(fn (Get $get) => $get('start_date') ?? null) + ->maxDate(now()) + ->live(), + + Checkbox::make('add_cases_in_monitoring') + ->label( + new HtmlString( + \sprintf( + '%s', + __('report.helpers.add_cases_in_monitoring'), + __('report.labels.add_cases_in_monitoring'), + ) + ) + ) + ->columnSpan(2), - Forms\Components\Checkbox::make('show_missing_values') + Checkbox::make('show_missing_values') ->label(__('report.labels.show_missing_values')) ->default(true) ->columnSpan(2), @@ -107,7 +136,8 @@ public function infolist(Infolist $infolist): Infolist ->setReportType($this->report_type) ->setStartDate($this->start_date) ->setEndDate($this->end_date) - ->setShowMissingValues($this->show_missing_values), + ->setShowMissingValues($this->show_missing_values) + ->setAddCasesInMonitoring($this->add_cases_in_monitoring), ]) ->schema([ $this->reportTable(), @@ -121,7 +151,8 @@ public function reportTable(): ReportTable ->setReportType($this->report_type ? ReportType::tryFrom($this->report_type) : null) ->setStartDate($this->start_date) ->setEndDate($this->end_date) - ->setShowMissingValue($this->show_missing_values); + ->setShowMissingValue($this->show_missing_values) + ->setAddCasesInMonitoring($this->add_cases_in_monitoring); } public function render(): View diff --git a/app/Filament/Organizations/Resources/BeneficiaryInterventionResource/Pages/CreateBeneficiaryIntervention.php b/app/Filament/Organizations/Resources/BeneficiaryInterventionResource/Pages/CreateBeneficiaryIntervention.php index 111dbdd1..2a620f6f 100644 --- a/app/Filament/Organizations/Resources/BeneficiaryInterventionResource/Pages/CreateBeneficiaryIntervention.php +++ b/app/Filament/Organizations/Resources/BeneficiaryInterventionResource/Pages/CreateBeneficiaryIntervention.php @@ -4,10 +4,13 @@ namespace App\Filament\Organizations\Resources\BeneficiaryInterventionResource\Pages; +use App\Concerns\PreventMultipleSubmit; use App\Filament\Organizations\Resources\BeneficiaryInterventionResource; use Filament\Resources\Pages\CreateRecord; class CreateBeneficiaryIntervention extends CreateRecord { + use PreventMultipleSubmit; + protected static string $resource = BeneficiaryInterventionResource::class; } diff --git a/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/CloseFile/CreateCloseFile.php b/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/CloseFile/CreateCloseFile.php index a7baf168..6bdfc8c0 100644 --- a/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/CloseFile/CreateCloseFile.php +++ b/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/CloseFile/CreateCloseFile.php @@ -4,6 +4,7 @@ namespace App\Filament\Organizations\Resources\BeneficiaryResource\Pages\CloseFile; +use App\Concerns\PreventMultipleSubmit; use App\Filament\Organizations\Resources\BeneficiaryResource; use App\Models\Specialist; use App\Services\Breadcrumb\BeneficiaryBreadcrumb; @@ -18,6 +19,7 @@ class CreateCloseFile extends EditRecord { use HasWizard; + use PreventMultipleSubmit; protected static string $resource = BeneficiaryResource::class; diff --git a/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/CreateBeneficiary.php b/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/CreateBeneficiary.php index a008d4a1..91bb9104 100644 --- a/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/CreateBeneficiary.php +++ b/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/CreateBeneficiary.php @@ -4,14 +4,20 @@ namespace App\Filament\Organizations\Resources\BeneficiaryResource\Pages; +use App\Concerns\PreventMultipleSubmit; use App\Enums\AddressType; use App\Filament\Organizations\Resources\BeneficiaryResource; +use App\Forms\Components\Notice; +use App\Forms\Components\Radio; use App\Models\Beneficiary; +use App\Models\Organization; +use App\Models\Scopes\BelongsToCurrentTenant; use App\Rules\ValidCNP; +use Filament\Facades\Filament; use Filament\Forms\Components\Actions\Action; use Filament\Forms\Components\Checkbox; use Filament\Forms\Components\Grid; -use Filament\Forms\Components\Hidden; +use Filament\Forms\Components\Group; use Filament\Forms\Components\Placeholder; use Filament\Forms\Components\Section; use Filament\Forms\Components\TextInput; @@ -21,11 +27,14 @@ use Filament\Resources\Pages\CreateRecord; use Filament\Resources\Pages\CreateRecord\Concerns\HasWizard; use Illuminate\Contracts\Support\Htmlable; +use Illuminate\Database\Query\Builder; use Illuminate\Support\HtmlString; +use Illuminate\Validation\Rules\Unique; class CreateBeneficiary extends CreateRecord { use HasWizard; + use PreventMultipleSubmit; protected static string $resource = BeneficiaryResource::class; @@ -72,7 +81,7 @@ protected function setParentBeneficiary(): void public function getStartStep(): int { - return $this->parentBeneficiary ? 2 : 1; + return $this->parentBeneficiary ? 3 : 1; } protected function getSteps(): array @@ -94,53 +103,191 @@ protected function getSteps(): array ->hiddenLabel() ->columnSpanFull() ->content(__('beneficiary.placeholder.consent')), - - Placeholder::make('consent_placeholder') - ->hiddenLabel() - ->columnSpanFull() - ->content(new HtmlString('' . __('beneficiary.placeholder.check_beneficiary_exists') . '')), - + ]), + ]), + Step::make(__('field.cnp')) + ->schema([ + Group::make() + ->maxWidth('3xl') + ->schema([ TextInput::make('cnp') - ->label(__('field.cnp')) - ->nullable() + ->label(__('field.beneficiary_cnp')) + ->placeholder(__('placeholder.cnp')) + ->maxLength(13) + ->mask('9999999999999') ->rule(new ValidCNP) - ->hidden() - ->hintAction( - Action::make('check_cnp') - ->label(__('field.check')) - ->action(function (Get $get, Set $set) { - $beneficiary = Beneficiary::query() - ->where('cnp', $get('cnp')) - ->first(); - if ($beneficiary !== null) { - $set('beneficiary_status', 1); - } else { - $set('beneficiary_status', 0); - } - }), + ->unique( + ignorable: $this->parentBeneficiary, + ignoreRecord: true, + modifyRuleUsing: function (Unique $rule, ?Beneficiary $record) { + $initialID = 0; + if ($this->parentBeneficiary?->id) { + $initialID = $parentBeneficiary->initial_id ?? $this->parentBeneficiary->id; + } + if (! $initialID && $record) { + $initialID = $record->initial_id ?? $record->id; + } + + return + $rule->where(fn (Builder $query) => $query->whereNot('id', $initialID) + ->where(fn (Builder $query) => $query->whereNot('initial_id', $initialID) + ->orWhereNull('initial_id'))) + ->where('organization_id', Filament::getTenant()->id); + } ) - ->lazy(), + ->live() + ->disabled(fn (Get $get) => $get('without_cnp')), - Hidden::make('beneficiary_status') + Checkbox::make('without_cnp') + ->label(__('field.without_cnp')) ->live(), - Placeholder::make('beneficiary_exists') - ->hiddenLabel() - ->columnSpanFull() - ->content(new HtmlString(__('beneficiary.placeholder.beneficiary_exists'))) - ->visible(fn (Get $get) => $get('beneficiary_status') === 1), + Notice::make('beneficiary_exist') + ->key('beneficiary_exist') + ->color('primary') + ->visible( + fn (Get $get) => auth()->user()->canSearchBeneficiary() ? + Beneficiary::query() + ->where('cnp', $get('cnp')) + ->whereIn( + 'organization_id', + auth()->user() + ->organizations + ->filter(fn (Organization $organization) => $organization->institution_id == Filament::getTenant()->institution_id) + ->pluck('id') + ->toArray() + ) + ->withoutGlobalScopes([BelongsToCurrentTenant::class]) + ->first() : + Beneficiary::query() + ->where('cnp', $get('cnp')) + ->first() + ) + ->content(function (Get $get) { + return new HtmlString(__('beneficiary.placeholder.beneficiary_exists')); + $beneficiary = Beneficiary::query() + ->where('cnp', $get('cnp')) + ->first(); - Placeholder::make('beneficiary_not_exists') - ->hiddenLabel() - ->columnSpanFull() - ->content(new HtmlString(__('beneficiary.placeholder.beneficiary_not_exists'))) - ->visible(fn (Get $get) => $get('beneficiary_status') === 0), + if (! $beneficiary) { + $organizations = auth()->user()->organizations + ->filter(fn (Organization $organization) => $organization->institution_id == Filament::getTenant()->institution_id); + $beneficiary = Beneficiary::query() + ->where('cnp', $get('cnp')) + ->whereIn('organization_id', $organizations->pluck('id')->toArray()) + ->withoutGlobalScopes([BelongsToCurrentTenant::class]) + ->with('organization') + ->first(); + + if (! $beneficiary) { + return ''; + } + + return new HtmlString(__('beneficiary.placeholder.beneficiary_exists', [ + 'url' => BeneficiaryResource::getUrl('view', ['record' => $beneficiary, 'tenant' => $beneficiary->organization]), + ])); + } + + return new HtmlString(__('beneficiary.placeholder.beneficiary_exists', [ + 'url' => BeneficiaryResource::getUrl('view', ['record' => $beneficiary]), + ])); + }) + ->registerActions([ + Action::make('view_beneficiary') + ->label(__('general.action.view_details')) + ->link() + ->url( + fn (Get $get) => BeneficiaryResource::getUrl('view', [ + 'record' => Beneficiary::query() + ->where('cnp', $get('cnp')) + ->first(), + ]) + ) + ->visible( + fn (Get $get) => Beneficiary::query() + ->where('cnp', $get('cnp')) + ->first() + ), + + Action::make('view_beneficiary_from_another_tenant') + ->label(__('general.action.view_details')) + ->link() + ->modalHeading(__('beneficiary.headings.modal_create_beneficiary_from_anther_tenant')) + ->form([ + Radio::make('copy_beneficiary') + ->label(__('beneficiary.labels.beneficiary_exist')) + ->options([ + 'yes' => __('beneficiary.labels.copy_data_from_another_tenant'), + 'no' => __('beneficiary.labels.continue_register_without_copy'), + ]), + ]) + ->modalSubmitActionLabel(__('beneficiary.action.register')) + ->action(function (array $data, Get $get, Set $set): void { + if ($data['copy_beneficiary'] !== 'yes') { + return; + } + + $beneficiary = Beneficiary::query() + ->where('cnp', $get('cnp')) + ->whereIn( + 'organization_id', + auth()->user() + ->organizations + ->filter( + fn (Organization $organization) => $organization->institution_id == Filament::getTenant()->institution_id + && $organization !== Filament::getTenant() + ) + ->pluck('id') + ->toArray() + ) + ->withoutGlobalScopes([BelongsToCurrentTenant::class]) + ->with(['effective_residence', 'legal_residence']) + ->first(); + + $ignoredFields = [ + 'id', + 'initial_id', + 'doesnt_have_children', + 'children_total_count', + 'children_care_count', + 'children_under_18_care_count', + 'children_18_care_count', + 'children_accompanying_count', + ]; + foreach ($beneficiary->toArray() as $beneficiaryKey => $beneficiaryValue) { + if (\in_array($beneficiaryKey, $ignoredFields)) { + continue; + } + $set($beneficiaryKey, $beneficiaryValue); + } + }) + ->visible( + fn (Get $get) => ! Beneficiary::query() + ->where('cnp', $get('cnp')) + ->first() && + Beneficiary::query() + ->where('cnp', $get('cnp')) + ->whereIn( + 'organization_id', + auth()->user() + ->organizations + ->filter( + fn (Organization $organization) => $organization->institution_id == Filament::getTenant()->institution_id + && $organization !== Filament::getTenant() + ) + ->pluck('id') + ->toArray() + ) + ->withoutGlobalScopes([BelongsToCurrentTenant::class]) + ->first() + ), + ]), ]), ]), Step::make('beneficiary') ->label(__('beneficiary.wizard.beneficiary.label')) - ->schema(EditBeneficiaryIdentity::getBeneficiaryIdentityFormSchema($this->parentBeneficiary)) + ->schema(EditBeneficiaryIdentity::getBeneficiaryIdentityFormSchema($this->parentBeneficiary, false)) ->afterStateHydrated(function (Set $set) { $legalResidence = AddressType::LEGAL_RESIDENCE->value; $effectiveResidence = AddressType::EFFECTIVE_RESIDENCE->value; diff --git a/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/DetailedEvaluation/CreateDetailedEvaluation.php b/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/DetailedEvaluation/CreateDetailedEvaluation.php index 93a706f3..0416760f 100644 --- a/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/DetailedEvaluation/CreateDetailedEvaluation.php +++ b/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/DetailedEvaluation/CreateDetailedEvaluation.php @@ -4,6 +4,7 @@ namespace App\Filament\Organizations\Resources\BeneficiaryResource\Pages\DetailedEvaluation; +use App\Concerns\PreventMultipleSubmit; use App\Filament\Organizations\Resources\BeneficiaryResource; use App\Models\BeneficiaryPartner; use App\Services\Breadcrumb\BeneficiaryBreadcrumb; @@ -15,6 +16,7 @@ class CreateDetailedEvaluation extends EditRecord { use HasWizard; + use PreventMultipleSubmit; protected static string $resource = BeneficiaryResource::class; diff --git a/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/EditBeneficiaryIdentity.php b/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/EditBeneficiaryIdentity.php index 7d08061a..09a74ca0 100644 --- a/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/EditBeneficiaryIdentity.php +++ b/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/EditBeneficiaryIdentity.php @@ -74,73 +74,79 @@ public function form(Form $form): Form ]); } - public static function getBeneficiaryIdentityFormSchema(?Beneficiary $parentBeneficiary = null): array + public static function getBeneficiaryIdentityFormSchema(?Beneficiary $parentBeneficiary = null, bool $withCnpField = true): array { + $firstPartOfSchema = [ + Hidden::make('initial_id'), + + TextInput::make('last_name') + ->label(__('field.last_name')) + ->placeholder(__('placeholder.last_name')) + ->maxLength(50) + ->required(), + + TextInput::make('first_name') + ->label(__('field.first_name')) + ->placeholder(__('placeholder.first_name')) + ->maxLength(50) + ->required(), + + TextInput::make('prior_name') + ->label(__('field.prior_name')) + ->placeholder(__('placeholder.prior_name')) + ->maxLength(50) + ->nullable(), + + Select::make('civil_status') + ->label(__('field.civil_status')) + ->placeholder(__('placeholder.civil_status')) + ->options(CivilStatus::options()) + ->enum(CivilStatus::class), + ]; + + if ($withCnpField) { + $firstPartOfSchema[] = TextInput::make('cnp') + ->label(__('field.cnp')) + ->placeholder(__('placeholder.cnp')) + ->maxLength(13) + ->mask('9999999999999') + ->unique( + ignorable: $parentBeneficiary, + ignoreRecord: true, + modifyRuleUsing: function (Unique $rule, ?Beneficiary $record) use ($parentBeneficiary) { + $initialID = 0; + if ($parentBeneficiary?->id) { + $initialID = $parentBeneficiary->initial_id ?? $parentBeneficiary->id; + } + if (! $initialID && $record) { + $initialID = $record->initial_id ?? $record->id; + } + + return + $rule->where(fn (Builder $query) => $query->whereNot('id', $initialID) + ->where(fn (Builder $query) => $query->whereNot('initial_id', $initialID) + ->orWhereNull('initial_id'))); + } + ) + ->nullable() + ->rule(new ValidCNP) + ->lazy() + ->afterStateUpdated(function (?string $state, Set $set) { + if ($state === null) { + return; + } + + if (filled($birthdate = (new Cnp($state))->getBirthDateFromCNP())) { + $set('birthdate', $birthdate); + } + }); + } + return [ Grid::make() ->maxWidth('3xl') ->schema([ - Hidden::make('initial_id'), - - TextInput::make('last_name') - ->label(__('field.last_name')) - ->placeholder(__('placeholder.last_name')) - ->maxLength(50) - ->required(), - - TextInput::make('first_name') - ->label(__('field.first_name')) - ->placeholder(__('placeholder.first_name')) - ->maxLength(50) - ->required(), - - TextInput::make('prior_name') - ->label(__('field.prior_name')) - ->placeholder(__('placeholder.prior_name')) - ->maxLength(50) - ->nullable(), - - Select::make('civil_status') - ->label(__('field.civil_status')) - ->placeholder(__('placeholder.civil_status')) - ->options(CivilStatus::options()) - ->enum(CivilStatus::class), - - TextInput::make('cnp') - ->label(__('field.cnp')) - ->placeholder(__('placeholder.cnp')) - ->maxLength(13) - ->mask('9999999999999') - ->unique( - ignorable: $parentBeneficiary, - ignoreRecord: true, - modifyRuleUsing: function (Unique $rule, ?Beneficiary $record) use ($parentBeneficiary) { - $initialID = 0; - if ($parentBeneficiary?->id) { - $initialID = $parentBeneficiary->initial_id ?? $parentBeneficiary->id; - } - if (! $initialID && $record) { - $initialID = $record->initial_id ?? $record->id; - } - - return - $rule->where(fn (Builder $query) => $query->whereNot('id', $initialID) - ->where(fn (Builder $query) => $query->whereNot('initial_id', $initialID) - ->orWhereNull('initial_id'))); - } - ) - ->nullable() - ->rule(new ValidCNP) - ->lazy() - ->afterStateUpdated(function (?string $state, Set $set) { - if ($state === null) { - return; - } - - if (filled($birthdate = (new Cnp($state))->getBirthDateFromCNP())) { - $set('birthdate', $birthdate); - } - }), + ...$firstPartOfSchema, Select::make('gender') ->label(__('field.gender')) diff --git a/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/InitialEvaluation/CreateInitialEvaluation.php b/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/InitialEvaluation/CreateInitialEvaluation.php index 11956cf5..7f5a9c4d 100644 --- a/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/InitialEvaluation/CreateInitialEvaluation.php +++ b/app/Filament/Organizations/Resources/BeneficiaryResource/Pages/InitialEvaluation/CreateInitialEvaluation.php @@ -4,6 +4,7 @@ namespace App\Filament\Organizations\Resources\BeneficiaryResource\Pages\InitialEvaluation; +use App\Concerns\PreventMultipleSubmit; use App\Filament\Organizations\Resources\BeneficiaryResource; use App\Services\Breadcrumb\BeneficiaryBreadcrumb; use Filament\Forms\Components\Wizard\Step; @@ -14,6 +15,7 @@ class CreateInitialEvaluation extends EditRecord { use HasWizard; + use PreventMultipleSubmit; protected static string $resource = BeneficiaryResource::class; diff --git a/app/Filament/Organizations/Resources/MonitoringResource/Pages/CreateMonitoring.php b/app/Filament/Organizations/Resources/MonitoringResource/Pages/CreateMonitoring.php index bf177b49..c3c6ca3e 100644 --- a/app/Filament/Organizations/Resources/MonitoringResource/Pages/CreateMonitoring.php +++ b/app/Filament/Organizations/Resources/MonitoringResource/Pages/CreateMonitoring.php @@ -5,6 +5,7 @@ namespace App\Filament\Organizations\Resources\MonitoringResource\Pages; use App\Concerns\HasParentResource; +use App\Concerns\PreventMultipleSubmit; use App\Filament\Organizations\Resources\MonitoringResource; use App\Models\Monitoring; use App\Models\Specialist; @@ -21,6 +22,7 @@ class CreateMonitoring extends CreateRecord { use HasWizard; use HasParentResource; + use PreventMultipleSubmit; protected static string $resource = MonitoringResource::class; diff --git a/app/Filament/Organizations/Resources/ServiceResource/Pages/CreateService.php b/app/Filament/Organizations/Resources/ServiceResource/Pages/CreateService.php index 1cd2221d..15b57afc 100644 --- a/app/Filament/Organizations/Resources/ServiceResource/Pages/CreateService.php +++ b/app/Filament/Organizations/Resources/ServiceResource/Pages/CreateService.php @@ -4,12 +4,15 @@ namespace App\Filament\Organizations\Resources\ServiceResource\Pages; +use App\Concerns\PreventMultipleSubmit; use App\Filament\Organizations\Resources\ServiceResource; use Filament\Resources\Pages\CreateRecord; use Illuminate\Contracts\Support\Htmlable; class CreateService extends CreateRecord { + use PreventMultipleSubmit; + protected static string $resource = ServiceResource::class; protected static bool $canCreateAnother = false; diff --git a/app/Filament/Organizations/Resources/UserResource/Pages/CreateUser.php b/app/Filament/Organizations/Resources/UserResource/Pages/CreateUser.php index 5d0e5588..e3f8652e 100644 --- a/app/Filament/Organizations/Resources/UserResource/Pages/CreateUser.php +++ b/app/Filament/Organizations/Resources/UserResource/Pages/CreateUser.php @@ -4,6 +4,7 @@ namespace App\Filament\Organizations\Resources\UserResource\Pages; +use App\Concerns\PreventMultipleSubmit; use App\Filament\Organizations\Resources\UserResource; use App\Models\User; use Filament\Facades\Filament; @@ -12,6 +13,8 @@ class CreateUser extends CreateRecord { + use PreventMultipleSubmit; + protected static string $resource = UserResource::class; protected static bool $canCreateAnother = false; diff --git a/app/Forms/Components/ReportTable.php b/app/Forms/Components/ReportTable.php index 1effe928..f126f3c5 100644 --- a/app/Forms/Components/ReportTable.php +++ b/app/Forms/Components/ReportTable.php @@ -5,7 +5,6 @@ namespace App\Forms\Components; use App\Enums\ReportType; -use App\Services\Reports\Beneficiaries; use App\Services\Reports\BeneficiariesV2; use Filament\Infolists\Components\Component; use Illuminate\Support\Collection; @@ -14,7 +13,7 @@ class ReportTable extends Component { protected string $view = 'forms.components.report-table'; - protected Beneficiaries | BeneficiariesV2 $reportService; + protected BeneficiariesV2 $reportService; protected ReportType | null $reportType = null; @@ -24,6 +23,8 @@ class ReportTable extends Component protected bool | null $showMissingValues = false; + protected bool | null $addCasesInMonitoring = false; + public static function make(string | null $id = null): static { $static = app(static::class, ['id' => $id]); @@ -36,7 +37,6 @@ protected function setUp(): void { parent::setUp(); $this->reportService = new BeneficiariesV2(); -// $this->reportService = new Beneficiaries(); } public function setReportType(ReportType | string | null $reportType): self @@ -71,14 +71,17 @@ public function setShowMissingValue(?bool $showMissingValue): self return $this; } - public function composeReport(): void + public function setAddCasesInMonitoring(?bool $addCasesInMonitoring): self { - $this->reportService->composeReport(); + $this->addCasesInMonitoring = $addCasesInMonitoring; + $this->reportService->setAddCasesInMonitoring($addCasesInMonitoring); + + return $this; } - public function getReportType(): ?ReportType + public function composeReport(): void { - return $this->reportService->getReportType(); + $this->reportService->composeReport(); } public function getReportData(): Collection diff --git a/app/Services/Reports/BeneficiariesReports/BaseGenerator.php b/app/Services/Reports/BeneficiariesReports/BaseGenerator.php index 9d236c41..dd082da8 100644 --- a/app/Services/Reports/BeneficiariesReports/BaseGenerator.php +++ b/app/Services/Reports/BeneficiariesReports/BaseGenerator.php @@ -4,38 +4,45 @@ namespace App\Services\Reports\BeneficiariesReports; +use App\Enums\ActivityDescription; +use App\Enums\CaseStatus; use App\Models\Beneficiary; +use Carbon\Carbon; +use DB; +use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Query\Builder; use Illuminate\Support\Collection; abstract class BaseGenerator { - protected string | null $startDate; + protected string | null $startDate = null; protected string | null $endDate = null; protected bool | null $showMissingValues = false; + protected bool | null $addCasesInMonitoring = false; + protected $query; public function __construct() { $this->query = Beneficiary::query() - ->leftJoin('close_files', 'beneficiaries.id', '=', 'close_files.beneficiary_id') - ->toBase() ->selectRaw('COUNT(*) as total_cases'); } public function setStartDate(?string $startDate): self { - $this->startDate = $startDate; + $this->startDate = $startDate ? Carbon::parse($startDate)->startOfDay()->format('Y-m-d H:i:s') : null; return $this; } public function setEndDate(?string $endDate): self { - $this->endDate = $endDate; + $this->endDate = $endDate ? + Carbon::parse($endDate)->endOfDay()->format('Y-m-d H:i:s') : + Carbon::now()->endOfDay()->format('Y-m-d H:i:s'); return $this; } @@ -47,6 +54,13 @@ public function setShowMissingValues(?bool $showMissingValues): self return $this; } + public function setAddCasesInMonitoring(?bool $addCasesInMonitoring): self + { + $this->addCasesInMonitoring = $addCasesInMonitoring; + + return $this; + } + public function getReportData(): Collection { $this->setSelectedFields(); @@ -56,6 +70,7 @@ public function getReportData(): Collection ->groupBy($this->getGroupBy()); return $this->query + ->toBase() ->get(); } @@ -89,16 +104,70 @@ public function addConditions(): void } } - if ($this->endDate) { - $this->query->where('beneficiaries.created_at', '<=', $this->endDate . ' 23:59:59'); - } + $this->addDateConditions(); + } - if ($this->startDate) { - $this->query->where( - fn (Builder $query) => $query->where('close_files.date', '>=', $this->startDate) - ->orWhereNull('close_files.date') + public function addDateConditions(): void + { + $this->query + ->when( + $this->startDate, + fn (EloquentBuilder $query) => $query + ->whereHas( + 'activity', + fn (EloquentBuilder $query) => $query + ->where( + fn (EloquentBuilder $query) => $query->whereJsonContains('properties->attributes->status', CaseStatus::ACTIVE->value) + ->when( + $this->addCasesInMonitoring, + fn (EloquentBuilder $query) => $query->orWhereJsonContains('properties->attributes->status', CaseStatus::MONITORED->value) + ) + ) + ->where('created_at', '<=', $this->endDate) + ->whereIn('activity_log.description', [ActivityDescription::CREATED->value, ActivityDescription::UPDATED->value]) + ->whereNotExists( + fn (Builder $subQuery) => $subQuery->select(DB::raw(1)) + ->from('activity_log as sublog') + ->whereColumn('sublog.subject_id', 'activity_log.subject_id') + ->whereIn('sublog.description', [ + ActivityDescription::CREATED->value, + ActivityDescription::UPDATED->value, + ]) + ->where('sublog.subject_type', 'beneficiary') + ->whereColumn('sublog.created_at', '>', 'activity_log.created_at') + ->whereJsonContainsKey('properties->attributes->status') + ->where('sublog.created_at', '<=', $this->endDate) + ) + ) + ->orWhereHas( + 'activity', + fn (EloquentBuilder $query) => $query->whereJsonContains('properties->old->status', CaseStatus::ACTIVE->value) + ->when( + $this->addCasesInMonitoring, + fn (EloquentBuilder $query) => $query->orWhereJsonContains('properties->old->status', CaseStatus::MONITORED->value) + ) + ->whereBetween('created_at', [$this->startDate, $this->endDate]) + ) + ) + ->when( + ! $this->startDate, + fn (EloquentBuilder $query) => $query + ->whereHas( + 'activity', + fn (EloquentBuilder $query) => $query + ->where( + fn (EloquentBuilder $query) => $query + ->whereJsonContains('properties->attributes->status', CaseStatus::ACTIVE->value) + ->when( + $this->addCasesInMonitoring, + fn (EloquentBuilder $query) => $query->orWhereJsonContains('properties->attributes->status', CaseStatus::MONITORED->value) + ) + ) + ->whereIn('description', [ActivityDescription::CREATED->value, ActivityDescription::UPDATED->value]) + ->where('created_at', '<=', $this->endDate) + ->where('subject_type', 'beneficiary') + ) ); - } } public function addRelatedTables(): void diff --git a/app/Services/Reports/BeneficiariesV2.php b/app/Services/Reports/BeneficiariesV2.php index f5baa90d..01f239be 100644 --- a/app/Services/Reports/BeneficiariesV2.php +++ b/app/Services/Reports/BeneficiariesV2.php @@ -20,6 +20,8 @@ class BeneficiariesV2 protected bool | null $showMissingValue = false; + protected bool | null $addCasesInMonitoring = false; + public function setReportType(ReportType | string | null $reportType): self { $this->reportType = $reportType; @@ -48,6 +50,13 @@ public function setShowMissingValue(?bool $showMissingValue): self return $this; } + public function setAddCasesInMonitoring(?bool $addCasesInMonitoring): self + { + $this->addCasesInMonitoring = $addCasesInMonitoring; + + return $this; + } + public function composeReport(): void { $generatorClass = str_replace(' ', '', ucwords(str_replace('_', ' ', $this->reportType->value))); @@ -61,7 +70,8 @@ public function composeReport(): void $this->generator ->setStartDate($this->startDate) ->setEndDate($this->endDate) - ->setShowMissingValues($this->showMissingValue); + ->setShowMissingValues($this->showMissingValue) + ->setAddCasesInMonitoring($this->addCasesInMonitoring); } public function getReportData(): Collection diff --git a/lang/ro/beneficiary.php b/lang/ro/beneficiary.php index 17834041..da49ab80 100644 --- a/lang/ro/beneficiary.php +++ b/lang/ro/beneficiary.php @@ -14,6 +14,10 @@ 'reactivated' => 'Reactivare de caz', 'related_cases' => 'Istoric caz (fișe conectate cazului)', 'case_manager' => 'Manager de caz', + 'beneficiary_exist' => 'Beneficiarul cu CNP-ul introdus există în baza de date.', + 'copy_data_from_another_tenant' => 'Copiază datele de identificare în noul dosar de management de caz', + 'continue_register_without_copy' => 'Continuă cu adăugarea beneficiarului, fără copierea datelor de identificare în noul dosar de management de caz', + ], 'page' => [ @@ -459,6 +463,7 @@ 'action' => [ 'create' => 'Înregistrează caz nou', + 'register' => 'Înregistrează caz', 'add_child' => 'Adaugă copil', 'add_row' => 'Adauga inca un rand', 'add_meet_row' => 'Adauga inca o intrevedere', @@ -480,6 +485,10 @@ 'personal_information' => 'Informații caz', ], + 'headings' => [ + 'modal_create_beneficiary_from_anther_tenant' => 'Beneficiar identificat în baza de date', + ], + 'placeholder' => [ 'full_name' => 'Introdu nume si prenume', 'first_name' => 'Nume de familie', @@ -510,9 +519,7 @@ 'description_of_situation' => 'Descrieți pe scurt situația', 'email' => 'Introdu un email', 'consent' => 'Odată înregistrat cazul în sistem, aceste formulare de obținere a consimțământului vor putea fi încarcate în sistem, în secțiunea Documente Beneficiar.', - 'check_beneficiary_exists' => 'Verifică dacă beneficiarul există în baza de date (Opțional)', - 'beneficiary_exists' => 'CNP-ul a fost identificat în această bază de date, asociat cazului Maria Popescu. Vezi detalii', - 'beneficiary_not_exists' => 'CNP-ul nu a fost identificat în această bază de date și nici în cea a altor centre ale instituției.', + 'beneficiary_exists' => 'CNP-ul a fost identificat în această bază de date.', 'file_name' => 'Nume document', 'reactivate_text_1' => 'Prin reactivarea unui caz se va duplica dosarului beneficiarului pentru a putea fi completat cu informații noi, fără a pierde informațiile despre evaluările și managementul de caz anterior.', 'reactivate_text_2' => 'Toate datele de identitate se vor copia din dosarul curent și pot fi actualizate manual pentru această nouă reactivare. Toate formularele vor fi disponibile pentru a fi completate cu informații noi.', diff --git a/lang/ro/field.php b/lang/ro/field.php index 5b2a4fbf..b72b563a 100644 --- a/lang/ro/field.php +++ b/lang/ro/field.php @@ -14,6 +14,8 @@ 'city' => 'Localitate', 'civil_status' => 'Stare civilă', 'cnp' => 'CNP', + 'beneficiary_cnp' => 'CNP-ul beneficiarului', + 'without_cnp' => 'Nu deține / Nu știe', 'contact_notes' => 'Notițe legate de contactare beneficiar', 'county' => 'Județ', 'create_beneficiary_consent' => 'Confirm că s-a obținut acordul beneficiarului pentru înregistrarea datelor personale și utilizarea datelor rezultate din evaluare și intervenție în scopul oferirii serviciilor de management de caz.', diff --git a/lang/ro/report.php b/lang/ro/report.php index b57b5229..3ff177c2 100644 --- a/lang/ro/report.php +++ b/lang/ro/report.php @@ -11,6 +11,7 @@ 'end_date' => 'Dată final raportare', 'show_missing_values' => 'Afișează și datele lipsă (missing values) în tabel', 'total' => 'Total cazuri', + 'add_cases_in_monitoring' => 'Include și cazurile în monitorizare', ], 'table_heading' => [ @@ -93,4 +94,8 @@ 'generate' => 'Generează raport', 'export' => 'Exportă date', ], + + 'helpers' => [ + 'add_cases_in_monitoring' => 'Sistemul include în statistici toți beneficiarii care au avut un caz cu status DESCHIS cel puțin o zi în intervalul de referință (definit de datele de început/final raportare). Bifarea acestei opțiuni va include și cazurile cu status ÎN MONITORIZARE în statisticile raportate.', + ], ];