https://github.com/rsemihkoca/expensemanagementapp
Expense management system written in C# and .Net 8 SDK
https://github.com/rsemihkoca/expensemanagementapp
akbank-bootcamp csharp docker dot-net hangfire
Last synced: about 1 month ago
JSON representation
Expense management system written in C# and .Net 8 SDK
- Host: GitHub
- URL: https://github.com/rsemihkoca/expensemanagementapp
- Owner: rsemihkoca
- Created: 2023-12-11T11:37:47.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2024-03-02T08:34:51.000Z (over 1 year ago)
- Last Synced: 2025-03-13T15:17:02.382Z (7 months ago)
- Topics: akbank-bootcamp, csharp, docker, dot-net, hangfire
- Language: C#
- Homepage:
- Size: 1.46 MB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
---
# Expense Application - [Loom Demo](https://www.loom.com/share/a5ce7a3dd11f4793b86aecbbb9b8f918)
## Prerequisites
- [.NET SDK 8](https://dotnet.microsoft.com/download) installed on your machine.
- Docker/Docker Compose installed on your machine.
- Git installed on your machine.## Clone the Repository
```bash
git clone https://github.com/yourusername/ExpenseApplication.git
cd ExpenseApplication
```## Docker Configuration
### 1. Restart Docker Containers
Execute the following command to bring down existing Docker containers, volumes, and remove orphaned containers, then bring the Docker environment back up:
```bash
docker-compose down -v --remove-orphans && docker-compose up
```### 2. Apply Migrations
Run the following commands to apply Entity Framework migrations and update the database:
```bash
cd ExpenseApplication
dotnet ef migrations add InitialCreate --project Infrastructure --startup-project Api
dotnet ef database update --project Infrastructure --startup-project Api
```
If you already have migrations, you can run the following command to drop the database and re-create it:```bash
cd ExpenseApplication
dotnet ef database update --project Infrastructure --startup-project Api
```The application will be accessible at `http://localhost:5245` by default.
## Entity Relationship Diagram (ERD)

CreatedBy: Admin UserId \
Status: Only admin can approve or reject expense requests.\
Description: User add description for expense request.\
PaymentDescription: Admin add description for expense request. Or error code from background job.---
## Expense
### Create Expense
Method
Path
POST
/api/Expense/
#### Description:
Create an expense record. Authorized users: [Admin, Personnel]### Get All Expenses
Method
Path
GET
/api/Expense/
#### Description:
Retrieve expense records. Authorized users: [Admin]### Update Expense
Method
Path
PUT
/api/Expense/{expenseRequestId}
#### Description:
Update an expense record by expense request ID. Authorized users: [Admin].\
Updating expense as approved do not fire background job. Only approve endpoint fires background job.
This endpoint can be used if expens is paid manually.### Delete Expense
Method
Path
DELETE
/api/Expense/{expenseRequestId}
#### Description:
Delete an expense record by expense request ID. Authorized users: [Admin]### Get Expense by ID
Method
Path
GET
/api/Expense/{expenseRequestId}
#### Description:
Retrieve an expense record by expense request ID. Authorized users: [Admin]### Approve Expense
Method
Path
PATCH
/api/Expense/Approve/{expenseRequestId}
#### Description:
Approve an expense record by expense request ID. Authorized users: [Admin]. \
Admin users approve expense records by giving expense request id.
Checks if already approved and completed or paymentstatus is in progress. If not, updates expense record and fires background job.### Reject Expense
Method
Path
PATCH
/api/Expense/Reject/{expenseRequestId}
#### Description:
Reject an expense record by expense request ID. Authorized users: [Admin]. \
Reject endpoint do not fire background job. Updates expense record immediately. And relevant fields are updated. Payment description, status, last updated date...### Get Expenses by User
Method
Path
GET
/api/Expense/ByUser
#### Description:
Retrieve expense records for the authenticated user. Authorized users: [Admin, Personnel].\
Admin users can give null user id to retrieve all expenses.
Personnel can only retrieve their own expenses.
Personnel can filter expenses by status and date range. Also Admin can filter expenses.
This api is queryable.---
### ExpenseCategory
#### Create Expense Category
Method
Path
POST
/api/ExpenseCategory
#### Description:
Create a new expense category. Authorized users: [Admin]#### Get All Expense Categories
Method
Path
GET
/api/ExpenseCategory
#### Description:
Retrieve all expense categories. Authorized users: [Admin]#### Update Expense Category
Method
Path
PUT
/api/ExpenseCategory/{expenseCategoryId}
#### Description:
Update an expense category by expense category ID. Authorized users: [Admin]#### Delete Expense Category
Method
Path
DELETE
/api/ExpenseCategory/{expenseCategoryId}
#### Description:
Delete an expense category by expense category ID. Authorized users: [Admin]
Category can not be deleted if category has pending expenses.#### Get All Expense Categories
Method
Path
GET
/api/ExpenseCategory/GetAllExpenseCategory
#### Description:
Retrieve all expense categories. Authorized users: [Admin]---
### PaymentSimulator
#### Process Payment
Method
Path
POST
/api/PaymentSimulator/ProcessPayment
#### Description:
Simulate the process of making a payment. Authorized users: [Anonymous]. \
Hangfire is used to simulate payment process. Hangfire is a background job library. It is used to simulate payment process.---
### Report
#### Approved Payment Frequency Report
Method
Path
POST
/api/Report/ApprovedPaymentFrequencyReport
#### Description:
Generate a report on the frequency of approved payments. Authorized users: [Admin].\
Example response:
```json
{
"type": "monthly",
"startDate": "01/01/2024 00:00:00",
"endDate": "31/01/2024 23:59:59",
"approvedCount": 16,
"approvedSum": 14400,
"averageApprovedAmount": 900
}
```#### Rejected Payment Frequency Report
Method
Path
POST
/api/Report/RejectedPaymentFrequencyReport
#### Description:
Generate a report on the frequency of rejected payments. Authorized users: [Admin].\
Example response:
```json
{
"type": "weekly",
"startDate": "22/01/2024 00:00:00",
"endDate": "28/01/2024 23:59:59",
"rejectedCount": 0,
"rejectedSum": 0,
"averageRejectedAmount": 0
}
```#### Personnel Expense Frequency Report
Method
Path
POST
/api/Report/PersonnelExpenseFrequencyReport
#### Description:
Generate a report on the frequency of personnel expenses. Authorized users: [Admin].\
Example response:
```json
{
"type": "monthly",
"startDate": "01/01/2024 00:00:00",
"endDate": "31/01/2024 23:59:59",
"totalPendingCount": 16,
"totalPendingSum": 6040,
"averagePendingAmount": 377.5,
"personnelExpenseFrequencies": [
{
"userId": 1,
"fullName": "Admin 1",
"pendingCount": 4,
"pendingSum": 1400,
"averagePendingAmount": 350
},
{
"userId": 2,
"fullName": "Admin 2"
}...
```#### Personnel Summary Report
Method
Path
POST
/api/Report/PersonnelSummaryReport
#### Description:
Generate a summary report on personnel expenses. Authorized users: [Admin, Personnel].\
Personnel can only retrieve their own expenses.\
Admin can retrieve all expenses if userId is null.\
Example response:
```json
{
"userId": 3,
"totalCount": 10,
"approvedCount": 4,
"rejectedCount": 2,
"pendingCount": 4,
"approvedPercentage": "40%",
"approvedSum": 6500,
"rejectedSum": 6500,
"pendingSum": 6500,
"expenses": [
{
"expenseRequestId": 3,
"userId": 0,
"categoryId": 3,
"expenseStatus": "Approved",
"paymentStatus": "Completed",
"paymentDescription": "Payment Not Made",
"amount": 2500,
"paymentMethod": "Cash",
"paymentLocation": "Office",
"documents": "PayrollDocs",
"description": "Personel maaşları ödendi.",
"creationDate": "09/01/2024 00:00:00",
"lastUpdateTime": "09/01/2024 00:00:00"
}...
{
```---
### Token
#### Generate Token
Method
Path
POST
/api/Token
#### Description:
Generate a new authentication token. Authorized users: [Anonymous].\
JWT is used for authentication.\
Roles: Admin, Personnel\---
### User#### Create User
Method
Path
POST
/api/User
#### Description:
Create a new user. Authorized users: [Admin].#### Get All Users
Method
Path
GET
/api/User
#### Description:
Retrieve all user records. Authorized users: [Admin].#### Update User
Method
Path
PUT
/api/User/{UserId}
#### Description:
Update a user by user ID. Authorized users: [Admin].#### Delete User
Method
Path
DELETE
/api/User/{UserId}
#### Description:
Delete a user by user ID. Authorized users: [Admin].\
User can not be deleted if user has pending expenses.#### Activate User
Method
Path
PATCH
/api/User/ActivateUser
#### Description:
Activate a user account. Authorized users: [Admin]. \
If a user try to login with deactivated account, user will get error message.
Users are blocked if they have 3 failed login attempts. Only admin can activate user.#### Deactivate User
Method
Path
PATCH
/api/User/DeactivateUser
#### Description:
Deactivate a user account. Authorized users: [Admin]. \
If a user try to login with deactivated account, user will get error message. \
Blocked users can not login. So they can not be created expenses.#### Get All Users
Method
Path
GET
/api/User/GetAllUser
#### Description:
Retrieve all user records. Authorized users: [Admin].---
### Additional Information- Handlervalidator.cs and fluentvalidation is used for validation.
- Entities have unique index fields so additional validations are added for those like CategoryName, UserName, Email.
- Dependencies injected, code is decoupled. Avoid code duplication. Code can extended easily.
- 2 Admin, 2 Personnel users are created in migration. 42 expenses are created.
- Further Improvements: ApiResponse Class, I did not used apiresponse class so as to return appropriate status code and message. But it can be improved. Handling enums, ClassLevelCascadeMode etc.### Runtime Screenshots(Projenin Çalışma Örneği)
Get token
Get JWT Token and use it in Authorization header.

After Authorization, you can use api endpoints. Here a test token you can also use it.
```jwttoken
eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJJZCI6IjEiLCJFbWFpbCI6ImFkbWluMUBleGFtcGxlLmNvbSIsIlVzZXJuYW1lIjoiYWRtaW4xIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiQWRtaW4iLCJleHAiOjE2NzA1ODkzNDg5LCJpc3MiOiJFeHBlbnNlQXBwbGljYXRpb24iLCJhdWQiOiJFeHBlbnNlQXBwbGljYXRpb24ifQ.iyL7pHsWY8q-O_gRqCIMctZtrSfK1rZ8vFYASk02utQ
```ExpenseRequestId 42 is pending. Admin can approve it.
```json
{
"expenseRequestId": 42,
"userId": 1,
"categoryId": 10,
"expenseStatus": "Pending",
"paymentStatus": "Pending",
"paymentDescription": "Payment Not Made",
"amount": 300,
"paymentMethod": "Cash",
"paymentLocation": "Office",
"documents": "EducationReceipts",
"description": "Eğitim masrafları ödendi.",
"creationDate": "13/01/2024 00:00:00",
"lastUpdateTime": "06/01/2024 00:00:00"
}
```Approve Expense

Payment simulator sleeps 6 seconds. And probabilisticly approves(90%) or rejects(10%) expense.\

Further information can be found in the [link](https://documenter.getpostman.com/view/23348379/2s9YymFQ2G).
Also in documentation folder there is a postman collection. You can import it to postman and test the api.