loan permissions
This commit is contained in:
@@ -72,14 +72,15 @@ public class LoansApiResource {
|
|||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("template")
|
@Path("template")
|
||||||
@Consumes({MediaType.APPLICATION_JSON})
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({MediaType.APPLICATION_JSON})
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
public String retrieveDetailsForNewLoanApplicationStepOne(
|
public String retrieveDetailsForNewLoanApplicationStepOne(
|
||||||
@QueryParam("clientId") final Long clientId,
|
@QueryParam("clientId") final Long clientId,
|
||||||
@QueryParam("productId") final Long productId,
|
@QueryParam("productId") final Long productId,
|
||||||
@Context final UriInfo uriInfo) {
|
@Context final UriInfo uriInfo) {
|
||||||
|
|
||||||
NewLoanData workflowData = this.loanReadPlatformService.retrieveClientAndProductDetails(clientId, productId);
|
NewLoanData workflowData = this.loanReadPlatformService
|
||||||
|
.retrieveClientAndProductDetails(clientId, productId);
|
||||||
|
|
||||||
String selectedFields = "";
|
String selectedFields = "";
|
||||||
return this.jsonFormattingService.convertRequest(workflowData,
|
return this.jsonFormattingService.convertRequest(workflowData,
|
||||||
@@ -89,21 +90,32 @@ public class LoansApiResource {
|
|||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("{loanId}")
|
@Path("{loanId}")
|
||||||
@Consumes({MediaType.APPLICATION_JSON})
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({MediaType.APPLICATION_JSON})
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
public String retrieveLoanAccountDetails(
|
public String retrieveLoanAccountDetails(
|
||||||
@PathParam("loanId") final Long loanId, @Context final UriInfo uriInfo) {
|
@PathParam("loanId") final Long loanId,
|
||||||
|
@Context final UriInfo uriInfo) {
|
||||||
|
|
||||||
LoanBasicDetailsData loanBasicDetails = this.loanReadPlatformService
|
LoanBasicDetailsData loanBasicDetails = this.loanReadPlatformService
|
||||||
.retrieveLoanAccountDetails(loanId);
|
.retrieveLoanAccountDetails(loanId);
|
||||||
|
|
||||||
Collection<LoanTransactionDatajpw> loanRepayments = this.loanReadPlatformService.retrieveLoanPayments(loanId);
|
Collection<LoanTransactionDatajpw> loanRepayments = this.loanReadPlatformService
|
||||||
Collection<LoanRepaymentPeriodDatajpw> repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId);
|
.retrieveLoanPayments(loanId);
|
||||||
|
Collection<LoanRepaymentPeriodDatajpw> repaymentSchedule = this.loanReadPlatformService
|
||||||
LoanAccountSummaryData summary = this.loanReadPlatformService.retrieveSummary(loanBasicDetails.getPrincipal(), repaymentSchedule, loanRepayments);
|
.retrieveRepaymentSchedule(loanId);
|
||||||
LoanPermissionData permissions = this.loanReadPlatformService.retrieveLoanPermissions(loanBasicDetails, summary.isWaiveAllowed(loanBasicDetails.getInArrearsTolerance()));
|
|
||||||
|
LoanAccountSummaryData summary = this.loanReadPlatformService
|
||||||
LoanAccountData loanAccount = new LoanAccountData(loanBasicDetails, summary, repaymentSchedule, loanRepayments, permissions);
|
.retrieveSummary(loanBasicDetails.getPrincipal(),
|
||||||
|
repaymentSchedule, loanRepayments);
|
||||||
|
|
||||||
|
LoanPermissionData permissions = this.loanReadPlatformService
|
||||||
|
.retrieveLoanPermissions(loanBasicDetails, summary
|
||||||
|
.isWaiveAllowed(loanBasicDetails
|
||||||
|
.getInArrearsTolerance()), loanRepayments
|
||||||
|
.size());
|
||||||
|
|
||||||
|
LoanAccountData loanAccount = new LoanAccountData(loanBasicDetails,
|
||||||
|
summary, repaymentSchedule, loanRepayments, permissions);
|
||||||
|
|
||||||
String selectedFields = "";
|
String selectedFields = "";
|
||||||
String associatedFields = "summary,repaymentSchedule,loanRepayments,permissions,convenienceData";
|
String associatedFields = "summary,repaymentSchedule,loanRepayments,permissions,convenienceData";
|
||||||
@@ -113,16 +125,19 @@ public class LoansApiResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Consumes({MediaType.APPLICATION_JSON})
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({MediaType.APPLICATION_JSON})
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
public Response calculateLoanScheduleOrSubmitLoanApplication(
|
public Response calculateLoanScheduleOrSubmitLoanApplication(
|
||||||
@QueryParam("command") final String commandParam,
|
@QueryParam("command") final String commandParam,
|
||||||
final String jsonRequestBody) {
|
final String jsonRequestBody) {
|
||||||
|
|
||||||
SubmitLoanApplicationCommand command = this.apiDataConversionService.convertJsonToSubmitLoanApplicationCommand(jsonRequestBody);
|
SubmitLoanApplicationCommand command = this.apiDataConversionService
|
||||||
|
.convertJsonToSubmitLoanApplicationCommand(jsonRequestBody);
|
||||||
CalculateLoanScheduleCommand calculateLoanScheduleCommand = command.toCalculateLoanScheduleCommand();
|
|
||||||
LoanSchedule loanSchedule = this.calculationPlatformService.calculateLoanSchedule(calculateLoanScheduleCommand);
|
CalculateLoanScheduleCommand calculateLoanScheduleCommand = command
|
||||||
|
.toCalculateLoanScheduleCommand();
|
||||||
|
LoanSchedule loanSchedule = this.calculationPlatformService
|
||||||
|
.calculateLoanSchedule(calculateLoanScheduleCommand);
|
||||||
|
|
||||||
// for now just auto generating the loan schedule and setting support
|
// for now just auto generating the loan schedule and setting support
|
||||||
// for 'manual' loan schedule creation later.
|
// for 'manual' loan schedule creation later.
|
||||||
@@ -132,31 +147,36 @@ public class LoansApiResource {
|
|||||||
return Response.ok().entity(loanSchedule).build();
|
return Response.ok().entity(loanSchedule).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityIdentifier identifier = this.loanWritePlatformService.submitLoanApplication(command);
|
EntityIdentifier identifier = this.loanWritePlatformService
|
||||||
|
.submitLoanApplication(command);
|
||||||
|
|
||||||
return Response.ok().entity(identifier).build();
|
return Response.ok().entity(identifier).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
@Path("{loanId}")
|
@Path("{loanId}")
|
||||||
@Consumes({MediaType.APPLICATION_JSON})
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({MediaType.APPLICATION_JSON})
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
public Response deleteLoanApplication(@PathParam("loanId") final Long loanId) {
|
public Response deleteLoanApplication(@PathParam("loanId") final Long loanId) {
|
||||||
|
|
||||||
EntityIdentifier identifier = this.loanWritePlatformService.deleteLoan(loanId);
|
EntityIdentifier identifier = this.loanWritePlatformService
|
||||||
|
.deleteLoan(loanId);
|
||||||
|
|
||||||
return Response.ok().entity(identifier).build();
|
return Response.ok().entity(identifier).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("{loanId}")
|
@Path("{loanId}")
|
||||||
@Consumes({MediaType.APPLICATION_JSON})
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({MediaType.APPLICATION_JSON})
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
public Response stateTransitions(@PathParam("loanId") final Long loanId,
|
public Response stateTransitions(@PathParam("loanId") final Long loanId,
|
||||||
@QueryParam("command") final String commandParam, final String jsonRequestBody) {
|
@QueryParam("command") final String commandParam,
|
||||||
|
final String jsonRequestBody) {
|
||||||
|
|
||||||
|
LoanStateTransitionCommand command = this.apiDataConversionService
|
||||||
|
.convertJsonToLoanStateTransitionCommand(loanId,
|
||||||
|
jsonRequestBody);
|
||||||
|
|
||||||
LoanStateTransitionCommand command = this.apiDataConversionService.convertJsonToLoanStateTransitionCommand(loanId, jsonRequestBody);
|
|
||||||
|
|
||||||
Response response = null;
|
Response response = null;
|
||||||
|
|
||||||
if (is(commandParam, "reject")) {
|
if (is(commandParam, "reject")) {
|
||||||
@@ -177,8 +197,9 @@ public class LoansApiResource {
|
|||||||
response = Response.ok().entity(identifier).build();
|
response = Response.ok().entity(identifier).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
UndoStateTransitionCommand undoCommand = new UndoStateTransitionCommand(loanId, command.getNote());
|
UndoStateTransitionCommand undoCommand = new UndoStateTransitionCommand(
|
||||||
|
loanId, command.getNote());
|
||||||
|
|
||||||
if (is(commandParam, "undoapproval")) {
|
if (is(commandParam, "undoapproval")) {
|
||||||
EntityIdentifier identifier = this.loanWritePlatformService
|
EntityIdentifier identifier = this.loanWritePlatformService
|
||||||
.undoLoanApproval(undoCommand);
|
.undoLoanApproval(undoCommand);
|
||||||
@@ -203,21 +224,25 @@ public class LoansApiResource {
|
|||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("{loanId}/transactions")
|
@Path("{loanId}/transactions")
|
||||||
@Consumes({MediaType.APPLICATION_JSON})
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({MediaType.APPLICATION_JSON})
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
public Response executeLoanTransaction(
|
public Response executeLoanTransaction(
|
||||||
@PathParam("loanId") final Long loanId,
|
@PathParam("loanId") final Long loanId,
|
||||||
@QueryParam("command") final String commandParam, final String jsonRequestBody) {
|
@QueryParam("command") final String commandParam,
|
||||||
|
final String jsonRequestBody) {
|
||||||
|
|
||||||
|
final LoanTransactionCommand command = this.apiDataConversionService
|
||||||
|
.convertJsonToLoanTransactionCommand(loanId, jsonRequestBody);
|
||||||
|
|
||||||
final LoanTransactionCommand command = this.apiDataConversionService.convertJsonToLoanTransactionCommand(loanId, jsonRequestBody);
|
|
||||||
|
|
||||||
Response response = null;
|
Response response = null;
|
||||||
|
|
||||||
if (is(commandParam, "repayment")) {
|
if (is(commandParam, "repayment")) {
|
||||||
EntityIdentifier identifier = this.loanWritePlatformService.makeLoanRepayment(command);
|
EntityIdentifier identifier = this.loanWritePlatformService
|
||||||
|
.makeLoanRepayment(command);
|
||||||
response = Response.ok().entity(identifier).build();
|
response = Response.ok().entity(identifier).build();
|
||||||
} else if (is(commandParam, "waiver")) {
|
} else if (is(commandParam, "waiver")) {
|
||||||
EntityIdentifier identifier = this.loanWritePlatformService.waiveLoanAmount(command);
|
EntityIdentifier identifier = this.loanWritePlatformService
|
||||||
|
.waiveLoanAmount(command);
|
||||||
response = Response.ok().entity(identifier).build();
|
response = Response.ok().entity(identifier).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,8 +255,8 @@ public class LoansApiResource {
|
|||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("{loanId}/transactions/template")
|
@Path("{loanId}/transactions/template")
|
||||||
@Consumes({MediaType.APPLICATION_JSON})
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({MediaType.APPLICATION_JSON})
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
public String retrieveNewRepaymentDetails(
|
public String retrieveNewRepaymentDetails(
|
||||||
@PathParam("loanId") final Long loanId,
|
@PathParam("loanId") final Long loanId,
|
||||||
@QueryParam("command") final String commandParam,
|
@QueryParam("command") final String commandParam,
|
||||||
@@ -262,8 +287,8 @@ public class LoansApiResource {
|
|||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("{loanId}/transactions/{transactionId}")
|
@Path("{loanId}/transactions/{transactionId}")
|
||||||
@Consumes({MediaType.APPLICATION_JSON})
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({MediaType.APPLICATION_JSON})
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
public String retrieveTransaction(@PathParam("loanId") final Long loanId,
|
public String retrieveTransaction(@PathParam("loanId") final Long loanId,
|
||||||
@PathParam("transactionId") final Long transactionId,
|
@PathParam("transactionId") final Long transactionId,
|
||||||
@Context final UriInfo uriInfo) {
|
@Context final UriInfo uriInfo) {
|
||||||
@@ -279,15 +304,19 @@ public class LoansApiResource {
|
|||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("{loanId}/transactions/{transactionId}")
|
@Path("{loanId}/transactions/{transactionId}")
|
||||||
@Consumes({MediaType.APPLICATION_JSON})
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({MediaType.APPLICATION_JSON})
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
public Response adjustLoanTransaction(
|
public Response adjustLoanTransaction(
|
||||||
@PathParam("loanId") final Long loanId,
|
@PathParam("loanId") final Long loanId,
|
||||||
@PathParam("transactionId") final Long transactionId, final String jsonRequestBody) {
|
@PathParam("transactionId") final Long transactionId,
|
||||||
|
final String jsonRequestBody) {
|
||||||
|
|
||||||
final AdjustLoanTransactionCommand command = this.apiDataConversionService.convertJsonToAdjustLoanTransactionCommand(loanId, transactionId, jsonRequestBody);
|
final AdjustLoanTransactionCommand command = this.apiDataConversionService
|
||||||
|
.convertJsonToAdjustLoanTransactionCommand(loanId,
|
||||||
EntityIdentifier identifier = this.loanWritePlatformService.adjustLoanTransaction(command);
|
transactionId, jsonRequestBody);
|
||||||
|
|
||||||
|
EntityIdentifier identifier = this.loanWritePlatformService
|
||||||
|
.adjustLoanTransaction(command);
|
||||||
|
|
||||||
return Response.ok().entity(identifier).build();
|
return Response.ok().entity(identifier).build();
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -24,7 +24,8 @@ public interface LoanReadPlatformService {
|
|||||||
Collection<LoanTransactionDatajpw> loanRepayments);
|
Collection<LoanTransactionDatajpw> loanRepayments);
|
||||||
|
|
||||||
LoanPermissionData retrieveLoanPermissions(
|
LoanPermissionData retrieveLoanPermissions(
|
||||||
LoanBasicDetailsData loanBasicDetails, boolean isWaiverAllowed);
|
LoanBasicDetailsData loanBasicDetails, boolean isWaiverAllowed,
|
||||||
|
int repaymentAndWaiveCount);
|
||||||
|
|
||||||
NewLoanData retrieveClientAndProductDetails(Long clientId, Long productId);
|
NewLoanData retrieveClientAndProductDetails(Long clientId, Long productId);
|
||||||
|
|
||||||
|
|||||||
+23
-37
@@ -5,20 +5,17 @@ import java.sql.ResultSet;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosng.platform.api.data.ClientData;
|
import org.mifosng.platform.api.data.ClientData;
|
||||||
import org.mifosng.platform.api.data.CurrencyData;
|
import org.mifosng.platform.api.data.CurrencyData;
|
||||||
import org.mifosng.platform.api.data.EnumOptionData;
|
import org.mifosng.platform.api.data.EnumOptionData;
|
||||||
import org.mifosng.platform.api.data.LoanAccountData;
|
|
||||||
import org.mifosng.platform.api.data.LoanAccountSummaryData;
|
import org.mifosng.platform.api.data.LoanAccountSummaryData;
|
||||||
import org.mifosng.platform.api.data.LoanBasicDetailsData;
|
import org.mifosng.platform.api.data.LoanBasicDetailsData;
|
||||||
import org.mifosng.platform.api.data.LoanPermissionData;
|
import org.mifosng.platform.api.data.LoanPermissionData;
|
||||||
import org.mifosng.platform.api.data.LoanProductData;
|
import org.mifosng.platform.api.data.LoanProductData;
|
||||||
import org.mifosng.platform.api.data.LoanProductLookup;
|
import org.mifosng.platform.api.data.LoanProductLookup;
|
||||||
import org.mifosng.platform.api.data.LoanRepaymentPeriodDatajpw;
|
import org.mifosng.platform.api.data.LoanRepaymentPeriodDatajpw;
|
||||||
import org.mifosng.platform.api.data.LoanRepaymentScheduleData;
|
|
||||||
import org.mifosng.platform.api.data.LoanTransactionData;
|
import org.mifosng.platform.api.data.LoanTransactionData;
|
||||||
import org.mifosng.platform.api.data.LoanTransactionDatajpw;
|
import org.mifosng.platform.api.data.LoanTransactionDatajpw;
|
||||||
import org.mifosng.platform.api.data.MoneyData;
|
import org.mifosng.platform.api.data.MoneyData;
|
||||||
@@ -40,8 +37,6 @@ import org.mifosng.platform.loan.domain.LoanTransactionType;
|
|||||||
import org.mifosng.platform.loanproduct.service.LoanEnumerations;
|
import org.mifosng.platform.loanproduct.service.LoanEnumerations;
|
||||||
import org.mifosng.platform.loanproduct.service.LoanProductReadPlatformService;
|
import org.mifosng.platform.loanproduct.service.LoanProductReadPlatformService;
|
||||||
import org.mifosng.platform.security.PlatformSecurityContext;
|
import org.mifosng.platform.security.PlatformSecurityContext;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.dao.EmptyResultDataAccessException;
|
import org.springframework.dao.EmptyResultDataAccessException;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
@@ -51,9 +46,6 @@ import org.springframework.stereotype.Service;
|
|||||||
@Service
|
@Service
|
||||||
public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||||
|
|
||||||
private final static Logger logger = LoggerFactory
|
|
||||||
.getLogger(LoanReadPlatformServiceImpl.class);
|
|
||||||
|
|
||||||
private final JdbcTemplate jdbcTemplate;
|
private final JdbcTemplate jdbcTemplate;
|
||||||
private final PlatformSecurityContext context;
|
private final PlatformSecurityContext context;
|
||||||
private final LoanRepository loanRepository;
|
private final LoanRepository loanRepository;
|
||||||
@@ -204,39 +196,33 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoanPermissionData retrieveLoanPermissions(
|
public LoanPermissionData retrieveLoanPermissions(
|
||||||
LoanBasicDetailsData loanBasicDetails, boolean isWaiverAllowed) {
|
LoanBasicDetailsData loanBasicDetails, boolean isWaiverAllowed,
|
||||||
/*
|
int repaymentAndWaiveCount) {
|
||||||
boolean waiveAllowed = isWaiverAllowed && (loanBasicDetails.getClosedOnDate() == null);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
boolean undoDisbursalAllowed = isDisbursed()
|
|
||||||
&& isOpenWithNoRepaymentMade();
|
|
||||||
boolean makeRepaymentAllowed = isDisbursed() && isNotClosed();
|
|
||||||
|
|
||||||
boolean rejectAllowed = isNotApproved() && isNotDisbursed()
|
boolean pendingApproval = (loanBasicDetails.getLifeCycleStatusId()
|
||||||
&& isNotClosed();
|
.equals(100));
|
||||||
boolean withdrawnByApplicantAllowed = isNotDisbursed() && isNotClosed();
|
boolean waitingForDisbursal = (loanBasicDetails.getLifeCycleStatusId()
|
||||||
boolean undoApprovalAllowed = isApproved() && isNotClosed();
|
.equals(200));
|
||||||
boolean disbursalAllowed = isApproved() && isNotDisbursed()
|
boolean isActive = (loanBasicDetails.getLifeCycleStatusId().equals(300));
|
||||||
&& isNotClosed();
|
|
||||||
|
|
||||||
return new LoanPermissionData(waiveAllowed,
|
boolean waiveAllowed = isWaiverAllowed && isActive;
|
||||||
makeRepaymentAllowed, rejectAllowed,
|
boolean makeRepaymentAllowed = isActive;
|
||||||
withdrawnByApplicantAllowed, undoApprovalAllowed,
|
|
||||||
undoDisbursalAllowed, disbursalAllowed,
|
|
||||||
isSubmittedAndPendingApproval(), isWaitingForDisbursal());
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
return new LoanPermissionData(true,
|
boolean rejectAllowed = pendingApproval;
|
||||||
true, true,
|
boolean withdrawnByApplicantAllowed = waitingForDisbursal
|
||||||
true, true,
|
|| pendingApproval;
|
||||||
true, true,
|
|
||||||
true, true);
|
boolean undoApprovalAllowed = waitingForDisbursal;
|
||||||
|
|
||||||
|
boolean undoDisbursalAllowed = isActive && (repaymentAndWaiveCount == 0);
|
||||||
|
|
||||||
|
boolean disbursalAllowed = waitingForDisbursal;
|
||||||
|
|
||||||
|
return new LoanPermissionData(waiveAllowed, makeRepaymentAllowed,
|
||||||
|
rejectAllowed, withdrawnByApplicantAllowed,
|
||||||
|
undoApprovalAllowed, undoDisbursalAllowed, disbursalAllowed,
|
||||||
|
pendingApproval, waitingForDisbursal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NewLoanData retrieveClientAndProductDetails(final Long clientId,
|
public NewLoanData retrieveClientAndProductDetails(final Long clientId,
|
||||||
|
|||||||
Reference in New Issue
Block a user