Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .agents/skills/usecase/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,6 @@ Find and read an existing, similar feature in the codebase as a reference implem

# 3. Implement steps

Use the `steps` skill to implement the steps.
Use the `steps` skill to implement the steps.
- If you cannot find that skill, STOP and inform user
- Don't forget to make commits at logical boundaries.
243 changes: 243 additions & 0 deletions .plans/use-cases/02_view_and_edit_requests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
# View/Edit Purchase Requests Use Case

## Overview

When a user clicks on a request ID in the requests grid, they can:
- View already-submitted requests (readonly view, with ability to withdraw if not already approved/rejected)
- Edit draft requests

## Current State

The following already exists:
- `GetPurchaseRequestById` - query handler for viewing a single request
- `UpdatePurchaseRequest` - command handler for editing (only works for Draft status)
- `SubmitPurchaseRequest` - command handler for submitting drafts
- `DeletePurchaseRequest` - command handler for deleting drafts
- Requests list page at `/requests` with navigation to `/requests/{id}`

## Missing Functionality

**Withdraw Request** - No endpoint/handler exists to withdraw a submitted (Pending) request.

When a request is withdrawn:
- Status changes from `Pending` back to `Draft`
- `SubmittedAt` is cleared
- Request can be edited again and re-submitted

## Implementation Plan

### 1. Domain Layer (ProcureHub)

#### 1.1 Add Withdraw method to PurchaseRequest model

Add `Withdraw()` method to `Models/PurchaseRequest.cs` that:
- Validates request is in `Pending` status
- Resets status to `Draft`
- Clears `SubmittedAt`
- Updates `UpdatedAt`

#### 1.2 Add validation error for withdraw

Add `CannotWithdrawNonPending` error to `Features/PurchaseRequests/Validation/PurchaseRequestErrors.cs`

#### 1.3 Create WithdrawPurchaseRequest command handler

Create `Features/PurchaseRequests/WithdrawPurchaseRequest.cs`:
- Command with `Id` parameter
- Handler that loads request, calls `Withdraw()`, saves changes
- Returns `Result` (success or failure)

### 2. API Layer (ProcureHub.WebApi)

#### 2.1 Add Withdraw endpoint

Add `POST /purchase-requests/{id}/withdraw` endpoint to `Features/PurchaseRequests/Endpoints.cs`:
- Requires `Requester` role
- Calls WithdrawPurchaseRequest handler
- Returns 204 on success

### 3. UI Layer (ProcureHub.BlazorApp)

#### 3.1 Create View/Edit Request page

Create `Components/Pages/Requests/View.razor` at route `/requests/{Id:guid}`:

**For Draft requests:**
- Display form with editable fields (same as New.razor)
- Show "Save Changes" button
- Show "Submit for Approval" button
- Show "Delete" button

**For Pending requests:**
- Display readonly view of all fields
- Show "Withdraw Request" button (returns to Draft)
- Show status badge prominently

**For Approved/Rejected requests:**
- Display readonly view of all fields
- Show status badge prominently
- Show approval/rejection info (date, reviewer)
- No action buttons (view only)

**Page Structure:**
- Two-column layout (same as New.razor)
- Left column: Request details form or readonly display
- Right column: Actions card (context-sensitive based on status)

### 4. Testing

#### 4.1 Add Withdraw endpoint to test theory data

Update `GetAllPurchaseRequestEndpoints` in `PurchaseRequestTests.cs` to include withdraw endpoint

#### 4.2 Add validation test for withdraw

Add `Test_WithdrawPurchaseRequest_validation` method

#### 4.3 Add withdraw functionality tests

Add tests for:
- Can withdraw pending request (returns to Draft, SubmittedAt cleared)
- Cannot withdraw draft request (validation error)
- Cannot withdraw approved request (validation error)
- Cannot withdraw rejected request (validation error)

#### 4.4 Add authorization tests

- Only request owner or admin can withdraw their pending request

## UI Design

### Draft State
```
┌─────────────────────────────────────────────────────────────┐
│ Purchase Request #PR-00001 │
│ Edit draft request │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────┐ ┌───────────────┐ │
│ │ Request Details │ │ Actions │ │
│ │ │ │ │ │
│ │ Title: [_________________] │ │ [Save Changes]│ │
│ │ │ │ │ │
│ │ Description: │ │ [Submit for │ │
│ │ [_________________________] │ │ Approval] │ │
│ │ [_________________________] │ │ │ │
│ │ │ │ [Delete] │ │
│ │ Category: [Dropdown_______] │ │ │ │
│ │ Department: [Dropdown_____] │ │ │ │
│ │ │ │ │ │
│ │ Estimated Amount: [€____] │ │ │ │
│ │ │ │ │ │
│ │ Business Justification: │ │ │ │
│ │ [_________________________] │ │ │ │
│ │ │ │ │ │
│ └──────────────────────────────────┘ └───────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```

