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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.account.command;

import java.io.Serial;
import lombok.Getter;
import lombok.Setter;
import org.apache.fineract.command.core.Command;
import org.apache.fineract.portfolio.account.data.RefundByTransferRequest;

@Getter
@Setter
public class AccountRefundByTransferCommand extends Command<RefundByTransferRequest> {

@Serial
private static final long serialVersionUID = 1L;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.account.command;

import java.io.Serial;
import lombok.Getter;
import lombok.Setter;
import org.apache.fineract.command.core.Command;
import org.apache.fineract.portfolio.account.data.AccountTransferRequest;

@Getter
@Setter
public class AccountTransferCreateCommand extends Command<AccountTransferRequest> {

@Serial
private static final long serialVersionUID = 1L;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.account.command;

import java.io.Serial;
import lombok.Getter;
import lombok.Setter;
import org.apache.fineract.command.core.Command;
import org.apache.fineract.portfolio.account.data.StandingInstructionCreateRequest;

@Getter
@Setter
public class StandingInstructionCreateCommand extends Command<StandingInstructionCreateRequest> {

@Serial
private static final long serialVersionUID = 1L;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.apache.fineract.portfolio.account.command;

import java.io.Serial;
import lombok.Getter;
import lombok.Setter;
import org.apache.fineract.command.core.Command;
import org.apache.fineract.portfolio.account.data.StandingInstructionUpdateRequest;

@Getter
@Setter
public class StandingInstructionUpdateCommand extends Command<StandingInstructionUpdateRequest> {

@Serial
private static final long serialVersionUID = 1L;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.account.data;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import java.io.Serial;
import java.io.Serializable;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.fineract.validation.constraints.Locale;

@Getter
@Setter
@NoArgsConstructor
public class AccountTransferRequest implements Serializable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use @LocalDate(dateField = "transferDate", formatField = "dateFormat", localeField = "locale") to validate the incoming date

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is happening is that in the service logic the date formatter is recomputed and the passed as a parameter. e.g. public class AccountTransfersWritePlatformServiceImpl the formatter is recomputed "final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName);
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName);

    final Locale locale = command.extractLocale();
    final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);

" if we standardize the above then this calculation need not be done (as parameter final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt,
transactionDate, transactionAmount, paymentDetail, transactionBooleanValues, backdatedTxnsAllowedTill);) there are other methods also?

Copy link
Contributor

@adamsaghy adamsaghy Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this request dto is for API layer (incoming request via API call) or is this DTO for service layer?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NewCommandProcessingInfrastructure is harmonizing the flow of request objects from API->Service->Repository and back as response objects, this DTO will take care of all of the above.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The entity will be a separate decoupling object which will be mapped via mapstruct.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"The API DTO to Service DTO is the same object,"-> But it SHOULD NOT! Service layer DTO and API layer DTO many situations has different datatype (see "date" of BusinessDateDTO and "date" of BusinessDateUpdateRequest), and ALWAYS has different annotations! For example @LocalDate(dateField = "date", formatField = "dateFormat", localeField = "locale") is there on BusinessDateUpdateRequest to enforce bean validation, which is not applicable for BusinessDateUpdateRequest.

I hope it helps to understand better why i am saying API layer DTOs (request and response) MUST BE different than service layer DTO. These DTOs are for different purposes with different configuration! They should not be mixed!

I think this is where the importance of typesafety comes in, as we are not dealing with JsonStrings which are then parsed and validated at a later stage. The request and response objects the filtering is happening right before the API logic is executed. Jakarta validation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kapilpanchal123 Can you stop replying nonsense, AI generated BS?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice :).

Copy link
Contributor

@adamsaghy adamsaghy Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not nice....

I think this is where the importance of typesafety comes in, as we are not dealing with JsonStrings which are then parsed and validated at a later stage. The request and response objects the filtering is happening right before the API logic is executed. Jakarta validation. -> Not one of these sentences has any meaning...

