/* This file is part of Cyclos (www.cyclos.org). A project of the Social Trade Organisation (www.socialtrade.org). Cyclos is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Cyclos is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Cyclos; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package nl.strohalm.cyclos.entities.accounts.fees.account; import java.math.BigDecimal; import java.util.Calendar; import java.util.Collection; import java.util.Map; import nl.strohalm.cyclos.entities.Entity; import nl.strohalm.cyclos.entities.Relationship; import nl.strohalm.cyclos.entities.accounts.MemberAccountType; import nl.strohalm.cyclos.entities.accounts.transactions.TransferType; import nl.strohalm.cyclos.entities.groups.MemberGroup; import nl.strohalm.cyclos.entities.settings.LocalSettings; import nl.strohalm.cyclos.utils.Amount; import nl.strohalm.cyclos.utils.DateHelper; import nl.strohalm.cyclos.utils.Period; import nl.strohalm.cyclos.utils.StringValuedEnum; import nl.strohalm.cyclos.utils.TimePeriod; import nl.strohalm.cyclos.utils.WeekDay; /** * Taxes are charged over all accounts of a given type * @author luis */ public class AccountFee extends Entity { public enum ChargeMode implements StringValuedEnum { FIXED("FA", Amount.Type.FIXED), VOLUME_PERCENTAGE("VP", Amount.Type.PERCENTAGE), NEGATIVE_VOLUME_PERCENTAGE("NV", Amount.Type.PERCENTAGE), BALANCE_PERCENTAGE("BP", Amount.Type.PERCENTAGE), NEGATIVE_BALANCE_PERCENTAGE("NB", Amount.Type.PERCENTAGE); private final String value; private final Amount.Type amountType; private ChargeMode(final String value, final Amount.Type amountType) { this.value = value; this.amountType = amountType; } public Amount.Type getAmountType() { return amountType; } @Override public String getValue() { return value; } public boolean isBalance() { return this == BALANCE_PERCENTAGE || this == NEGATIVE_BALANCE_PERCENTAGE; } public boolean isFixed() { return this == FIXED; } public boolean isNegative() { return this == NEGATIVE_BALANCE_PERCENTAGE || this == NEGATIVE_VOLUME_PERCENTAGE; } public boolean isVolume() { return this == VOLUME_PERCENTAGE || this == NEGATIVE_VOLUME_PERCENTAGE; } } public enum InvoiceMode implements StringValuedEnum { NOT_ENOUGH_CREDITS("C"), NEVER("N"), ALWAYS("A"); private final String value; private InvoiceMode(final String value) { this.value = value; } @Override public String getValue() { return value; } } public enum PaymentDirection implements StringValuedEnum { TO_SYSTEM("S"), TO_MEMBER("M"); private final String value; private PaymentDirection(final String value) { this.value = value; } @Override public String getValue() { return value; } } public static enum Relationships implements Relationship { ACCOUNT_TYPE("accountType"), GROUPS("groups"), LOGS("logs"), TRANSFER_TYPE("transferType"); private final String name; private Relationships(final String name) { this.name = name; } @Override public String getName() { return name; } } public enum RunMode implements StringValuedEnum { MANUAL("M"), SCHEDULED("S"); private final String value; private RunMode(final String value) { this.value = value; } @Override public String getValue() { return value; } } private static final long serialVersionUID = -3296543930198792794L; private MemberAccountType accountType; private String name; private String description; private ChargeMode chargeMode; private boolean enabled; private Calendar enabledSince; private RunMode runMode; private TimePeriod recurrence; private Byte day; private Byte hour; private InvoiceMode invoiceMode; private PaymentDirection paymentDirection; private BigDecimal amount; private BigDecimal freeBase; private TransferType transferType; private Collection<MemberGroup> groups; private Collection<AccountFeeLog> logs; public MemberAccountType getAccountType() { return accountType; } public BigDecimal getAmount() { return amount; } public Amount getAmountValue() { final Amount amount = new Amount(); amount.setType(chargeMode.getAmountType()); amount.setValue(this.amount); return amount; } public ChargeMode getChargeMode() { return chargeMode; } public Byte getDay() { return day; } public String getDescription() { return description; } public Calendar getEnabledSince() { return enabledSince; } public BigDecimal getFreeBase() { return freeBase; } public Collection<MemberGroup> getGroups() { return groups; } public Byte getHour() { return hour; } public InvoiceMode getInvoiceMode() { return invoiceMode; } public AccountFeeLog getLastExecution() { // Logs are expected to be order by date descending return logs == null || logs.isEmpty() ? null : logs.iterator().next(); } public Collection<AccountFeeLog> getLogs() { return logs; } @Override public String getName() { return name; } public Calendar getNextExecutionDate() { if (runMode != RunMode.SCHEDULED || enabledSince == null) { return null; } // Resolve the period Period period; final AccountFeeLog lastLog = getLastExecution(); if (lastLog == null) { // The account fee has never ran. Get the full period which contains the enabled since date period = recurrence.currentPeriod(enabledSince); } else { // Get the next period, counting from the last execution final Calendar begin = DateHelper.truncateNextDay(lastLog.getPeriod().getEnd()); period = recurrence.periodStartingAt(begin); // If the fee was re-enabled long after the last execution, use the enabledSince instead if (enabledSince.after(period.getEnd())) { period = recurrence.currentPeriod(enabledSince); } } Calendar executionDate = DateHelper.truncateNextDay(period.getEnd()); if (enabledSince.after(executionDate)) { executionDate = DateHelper.truncateNextDay(recurrence.currentPeriod(enabledSince).getEnd()); } switch (recurrence.getField()) { case WEEKS: while (executionDate.get(Calendar.DAY_OF_WEEK) != day) { executionDate.add(Calendar.DAY_OF_MONTH, 1); } break; case MONTHS: executionDate.set(Calendar.DAY_OF_MONTH, day); } if (hour != null) { executionDate.set(Calendar.HOUR_OF_DAY, hour); } return executionDate; } public PaymentDirection getPaymentDirection() { return paymentDirection; } public TimePeriod getRecurrence() { return recurrence; } public RunMode getRunMode() { return runMode; } public TransferType getTransferType() { return transferType; } public WeekDay getWeekDay() { if (recurrence != null && recurrence.getField() == TimePeriod.Field.WEEKS) { return WeekDay.valueOf(day); } return null; } public boolean isEnabled() { return enabled; } public boolean isManual() { return runMode == RunMode.MANUAL; } public boolean isMemberToSystem() { return paymentDirection == PaymentDirection.TO_SYSTEM; } public void setAccountType(final MemberAccountType accountType) { this.accountType = accountType; } public void setAmount(final BigDecimal value) { amount = value; } public void setChargeMode(final ChargeMode chargeMode) { this.chargeMode = chargeMode; } public void setDay(final Byte day) { this.day = day; } public void setDescription(final String description) { this.description = description; } public void setEnabled(final boolean enabled) { this.enabled = enabled; } public void setEnabledSince(final Calendar enabledSince) { this.enabledSince = enabledSince; } public void setFreeBase(final BigDecimal freeBase) { this.freeBase = freeBase; } public void setGroups(final Collection<MemberGroup> groups) { this.groups = groups; } public void setHour(final Byte hour) { this.hour = hour; } public void setInvoiceMode(final InvoiceMode invoiceMode) { this.invoiceMode = invoiceMode; } public void setLogs(final Collection<AccountFeeLog> logs) { this.logs = logs; } public void setName(final String name) { this.name = name; } public void setPaymentDirection(final PaymentDirection paymentDirection) { this.paymentDirection = paymentDirection; } public void setRecurrence(final TimePeriod recurrence) { this.recurrence = recurrence; } public void setRunMode(final RunMode type) { runMode = type; } public void setTransferType(final TransferType transferType) { this.transferType = transferType; } @Override public String toString() { return getId() + " - " + name; } @Override protected void appendVariableValues(final Map<String, Object> variables, final LocalSettings localSettings) { variables.put("account_fee", name); } }