package sample.model.asset;
import java.math.BigDecimal;
import java.time.*;
import java.util.List;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import lombok.*;
import sample.ActionStatusType;
import sample.ValidationException.ErrorKeys;
import sample.context.Dto;
import sample.context.orm.*;
import sample.model.asset.type.CashflowType;
import sample.model.constraints.*;
import sample.util.*;
/**
* 入出金キャッシュフローを表現します。
* キャッシュフローは振込/振替といったキャッシュフローアクションから生成される確定状態(依頼取消等の無い)の入出金情報です。
* low: 概念を伝えるだけなので必要最低限の項目で表現しています。
* low: 検索関連は主に経理確認や帳票等での利用を想定します
*/
@Entity
@Data
@EqualsAndHashCode(callSuper = false)
public class Cashflow extends OrmActiveMetaRecord<Cashflow> {
private static final long serialVersionUID = 1L;
/** ID */
@Id
@GeneratedValue
private Long id;
/** 口座ID */
@IdStr
private String accountId;
/** 通貨 */
@Currency
private String currency;
/** 金額 */
@Amount
private BigDecimal amount;
/** 入出金 */
@NotNull
@Enumerated(EnumType.STRING)
private CashflowType cashflowType;
/** 摘要 */
@Category
private String remark;
/** 発生日/日時 */
@ISODate
private LocalDate eventDay;
@ISODateTime
private LocalDateTime eventDate;
/** 受渡日 */
@ISODate
private LocalDate valueDay;
/** 処理種別 */
@NotNull
@Enumerated(EnumType.STRING)
private ActionStatusType statusType;
/** 登録日時 */
@ISODateTime
private LocalDateTime createDate;
/** 登録者ID */
@IdStr
private String createId;
/** 更新日時 */
@ISODateTime
private LocalDateTime updateDate;
/** 更新者ID */
@IdStr
private String updateId;
/** キャッシュフローを処理済みにして残高へ反映します。 */
public Cashflow realize(final OrmRepository rep) {
validate((v) -> {
v.verify(canRealize(rep), AssetErrorKeys.CashflowRealizeDay);
v.verify(statusType.isUnprocessing(), ErrorKeys.ActionUnprocessing); // 「既に処理中/処理済です」
});
setStatusType(ActionStatusType.Processed);
update(rep);
CashBalance.getOrNew(rep, accountId, currency).add(rep, amount);
return this;
}
/**
* キャッシュフローをエラー状態にします。
* <p>処理中に失敗した際に呼び出してください。
* low: 実際はエラー事由などを引数に取って保持する
*/
public Cashflow error(final OrmRepository rep) {
validate((v) -> v.verify(statusType.isUnprocessed(), ErrorKeys.ActionUnprocessing));
setStatusType(ActionStatusType.Error);
return update(rep);
}
/** キャッシュフローを実現(受渡)可能か判定します。 */
public boolean canRealize(final OrmRepository rep) {
return rep.dh().time().tp().afterEqualsDay(valueDay);
}
/** キャッシュフローを取得します。(例外付) */
public static Cashflow load(final OrmRepository rep, Long id) {
return rep.load(Cashflow.class, id);
}
/**
* 指定受渡日時点で未実現のキャッシュフロー一覧を検索します。(口座通貨別)
*/
public static List<Cashflow> findUnrealize(final OrmRepository rep, String accountId, String currency,
LocalDate valueDay) {
return rep.tmpl().find(
"from Cashflow c where c.accountId=?1 and c.currency=?2 and c.valueDay<=?3 and c.statusType in ?4 order by c.id",
accountId, currency, valueDay, ActionStatusType.unprocessingTypes);
}
/**
* 指定受渡日で実現対象となるキャッシュフロー一覧を検索します。
*/
public static List<Cashflow> findDoRealize(final OrmRepository rep, LocalDate valueDay) {
return rep.tmpl().find("from Cashflow c where c.valueDay=?1 and c.statusType in ?2 order by c.id", valueDay,
ActionStatusType.unprocessedTypes);
}
/**
* キャッシュフローを登録します。
* 受渡日を迎えていた時はそのまま残高へ反映します。
*/
public static Cashflow register(final OrmRepository rep, final RegCashflow p) {
TimePoint now = rep.dh().time().tp();
Validator.validate((v) -> v.checkField(now.beforeEqualsDay(p.getValueDay()),
"valueDay", AssetErrorKeys.CashflowBeforeEqualsDay));
Cashflow cf = p.create(now).save(rep);
return cf.canRealize(rep) ? cf.realize(rep) : cf;
}
/** 入出金キャッシュフローの登録パラメタ。 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class RegCashflow implements Dto {
private static final long serialVersionUID = 1L;
@IdStr
private String accountId;
@Currency
private String currency;
@Amount
private BigDecimal amount;
@NotNull
private CashflowType cashflowType;
@Category
private String remark;
/** 未設定時は営業日を設定 */
@ISODateEmpty
private LocalDate eventDay;
@ISODate
private LocalDate valueDay;
public Cashflow create(final TimePoint now) {
TimePoint eventDate = eventDay == null ? now : new TimePoint(eventDay, now.getDate());
Cashflow m = new Cashflow();
m.setAccountId(accountId);
m.setCurrency(currency);
m.setAmount(amount);
m.setCashflowType(cashflowType);
m.setRemark(remark);
m.setEventDay(eventDate.getDay());
m.setEventDate(eventDate.getDate());
m.setValueDay(valueDay);
m.setStatusType(ActionStatusType.Unprocessed);
return m;
}
}
}