blob: 1410c5f99a1e084fab5a9d5a308a0527a6041b7e [file] [log] [blame]
/**
* 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.client.domain;
import java.math.BigDecimal;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.office.domain.OrganisationCurrency;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
import org.apache.fineract.portfolio.client.api.ClientApiConstants;
import org.joda.time.LocalDate;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_client_charge")
public class ClientCharge extends AbstractPersistable<Long> {
@ManyToOne(optional = false)
@JoinColumn(name = "client_id", referencedColumnName = "id", nullable = false)
private Client client;
@ManyToOne(optional = false)
@JoinColumn(name = "charge_id", referencedColumnName = "id", nullable = false)
private Charge charge;
@Column(name = "charge_time_enum", nullable = false)
private Integer chargeTime;
@Temporal(TemporalType.DATE)
@Column(name = "charge_due_date")
private Date dueDate;
@Column(name = "charge_calculation_enum")
private Integer chargeCalculation;
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount;
@Column(name = "amount_paid_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal amountPaid;
@Column(name = "amount_waived_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal amountWaived;
@Column(name = "amount_writtenoff_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal amountWrittenOff;
@Column(name = "amount_outstanding_derived", scale = 6, precision = 19, nullable = false)
private BigDecimal amountOutstanding;
@Column(name = "is_penalty", nullable = false)
private boolean penaltyCharge = false;
@Column(name = "is_paid_derived", nullable = false)
private boolean paid = false;
@Column(name = "waived", nullable = false)
private boolean waived = false;
@Column(name = "is_active", nullable = false)
private boolean status = true;
@Temporal(TemporalType.DATE)
@Column(name = "inactivated_on_date")
private Date inactivationDate;
@Transient
private OrganisationCurrency currency;
protected ClientCharge() {
//
}
public static ClientCharge createNew(final Client client, final Charge charge, final JsonCommand command) {
BigDecimal amount = command.bigDecimalValueOfParameterNamed(ClientApiConstants.amountParamName);
final LocalDate dueDate = command.localDateValueOfParameterNamed(ClientApiConstants.dueAsOfDateParamName);
final boolean status = true;
// Derive from charge definition if not passed in as a parameter
amount = (amount == null) ? charge.getAmount() : amount;
return new ClientCharge(client, charge, amount, dueDate, status);
}
private ClientCharge(final Client client, final Charge charge, final BigDecimal amount, final LocalDate dueDate, final boolean status) {
this.client = client;
this.charge = charge;
this.penaltyCharge = charge.isPenalty();
this.chargeTime = charge.getChargeTimeType();
this.dueDate = (dueDate == null) ? null : dueDate.toDate();
this.chargeCalculation = charge.getChargeCalculation();
BigDecimal chargeAmount = charge.getAmount();
if (amount != null) {
chargeAmount = amount;
}
populateDerivedFields(chargeAmount);
this.paid = determineIfFullyPaid();
this.status = status;
}
public Money pay(final Money amountPaid) {
Money amountPaidToDate = Money.of(this.getCurrency(), this.amountPaid);
Money amountOutstanding = Money.of(this.getCurrency(), this.amountOutstanding);
amountPaidToDate = amountPaidToDate.plus(amountPaid);
amountOutstanding = amountOutstanding.minus(amountPaid);
this.amountPaid = amountPaidToDate.getAmount();
this.amountOutstanding = amountOutstanding.getAmount();
this.paid = determineIfFullyPaid();
return Money.of(this.getCurrency(), this.amountOutstanding);
}
public void undoPayment(final Money transactionAmount) {
Money amountPaid = getAmountPaid();
amountPaid = amountPaid.minus(transactionAmount);
this.amountPaid = amountPaid.getAmount();
this.amountOutstanding = calculateOutstanding();
this.paid = false;
this.status = true;
}
public Money waive() {
Money amountWaivedToDate = getAmountWaived();
Money amountOutstanding = getAmountOutstanding();
Money totalAmountWaived = amountWaivedToDate.plus(amountOutstanding);
this.amountWaived = totalAmountWaived.getAmount();
this.amountOutstanding = BigDecimal.ZERO;
this.waived = true;
return totalAmountWaived;
}
public void undoWaiver(final Money transactionAmount) {
Money amountWaived = getAmountWaived();
amountWaived = amountWaived.minus(transactionAmount);
this.amountWaived = amountWaived.getAmount();
this.amountOutstanding = calculateOutstanding();
this.waived = false;
this.status = true;
}
private void populateDerivedFields(final BigDecimal amount) {
switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
case INVALID:
this.amount = null;
this.amountPaid = null;
this.amountOutstanding = BigDecimal.ZERO;
this.amountWaived = null;
this.amountWrittenOff = null;
break;
case FLAT:
this.amount = amount;
this.amountPaid = null;
this.amountOutstanding = amount;
this.amountWaived = null;
this.amountWrittenOff = null;
break;
default:
break;
}
}
public boolean isOnSpecifiedDueDate() {
return ChargeTimeType.fromInt(this.chargeTime).isOnSpecifiedDueDate();
}
private boolean determineIfFullyPaid() {
return BigDecimal.ZERO.compareTo(calculateOutstanding()) == 0;
}
private BigDecimal calculateOutstanding() {
BigDecimal amountPaidLocal = BigDecimal.ZERO;
if (this.amountPaid != null) {
amountPaidLocal = this.amountPaid;
}
BigDecimal amountWaivedLocal = BigDecimal.ZERO;
if (this.amountWaived != null) {
amountWaivedLocal = this.amountWaived;
}
BigDecimal amountWrittenOffLocal = BigDecimal.ZERO;
if (this.amountWrittenOff != null) {
amountWrittenOffLocal = this.amountWrittenOff;
}
final BigDecimal totalAccountedFor = amountPaidLocal.add(amountWaivedLocal).add(amountWrittenOffLocal);
return this.amount.subtract(totalAccountedFor);
}
public LocalDate getDueLocalDate() {
LocalDate dueDate = null;
if (this.dueDate != null) {
dueDate = new LocalDate(this.dueDate);
}
return dueDate;
}
public Client getClient() {
return this.client;
}
public Charge getCharge() {
return this.charge;
}
public Integer getChargeTime() {
return this.chargeTime;
}
public Date getDueDate() {
return this.dueDate;
}
public Integer getChargeCalculation() {
return this.chargeCalculation;
}
public boolean isPenaltyCharge() {
return this.penaltyCharge;
}
public boolean isPaid() {
return this.paid;
}
public boolean isWaived() {
return this.waived;
}
public boolean isActive() {
return this.status;
}
public boolean isNotActive() {
return !this.status;
}
public Date getInactivationDate() {
return this.inactivationDate;
}
public Long getClientId() {
return client.getId();
}
public Long getOfficeId() {
return this.client.getOffice().getId();
}
public void setCurrency(OrganisationCurrency currency) {
this.currency = currency;
}
public MonetaryCurrency getCurrency() {
return this.currency.toMonetaryCurrency();
}
public boolean isPaidOrPartiallyPaid(final MonetaryCurrency currency) {
final Money amountWaivedOrWrittenOff = getAmountWaived().plus(getAmountWrittenOff());
return Money.of(currency, this.amountPaid).plus(amountWaivedOrWrittenOff).isGreaterThanZero();
}
public Money getAmount() {
return Money.of(getCurrency(), this.amount);
}
public Money getAmountPaid() {
return Money.of(getCurrency(), this.amountPaid);
}
public Money getAmountWaived() {
return Money.of(getCurrency(), this.amountWaived);
}
public Money getAmountWrittenOff() {
return Money.of(getCurrency(), this.amountWrittenOff);
}
public Money getAmountOutstanding() {
return Money.of(getCurrency(), this.amountOutstanding);
}
}