I have asked you to take a look at BusinessDateUpdateRequest and BusinessDateDTO where you will see why (correctly) the two DTOs are different! They have different purposes and different annotations.

BusinessDateDTO is used at service layer, all fields are using the relevant internal types (type is BusinessDateType). No Bean validation on it. No @JsonLocalDateArrayFormat to format the LocalDate field before serialization. (used in BusinessDateUpdateResponse)
BusinessDateUpdateRequest is used at API layer, all fields are using simple data type (like String), including date and type. Bean Validation are enforced on this DTO and its values (see number of Bean validation annotations).

There are number of difference between the DTOs: data types, fields and annotations wise.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the disjoint was in this Data Architecture, my earlier statement DTO -> Mapper -> Entity should be Data -> Mapper -> Service Objects -> Mapper -> Entity.

[ Client JSON Request ]

(DTO Layer)
──────────────────────
LoanProductRequestDTO

(Mapper / Converter Layer)
──────────────────────
EnumOptionData, Enumerations, Mappers, Assemblers

(Entity / Domain Layer)
──────────────────────
LoanProduct, Client, Account

(Repository / Persistence)

I used to think that the DTO is directly mapped to the Entity using a Mapper like mapstruct. But we are using a lot of enums in the application, so there has to be decoupling between the client facing DTO's, Service layer objects and then the entity or repository layer objects?


@Serial
private static final long serialVersionUID = 1L;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.transfer.description.not.blank}")
@Size(max = 200, message = "{org.apache.fineract.portfolio.account.data.transfer.description.size}")
private String transferDescription;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.to.office.id.not.blank}")
private String toOfficeId;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.to.account.type.not.blank}")
private String toAccountType;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.date.format.not.blank}")
private String dateFormat;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.transfer.amount.not.blank}")
private String transferAmount;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.to.account.id.not.blank}")
private String toAccountId;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.from.client.id.not.blank}")
private String fromClientId;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.locale.not.blank}")
@Size(max = 50, message = "{org.apache.fineract.portfolio.account.data.locale.size}")
@Locale
private String locale;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.transfer.date.not.blank}")
private String transferDate;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.from.account.type.not.blank}")
private String fromAccountType;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.to.client.id.not.blank}")
private String toClientId;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.from.account.id.not.blank}")
private String fromAccountId;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.from.office.id.not.blank}")
private String fromOfficeId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.account.data;

import java.io.Serial;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AccountTransferResponse implements Serializable {

@Serial
private static final long serialVersionUID = 1L;

private Long savingsId;
private Long loanId;
private Long resourceId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.account.data;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import java.io.Serial;
import java.io.Serializable;
import org.apache.fineract.validation.constraints.Locale;

public class RefundByTransferRequest implements Serializable {

@Serial
private static final long serialVersionUID = 1L;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.transfer.description.not.blank}")
@Size(max = 200, message = "{org.apache.fineract.portfolio.account.data.transfer.description.size}")
private String transferDescription;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.to.office.id.not.blank}")
private String toOfficeId;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.to.account.type.not.blank}")
private String toAccountType;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.date.format.not.blank}")
private String dateFormat;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.transfer.amount.not.blank}")
private String transferAmount;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.to.account.id.not.blank}")
private String toAccountId;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.from.client.id.not.blank}")
private String fromClientId;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.locale.not.blank}")
@Size(max = 50, message = "{org.apache.fineract.portfolio.account.data.locale.size}")
@Locale
private String locale;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.transfer.date.not.blank}")
private String transferDate;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.from.account.type.not.blank}")
private String fromAccountType;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.to.client.id.not.blank}")
private String toClientId;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.from.account.id.not.blank}")
private String fromAccountId;

@NotBlank(message = "{org.apache.fineract.portfolio.account.data.from.office.id.not.blank}")
private String fromOfficeId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.account.data;

import java.io.Serial;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class RefundByTransferResponse implements Serializable {

@Serial
private static final long serialVersionUID = 1L;

private Long savingsId;
private Long resourceId;
}
Loading