### Pending State (Readonly with Withdraw)
```
┌─────────────────────────────────────────────────────────────┐
│ Purchase Request #PR-00001 [PENDING] │
│ Submitted: 2025-02-13 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────┐ ┌───────────────┐ │
│ │ Request Details │ │ Actions │ │
│ │ │ │ │ │
│ │ Title: MacBook Pro │ │ [Withdraw] │ │
│ │ │ │ │ │
│ │ Description: │ │ Return to │ │
│ │ Need laptop for dev │ │ draft status │ │
│ │ │ │ to edit │ │
│ │ Category: IT Equipment │ │ │ │
│ │ Department: Engineering │ │ │ │
│ │ │ │ │ │
│ │ Estimated Amount: €1,500.00 │ │ │ │
│ │ │ │ │ │
│ │ Business Justification: │ │ │ │
│ │ New hire equipment │ │ │ │
│ │ │ │ │ │
│ └──────────────────────────────────┘ └───────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```

### Approved/Rejected State (Readonly only)
```
┌─────────────────────────────────────────────────────────────┐
│ Purchase Request #PR-00001 [APPROVED] │
│ Submitted: 2025-02-13 │
│ Approved: 2025-02-13 by John Smith │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────┐ │
│ │ Request Details │ │
│ │ │ │
│ │ Title: MacBook Pro │ │
│ │ │ │
│ │ ... (all fields readonly) │ │
│ │ │ │
│ └──────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```

## State Machine

```
┌─────────┐
┌─────────►│ Draft │◄──────────┐
│ └────┬────┘ │
│ │ Update │ Withdraw
│ │ (edit fields) │
│ ▼ │
│ ┌─────────┐ │
Delete│ Submit│ Pending │ │
│ └────┬────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ │
└───┤Approved │ │Rejected │─────┘
└─────────┘ └─────────┘
```

## Tests to Write

1. **API Tests** (PurchaseRequestTests.cs)
- Cross-cutting tests: Add withdraw endpoint to `GetAllPurchaseRequestEndpoints`
- Validation test: `Test_WithdrawPurchaseRequest_validation`
- `Can_withdraw_pending_purchase_request`
- `Cannot_withdraw_draft_purchase_request`
- `Cannot_withdraw_approved_purchase_request`
- `Cannot_withdraw_rejected_purchase_request`
- `Cannot_withdraw_nonexistent_purchase_request`

2. **Unit Tests** (optional)
- `PurchaseRequest.Withdraw_ResetsStatusToDraft`
- `PurchaseRequest.Withdraw_ClearsSubmittedAt`
- `PurchaseRequest.Withdraw_ThrowsForNonPendingStatus`

## Files to Modify

1. `ProcureHub/Models/PurchaseRequest.cs` - Add Withdraw method
2. `ProcureHub/Features/PurchaseRequests/Validation/PurchaseRequestErrors.cs` - Add CannotWithdrawNonPending
3. `ProcureHub/Features/PurchaseRequests/WithdrawPurchaseRequest.cs` - New file
4. `ProcureHub.WebApi/Features/PurchaseRequests/Endpoints.cs` - Add withdraw endpoint
5. `ProcureHub.WebApi.Tests/Features/PurchaseRequestTests.cs` - Add tests
6. `ProcureHub.BlazorApp/Components/Pages/Requests/View.razor` - New file
7. `ProcureHub.BlazorApp/Components/Pages/Requests/Index.razor` - Update view button to navigate

## Success Criteria

- [ ] User can view any of their requests at `/requests/{id}`
- [ ] Draft requests show editable form with Save, Submit, Delete actions
- [ ] Pending requests show readonly view with Withdraw action
- [ ] Approved/Rejected requests show readonly view with no actions
- [ ] Withdrawing a pending request returns it to Draft status
- [ ] All endpoints require authentication
- [ ] Only request owners/admins can view/edit their requests
- [ ] All tests pass
139 changes: 139 additions & 0 deletions .plans/use-cases/02_view_and_edit_requests_steps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# View/Edit Purchase Requests - Implementation Steps

## Phase 1: Backend Implementation

### Step 1: Add Withdraw method to PurchaseRequest model
- [x] Open `ProcureHub/Models/PurchaseRequest.cs`
- [x] Add `Withdraw()` method after `CanDelete()` method (around line 101)
- [x] Method should:
- Check if Status is Pending, return failure if not
- Set Status to Draft
- Set SubmittedAt to null
- Set UpdatedAt to DateTime.UtcNow
- Return Result.Success()

### Step 2: Add CannotWithdrawNonPending error
- [x] Open `ProcureHub/Features/PurchaseRequests/Validation/PurchaseRequestErrors.cs`
- [x] Add `CannotWithdrawNonPending` error after `CannotDeleteNonDraft`
- [x] Use similar pattern: Error.Validation with appropriate title, detail, and Status field error

### Step 3: Create WithdrawPurchaseRequest command handler
- [x] Create new file `ProcureHub/Features/PurchaseRequests/WithdrawPurchaseRequest.cs`
- [x] Add Command record with Id property
- [x] Add CommandValidator with Id.NotEmpty() rule
- [x] Add Handler that:
- Loads purchase request by Id
- Returns NotFound if doesn't exist
- Calls pr.Withdraw()
- Saves changes if successful
- Returns result

