diff --git a/app/Helpers/GeometryHelper.php b/app/Helpers/GeometryHelper.php index 96c918bcc..b0a4099b7 100644 --- a/app/Helpers/GeometryHelper.php +++ b/app/Helpers/GeometryHelper.php @@ -4,6 +4,7 @@ use App\Models\V2\PolygonGeometry; use App\Models\V2\Projects\Project; +use App\Models\V2\Sites\CriteriaSite; use Illuminate\Support\Facades\Log; class GeometryHelper @@ -117,4 +118,19 @@ public static function getPolygonsBbox($polygonsIds) return [$minX, $minY, $maxX, $maxY]; } + + public static function getCriteriaDataForPolygonGeometry($polygonGeometry) + { + return CriteriaSite::whereIn( + 'id', + $polygonGeometry + ->criteriaSite() + ->groupBy('criteria_id') + ->selectRaw('max(id) as latest_id') + )->get([ + 'criteria_id', + 'valid', + 'created_at as latest_created_at', + ]); + } } diff --git a/app/Http/Controllers/V2/AuditStatus/GetAuditStatusController.php b/app/Http/Controllers/V2/AuditStatus/GetAuditStatusController.php new file mode 100644 index 000000000..e0132480b --- /dev/null +++ b/app/Http/Controllers/V2/AuditStatus/GetAuditStatusController.php @@ -0,0 +1,25 @@ +auditStatuses() + ->orderBy('updated_at', 'desc') + ->orderBy('created_at', 'desc') + ->get(); + + foreach ($auditStatuses as $auditStatus) { + $auditStatus->entity_name = $auditable->getAuditableNameAttribute(); + } + + return AuditStatusResource::collection($auditStatuses); + } +} diff --git a/app/Http/Controllers/V2/AuditStatus/StoreAuditStatusController.php b/app/Http/Controllers/V2/AuditStatus/StoreAuditStatusController.php new file mode 100644 index 000000000..39cfd9349 --- /dev/null +++ b/app/Http/Controllers/V2/AuditStatus/StoreAuditStatusController.php @@ -0,0 +1,34 @@ +all(); + + if ($body['type'] === 'change-request') { + AuditStatus::where([ + ['auditable_id', $auditable->id], + ['type', 'change-request'], + ['is_active', true], + ])->update(['is_active' => false]); + $auditStatus = $this->saveAuditStatus(get_class($auditable), $auditable->id, $body['status'], $body['comment'], $body['type'], $body['is_active'], $body['request_removed']); + } else { + $auditStatus = $this->saveAuditStatus(get_class($auditable), $auditable->id, $body['status'], $body['comment'], $body['type']); + } + $auditStatus->entity_name = $auditable->name; + + return new AuditStatusResource($auditStatus); + } +} diff --git a/app/Http/Controllers/V2/Auditable/UpdateAuditableStatusController.php b/app/Http/Controllers/V2/Auditable/UpdateAuditableStatusController.php new file mode 100644 index 000000000..96e1c0d4d --- /dev/null +++ b/app/Http/Controllers/V2/Auditable/UpdateAuditableStatusController.php @@ -0,0 +1,99 @@ +authorize('update', $auditable); + + if (! $this->canChangeStatus($auditable, $request->status)) { + return response()->json(['message' => 'Cannot change status'], 400); + } + + $body = $request->all(); + $status = $body['status']; + + $auditable->status = $status; + $auditable->save(); + + if (isset($body['status'])) { + $this->saveAuditStatus(get_class($auditable), $auditable->id, $status, $body['comment'], $body['type']); + } elseif (isset($body['is_active'])) { + AuditStatus::where('auditable_id', $auditable->id) + ->where('type', $body['type']) + ->where('is_active', true) + ->update(['is_active' => false]); + $this->saveAuditStatus(get_class($auditable), $auditable->id, $status, $body['comment'], $body['type'], $body['is_active'], $body['request_removed']); + } + + return $auditable; + } + + private function canChangeStatus($auditable, $status): bool + { + switch(get_class($auditable)) { + case 'App\Models\V2\Sites\Site': + return $this->canChangeSiteStatusTo($auditable, $status); + case 'App\Models\V2\Sites\SitePolygon': + return $this->canChangeSitePolygonStatusTo($auditable, $status); + default: + return true; + } + } + + private function canChangeSiteStatusTo($auditable, $status) + { + if ($status === 'approved') { + return ! SitePolygon::where('site_id', $auditable->id)->where('status', 'approved')->exists(); + } + + return true; + } + + private function canChangeSitePolygonStatusTo($sitePolygon, $status) + { + if ($status === 'approved') { + $geometry = $sitePolygon->polygonGeometry()->first(); + + if ($geometry === null) { + return false; + } + + $criteriaList = GeometryHelper::getCriteriaDataForPolygonGeometry($geometry); + + if (empty($criteriaList)) { + return false; + } + + $criteriaList = array_filter($criteriaList, function ($criteria) { + return $criteria['criteria_id'] !== PolygonService::ESTIMATED_AREA_CRITERIA_ID; + }); + + $canApprove = true; + foreach ($criteriaList as $criteria) { + if (! $criteria['valid']) { + $canApprove = false; + + break; + } + } + + return $canApprove; + } + + return true; + } +} diff --git a/app/Http/Controllers/V2/Polygons/ViewAllSitesPolygonsForProjectController.php b/app/Http/Controllers/V2/Polygons/ViewAllSitesPolygonsForProjectController.php new file mode 100644 index 000000000..c21c12425 --- /dev/null +++ b/app/Http/Controllers/V2/Polygons/ViewAllSitesPolygonsForProjectController.php @@ -0,0 +1,17 @@ +sitePolygons()->get()); + } +} diff --git a/app/Http/Controllers/V2/Sites/SiteCheckApproveController.php b/app/Http/Controllers/V2/Sites/SiteCheckApproveController.php new file mode 100644 index 000000000..41a597a7b --- /dev/null +++ b/app/Http/Controllers/V2/Sites/SiteCheckApproveController.php @@ -0,0 +1,18 @@ +sitePolygons()->where('status', '!=', 'approved')->exists(); + + return new JsonResource(['can_approve' => $hasNonApproved]); + } +} diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index 038c84b2f..655ab0bc4 100644 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -2,9 +2,9 @@ namespace App\Http\Controllers\V2\Terrafund; +use App\Helpers\GeometryHelper; use App\Http\Controllers\Controller; use App\Models\V2\PolygonGeometry; -use App\Models\V2\Sites\CriteriaSite; use App\Models\V2\Sites\SitePolygon; use App\Models\V2\WorldCountryGeneralized; use App\Services\PolygonService; @@ -313,30 +313,12 @@ public function getCriteriaData(Request $request) return response()->json(['error' => 'Polygon not found for the given UUID'], 404); } - // Fetch data from criteria_site with distinct criteria_id based on the latest created_at - $criteriaDataQuery = 'SELECT criteria_id, MAX(created_at) AS latest_created_at - FROM criteria_site - WHERE polygon_id = ? - GROUP BY criteria_id'; + $criteriaList = GeometryHelper::getCriteriaDataForPolygonGeometry($geometry); - $criteriaData = DB::select($criteriaDataQuery, [$uuid]); - - if (empty($criteriaData)) { + if (empty($criteriaList)) { return response()->json(['error' => 'Criteria data not found for the given polygon ID'], 404); } - // Determine the validity of each criteria - $criteriaList = []; - foreach ($criteriaData as $criteria) { - $criteriaId = $criteria->criteria_id; - $valid = CriteriaSite::where(['polygon_id' => $uuid, 'criteria_id' => $criteriaId])->orderBy('created_at', 'desc')->select('valid')->first()?->valid; - $criteriaList[] = [ - 'criteria_id' => $criteriaId, - 'latest_created_at' => $criteria->latest_created_at, - 'valid' => $valid, - ]; - } - return response()->json(['polygon_id' => $uuid, 'criteria_list' => $criteriaList]); } diff --git a/app/Http/Middleware/ModelInterfaceBindingMiddleware.php b/app/Http/Middleware/ModelInterfaceBindingMiddleware.php index 53ab7f0ab..fb83beb0d 100644 --- a/app/Http/Middleware/ModelInterfaceBindingMiddleware.php +++ b/app/Http/Middleware/ModelInterfaceBindingMiddleware.php @@ -2,6 +2,7 @@ namespace App\Http\Middleware; +use App\Models\V2\AuditStatus\AuditStatus; use App\Models\V2\Forms\Form; use App\Models\V2\Forms\FormQuestionOption; use App\Models\V2\FundingProgramme; @@ -14,6 +15,7 @@ use App\Models\V2\Projects\ProjectReport; use App\Models\V2\Sites\Site; use App\Models\V2\Sites\SiteMonitoring; +use App\Models\V2\Sites\SitePolygon; use App\Models\V2\Sites\SiteReport; use Closure; use Illuminate\Http\Request; @@ -49,6 +51,8 @@ class ModelInterfaceBindingMiddleware 'form-question-option' => FormQuestionOption::class, 'project-monitoring' => ProjectMonitoring::class, 'site-monitoring' => SiteMonitoring::class, + 'site-polygon' => SitePolygon::class, + 'audit-status' => AuditStatus::class, ]; private static array $typeSlugsCache = []; diff --git a/app/Http/Requests/V2/AuditStatus/AuditStatusCreateRequest.php b/app/Http/Requests/V2/AuditStatus/AuditStatusCreateRequest.php new file mode 100644 index 000000000..81b973a63 --- /dev/null +++ b/app/Http/Requests/V2/AuditStatus/AuditStatusCreateRequest.php @@ -0,0 +1,24 @@ + 'sometimes|nullable|string', + 'comment' => 'sometimes|nullable|string', + 'status' => 'sometimes|nullable|string', + 'is_active' => 'sometimes|nullable|boolean', + 'request_removed' => 'sometimes|nullable|boolean', + ]; + } +} diff --git a/app/Http/Requests/V2/AuditStatus/AuditStatusUpdateRequest.php b/app/Http/Requests/V2/AuditStatus/AuditStatusUpdateRequest.php new file mode 100644 index 000000000..3f49cf9ad --- /dev/null +++ b/app/Http/Requests/V2/AuditStatus/AuditStatusUpdateRequest.php @@ -0,0 +1,24 @@ + 'sometimes|nullable|string', + 'comment' => 'sometimes|nullable|string', + 'status' => 'sometimes|nullable|string', + 'is_active' => 'sometimes|nullable|boolean', + 'request_removed' => 'sometimes|nullable|boolean', + ]; + } +} diff --git a/app/Http/Resources/V2/AuditStatusResource.php b/app/Http/Resources/V2/AuditStatusResource.php new file mode 100644 index 000000000..52a1f8053 --- /dev/null +++ b/app/Http/Resources/V2/AuditStatusResource.php @@ -0,0 +1,33 @@ + $this->id, + 'uuid' => $this->uuid, + 'entity_name' => $this->entity_name, + 'status' => $this->status, + 'comment' => $this->comment, + 'first_name' => $this->first_name, + 'last_name' => $this->last_name, + 'type' => $this->type, + 'is_submitted' => $this->is_submitted, + 'is_active' => $this->is_active, + 'request_removed' => $this->request_removed, + 'date_created' => $this->date_created, + 'created_by' => $this->created_by, + ]; + + return $this->appendFilesToResource($data); + } +} diff --git a/app/Http/Resources/V2/SitePolygon/SitePolygonResource.php b/app/Http/Resources/V2/SitePolygon/SitePolygonResource.php new file mode 100644 index 000000000..034f346d6 --- /dev/null +++ b/app/Http/Resources/V2/SitePolygon/SitePolygonResource.php @@ -0,0 +1,26 @@ + $this->id, + 'uuid' => $this->uuid, + 'poly_name' => $this->poly_name, + 'status' => $this->status, + 'date_created' => $this->date_created, + 'created_by' => $this->created_by, + ]; + } +} diff --git a/app/Models/Traits/SaveAuditStatusTrait.php b/app/Models/Traits/SaveAuditStatusTrait.php new file mode 100644 index 000000000..4944ca76a --- /dev/null +++ b/app/Models/Traits/SaveAuditStatusTrait.php @@ -0,0 +1,27 @@ + $auditable_type, + 'auditable_id' => $auditable_id, + 'status' => $status, + 'comment' => $comment, + 'date_created' => now(), + 'created_by' => Auth::user()->email_address, + 'type' => $type, + 'is_submitted' => $is_submitted, + 'is_active' => $is_active, + 'first_name' => Auth::user()->first_name, + 'last_name' => Auth::user()->last_name, + 'request_removed' => $request_removed, + ]); + } +} diff --git a/app/Models/V2/AuditStatus/AuditStatus.php b/app/Models/V2/AuditStatus/AuditStatus.php new file mode 100644 index 000000000..1220dff86 --- /dev/null +++ b/app/Models/V2/AuditStatus/AuditStatus.php @@ -0,0 +1,61 @@ + [ + 'validation' => 'general-documents', + 'multiple' => true, + ], + ]; + + public function registerMediaConversions(Media $media = null): void + { + $this->addMediaConversion('thumbnail') + ->width(350) + ->height(211) + ->nonQueued(); + } + + public function getRouteKeyName() + { + return 'uuid'; + } + + public function auditable() + { + return $this->morphTo(); + } +} diff --git a/app/Models/V2/AuditableModel.php b/app/Models/V2/AuditableModel.php new file mode 100644 index 000000000..03412bc0b --- /dev/null +++ b/app/Models/V2/AuditableModel.php @@ -0,0 +1,10 @@ +morphMany(AuditStatus::class, 'auditable'); + } + + public function getAuditableNameAttribute(): string + { + return $this->name; + } + /** * @return HasManyThrough A relation for all site reports associated with this project that is for an approved * site, and has a report status past due/started (has been submitted). diff --git a/app/Models/V2/Sites/Site.php b/app/Models/V2/Sites/Site.php index 96b69b828..06725d0b0 100644 --- a/app/Models/V2/Sites/Site.php +++ b/app/Models/V2/Sites/Site.php @@ -11,6 +11,8 @@ use App\Models\Traits\HasUuid; use App\Models\Traits\HasV2MediaCollections; use App\Models\Traits\UsesLinkedFields; +use App\Models\V2\AuditableModel; +use App\Models\V2\AuditStatus\AuditStatus; use App\Models\V2\Disturbance; use App\Models\V2\EntityModel; use App\Models\V2\Invasive; @@ -41,7 +43,7 @@ /** * @property string project_id */ -class Site extends Model implements MediaModel, AuditableContract, EntityModel +class Site extends Model implements MediaModel, AuditableContract, EntityModel, AuditableModel { use HasFactory; use HasUuid; @@ -363,4 +365,14 @@ public function scopeHasMonitoringData(Builder $query, $hasMonitoringData): Buil ? $query->has('monitoring') : $query->doesntHave('monitoring'); } + + public function auditStatuses(): MorphMany + { + return $this->morphMany(AuditStatus::class, 'auditable'); + } + + public function getAuditableNameAttribute(): string + { + return $this->name; + } } diff --git a/app/Models/V2/Sites/SitePolygon.php b/app/Models/V2/Sites/SitePolygon.php index d9bf5e11a..0d37ca430 100644 --- a/app/Models/V2/Sites/SitePolygon.php +++ b/app/Models/V2/Sites/SitePolygon.php @@ -3,6 +3,8 @@ namespace App\Models\V2\Sites; use App\Models\Traits\HasUuid; +use App\Models\V2\AuditableModel; +use App\Models\V2\AuditStatus\AuditStatus; use App\Models\V2\PolygonGeometry; use App\Models\V2\Projects\Project; use App\Models\V2\User; @@ -11,6 +13,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasOne; +use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\SoftDeletes; use Znck\Eloquent\Relations\BelongsToThrough; use Znck\Eloquent\Traits\BelongsToThrough as BelongsToThroughTrait; @@ -18,7 +21,7 @@ /** * @method static forPolygonGeometry($value): Builder */ -class SitePolygon extends Model +class SitePolygon extends Model implements AuditableModel { use HasUuid; use SoftDeletes; @@ -72,4 +75,19 @@ public function createdBy(): HasOne { return $this->hasOne(User::class, 'id', 'created_by'); } + + public function getRouteKeyName() + { + return 'uuid'; + } + + public function auditStatuses(): MorphMany + { + return $this->morphMany(AuditStatus::class, 'auditable'); + } + + public function getAuditableNameAttribute(): string + { + return $this->poly_name; + } } diff --git a/app/Policies/V2/AuditStatus/AuditStatusPolicy.php b/app/Policies/V2/AuditStatus/AuditStatusPolicy.php new file mode 100644 index 000000000..a7a4877b6 --- /dev/null +++ b/app/Policies/V2/AuditStatus/AuditStatusPolicy.php @@ -0,0 +1,15 @@ +email_address == $auditStatus->created_by; + } +} diff --git a/app/Policies/V2/Sites/SitePolygonPolicy.php b/app/Policies/V2/Sites/SitePolygonPolicy.php new file mode 100644 index 000000000..8781fe005 --- /dev/null +++ b/app/Policies/V2/Sites/SitePolygonPolicy.php @@ -0,0 +1,27 @@ +site()->first(); + + if ($user->can('framework-' . $site->framework_key)) { + return true; + } + + return $user->can('manage-own') && $this->isTheirs($user, $site); + } + + protected function isTheirs(?User $user, ?Site $site = null): bool + { + return $user->organisation_id == $site->project->organisation_id || $user->projects->contains($site->project_id); + } +} diff --git a/database/migrations/2024_06_06_023733_create_audit_statuses_table.php b/database/migrations/2024_06_06_023733_create_audit_statuses_table.php new file mode 100644 index 000000000..a1bbb5e5f --- /dev/null +++ b/database/migrations/2024_06_06_023733_create_audit_statuses_table.php @@ -0,0 +1,41 @@ +id(); + $table->uuid('uuid')->unique(); + $table->string('status')->nullable(); + $table->string('comment')->nullable(); + $table->string('first_name')->nullable(); + $table->string('last_name')->nullable(); + $table->enum('type', ['change-request', 'status', 'submission', 'comment'])->nullable(); + $table->boolean('is_submitted')->nullable(); + $table->boolean('is_active')->nullable(); + $table->boolean('request_removed')->nullable(); + $table->date('date_created')->nullable(); + $table->string('created_by')->nullable(); + $table->softDeletes(); + $table->timestamps(); + + $table->morphs('auditable'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('audit_statuses'); + } +}; diff --git a/openapi-src/V2/definitions/AuditStatusCreateRequest.yml b/openapi-src/V2/definitions/AuditStatusCreateRequest.yml new file mode 100644 index 000000000..adbb80ae0 --- /dev/null +++ b/openapi-src/V2/definitions/AuditStatusCreateRequest.yml @@ -0,0 +1,13 @@ +title: AuditStatusCreateRequest +type: object +properties: + status: + type: string + comment: + type: string + type: + type: string + is_active: + type: boolean + request_removed: + type: boolean diff --git a/openapi-src/V2/definitions/AuditStatusResponse.yml b/openapi-src/V2/definitions/AuditStatusResponse.yml new file mode 100644 index 000000000..f8ce37e3d --- /dev/null +++ b/openapi-src/V2/definitions/AuditStatusResponse.yml @@ -0,0 +1,34 @@ +title: AuditStatusResponse +type: object +properties: + id: + type: string + uuid: + type: string + entity_name: + type: string + status: + type: string + comment: + type: string + first_name: + type: string + last_name: + type: string + type: + type: string + is_submitted: + type: boolean + is_active: + type: boolean + request_removed: + type: boolean + date_created: + type: string + format: date + created_by: + type: string + attachments: + type: array + items: + $ref: './_index.yml#/V2FileRead' diff --git a/openapi-src/V2/definitions/AuditStatusUpdateRequest.yml b/openapi-src/V2/definitions/AuditStatusUpdateRequest.yml new file mode 100644 index 000000000..718063017 --- /dev/null +++ b/openapi-src/V2/definitions/AuditStatusUpdateRequest.yml @@ -0,0 +1,13 @@ +title: AuditStatusUpdateRequest +type: object +properties: + type: + type: string + comment: + type: string + status: + type: string + is_active: + type: boolean + request_removed: + type: boolean diff --git a/openapi-src/V2/definitions/SiteCheckApproveResponse.yml b/openapi-src/V2/definitions/SiteCheckApproveResponse.yml new file mode 100644 index 000000000..e47b48b18 --- /dev/null +++ b/openapi-src/V2/definitions/SiteCheckApproveResponse.yml @@ -0,0 +1,5 @@ +title: SiteCheckApproveResponse +type: object +properties: + can_approve: + type: boolean diff --git a/openapi-src/V2/definitions/SitePolygonResource.yml b/openapi-src/V2/definitions/SitePolygonResource.yml new file mode 100644 index 000000000..97839a16a --- /dev/null +++ b/openapi-src/V2/definitions/SitePolygonResource.yml @@ -0,0 +1,16 @@ +title: SitePolygonResource +type: object +properties: + id: + type: integer + uuid: + type: string + poly_name: + type: string + status: + type: string + date_created: + type: string + format: date-time + created_by: + type: string diff --git a/openapi-src/V2/definitions/_index.yml b/openapi-src/V2/definitions/_index.yml index c96900df3..c5587fa58 100644 --- a/openapi-src/V2/definitions/_index.yml +++ b/openapi-src/V2/definitions/_index.yml @@ -276,6 +276,10 @@ GeoJSON: $ref: './GeoJSON.yml' GeometryPost: $ref: './GeometryPost.yml' +AuditStatusCreateRequest: + $ref: './AuditStatusCreateRequest.yml' +AuditStatusResponse: + $ref: './AuditStatusResponse.yml' V2TerrafundCriteriaData: $ref: './V2TerrafundCriteriaData.yml' V2TerrafundCriteriaSite: @@ -325,4 +329,10 @@ DashboardPolygonData: GeojsonData: $ref: './GeojsonData.yml' EntityTypeResponse: - $ref: './EntityTypeResponse.yml' \ No newline at end of file + $ref: './EntityTypeResponse.yml' +AuditStatusUpdateRequest: + $ref: './AuditStatusUpdateRequest.yml' +SitePolygonResource: + $ref: './SitePolygonResource.yml' +SiteCheckApproveResponse: + $ref: './SiteCheckApproveResponse.yml' diff --git a/openapi-src/V2/paths/AuditStatus/get-v2-audit-status.yml b/openapi-src/V2/paths/AuditStatus/get-v2-audit-status.yml new file mode 100644 index 000000000..7fbe039d4 --- /dev/null +++ b/openapi-src/V2/paths/AuditStatus/get-v2-audit-status.yml @@ -0,0 +1,21 @@ +operationId: get-v2-audits-status +summary: Get all audits status by entity and entity uuid +tags: + - V2 Audits Status +parameters: + - type: string + name: ENTITY + in: path + required: true + description: allowed values project/site/site-polygon + - type: string + name: UUID + in: path + required: true +responses: + '200': + description: OK + schema: + type: array + items: + $ref: '../../definitions/_index.yml#/AuditStatusResponse' diff --git a/openapi-src/V2/paths/AuditStatus/post-v2-audit-status.yml b/openapi-src/V2/paths/AuditStatus/post-v2-audit-status.yml new file mode 100644 index 000000000..c492736d9 --- /dev/null +++ b/openapi-src/V2/paths/AuditStatus/post-v2-audit-status.yml @@ -0,0 +1,24 @@ +operationId: post-v2-audit-status +summary: Create a new audit status +tags: + - V2 Audits Status +parameters: + - type: string + name: ENTITY + in: path + required: true + description: allowed values project/site/site-polygon + - type: string + name: UUID + in: path + required: true + - in: body + name: body + description: Body to create a new audit status + schema: + $ref: '../../definitions/_index.yml#/AuditStatusCreateRequest' +responses: + '201': + description: Created + schema: + $ref: '../../definitions/_index.yml#/AuditStatusResponse' diff --git a/openapi-src/V2/paths/Entity/put-v2-entity-uuid-status.yml b/openapi-src/V2/paths/Entity/put-v2-entity-uuid-status.yml new file mode 100644 index 000000000..1e9fc5481 --- /dev/null +++ b/openapi-src/V2/paths/Entity/put-v2-entity-uuid-status.yml @@ -0,0 +1,19 @@ + +summary: Update the status of a specific entity +tags: + - V2 Projects + - V2 Sites + - V2 SitePolygons +parameters: + - type: string + name: ENTITY + in: path + required: true + description: allowed values project/site/site-polygons + - type: string + name: UUID + in: path + required: true +responses: + '200': + description: OK diff --git a/openapi-src/V2/paths/Projects/get-v2-projects-uuid-site-polygons-all.yml b/openapi-src/V2/paths/Projects/get-v2-projects-uuid-site-polygons-all.yml new file mode 100644 index 000000000..7af2ea421 --- /dev/null +++ b/openapi-src/V2/paths/Projects/get-v2-projects-uuid-site-polygons-all.yml @@ -0,0 +1,20 @@ +summary: Get all the SitePolygons from all sites belonging to a specific project +operationId: get-v2-projects-uuid-site-polygons-all +tags: + - V2 Projects + - V2 Sites + - V2 SitePolygons +parameters: + - type: string + name: UUID + in: path + required: true +produces: + - application/json +responses: + '200': + description: OK + schema: + type: array + items: + $ref: '../../definitions/_index.yml#/SitePolygonResource' diff --git a/openapi-src/V2/paths/Sites/get-v2-sites-uuid-check-approve.yml b/openapi-src/V2/paths/Sites/get-v2-sites-uuid-check-approve.yml new file mode 100644 index 000000000..1ba6f053d --- /dev/null +++ b/openapi-src/V2/paths/Sites/get-v2-sites-uuid-check-approve.yml @@ -0,0 +1,17 @@ +summary: Check if a site can be approved +operationId: get-v2-sites-uuid-check-approve +tags: + - V2 Sites +parameters: + - type: string + name: site + in: path + required: true +responses: + '200': + description: OK + schema: + type: object + properties: + data: + $ref: '../../definitions/_index.yml#/SiteCheckApproveResponse' diff --git a/openapi-src/V2/paths/_index.yml b/openapi-src/V2/paths/_index.yml index 4a3158a6a..025d73b78 100644 --- a/openapi-src/V2/paths/_index.yml +++ b/openapi-src/V2/paths/_index.yml @@ -2537,6 +2537,11 @@ /v2/geometry/{UUID}: put: $ref: './Geometry/put-v2-geometry-uuid.yml' +/v2/audit-status/{ENTITY}/{UUID}: + get: + $ref: './AuditStatus/get-v2-audit-status.yml' + post: + $ref: './AuditStatus/post-v2-audit-status.yml' /v2/sites/{site}/polygon: get: $ref: './Sites/get-v2-sites-polygons-data.yml' @@ -2615,4 +2620,13 @@ $ref: './Terrafund/get-v2-terrafund-polygon-geojson-uuid.yml' /v2/type-entity: get: - $ref: './Entity/get-v2-type-entity.yml' \ No newline at end of file + $ref: './Entity/get-v2-type-entity.yml' +/v2/{ENTITY}/{UUID}/status: + put: + $ref: './Entity/put-v2-entity-uuid-status.yml' +/v2/projects/{UUID}/site-polygons/all: + get: + $ref: './Projects/get-v2-projects-uuid-site-polygons-all.yml' +/v2/sites/{site}/check-approve: + get: + $ref: './Sites/get-v2-sites-uuid-check-approve.yml' diff --git a/resources/docs/swagger-v2.yml b/resources/docs/swagger-v2.yml index 1965c3011..e450ab440 100644 --- a/resources/docs/swagger-v2.yml +++ b/resources/docs/swagger-v2.yml @@ -44058,6 +44058,81 @@ definitions: message: type: string description: Human readable string in English to describe the error. + AuditStatusCreateRequest: + title: AuditStatusCreateRequest + type: object + properties: + status: + type: string + comment: + type: string + type: + type: string + is_active: + type: boolean + request_removed: + type: boolean + AuditStatusResponse: + title: AuditStatusResponse + type: object + properties: + id: + type: string + uuid: + type: string + entity_name: + type: string + status: + type: string + comment: + type: string + first_name: + type: string + last_name: + type: string + type: + type: string + is_submitted: + type: boolean + is_active: + type: boolean + request_removed: + type: boolean + date_created: + type: string + format: date + created_by: + type: string + attachments: + type: array + items: + title: V2FileRead + type: object + properties: + uuid: + type: string + url: + type: string + thumb_url: + type: string + collection_name: + type: string + title: + type: string + file_name: + type: string + mime_type: + type: string + size: + type: integer + lat: + type: integer + lng: + type: integer + is_public: + type: boolean + created_at: + type: string V2TerrafundCriteriaData: type: object properties: @@ -44658,6 +44733,43 @@ definitions: items: type: number description: Bounding box of the entity + AuditStatusUpdateRequest: + title: AuditStatusUpdateRequest + type: object + properties: + type: + type: string + comment: + type: string + status: + type: string + is_active: + type: boolean + request_removed: + type: boolean + SitePolygonResource: + title: SitePolygonResource + type: object + properties: + id: + type: integer + uuid: + type: string + poly_name: + type: string + status: + type: string + date_created: + type: string + format: date-time + created_by: + type: string + SiteCheckApproveResponse: + title: SiteCheckApproveResponse + type: object + properties: + can_approve: + type: boolean paths: '/v2/tree-species/{entity}/{UUID}': get: @@ -94744,6 +94856,184 @@ paths: description: This account does not have permission to update the polygon. '404': description: Geometry was not found. + '/v2/audit-status/{ENTITY}/{UUID}': + get: + operationId: get-v2-audits-status + summary: Get all audits status by entity and entity uuid + tags: + - V2 Audits Status + parameters: + - type: string + name: ENTITY + in: path + required: true + description: allowed values project/site/site-polygon + - type: string + name: UUID + in: path + required: true + responses: + '200': + description: OK + schema: + type: array + items: + title: AuditStatusResponse + type: object + properties: + id: + type: string + uuid: + type: string + entity_name: + type: string + status: + type: string + comment: + type: string + first_name: + type: string + last_name: + type: string + type: + type: string + is_submitted: + type: boolean + is_active: + type: boolean + request_removed: + type: boolean + date_created: + type: string + format: date + created_by: + type: string + attachments: + type: array + items: + title: V2FileRead + type: object + properties: + uuid: + type: string + url: + type: string + thumb_url: + type: string + collection_name: + type: string + title: + type: string + file_name: + type: string + mime_type: + type: string + size: + type: integer + lat: + type: integer + lng: + type: integer + is_public: + type: boolean + created_at: + type: string + post: + operationId: post-v2-audit-status + summary: Create a new audit status + tags: + - V2 Audits Status + parameters: + - type: string + name: ENTITY + in: path + required: true + description: allowed values project/site/site-polygon + - type: string + name: UUID + in: path + required: true + - in: body + name: body + description: Body to create a new audit status + schema: + title: AuditStatusCreateRequest + type: object + properties: + status: + type: string + comment: + type: string + type: + type: string + is_active: + type: boolean + request_removed: + type: boolean + responses: + '201': + description: Created + schema: + title: AuditStatusResponse + type: object + properties: + id: + type: string + uuid: + type: string + entity_name: + type: string + status: + type: string + comment: + type: string + first_name: + type: string + last_name: + type: string + type: + type: string + is_submitted: + type: boolean + is_active: + type: boolean + request_removed: + type: boolean + date_created: + type: string + format: date + created_by: + type: string + attachments: + type: array + items: + title: V2FileRead + type: object + properties: + uuid: + type: string + url: + type: string + thumb_url: + type: string + collection_name: + type: string + title: + type: string + file_name: + type: string + mime_type: + type: string + size: + type: integer + lat: + type: integer + lng: + type: integer + is_public: + type: boolean + created_at: + type: string '/v2/sites/{site}/polygon': get: summary: Get polygons for a specific site @@ -95894,3 +96184,83 @@ paths: error: type: string description: Error message + '/v2/{ENTITY}/{UUID}/status': + put: + summary: Update the status of a specific entity + tags: + - V2 Projects + - V2 Sites + - V2 SitePolygons + parameters: + - type: string + name: ENTITY + in: path + required: true + description: allowed values project/site/site-polygons + - type: string + name: UUID + in: path + required: true + responses: + '200': + description: OK + '/v2/projects/{UUID}/site-polygons/all': + get: + summary: Get all the SitePolygons from all sites belonging to a specific project + operationId: get-v2-projects-uuid-site-polygons-all + tags: + - V2 Projects + - V2 Sites + - V2 SitePolygons + parameters: + - type: string + name: UUID + in: path + required: true + produces: + - application/json + responses: + '200': + description: OK + schema: + type: array + items: + title: SitePolygonResource + type: object + properties: + id: + type: integer + uuid: + type: string + poly_name: + type: string + status: + type: string + date_created: + type: string + format: date-time + created_by: + type: string + '/v2/sites/{site}/check-approve': + get: + summary: Check if a site can be approved + operationId: get-v2-sites-uuid-check-approve + tags: + - V2 Sites + parameters: + - type: string + name: site + in: path + required: true + responses: + '200': + description: OK + schema: + type: object + properties: + data: + title: SiteCheckApproveResponse + type: object + properties: + can_approve: + type: boolean diff --git a/routes/api_v2.php b/routes/api_v2.php index 9aedba714..b678bb755 100644 --- a/routes/api_v2.php +++ b/routes/api_v2.php @@ -8,7 +8,10 @@ use App\Http\Controllers\V2\Applications\ExportApplicationController; use App\Http\Controllers\V2\Applications\ViewApplicationController; use App\Http\Controllers\V2\Applications\ViewMyApplicationController; +use App\Http\Controllers\V2\Auditable\UpdateAuditableStatusController; use App\Http\Controllers\V2\Audits\AdminIndexAuditsController; +use App\Http\Controllers\V2\AuditStatus\GetAuditStatusController; +use App\Http\Controllers\V2\AuditStatus\StoreAuditStatusController; use App\Http\Controllers\V2\BaselineMonitoring\BaselineMonitoringImportController; use App\Http\Controllers\V2\BaselineMonitoring\BaselineMonitoringProjectController; use App\Http\Controllers\V2\BaselineMonitoring\BaselineMonitoringSiteController; @@ -118,6 +121,7 @@ use App\Http\Controllers\V2\OwnershipStake\DeleteOwnershipStakeController; use App\Http\Controllers\V2\OwnershipStake\StoreOwnershipStakeController; use App\Http\Controllers\V2\OwnershipStake\UpdateOwnershipStakeController; +use App\Http\Controllers\V2\Polygons\ViewAllSitesPolygonsForProjectController; use App\Http\Controllers\V2\Polygons\ViewSitesPolygonsForProjectController; use App\Http\Controllers\V2\ProjectPitches\AdminIndexProjectPitchController; use App\Http\Controllers\V2\ProjectPitches\DeleteProjectPitchController; @@ -163,6 +167,7 @@ use App\Http\Controllers\V2\Sites\Monitoring\AdminSoftDeleteSiteMonitoringController; use App\Http\Controllers\V2\Sites\Monitoring\AdminUpdateSiteMonitoringController; use App\Http\Controllers\V2\Sites\Monitoring\ViewSiteMonitoringController; +use App\Http\Controllers\V2\Sites\SiteCheckApproveController; use App\Http\Controllers\V2\Sites\SitePolygonDataController; use App\Http\Controllers\V2\Sites\SoftDeleteSiteController; use App\Http\Controllers\V2\Sites\ViewASitesMonitoringsController; @@ -197,6 +202,7 @@ use App\Http\Controllers\V2\User\UpdateMyBannersController; use App\Http\Controllers\V2\Workdays\GetWorkdaysForEntityController; use App\Http\Middleware\ModelInterfaceBindingMiddleware; +use App\Models\V2\AuditableModel; use App\Models\V2\EntityModel; use App\Models\V2\MediaModel; use Illuminate\Support\Facades\Route; @@ -521,6 +527,7 @@ Route::get('/{project}/partners', ViewProjectMonitoringPartnersController::class); Route::get('/{project}/sites', ViewProjectSitesController::class); Route::get('/{project}/site-polygons', ViewSitesPolygonsForProjectController::class); + Route::get('/{project}/site-polygons/all', ViewAllSitesPolygonsForProjectController::class); Route::get('/{project}/nurseries', ViewProjectNurseriesController::class); Route::get('/{project}/files', ViewProjectGalleryController::class); Route::get('/{project}/monitorings', ViewAProjectsMonitoringsController::class); @@ -564,6 +571,7 @@ Route::post('/geometry', [GeometryController::class, 'storeSiteGeometry']); Route::get('/polygon', [SitePolygonDataController::class, 'getSitePolygonData']); Route::get('/bbox', [SitePolygonDataController::class, 'getBboxOfCompleteSite']); + Route::get('/check-approve', SiteCheckApproveController::class); }); Route::prefix('geometry')->group(function () { @@ -670,6 +678,15 @@ function () { //Route::put('file/{uuid}', [FilePropertiesController::class, 'update']); //Route::delete('file/{uuid}', [FilePropertiesController::class, 'destroy']); +ModelInterfaceBindingMiddleware::with(AuditableModel::class, function () { + Route::post('/{auditable}', StoreAuditStatusController::class); + Route::get('/{auditable}', GetAuditStatusController::class); +}, prefix: 'audit-status'); + +ModelInterfaceBindingMiddleware::with(AuditableModel::class, function () { + Route::put('/{auditable}/status', UpdateAuditableStatusController::class); +}); + Route::prefix('dashboard')->group(function () { Route::get('/restoration-strategy', ViewRestorationStrategyController::class); Route::get('/jobs-created', GetJobsCreatedController::class);