+ * if signSensitive == true then
+ * if amt >= 0
+ * account_DR DR amt
+ * account_CR CR amt
+ * if amt < 0
+ * account_CR DR -amt
+ * account_DR CR -amt
+ * if signSensitive == false then:
+ * account_DR DR amt
+ * account_CR CR -amt
+ * (same as when signSensitive==true and amt>=0)
+ *
+ * Note:
+ *
+ *
Operation index is automatically incremented
+ *
+ * @param docLine Document line or null
+ * @param account_DR DR account
+ * @param account_CR CR account
+ * @param C_Currency_ID Currency
+ * @param Amt amount
+ * @param signSensitive if true, the DR and CR account will switch when amount is negative
+ * @return resulting two fact lines
+ * @category arhipac
+ */
+ public static FactLine[] createSimpleOperation (
+ Fact fact,
+ DocLine docLine,
+ MAccount account_DR, MAccount account_CR,
+ int C_Currency_ID, BigDecimal amt, boolean signSensitive)
+ {
+ FactLine[] lines = new FactLine[2];
+ //newTrxIndex();
+ if (signSensitive) {
+ lines[0] = fact.createLine(docLine, account_DR, C_Currency_ID, amt);
+ lines[1] = fact.createLine(docLine, account_CR, C_Currency_ID, amt.negate());
+ }
+ else {
+ lines[0] = fact.createLine(docLine, account_DR, C_Currency_ID, amt, null);
+ lines[1] = fact.createLine(docLine, account_CR, C_Currency_ID, null, amt);
+ }
+ //newTrxIndex();
+ //
+ return lines;
+ } // createLine
+}
diff --git a/org.adempiere.base/src/org/compiere/model/MAssetClass.java b/org.adempiere.base/src/org/compiere/model/MAssetClass.java
new file mode 100644
index 0000000000..5efa563129
--- /dev/null
+++ b/org.adempiere.base/src/org/compiere/model/MAssetClass.java
@@ -0,0 +1,210 @@
+package org.compiere.model;
+
+import java.sql.ResultSet;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Properties;
+
+import org.compiere.model.Query;
+import org.compiere.util.CCache;
+import org.compiere.util.CLogMgt;
+import org.compiere.util.Env;
+import org.compiere.util.TimeUtil;
+import org.idempiere.fa.feature.UseLifeImpl;
+
+/** Asset Class
+ * @author Teo Sarca, SC Arhipac SRL
+ * @version $Id$
+ */
+public class MAssetClass extends X_A_Asset_Class
+{
+ /**
+ *
+ */
+ public MAssetClass(Properties ctx, int A_Asset_Class_ID, String trxName)
+ {
+ super (ctx, A_Asset_Class_ID, trxName);
+ } // MAssetClass
+
+ /**
+ * Load Constructor
+ * @param ctx context
+ * @param rs result set record
+ */
+ public MAssetClass (Properties ctx, ResultSet rs, String trxName)
+ {
+ super (ctx, rs, trxName);
+ } // MAssetClass
+
+ /** */
+ private static CCache s_cache = new CCache("A_Asset_Class", 20);
+
+ /** Get Asset Class from cache
+ * @param ctx context
+ * @param id A_Asset_Class_ID
+ * @return MAssetClass or null if not found
+ */
+ public static MAssetClass get(Properties ctx, int id) {
+ if (id <= 0) {
+ return null;
+ }
+
+ MAssetClass assetClass = s_cache.get(id);
+ if (assetClass == null) {
+ assetClass = new MAssetClass(ctx, id, null);
+ }
+ if (assetClass.get_ID() != id) {
+ return null;
+ }
+ s_cache.put(id, assetClass);
+ return assetClass;
+ } // get
+
+ /**
+ *
+ */
+ public static MAssetClass get(Properties ctx, String value)
+ {
+ // TODO: maybe logging
+ final String whereClause = "UPPER(Value)=UPPER(?) AND AD_Client_ID IN (0,?)";
+ return new Query(ctx, Table_Name, whereClause, null)
+ .setParameters(new Object[]{value, Env.getAD_Client_ID(ctx)})
+ .setOrderBy("AD_Client_ID DESC")
+ .firstOnly();
+ } // get
+
+ /**
+ *
+ */
+ public void setDescription() {
+ StringBuffer description = new StringBuffer();
+ String value = getValue();
+ if (value != null) {
+ description.append(value).append(" ");
+ }
+
+ String name = getName();
+ if (name != null) {
+ description.append(name);
+ }
+ super.setDescription(description.toString());
+ }
+
+ /**
+ *
+ */
+ public void setLevels() {
+ setMFX_Grupa(0);
+ setMFX_SubGrupa(0);
+ setMFX_Clasa(0);
+ setMFX_SubClasa(0);
+
+ String value = getValue();
+ if (value == null || value.length() == 0)
+ return;
+
+ String[] arr = value.split("\\.");
+ try {
+ if (arr.length >= 1)
+ setMFX_Grupa(Integer.valueOf(arr[0]));
+ if (arr.length >= 2)
+ setMFX_SubGrupa(Integer.valueOf(arr[1]));
+ if (arr.length >= 3)
+ setMFX_Clasa(Integer.valueOf(arr[2]));
+ if (arr.length >= 4)
+ setMFX_SubClasa(Integer.valueOf(arr[3]));
+ } catch (NumberFormatException e) {
+ log.warning("@Error@ @Value@=" + value);
+ }
+ }
+
+ /**
+ *
+ */
+ public int getA_Life_Period_Min(Timestamp serviceDate) {
+ Calendar cal = TimeUtil.getCalendar(serviceDate);
+ if (cal.get(Calendar.YEAR) >= 2004) {
+ return getA_Life_Period_Min();
+ }
+ else {
+ return getA_Life_Period_2004();
+ }
+ }
+
+ /** Validate */
+ public String validate(boolean saveError, int A_Life_Period, Timestamp serviceDate) {
+ log.fine("Entering");
+ int A_Life_Period_Min = 0;
+ int A_Life_Period_Max = 1000000;
+ Calendar cal = TimeUtil.getCalendar(serviceDate);
+ if (cal.get(Calendar.YEAR) >= 2004) {
+ A_Life_Period_Min = getA_Life_Period_Min();
+ A_Life_Period_Max = getA_Life_Period_Max();
+ }
+ else {
+ A_Life_Period_Min = getA_Life_Period_2004();
+ A_Life_Period_Max = getA_Life_Period_2004();
+ }
+ // logging:
+ if (CLogMgt.isLevelFine()) {
+ log.fine("serviceDate=" + serviceDate + ", A_Life_Period_Min=" + A_Life_Period_Min + ", A_Life_Period_Max=" + A_Life_Period_Max + ", A_Life_Period=" + A_Life_Period);
+ }
+
+
+ if (A_Life_Period < A_Life_Period_Min || A_Life_Period > A_Life_Period_Max) {
+ String errmsg = "@UseLifeMonths@=" + A_Life_Period + " @NotBetween@ " + A_Life_Period_Min + " - " + A_Life_Period_Max;
+ if (saveError) {
+ log.saveError("Error", errmsg);
+ }
+ if(CLogMgt.isLevelFine()) {
+ log.fine("Leaving: " + errmsg);
+ Thread.dumpStack();
+ }
+ return errmsg;
+ }
+
+ log.fine("Leaving: OK!");
+ return "";
+ }
+
+ /** Validate UseLifeImpl model
+ */
+ public boolean validate(UseLifeImpl asset) {
+ if(CLogMgt.isLevelFine()) log.fine("Entering: UseLifeImpl=" + asset);
+
+ if (!asset.isFiscal()) {
+ log.fine("Leaving: fiscal=false [RETURN TRUE]");
+ return true;
+ }
+ else {
+ log.fine("asset is fiscal");
+ }
+
+ int A_Life_Period = asset.getUseLifeMonths();
+ Timestamp serviceDate = asset.getAssetServiceDate();
+ String errmsg = validate(true, A_Life_Period, serviceDate);
+ boolean ok = (errmsg == null || errmsg.length() == 0);
+
+ log.fine("Leaving: ok=" + ok);
+ return ok;
+ }
+
+ /** Depreciated check
+ *
+ */
+ public boolean isDepreciated() {
+ return !(getA_Life_Period_Min() == 0 && getA_Life_Period_Max() ==0);
+ }
+
+ /**
+ *
+ */
+ public boolean beforeSave (boolean newRecord) {
+ setDescription();
+ if (is_ValueChanged("Value")) {
+ setValue(getValue().trim());
+ setLevels();
+ }
+ return true;
+ }
+}
diff --git a/org.adempiere.base/src/org/compiere/model/MAssetDisposed.java b/org.adempiere.base/src/org/compiere/model/MAssetDisposed.java
new file mode 100644
index 0000000000..5006b86b36
--- /dev/null
+++ b/org.adempiere.base/src/org/compiere/model/MAssetDisposed.java
@@ -0,0 +1,438 @@
+package org.compiere.model;
+
+import java.io.File;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.sql.ResultSet;
+import java.sql.Timestamp;
+import java.util.List;
+import java.util.Properties;
+
+import org.compiere.model.MClient;
+import org.compiere.model.MDocType;
+import org.compiere.model.MPeriod;
+import org.compiere.model.ModelValidationEngine;
+import org.compiere.model.ModelValidator;
+import org.compiere.process.DocAction;
+import org.compiere.process.DocumentEngine;
+import org.compiere.util.Env;
+import org.idempiere.fa.exceptions.AssetAlreadyDepreciatedException;
+import org.idempiere.fa.exceptions.AssetException;
+import org.idempiere.fa.exceptions.AssetNotImplementedException;
+import org.idempiere.fa.exceptions.AssetNotSupportedException;
+import org.idempiere.fa.exceptions.AssetStatusChangedException;
+import org.idempiere.fa.feature.UseLifeImpl;
+import org.idempiere.fa.util.POCacheLocal;
+
+import com.sun.enterprise.connectors.util.SetMethodAction;
+
+
+
+/**
+ * Asset Disposal Model
+ * @author Teo Sarca, SC ARHIPAC SERVICE SRL
+ */
+public class MAssetDisposed extends X_A_Asset_Disposed
+implements DocAction
+{
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1763997880662445638L;
+
+ public MAssetDisposed (Properties ctx, int A_Asset_Disposed_ID, String trxName)
+ {
+ super (ctx, A_Asset_Disposed_ID, trxName);
+ if (A_Asset_Disposed_ID == 0)
+ {
+ setProcessed (false);
+ setProcessing (false);
+ }
+
+ }
+
+ //@win: autocreate asset disposal from ar invoice
+ public static MAssetDisposed createAssetDisposed (MInvoiceLine invLine) {
+ MAssetDisposed assetDisposed = new MAssetDisposed(invLine);
+ assetDisposed.dump();
+ return assetDisposed;
+ }
+
+ private MAssetDisposed (MInvoiceLine invLine) {
+ this(invLine.getCtx(),0,invLine.get_TrxName());
+ log.finest("Entering: Project=" + invLine);
+ setAD_Org_ID(invLine.getAD_Org_ID());
+ setPostingType(POSTINGTYPE_Actual);
+ setDateDoc(invLine.getC_Invoice().getDateInvoiced());
+ setDateAcct(invLine.getC_Invoice().getDateInvoiced());
+ setA_Disposed_Date(invLine.getC_Invoice().getDateInvoiced());
+ setA_Disposed_Method(A_DISPOSED_METHOD_Trade);
+ setA_Asset_ID(invLine.getA_Asset_ID());
+ set_ValueNoCheck("C_Invoice_ID", invLine.getC_Invoice_ID());
+ setM_InvoiceLine(invLine);
+ saveEx(invLine.get_TrxName());
+ }
+
+ private final POCacheLocal m_cacheInvoiceLine = POCacheLocal.newInstance(this, MInvoiceLine.class);
+ public MInvoiceLine getM_InvoiceLine(boolean requery)
+ {
+ return m_cacheInvoiceLine.get(requery);
+ }
+ private void setM_InvoiceLine(MInvoiceLine invLine)
+ {
+ set_Value("C_InvoiceLine_ID", invLine.get_ID());
+ m_cacheInvoiceLine.set(invLine);
+ }
+ //end @win: autocreate asset disposal from ar invoice
+
+ public MAssetDisposed (Properties ctx, ResultSet rs, String trxName)
+ {
+ super (ctx, rs, trxName);
+ }
+
+ public MAsset getAsset()
+ {
+ return MAsset.get(getCtx(), getA_Asset_ID(), null);
+ }
+
+
+ public boolean processIt (String processAction)
+ {
+ m_processMsg = null;
+ DocumentEngine engine = new DocumentEngine (this, getDocStatus());
+ return engine.processIt (processAction, getDocAction());
+ } // processIt
+
+ /** Process Message */
+ private String m_processMsg = null;
+ /** Just Prepared Flag */
+ private boolean m_justPrepared = false;
+
+
+ public boolean unlockIt()
+ {
+ setProcessing(false);
+ return true;
+ } // unlockIt
+
+
+ public boolean invalidateIt()
+ {
+ return false;
+ } // invalidateIt
+
+
+ public String prepareIt()
+ {
+ m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE);
+ if (m_processMsg != null)
+ {
+ return DocAction.STATUS_Invalid;
+ }
+
+ MPeriod.testPeriodOpen(getCtx(), getDateAcct(), MDocType.DOCBASETYPE_GLDocument, getAD_Org_ID());
+
+ //saveEx() //commented by @win
+ updateFromAsset(this);
+ saveEx(get_TrxName()); //added by @win
+ if (is_Changed())
+ {
+ throw new AssetStatusChangedException();
+ }
+
+ // Check that the FA is not just depreciated
+ MDepreciationWorkfile assetwk = MDepreciationWorkfile.get(getCtx(), getA_Asset_ID(), getPostingType(), get_TrxName());
+ if (assetwk.isDepreciated(getDateAcct()))
+ {
+ throw new AssetAlreadyDepreciatedException();
+ }
+ MDepreciationExp.checkExistsNotProcessedEntries(getCtx(), getA_Asset_ID(), getDateAcct(), getPostingType(), get_TrxName());
+
+ m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE);
+ if (m_processMsg != null)
+ {
+ return DocAction.STATUS_Invalid;
+ }
+ //
+ m_justPrepared = true;
+ setDocAction(DOCACTION_Complete);
+ return DocAction.STATUS_InProgress;
+ } // prepareIt
+
+
+ public boolean approveIt()
+ {
+ log.info("approveIt - " + toString());
+ setIsApproved(true);
+ return true;
+ } // approveIt
+
+
+ public boolean rejectIt()
+ {
+ log.info("rejectIt - " + toString());
+ setIsApproved(false);
+ return true;
+ } // rejectIt
+
+
+ public String completeIt()
+ {
+ // Re-Check
+ if (!m_justPrepared)
+ {
+ String status = prepareIt();
+ if (!DocAction.STATUS_InProgress.equals(status))
+ return status;
+ }
+
+ String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE);
+ if (valid != null)
+ {
+ m_processMsg = valid;
+ return DocAction.STATUS_Invalid;
+ }
+
+ // Implicit Approval
+ if (!isApproved())
+ approveIt();
+ log.info(toString());
+ //
+
+ //loading asset
+ MAsset asset = getAsset();
+ log.fine("asset=" + asset);
+
+ // Activation
+ if(!isDisposal())
+ {
+ String method = getA_Activation_Method();
+ if(method.equals(A_ACTIVATION_METHOD_Activation))
+ { // reactivation
+ asset.changeStatus(MAsset.A_ASSET_STATUS_Activated, getDateDoc());
+ }
+ else
+ {
+ throw new AssetNotSupportedException(COLUMNNAME_A_Activation_Method, method);
+ }
+ }
+ // Preservation/Partial Retirement/etc
+ else
+ {
+ String method = getA_Disposed_Method();
+ if (A_DISPOSED_METHOD_Preservation.equals(method))
+ {
+ asset.changeStatus(MAsset.A_ASSET_STATUS_Preservation, getDateDoc());
+ }
+ else if (A_DISPOSED_METHOD_Simple.equals(method)
+ || A_DISPOSED_METHOD_Trade.equals(method)
+ )
+ {
+ asset.changeStatus(MAsset.A_ASSET_STATUS_Disposed, null);
+ setA_Disposal_Amt(getA_Asset_Cost());
+ setA_Accumulated_Depr_Delta(getA_Accumulated_Depr());
+ setExpense(getA_Disposal_Amt().subtract(getA_Accumulated_Depr_Delta()));
+ createDisposal();
+ }
+ else if (A_DISPOSED_METHOD_PartialRetirement.equals(method))
+ {
+ createDisposal();
+ }
+ else
+ {
+ throw new AssetNotSupportedException(COLUMNNAME_A_Disposed_Method, method);
+ }
+ }
+
+ asset.saveEx(get_TrxName());
+
+
+ // User Validation
+ valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE);
+ if (valid != null)
+ {
+ m_processMsg = valid;
+ return DocAction.STATUS_Invalid;
+ }
+
+ // Done
+ setProcessed(true);
+ setDocAction(DOCACTION_Close);
+ return DocAction.STATUS_Completed;
+ } // completeIt
+
+
+ public boolean voidIt()
+ {
+ throw new AssetNotImplementedException("");
+ } // voidIt
+
+
+ public boolean closeIt()
+ {
+ setDocAction(DOCACTION_None);
+ return true;
+ } // closeIt
+
+
+ public boolean reverseCorrectIt()
+ {
+ throw new AssetNotImplementedException("");
+ } // reverseCorrectionIt
+
+
+ public boolean reverseAccrualIt()
+ {
+ throw new AssetNotImplementedException("");
+ }
+
+
+ public boolean reActivateIt()
+ {
+ throw new AssetNotImplementedException("");
+ }
+
+
+
+ public String getSummary()
+ {
+ return new StringBuffer()
+ .append(getDocumentNo()).append("/").append(getDateDoc())
+ .toString();
+ }
+
+
+ public String getProcessMsg()
+ {
+ return m_processMsg;
+ } // getProcessMsg
+
+
+ public int getDoc_User_ID()
+ {
+ return getCreatedBy();
+ }
+
+
+ public BigDecimal getApprovalAmt()
+ {
+ return Env.ZERO;
+ }
+
+
+ public int getC_Currency_ID()
+ {
+ return MClient.get(getCtx(), getAD_Client_ID()).getAcctSchema().getC_Currency_ID();
+ }
+
+
+ protected boolean beforeSave (boolean newRecord)
+ {
+ if (getDateAcct() == null)
+ {
+ setDateAcct(getDateDoc());
+ }
+ if (newRecord || is_ValueChanged(COLUMNNAME_DateAcct))
+ {
+ setC_Period_ID(MPeriod.get(getCtx(), getDateAcct(), getAD_Org_ID()).get_ID());
+ }
+ if (getA_Disposed_Date() == null)
+ {
+ setA_Disposed_Date(getDateAcct());
+ }
+ /* commented by @win - asset type
+ if (!MAssetType.isFixedAsset(getA_Asset_ID()))
+ {
+ throw new AssetException("This is not a Fixed Asset!");
+ }
+ */
+ return true;
+ }
+
+ /**
+ * Copy fields from A_Asset
+ * @param model
+ * @param A_Asset_ID
+ */
+ public static void updateFromAsset(I_A_Asset_Disposed bean)
+ {
+ int asset_id = bean.getA_Asset_ID();
+ SetGetUtil.copyValues(
+ SetGetUtil.wrap(bean),
+ MAsset.Table_Name, asset_id,
+ new String[] {
+ MAsset.COLUMNNAME_IsDisposed,
+ MAsset.COLUMNNAME_A_Asset_Status,
+ "AD_Org_ID",
+ }
+ );
+
+ MDepreciationWorkfile wk = MDepreciationWorkfile.get(Env.getCtx(), asset_id, bean.getPostingType(), null);
+ if (wk != null)
+ {
+ bean.setA_Asset_Cost(wk.getA_Asset_Cost());
+ bean.setA_Accumulated_Depr(wk.getA_Accumulated_Depr());
+ }
+ else
+ {
+ bean.setA_Asset_Cost(Env.ZERO);
+ bean.setA_Accumulated_Depr(Env.ZERO);
+ }
+ }
+
+
+ public File createPDF ()
+ {
+ return null;
+ } // createPDF
+
+
+ public String getDocumentInfo()
+ {
+ return getDocumentNo();
+ } // getDocumentInfo
+
+ /**
+ * Check if this is a disposal (if the asset is not disposed)
+ * @return true if is disposal
+ */
+ public boolean isDisposal()
+ {
+ return !isDisposed();
+ }
+
+ public static void setA_Disposal_Amt(I_A_Asset_Disposed bean)
+ {
+ int precision = 2;
+ BigDecimal A_Asset_Cost = bean.getA_Asset_Cost();
+ BigDecimal A_Disposal_Amt = bean.getA_Disposal_Amt();
+ BigDecimal coef = Env.ZERO;
+ if (A_Asset_Cost.signum() != 0)
+ {
+ coef = A_Disposal_Amt.divide(A_Asset_Cost, 12, RoundingMode.HALF_UP);
+ }
+ //
+ BigDecimal A_Accumulated_Depr = bean.getA_Accumulated_Depr();
+ BigDecimal A_Accumulated_Depr_Delta = A_Accumulated_Depr.multiply(coef).setScale(precision, RoundingMode.HALF_UP);
+ BigDecimal Expense = A_Disposal_Amt.subtract(A_Accumulated_Depr_Delta);
+ //
+ bean.setA_Accumulated_Depr_Delta(A_Accumulated_Depr_Delta);
+ bean.setExpense(Expense);
+ }
+
+ private void createDisposal()
+ {
+ MDepreciationWorkfile assetwk = MDepreciationWorkfile.get(getCtx(), getA_Asset_ID(), getPostingType(), get_TrxName());
+ assetwk.adjustCost(getA_Disposal_Amt().negate(), Env.ZERO, false);
+ assetwk.adjustAccumulatedDepr(getA_Accumulated_Depr_Delta().negate(), getA_Accumulated_Depr_Delta().negate(), false);
+ assetwk.saveEx();
+ assetwk.buildDepreciation();
+ //
+ // Delete not processed expense entries
+ List list = MDepreciationExp.getNotProcessedEntries(getCtx(), getA_Asset_ID(), getPostingType(), get_TrxName());
+ for (MDepreciationExp ex : list)
+ {
+ ex.deleteEx(false);
+ }
+ }
+}
diff --git a/org.adempiere.base/src/org/compiere/model/MAssetProduct.java b/org.adempiere.base/src/org/compiere/model/MAssetProduct.java
new file mode 100644
index 0000000000..8c5a15e56f
--- /dev/null
+++ b/org.adempiere.base/src/org/compiere/model/MAssetProduct.java
@@ -0,0 +1,93 @@
+package org.compiere.model;
+
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.util.Properties;
+
+import org.compiere.model.Query;
+
+/**
+ * Asset Product
+ * @author Teo Sarca, SC ARHIPAC SERIVCE SRL
+ */
+public class MAssetProduct extends X_A_Asset_Product
+{
+ private static final long serialVersionUID = 1L;
+
+ /** Standard Constructor */
+ public MAssetProduct (Properties ctx, int A_Asset_Product_ID, String trxName)
+ {
+ super (ctx, A_Asset_Product_ID, trxName);
+ }
+
+ /** Load Constructor */
+ public MAssetProduct (Properties ctx, ResultSet rs, String trxName)
+ {
+ super (ctx, rs, trxName);
+ }
+
+ /**
+ * Set product and ASI
+ * @param M_Product_ID
+ * @param M_ASI_ID
+ */
+ public void setProduct(int M_Product_ID, int M_ASI_ID)
+ {
+ setM_Product_ID(M_Product_ID);
+ setM_AttributeSetInstance_ID(M_ASI_ID);
+ }
+
+ /**
+ * Get/Create Asset Product.
+ * Doesn't save newly create one.
+ * @param ctx
+ * @param A_Asset_ID
+ * @param M_Product_ID
+ * @param M_ASI_ID
+ * @param trxName
+ * @return MAssetProduct
+ */
+ public static MAssetProduct getCreate(Properties ctx,
+ int A_Asset_ID, int M_Product_ID, int M_ASI_ID,
+ String trxName)
+ {
+ if (M_Product_ID <= 0) {
+ return null;
+ }
+ final String whereClause = COLUMNNAME_A_Asset_ID + "=?"
+ + " AND " + COLUMNNAME_M_Product_ID + "=?"
+ + " AND " + COLUMNNAME_M_AttributeSetInstance_ID + "=?";
+ MAssetProduct ap = new Query(ctx, MAssetProduct.Table_Name, whereClause, trxName)
+ .setParameters(new Object[]{A_Asset_ID, M_Product_ID, M_ASI_ID})
+ .firstOnly();
+ // If found, return
+ if (ap != null)
+ return ap;
+
+ // Create new
+ ap = new MAssetProduct(ctx, 0, trxName);
+ ap.setA_Asset_ID(A_Asset_ID);
+ ap.setProduct(M_Product_ID, M_ASI_ID);
+
+ return ap;
+ } // get
+
+ /**
+ * Add given qty to current qty
+ * @param qty
+ */
+ public void addA_Qty_Current(BigDecimal qty)
+ {
+ setA_QTY_Current(getA_QTY_Current().add(qty));
+ }
+
+ /**
+ * Update given asset.
+ * Note: does not save asset
+ */
+ public void updateAsset(MAsset asset)
+ {
+ asset.setM_Product_ID(getM_Product_ID());
+ asset.setM_AttributeSetInstance_ID(getM_AttributeSetInstance_ID());
+ }
+}
diff --git a/org.adempiere.base/src/org/compiere/model/MAssetReval.java b/org.adempiere.base/src/org/compiere/model/MAssetReval.java
new file mode 100644
index 0000000000..2241df3cf5
--- /dev/null
+++ b/org.adempiere.base/src/org/compiere/model/MAssetReval.java
@@ -0,0 +1,258 @@
+/**
+ *
+ */
+package org.compiere.model;
+
+import java.io.File;
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.Timestamp;
+import java.util.Properties;
+
+import org.adempiere.exceptions.AdempiereException;
+import org.compiere.model.MDocType;
+import org.compiere.model.MPeriod;
+import org.compiere.model.ModelValidationEngine;
+import org.compiere.model.ModelValidator;
+import org.compiere.process.DocAction;
+import org.compiere.process.DocumentEngine;
+import org.compiere.util.Env;
+import org.compiere.util.TimeUtil;
+
+
+/**
+ * @author Anca Bradau www.arhipac.ro
+ *
+ */
+public class MAssetReval extends X_A_Asset_Reval
+implements DocAction
+{
+ private static final long serialVersionUID = 1L;
+
+ private boolean m_justPrepared = false;
+
+ public MAssetReval(Properties ctx, int X_A_Asset_Reval_ID, String trxName)
+ {
+ super(ctx, X_A_Asset_Reval_ID, trxName);
+ if (X_A_Asset_Reval_ID == 0)
+ {
+ setDocStatus(DOCSTATUS_Drafted);
+ setDocAction(DOCACTION_Complete);
+ setProcessed(false);
+ }
+ }
+ public MAssetReval(Properties ctx, ResultSet rs, String trxName)
+ {
+ super (ctx, rs, trxName);
+ }
+
+ public boolean approveIt()
+ {
+ return false;
+ }
+
+ public boolean closeIt() {
+ setDocAction(DOCACTION_None);
+ return true;
+ }
+
+ public String prepareIt()
+ {
+ m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE);
+ if (m_processMsg != null)
+ {
+ return DocAction.STATUS_Invalid;
+ }
+ // test if period is open
+ MPeriod.testPeriodOpen(getCtx(), getDateAcct(), MDocType.DOCBASETYPE_GLJournal, getAD_Org_ID());
+
+ // test if asset is already Depreciated
+ MDepreciationWorkfile assetwk = MDepreciationWorkfile.get(getCtx(), getA_Asset_ID(), getPostingType());
+
+ if (!assetwk.isDepreciated(getDateAcct()))
+ {
+ throw new AdempiereException("Asset is not depreciated at this moment");
+
+ }
+
+ // test if Asset Cost and Accumulated Depreciation are changed
+ if (assetwk.getA_Asset_Cost().equals(getA_Asset_Cost_Change())
+ && assetwk.getA_Accumulated_Depr().equals(getA_Change_Acumulated_Depr()))
+ {
+ throw new AdempiereException("Nothing has changed");
+ }
+
+ //test if Asset Cost is changed
+ if (assetwk.getA_Asset_Cost().equals(getA_Asset_Cost_Change())
+ && !assetwk.getA_Accumulated_Depr().equals(getA_Change_Acumulated_Depr()))
+ {
+ throw new AdempiereException("It has changed the cost of Asset");
+ }
+
+ // test if Accumulated depreciation is changed
+ if (!assetwk.getA_Asset_Cost().equals(getA_Asset_Cost_Change())
+ && assetwk.getA_Accumulated_Depr().equals(getA_Change_Acumulated_Depr()))
+ {
+ throw new AdempiereException("It has changed the cumulative depreciation");
+ }
+
+ if (!isLastDepreciated(getDateAcct()))
+ {
+ throw new AdempiereException("It can only review the last month processed");
+ }
+
+ m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE);
+ if (m_processMsg != null)
+ return DocAction.STATUS_Invalid;
+
+ m_justPrepared = true;
+ if (!DOCACTION_Complete.equals(getDocAction()))
+ setDocAction(DOCACTION_Complete);
+ return DocAction.STATUS_InProgress;
+ }
+ //return true if is last record depreciated
+ public boolean isLastDepreciated(Timestamp date)
+ {
+ MDepreciationWorkfile assetwk = MDepreciationWorkfile.get(getCtx(), getA_Asset_ID(), getPostingType());
+ Timestamp lastActionDate = assetwk.getLastActionDate();
+ boolean isLastDepr = TimeUtil.getMonthLastDay(date).equals(lastActionDate);
+ return isLastDepr;
+
+ }
+
+ public String completeIt() {
+
+ if (!m_justPrepared)
+ {
+ String status = prepareIt();
+ if (!DocAction.STATUS_InProgress.equals(status))
+ return status;
+ }
+
+
+ m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE);
+ if (m_processMsg != null)
+ return DocAction.STATUS_Invalid;
+
+ MDepreciationWorkfile assetwk = MDepreciationWorkfile.get(getCtx(), getA_Asset_ID(), getPostingType(), get_TrxName());
+ assetwk.setA_Asset_Cost(getA_Asset_Cost_Change());
+ assetwk.setA_Accumulated_Depr(getA_Change_Acumulated_Depr());
+ assetwk.saveEx();
+ MAsset asset = MAsset.get(getCtx(), getA_Asset_ID(), get_TrxName());
+ asset.setA_Asset_RevalDate(this.getDateDoc());
+ asset.saveEx();
+ //rebuild depreciation
+ /* commented out by @win, deprecating existing design
+ assetwk.buildDepreciation();
+ */
+
+ // User Validation
+ String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE);
+ if (valid != null)
+ {
+ m_processMsg = valid;
+ return DocAction.STATUS_Invalid;
+ }
+
+ // Set the definite document number after completed (if needed)
+ //setDefiniteDocumentNo();
+
+ setProcessed(true);
+ setDocAction(DOCACTION_Close);
+ return DocAction.STATUS_Completed;
+ }
+
+ public File createPDF()
+ {
+ return null;
+ }
+
+ public BigDecimal getApprovalAmt()
+ {
+ return Env.ZERO;
+ }
+
+ public int getC_Currency_ID()
+ {
+ return 0;
+ }
+
+ public int getDoc_User_ID()
+ {
+ return getCreatedBy();
+ }
+
+ public String getDocumentInfo()
+ {
+ return getDocumentNo() + "/" + getDateAcct();
+ }
+
+ public String getProcessMsg() {
+ return m_processMsg;
+ }
+ private String m_processMsg = null;
+
+
+ public String getSummary()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("@DocumentNo@ #").append(getDocumentNo());
+ return sb.toString();
+ }
+
+ public boolean invalidateIt()
+ {
+ return false;
+ }
+
+ public boolean processIt(String action) throws Exception
+ {
+ m_processMsg = null;
+ DocumentEngine engine = new DocumentEngine (this, getDocStatus());
+ return engine.processIt (action, getDocAction());
+ }
+
+
+ public boolean reActivateIt()
+ {
+ return false;
+ }
+
+ public boolean rejectIt()
+ {
+ return false;
+ }
+
+ public boolean reverseAccrualIt()
+ {
+ return false;
+ }
+
+ public boolean reverseCorrectIt()
+ {
+ return false;
+ }
+
+ public boolean unlockIt()
+ {
+ return false;
+ }
+
+ public boolean voidIt()
+ {
+ return false;
+ }
+ public String getDocumentNo()
+ {
+ return null;
+ }
+ /** Before save
+ * @param newRecord
+ * @return true on success
+ */
+ //protected boolean beforeSave (boolean newRecord)
+ //{
+ //return true;
+ //}
+
+}
diff --git a/org.adempiere.base/src/org/compiere/model/MAssetType.java b/org.adempiere.base/src/org/compiere/model/MAssetType.java
new file mode 100644
index 0000000000..e70ed6c7c0
--- /dev/null
+++ b/org.adempiere.base/src/org/compiere/model/MAssetType.java
@@ -0,0 +1,231 @@
+package org.compiere.model;
+
+import java.sql.ResultSet;
+import java.util.Properties;
+
+import org.compiere.model.CalloutEngine;
+import org.compiere.model.GridField;
+import org.compiere.model.GridTab;
+import org.compiere.model.Query;
+import org.compiere.util.ArhRuntimeException;
+import org.compiere.util.CCache;
+import org.compiere.util.Env;
+
+/**
+ * Asset Type
+ * @author Teo Sarca, SC ARHIPAC SERVICE SRL
+ */
+public class MAssetType extends X_A_Asset_Type
+{
+ private static final long serialVersionUID = 1L;
+
+ public static final String A_ASSET_TYPE_MFX = "MFX";
+ public static final String A_ASSET_TYPE_INV = "INV";
+
+ public static final int A_ASSET_TYPE_ID_MFX = 1;
+ public static final int A_ASSET_TYPE_ID_INV = 2;
+ public static final int A_ASSET_TYPE_ID_SUP = 3;
+ /** Obiecte tert */
+ public static final int A_ASSET_TYPE_ID_OIN = 4;
+
+ public static interface Model
+ {
+ /** Get Context */
+ public Properties getCtx();
+ /** Get Asset Type */
+ public int getA_Asset_Type_ID();
+ /** Get In Possession. The asset is in the possession of the organization */
+ public boolean isInPosession();
+ /** Get Owned. The asset is owned by the organization */
+ public boolean isOwned();
+ /** Get Is Depreciated */
+ public boolean isDepreciated();
+ };
+
+ /** Static Cache: A_Asset_Type.A_Asset_Type_ID-> MAssetType */
+ private static CCache s_cache = new CCache(Table_Name, 10, 0);
+
+ /** Get Asset Type
+ * @param ctx context
+ * @param A_Asset_Type_ID
+ * @return asset type object
+ */
+ public static MAssetType get (Properties ctx, int A_Asset_Type_ID)
+ {
+ if (A_Asset_Type_ID <= 0)
+ return null;
+ MAssetType o = s_cache.get(A_Asset_Type_ID);
+ if (o != null)
+ return o;
+ o = new MAssetType(ctx, A_Asset_Type_ID, null);
+ if (o.get_ID() > 0) {
+ s_cache.put(A_Asset_Type_ID, o);
+ return o;
+ }
+ return null;
+ }
+
+ /** Get Asset Type
+ * @param ctx context
+ * @param id id as Number
+ * @return asset type object
+ */
+ public static MAssetType get (Properties ctx, Object id)
+ {
+ if (id == null)
+ return null;
+ return get(ctx, ((Number)id).intValue());
+ }
+
+ /** Standard Constructor */
+ public MAssetType (Properties ctx, int A_Asset_Type_ID, String trxName)
+ {
+ super (ctx, A_Asset_Type_ID, trxName);
+ }
+
+ /** Load Constructor */
+ public MAssetType (Properties ctx, ResultSet rs, String trxName)
+ {
+ super (ctx, rs, trxName);
+ }
+
+ /** Is Fixed Asset
+ */
+ public boolean isFixedAsset()
+ {
+ return A_ASSET_TYPE_MFX.equals(getValue());
+ }
+
+ public static boolean isFixedAsset(int A_Asset_ID)
+ {
+ final String whereClause = MAsset.COLUMNNAME_A_Asset_ID+"=?"
+ +" AND "+MAsset.COLUMNNAME_A_Asset_Type+"=?";
+
+ return new Query(Env.getCtx(), MAsset.Table_Name, whereClause, null)
+ .setParameters(new Object[]{A_Asset_ID, A_ASSET_TYPE_MFX})
+ .match();
+ }
+
+ public static boolean isFixedAsset(MAsset asset)
+ {
+ return asset != null && A_ASSET_TYPE_MFX.equals(asset.getA_Asset_Type());
+ }
+
+ public static boolean isFixedAssetGroup(Properties ctx, int A_Asset_Group_ID)
+ {
+ if (A_Asset_Group_ID <= 0)
+ return false;
+ MAssetGroup assetGroup = MAssetGroup.get(ctx, A_Asset_Group_ID);
+ //
+ int assetType_ID = assetGroup.getA_Asset_Type_ID();
+ if (assetType_ID <= 0)
+ return false;
+ MAssetType assetType = MAssetType.get(ctx, assetType_ID);
+ //
+ return assetType.isFixedAsset();
+ }
+
+ /** Is Inventory Object
+ */
+ public boolean isInventoryObject() {
+ return A_ASSET_TYPE_INV.equals(getValue());
+ }
+
+ /** Convert an Yes-No-Unknown field to Boolean */
+ protected static Boolean getBoolean (String value, boolean useDefaults)
+ {
+ if (value == null || value.length() == 0)
+ return null;
+ String f = value.substring(0, 1);
+ if ("N".equals(f))
+ return Boolean.FALSE;
+ else if ("Y".equals(f))
+ return Boolean.TRUE;
+ else if ("X".equals(f) && useDefaults)
+ return getBoolean(value.substring(1), false);
+ else
+ return null;
+ }
+
+ /**
+ * Validate a Model
+ * @param m model
+ * @thorows ContextUserException
+ */
+ public static void validate(Model m)
+ {
+ // Load Asset Type
+ MAssetType assetType = MAssetType.get(m.getCtx(), m.getA_Asset_Type_ID());
+ if (assetType == null)
+ {
+ throw new ArhRuntimeException(m.getCtx(), "@NotFound@ @A_Asset_Type_ID@")
+ .addInfo("@A_Asset_Type_ID", m.getA_Asset_Type_ID());
+ }
+
+ ArhRuntimeException err = new ArhRuntimeException(m.getCtx(), "");
+ Boolean f = getBoolean(assetType.getIsOwned(), false);
+ if (f != null && f.booleanValue() != m.isOwned()) {
+ err.addInfo("@IsOwned@ <> @" + f + "@");
+ }
+ f = getBoolean(assetType.getIsInPosession(), false);
+ if (f != null && f.booleanValue() != m.isInPosession()) {
+ err.addInfo("@IsInPosession@ <> @" + f + "@");
+ }
+ f = getBoolean(assetType.getIsDepreciable(), false);
+ if (f != null && f.booleanValue() != m.isDepreciated()) {
+ err.addInfo("@IsDepreciated@ <> @" + f + "@");
+ }
+
+ if (err.hasInfo())
+ throw err;
+ }
+
+ /**
+ * Update the given SetGetModel; Does not set A_Asset_Type_ID
+ * @param model
+ * @param useDefaults in case is not a concrete value, use defaults
+ */
+ public boolean update(SetGetModel model, boolean useDefaults)
+ {
+// boolean useDefaults = true;
+ Boolean f = getBoolean(getIsOwned(), useDefaults);
+ if (f != null)
+ model.set_AttrValue("IsOwned", f);
+ f = getBoolean(getIsInPosession(), useDefaults);
+ if (f != null)
+ model.set_AttrValue("IsInPosession", f);
+ f = getBoolean(getIsDepreciable(), useDefaults);
+ if (f != null) {
+ model.set_AttrValue("IsDepreciated", f);
+ }
+
+ if (!isFixedAsset()) {
+ model.set_AttrValue("A_Asset_Class_ID", null);
+ }
+
+ model.set_AttrValue("A_Asset_Type", getValue());
+
+ return true;
+ }
+
+ /** Callout Class */
+ public static class Callout extends CalloutEngine
+ {
+ public String assetType(Properties ctx, int WindowNo, GridTab mTab, GridField mField, Object value) {
+ if (isCalloutActive())
+ return "";
+
+ int A_Asset_Type_ID = 0;
+ if (value != null && value instanceof Number)
+ A_Asset_Type_ID = ((Number)value).intValue();
+ else
+ return "";
+ MAssetType assetType = MAssetType.get(ctx, A_Asset_Type_ID);
+ if (assetType != null)
+ assetType.update(SetGetUtil.wrap(mTab), true);
+ //
+ return "";
+ }
+ };
+
+} // class MAssetType
diff --git a/org.adempiere.base/src/org/compiere/model/MConversionRateUtil.java b/org.adempiere.base/src/org/compiere/model/MConversionRateUtil.java
new file mode 100644
index 0000000000..7ebf89f471
--- /dev/null
+++ b/org.adempiere.base/src/org/compiere/model/MConversionRateUtil.java
@@ -0,0 +1,103 @@
+/**
+ *
+ */
+package org.compiere.model;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.sql.Timestamp;
+
+import org.compiere.util.CLogMgt;
+import org.compiere.util.CLogger;
+import org.compiere.util.Env;
+import org.idempiere.exceptions.NoCurrencyConversionException;
+
+/**
+ * @author Teo Sarca, SC ARHIPAC SERVICE SRL
+ */
+public final class MConversionRateUtil
+{
+ /** Logger */
+ private static final CLogger s_log = CLogger.getCLogger (MConversionRateUtil.class);
+
+ private MConversionRateUtil()
+ {
+ // nothing
+ }
+
+ /**
+ * Convert an amount to base currency and update model fields (CurrencyRate, "AmtName").
+ * @param model
+ * @param DateName conversion date field name
+ * @param SourceAmtName source amount field name
+ * @param AmtName converted amount field name (optional); if null then the converted amount field will not be updated
+ * @param changedColumnName the column that has changed (the controller); optional
+ * @return converted amount or null if error
+ */
+ public static BigDecimal convertBase(SetGetModel model, String DateName,
+ String SourceAmtName, String AmtName, String changedColumnName)
+ {
+ // If Currency changed, reset rate
+ if (changedColumnName != null && "C_Currency_ID".equalsIgnoreCase(changedColumnName))
+ {
+ model.set_AttrValue("CurrencyRate", Env.ZERO);
+ }
+
+ // Source Amount
+ BigDecimal sourceAmt = SetGetUtil.get_AttrValueAsBigDecimal(model, SourceAmtName);
+ if (sourceAmt == null || sourceAmt.signum() == 0)
+ {
+ if (AmtName != null)
+ {
+ model.set_AttrValue(AmtName, Env.ZERO);
+ }
+ return Env.ZERO;
+ }
+
+ // AD_Client_ID
+ int AD_Client_ID = SetGetUtil.get_AttrValueAsInt(model, "AD_Client_ID");
+
+ // Currency To
+ int C_Currency_ID_To = MClient.get(model.getCtx(), AD_Client_ID).getAcctSchema().getC_Currency_ID();
+ //~ model.set_AttrValue("C_Currency_ID_To", Integer.valueOf(C_Currency_ID_To));
+
+ // Get Rate
+ BigDecimal rate = SetGetUtil.get_AttrValueAsBigDecimal(model, "CurrencyRate");
+ if (rate == null || rate.signum() == 0)
+ {
+ int AD_Org_ID = SetGetUtil.get_AttrValueAsInt(model, "AD_Client_ID");
+ Timestamp ConvDate = SetGetUtil.get_AttrValueAsDate(model, DateName);
+ int C_Currency_ID = SetGetUtil.get_AttrValueAsInt(model, "C_Currency_ID");
+ if (C_Currency_ID == C_Currency_ID_To)
+ {
+ rate = Env.ONE;
+ }
+ else
+ {
+ int C_ConversionType_ID = SetGetUtil.get_AttrValueAsInt(model, "C_ConversionType_ID");
+ rate = MConversionRate.getRate (C_Currency_ID, C_Currency_ID_To,
+ ConvDate, C_ConversionType_ID,
+ AD_Client_ID, AD_Org_ID);
+ if (rate == null)
+ { // NoCurrencyConversion
+ throw new NoCurrencyConversionException(C_Currency_ID, C_Currency_ID_To,
+ ConvDate, C_ConversionType_ID,
+ AD_Client_ID, AD_Org_ID);
+ }
+ }
+ }
+ model.set_AttrValue("CurrencyRate", rate);
+
+ // Calculate converted amount
+ BigDecimal amt = sourceAmt.multiply(rate);
+ int stdPrecision = MCurrency.getStdPrecision(model.getCtx(), C_Currency_ID_To);
+ amt = amt.setScale(stdPrecision, RoundingMode.HALF_UP);
+
+ // Update model
+ if (AmtName != null)
+ model.set_AttrValue(AmtName, amt);
+ // Return amt
+ if (CLogMgt.isLevelFine()) s_log.fine("amt=" + sourceAmt + " * " + rate + "=" + amt + ", scale=" + stdPrecision);
+ return amt;
+ } // convert
+}
diff --git a/org.adempiere.base/src/org/compiere/model/MDepreciation.java b/org.adempiere.base/src/org/compiere/model/MDepreciation.java
new file mode 100644
index 0000000000..0baf94cd4a
--- /dev/null
+++ b/org.adempiere.base/src/org/compiere/model/MDepreciation.java
@@ -0,0 +1,462 @@
+package org.compiere.model;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.sql.ResultSet;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+import org.compiere.model.Query;
+import org.compiere.util.CCache;
+import org.compiere.util.CLogMgt;
+import org.compiere.util.CLogger;
+import org.compiere.util.Env;
+import org.idempiere.fa.exceptions.AssetNotImplementedException;
+import org.idempiere.fa.exceptions.AssetNotSupportedException;
+
+
+/**
+ * Depreciation Engine (eg. SL, ARH_VAR ...)
+ * @author Teo Sarca, SC ARHIPAC SERVICE SRL
+ */
+public class MDepreciation extends X_A_Depreciation
+{
+ private static final long serialVersionUID = 1L;
+
+ /** Standard Constructor */
+ public MDepreciation (Properties ctx, int A_Depreciation_ID, String trxName)
+ {
+ super (ctx, A_Depreciation_ID, trxName);
+ } // MDepreciation
+
+ /**
+ * Load Constructor
+ * @param ctx context
+ * @param rs result set record
+ */
+ public MDepreciation (Properties ctx, ResultSet rs, String trxName)
+ {
+ super (ctx, rs, trxName);
+ } // MDepreciation
+
+ /** Cache */
+ private static CCache
+ s_cache = new CCache(Table_Name, 5);
+ /** Cache for type */
+ private static CCache
+ s_cache_forType = new CCache(Table_Name+"_DepreciationType", 5);
+ /** Static logger */
+ private static Logger s_log = CLogger.getCLogger(MDepreciation.class);
+ /** The accuracy of calculation on depreciation */
+ private final static int m_precision = 2;
+
+ /* Constrants */
+ private static final BigDecimal BD_12 = BigDecimal.valueOf(12);
+
+ private static void addToCache(MDepreciation depr)
+ {
+ if (depr == null)
+ {
+ return ;
+ }
+
+ s_cache.put(depr.get_ID(), depr);
+ String key = "" + depr.getAD_Client_ID() + "_" + depr.getDepreciationType();
+ s_cache_forType.put(key, depr);
+ }
+
+ /**
+ * Get Depreciation method
+ * @param ctx
+ * @param A_Depreciation_ID depreciation id
+ */
+ public static MDepreciation get(Properties ctx, int A_Depreciation_ID)
+ {
+ MDepreciation depr = s_cache.get(A_Depreciation_ID);
+ if (depr != null)
+ {
+ return depr;
+ }
+ depr = new MDepreciation(ctx, A_Depreciation_ID, null);
+ if (depr.get_ID() > 0)
+ {
+ addToCache(depr);
+ }
+ else
+ {
+ depr = null;
+ }
+ return depr;
+ } // get
+
+ /**
+ * Get Depreciation method
+ * @param ctx
+ * @param depreciationType depreciation type (e.g. SL)
+ */
+ public static MDepreciation get(Properties ctx, String depreciationType)
+ {
+ int AD_Client_ID = Env.getAD_Client_ID(ctx);
+ String key = "" + AD_Client_ID + "_" + depreciationType;
+ MDepreciation depr = s_cache_forType.get(key);
+ if (depr != null)
+ {
+ return depr;
+ }
+
+ final String whereClause = COLUMNNAME_DepreciationType+"=?"
+ +" AND AD_Client_ID IN (0,?)";
+ depr = new Query(ctx, Table_Name, whereClause, null)
+ .setOrderBy("AD_Client_ID DESC")
+ .setParameters(new Object[]{depreciationType, AD_Client_ID})
+ .firstOnly();
+ addToCache(depr);
+ return depr;
+ } // get
+
+ /**
+ * Returns the precision of calculation of depreciation
+ * @return accuracy of calculation of depreciation
+ */
+ public static int getPrecision()
+ {
+ return m_precision;
+ }
+
+ /**
+ * Needs to be adjusted in last month's amortization
+ */
+ public boolean requireLastPeriodAdjustment()
+ {
+ return !"ARH_ZERO".equals(getDepreciationType());
+ }
+
+ /**
+ * Calculate the value of depreciation over time
+ * @param assetwk - Fixed Assets worksheet
+ * @param assetAcct - FA default accounting elements
+ * @param A_Current_Period - current period
+ * @param Accum_Dep accumulated depreciation until present period
+ * @return amortized value
+ */
+ public BigDecimal invoke(MDepreciationWorkfile assetwk, MAssetAcct assetAcct,
+ int A_Current_Period, BigDecimal Accum_Dep)
+ {
+ String depreciationType = getDepreciationType();
+ BigDecimal retValue = null;
+ //~ int offset = getFixMonthOffset();
+ //~ A_Current_Period += offset;
+
+ if(CLogMgt.isLevelFinest())
+ {
+ log.fine("Entering: DepreciationType=" + depreciationType
+ + ", assetwk=" + assetwk+ ", assetacct=" + assetAcct
+ + ", A_Current_Period=" + A_Current_Period //+ " (offset=" + offset + ")"
+ + ", Accum_Dep=" + Accum_Dep
+ );
+ }
+
+ if (!canInvoke(assetwk, assetAcct, A_Current_Period, Accum_Dep))
+ {
+ return BigDecimal.ZERO;
+ }
+ if (depreciationType.equalsIgnoreCase("SL"))
+ {
+ retValue = apply_SL(assetwk, assetAcct, A_Current_Period, Accum_Dep);
+ }
+ else if (depreciationType.equalsIgnoreCase("ARH_VAR"))
+ {
+ retValue = apply_ARH_VAR(assetwk, assetAcct, A_Current_Period, Accum_Dep);
+ }
+ else if (depreciationType.equalsIgnoreCase("ARH_AD1"))
+ {
+ retValue = apply_ARH_AD1(assetwk, assetAcct, A_Current_Period, Accum_Dep);
+ }
+ else if (depreciationType.equalsIgnoreCase("ARH_AD2"))
+ {
+ retValue = apply_ARH_AD2(assetwk, assetAcct, A_Current_Period, Accum_Dep);
+ }
+ else if (depreciationType.equalsIgnoreCase("ARH_ZERO"))
+ {
+ retValue = apply_ARH_ZERO(assetwk, assetAcct, A_Current_Period, Accum_Dep);
+ }
+ else
+ {
+ throw new AssetNotSupportedException(COLUMNNAME_DepreciationType, depreciationType);
+ }
+ //
+ if (retValue == null)
+ {
+ retValue = BigDecimal.ZERO;
+ }
+ retValue = retValue.setScale(getPrecision(), RoundingMode.HALF_UP);
+ //
+ if(CLogMgt.isLevelFinest()) log.fine("Leaving: retValue=" + retValue);
+ return retValue;
+ } // invoke
+
+ /**
+ * Check if the method can be invoked to give parameters
+ * @param assetwk
+ * @param assetAcct
+ * @param A_Current_Period between 0 to UseLifeMonths - 1
+ * @param Accum_Dep
+ */
+ public boolean canInvoke(MDepreciationWorkfile assetwk, MAssetAcct assetAcct,
+ int A_Current_Period, BigDecimal Accum_Dep)
+ {
+ //~ MDepreciationWorkfile wk = MDepreciationWorkfile.get(getCtx(), A_Asset_ID, PostingType);
+ if (assetwk == null)
+ {
+ log.warning("@NotFound@ @A_Depreciation_Workfile_ID@");
+ return false;
+ }
+ //TODO review this method - red1
+ int offset = 0 ; //getFixMonthOffset(); this field does not exist in AD nor DB but exists in X_. - red1
+ int lifePeriods = assetwk.getUseLifeMonths(assetwk.isFiscal());
+ boolean ok = (offset <= A_Current_Period);
+
+ if(CLogMgt.isLevelFinest()) log.finest("A_Current_Period=" + A_Current_Period + ", lifePeriods=" + lifePeriods + " (offset=" + offset + ") ==> OK=" + ok);
+ return ok;
+ } // canInvoke
+
+ /**
+ * Without depreciation
+ * @param A_Asset_ID Assets IDs (ignored)
+ * @param A_Current_Period current period (in months, between 0 and UseLifeMonths - 1) (ignored)
+ * @param PostingType posting type (eg. A - Actual, S - Statistics ...) (ignored)
+ * @param A_Asset_Acct_ID FA accounting IDs (see table A_Asset_Acct) (ignored)
+ * @param Accum_Dep Accumulated depreciation from this method (ignored)
+ * @return Env.ZERO
+ */
+ private BigDecimal apply_ARH_ZERO (MDepreciationWorkfile wk, MAssetAcct assetAcct,
+ int A_Current_Period, BigDecimal Accum_Dep)
+ {
+ return Env.ZERO;
+ }
+
+ /** Linear depreciation regime
+ * @param A_Asset_ID Assets IDs
+ * @param A_Current_Period current period (in months, between 0 and UseLifeMonths - 1)
+ * @param PostingType posting type (eg. A - Actual, S - Statistics ...)
+ * @param A_Asset_Acct_ID FA accounting IDs (see table A_Asset_Acct)
+ * @param Accum_Dep Accumulated depreciation from this method
+ * @return depreciation for the current month
+ */
+ private BigDecimal apply_SL (MDepreciationWorkfile wk, MAssetAcct assetAcct,
+ int A_Current_Period, BigDecimal Accum_Dep)
+ {
+ BigDecimal remainingPeriods = new BigDecimal(wk.getRemainingPeriods(A_Current_Period - 1));
+ BigDecimal remainingAmt = wk.getRemainingCost(Accum_Dep);
+ BigDecimal amtPerPeriod = Env.ZERO;
+ if (remainingPeriods.signum() != 0)
+ {
+ amtPerPeriod = remainingAmt.divide(remainingPeriods, getPrecision(), RoundingMode.HALF_UP);
+ }
+
+ if(CLogMgt.isLevelFinest())
+ {
+ log.finest("currentPeriod=" + A_Current_Period + ", remainingAmt=" + remainingAmt + ", remainingPeriods=" + remainingPeriods + " => amtPerPeriod=" + amtPerPeriod);
+ }
+
+ return amtPerPeriod;
+ }
+
+ /**
+ * Accelerated depreciation regime
+ * @param A_Current_Period current period (in months, between 0 and UseLifeMonths - 1)
+ * @param PostingType posting type (eg. A - Actual, S - Statistics ...)
+ * @param A_Asset_Acct_ID FA accounting IDs (see table A_Asset_Acct)
+ * @param Accum_Dep Accumulated depreciation from this method
+ * @return depreciation for the current month
+ */
+ private BigDecimal apply_ARH_VAR (MDepreciationWorkfile wk, MAssetAcct acct,
+ int A_Current_Period, BigDecimal Accum_Dep)
+ {
+ BigDecimal amt = wk.getActualCost();
+ BigDecimal varPercent = acct.getA_Depreciation_Variable_Perc(wk.isFiscal()).setScale(getPrecision(), RoundingMode.HALF_UP);
+ //~ int lifePeriods = wk.getUseLifeMonths(wk.isFiscal());
+ BigDecimal assetExp = BigDecimal.ZERO;
+
+ // First time in first year
+ if (A_Current_Period == 0)
+ {
+ assetExp = amt.multiply(varPercent);
+ }
+ // Periods of the first year (without first)
+ else if (A_Current_Period < 12)
+ {
+ // do nothing;
+ }
+ // Following periods
+ else
+ {
+ BigDecimal remainingAmt = wk.getRemainingCost(Accum_Dep);
+ BigDecimal remainingPeriods = new BigDecimal(wk.getRemainingPeriods(A_Current_Period));
+ assetExp = remainingAmt.divide(remainingPeriods, getPrecision(), RoundingMode.HALF_UP);
+ // logging
+ if (CLogMgt.isLevelFinest()) {
+ log.fine("remainingAmt=" + remainingAmt + ", remainingPeriods=" + remainingPeriods+ " => assetExp=" + assetExp);
+ }
+ }
+
+ return assetExp;
+ }
+
+
+ /** Digressive depreciation regime (AD1)
+ * @param A_Current_Period current period (in months, between 0 and UseLifeMonths - 1)
+ * @param PostingType posting type (eg. A - Actual, S - Statistics ...)
+ * @param A_Asset_Acct_ID FA accounting IDs (see table A_Asset_Acct)
+ * @param Accum_Dep Accumulated depreciation from this method
+ * @return depreciation for the current month
+ * TODO RE TEST IT!
+ */
+ private BigDecimal apply_ARH_AD1(MDepreciationWorkfile wk, MAssetAcct assetAcct, int A_Current_Period,BigDecimal Accum_Dep)
+ {
+ //~ /** Current Worksheet */
+ //~ MDepreciationWorkfile wk = MDepreciationWorkfile.get(getCtx(), A_Asset_ID, PostingType);
+
+ /** FAs' value = acquisition value - the amount recovered */
+ BigDecimal assetAmt = wk.getActualCost();
+ /** Life in months */
+ int A_Life_Period = wk.getA_Life_Period();
+ /** Year = integer part of (current period / 12) => first year will be 0 */
+ int A_Current_Year = (int)(A_Current_Period / 12);
+ /** Life in years = integer part of (the life in months / 12) => first year will be 0 */
+ int A_Life_Year = (int)(A_Life_Period / 12);
+ //~ /** Number of years of use remaining (including current year) */
+ //~ int A_RemainingLife_Year = A_Life_Year - A_Current_Year;
+
+
+ /** Coefficient K */
+ /* @win : looks like a country specific requirement
+ BigDecimal coef_K = get_AD_K(A_Life_Year);
+ */
+
+ /** Linear damping coefficient for one year = 1 / total number of years */
+ BigDecimal coef_sl = BigDecimal.ONE.divide(new BigDecimal(A_Life_Year), getPrecision() + 2, RoundingMode.DOWN);
+ /** Degressive damping coefficient for one year = one-year linear depreciation * coeficient K */
+ //BigDecimal coef_ad1 = coef_sl.multiply(coef_K); //commented by @win
+ BigDecimal coef_ad1 = coef_sl.multiply(new BigDecimal(2.0)); //added by @win
+
+ /** AD2 */
+ //~ BigDecimal DUR = BD_100.multiply(
+
+ // logging
+ if (CLogMgt.isLevelFinest()) {
+ log.fine("assetAmt=" + assetAmt + ", A_Life_Period=" + A_Life_Period);
+ log.fine("A_Current_Year=" + A_Current_Year + ", A_Life_Year=" + A_Life_Year);
+ //log.fine("coef_K=" + coef_K + ", coef_sl=" + coef_sl + ", coef_ad1=" + coef_ad1); //commented out by @win
+ }
+
+ /** Depreciation for the current year, is calculated below */
+ BigDecimal amtPerYear = BigDecimal.ZERO;
+ /** They went on linear depreciation */
+ boolean is_SL = false;
+ /** Year linear depreciation = depreciation remaining / number of years remaining (including this year) */
+ BigDecimal amt_sl = BigDecimal.ZERO;
+ /** Remaining depreciation */
+ BigDecimal amt_r = assetAmt;
+
+ for (int curr_year = 0; curr_year <= A_Current_Year; curr_year++) {
+ if (!is_SL) {
+ /** Number of years of use remaining (including current year) */
+ int A_RemainingLife_Year = A_Life_Year - curr_year;
+ /** Degressive current year depreciation */
+ BigDecimal amt_ad1 = amt_r.multiply(coef_ad1);
+ /** Year linear depreciation = depreciation remaining / number of years remaining (including this year) */
+ amt_sl = amt_r.divide(new BigDecimal(A_RemainingLife_Year), getPrecision(), RoundingMode.HALF_UP);
+ // logging
+ if (CLogMgt.isLevelFinest()) {
+ s_log.fine("amt_r=" + amt_r + ", amt_ad1=amt_r*coef_ad1=" + amt_ad1 + ", amt_sl=amt_r/A_RemainingLife_Year=" + amt_sl + ", A_Current_Year=" + A_Current_Year + ", A_RemainingLife_Year=" + A_RemainingLife_Year);
+ }
+
+ /** If the first year or if the value depreciation value depreciation degressive more linear ... */
+ if (curr_year == 0 || amt_ad1.compareTo(amt_sl) >= 0) {
+ amtPerYear = amt_ad1;
+ }
+ else {
+ amtPerYear = amt_sl;
+ is_SL = true;
+ s_log.fine("*** PASS IT ON linear amt_sl= " + amt_sl + " ***");
+ }
+ }
+ else {
+ amtPerYear = amt_sl;
+ s_log.fine("* linear *");
+ }
+
+ amt_r = amt_r.subtract(amtPerYear);
+ if (CLogMgt.isLevelFinest()) s_log.fine("year=" + curr_year + ", amtPerYear=" + amtPerYear + ", amt_r=" + amt_r); //logging
+ }
+ if (CLogMgt.isLevelFinest()) s_log.fine("amt_r=" + amt_r + ", amtPerYear=" + amtPerYear); //logging
+
+ /** Damping value for the current month */
+ BigDecimal assetExp = getPeriodExp(A_Current_Period, amtPerYear);
+
+ /** Depreciation refund */
+ if (CLogMgt.isLevelFinest()) log.fine("assetExp=" + assetExp);
+ return assetExp;
+ }
+
+ /**
+ *
+ */
+ private BigDecimal apply_ARH_AD2(MDepreciationWorkfile wk, MAssetAcct assetAcct, int A_Current_Period,BigDecimal Accum_Dep)
+ {
+ throw new AssetNotImplementedException("AD2");
+ }
+
+ /** For depreciation regime skimmed returns coefficient K depending on the life of FAs
+ * @param A_Life_Year life in years
+ * @return coeficient K degressive method for
+ * @see #apply_ARH_AD1(int, int, String, int, BigDecimal)
+ */
+ private static BigDecimal get_AD_K(int A_Life_Year)
+ {
+ if (A_Life_Year < 2) {
+ throw new IllegalArgumentException("@A_Life_Year@ = " + A_Life_Year + " < 2");
+ }
+ // A_Life_Year in [2, 5]
+ else if (A_Life_Year <= 5) {
+ return new BigDecimal(1.5);
+ }
+ // A_Life_Year in (5, 10]
+ else if (A_Life_Year <= 10) {
+ return new BigDecimal(2.0);
+ }
+ // A_Life_Year in (10, infinit)
+ else {
+ return new BigDecimal(2.5);
+ }
+ }
+
+ /** Calculate the value of depreciation over a month (period). In the last month of the year we add errors from the adjustment calculation
+ * @param A_Current_Period current month's index
+ * @param amtPerYear value of depreciation per year
+ * @return rounded value to the nearest month/decimal getPrecision ()
+ */
+ protected BigDecimal getPeriodExp(int A_Current_Period, BigDecimal amtPerYear)
+ {
+ /** Monthly amount */
+ BigDecimal amtPerMonth = amtPerYear.divide(BD_12, getPrecision(), RoundingMode.HALF_UP);
+ /** Value adjustment */
+ BigDecimal adj = BigDecimal.ZERO;
+ /** The amount a month (with adjustments) */
+ BigDecimal assetExp = amtPerMonth;
+
+ // if last month of the year, calculate the adjustment
+ // NOTE: All adjusted value shall be rounded to getPrecision () decimal
+ if (A_Current_Period % 12 == 11)
+ {
+ adj = amtPerYear.subtract(amtPerMonth.multiply(BD_12));
+ assetExp = assetExp.add(adj).setScale(getPrecision(), RoundingMode.HALF_UP);
+ }
+
+ if (CLogMgt.isLevelFinest())
+ {
+ log.fine("amtPerYear=" + amtPerYear + ", amtPerMonth=" + amtPerMonth + ", adj=" + adj + " => assetExp=" + assetExp);
+ }
+ return assetExp;
+ }
+}
diff --git a/org.adempiere.base/src/org/compiere/model/MDepreciationBuild.java b/org.adempiere.base/src/org/compiere/model/MDepreciationBuild.java
new file mode 100644
index 0000000000..f1dca0be17
--- /dev/null
+++ b/org.adempiere.base/src/org/compiere/model/MDepreciationBuild.java
@@ -0,0 +1,28 @@
+package org.compiere.model;
+
+import java.sql.ResultSet;
+import java.util.Properties;
+
+
+/** Depreciation Build
+ * @author Teo Sarca, SC ARHIPAC SRL
+ * @version $Id$ -- Release 2.5.3a - 2006-06-22 18:03:22.896
+ */
+public class MDepreciationBuild extends X_A_Depreciation_Build
+{
+ /** Standard Constructor */
+ public MDepreciationBuild (Properties ctx, int A_Depreciation_Build_ID, String trxName)
+ {
+ super (ctx, A_Depreciation_Build_ID, trxName);
+ /** if (A_Depreciation_Build_ID == 0)
+ {
+ setA_Depreciation_Build_ID (0);
+ }
+ */
+ }
+ /** Load Constructor */
+ public MDepreciationBuild (Properties ctx, ResultSet rs, String trxName)
+ {
+ super (ctx, rs, trxName);
+ }
+}
diff --git a/org.adempiere.base/src/org/compiere/model/MDepreciationConvention.java b/org.adempiere.base/src/org/compiere/model/MDepreciationConvention.java
new file mode 100644
index 0000000000..e05c82ebfc
--- /dev/null
+++ b/org.adempiere.base/src/org/compiere/model/MDepreciationConvention.java
@@ -0,0 +1,116 @@
+package org.compiere.model;
+import java.sql.*;
+import java.math.*;
+import java.util.*;
+import java.util.logging.*;
+import org.compiere.model.*;
+import org.compiere.util.*;
+
+/** Convention for the first year of depreciation (ex. FMCON, FYCON ...)
+ * @author Teo Sarca, SC Arhipac SRL
+ * @version $Id$
+ */
+public class MDepreciationConvention extends X_A_Depreciation_Convention
+{
+ /**
+ * Default Constructor
+ * @param ctx context
+ * @param A_Depreciation_Convention_ID id
+ * @param trxName transaction name
+ */
+ public MDepreciationConvention(Properties ctx, int A_Depreciation_Convention_ID, String trxName)
+ {
+ super (ctx, A_Depreciation_Convention_ID, trxName);
+ //~ if (A_Depreciation_Convention_ID == 0)
+ //~ {
+ //~ }
+ } // MDepreciationConvention
+
+ /**
+ * Load Constructor
+ * @param ctx context
+ * @param rs result set record
+ */
+ public MDepreciationConvention (Properties ctx, ResultSet rs, String trxName)
+ {
+ super (ctx, rs, trxName);
+ } // MDepreciationConvention
+
+ /** Cache */
+ private static CCache s_cache = new CCache("A_Depreciation_Convention", 5);
+ //~ /** Static logger */
+ //~ private static Logger s_log = CLogger.getCLogger(MDepreciationConvention.class);
+
+ public static MDepreciationConvention get(Properties ctx, int A_Depreciation_Convention_ID) {
+ Integer key = Integer.valueOf(A_Depreciation_Convention_ID);
+ MDepreciationConvention conv = s_cache.get(key);
+ if (conv != null) {
+ return conv;
+ }
+ conv = new MDepreciationConvention(ctx, A_Depreciation_Convention_ID, null);
+ if (conv.get_ID() > 0) {
+ s_cache.put(key, conv);
+ } else {
+ conv = null;
+ }
+ return conv;
+ } // get
+
+ /** */
+ public BigDecimal invoke (MDepreciationWorkfile assetwk, MAssetAcct assetAcct, int Flag, int Period) {
+ return invoke(assetwk.getA_Asset_ID(), assetAcct.getPostingType(), assetAcct.get_ID(), Flag, Period);
+ }
+
+ /** */
+ public BigDecimal invoke (int A_Asset_ID, String PostingType, int A_Asset_Acct_ID, int Flag, int Period) {
+ String conventionType = getConventionType();
+ BigDecimal retValue = null;
+
+ if(CLogMgt.isLevelFine())
+ log.fine("Entering: ConventionType=" + conventionType
+ + "A_Asset_ID=" + A_Asset_ID + ", PostingType=" + PostingType + ", A_Asset_Acct_ID=" + A_Asset_Acct_ID
+ + ", Flag=" + Flag + ", Period=" + Period
+ );
+
+ if (conventionType.equalsIgnoreCase("FMCON")) {
+ return apply_FMCON(A_Asset_ID, PostingType, A_Asset_Acct_ID, Flag, Period);
+ }
+ else {
+ String sql = "{ ? = call "+ conventionType + "(?, ?, ?, ?, ?) }";
+ CallableStatement cs = null;
+ try {
+ cs = DB.prepareCall(sql);
+ cs.registerOutParameter(1, java.sql.Types.DECIMAL);
+ cs.setInt(2, A_Asset_ID);
+ cs.setString(3, PostingType);
+ cs.setInt(4, A_Asset_Acct_ID);
+ cs.setInt(5, Flag);
+ cs.setInt(6, Period);
+ cs.execute();
+ retValue = cs.getBigDecimal(1);
+ cs.close();
+ } catch (Exception e) {
+ log.log(Level.SEVERE, sql, e);
+ }
+ finally {
+ try {
+ if (cs != null) cs.close();
+ } catch (SQLException e) {
+ log.log(Level.FINEST, "Error", e);
+ }
+ cs = null;
+ }
+ }
+ //
+ if (retValue == null) {
+ retValue = BigDecimal.ZERO;
+ }
+ //
+ if(CLogMgt.isLevelFine()) log.fine("Leaving: retValue=" + retValue);
+ return retValue;
+ }
+
+ public BigDecimal apply_FMCON(int A_Asset_ID, String PostingType, int A_Asset_Acct_ID, int Flag, int Period) {
+ return BigDecimal.ONE;
+ }
+}
diff --git a/org.adempiere.base/src/org/compiere/model/MDepreciationEntry.java b/org.adempiere.base/src/org/compiere/model/MDepreciationEntry.java
new file mode 100644
index 0000000000..ba31fcca2c
--- /dev/null
+++ b/org.adempiere.base/src/org/compiere/model/MDepreciationEntry.java
@@ -0,0 +1,373 @@
+package org.compiere.model;
+
+import java.io.File;
+import java.math.BigDecimal;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.logging.Level;
+
+import org.adempiere.exceptions.AdempiereException;
+import org.compiere.model.MAcctSchema;
+import org.compiere.model.MClient;
+import org.compiere.model.MPeriod;
+import org.compiere.model.ModelValidationEngine;
+import org.compiere.model.ModelValidator;
+import org.compiere.model.Query;
+import org.compiere.process.DocAction;
+import org.compiere.process.DocumentEngine;
+import org.compiere.util.DB;
+import org.compiere.util.Env;
+import org.compiere.util.TimeUtil;
+import org.compiere.util.Trx;
+import org.compiere.util.TrxRunnable;
+import org.idempiere.fa.exceptions.AssetArrayException;
+import org.idempiere.fa.exceptions.AssetException;
+
+
+/**
+ * Depreciation Entry
+ * @author Teo Sarca, SC ARHIPAC SERVICE SRL
+ */
+public class MDepreciationEntry extends X_A_Depreciation_Entry
+implements DocAction
+{
+ private static final long serialVersionUID = 1L;
+
+ /** Standard Constructor */
+ public MDepreciationEntry(Properties ctx, int A_Depreciation_Entry_ID, String trxName)
+ {
+ super (ctx, A_Depreciation_Entry_ID, trxName);
+ if (A_Depreciation_Entry_ID == 0)
+ {
+ MAcctSchema acctSchema = MClient.get(getCtx()).getAcctSchema();
+ setC_AcctSchema_ID(acctSchema.get_ID());
+ setC_Currency_ID(acctSchema.getC_Currency_ID());
+ setA_Entry_Type (A_ENTRY_TYPE_Depreciation); // TODO: workaround
+ setPostingType (POSTINGTYPE_Actual); // A
+ setProcessed (false);
+ setProcessing (false);
+ setPosted(false);
+ }
+ }
+
+ /** Load Constructor */
+ public MDepreciationEntry (Properties ctx, ResultSet rs, String trxName)
+ {
+ super (ctx, rs, trxName);
+ }
+
+
+ protected boolean beforeSave(boolean newRecord)
+ {
+ setC_Period_ID();
+ return true;
+ }
+
+
+ protected boolean afterSave(boolean newRecord, boolean success)
+ {
+ if (!success)
+ {
+ return false;
+ }
+ if (!isProcessed() && (newRecord || is_ValueChanged(COLUMNNAME_DateAcct)))
+ {
+ selectLines();
+ }
+ return true;
+ }
+
+
+ protected boolean afterDelete(boolean success)
+ {
+ if (!success)
+ {
+ return false;
+ }
+
+ unselectLines();
+ return true;
+ }
+
+ public void setC_Period_ID()
+ {
+ MPeriod period = MPeriod.get(getCtx(), getDateAcct(), getAD_Org_ID());
+ if (period == null)
+ {
+ throw new AdempiereException("@NotFound@ @C_Period_ID@");
+ }
+ setC_Period_ID(period.get_ID());
+ }
+
+ private void unselectLines()
+ {
+ String sql = "UPDATE " + MDepreciationExp.Table_Name + " SET "
+ + MDepreciationExp.COLUMNNAME_A_Depreciation_Entry_ID + "=NULL "
+ + " WHERE "
+ + MDepreciationExp.COLUMNNAME_A_Depreciation_Entry_ID + "=?";
+ int id = get_ID();
+ if (id <= 0)
+ { // Use old ID is current ID is missing (i.e. object was deleted)
+ id = get_IDOld();
+ }
+ int no = DB.executeUpdateEx(sql, new Object[]{id}, get_TrxName());
+ log.fine("Updated #" + no);
+ }
+
+ private void selectLines()
+ {
+ // Reset selected lines:
+ unselectLines();
+ // Select lines:
+ final String sql = "UPDATE " + MDepreciationExp.Table_Name + " SET "
+ + MDepreciationExp.COLUMNNAME_A_Depreciation_Entry_ID + "=?"
+ + " WHERE "
+ + MDepreciationExp.COLUMNNAME_A_Depreciation_Entry_ID + " IS NULL"
+ + " AND TRUNC("+MDepreciationExp.COLUMNNAME_DateAcct+",'MONTH') = ?"
+ + " AND AD_Client_ID=? AND AD_Org_ID=?";
+ ;
+ Timestamp dateAcct = TimeUtil.trunc(getDateAcct(), TimeUtil.TRUNC_MONTH);
+ int no = DB.executeUpdateEx(sql, new Object[]{get_ID(), dateAcct, getAD_Client_ID(), getAD_Org_ID()}, get_TrxName());
+ log.fine("Updated #" + no);
+ }
+
+ /**
+ * Get Lines
+ */
+ public Iterator getLinesIterator(boolean onlyNotProcessed)
+ {
+ final String trxName = get_TrxName();
+ final List