### Step 4: Add Withdraw endpoint
- [x] Open `ProcureHub.WebApi/Features/PurchaseRequests/Endpoints.cs`
- [x] Add MapPost endpoint for `/purchase-requests/{id:guid}/withdraw` after Delete endpoint
- [x] Follow pattern of other command endpoints
- [x] Require Requester role authorization
- [x] Produces 204 NoContent and 404 NotFound

### Step 5: Add API tests for Withdraw endpoint
- [x] Open `ProcureHub.WebApi.Tests/Features/PurchaseRequestTests.cs`
- [x] Add withdraw endpoint to `GetAllPurchaseRequestEndpoints` theory data
- [x] Add `Test_WithdrawPurchaseRequest_validation` method
- [x] Add `Can_withdraw_pending_purchase_request` test
- [x] Add `Cannot_withdraw_draft_purchase_request` test
- [x] Add `Cannot_withdraw_approved_purchase_request` test
- [x] Add `Cannot_withdraw_rejected_purchase_request` test

## Phase 2: UI Implementation

### Step 6: Create View/Edit Request page
- [x] Create new file `ProcureHub.BlazorApp/Components/Pages/Requests/View.razor`
- [x] Add `@page "/requests/{Id:guid}"` route
- [x] Add `[Authorize]` attribute
- [x] Inject required handlers:
- GetPurchaseRequestById handler
- UpdatePurchaseRequest handler
- SubmitPurchaseRequest handler
- WithdrawPurchaseRequest handler
- DeletePurchaseRequest handler
- QueryCategories handler
- QueryDepartments handler
- CurrentUserProvider
- NavigationManager
- NotificationService
- ILogger

### Step 7: Implement page structure
- [x] Add PageTitle and PageHeader
- [x] Create two-column layout (8/4 ratio like New.razor)
- [x] Left column: Request Details card
- [x] Right column: Actions card

### Step 8: Implement loading state
- [x] Add `_loading` field
- [x] Show RadzenProgressBarCircular while loading
- [x] Load request data in OnInitializedAsync
- [x] Load categories and departments for dropdowns

### Step 9: Implement Draft view (editable)
- [x] When Status == Draft:
- Show EditForm with FluentValidator
- Show editable fields: Title, Description, Category, Department, Amount, Justification
- Actions: "Save Changes", "Submit for Approval", "Delete"

### Step 10: Implement Pending view (readonly with withdraw)
- [x] When Status == Pending:
- Show readonly display of all fields (RadzenText, not form inputs)
- Show status badge prominently
- Show SubmittedAt date
- Actions: "Withdraw Request" button

### Step 11: Implement Approved/Rejected view (readonly)
- [x] When Status is Approved or Rejected:
- Show readonly display of all fields
- Show status badge prominently
- Show SubmittedAt, ReviewedAt, ReviewedBy info
- No action buttons

### Step 12: Implement action handlers
- [x] SaveChanges method: calls Update handler
- [x] SubmitRequest method: calls Submit handler, navigates on success
- [x] WithdrawRequest method: calls Withdraw handler, refreshes data on success
- [x] DeleteRequest method: calls Delete handler, navigates on success
- [x] All methods show notifications on success/failure

### Step 13: Update Requests list navigation
- [x] Open `ProcureHub.BlazorApp/Components/Pages/Requests/Index.razor`
- [x] Verify view button navigates to `/requests/{pr.Id}`
- [x] Consider making RequestNumber clickable as well

## Phase 3: Testing and Validation

### Step 14: Build and verify
- [x] Run `dotnet build` to ensure no compilation errors
- [x] Run API tests to verify withdraw functionality
- [x] Verify all tests pass

### Step 15: Manual UI testing
- [ ] Navigate to requests list
- [ ] Click on a Draft request - should show editable form
- [ ] Save changes - should persist
- [ ] Submit - should change to Pending and show readonly view
- [ ] Withdraw - should return to Draft
- [ ] Submit again
- [ ] Login as approver and approve
- [ ] Navigate back to request - should show Approved readonly view
- [ ] Verify no actions available for Approved/Rejected

## Files Summary

### New Files
1. `ProcureHub/Features/PurchaseRequests/WithdrawPurchaseRequest.cs`
2. `ProcureHub.BlazorApp/Components/Pages/Requests/View.razor`

### Modified Files
1. `ProcureHub/Models/PurchaseRequest.cs`
2. `ProcureHub/Features/PurchaseRequests/Validation/PurchaseRequestErrors.cs`
3. `ProcureHub.WebApi/Features/PurchaseRequests/Endpoints.cs`
4. `ProcureHub.WebApi.Tests/Features/PurchaseRequestTests.cs`
5. `ProcureHub.BlazorApp/Components/Pages/Requests/Index.razor` (minor)
Loading
Loading