/* * jPOS Project [http://jpos.org] * Copyright (C) 2000-2017 jPOS Software SRL * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.jpos.gl; import java.util.Date; import java.util.Set; import java.io.Serializable; import java.text.ParseException; import org.jdom2.Element; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.jpos.util.Tags; /** * Base class for Composite and Final accounts. * * @see GLEntry * @see CompositeAccount * @see FinalAccount * * @author <a href="mailto:apr@jpos.org">Alejandro Revilla</a> */ public abstract class Account implements Serializable, Comparable, Cloneable { private long id; /** * 0 - type is undefined (used in top level [chart-of=]account) */ public static final int CHART = 0; /** * 1 - this is a debit type account */ public static final int DEBIT = 1; /** * 2 - this is a credit type account */ public static final int CREDIT = 2; String code; String description; Date created; Date expiration; String currencyCode; int type; Tags tags; CompositeAccount parent, root; /** * Default constructor. */ public Account () { super (); } /** * Internal id. * @return internal id */ public long getId() { return id; } /** * Internal id. * @param id internal Id */ @SuppressWarnings("unused") public void setId (long id) { this.id = id; } /** * Account creation date. * @param created creation date */ public void setCreated (Date created) { this.created = created; } /** * Account creation date. * @return creation date */ public Date getCreated () { return created; } /** * Expiration date. * @param expiration expiration date. */ public void setExpiration (Date expiration) { this.expiration = expiration; } /** * Expiration date. * @return expiration date. */ public Date getExpiration () { return expiration; } /** * Account code. * * We use an account id with no business meaning, so we need * an account <code>code</code> * * @param code the code. */ public void setCode (String code) { this.code = code; } /** * Account code. * @return account code. */ public String getCode() { return code; } /** * Description. * @param description account description. */ public void setDescription (String description) { this.description = description; } /** * Description. * @return account description. */ public String getDescription () { return description; } public Tags getTags() { return tags; } public void setTags(Tags tags) { this.tags = tags; } /** * Parent Account. * * @return parent account (null if this account is the toplevel root) */ public CompositeAccount getParent () { return parent; } /** * Parent Account. * @param parent parent account (null if this account is the toplevel root) */ public void setParent (CompositeAccount parent) { this.parent = parent; } /** * Root account. * The root account is a top level {@link CompositeAccount} * representing a Chart of Accounts. * @return root account. */ public CompositeAccount getRoot () { return root; } /** * Root account. * * @param root root account. */ public void setRoot (CompositeAccount root) { this.root = root; } /** * Account's children. * {@link CompositeAccount} uses this property to store its children. * Calling this method on a {@link FinalAccount} raises * an IllegalArgumentException. * @param children a list of childrens */ @SuppressWarnings("unused") public abstract void setChildren (Set<Account> children); /** * Account's children. * {@link CompositeAccount} uses this property to store its children. * Calling this method on a {@link FinalAccount} would return null * an IllegalArgumentException. * @return children or null. */ public abstract Set<Account> getChildren (); /** * Creates a JDOM Element as defined in * <a href="http://jpos.org/minigl.dtd">minigl.dtd</a> * @return Account converted to XML */ public abstract Element toXML (); /** * Currency Code. * @return currency code. */ public String getCurrencyCode () { return currencyCode; } /** * Currency Code. * @param currencyCode a currency code. * @see Currency * @throws IllegalArgumentException if currency code doesn't match the parent's one */ public void setCurrencyCode (String currencyCode) throws IllegalArgumentException { if (currencyCode != null) { // go up until first level, don't process the chart itself for (Account p = getParent(); p != null && p.getParent() != null; p = p.getParent()) { String pcc = p.getCurrencyCode(); if (pcc != null && !pcc.equals (currencyCode)) throw new IllegalArgumentException ( "Illegal currency "+currencyCode); } } this.currencyCode = currencyCode; } /** * Type. * @param type (either DEBIT, CREDIT or CHART) */ public void setType (int type) { this.type = type; } /** * Type. * @return type (either DEBIT, CREDIT or CHART) */ public int getType () { return type; } /** * @return true if account's type is a debit. */ public boolean isDebit () { return (type & DEBIT) == DEBIT; } /** * @return true if account's type is a credit. */ public boolean isCredit () { return (type & CREDIT) == CREDIT; } /** * @return true if account's type is chart (neither DEBIT nor CREDIT) */ public boolean isChart () { return (type & (DEBIT|CREDIT)) == CHART; } public boolean isFinalAccount () { return false; } public boolean isCompositeAccount () { return !isFinalAccount(); } /** * Account's type * @return "debit", "credit" or null. */ public String getTypeAsString() { if (isDebit()) return "debit"; else if (isCredit()) return "credit"; else return null; } public boolean isAncestor(CompositeAccount ancestor) { for (Account p = getParent(); p != null; p = p.getParent()) { if (p.equals(ancestor)) return true; } return false; } /** * Helper method used to create a JDOM Element as defined in * <a href="http://jpos.org/minigl.dtd">minigl.dtd</a> * * @param elem the Element to be populated * @return populated element */ protected Element toXML (Element elem) { elem.setAttribute ("code", getCode ()); elem.addContent (new Element("description").setText (getDescription())); if (getCurrencyCode() != null) elem.setAttribute ("currency", getCurrencyCode()); if (getTags () != null) { Element tags = new Element ("tags").setText (getTags().toString()); elem.addContent (tags); } if (isDebit ()) elem.setAttribute ("type", "debit"); else if (isCredit()) elem.setAttribute ("type", "credit"); Util.setDateAttribute (elem, "created", getCreated()); return elem; } /** * Parses a JDOM Element as defined in * <a href="http://jpos.org/minigl.dtd">minigl.dtd</a> * @param elem the element * @throws ParseException on XML error */ public void fromXML (Element elem) throws ParseException { setCode (elem.getAttributeValue ("code")); setDescription (elem.getChildTextTrim ("description")); setCreated (Util.parseDate (elem.getAttributeValue ("created"))); if (getCreated() == null) setCreated (new Date()); setExpiration ( Util.parseDate (elem.getAttributeValue ("expiration")) ); String cc = elem.getAttributeValue ("currency"); if (cc != null) setCurrencyCode (cc); String at = elem.getAttributeValue ("type"); if (at != null) { if ("debit".equals (at)) setType (DEBIT); else if ("credit".equals (at)) setType (CREDIT); else throw new IllegalArgumentException ("Invalid type "+at); } else { setType (0); } setTags (new Tags(elem.getChildTextTrim ("tags"))); Account p = getParent(); if (p != null) { int parentType = p.getType(); if (parentType > 0 && parentType != getType()) { throw new IllegalArgumentException ( "type '" + getTypeAsString() + "' doesn't match parent's type '" + p.getTypeAsString() + "'" + " in account " + getCode() ); } } } public String toString() { return new ToStringBuilder(this) .append("id", getId()) .append("code",getCode()) .toString(); } public boolean equals(Object other) { if ( !(other instanceof Account) ) return false; Account castOther = (Account) other; return new EqualsBuilder() .append(this.getId(), castOther.getId()) .isEquals(); } /** * @param other account * @return true if other has same type as this account */ public boolean equalsType (Account other) { return ( getType() & (CREDIT | DEBIT)) == (other.getType() & (CREDIT | DEBIT)); } public int hashCode() { return new HashCodeBuilder() .append(getId()) .toHashCode(); } public int compareTo (Object o) { if (o instanceof Account) { Account other = (Account) o; if (getCode() == null || other.getCode() == null) { return getCode() == other.getCode() ? 0 : 1; } else if (getCode() == null) { return 1; } return getCode().compareTo (((Account)o).getCode()); } return 1; } public Object clone() throws CloneNotSupportedException { return super.clone(); } }