Added project
This commit is contained in:
13
src/main/java/ru/resprojects/dfops/DfopsApplication.java
Normal file
13
src/main/java/ru/resprojects/dfops/DfopsApplication.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package ru.resprojects.dfops;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class DfopsApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(DfopsApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
22
src/main/java/ru/resprojects/dfops/config/AppConfig.java
Normal file
22
src/main/java/ru/resprojects/dfops/config/AppConfig.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package ru.resprojects.dfops.config;
|
||||
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class AppConfig {
|
||||
|
||||
public static final String DEFAULT_PAGE_LIMIT = "10";
|
||||
|
||||
@Bean
|
||||
public OpenAPI customOpenApi() {
|
||||
return new OpenAPI()
|
||||
.components(new Components())
|
||||
.info(new Info().title("Driver Finance Operation REST Service API").description(
|
||||
"REST-сервис для приема и регистрации операций в базе данных по расчетам с водителями."));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
package ru.resprojects.dfops.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.resprojects.dfops.config.AppConfig;
|
||||
import ru.resprojects.dfops.dto.ResponseDto;
|
||||
import ru.resprojects.dfops.dto.account.AccountDto;
|
||||
import ru.resprojects.dfops.exception.BadResourceException;
|
||||
import ru.resprojects.dfops.exception.ErrorMessage;
|
||||
import ru.resprojects.dfops.model.Account;
|
||||
import ru.resprojects.dfops.model.Employee;
|
||||
import ru.resprojects.dfops.service.AccountService;
|
||||
import ru.resprojects.dfops.service.EmployeeService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/account")
|
||||
@Tag(name ="Расчётный счёт работника", description = "REST API для работы с сущностью 'Account'")
|
||||
public class AccountController {
|
||||
|
||||
private final EmployeeService employeeService;
|
||||
|
||||
private final AccountService accountService;
|
||||
|
||||
public AccountController(EmployeeService employeeService, AccountService accountService) {
|
||||
this.employeeService = employeeService;
|
||||
this.accountService = accountService;
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Вывод лицевых счетов работника",
|
||||
description = "Возвращает список лицевых счетов выбранного работника",
|
||||
tags = { "account" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ResponseDto.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "204",
|
||||
description = "лицевые счета у выбранного работника отсутствуют",
|
||||
content = @Content(
|
||||
schema = @Schema(),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "не найден работник у которого необходимо вывести список лицевых счетов",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@GetMapping(value = "/{employee_id}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<ResponseDto<Account>> getAll(
|
||||
@Parameter(description="ID работника", required = true,
|
||||
example = "5002", schema=@Schema(implementation = Long.class))
|
||||
@PathVariable("employee_id") Long employee_id,
|
||||
@Parameter(description="Параметр запроса для постраничного вывода, задает номер текущей страницы",
|
||||
example = "1", schema=@Schema(implementation = Integer.class))
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer pageNo,
|
||||
@Parameter(description="Параметр запроса для постраничного вывода, задает количество элементов на страницу",
|
||||
example = AppConfig.DEFAULT_PAGE_LIMIT, schema=@Schema(implementation = Integer.class))
|
||||
@RequestParam(value = "limit", defaultValue = AppConfig.DEFAULT_PAGE_LIMIT) Integer limit)
|
||||
{
|
||||
Employee employee = employeeService.get(employee_id);
|
||||
ResponseDto<Account> response = new ResponseDto<>(accountService.getAll(employee.getId(), pageNo, limit));
|
||||
if (response.getElements().isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.NO_CONTENT).body(null);
|
||||
}
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Поиск лицевого счёта",
|
||||
description = "Возвращает информацию о лицевом счёте",
|
||||
tags = { "account" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = Account.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "запрашиваемый объект не найден",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@GetMapping(value = "/get/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<Account> getById(
|
||||
@Parameter(description="ID лицевого счёта", required = true,
|
||||
example = "5002", schema=@Schema(implementation = Long.class))
|
||||
@PathVariable("id") Long id
|
||||
) {
|
||||
return ResponseEntity.ok(accountService.get(id));
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Создание новой записи",
|
||||
description = "Создаёт новую запись и возвращает созданный объект",
|
||||
tags = { "account" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = Account.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "400",
|
||||
description = "некорректный запрос",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "работник для которого создаётся аккаунт не найден",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "409",
|
||||
description = "элемент уже существует в базе",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@PostMapping(value = "/create", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<Account> create(
|
||||
@Parameter(description="Создаваемый объект лицевого счёта",
|
||||
required=true, schema=@Schema(implementation = AccountDto.class))
|
||||
@RequestBody AccountDto accountDto) {
|
||||
if (accountDto == null) {
|
||||
throw new BadResourceException("Request body is null");
|
||||
}
|
||||
Employee employee = employeeService.get(accountDto.getEmployeeId());
|
||||
return ResponseEntity.ok(accountService.create(employee, accountDto.getPersonalAccount()));
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Удаление записи",
|
||||
description = "Удаление записи из базы данных",
|
||||
tags = { "account" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция"
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "элемент не найден",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@DeleteMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<Void> delete(
|
||||
@Parameter(description="ID лицевого счёта", required = true,
|
||||
example = "5002", schema=@Schema(implementation = Long.class))
|
||||
@PathVariable("id") Long id) {
|
||||
accountService.delete(id);
|
||||
return ResponseEntity.status(HttpStatus.OK).body(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
package ru.resprojects.dfops.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.resprojects.dfops.config.AppConfig;
|
||||
import ru.resprojects.dfops.dto.ResponseDto;
|
||||
import ru.resprojects.dfops.dto.employee.EmployeeDto;
|
||||
import ru.resprojects.dfops.exception.BadRequestException;
|
||||
import ru.resprojects.dfops.exception.ErrorMessage;
|
||||
import ru.resprojects.dfops.model.Employee;
|
||||
import ru.resprojects.dfops.service.EmployeeService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/employee")
|
||||
@Tag(name ="Работник (водитель)", description = "REST API для работы с сущностью 'Employee'")
|
||||
public class EmployeeController {
|
||||
|
||||
private final EmployeeService employeeService;
|
||||
|
||||
public EmployeeController(EmployeeService employeeService) {
|
||||
this.employeeService = employeeService;
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Получить список работников",
|
||||
description = "Возвращает список работников",
|
||||
tags = { "employee" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ResponseDto.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "204",
|
||||
description = "список работников пуст",
|
||||
content = @Content(
|
||||
schema = @Schema(),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<ResponseDto<Employee>> getAll(
|
||||
@Parameter(description="Параметр запроса для постраничного вывода, задает номер текущей страницы",
|
||||
example = "1", schema=@Schema(implementation = Integer.class))
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer pageNo,
|
||||
@Parameter(description="Параметр запроса для постраничного вывода, задает количество элементов на страницу",
|
||||
example = AppConfig.DEFAULT_PAGE_LIMIT,schema=@Schema(implementation = Integer.class))
|
||||
@RequestParam(value = "limit", defaultValue = AppConfig.DEFAULT_PAGE_LIMIT) Integer limit)
|
||||
{
|
||||
ResponseDto<Employee> response = new ResponseDto<>(employeeService.getAll(pageNo, limit));
|
||||
if (response.getElements().isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.NO_CONTENT).body(null);
|
||||
}
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Поиск работника",
|
||||
description = "Возвращает информацию о работнике",
|
||||
tags = { "employee" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = Employee.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "запрашиваемый объект не найден",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@GetMapping(value = "/get/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<Employee> getById(
|
||||
@Parameter(description="ID работника", required = true,
|
||||
example = "5000", schema=@Schema(implementation = Long.class))
|
||||
@PathVariable("id") Long id) {
|
||||
return ResponseEntity.ok(employeeService.get(id));
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Поиск работника по e-mail",
|
||||
description = "Возвращает информацию о работнике",
|
||||
tags = { "employee" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = Employee.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "400",
|
||||
description = "некорректный запрос",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "запрашиваемый объект не найден",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@GetMapping(value = "/get", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<Employee> getByEmail(
|
||||
@Parameter(description="Email работника", required=true,
|
||||
example = "ivanov@example.com", schema=@Schema(implementation = String.class))
|
||||
@RequestParam(value = "email") String email) {
|
||||
if (email == null || email.isBlank()) {
|
||||
throw new BadRequestException("Email is null or empty");
|
||||
}
|
||||
return ResponseEntity.ok(employeeService.getByEmail(email));
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Создание новой записи",
|
||||
description = "Создаёт новую запись и возвращает созданный объект",
|
||||
tags = { "employee" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = Employee.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "400",
|
||||
description = "некорректный запрос",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "409",
|
||||
description = "элемент уже существует в базе",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@PostMapping(value = "/create", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<Employee> create(
|
||||
@Parameter(description="Создаваемый объект",
|
||||
required=true, schema=@Schema(implementation = EmployeeDto.class))
|
||||
@RequestBody EmployeeDto employeeDto) {
|
||||
if (employeeDto == null) {
|
||||
throw new BadRequestException("Request body is null");
|
||||
}
|
||||
Employee employee = new Employee(employeeDto.getName(), employeeDto.getEmail());
|
||||
return ResponseEntity.ok(employeeService.create(employee));
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Удаление записи",
|
||||
description = "Удаление записи из базы данных",
|
||||
tags = { "employee" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция"
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "элемент не найден",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@DeleteMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<Employee> delete(
|
||||
@Parameter(description="ID работника", required = true,
|
||||
example = "5000", schema=@Schema(implementation = Long.class))
|
||||
@PathVariable("id") Long id) {
|
||||
employeeService.delete(id);
|
||||
return ResponseEntity.status(HttpStatus.OK).body(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,605 @@
|
||||
package ru.resprojects.dfops.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.resprojects.dfops.config.AppConfig;
|
||||
import ru.resprojects.dfops.dto.ResponseDto;
|
||||
import ru.resprojects.dfops.dto.account.AccountBalanceDto;
|
||||
import ru.resprojects.dfops.dto.operation.OperationAmountDto;
|
||||
import ru.resprojects.dfops.dto.operation.OperationDto;
|
||||
import ru.resprojects.dfops.dto.operation.OperationTransferDto;
|
||||
import ru.resprojects.dfops.exception.BadRequestException;
|
||||
import ru.resprojects.dfops.exception.BadResourceException;
|
||||
import ru.resprojects.dfops.exception.ErrorMessage;
|
||||
import ru.resprojects.dfops.model.Account;
|
||||
import ru.resprojects.dfops.service.AccountService;
|
||||
import ru.resprojects.dfops.service.OperationService;
|
||||
import ru.resprojects.dfops.util.DateTimeUtil;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/operation")
|
||||
@Tag(name ="Операции над счётом", description = "REST API для работы с сущностью 'Operation'")
|
||||
public class OperationController {
|
||||
|
||||
private final AccountService accountService;
|
||||
|
||||
private final OperationService operationService;
|
||||
|
||||
public OperationController(AccountService accountService, OperationService operationService) {
|
||||
this.accountService = accountService;
|
||||
this.operationService = operationService;
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Вывод всех операций по лицевому счёту работника",
|
||||
description = "Возвращает список всех операций по выбранному лицевому счету работника",
|
||||
tags = { "operation" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ResponseDto.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "204",
|
||||
description = "список операций по счёту пуст",
|
||||
content = @Content(
|
||||
schema = @Schema(),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "лицевой счёт не найден",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@GetMapping(value = "/{account_id}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<ResponseDto<ru.resprojects.dfops.model.Operation>> getAll(
|
||||
@Parameter(description="ID лицевого счёта", schema=@Schema(implementation = Long.class), required = true, example = "5002")
|
||||
@PathVariable("account_id") Long accountId,
|
||||
@Parameter(description="Параметр запроса для постраничного вывода. Задает номер текущей страницы",
|
||||
example = "1", schema=@Schema(implementation = Integer.class))
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer pageNo,
|
||||
@Parameter(description="Параметр запроса для постраничного вывода. Задает количество элементов на страницу",
|
||||
example = AppConfig.DEFAULT_PAGE_LIMIT, schema=@Schema(implementation = Integer.class))
|
||||
@RequestParam(value = "limit", defaultValue = AppConfig.DEFAULT_PAGE_LIMIT) Integer limit)
|
||||
{
|
||||
Account account = accountService.get(accountId);
|
||||
ResponseDto<ru.resprojects.dfops.model.Operation> response = new ResponseDto<>(operationService.getAllByAccountId(account.getId(), pageNo, limit));
|
||||
if (response.getElements().isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.NO_CONTENT).body(null);
|
||||
}
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Вывод всех операций за период по лицевому счёту работника",
|
||||
description = "Возвращает список всех операций за период по выбранному лицевому счету работника",
|
||||
tags = { "operation" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ResponseDto.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "204",
|
||||
description = "список операций по счёту пуст",
|
||||
content = @Content(
|
||||
schema = @Schema(),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "400",
|
||||
description = "некорректный запрос",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "лицевой счёт не найден",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@GetMapping(value = "/{account_id}/filter", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<ResponseDto<ru.resprojects.dfops.model.Operation>> getAllBetween(
|
||||
@Parameter(description="ID лицевого счёта", schema=@Schema(implementation = Long.class), required = true, example = "5002")
|
||||
@PathVariable("account_id") Long accountId,
|
||||
@Parameter(description="Начальная дата и время (формат dd.MM.yyyy-HH:mm)", schema=@Schema(implementation = String.class),
|
||||
example = "01.05.2020-00:00", required = true)
|
||||
@RequestParam(value = "startDateTime") @DateTimeFormat(pattern = DateTimeUtil.DATE_TIME_PATTERN_REQUEST) LocalDateTime startDateTime,
|
||||
@Parameter(description="Конечная дата и время (формат dd.MM.yyyy-HH:mm)", schema=@Schema(implementation = String.class),
|
||||
example = "31.05.2020-23:59", required = true)
|
||||
@RequestParam(value = "endDateTime") @DateTimeFormat(pattern = DateTimeUtil.DATE_TIME_PATTERN_REQUEST) LocalDateTime endDateTime,
|
||||
@Parameter(description="Параметр запроса для постраничного вывода. Задает номер текущей страницы.",
|
||||
example = "1", schema=@Schema(implementation = Integer.class))
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer pageNo,
|
||||
@Parameter(description="Параметр запроса для постраничного вывода. Задает количество элементов на страницу.",
|
||||
example = AppConfig.DEFAULT_PAGE_LIMIT, schema=@Schema(implementation = Integer.class))
|
||||
@RequestParam(value = "limit", defaultValue = AppConfig.DEFAULT_PAGE_LIMIT) Integer limit
|
||||
)
|
||||
{
|
||||
checkDateTimeFilter(startDateTime, endDateTime);
|
||||
Account account = accountService.get(accountId);
|
||||
ResponseDto<ru.resprojects.dfops.model.Operation> response = new ResponseDto<>(
|
||||
operationService.getAllByAccountIdBetween(account.getId(), startDateTime, endDateTime, pageNo, limit)
|
||||
);
|
||||
if (response.getElements().isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.NO_CONTENT).body(null);
|
||||
}
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Вывод оборота по операциям за период для лицевого счёта",
|
||||
description = "Возвращает оборот по операциям за период для указанного лицевого счёта работника",
|
||||
tags = { "operation" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ResponseDto.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "400",
|
||||
description = "некорректный запрос",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "лицевой счёт не найден",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@GetMapping(value = "/{account_id}/operations_amount/filter", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<ResponseDto<OperationAmountDto>> getOperationsAmountBetween(
|
||||
@Parameter(description="ID лицевого счёта", schema=@Schema(implementation = Long.class), required = true, example = "5002")
|
||||
@PathVariable("account_id") Long accountId,
|
||||
@Parameter(description="Начальная дата и время (формат dd.MM.yyyy-HH:mm)", schema=@Schema(implementation = String.class),
|
||||
example = "01.05.2020-00:00", required = true)
|
||||
@RequestParam(value = "startDateTime") @DateTimeFormat(pattern = DateTimeUtil.DATE_TIME_PATTERN_REQUEST) LocalDateTime startDateTime,
|
||||
@Parameter(description="Конечная дата и время (формат dd.MM.yyyy-HH:mm)", schema=@Schema(implementation = String.class),
|
||||
example = "31.05.2020-23:59", required = true)
|
||||
@RequestParam(value = "endDateTime") @DateTimeFormat(pattern = DateTimeUtil.DATE_TIME_PATTERN_REQUEST) LocalDateTime endDateTime
|
||||
)
|
||||
{
|
||||
checkDateTimeFilter(startDateTime, endDateTime);
|
||||
Account account = accountService.get(accountId);
|
||||
List<OperationAmountDto> result = operationService.getOperationsAmountBetween(account.getId(), startDateTime, endDateTime);
|
||||
ResponseDto<OperationAmountDto> responseDto = new ResponseDto<>();
|
||||
responseDto.setElements(result);
|
||||
responseDto.setPageCount(1);
|
||||
responseDto.setCurrentPage(1);
|
||||
responseDto.setTotalItems(result.size());
|
||||
return ResponseEntity.ok(responseDto);
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Вывод дебет лицевого счёта за период",
|
||||
description = "Возвращает дебет лицевого счёта работника за указанный период",
|
||||
tags = { "operation" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = OperationAmountDto.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "400",
|
||||
description = "некорректный запрос",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "лицевой счёт не найден",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@GetMapping(value = "/{account_id}/debit/filter", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<OperationAmountDto> getDebitBetween(
|
||||
@Parameter(description="ID лицевого счёта", schema=@Schema(implementation = Long.class), required = true, example = "5002")
|
||||
@PathVariable("account_id") Long accountId,
|
||||
@Parameter(description="Начальная дата и время (формат dd.MM.yyyy-HH:mm)", schema=@Schema(implementation = String.class),
|
||||
example = "01.05.2020-00:00", required = true)
|
||||
@RequestParam(value = "startDateTime") @DateTimeFormat(pattern = DateTimeUtil.DATE_TIME_PATTERN_REQUEST) LocalDateTime startDateTime,
|
||||
@Parameter(description="Конечная дата и время (формат dd.MM.yyyy-HH:mm)", schema=@Schema(implementation = String.class),
|
||||
example = "31.05.2020-23:59", required = true)
|
||||
@RequestParam(value = "endDateTime") @DateTimeFormat(pattern = DateTimeUtil.DATE_TIME_PATTERN_REQUEST) LocalDateTime endDateTime
|
||||
)
|
||||
{
|
||||
checkDateTimeFilter(startDateTime, endDateTime);
|
||||
Account account = accountService.get(accountId);
|
||||
BigDecimal result = operationService.getDebitBetween(account.getId(), startDateTime, endDateTime);
|
||||
OperationAmountDto operationAmountDto = new OperationAmountDto(result, ru.resprojects.dfops.model.Operation.OperationType.DEPOSIT);
|
||||
return ResponseEntity.ok(operationAmountDto);
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Вывод кредит лицевого счёта",
|
||||
description = "Возвращает кредит лицевого счёта работника за период",
|
||||
tags = { "operation" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = OperationAmountDto.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "400",
|
||||
description = "некорректный запрос",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "лицевой счёт не найден",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@GetMapping(value = "/{account_id}/credit/filter", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<OperationAmountDto> getCreditBetween(
|
||||
@Parameter(description="ID лицевого счёта", schema=@Schema(implementation = Long.class), required = true, example = "5002")
|
||||
@PathVariable("account_id") Long accountId,
|
||||
@Parameter(description="Начальная дата и время (формат dd.MM.yyyy-HH:mm)", schema=@Schema(implementation = String.class),
|
||||
example = "01.05.2020-00:00", required = true)
|
||||
@RequestParam(value = "startDateTime") @DateTimeFormat(pattern = DateTimeUtil.DATE_TIME_PATTERN_REQUEST) LocalDateTime startDateTime,
|
||||
@Parameter(description="Конечная дата и время (формат dd.MM.yyyy-HH:mm)", schema=@Schema(implementation = String.class),
|
||||
example = "31.05.2020-23:59", required = true)
|
||||
@RequestParam(value = "endDateTime") @DateTimeFormat(pattern = DateTimeUtil.DATE_TIME_PATTERN_REQUEST) LocalDateTime endDateTime
|
||||
)
|
||||
{
|
||||
checkDateTimeFilter(startDateTime, endDateTime);
|
||||
Account account = accountService.get(accountId);
|
||||
BigDecimal result = operationService.getCreditBetween(account.getId(), startDateTime, endDateTime);
|
||||
OperationAmountDto operationAmountDto = new OperationAmountDto(result, ru.resprojects.dfops.model.Operation.OperationType.WITHDRAW);
|
||||
return ResponseEntity.ok(operationAmountDto);
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Текущий баланс лицевого счёта",
|
||||
description = "Выводит текущий баланс лицевого счёта",
|
||||
tags = { "operation" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = AccountBalanceDto.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "лицевой счёт не найден",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@GetMapping(value = "/{account_id}/balance", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<AccountBalanceDto> getBalance(
|
||||
@Parameter(description="ID лицевого счёта", schema=@Schema(implementation = Long.class), required = true, example = "5002")
|
||||
@PathVariable("account_id") Long accountId) {
|
||||
Account account = accountService.get(accountId);
|
||||
BigDecimal balance = operationService.getCurrentBalance(account.getId());
|
||||
AccountBalanceDto balanceDto = new AccountBalanceDto(account.getId(), account.getPersonalAccount(), balance);
|
||||
return ResponseEntity.ok(balanceDto);
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Зачисление на лицевой счёт",
|
||||
description = "Зачисление указанной суммы на лицевого счёта работника",
|
||||
tags = { "operation" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ru.resprojects.dfops.model.Operation.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "400",
|
||||
description = "некорректный запрос",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "403",
|
||||
description = "некорректные данные",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "лицевой счёт не найден",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@PostMapping(value = "/deposit", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<ru.resprojects.dfops.model.Operation> deposit(
|
||||
@Parameter(description="Объект с даннами для проведения операции по лицевому счёту", required=true, schema=@Schema(implementation = OperationDto.class))
|
||||
@RequestBody OperationDto operationDto
|
||||
)
|
||||
{
|
||||
if (operationDto == null) {
|
||||
throw new BadRequestException("Request body is null");
|
||||
}
|
||||
if (operationDto.getOperationType() == null) {
|
||||
throw new BadRequestException("Unknown operation type");
|
||||
}
|
||||
if (!ru.resprojects.dfops.model.Operation.OperationType.DEPOSIT.equals(operationDto.getOperationType())) {
|
||||
throw new BadRequestException("Must be DEPOSIT but " + operationDto.getOperationType());
|
||||
}
|
||||
Account account = accountService.get(operationDto.getAccountId());
|
||||
return ResponseEntity.ok(operationService.deposit(account, operationDto.getAmount()));
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Списание с лицевого счёта",
|
||||
description = "Списание указанной суммы с лицевого счёта работника",
|
||||
tags = { "operation" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ru.resprojects.dfops.model.Operation.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "400",
|
||||
description = "некорректный запрос",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "403",
|
||||
description = "некорректные данные",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "лицевой счёт не найден",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@PostMapping(value = "/withdraw", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<ru.resprojects.dfops.model.Operation> withdraw(
|
||||
@Parameter(description="Объект с даннами для проведения операции по лицевому счёту", required=true, schema=@Schema(implementation = OperationDto.class))
|
||||
@RequestBody OperationDto operationDto
|
||||
)
|
||||
{
|
||||
if (operationDto == null) {
|
||||
throw new BadRequestException("Request body is null");
|
||||
}
|
||||
if (operationDto.getOperationType() == null) {
|
||||
throw new BadRequestException("Unknown operation type");
|
||||
}
|
||||
if (!ru.resprojects.dfops.model.Operation.OperationType.WITHDRAW.equals(operationDto.getOperationType())) {
|
||||
throw new BadRequestException("Must be WITHDRAW but " + operationDto.getOperationType());
|
||||
}
|
||||
Account account = accountService.get(operationDto.getAccountId());
|
||||
return ResponseEntity.ok(operationService.withdraw(account, operationDto.getAmount()));
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Перевод с лицевого счёта на лицевого счёта",
|
||||
description = "Перевод с лицевого счёта на лицевой счёт работника",
|
||||
tags = { "operation" }
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "успешная операция"
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "400",
|
||||
description = "некорректный запрос",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "лицевой счёт не найден",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "403",
|
||||
description = "некорректные данные",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "неизвестная ошибка",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = ErrorMessage.class),
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE
|
||||
)
|
||||
)
|
||||
})
|
||||
@PostMapping(value = "/transfer", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<Void> transfer(
|
||||
@Parameter(description="Объект с даннами для перевода суммы между счетами", required=true, schema=@Schema(implementation = OperationTransferDto.class))
|
||||
@RequestBody OperationTransferDto operationTransferDto
|
||||
)
|
||||
{
|
||||
if (operationTransferDto == null) {
|
||||
throw new BadResourceException("Request body is null");
|
||||
}
|
||||
Account from = accountService.get(operationTransferDto.getAccountIdFrom());
|
||||
Account to = accountService.get(operationTransferDto.getAccountIdTo());
|
||||
operationService.transfer(from, to, operationTransferDto.getAmount());
|
||||
return ResponseEntity.ok().body(null);
|
||||
}
|
||||
|
||||
private void checkDateTimeFilter(LocalDateTime startDateTime, LocalDateTime endDateTime) {
|
||||
if (startDateTime == null || endDateTime == null) {
|
||||
throw new BadRequestException("Start date-time or end date-time is null");
|
||||
}
|
||||
if (startDateTime.compareTo(endDateTime) > 0) {
|
||||
throw new BadRequestException("Start date-time later than end date-time");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package ru.resprojects.dfops.controller;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import ru.resprojects.dfops.exception.BadRequestException;
|
||||
import ru.resprojects.dfops.exception.BadResourceException;
|
||||
import ru.resprojects.dfops.exception.ErrorMessage;
|
||||
import ru.resprojects.dfops.exception.ResourceAlreadyExistsException;
|
||||
import ru.resprojects.dfops.exception.ResourceNotFoundException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class RestExceptionHandler {
|
||||
|
||||
@ExceptionHandler(value = {ResourceNotFoundException.class})
|
||||
public ResponseEntity<ErrorMessage> resourceNotFound(HttpServletRequest request, ResourceNotFoundException exception) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(exception.getErrorMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = {BadResourceException.class})
|
||||
public ResponseEntity<ErrorMessage> badResource(HttpServletRequest request, BadResourceException exception) {
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(exception.getErrorMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = {BadRequestException.class})
|
||||
public ResponseEntity<ErrorMessage> badRequest(HttpServletRequest request, BadRequestException exception) {
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(exception.getErrorMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = {ResourceAlreadyExistsException.class})
|
||||
public ResponseEntity<ErrorMessage> alreadyExistResource(HttpServletRequest request, ResourceAlreadyExistsException exception) {
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(exception.getErrorMessage());
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ErrorMessage> handleError(HttpServletRequest requesr, Exception exception) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ErrorMessage("Unexpected error: " + exception.getMessage()));
|
||||
}
|
||||
|
||||
}
|
||||
47
src/main/java/ru/resprojects/dfops/dto/ResponseDto.java
Normal file
47
src/main/java/ru/resprojects/dfops/dto/ResponseDto.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package ru.resprojects.dfops.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@Schema(description = "Объект для постраничного вывода набора элементов")
|
||||
public class ResponseDto<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3654370454854576301L;
|
||||
|
||||
@Schema(description = "Список элементов")
|
||||
@JsonProperty("elements")
|
||||
private Collection<T> elements;
|
||||
|
||||
@Schema(description = "Номер текущей страницы")
|
||||
@JsonProperty("currentPage")
|
||||
private int currentPage;
|
||||
|
||||
@Schema(description = "Всего элементов")
|
||||
@JsonProperty("totalItems")
|
||||
private long totalItems;
|
||||
|
||||
@Schema(description = "Номер всего страниц")
|
||||
@JsonProperty("totalPages")
|
||||
private int pageCount;
|
||||
|
||||
public ResponseDto(Page<T> response) {
|
||||
this.elements = response.getContent();
|
||||
this.currentPage = response.getNumber() + 1;
|
||||
this.totalItems = response.getTotalElements();
|
||||
this.pageCount = response.getTotalPages();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package ru.resprojects.dfops.dto.account;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "Объект для передачи данных о текущем балансе лицевого счёта")
|
||||
public class AccountBalanceDto implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -82064792530944046L;
|
||||
|
||||
@Schema(description = "ID лицевого счёта")
|
||||
@JsonProperty("account_id")
|
||||
private Long accountId;
|
||||
|
||||
@Schema(description = "Номер лицевого счёта")
|
||||
@JsonProperty("personal_account")
|
||||
private String personalAccount;
|
||||
|
||||
@Schema(description = "Текущий баланс лицевого счёта")
|
||||
@JsonProperty("balance")
|
||||
private BigDecimal balance;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package ru.resprojects.dfops.dto.account;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "Объект для передачи данных о регистрации лицевого счёта для выбранного работника")
|
||||
public class AccountDto implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 8356762606534341601L;
|
||||
|
||||
@Schema(description = "id работника", example = "5000", required = true)
|
||||
@JsonProperty("employee_id")
|
||||
@NotNull
|
||||
private Long employeeId;
|
||||
|
||||
@Schema(description = "лицевой счёт работника", example = "123456789", required = true)
|
||||
@JsonProperty("personal_account")
|
||||
@NotBlank
|
||||
private String personalAccount;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package ru.resprojects.dfops.dto.employee;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.io.Serializable;
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "Объект для передачи данных о регистрации работника в БД")
|
||||
public class EmployeeDto implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 313229127008191126L;
|
||||
|
||||
@Schema(description = "Имя работника", example = "Alex", required = true)
|
||||
@JsonProperty("name")
|
||||
@NotBlank
|
||||
private String name;
|
||||
|
||||
@Schema(description = "E-mail работника", example = "alex@example.com", required = true)
|
||||
@JsonProperty("email")
|
||||
@NotBlank
|
||||
private String email;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package ru.resprojects.dfops.dto.operation;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import ru.resprojects.dfops.model.Operation;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "Объект для передачи данных о сумме и типе операций по счёту")
|
||||
public class OperationAmountDto implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 3448183919367413609L;
|
||||
|
||||
@Schema(description = "Сумма")
|
||||
@JsonProperty("operation_sum")
|
||||
private BigDecimal sum;
|
||||
|
||||
@Schema(description = "Тип операции: WITHDRAW - списание, DEPOSIT - зачисление")
|
||||
@JsonProperty("operation_type")
|
||||
private Operation.OperationType operationType;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package ru.resprojects.dfops.dto.operation;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import ru.resprojects.dfops.model.Operation;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "Операция по лицевому счёту")
|
||||
public class OperationDto implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 6502515027792967205L;
|
||||
|
||||
@Schema(description = "ID лицевого счёта", example = "5002", required = true)
|
||||
@JsonProperty("account_id")
|
||||
private Long accountId;
|
||||
|
||||
@Schema(description = "Сумма операции", example = "100.25", required = true)
|
||||
@JsonProperty("operation_amount")
|
||||
double amount;
|
||||
|
||||
@Schema(description = "Тип операции: DEPOSIT - начисление, WITHDRAW - списание", example = "WITHDRAW", required = true)
|
||||
@JsonProperty("operation_type")
|
||||
Operation.OperationType operationType;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package ru.resprojects.dfops.dto.operation;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "Перевод между лицевыми счетами работника. ID лицевых счетов " +
|
||||
"откуда переводят и куда переводят должны принадлежать одному и тому же работнику")
|
||||
public class OperationTransferDto implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 5904536254136545140L;
|
||||
|
||||
@Schema(description = "ID лицевого счёта откуда переводят", example = "5002", required = true)
|
||||
@JsonProperty("account_id_from")
|
||||
private Long accountIdFrom;
|
||||
|
||||
@Schema(description = "ID лицевого счёта куда переводят", example = "5003", required = true)
|
||||
@JsonProperty("account_id_to")
|
||||
private Long accountIdTo;
|
||||
|
||||
@Schema(description = "Переводимая сумма", example = "100.00", required = true)
|
||||
@JsonProperty("operation_amount")
|
||||
double amount;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package ru.resprojects.dfops.exception;
|
||||
|
||||
public class BadRequestException extends RuntimeException {
|
||||
|
||||
public BadRequestException() {
|
||||
}
|
||||
|
||||
public BadRequestException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public ErrorMessage getErrorMessage() {
|
||||
return new ErrorMessage(this.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package ru.resprojects.dfops.exception;
|
||||
|
||||
public class BadResourceException extends RuntimeException {
|
||||
|
||||
public BadResourceException() {
|
||||
}
|
||||
|
||||
public BadResourceException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public ErrorMessage getErrorMessage() {
|
||||
return new ErrorMessage(this.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package ru.resprojects.dfops.exception;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "Форма вывода ошибки")
|
||||
public class ErrorMessage implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 4089956800265998558L;
|
||||
|
||||
@Schema(description = "Сообщение о ошибке")
|
||||
@JsonProperty("msg")
|
||||
private String message;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package ru.resprojects.dfops.exception;
|
||||
|
||||
public class ResourceAlreadyExistsException extends RuntimeException {
|
||||
|
||||
public ResourceAlreadyExistsException() {
|
||||
}
|
||||
|
||||
public ResourceAlreadyExistsException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public ErrorMessage getErrorMessage() {
|
||||
return new ErrorMessage(this.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package ru.resprojects.dfops.exception;
|
||||
|
||||
public class ResourceNotFoundException extends RuntimeException {
|
||||
|
||||
public ResourceNotFoundException() {
|
||||
}
|
||||
|
||||
public ResourceNotFoundException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public ErrorMessage getErrorMessage() {
|
||||
return new ErrorMessage(this.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package ru.resprojects.dfops.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
|
||||
@MappedSuperclass
|
||||
@Access(AccessType.FIELD)
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
public abstract class AbstractBaseEntity {
|
||||
|
||||
public static final int START_SEQ = 5000;
|
||||
|
||||
@Schema(description = "Уникальный идентификатор")
|
||||
@Id
|
||||
@SequenceGenerator(
|
||||
name = "seq_employees",
|
||||
sequenceName = "seq_employees",
|
||||
allocationSize = 1,
|
||||
initialValue = START_SEQ
|
||||
)
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_employees")
|
||||
@JsonProperty("id")
|
||||
protected Long id;
|
||||
|
||||
protected AbstractBaseEntity() {
|
||||
}
|
||||
|
||||
protected AbstractBaseEntity(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
}
|
||||
72
src/main/java/ru/resprojects/dfops/model/Account.java
Normal file
72
src/main/java/ru/resprojects/dfops/model/Account.java
Normal file
@@ -0,0 +1,72 @@
|
||||
package ru.resprojects.dfops.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.hibernate.annotations.OnDelete;
|
||||
import org.hibernate.annotations.OnDeleteAction;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Table(name = "employee_personal_accounts", uniqueConstraints = {
|
||||
@UniqueConstraint(
|
||||
columnNames = {"employee_id", "personal_account"},
|
||||
name = "employee_personal_accounts_unique_employee_account_idx"
|
||||
)
|
||||
})
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "Счёт работника")
|
||||
public class Account extends AbstractBaseEntity {
|
||||
|
||||
@JsonIgnore
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "employee_id", nullable = false)
|
||||
@OnDelete(action = OnDeleteAction.CASCADE)
|
||||
private Employee employee;
|
||||
|
||||
@Schema(description = "Лицевой счёт работника")
|
||||
@Column(name = "personal_account", nullable = false, unique = true)
|
||||
@NotBlank
|
||||
@Size(min = 2, max = 50)
|
||||
@JsonProperty("personal_account")
|
||||
private String personalAccount;
|
||||
|
||||
@JsonIgnore
|
||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "account")
|
||||
private List<Operation> operations;
|
||||
|
||||
public Account(Employee employee, String personalAccount) {
|
||||
this(null, employee, personalAccount);
|
||||
}
|
||||
|
||||
public Account(Long id, Employee employee, String personalAccount) {
|
||||
super(id);
|
||||
this.employee = employee;
|
||||
this.personalAccount = personalAccount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Account{" +
|
||||
"personalAccount='" + personalAccount + '\'' +
|
||||
", id=" + id +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
59
src/main/java/ru/resprojects/dfops/model/Employee.java
Normal file
59
src/main/java/ru/resprojects/dfops/model/Employee.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package ru.resprojects.dfops.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Table(name = "employees", uniqueConstraints = {@UniqueConstraint(columnNames = "email", name = "employees_unique_email_idx")})
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString(of = {"name", "email"})
|
||||
@Schema(description = "Работник")
|
||||
public class Employee extends AbstractBaseEntity {
|
||||
|
||||
@Schema(description = "Имя работника")
|
||||
@Column(name = "name", nullable = false)
|
||||
@NotBlank
|
||||
@Size(min = 2, max = 100)
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "E-mail работника")
|
||||
@Column(name = "email", nullable = false, unique = true)
|
||||
@Email
|
||||
@NotBlank
|
||||
@Size(max = 100)
|
||||
@JsonProperty("email")
|
||||
private String email;
|
||||
|
||||
@JsonIgnore
|
||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "employee")
|
||||
private List<Account> accounts;
|
||||
|
||||
public Employee(String name, String email) {
|
||||
this(null, name, email);
|
||||
}
|
||||
|
||||
public Employee(Long id, String name, String email) {
|
||||
super(id);
|
||||
this.name = name;
|
||||
this.email = email;
|
||||
}
|
||||
}
|
||||
94
src/main/java/ru/resprojects/dfops/model/Operation.java
Normal file
94
src/main/java/ru/resprojects/dfops/model/Operation.java
Normal file
@@ -0,0 +1,94 @@
|
||||
package ru.resprojects.dfops.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.hibernate.annotations.OnDelete;
|
||||
import org.hibernate.annotations.OnDeleteAction;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import ru.resprojects.dfops.util.DateTimeUtil;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "personal_account_operations", uniqueConstraints = {
|
||||
@UniqueConstraint(
|
||||
columnNames = {"personal_account_id", "operation_date_time"},
|
||||
name = "personal_account_operations_unique_account_datetime_idx"
|
||||
)
|
||||
})
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "Операция по лицевому счёту")
|
||||
public class Operation extends AbstractBaseEntity {
|
||||
|
||||
//TODO найти как отобразить в OpenApi Enum
|
||||
public static enum OperationType {
|
||||
DEPOSIT,
|
||||
WITHDRAW
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "personal_account_id", nullable = false)
|
||||
@OnDelete(action = OnDeleteAction.CASCADE)
|
||||
private Account account;
|
||||
|
||||
@Schema(description = "Дата и время операции")
|
||||
@Column(name = "operation_date_time", nullable = false)
|
||||
@NotNull
|
||||
@DateTimeFormat(pattern = DateTimeUtil.DATE_TIME_PATTERN)
|
||||
@JsonProperty("operation_date_time")
|
||||
private LocalDateTime dateTime;
|
||||
|
||||
@Schema(description = "Тип операции")
|
||||
@Column(name = "operation_type", nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
@NotNull
|
||||
@JsonProperty("operation_type")
|
||||
private OperationType operationType;
|
||||
|
||||
@Schema(description = "Сумма операции")
|
||||
@Column(name = "operation_value", nullable = false)
|
||||
@NotNull
|
||||
@JsonProperty("operation_amount")
|
||||
private BigDecimal operationValue;
|
||||
|
||||
public Operation(Account account, OperationType operationType, BigDecimal operationValue) {
|
||||
this(null, account, operationType, operationValue);
|
||||
}
|
||||
|
||||
public Operation(Long id, Account account, OperationType operationType, BigDecimal operationValue) {
|
||||
super(id);
|
||||
this.account = account;
|
||||
this.dateTime = LocalDateTime.now();
|
||||
this.operationType = operationType;
|
||||
this.operationValue = operationValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Operation{" +
|
||||
" dateTime=" + dateTime +
|
||||
", operationType=" + operationType +
|
||||
", operationValue=" + operationValue +
|
||||
", id=" + id +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package ru.resprojects.dfops.repository;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.EntityGraph;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.resprojects.dfops.model.Account;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface AccountRepository extends JpaRepository<Account, Long> {
|
||||
|
||||
|
||||
Page<Account> findAllByEmployee_Id(long employeeId, Pageable pageable);
|
||||
|
||||
boolean existsByPersonalAccount(String personalAccount);
|
||||
|
||||
@EntityGraph(attributePaths = {"employee"}, type = EntityGraph.EntityGraphType.LOAD)
|
||||
@Query("SELECT a FROM Account a WHERE a.id=:id")
|
||||
Optional<Account> getWithEmployee(@Param("id") long id);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package ru.resprojects.dfops.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.resprojects.dfops.model.Employee;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
|
||||
|
||||
Optional<Employee> findByEmail(String email);
|
||||
|
||||
boolean existsByEmail(String email);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package ru.resprojects.dfops.repository;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.resprojects.dfops.dto.operation.OperationAmountDto;
|
||||
import ru.resprojects.dfops.model.Operation;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface OperationRepository extends JpaRepository<Operation, Long> {
|
||||
|
||||
Page<Operation> getAllByAccount_Id(long accountId, Pageable pageable);
|
||||
|
||||
@SuppressWarnings("JpaQlInspection")
|
||||
@Query("SELECT o " +
|
||||
"FROM Operation o " +
|
||||
"WHERE o.account.id=:accountId AND o.dateTime BETWEEN :startDate AND :endDate")
|
||||
Page<Operation> getAllByAccountIdBetween(
|
||||
@Param("accountId") long accountId,
|
||||
@Param("startDate") LocalDateTime startDate,
|
||||
@Param("endDate") LocalDateTime endDate,
|
||||
Pageable pageable
|
||||
);
|
||||
|
||||
@Query("SELECT new ru.resprojects.dfops.dto.operation.OperationAmountDto(sum(o.operationValue), o.operationType) " +
|
||||
"FROM Operation o " +
|
||||
"WHERE o.account.id=:accountId " +
|
||||
"GROUP BY o.operationType")
|
||||
List<OperationAmountDto> getOperationsAmount(@Param("accountId") long accountId);
|
||||
|
||||
@SuppressWarnings("JpaQlInspection")
|
||||
@Query("SELECT new ru.resprojects.dfops.dto.operation.OperationAmountDto(sum(o.operationValue), o.operationType) " +
|
||||
"FROM Operation o " +
|
||||
"WHERE o.account.id=:accountId AND o.dateTime BETWEEN :startDate AND :endDate " +
|
||||
"GROUP BY o.operationType")
|
||||
List<OperationAmountDto> getOperationsAmountBetween(
|
||||
@Param("accountId") long accountId,
|
||||
@Param("startDate") LocalDateTime startDate,
|
||||
@Param("endDate") LocalDateTime endDate
|
||||
);
|
||||
|
||||
|
||||
@SuppressWarnings("JpaQlInspection")
|
||||
@Query("SELECT sum(o.operationValue) " +
|
||||
"FROM Operation o " +
|
||||
"WHERE o.account.id=:accountId AND o.operationType = :operationType AND o.dateTime BETWEEN :startDate AND :endDate " +
|
||||
"GROUP BY o.operationType")
|
||||
Optional<BigDecimal> getOperationAmountBetween(
|
||||
@Param("accountId") long accountId,
|
||||
@Param("startDate") LocalDateTime startDate,
|
||||
@Param("endDate") LocalDateTime endDate,
|
||||
@Param("operationType") Operation.OperationType operationType
|
||||
);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ru.resprojects.dfops.service;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import ru.resprojects.dfops.exception.BadResourceException;
|
||||
import ru.resprojects.dfops.exception.ResourceAlreadyExistsException;
|
||||
import ru.resprojects.dfops.exception.ResourceNotFoundException;
|
||||
import ru.resprojects.dfops.model.Account;
|
||||
import ru.resprojects.dfops.model.Employee;
|
||||
|
||||
|
||||
public interface AccountService {
|
||||
|
||||
Account create(Employee employee, String personalAccount) throws BadResourceException, ResourceAlreadyExistsException;
|
||||
|
||||
void delete(long id) throws ResourceNotFoundException;
|
||||
|
||||
Account get(long id) throws ResourceNotFoundException;
|
||||
|
||||
Page<Account> getAll(long employeeId, int pageNumber, int rowPerPage);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package ru.resprojects.dfops.service;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import ru.resprojects.dfops.exception.BadResourceException;
|
||||
import ru.resprojects.dfops.exception.ResourceAlreadyExistsException;
|
||||
import ru.resprojects.dfops.exception.ResourceNotFoundException;
|
||||
import ru.resprojects.dfops.model.Account;
|
||||
import ru.resprojects.dfops.model.Employee;
|
||||
import ru.resprojects.dfops.repository.AccountRepository;
|
||||
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
public class AccountServiceImpl implements AccountService {
|
||||
|
||||
private final AccountRepository accountRepository;
|
||||
|
||||
public AccountServiceImpl(AccountRepository accountRepository) {
|
||||
this.accountRepository = accountRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public Account create(Employee employee, String personalAccount) throws BadResourceException, ResourceAlreadyExistsException {
|
||||
if (employee == null) {
|
||||
throw new BadResourceException("Failed to save account. Employee is null");
|
||||
}
|
||||
if (StringUtils.isEmpty(personalAccount)) {
|
||||
throw new BadResourceException("Failed to save account. Personal account is null or empty");
|
||||
}
|
||||
if (accountRepository.existsByPersonalAccount(personalAccount)) {
|
||||
String message = String.format(
|
||||
"Failed to save account. Bank account with %s for employee id %d is exists",
|
||||
personalAccount,
|
||||
employee.getId()
|
||||
);
|
||||
throw new ResourceAlreadyExistsException(message);
|
||||
}
|
||||
Account account = new Account(employee, personalAccount);
|
||||
return accountRepository.save(account);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void delete(long id) throws ResourceNotFoundException {
|
||||
if (get(id) != null) {
|
||||
accountRepository.deleteById(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account get(long id) throws ResourceNotFoundException {
|
||||
return accountRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Cannot find Account with id: " + id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Account> getAll(long employeeId, int pageNumber, int rowPerPage) {
|
||||
return accountRepository.findAllByEmployee_Id(employeeId, PageRequest.of(pageNumber - 1, rowPerPage));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ru.resprojects.dfops.service;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import ru.resprojects.dfops.exception.BadResourceException;
|
||||
import ru.resprojects.dfops.exception.ResourceAlreadyExistsException;
|
||||
import ru.resprojects.dfops.exception.ResourceNotFoundException;
|
||||
import ru.resprojects.dfops.model.Employee;
|
||||
|
||||
public interface EmployeeService {
|
||||
|
||||
Employee create(Employee employee) throws ResourceAlreadyExistsException, BadResourceException;
|
||||
|
||||
void delete(long id) throws ResourceNotFoundException;
|
||||
|
||||
Employee get(long id) throws ResourceNotFoundException;
|
||||
|
||||
Employee getByEmail(String email) throws ResourceNotFoundException;
|
||||
|
||||
Page<Employee> getAll(int pageNumber, int rowPerPage);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package ru.resprojects.dfops.service;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import ru.resprojects.dfops.exception.BadResourceException;
|
||||
import ru.resprojects.dfops.exception.ResourceAlreadyExistsException;
|
||||
import ru.resprojects.dfops.exception.ResourceNotFoundException;
|
||||
import ru.resprojects.dfops.model.Employee;
|
||||
import ru.resprojects.dfops.repository.EmployeeRepository;
|
||||
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
public class EmployeeServiceImpl implements EmployeeService {
|
||||
|
||||
private final EmployeeRepository employeeRepository;
|
||||
|
||||
public EmployeeServiceImpl(EmployeeRepository employeeRepository) {
|
||||
this.employeeRepository = employeeRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public Employee create(Employee employee) throws ResourceAlreadyExistsException, BadResourceException {
|
||||
if (employee == null) {
|
||||
throw new BadResourceException("Failed to save employee. Employee is null");
|
||||
}
|
||||
String email = employee.getEmail();
|
||||
if (StringUtils.isEmpty(email)) {
|
||||
throw new BadResourceException("Failed to save employee. Employee e-mail is null or empty");
|
||||
}
|
||||
if (employeeRepository.existsByEmail(email)) {
|
||||
throw new ResourceAlreadyExistsException("Failed to save employee. Employee with email is exists");
|
||||
}
|
||||
return employeeRepository.save(employee);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void delete(long id) throws ResourceNotFoundException {
|
||||
if (get(id) != null) {
|
||||
employeeRepository.deleteById(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Employee get(long id) throws ResourceNotFoundException {
|
||||
return employeeRepository.findById(id).orElseThrow(
|
||||
() -> new ResourceNotFoundException("Cannot find Employee with id: " + id)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Employee getByEmail(String email) throws ResourceNotFoundException {
|
||||
return employeeRepository.findByEmail(email).orElseThrow(
|
||||
() -> new ResourceNotFoundException("Cannot find Employee with e-mail: " + email)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Employee> getAll(int pageNumber, int rowPerPage) {
|
||||
return employeeRepository.findAll(PageRequest.of(pageNumber - 1, rowPerPage));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package ru.resprojects.dfops.service;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import ru.resprojects.dfops.dto.operation.OperationAmountDto;
|
||||
import ru.resprojects.dfops.exception.BadResourceException;
|
||||
import ru.resprojects.dfops.exception.ResourceNotFoundException;
|
||||
import ru.resprojects.dfops.model.Account;
|
||||
import ru.resprojects.dfops.model.Operation;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
public interface OperationService {
|
||||
|
||||
Operation deposit(Account account, double amount);
|
||||
|
||||
Operation withdraw(Account account, double amount) throws BadResourceException;
|
||||
|
||||
BigDecimal getCurrentBalance(long accountId);
|
||||
|
||||
BigDecimal getDebitBetween(long accountId, LocalDateTime start, LocalDateTime end) throws ResourceNotFoundException;
|
||||
|
||||
BigDecimal getCreditBetween(long accountId, LocalDateTime start, LocalDateTime end) throws ResourceNotFoundException;
|
||||
|
||||
List<OperationAmountDto> getOperationsAmountBetween(long accountId, LocalDateTime start, LocalDateTime end);
|
||||
|
||||
Page<Operation> getAllByAccountId(long accountId, int pageNumber, int rowPerPage);
|
||||
|
||||
Page<Operation> getAllByAccountIdBetween(long accountId, LocalDateTime start, LocalDateTime end, int pageNumber, int rowPerPage);
|
||||
|
||||
void transfer(Account accountFrom, Account accountTo, double amount) throws BadResourceException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package ru.resprojects.dfops.service;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.resprojects.dfops.dto.operation.OperationAmountDto;
|
||||
import ru.resprojects.dfops.exception.BadResourceException;
|
||||
import ru.resprojects.dfops.exception.ResourceNotFoundException;
|
||||
import ru.resprojects.dfops.model.Account;
|
||||
import ru.resprojects.dfops.model.Operation;
|
||||
import ru.resprojects.dfops.repository.OperationRepository;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
public class OperationServiceImpl implements OperationService {
|
||||
|
||||
private final OperationRepository operationRepository;
|
||||
|
||||
public OperationServiceImpl(OperationRepository operationRepository) {
|
||||
this.operationRepository = operationRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public Operation deposit(Account account, double amount) {
|
||||
BigDecimal depositAmount = new BigDecimal(String.valueOf(amount));
|
||||
Operation operation = new Operation(account, Operation.OperationType.DEPOSIT, depositAmount.setScale(2, RoundingMode.HALF_UP));
|
||||
return operationRepository.save(operation);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public Operation withdraw(Account account, double amount) throws BadResourceException {
|
||||
double currentBalance = getCurrentBalance(account.getId()).doubleValue();
|
||||
if (currentBalance < amount) {
|
||||
throw new BadResourceException(
|
||||
"Cannot withdraw from account = " + account.getPersonalAccount()
|
||||
+ " because current balance of account " + account.getPersonalAccount() + "(" + currentBalance + ")"
|
||||
+ " < amount = " + amount
|
||||
);
|
||||
}
|
||||
BigDecimal withdrawAmount = new BigDecimal(String.valueOf(amount));
|
||||
Operation operation = new Operation(account, Operation.OperationType.WITHDRAW, withdrawAmount.setScale(2, RoundingMode.HALF_UP));
|
||||
return operationRepository.save(operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getCurrentBalance(long accountId) {
|
||||
List<OperationAmountDto> operationAmountDtos = operationRepository.getOperationsAmount(accountId);
|
||||
BigDecimal sum = new BigDecimal("0.00");
|
||||
for (OperationAmountDto amountDto : operationAmountDtos) {
|
||||
if (Operation.OperationType.WITHDRAW.equals(amountDto.getOperationType())) {
|
||||
sum = sum.subtract(amountDto.getSum());
|
||||
} else {
|
||||
sum = sum.add(amountDto.getSum());
|
||||
}
|
||||
}
|
||||
return sum.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getDebitBetween(long accountId, LocalDateTime start, LocalDateTime end) throws ResourceNotFoundException {
|
||||
BigDecimal sum = operationRepository.getOperationAmountBetween(accountId, start, end, Operation.OperationType.DEPOSIT).orElse(new BigDecimal("0.00"));
|
||||
return sum.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getCreditBetween(long accountId, LocalDateTime start, LocalDateTime end) throws ResourceNotFoundException {
|
||||
BigDecimal sum = operationRepository.getOperationAmountBetween(accountId, start, end, Operation.OperationType.WITHDRAW).orElse(new BigDecimal("0.00"));
|
||||
return sum.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OperationAmountDto> getOperationsAmountBetween(long accountId, LocalDateTime start, LocalDateTime end) {
|
||||
return operationRepository.getOperationsAmountBetween(accountId, start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Operation> getAllByAccountId(long accountId, int pageNumber, int rowPerPage) {
|
||||
return operationRepository.getAllByAccount_Id(accountId, PageRequest.of(pageNumber - 1, rowPerPage));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Operation> getAllByAccountIdBetween(long accountId, LocalDateTime start, LocalDateTime end, int pageNumber, int rowPerPage) {
|
||||
return operationRepository.getAllByAccountIdBetween(accountId, start, end, PageRequest.of(pageNumber - 1, rowPerPage));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void transfer(Account accountFrom, Account accountTo, double amount) throws BadResourceException {
|
||||
if (!accountFrom.getEmployee().getId().equals(accountTo.getEmployee().getId())) {
|
||||
throw new BadResourceException("Accounts from and account to do not belong to one employee");
|
||||
}
|
||||
withdraw(accountFrom, amount);
|
||||
deposit(accountTo, amount);
|
||||
}
|
||||
|
||||
}
|
||||
31
src/main/java/ru/resprojects/dfops/util/DateTimeUtil.java
Normal file
31
src/main/java/ru/resprojects/dfops/util/DateTimeUtil.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package ru.resprojects.dfops.util;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
public final class DateTimeUtil {
|
||||
|
||||
public static final String DATE_TIME_PATTERN = "dd.MM.yyyy HH:mm";
|
||||
public static final String DATE_TIME_PATTERN_REQUEST = "dd.MM.yyyy-HH:mm";
|
||||
public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN);
|
||||
|
||||
private DateTimeUtil() {
|
||||
}
|
||||
|
||||
public static String toString(LocalDateTime localDateTime) {
|
||||
return localDateTime == null ? "" : localDateTime.format(DATE_TIME_FORMATTER);
|
||||
}
|
||||
|
||||
public static LocalDate parseLocalDate(String localDateString) {
|
||||
return StringUtils.isEmpty(localDateString) ? null : LocalDate.parse(localDateString);
|
||||
}
|
||||
|
||||
public static LocalTime parseLocalTime(String localTimeString) {
|
||||
return StringUtils.isEmpty(localTimeString) ? null : LocalTime.parse(localTimeString);
|
||||
}
|
||||
|
||||
}
|
||||
80
src/main/resources/application.yml
Normal file
80
src/main/resources/application.yml
Normal file
@@ -0,0 +1,80 @@
|
||||
spring:
|
||||
profiles:
|
||||
active: pgsql
|
||||
mvc:
|
||||
servlet:
|
||||
path: /rest/v1/
|
||||
---
|
||||
spring:
|
||||
profiles: pgsql, prod
|
||||
jpa:
|
||||
database: postgresql
|
||||
generate-ddl: true
|
||||
properties:
|
||||
hibernate:
|
||||
jdbc:
|
||||
lob:
|
||||
non_contextual_creation: true
|
||||
database-platform: org.hibernate.dialect.PostgreSQL9Dialect
|
||||
open-in-view: false
|
||||
datasource:
|
||||
platform: postgresql
|
||||
initialization-mode: never
|
||||
---
|
||||
spring:
|
||||
profiles: pgsql
|
||||
datasource:
|
||||
url: jdbc:postgresql://localhost/test
|
||||
username: test
|
||||
password: test
|
||||
---
|
||||
spring:
|
||||
profiles: prod
|
||||
datasource:
|
||||
url: ${DFOPS_PGSQL_DB_HOST}:${DFOPS_PGSQL_DB_PORT}/${DFOPS_PGSQL_DB_NAME}
|
||||
username: ${DFOPS_PGSQL_DB_USER}
|
||||
password: ${DFOPS_PGSQL_DB_PASSWORD}
|
||||
---
|
||||
spring:
|
||||
profiles: test, demo
|
||||
jpa:
|
||||
database: h2
|
||||
open-in-view: false
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
database-platform: org.hibernate.dialect.H2Dialect
|
||||
datasource:
|
||||
url: jdbc:h2:mem:restsrv;DB_CLOSE_ON_EXIT=FALSE
|
||||
initialization-mode: always
|
||||
platform: h2
|
||||
---
|
||||
spring:
|
||||
profiles: demo, pgsql, test, prod
|
||||
jpa:
|
||||
properties:
|
||||
hibernate:
|
||||
format_sql: false
|
||||
jdbc:
|
||||
batch_size: 10
|
||||
order_inserts: true
|
||||
order_updates: true
|
||||
---
|
||||
spring:
|
||||
profiles: test
|
||||
jpa:
|
||||
show-sql: false
|
||||
properties:
|
||||
hibernate:
|
||||
generate_statistics: false
|
||||
---
|
||||
logging:
|
||||
level:
|
||||
ru.resprojects: debug
|
||||
org.springframework.transaction: debug
|
||||
org.springframework: error
|
||||
pattern:
|
||||
file: "%d %p %c{1.} [%t] %m%n"
|
||||
console: "%clr(%d{HH:mm:ss.SSS}){yellow} %clr(%-5p) %clr(---){faint} %clr([%t]){cyan} %clr(%logger{36}){blue} %clr(:){red} %clr(%msg){faint}%n"
|
||||
file:
|
||||
name: restsrv.log
|
||||
max-size: 5MB
|
||||
39
src/main/resources/data-h2.sql
Normal file
39
src/main/resources/data-h2.sql
Normal file
@@ -0,0 +1,39 @@
|
||||
DELETE FROM personal_account_operations;
|
||||
DELETE FROM employee_personal_accounts;
|
||||
DELETE FROM employees;
|
||||
ALTER SEQUENCE seq_employees RESTART WITH 5000;
|
||||
|
||||
INSERT INTO employees (name, email) VALUES
|
||||
('Ivanov Ivan Ivanovich', 'ivanov@example.com'),
|
||||
('Petrov Vasily Victorovich', 'petrov@example.com');
|
||||
|
||||
INSERT INTO employee_personal_accounts (personal_account, employee_id)
|
||||
VALUES ('4154014152522741', 5000),
|
||||
('4131668358915203', 5000),
|
||||
('4281563275602455', 5000),
|
||||
('4103234971123321', 5001),
|
||||
('4132555843841699', 5001);
|
||||
|
||||
INSERT INTO personal_account_operations (operation_date_time, operation_type, operation_value, personal_account_id)
|
||||
VALUES (parsedatetime('2020-05-30 10:00:00', 'yyyy-MM-dd hh:mm:ss'), 'DEPOSIT', 840.35, 5002),
|
||||
(parsedatetime('2020-05-28 11:05:10', 'yyyy-MM-dd hh:mm:ss'), 'DEPOSIT', 625.00, 5002),
|
||||
(parsedatetime('2020-05-25 11:41:10', 'yyyy-MM-dd hh:mm:ss'), 'DEPOSIT', 1080.45, 5002),
|
||||
(parsedatetime('2020-05-30 14:00:10', 'yyyy-MM-dd hh:mm:ss'), 'WITHDRAW', 652.33, 5002),
|
||||
(parsedatetime('2020-05-26 18:10:10', 'yyyy-MM-dd hh:mm:ss'), 'WITHDRAW', 420.00, 5002),
|
||||
(parsedatetime('2020-06-30 10:00:00', 'yyyy-MM-dd hh:mm:ss'), 'DEPOSIT', 1500.52, 5003),
|
||||
(parsedatetime('2020-06-30 11:05:10', 'yyyy-MM-dd hh:mm:ss'), 'DEPOSIT', 800.73, 5003),
|
||||
(parsedatetime('2020-06-30 14:00:10', 'yyyy-MM-dd hh:mm:ss'), 'WITHDRAW', 170.35, 5003),
|
||||
(parsedatetime('2020-06-30 18:10:10', 'yyyy-MM-dd hh:mm:ss'), 'WITHDRAW', 320.00, 5003),
|
||||
(parsedatetime('2020-07-15 12:05:10', 'yyyy-MM-dd hh:mm:ss'), 'DEPOSIT', 800.73, 5004),
|
||||
(parsedatetime('2020-07-15 12:41:10', 'yyyy-MM-dd hh:mm:ss'), 'DEPOSIT', 350.00, 5004),
|
||||
(parsedatetime('2020-07-15 15:00:10', 'yyyy-MM-dd hh:mm:ss'), 'WITHDRAW', 900.35, 5004),
|
||||
(parsedatetime('2020-07-15 17:10:10', 'yyyy-MM-dd hh:mm:ss'), 'WITHDRAW', 600.00, 5004),
|
||||
(parsedatetime('2020-05-15 11:05:10', 'yyyy-MM-dd hh:mm:ss'), 'DEPOSIT', 976.33, 5005),
|
||||
(parsedatetime('2020-05-15 11:41:10', 'yyyy-MM-dd hh:mm:ss'), 'DEPOSIT', 850.00, 5005),
|
||||
(parsedatetime('2020-05-15 14:00:10', 'yyyy-MM-dd hh:mm:ss'), 'WITHDRAW', 200.00, 5005),
|
||||
(parsedatetime('2020-05-15 18:10:10', 'yyyy-MM-dd hh:mm:ss'), 'WITHDRAW', 375.85, 5005),
|
||||
(parsedatetime('2020-04-30 09:00:00', 'yyyy-MM-dd hh:mm:ss'), 'DEPOSIT', 1200.52, 5006),
|
||||
(parsedatetime('2020-04-30 10:35:00', 'yyyy-MM-dd hh:mm:ss'), 'DEPOSIT', 300.53, 5006),
|
||||
(parsedatetime('2020-04-30 10:55:00', 'yyyy-MM-dd hh:mm:ss'), 'DEPOSIT', 450.60, 5006),
|
||||
(parsedatetime('2020-04-30 12:20:10', 'yyyy-MM-dd hh:mm:ss'), 'WITHDRAW', 300.00, 5006),
|
||||
(parsedatetime('2020-04-30 14:10:10', 'yyyy-MM-dd hh:mm:ss'), 'WITHDRAW', 402.95, 5006);
|
||||
40
src/main/resources/data-postgresql.sql
Normal file
40
src/main/resources/data-postgresql.sql
Normal file
@@ -0,0 +1,40 @@
|
||||
DELETE FROM personal_account_operations;
|
||||
DELETE FROM employee_personal_accounts;
|
||||
DELETE FROM employees;
|
||||
ALTER SEQUENCE seq_employees RESTART WITH 5000;
|
||||
|
||||
INSERT INTO employees (name, email) VALUES
|
||||
('Ivanov Ivan Ivanovich', 'ivanov@example.com'),
|
||||
('Petrov Vasily Victorovich', 'petrov@example.com');
|
||||
|
||||
INSERT INTO employee_personal_accounts (personal_account, employee_id)
|
||||
VALUES ('4154014152522741', 5000),
|
||||
('4131668358915203', 5000),
|
||||
('4281563275602455', 5000),
|
||||
('4103234971123321', 5001),
|
||||
('4132555843841699', 5001);
|
||||
|
||||
INSERT INTO personal_account_operations (operation_date_time, operation_type, operation_value, personal_account_id)
|
||||
VALUES ('2020-05-30 10:00:00'::timestamp, 'DEPOSIT', 840.35, 5002),
|
||||
('2020-05-28 11:05:10'::timestamp, 'DEPOSIT', 625.00, 5002),
|
||||
('2020-05-25 11:41:10'::timestamp, 'DEPOSIT', 1080.45, 5002),
|
||||
('2020-05-30 14:00:10'::timestamp, 'WITHDRAW', 652.33, 5002),
|
||||
('2020-05-26 18:10:10'::timestamp, 'WITHDRAW', 420.00, 5002),
|
||||
('2020-06-30 10:00:00'::timestamp, 'DEPOSIT', 1500.52, 5003),
|
||||
('2020-06-30 11:05:10'::timestamp, 'DEPOSIT', 800.73, 5003),
|
||||
('2020-06-30 14:00:10'::timestamp, 'WITHDRAW', 170.35, 5003),
|
||||
('2020-06-30 18:10:10'::timestamp, 'WITHDRAW', 320.00, 5003),
|
||||
('2020-07-15 12:05:10'::timestamp, 'DEPOSIT', 800.73, 5004),
|
||||
('2020-07-15 12:41:10'::timestamp, 'DEPOSIT', 350.00, 5004),
|
||||
('2020-07-15 15:00:10'::timestamp, 'WITHDRAW', 900.35, 5004),
|
||||
('2020-07-15 17:10:10'::timestamp, 'WITHDRAW', 600.00, 5004),
|
||||
('2020-05-15 11:05:10'::timestamp, 'DEPOSIT', 976.33, 5005),
|
||||
('2020-05-15 11:41:10'::timestamp, 'DEPOSIT', 850.00, 5005),
|
||||
('2020-05-15 14:00:10'::timestamp, 'WITHDRAW', 200.00, 5005),
|
||||
('2020-05-15 18:10:10'::timestamp, 'WITHDRAW', 375.85, 5005),
|
||||
('2020-04-30 09:00:00'::timestamp, 'DEPOSIT', 1200.52, 5006),
|
||||
('2020-04-30 10:35:00'::timestamp, 'DEPOSIT', 300.53, 5006),
|
||||
('2020-04-30 10:55:00'::timestamp, 'DEPOSIT', 450.60, 5006),
|
||||
('2020-04-30 12:20:10'::timestamp, 'WITHDRAW', 300.00, 5006),
|
||||
('2020-04-30 14:10:10'::timestamp, 'WITHDRAW', 402.95, 5006);
|
||||
|
||||
35
src/main/resources/schema-h2.sql
Normal file
35
src/main/resources/schema-h2.sql
Normal file
@@ -0,0 +1,35 @@
|
||||
DROP TABLE IF EXISTS personal_account_operations;
|
||||
DROP TABLE IF EXISTS employee_personal_accounts;
|
||||
DROP TABLE IF EXISTS employees;
|
||||
DROP SEQUENCE IF EXISTS seq_employees;
|
||||
|
||||
CREATE SEQUENCE seq_employees MINVALUE 5000;
|
||||
|
||||
CREATE TABLE employees
|
||||
(
|
||||
id BIGINT DEFAULT seq_employees.nextval PRIMARY KEY,
|
||||
name VARCHAR NOT NULL,
|
||||
email VARCHAR NOT NULL
|
||||
);
|
||||
CREATE UNIQUE INDEX employees_unique_email_idx ON employees (email);
|
||||
|
||||
CREATE TABLE employee_personal_accounts (
|
||||
id BIGINT DEFAULT seq_employees.nextval PRIMARY KEY,
|
||||
employee_id BIGINT NOT NULL,
|
||||
personal_account VARCHAR NOT NULL,
|
||||
FOREIGN KEY (employee_id) REFERENCES employees (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE UNIQUE INDEX employee_personal_accounts_unique_employee_account_idx
|
||||
ON employee_personal_accounts (employee_id, personal_account);
|
||||
|
||||
CREATE TABLE personal_account_operations (
|
||||
id BIGINT DEFAULT seq_employees.nextval PRIMARY KEY,
|
||||
personal_account_id BIGINT NOT NULL,
|
||||
operation_date_time TIMESTAMP NOT NULL,
|
||||
operation_type VARCHAR NOT NULL,
|
||||
-- Money data on PostgreSQL using Java https://stackoverflow.com/a/18170030
|
||||
operation_value DECIMAL NOT NULL,
|
||||
FOREIGN KEY (personal_account_id) REFERENCES employee_personal_accounts (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE UNIQUE INDEX personal_account_operations_unique_account_datetime_idx
|
||||
ON personal_account_operations (personal_account_id, operation_date_time);
|
||||
35
src/main/resources/schema-postgresql.sql
Normal file
35
src/main/resources/schema-postgresql.sql
Normal file
@@ -0,0 +1,35 @@
|
||||
DROP TABLE IF EXISTS personal_account_operations;
|
||||
DROP TABLE IF EXISTS employee_personal_accounts;
|
||||
DROP TABLE IF EXISTS employees;
|
||||
DROP SEQUENCE IF EXISTS seq_employees cascade;
|
||||
|
||||
CREATE SEQUENCE seq_employees START 5000;
|
||||
|
||||
CREATE TABLE employees
|
||||
(
|
||||
id BIGINT PRIMARY KEY DEFAULT nextval('seq_employees'),
|
||||
name VARCHAR NOT NULL,
|
||||
email VARCHAR NOT NULL
|
||||
);
|
||||
CREATE UNIQUE INDEX employees_unique_email_idx ON employees (email);
|
||||
|
||||
CREATE TABLE employee_personal_accounts (
|
||||
id BIGINT PRIMARY KEY DEFAULT nextval('seq_employees'),
|
||||
employee_id BIGINT NOT NULL,
|
||||
personal_account VARCHAR NOT NULL,
|
||||
FOREIGN KEY (employee_id) REFERENCES employees (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE UNIQUE INDEX employee_personal_accounts_unique_employee_account_idx
|
||||
ON employee_personal_accounts (employee_id, personal_account);
|
||||
|
||||
CREATE TABLE personal_account_operations (
|
||||
id BIGINT PRIMARY KEY DEFAULT nextval('seq_employees'),
|
||||
personal_account_id BIGINT NOT NULL,
|
||||
operation_date_time TIMESTAMP NOT NULL,
|
||||
operation_type VARCHAR NOT NULL,
|
||||
-- Money data on PostgreSQL using Java https://stackoverflow.com/a/18170030
|
||||
operation_value DECIMAL NOT NULL,
|
||||
FOREIGN KEY (personal_account_id) REFERENCES employee_personal_accounts (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE UNIQUE INDEX personal_account_operations_unique_account_datetime_idx
|
||||
ON personal_account_operations (personal_account_id, operation_date_time);
|
||||
@@ -0,0 +1,141 @@
|
||||
package ru.resprojects.dfops.controller;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
import org.springframework.test.context.jdbc.SqlConfig;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import ru.resprojects.dfops.dto.ResponseDto;
|
||||
import ru.resprojects.dfops.dto.account.AccountDto;
|
||||
import ru.resprojects.dfops.exception.ResourceNotFoundException;
|
||||
import ru.resprojects.dfops.model.Account;
|
||||
import ru.resprojects.dfops.model.Employee;
|
||||
import ru.resprojects.dfops.service.AccountService;
|
||||
import ru.resprojects.dfops.service.EmployeeService;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@ActiveProfiles(profiles = {"test"})
|
||||
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD,
|
||||
scripts = {"classpath:schema-h2.sql", "classpath:data-h2.sql"},
|
||||
config = @SqlConfig(encoding = "UTF-8"))
|
||||
public class AccountControllerTest {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
private AccountService accountService;
|
||||
|
||||
@Autowired
|
||||
private EmployeeService employeeService;
|
||||
|
||||
@Test
|
||||
public void whenRequestGetAllAccountsThenReturnStatus200AndNotEmptyAccountElementList() {
|
||||
ParameterizedTypeReference<ResponseDto<Account>> responseType = new ParameterizedTypeReference<>() { };
|
||||
|
||||
ResponseEntity<ResponseDto<Account>> response = restTemplate.exchange("/rest/v1/account/5000", HttpMethod.GET, null, responseType);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
assertThat(response.getBody().getElements()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestGetAllAccountsForEmployeeIdWithoutAccountsThenReturnStatus204() {
|
||||
Employee employee = employeeService.create(new Employee("Alex", "alex@example.com"));
|
||||
ParameterizedTypeReference<ResponseDto<Account>> responseType = new ParameterizedTypeReference<>() { };
|
||||
|
||||
ResponseEntity<ResponseDto<Account>> response = restTemplate.exchange("/rest/v1/account/" + employee.getId(), HttpMethod.GET, null, responseType);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestGetAllAccountsWithIncorrectEmployeeIdThenReturnStatus404() {
|
||||
ParameterizedTypeReference<ResponseDto<Account>> responseType = new ParameterizedTypeReference<>() { };
|
||||
|
||||
ResponseEntity<ResponseDto<Account>> response = restTemplate.exchange("/rest/v1/account/1", HttpMethod.GET, null, responseType);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestCreateAccountThenReturnStatus200AccountWithId() {
|
||||
AccountDto accountDto = new AccountDto(5000L, "1234");
|
||||
|
||||
ResponseEntity<Account> response = restTemplate.postForEntity("/rest/v1/account/create", accountDto, Account.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
assertThat(response.getBody().getId()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestCreateAccountWithIncorrectEmployeeIdThenReturnStatus404() {
|
||||
AccountDto accountDto = new AccountDto(1L, "1234");
|
||||
|
||||
ResponseEntity<Account> response = restTemplate.postForEntity("/rest/v1/account/create", accountDto, Account.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestCreateAccountWithIncorrectBankAccountThenReturnStatus400() {
|
||||
AccountDto accountDto = new AccountDto(5000L, null);
|
||||
|
||||
ResponseEntity<Account> response = restTemplate.postForEntity("/rest/v1/account/create", accountDto, Account.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestCreateAccountWithExistentBankAccountThenReturnStatus409() {
|
||||
AccountDto accountDto = new AccountDto(5000L, "4154014152522741");
|
||||
|
||||
ResponseEntity<Account> response = restTemplate.postForEntity("/rest/v1/account/create", accountDto, Account.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CONFLICT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestAccountByIdThenReturnStatus200AndAccount() {
|
||||
ResponseEntity<Account> response = restTemplate.getForEntity("/rest/v1/account/get/5002", Account.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTryRequestNonexistentAccountThenReturnStatus404() {
|
||||
ResponseEntity<Account> response = restTemplate.getForEntity("/rest/v1/account/get/1010", Account.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test(expected = ResourceNotFoundException.class)
|
||||
public void whenRequestDeleteAccountByIdThenDeleteAccount() {
|
||||
restTemplate.delete("/rest/v1/account/5002");
|
||||
|
||||
accountService.get(5002);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTryRequestDeleteAccountWithIncorrectIdThenReturnStatus404() {
|
||||
ResponseEntity<Void> response = restTemplate.exchange("/rest/v1/account/1010", HttpMethod.DELETE, null, Void.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package ru.resprojects.dfops.controller;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
import org.springframework.test.context.jdbc.SqlConfig;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import ru.resprojects.dfops.dto.ResponseDto;
|
||||
import ru.resprojects.dfops.dto.employee.EmployeeDto;
|
||||
import ru.resprojects.dfops.exception.ResourceNotFoundException;
|
||||
import ru.resprojects.dfops.model.Employee;
|
||||
import ru.resprojects.dfops.service.EmployeeService;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@ActiveProfiles(profiles = {"test"})
|
||||
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD,
|
||||
scripts = {"classpath:schema-h2.sql", "classpath:data-h2.sql"},
|
||||
config = @SqlConfig(encoding = "UTF-8"))
|
||||
public class EmployeeControllerTest {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
private EmployeeService employeeService;
|
||||
|
||||
@Test
|
||||
public void whenRequestGetAllEmployeesThenReturnStatus200AndNotEmptyElementList() {
|
||||
ParameterizedTypeReference<ResponseDto<Employee>> responseType = new ParameterizedTypeReference<>() { };
|
||||
|
||||
ResponseEntity<ResponseDto<Employee>> response = restTemplate.exchange("/rest/v1/employee/", HttpMethod.GET, null, responseType);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
assertThat(response.getBody().getElements()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestGetAllEmployeesWithPageLimitThenReturnStatus200AndNotEmptyElementList() {
|
||||
ParameterizedTypeReference<ResponseDto<Employee>> responseType = new ParameterizedTypeReference<>() { };
|
||||
|
||||
ResponseEntity<ResponseDto<Employee>> response = restTemplate.exchange("/rest/v1/employee/?page=1&limit=1", HttpMethod.GET, null, responseType);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
assertThat(response.getBody().getElements()).isNotEmpty();
|
||||
assertThat(response.getBody().getPageCount()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestGetEmployeeByIdThenReturnStatus200AndEmployee() {
|
||||
Employee employee = employeeService.get(5001);
|
||||
|
||||
ResponseEntity<Employee> response = restTemplate.getForEntity("/rest/v1/employee/get/5001", Employee.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
assertThat(response.getBody().getName()).isEqualTo(employee.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTryRequestGetEmployeeByIdWithIncorrectIdThenReturnStatus404() {
|
||||
ResponseEntity<Employee> response = restTemplate.getForEntity("/rest/v1/employee/get/1", Employee.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestGetEmployeeByEmailThenReturnStatus200AndEmployee() {
|
||||
Employee employee = employeeService.getByEmail("ivanov@example.com");
|
||||
|
||||
ResponseEntity<Employee> response = restTemplate.getForEntity("/rest/v1/employee/get?email=ivanov@example.com", Employee.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
assertThat(response.getBody().getId()).isEqualTo(employee.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTryRequestGetEmployeeByEmailWithIncorrectEmailThenReturnStatus404() {
|
||||
ResponseEntity<Employee> response = restTemplate.getForEntity("/rest/v1/employee/get?email=123@example.com", Employee.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestCreateEmployeeThenReturnStatus200AndCreatedEmployeeWithId() {
|
||||
EmployeeDto employeeDto = new EmployeeDto("Alex", "alex@example.com");
|
||||
ResponseEntity<Employee> response = restTemplate.postForEntity("/rest/v1/employee/create", employeeDto, Employee.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
assertThat(response.getBody().getId()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTryRequestCreateEmployeeWithIncorrectBodyThenReturnStatus403() {
|
||||
//EmployeeDto employeeDto = new EmployeeDto("Alex", "alex@example.com");
|
||||
ResponseEntity<Employee> response = restTemplate.postForEntity("/rest/v1/employee/create", null, Employee.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTryRequestCreateEmployeeWithEmptyNameThenReturnStatus403() {
|
||||
EmployeeDto employeeDto = new EmployeeDto(null, "alex@example.com");
|
||||
ResponseEntity<Employee> response = restTemplate.postForEntity("/rest/v1/employee/create", null, Employee.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTryRequestCreateEmployeeWithEmptyEmailThenReturnStatus403() {
|
||||
EmployeeDto employeeDto = new EmployeeDto("Alex", null);
|
||||
ResponseEntity<Employee> response = restTemplate.postForEntity("/rest/v1/employee/create", null, Employee.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test(expected = ResourceNotFoundException.class)
|
||||
public void whenRequestDeleteEmployeeThenReturnDeleteEmployeeFromDb() {
|
||||
restTemplate.delete("/rest/v1/employee/5001");
|
||||
|
||||
employeeService.get(5001);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
package ru.resprojects.dfops.controller;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
import org.springframework.test.context.jdbc.SqlConfig;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import ru.resprojects.dfops.dto.ResponseDto;
|
||||
import ru.resprojects.dfops.dto.account.AccountBalanceDto;
|
||||
import ru.resprojects.dfops.dto.operation.OperationAmountDto;
|
||||
import ru.resprojects.dfops.dto.operation.OperationDto;
|
||||
import ru.resprojects.dfops.dto.operation.OperationTransferDto;
|
||||
import ru.resprojects.dfops.exception.ErrorMessage;
|
||||
import ru.resprojects.dfops.model.Account;
|
||||
import ru.resprojects.dfops.model.Employee;
|
||||
import ru.resprojects.dfops.model.Operation;
|
||||
import ru.resprojects.dfops.service.AccountService;
|
||||
import ru.resprojects.dfops.service.OperationService;
|
||||
import ru.resprojects.dfops.util.DateTimeUtil;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.Month;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@ActiveProfiles(profiles = {"test"})
|
||||
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD,
|
||||
scripts = {"classpath:schema-h2.sql", "classpath:data-h2.sql"},
|
||||
config = @SqlConfig(encoding = "UTF-8"))
|
||||
public class OperationControllerTest {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(OperationControllerTest.class);
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
private OperationService operationService;
|
||||
|
||||
@Autowired
|
||||
private AccountService accountService;
|
||||
|
||||
@Test
|
||||
public void whenTransferFromOneAccountToAnotherAccountEmployeeThenReturnStatus200() {
|
||||
OperationTransferDto transferDto = new OperationTransferDto(5002L, 5003L, 1.0);
|
||||
|
||||
ResponseEntity<Void> response = restTemplate.postForEntity("/rest/v1/operation/transfer", transferDto, Void.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTryTransferFromOneAccountEmployeeOneToAnotherAccountEmployeeTwoThenReturnStatus400() {
|
||||
OperationTransferDto transferDto = new OperationTransferDto(5002L, 5006L, 1.0);
|
||||
|
||||
ResponseEntity<Void> response = restTemplate.postForEntity("/rest/v1/operation/transfer", transferDto, Void.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTryTransferFromOneAccountToAnotherAccountWithIncorrectAccountIdThenReturnStatus400() {
|
||||
OperationTransferDto transferDto = new OperationTransferDto(null, 5006L, 1.0);
|
||||
|
||||
ResponseEntity<Void> response = restTemplate.postForEntity("/rest/v1/operation/transfer", transferDto, Void.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTryTransferFromOneAccountToAnotherAccountWithNonexistentAccountIdThenReturnStatus404() {
|
||||
OperationTransferDto transferDto = new OperationTransferDto(1L, 5006L, 1.0);
|
||||
|
||||
ResponseEntity<Void> response = restTemplate.postForEntity("/rest/v1/operation/transfer", transferDto, Void.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestWithdrawToAccountThenReturnStatus200() {
|
||||
OperationDto operationDto = new OperationDto(5002L, 1L, Operation.OperationType.WITHDRAW);
|
||||
|
||||
ResponseEntity<Operation> response = restTemplate.postForEntity("/rest/v1/operation/withdraw", operationDto, Operation.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestWithdrawWithNonexistentAccountIdThenReturnStatus404() {
|
||||
OperationDto operationDto = new OperationDto(1010L, 1L, Operation.OperationType.WITHDRAW);
|
||||
|
||||
ResponseEntity<Operation> response = restTemplate.postForEntity("/rest/v1/operation/withdraw", operationDto, Operation.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestWithdrawWithIncorrectAccountIdThenReturnStatus403() {
|
||||
OperationDto operationDto = new OperationDto(null, 1L, Operation.OperationType.WITHDRAW);
|
||||
|
||||
ResponseEntity<Operation> response = restTemplate.postForEntity("/rest/v1/operation/withdraw", operationDto, Operation.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestWithdrawWithIncorrectOperationTypeIdThenReturnStatus400() {
|
||||
OperationDto operationDto = new OperationDto(5005L, 1L, Operation.OperationType.DEPOSIT);
|
||||
|
||||
ResponseEntity<Operation> response = restTemplate.postForEntity("/rest/v1/operation/withdraw", operationDto, Operation.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestDepositToAccountThenReturnStatus200() {
|
||||
OperationDto operationDto = new OperationDto(5002L, 1L, Operation.OperationType.DEPOSIT);
|
||||
|
||||
ResponseEntity<Operation> response = restTemplate.postForEntity("/rest/v1/operation/deposit", operationDto, Operation.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestDepositWithNonexistentAccountIdThenReturnStatus404() {
|
||||
OperationDto operationDto = new OperationDto(1010L, 1L, Operation.OperationType.DEPOSIT);
|
||||
|
||||
ResponseEntity<Operation> response = restTemplate.postForEntity("/rest/v1/operation/deposit", operationDto, Operation.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestDepositWithIncorrectAccountIdThenReturnStatus403() {
|
||||
OperationDto operationDto = new OperationDto(null, 1L, Operation.OperationType.DEPOSIT);
|
||||
|
||||
ResponseEntity<Operation> response = restTemplate.postForEntity("/rest/v1/operation/deposit", operationDto, Operation.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRequestDepositWithIncorrectOperationTypeIdThenReturnStatus400() {
|
||||
OperationDto operationDto = new OperationDto(null, 1L, Operation.OperationType.WITHDRAW);
|
||||
|
||||
ResponseEntity<Operation> response = restTemplate.postForEntity("/rest/v1/operation/deposit", operationDto, Operation.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetAllOperationsForAccountThenReturnStatus200AndNotNullOperationList() {
|
||||
ParameterizedTypeReference<ResponseDto<Operation>> responseType = new ParameterizedTypeReference<>() { };
|
||||
|
||||
ResponseEntity<ResponseDto<Operation>> response = restTemplate.exchange("/rest/v1/operation/5002", HttpMethod.GET, null, responseType);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
assertThat(response.getBody().getElements()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNoOperationsForAccountThenReturnStatus204() {
|
||||
Account account = accountService.create(new Employee(5000L, "1", "1"), "123");
|
||||
ParameterizedTypeReference<ResponseDto<Operation>> responseType = new ParameterizedTypeReference<>() { };
|
||||
|
||||
ResponseEntity<ResponseDto<Operation>> response = restTemplate.exchange("/rest/v1/operation/" + account.getId(), HttpMethod.GET, null, responseType);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTryGetAllOperationsForNonexistentAccountThenReturnStatus404() {
|
||||
ParameterizedTypeReference<ResponseDto<Operation>> responseType = new ParameterizedTypeReference<>() { };
|
||||
|
||||
ResponseEntity<ResponseDto<Operation>> response = restTemplate.exchange("/rest/v1/operation/1", HttpMethod.GET, null, responseType);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetCurrentBalanceForAccountThenReturnStatus200AndCurrentBalance() {
|
||||
BigDecimal balanceAccount5002 = operationService.getCurrentBalance(5002);
|
||||
|
||||
ResponseEntity<AccountBalanceDto> response = restTemplate.getForEntity("/rest/v1/operation/5002/balance", AccountBalanceDto.class);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
assertThat(response.getBody().getBalance()).isEqualByComparingTo(balanceAccount5002);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetCreditForPeriodForAccountThenReturnStatus200AndCredit() {
|
||||
DateTimeFormatter format = DateTimeFormatter.ofPattern(DateTimeUtil.DATE_TIME_PATTERN_REQUEST);
|
||||
LocalDateTime start = LocalDateTime.of(2020, Month.MAY, 1, 0, 0);
|
||||
LocalDateTime end = LocalDateTime.of(2020, Month.MAY, 31, 23, 59);
|
||||
String startDateTime = start.format(format);
|
||||
String endDateTime = end.format(format);
|
||||
BigDecimal creditAccount5002 = operationService.getCreditBetween(5002, start, end);
|
||||
|
||||
ResponseEntity<OperationAmountDto> response = restTemplate.getForEntity(
|
||||
"/rest/v1/operation/5002/credit/filter?startDateTime=" + startDateTime + "&endDateTime=" + endDateTime,
|
||||
OperationAmountDto.class
|
||||
);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
assertThat(response.getBody().getOperationType()).isEqualTo(Operation.OperationType.WITHDRAW);
|
||||
assertThat(response.getBody().getSum()).isEqualByComparingTo(creditAccount5002);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetCreditForPeriodWithEndDateTimeLaterThanStartDateTimeThenReturnStatus400() {
|
||||
DateTimeFormatter format = DateTimeFormatter.ofPattern(DateTimeUtil.DATE_TIME_PATTERN_REQUEST);
|
||||
LocalDateTime start = LocalDateTime.of(2020, Month.MAY, 1, 0, 0);
|
||||
LocalDateTime end = LocalDateTime.of(2020, Month.MAY, 31, 23, 59);
|
||||
String startDateTime = end.format(format);
|
||||
String endDateTime = start.format(format);
|
||||
|
||||
ResponseEntity<ErrorMessage> response = restTemplate.getForEntity(
|
||||
"/rest/v1/operation/5002/credit/filter?startDateTime=" + startDateTime + "&endDateTime=" + endDateTime,
|
||||
ErrorMessage.class
|
||||
);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetCreditForPeriodWithEmptyStartDateTimeThenReturnStatus400() {
|
||||
String startDateTime = "";
|
||||
String endDateTime = "";
|
||||
|
||||
ResponseEntity<ErrorMessage> response = restTemplate.getForEntity(
|
||||
"/rest/v1/operation/5002/credit/filter?startDateTime=" + startDateTime + "&endDateTime=" + endDateTime,
|
||||
ErrorMessage.class
|
||||
);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetCreditForPeriodWhereNoOperationWithdrawThenReturnStatus200AndCreditZero() {
|
||||
DateTimeFormatter format = DateTimeFormatter.ofPattern(DateTimeUtil.DATE_TIME_PATTERN_REQUEST);
|
||||
LocalDateTime start = LocalDateTime.of(2020, Month.JANUARY, 1, 0, 0);
|
||||
LocalDateTime end = LocalDateTime.of(2020, Month.JANUARY, 31, 23, 59);
|
||||
String startDateTime = start.format(format);
|
||||
String endDateTime = end.format(format);
|
||||
|
||||
ResponseEntity<OperationAmountDto> response = restTemplate.getForEntity(
|
||||
"/rest/v1/operation/5002/credit/filter?startDateTime=" + startDateTime + "&endDateTime=" + endDateTime,
|
||||
OperationAmountDto.class
|
||||
);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
assertThat(response.getBody().getOperationType()).isEqualTo(Operation.OperationType.WITHDRAW);
|
||||
assertThat(response.getBody().getSum()).isEqualByComparingTo(new BigDecimal("0.00"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetOperationsAmountForPeriodThenReturnStatus200AndOperationsAmount() {
|
||||
DateTimeFormatter format = DateTimeFormatter.ofPattern(DateTimeUtil.DATE_TIME_PATTERN_REQUEST);
|
||||
LocalDateTime start = LocalDateTime.of(2020, Month.MAY, 1, 0, 0);
|
||||
LocalDateTime end = LocalDateTime.of(2020, Month.MAY, 31, 23, 59);
|
||||
String startDateTime = start.format(format);
|
||||
String endDateTime = end.format(format);
|
||||
ParameterizedTypeReference<ResponseDto<OperationAmountDto>> responseType = new ParameterizedTypeReference<>() { };
|
||||
List<OperationAmountDto> operationAmountDtos = operationService.getOperationsAmountBetween(5002, start, end);
|
||||
|
||||
ResponseEntity<ResponseDto<OperationAmountDto>> response = restTemplate.exchange(
|
||||
"/rest/v1/operation/5002/operations_amount/filter?startDateTime=" + startDateTime + "&endDateTime=" + endDateTime,
|
||||
HttpMethod.GET,
|
||||
null,
|
||||
responseType
|
||||
);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
assertThat(response.getBody().getElements()).isNotEmpty();
|
||||
assertThat(response.getBody().getElements()).isSubsetOf(operationAmountDtos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetOperationsAmountForPeriodWhereNoDepositAndWithdrawThenReturnStatus200AndEmptyList() {
|
||||
DateTimeFormatter format = DateTimeFormatter.ofPattern(DateTimeUtil.DATE_TIME_PATTERN_REQUEST);
|
||||
LocalDateTime start = LocalDateTime.of(2020, Month.JANUARY, 1, 0, 0);
|
||||
LocalDateTime end = LocalDateTime.of(2020, Month.JANUARY, 31, 23, 59);
|
||||
String startDateTime = start.format(format);
|
||||
String endDateTime = end.format(format);
|
||||
ParameterizedTypeReference<ResponseDto<OperationAmountDto>> responseType = new ParameterizedTypeReference<>() { };
|
||||
|
||||
ResponseEntity<ResponseDto<OperationAmountDto>> response = restTemplate.exchange(
|
||||
"/rest/v1/operation/5002/operations_amount/filter?startDateTime=" + startDateTime + "&endDateTime=" + endDateTime,
|
||||
HttpMethod.GET,
|
||||
null,
|
||||
responseType
|
||||
);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getBody()).isNotNull();
|
||||
assertThat(response.getBody().getElements()).isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package ru.resprojects.dfops.service;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
import org.springframework.test.context.jdbc.SqlConfig;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import ru.resprojects.dfops.DfopsApplication;
|
||||
import ru.resprojects.dfops.exception.BadResourceException;
|
||||
import ru.resprojects.dfops.exception.ResourceAlreadyExistsException;
|
||||
import ru.resprojects.dfops.exception.ResourceNotFoundException;
|
||||
import ru.resprojects.dfops.model.Account;
|
||||
import ru.resprojects.dfops.model.Employee;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = DfopsApplication.class)
|
||||
@ActiveProfiles(profiles = {"test"})
|
||||
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD,
|
||||
scripts = {"classpath:schema-h2.sql", "classpath:data-h2.sql"},
|
||||
config = @SqlConfig(encoding = "UTF-8"))
|
||||
public class AccountServiceTest {
|
||||
|
||||
private static final long EMPLOYEE_ID = 5000L;
|
||||
|
||||
@Autowired
|
||||
private AccountService accountService;
|
||||
|
||||
private Account savedAccount;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
Employee employee = new Employee(EMPLOYEE_ID, "Ivanov Ivan Ivanovich", "ivanov@example.com");
|
||||
savedAccount = accountService.create(employee, "1234567899876543");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetAllAccountsThenReturnNotEmptyList() {
|
||||
Page<Account> accounts = accountService.getAll(EMPLOYEE_ID, 1, 10);
|
||||
|
||||
assertThat(accounts.hasContent()).isTrue();
|
||||
assertThat(accounts.getContent()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetAccountByIdThenReturnAccount() {
|
||||
Account account = accountService.get(savedAccount.getId());
|
||||
|
||||
assertThat(account).isNotNull();
|
||||
assertThat(account).isEqualTo(savedAccount);
|
||||
assertThat(account.getEmployee().getId()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteAccountTest() {
|
||||
accountService.delete(savedAccount.getId());
|
||||
|
||||
Page<Account> accounts = accountService.getAll(EMPLOYEE_ID, 1, 10);
|
||||
|
||||
assertThat(accounts.hasContent()).isTrue();
|
||||
assertThat(accounts.getContent().contains(savedAccount)).isFalse();
|
||||
}
|
||||
|
||||
@Test(expected = BadResourceException.class)
|
||||
public void whenTrySaveNullThenException() {
|
||||
accountService.create(null, null);
|
||||
}
|
||||
|
||||
@Test(expected = ResourceAlreadyExistsException.class)
|
||||
public void whenSaveAccountWithExistingBankAccountThenException() {
|
||||
accountService.create(savedAccount.getEmployee(), savedAccount.getPersonalAccount());
|
||||
}
|
||||
|
||||
@Test(expected = ResourceNotFoundException.class)
|
||||
public void whenTryDeleteByNonexistentIdThenException() {
|
||||
accountService.delete(5);
|
||||
}
|
||||
|
||||
@Test(expected = ResourceNotFoundException.class)
|
||||
public void whenGetByNonexistentIdThenException() {
|
||||
accountService.get(1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package ru.resprojects.dfops.service;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
import org.springframework.test.context.jdbc.SqlConfig;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import ru.resprojects.dfops.DfopsApplication;
|
||||
import ru.resprojects.dfops.exception.BadResourceException;
|
||||
import ru.resprojects.dfops.exception.ResourceAlreadyExistsException;
|
||||
import ru.resprojects.dfops.exception.ResourceNotFoundException;
|
||||
import ru.resprojects.dfops.model.Employee;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = DfopsApplication.class)
|
||||
@ActiveProfiles(profiles = {"test"})
|
||||
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD,
|
||||
scripts = {"classpath:schema-h2.sql", "classpath:data-h2.sql"},
|
||||
config = @SqlConfig(encoding = "UTF-8"))
|
||||
public class EmployeeServiceTest {
|
||||
|
||||
@Autowired
|
||||
private EmployeeService employeeService;
|
||||
|
||||
@Test
|
||||
public void whenSaveEmployeeThenReturnEmployeeWithId() {
|
||||
Employee employee = new Employee("Alex", "example@example.com");
|
||||
|
||||
Employee employeeWithId = employeeService.create(employee);
|
||||
|
||||
assertThat(employeeWithId).isNotNull();
|
||||
assertThat(employeeWithId.getId()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetByEmailThenReturnEmploy() {
|
||||
Employee employee = new Employee("Alex", "example@example.com");
|
||||
employeeService.create(employee);
|
||||
|
||||
Employee existingEmployee = employeeService.getByEmail("example@example.com");
|
||||
|
||||
assertThat(existingEmployee).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteEmployeeTest() {
|
||||
Employee employee = new Employee("Alex", "example@example.com");
|
||||
Employee savedEmployee = employeeService.create(employee);
|
||||
|
||||
employeeService.delete(savedEmployee.getId());
|
||||
|
||||
Page<Employee> employees = employeeService.getAll(1, 10);
|
||||
assertThat(employees.hasContent()).isTrue();
|
||||
assertThat(employees.getContent().contains(savedEmployee)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetAllEmployeesThenReturnNotEmptyListOfEmployees() {
|
||||
Page<Employee> employees = employeeService.getAll(1, 10);
|
||||
|
||||
assertThat(employees.getContent()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test(expected = BadResourceException.class)
|
||||
public void whenTrySaveNullThenException() {
|
||||
employeeService.create(null);
|
||||
}
|
||||
|
||||
@Test(expected = ResourceAlreadyExistsException.class)
|
||||
public void whenSaveEmployeeWithExistingEmailThenException() {
|
||||
Employee employee = new Employee("Alex", "example@example.com");
|
||||
employeeService.create(employee);
|
||||
|
||||
employeeService.create(employee);
|
||||
}
|
||||
|
||||
@Test(expected = ResourceNotFoundException.class)
|
||||
public void whenTryDeleteByNonexistentIdThenException() {
|
||||
employeeService.delete(5);
|
||||
}
|
||||
|
||||
@Test(expected = ResourceNotFoundException.class)
|
||||
public void whenGetByNonexistentIdThenException() {
|
||||
employeeService.get(1);
|
||||
}
|
||||
|
||||
@Test(expected = ResourceNotFoundException.class)
|
||||
public void whenGetByNonexistentEmailThenException() {
|
||||
employeeService.getByEmail("abc@example.com");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
package ru.resprojects.dfops.service;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
import org.springframework.test.context.jdbc.SqlConfig;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import ru.resprojects.dfops.DfopsApplication;
|
||||
import ru.resprojects.dfops.dto.operation.OperationAmountDto;
|
||||
import ru.resprojects.dfops.exception.BadResourceException;
|
||||
import ru.resprojects.dfops.model.Account;
|
||||
import ru.resprojects.dfops.model.Employee;
|
||||
import ru.resprojects.dfops.model.Operation;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.Month;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = DfopsApplication.class)
|
||||
@ActiveProfiles(profiles = {"test"})
|
||||
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD,
|
||||
scripts = {"classpath:schema-h2.sql", "classpath:data-h2.sql"},
|
||||
config = @SqlConfig(encoding = "UTF-8"))
|
||||
public class OperationServiceTest {
|
||||
|
||||
private static final long EMPLOYEE_ONE_ID = 5000L;
|
||||
private static final long EMPLOYEE_TWO_ID = 5001L;
|
||||
private static final long ACCOUNT_ONE_EMPLOYEE_ONE_ID = 5002L;
|
||||
private static final long ACCOUNT_TWO_EMPLOYEE_ONE_ID = 5003L;
|
||||
private static final long ACCOUNT_THREE_EMPLOYEE_TWO_ID = 5006L;
|
||||
private static final double CURRENT_BALANCE_ACCOUNT_ONE = 1473.47;
|
||||
private static final long ALL_OPERATIONS_ACCOUNT_ONE = 5;
|
||||
|
||||
@Autowired
|
||||
private OperationService operationService;
|
||||
|
||||
private Account accountOne;
|
||||
private Account accountTwo;
|
||||
private Account accountThree;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
Employee employeeOne = new Employee(EMPLOYEE_ONE_ID, "Ivanov Ivan Ivanovich", "ivanov@example.com");
|
||||
Employee employeeTwo = new Employee(EMPLOYEE_TWO_ID, "Petrov Vasily Victorovich", "petrov@example.com");
|
||||
accountOne = new Account(ACCOUNT_ONE_EMPLOYEE_ONE_ID, employeeOne, "4154014152522741");
|
||||
accountTwo = new Account(ACCOUNT_TWO_EMPLOYEE_ONE_ID, employeeOne, "4131668358915203");
|
||||
accountThree = new Account(ACCOUNT_THREE_EMPLOYEE_TWO_ID, employeeTwo, "4132555843841699");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentBalanceTest() {
|
||||
double currentBalance = operationService.getCurrentBalance(ACCOUNT_ONE_EMPLOYEE_ONE_ID).doubleValue();
|
||||
|
||||
assertThat(currentBalance).isEqualTo(CURRENT_BALANCE_ACCOUNT_ONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenDepositToAccountThenIncreaseCurrentBalance() {
|
||||
double amount = 200;
|
||||
double originalBalance = operationService.getCurrentBalance(ACCOUNT_ONE_EMPLOYEE_ONE_ID).doubleValue();
|
||||
|
||||
operationService.deposit(accountOne, amount);
|
||||
|
||||
double changedBalance = operationService.getCurrentBalance(ACCOUNT_ONE_EMPLOYEE_ONE_ID).doubleValue();
|
||||
assertThat(changedBalance).isGreaterThan(originalBalance);
|
||||
assertThat(changedBalance - originalBalance).isEqualTo(amount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenWithdrawFromAccountThenDecreaseCurrentBalance() {
|
||||
double amount = 200;
|
||||
double originalBalance = operationService.getCurrentBalance(ACCOUNT_ONE_EMPLOYEE_ONE_ID).doubleValue();
|
||||
|
||||
operationService.withdraw(accountOne, amount);
|
||||
|
||||
double changedBalance = operationService.getCurrentBalance(ACCOUNT_ONE_EMPLOYEE_ONE_ID).doubleValue();
|
||||
assertThat(changedBalance).isLessThan(originalBalance);
|
||||
assertThat(originalBalance - changedBalance).isEqualTo(amount);
|
||||
}
|
||||
|
||||
@Test(expected = BadResourceException.class)
|
||||
public void whenTryWithdrawFromAccountMoreThanCurrentBalanceThenException() {
|
||||
double amount = 200;
|
||||
double originalBalance = operationService.getCurrentBalance(ACCOUNT_ONE_EMPLOYEE_ONE_ID).doubleValue();
|
||||
|
||||
operationService.withdraw(accountOne, originalBalance + amount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetDebitBetweenThenReturnDebitAtPeriod() {
|
||||
double depositAmount = 200.00;
|
||||
LocalDateTime start = LocalDateTime.of(LocalDate.now(), LocalTime.of(0, 0));
|
||||
LocalDateTime end = LocalDateTime.of(LocalDate.now(), LocalTime.of(23, 59));
|
||||
operationService.deposit(accountOne, depositAmount);
|
||||
|
||||
double debitAtPeriod = operationService.getDebitBetween(ACCOUNT_ONE_EMPLOYEE_ONE_ID, start, end).doubleValue();
|
||||
|
||||
assertThat(debitAtPeriod).isEqualTo(depositAmount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetDebitBetweenWithNonexistentAccountIdThenReturnZeroValue() {
|
||||
LocalDateTime start = LocalDateTime.of(LocalDate.now(), LocalTime.of(0, 0));
|
||||
LocalDateTime end = LocalDateTime.of(LocalDate.now(), LocalTime.of(23, 59));
|
||||
|
||||
BigDecimal result = operationService.getDebitBetween(123, start, end);
|
||||
|
||||
assertThat(result).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetCreditBetweenThenReturnCreditAtPeriod() {
|
||||
double withdrawAmount = 200.00;
|
||||
LocalDateTime start = LocalDateTime.of(LocalDate.now(), LocalTime.of(0, 0));
|
||||
LocalDateTime end = LocalDateTime.of(LocalDate.now(), LocalTime.of(23, 59));
|
||||
operationService.withdraw(accountOne, withdrawAmount);
|
||||
|
||||
double creditAtPeriod = operationService.getCreditBetween(ACCOUNT_ONE_EMPLOYEE_ONE_ID, start, end).doubleValue();
|
||||
|
||||
assertThat(creditAtPeriod).isEqualTo(withdrawAmount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetCreditBetweenWithNonexistentAccountIdThenReturnZeroValue() {
|
||||
LocalDateTime start = LocalDateTime.of(LocalDate.now(), LocalTime.of(0, 0));
|
||||
LocalDateTime end = LocalDateTime.of(LocalDate.now(), LocalTime.of(23, 59));
|
||||
|
||||
BigDecimal result = operationService.getCreditBetween(123, start, end);
|
||||
|
||||
assertThat(result).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetAllByAccountIdThenReturnAllOperationsForAccountId() {
|
||||
Page<Operation> allOperationsForAccountOne = operationService.getAllByAccountId(ACCOUNT_ONE_EMPLOYEE_ONE_ID, 1, 10);
|
||||
|
||||
assertThat(allOperationsForAccountOne.hasContent()).isTrue();
|
||||
assertThat(allOperationsForAccountOne.getContent()).isNotEmpty();
|
||||
assertThat(allOperationsForAccountOne.getContent().size()).isEqualTo(ALL_OPERATIONS_ACCOUNT_ONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetAllByAccountIdWithNonexistentAccountThenReturnEmptyList() {
|
||||
Page<Operation> allOperationsForAccountOne = operationService.getAllByAccountId(123, 1, 10);
|
||||
|
||||
assertThat(allOperationsForAccountOne.hasContent()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetAllByAccountIdBetweenThenReturnAllOperationsAtPeriodForAccountId() {
|
||||
operationService.deposit(accountOne, 10.00);
|
||||
operationService.withdraw(accountOne, 10.00);
|
||||
LocalDateTime start = LocalDateTime.of(LocalDate.now(), LocalTime.of(0, 0));
|
||||
LocalDateTime end = LocalDateTime.of(LocalDate.now(), LocalTime.of(23, 59));
|
||||
Page<Operation> allOperationsForAccountOne = operationService.getAllByAccountIdBetween(ACCOUNT_ONE_EMPLOYEE_ONE_ID, start, end, 1, 10);
|
||||
|
||||
assertThat(allOperationsForAccountOne.hasContent()).isTrue();
|
||||
assertThat(allOperationsForAccountOne.getContent()).isNotEmpty();
|
||||
assertThat(allOperationsForAccountOne.getContent().size()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetAllByAccountIdBetweenWithNonexistentAccountThenReturnEmptyList() {
|
||||
LocalDateTime start = LocalDateTime.of(LocalDate.of(2020, Month.JANUARY, 1), LocalTime.of(0, 0));
|
||||
LocalDateTime end = LocalDateTime.of(LocalDate.of(2020, Month.DECEMBER, 31), LocalTime.of(23, 59));
|
||||
Page<Operation> allOperationsForAccountOne = operationService.getAllByAccountIdBetween(123, start, end, 1, 10);
|
||||
|
||||
assertThat(allOperationsForAccountOne.hasContent()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTransferFromAccountOneToAccountTwoThenWithdrawFromAccountOneAndDepositToAccountTwo() {
|
||||
double transferAmount = 1.00;
|
||||
double currentBalanceAccountOne = operationService.getCurrentBalance(ACCOUNT_ONE_EMPLOYEE_ONE_ID).doubleValue();
|
||||
double currentBalanceAccountTwo = operationService.getCurrentBalance(ACCOUNT_TWO_EMPLOYEE_ONE_ID).doubleValue();
|
||||
|
||||
operationService.transfer(accountOne, accountTwo, transferAmount);
|
||||
|
||||
double currentBalanceAfterTransferAccountOne = operationService.getCurrentBalance(ACCOUNT_ONE_EMPLOYEE_ONE_ID).doubleValue();
|
||||
double currentBalanceAfterTransferAccountTwo = operationService.getCurrentBalance(ACCOUNT_TWO_EMPLOYEE_ONE_ID).doubleValue();
|
||||
|
||||
assertThat(currentBalanceAfterTransferAccountOne).isLessThan(currentBalanceAccountOne);
|
||||
assertThat(currentBalanceAfterTransferAccountTwo).isGreaterThan(currentBalanceAccountTwo);
|
||||
assertThat(currentBalanceAccountOne - currentBalanceAfterTransferAccountOne).isEqualTo(transferAmount);
|
||||
assertThat(currentBalanceAfterTransferAccountTwo - currentBalanceAccountTwo).isEqualTo(transferAmount);
|
||||
}
|
||||
|
||||
@Test(expected = BadResourceException.class)
|
||||
public void whenTransferFromAccountOneToAccountThreeThenException() {
|
||||
double transferAmount = 1.00;
|
||||
|
||||
operationService.transfer(accountOne, accountThree, transferAmount);
|
||||
}
|
||||
|
||||
@Test(expected = BadResourceException.class)
|
||||
public void whenTransferFromAccountOneToAccountTwoButTransferAmountMoreThanCurrentBalanceFromAccountOneThenException() {
|
||||
double transferAmount = operationService.getCurrentBalance(accountOne.getId()).doubleValue() + 10.00;
|
||||
|
||||
operationService.transfer(accountOne, accountThree, transferAmount);
|
||||
}
|
||||
|
||||
//TODO переделать тест
|
||||
@Test
|
||||
public void whenGetOperationsAmountBetweenThenReturnListOfOperationsAmount() {
|
||||
LocalDateTime start = LocalDateTime.of(LocalDate.of(2020, Month.MAY, 1), LocalTime.of(0, 0));
|
||||
LocalDateTime end = LocalDateTime.of(LocalDate.of(2020, Month.MAY, 31), LocalTime.of(23, 59));
|
||||
List<OperationAmountDto> result = operationService.getOperationsAmountBetween(ACCOUNT_ONE_EMPLOYEE_ONE_ID, start, end);
|
||||
|
||||
assertThat(result).isNotEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user