PageRenderTime 71ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/org.adempiere.base/src/org/compiere/model/PO.java

https://bitbucket.org/idempiere/idempiere/
Java | 4737 lines | 3385 code | 295 blank | 1057 comment | 1081 complexity | a1e28fef5a408d013f14412b0a13ea0f MD5 | raw file
  1. /******************************************************************************
  2. * Product: Adempiere ERP & CRM Smart Business Solution *
  3. * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. *
  4. * This program is free software; you can redistribute it and/or modify it *
  5. * under the terms version 2 of the GNU General Public License as published *
  6. * by the Free Software Foundation. This program is distributed in the hope *
  7. * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
  8. * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
  9. * See the GNU General Public License for more details. *
  10. * You should have received a copy of the GNU General Public License along *
  11. * with this program; if not, write to the Free Software Foundation, Inc., *
  12. * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
  13. * For the text or an alternative of this public license, you may reach us *
  14. * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
  15. * or via info@compiere.org or http://www.compiere.org/license.html *
  16. *****************************************************************************/
  17. package org.compiere.model;
  18. import java.io.IOException;
  19. import java.io.ObjectInputStream;
  20. import java.io.Serializable;
  21. import java.io.StringWriter;
  22. import java.math.BigDecimal;
  23. import java.sql.Blob;
  24. import java.sql.Clob;
  25. import java.sql.PreparedStatement;
  26. import java.sql.ResultSet;
  27. import java.sql.SQLException;
  28. import java.sql.Savepoint;
  29. import java.sql.Timestamp;
  30. import java.text.Collator;
  31. import java.util.ArrayList;
  32. import java.util.Comparator;
  33. import java.util.HashMap;
  34. import java.util.Iterator;
  35. import java.util.List;
  36. import java.util.Properties;
  37. import java.util.UUID;
  38. import java.util.logging.Level;
  39. import javax.xml.parsers.DocumentBuilder;
  40. import javax.xml.parsers.DocumentBuilderFactory;
  41. import javax.xml.transform.Transformer;
  42. import javax.xml.transform.TransformerFactory;
  43. import javax.xml.transform.dom.DOMSource;
  44. import javax.xml.transform.stream.StreamResult;
  45. import org.adempiere.base.event.EventManager;
  46. import org.adempiere.base.event.IEventTopics;
  47. import org.adempiere.exceptions.AdempiereException;
  48. import org.adempiere.exceptions.DBException;
  49. import org.adempiere.process.UUIDGenerator;
  50. import org.compiere.Adempiere;
  51. import org.compiere.acct.Doc;
  52. import org.compiere.util.CCache;
  53. import org.compiere.util.CLogMgt;
  54. import org.compiere.util.CLogger;
  55. import org.compiere.util.CacheMgt;
  56. import org.compiere.util.DB;
  57. import org.compiere.util.DisplayType;
  58. import org.compiere.util.Env;
  59. import org.compiere.util.Evaluatee;
  60. import org.compiere.util.Ini;
  61. import org.compiere.util.Language;
  62. import org.compiere.util.Msg;
  63. import org.compiere.util.SecureEngine;
  64. import org.compiere.util.Trace;
  65. import org.compiere.util.Trx;
  66. import org.compiere.util.ValueNamePair;
  67. import org.osgi.service.event.Event;
  68. import org.w3c.dom.Document;
  69. import org.w3c.dom.Element;
  70. /**
  71. * Persistent Object.
  72. * Superclass for actual implementations
  73. *
  74. * @author Jorg Janke
  75. * @version $Id: PO.java,v 1.12 2006/08/09 16:38:47 jjanke Exp $
  76. *
  77. * @author Teo Sarca, SC ARHIPAC SERVICE SRL
  78. * <li>FR [ 1675490 ] ModelValidator on modelChange after events
  79. * <li>BF [ 1704828 ] PO.is_Changed() and PO.is_ValueChanged are not consistent
  80. * <li>FR [ 1720995 ] Add PO.saveEx() and PO.deleteEx() methods
  81. * <li>BF [ 1990856 ] PO.set_Value* : truncate string more than needed
  82. * <li>FR [ 2042844 ] PO.get_Translation improvements
  83. * <li>FR [ 2818369 ] Implement PO.get_ValueAs*(columnName)
  84. * https://sourceforge.net/tracker/?func=detail&aid=2818369&group_id=176962&atid=879335
  85. * <li>BF [ 2849122 ] PO.AfterSave is not rollback on error
  86. * https://sourceforge.net/tracker/?func=detail&aid=2849122&group_id=176962&atid=879332
  87. * <li>BF [ 2859125 ] Can't set AD_OrgBP_ID
  88. * https://sourceforge.net/tracker/index.php?func=detail&aid=2859125&group_id=176962&atid=879332
  89. * <li>BF [ 2866493 ] VTreePanel is not saving who did the node move
  90. * https://sourceforge.net/tracker/?func=detail&atid=879332&aid=2866493&group_id=176962
  91. * @author Teo Sarca, teo.sarca@gmail.com
  92. * <li>BF [ 2876259 ] PO.insertTranslation query is not correct
  93. * https://sourceforge.net/tracker/?func=detail&aid=2876259&group_id=176962&atid=879332
  94. * @author Victor Perez, e-Evolution SC
  95. * <li>[ 2195894 ] Improve performance in PO engine
  96. * <li>http://sourceforge.net/tracker/index.php?func=detail&aid=2195894&group_id=176962&atid=879335
  97. * <li>BF [2947622] The replication ID (Primary Key) is not working
  98. * <li>https://sourceforge.net/tracker/?func=detail&aid=2947622&group_id=176962&atid=879332
  99. */
  100. public abstract class PO
  101. implements Serializable, Comparator<Object>, Evaluatee, Cloneable
  102. {
  103. /**
  104. *
  105. */
  106. private static final long serialVersionUID = -2997116608353367498L;
  107. public static final String LOCAL_TRX_PREFIX = "POSave";
  108. private static final String USE_TIMEOUT_FOR_UPDATE = "org.adempiere.po.useTimeoutForUpdate";
  109. /** default timeout, 300 seconds **/
  110. private static final int QUERY_TIME_OUT = 300;
  111. /**
  112. * Set Document Value Workflow Manager
  113. * @param docWFMgr mgr
  114. */
  115. public static void setDocWorkflowMgr (DocWorkflowMgr docWFMgr)
  116. {
  117. s_docWFMgr = docWFMgr;
  118. s_log.config (s_docWFMgr.toString());
  119. } // setDocWorkflowMgr
  120. /** Document Value Workflow Manager */
  121. private static DocWorkflowMgr s_docWFMgr = null;
  122. /** User Maintained Entity Type */
  123. static public final String ENTITYTYPE_UserMaintained = "U";
  124. /** Dictionary Maintained Entity Type */
  125. static public final String ENTITYTYPE_Dictionary = "D";
  126. /**************************************************************************
  127. * Create New Persistent Object
  128. * @param ctx context
  129. */
  130. public PO (Properties ctx)
  131. {
  132. this (ctx, 0, null, null);
  133. } // PO
  134. /**
  135. * Create & Load existing Persistent Object
  136. * @param ID The unique ID of the object
  137. * @param ctx context
  138. * @param trxName transaction name
  139. */
  140. public PO (Properties ctx, int ID, String trxName)
  141. {
  142. this (ctx, ID, trxName, null);
  143. } // PO
  144. /**
  145. * Create & Load existing Persistent Object.
  146. * @param ctx context
  147. * @param rs optional - load from current result set position (no navigation, not closed)
  148. * if null, a new record is created.
  149. * @param trxName transaction name
  150. */
  151. public PO (Properties ctx, ResultSet rs, String trxName)
  152. {
  153. this (ctx, 0, trxName, rs);
  154. } // PO
  155. /**
  156. * Create & Load existing Persistent Object.
  157. * <pre>
  158. * You load
  159. * - an existing single key record with new PO (ctx, Record_ID)
  160. * or new PO (ctx, Record_ID, trxName)
  161. * or new PO (ctx, rs, get_TrxName())
  162. * - a new single key record with new PO (ctx, 0)
  163. * - an existing multi key record with new PO (ctx, rs, get_TrxName())
  164. * - a new multi key record with new PO (ctx, null)
  165. * The ID for new single key records is created automatically,
  166. * you need to set the IDs for multi-key records explicitly.
  167. * </pre>
  168. * @param ctx context
  169. * @param ID the ID if 0, the record defaults are applied - ignored if re exists
  170. * @param trxName transaction name
  171. * @param rs optional - load from current result set position (no navigation, not closed)
  172. */
  173. public PO (Properties ctx, int ID, String trxName, ResultSet rs)
  174. {
  175. p_ctx = ctx != null ? ctx : Env.getCtx();
  176. m_trxName = trxName;
  177. p_info = initPO(ctx);
  178. if (p_info == null || p_info.getTableName() == null)
  179. throw new IllegalArgumentException ("Invalid PO Info - " + p_info);
  180. //
  181. int size = p_info.getColumnCount();
  182. m_oldValues = new Object[size];
  183. m_newValues = new Object[size];
  184. m_setErrors = new ValueNamePair[size];
  185. if (rs != null)
  186. load(rs); // will not have virtual columns
  187. else
  188. load(ID, trxName);
  189. } // PO
  190. /**
  191. * Create New PO by Copying existing (key not copied).
  192. * @param ctx context
  193. * @param source source object
  194. * @param AD_Client_ID client
  195. * @param AD_Org_ID org
  196. */
  197. public PO (Properties ctx, PO source, int AD_Client_ID, int AD_Org_ID)
  198. {
  199. this (ctx, 0, null, null); // create new
  200. //
  201. if (source != null)
  202. copyValues (source, this);
  203. setAD_Client_ID(AD_Client_ID);
  204. setAD_Org_ID(AD_Org_ID);
  205. } // PO
  206. /** Logger */
  207. protected transient CLogger log = CLogger.getCLogger (getClass());
  208. /** Static Logger */
  209. private static CLogger s_log = CLogger.getCLogger (PO.class);
  210. /** Context */
  211. protected Properties p_ctx;
  212. /** Model Info */
  213. protected volatile POInfo p_info = null;
  214. /** Original Values */
  215. private Object[] m_oldValues = null;
  216. /** New Values */
  217. private Object[] m_newValues = null;
  218. /** Errors when setting */
  219. private ValueNamePair[] m_setErrors = null;
  220. /** Record_IDs */
  221. private Object[] m_IDs = new Object[] {I_ZERO};
  222. /** Key Columns */
  223. private String[] m_KeyColumns = null;
  224. /** Create New for Multi Key */
  225. private boolean m_createNew = false;
  226. /** Attachment with entries */
  227. private MAttachment m_attachment = null;
  228. /** Deleted ID */
  229. private int m_idOld = 0;
  230. /** Custom Columns */
  231. private HashMap<String,String> m_custom = null;
  232. /** Attributes */
  233. private HashMap<String,Object> m_attributes = null;
  234. /** Zero Integer */
  235. protected static final Integer I_ZERO = new Integer(0);
  236. /** Accounting Columns */
  237. private ArrayList <String> s_acctColumns = null;
  238. /** Trifon - Indicates that this record is created by replication functionality.*/
  239. private boolean m_isReplication = false;
  240. /** Access Level S__ 100 4 System info */
  241. public static final int ACCESSLEVEL_SYSTEM = 4;
  242. /** Access Level _C_ 010 2 Client info */
  243. public static final int ACCESSLEVEL_CLIENT = 2;
  244. /** Access Level __O 001 1 Organization info */
  245. public static final int ACCESSLEVEL_ORG = 1;
  246. /** Access Level SCO 111 7 System shared info */
  247. public static final int ACCESSLEVEL_ALL = 7;
  248. /** Access Level SC_ 110 6 System/Client info */
  249. public static final int ACCESSLEVEL_SYSTEMCLIENT = 6;
  250. /** Access Level _CO 011 3 Client shared info */
  251. public static final int ACCESSLEVEL_CLIENTORG = 3;
  252. /**
  253. * Initialize and return PO_Info
  254. * @param ctx context
  255. * @return POInfo
  256. */
  257. abstract protected POInfo initPO (Properties ctx);
  258. /**
  259. * Get Table Access Level
  260. * @return Access Level
  261. */
  262. abstract protected int get_AccessLevel();
  263. /**
  264. * String representation
  265. * @return String representation
  266. */
  267. public String toString()
  268. {
  269. StringBuilder sb = new StringBuilder("PO[")
  270. .append(get_WhereClause(true)).append("]");
  271. return sb.toString();
  272. } // toString
  273. /**
  274. * Equals based on ID
  275. * @param cmp comparator
  276. * @return true if ID the same
  277. */
  278. public boolean equals (Object cmp)
  279. {
  280. if (cmp == null)
  281. return false;
  282. if (!(cmp instanceof PO))
  283. return false;
  284. if (cmp.getClass().equals(this.getClass()))
  285. // if both ID's are zero they can't be compared by ID
  286. if (((PO)cmp).get_ID() == 0 && get_ID() == 0)
  287. return super.equals(cmp);
  288. else
  289. return ((PO)cmp).get_ID() == get_ID();
  290. return super.equals(cmp);
  291. } // equals
  292. public int hashCode()
  293. {
  294. assert false : "hashCode not designed";
  295. return 42; // any arbitrary constant will do
  296. }
  297. /**
  298. * Compare based on DocumentNo, Value, Name, Description
  299. * @param o1 Object 1
  300. * @param o2 Object 2
  301. * @return -1 if o1 < o2
  302. */
  303. public int compare (Object o1, Object o2)
  304. {
  305. if (o1 == null)
  306. return -1;
  307. else if (o2 == null)
  308. return 1;
  309. if (!(o1 instanceof PO))
  310. throw new ClassCastException ("Not PO -1- " + o1);
  311. if (!(o2 instanceof PO))
  312. throw new ClassCastException ("Not PO -2- " + o2);
  313. // same class
  314. Collator collator = Collator.getInstance();
  315. if (o1.getClass().equals(o2.getClass()))
  316. {
  317. int index = get_ColumnIndex("DocumentNo");
  318. if (index == -1)
  319. index = get_ColumnIndex("Value");
  320. if (index == -1)
  321. index = get_ColumnIndex("Name");
  322. if (index == -1)
  323. index = get_ColumnIndex("Description");
  324. if (index != -1)
  325. {
  326. PO po1 = (PO)o1;
  327. Object comp1 = po1.get_Value(index);
  328. PO po2 = (PO)o2;
  329. Object comp2 = po2.get_Value(index);
  330. if (comp1 == null)
  331. return -1;
  332. else if (comp2 == null)
  333. return 1;
  334. return collator.compare(comp1.toString(), comp2.toString());
  335. }
  336. }
  337. return collator.compare(o1.toString(), o2.toString());
  338. } // compare
  339. /**
  340. * Get TableName.
  341. * @return table name
  342. */
  343. public String get_TableName()
  344. {
  345. return p_info.getTableName();
  346. } // get_TableName
  347. /**
  348. * Get Key Columns.
  349. * @return table name
  350. */
  351. public String[] get_KeyColumns()
  352. {
  353. return m_KeyColumns;
  354. } // get_KeyColumns
  355. /**
  356. * Get Table ID.
  357. * @return table id
  358. */
  359. public int get_Table_ID()
  360. {
  361. return p_info.getAD_Table_ID();
  362. } // get_TableID
  363. /**
  364. * Return Single Key Record ID
  365. * @return ID or 0
  366. */
  367. public int get_ID()
  368. {
  369. Object oo = m_IDs[0];
  370. if (oo != null && oo instanceof Integer)
  371. return ((Integer)oo).intValue();
  372. return 0;
  373. } // getID
  374. /**
  375. * Return Deleted Single Key Record ID
  376. * @return ID or 0
  377. */
  378. public int get_IDOld()
  379. {
  380. return m_idOld;
  381. } // getID
  382. /**
  383. * Get Context
  384. * @return context
  385. */
  386. public Properties getCtx()
  387. {
  388. return p_ctx;
  389. } // getCtx
  390. /**
  391. * Get Logger
  392. * @return logger
  393. */
  394. public CLogger get_Logger()
  395. {
  396. return log;
  397. } // getLogger
  398. /**************************************************************************
  399. * Get Value
  400. * @param index index
  401. * @return value
  402. */
  403. public final Object get_Value (int index)
  404. {
  405. if (index < 0 || index >= get_ColumnCount())
  406. {
  407. log.log(Level.WARNING, "Index invalid - " + index);
  408. return null;
  409. }
  410. if (m_newValues[index] != null)
  411. {
  412. if (m_newValues[index].equals(Null.NULL))
  413. return null;
  414. return m_newValues[index];
  415. }
  416. return m_oldValues[index];
  417. } // get_Value
  418. /**
  419. * Get Value as int
  420. * @param index index
  421. * @return int value or 0
  422. */
  423. public int get_ValueAsInt (int index)
  424. {
  425. Object value = get_Value(index);
  426. if (value == null)
  427. return 0;
  428. if (value instanceof Integer)
  429. return ((Integer)value).intValue();
  430. try
  431. {
  432. return Integer.parseInt(value.toString());
  433. }
  434. catch (NumberFormatException ex)
  435. {
  436. log.warning(p_info.getColumnName(index) + " - " + ex.getMessage());
  437. return 0;
  438. }
  439. } // get_ValueAsInt
  440. /**
  441. * Get Value
  442. * @param columnName column name
  443. * @return value or null
  444. */
  445. public final Object get_Value (String columnName)
  446. {
  447. int index = get_ColumnIndex(columnName);
  448. if (index < 0)
  449. {
  450. log.log(Level.WARNING, "Column not found - " + columnName);
  451. Trace.printStack();
  452. return null;
  453. }
  454. return get_Value (index);
  455. } // get_Value
  456. /**
  457. * Get Encrypted Value
  458. * @param columnName column name
  459. * @return value or null
  460. */
  461. protected final Object get_ValueE (String columnName)
  462. {
  463. return get_Value (columnName);
  464. } // get_ValueE
  465. /**
  466. * Get Column Value
  467. * @param variableName name
  468. * @return value or ""
  469. */
  470. public String get_ValueAsString (String variableName)
  471. {
  472. Object value = get_Value (variableName);
  473. if (value == null)
  474. return "";
  475. return value.toString();
  476. } // get_ValueAsString
  477. /**
  478. * Get Value of Column
  479. * @param AD_Column_ID column
  480. * @return value or null
  481. */
  482. public final Object get_ValueOfColumn (int AD_Column_ID)
  483. {
  484. int index = p_info.getColumnIndex(AD_Column_ID);
  485. if (index < 0)
  486. {
  487. log.log(Level.WARNING, "Not found - AD_Column_ID=" + AD_Column_ID);
  488. return null;
  489. }
  490. return get_Value (index);
  491. } // get_ValueOfColumn
  492. /**
  493. * Get Old Value
  494. * @param index index
  495. * @return value
  496. */
  497. public final Object get_ValueOld (int index)
  498. {
  499. if (index < 0 || index >= get_ColumnCount())
  500. {
  501. log.log(Level.WARNING, "Index invalid - " + index);
  502. return null;
  503. }
  504. return m_oldValues[index];
  505. } // get_ValueOld
  506. /**
  507. * Get Old Value
  508. * @param columnName column name
  509. * @return value or null
  510. */
  511. public final Object get_ValueOld (String columnName)
  512. {
  513. int index = get_ColumnIndex(columnName);
  514. if (index < 0)
  515. {
  516. log.log(Level.WARNING, "Column not found - " + columnName);
  517. return null;
  518. }
  519. return get_ValueOld (index);
  520. } // get_ValueOld
  521. /**
  522. * Get Old Value as int
  523. * @param columnName column name
  524. * @return int value or 0
  525. */
  526. public int get_ValueOldAsInt (String columnName)
  527. {
  528. Object value = get_ValueOld(columnName);
  529. if (value == null)
  530. return 0;
  531. if (value instanceof Integer)
  532. return ((Integer)value).intValue();
  533. try
  534. {
  535. return Integer.parseInt(value.toString());
  536. }
  537. catch (NumberFormatException ex)
  538. {
  539. log.warning(columnName + " - " + ex.getMessage());
  540. return 0;
  541. }
  542. } // get_ValueOldAsInt
  543. /**
  544. * Is Value Changed
  545. * @param index index
  546. * @return true if changed
  547. */
  548. public final boolean is_ValueChanged (int index)
  549. {
  550. if (index < 0 || index >= get_ColumnCount())
  551. {
  552. log.log(Level.WARNING, "Index invalid - " + index);
  553. return false;
  554. }
  555. if (m_newValues[index] == null)
  556. return false;
  557. if (m_newValues[index] == Null.NULL && m_oldValues[index] == null)
  558. return false;
  559. return !m_newValues[index].equals(m_oldValues[index]);
  560. } // is_ValueChanged
  561. /**
  562. * Is Value Changed
  563. * @param columnName column name
  564. * @return true if changed
  565. */
  566. public final boolean is_ValueChanged (String columnName)
  567. {
  568. int index = get_ColumnIndex(columnName);
  569. if (index < 0)
  570. {
  571. log.log(Level.WARNING, "Column not found - " + columnName);
  572. return false;
  573. }
  574. return is_ValueChanged (index);
  575. } // is_ValueChanged
  576. /**
  577. * Return new - old.
  578. * - New Value if Old Value is null
  579. * - New Value - Old Value if Number
  580. * - otherwise null
  581. * @param index index
  582. * @return new - old or null if not appropriate or not changed
  583. */
  584. public final Object get_ValueDifference (int index)
  585. {
  586. if (index < 0 || index >= get_ColumnCount())
  587. {
  588. log.log(Level.WARNING, "Index invalid - " + index);
  589. return null;
  590. }
  591. Object nValue = m_newValues[index];
  592. // No new Value or NULL
  593. if (nValue == null || nValue == Null.NULL)
  594. return null;
  595. //
  596. Object oValue = m_oldValues[index];
  597. if (oValue == null || oValue == Null.NULL)
  598. return nValue;
  599. if (nValue instanceof BigDecimal)
  600. {
  601. BigDecimal obd = (BigDecimal)oValue;
  602. return ((BigDecimal)nValue).subtract(obd);
  603. }
  604. else if (nValue instanceof Integer)
  605. {
  606. int result = ((Integer)nValue).intValue();
  607. result -= ((Integer)oValue).intValue();
  608. return new Integer(result);
  609. }
  610. //
  611. log.warning("Invalid type - New=" + nValue);
  612. return null;
  613. } // get_ValueDifference
  614. /**
  615. * Return new - old.
  616. * - New Value if Old Value is null
  617. * - New Value - Old Value if Number
  618. * - otherwise null
  619. * @param columnName column name
  620. * @return new - old or null if not appropriate or not changed
  621. */
  622. public final Object get_ValueDifference (String columnName)
  623. {
  624. int index = get_ColumnIndex(columnName);
  625. if (index < 0)
  626. {
  627. log.log(Level.WARNING, "Column not found - " + columnName);
  628. return null;
  629. }
  630. return get_ValueDifference (index);
  631. } // get_ValueDifference
  632. /**************************************************************************
  633. * Set Value
  634. * @param ColumnName column name
  635. * @param value value
  636. * @return true if value set
  637. */
  638. protected final boolean set_Value (String ColumnName, Object value)
  639. {
  640. return set_Value(ColumnName, value, true);
  641. }
  642. /**************************************************************************
  643. * Set Value
  644. * @param ColumnName column name
  645. * @param value value
  646. * @param checkWritable
  647. * @return true if value set
  648. */
  649. protected final boolean set_Value (String ColumnName, Object value, boolean checkWritable)
  650. {
  651. if (value instanceof String && ColumnName.equals("WhereClause")
  652. && value.toString().toUpperCase().indexOf("=NULL") != -1)
  653. log.warning("Invalid Null Value - " + ColumnName + "=" + value);
  654. int index = get_ColumnIndex(ColumnName);
  655. if (index < 0)
  656. {
  657. log.log(Level.SEVERE, "Column not found - " + ColumnName);
  658. log.saveError("ColumnNotFound", "Column not found - " + ColumnName);
  659. return false;
  660. }
  661. if (ColumnName.endsWith("_ID") && value instanceof String )
  662. {
  663. // Convert to Integer only if info class is Integer - teo_sarca [ 2859125 ]
  664. Class<?> clazz = p_info.getColumnClass(p_info.getColumnIndex(ColumnName));
  665. if (Integer.class == clazz)
  666. {
  667. log.severe("Invalid Data Type for " + ColumnName + "=" + value);
  668. value = Integer.parseInt((String)value);
  669. }
  670. }
  671. return set_Value (index, value, checkWritable);
  672. } // setValue
  673. /**
  674. * Set Encrypted Value
  675. * @param ColumnName column name
  676. * @param value value
  677. * @return true if value set
  678. */
  679. protected final boolean set_ValueE (String ColumnName, Object value)
  680. {
  681. return set_Value (ColumnName, value);
  682. } // setValueE
  683. /**
  684. * Set Value if updateable and correct class.
  685. * (and to NULL if not mandatory)
  686. * @param index index
  687. * @param value value
  688. * @return true if value set
  689. */
  690. protected final boolean set_Value (int index, Object value)
  691. {
  692. return set_Value(index, value, true);
  693. }
  694. /**
  695. * Set Value if updateable and correct class.
  696. * (and to NULL if not mandatory)
  697. * @param index index
  698. * @param value value
  699. * @param checkWritable
  700. * @return true if value set
  701. */
  702. protected final boolean set_Value (int index, Object value, boolean checkWritable)
  703. {
  704. if (index < 0 || index >= get_ColumnCount())
  705. {
  706. log.log(Level.WARNING, "Index invalid - " + index);
  707. return false;
  708. }
  709. String ColumnName = p_info.getColumnName(index);
  710. String colInfo = " - " + ColumnName;
  711. //
  712. m_setErrors[index] = null;
  713. if (checkWritable)
  714. {
  715. if (p_info.isVirtualColumn(index))
  716. {
  717. log.log(Level.WARNING, "Virtual Column" + colInfo);
  718. log.saveError("VirtualColumn", "Virtual Column" + colInfo);
  719. m_setErrors[index] = new ValueNamePair("VirtualColumn", "Virtual Column" + colInfo);
  720. return false;
  721. }
  722. //
  723. // globalqss -- Bug 1618469 - is throwing not updateable even on new records
  724. // if (!p_info.isColumnUpdateable(index))
  725. if ( ( ! p_info.isColumnUpdateable(index) ) && ( ! is_new() ) )
  726. {
  727. colInfo += " - NewValue=" + value + " - OldValue=" + get_Value(index);
  728. log.log(Level.WARNING, "Column not updateable" + colInfo);
  729. log.saveError("ColumnReadonly", "Column not updateable" + colInfo);
  730. m_setErrors[index] = new ValueNamePair("ColumnReadonly", "Column not updateable" + colInfo);
  731. return false;
  732. }
  733. }
  734. //
  735. if (value == null)
  736. {
  737. if (checkWritable && p_info.isColumnMandatory(index))
  738. {
  739. log.saveError("FillMandatory", ColumnName + " is mandatory.");
  740. m_setErrors[index] = new ValueNamePair("FillMandatory", ColumnName + " is mandatory.");
  741. return false;
  742. }
  743. m_newValues[index] = Null.NULL; // correct
  744. if (log.isLoggable(Level.FINER)) log.finer(ColumnName + " = null");
  745. }
  746. else
  747. {
  748. // matching class or generic object
  749. if (value.getClass().equals(p_info.getColumnClass(index))
  750. || p_info.getColumnClass(index) == Object.class)
  751. m_newValues[index] = value; // correct
  752. // Integer can be set as BigDecimal
  753. else if (value.getClass() == BigDecimal.class
  754. && p_info.getColumnClass(index) == Integer.class)
  755. m_newValues[index] = new Integer (((BigDecimal)value).intValue());
  756. // Set Boolean
  757. else if (p_info.getColumnClass(index) == Boolean.class
  758. && ("Y".equals(value) || "N".equals(value)) )
  759. m_newValues[index] = new Boolean("Y".equals(value));
  760. // added by vpj-cd
  761. // To solve BUG [ 1618423 ] Set Project Type button in Project window throws warning
  762. // generated because C_Project.C_Project_Type_ID is defined as button in dictionary
  763. // although is ID (integer) in database
  764. else if (value.getClass() == Integer.class
  765. && p_info.getColumnClass(index) == String.class)
  766. m_newValues[index] = value;
  767. else if (value.getClass() == String.class
  768. && p_info.getColumnClass(index) == Integer.class)
  769. try
  770. {
  771. m_newValues[index] = new Integer((String)value);
  772. }
  773. catch (NumberFormatException e)
  774. {
  775. String errmsg = ColumnName
  776. + " - Class invalid: " + value.getClass().toString()
  777. + ", Should be " + p_info.getColumnClass(index).toString() + ": " + value;
  778. log.log(Level.SEVERE, errmsg);
  779. log.saveError("WrongDataType", errmsg);
  780. m_setErrors[index] = new ValueNamePair("WrongDataType", errmsg);
  781. return false;
  782. }
  783. else
  784. {
  785. String errmsg = ColumnName
  786. + " - Class invalid: " + value.getClass().toString()
  787. + ", Should be " + p_info.getColumnClass(index).toString() + ": " + value;
  788. log.log(Level.SEVERE, errmsg);
  789. log.saveError("WrongDataType", errmsg);
  790. m_setErrors[index] = new ValueNamePair("WrongDataType", errmsg);
  791. return false;
  792. }
  793. // Validate (Min/Max)
  794. String error = p_info.validate(index, value);
  795. if (error != null)
  796. {
  797. log.log(Level.WARNING, ColumnName + "=" + value + " - " + error);
  798. int separatorIndex = error.indexOf(";");
  799. if (separatorIndex > 0) {
  800. log.saveError(error.substring(0,separatorIndex), error.substring(separatorIndex+1));
  801. m_setErrors[index] = new ValueNamePair(error.substring(0,separatorIndex), error.substring(separatorIndex+1));
  802. } else {
  803. log.saveError(error, ColumnName);
  804. m_setErrors[index] = new ValueNamePair(error, ColumnName);
  805. }
  806. return false;
  807. }
  808. // Length for String
  809. if (p_info.getColumnClass(index) == String.class)
  810. {
  811. String stringValue = value.toString();
  812. int length = p_info.getFieldLength(index);
  813. if (stringValue.length() > length && length > 0)
  814. {
  815. log.warning(ColumnName + " - Value too long - truncated to length=" + length);
  816. m_newValues[index] = stringValue.substring(0,length);
  817. }
  818. }
  819. // Validate reference list [1762461]
  820. if (p_info.getColumn(index).DisplayType == DisplayType.List &&
  821. p_info.getColumn(index).AD_Reference_Value_ID > 0 &&
  822. value instanceof String) {
  823. if (MRefList.get(getCtx(), p_info.getColumn(index).AD_Reference_Value_ID,
  824. (String) value, get_TrxName()) != null)
  825. ;
  826. else {
  827. StringBuilder validValues = new StringBuilder();
  828. for (ValueNamePair vp : MRefList.getList(getCtx(), p_info.getColumn(index).AD_Reference_Value_ID, false))
  829. validValues.append(" - ").append(vp.getValue());
  830. String errmsg = ColumnName + " Invalid value - "
  831. + value + " - Reference_ID=" + p_info.getColumn(index).AD_Reference_Value_ID + validValues.toString();
  832. log.saveError("Validate", errmsg);
  833. m_setErrors[index] = new ValueNamePair("Validate", errmsg);
  834. return false;
  835. }
  836. }
  837. if (log.isLoggable(Level.FINEST)) log.finest(ColumnName + " = " + m_newValues[index] + " (OldValue="+m_oldValues[index]+")");
  838. }
  839. set_Keys (ColumnName, m_newValues[index]);
  840. // FR 2962094 Fill ProcessedOn when the Processed column is changing from N to Y
  841. setProcessedOn(ColumnName, value, m_oldValues[index]);
  842. return true;
  843. } // setValue
  844. /* FR 2962094 - Finish implementation of weighted average costing
  845. Fill the column ProcessedOn (if it exists) with a bigdecimal representation of current timestamp (with nanoseconds)
  846. */
  847. public void setProcessedOn(String ColumnName, Object value, Object oldValue) {
  848. if ("Processed".equals(ColumnName)
  849. && value instanceof Boolean
  850. && ((Boolean)value).booleanValue() == true
  851. && (oldValue == null
  852. || (oldValue instanceof Boolean
  853. && ((Boolean)oldValue).booleanValue() == false))) {
  854. if (get_ColumnIndex("ProcessedOn") > 0) {
  855. // fill processed on column
  856. //get current time from db
  857. Timestamp ts = DB.getSQLValueTS(null, "SELECT CURRENT_TIMESTAMP FROM DUAL");
  858. long mili = ts.getTime();
  859. int nano = ts.getNanos();
  860. double doublets = Double.parseDouble(Long.toString(mili) + "." + Integer.toString(nano));
  861. BigDecimal bdtimestamp = BigDecimal.valueOf(doublets);
  862. set_Value("ProcessedOn", bdtimestamp);
  863. }
  864. }
  865. }
  866. /**
  867. * Set Value w/o check (update, r/o, ..).
  868. * Used when Column is R/O
  869. * Required for key and parent values
  870. * @param ColumnName column name
  871. * @param value value
  872. * @return true if value set
  873. */
  874. public final boolean set_ValueNoCheck (String ColumnName, Object value)
  875. {
  876. return set_Value(ColumnName, value, false);
  877. } // set_ValueNoCheck
  878. /**
  879. * Set Encrypted Value w/o check (update, r/o, ..).
  880. * Used when Column is R/O
  881. * Required for key and parent values
  882. * @param ColumnName column name
  883. * @param value value
  884. * @return true if value set
  885. */
  886. protected final boolean set_ValueNoCheckE (String ColumnName, Object value)
  887. {
  888. return set_ValueNoCheck (ColumnName, value);
  889. } // set_ValueNoCheckE
  890. /**
  891. * Set value of Column
  892. * @param columnName
  893. * @param value
  894. */
  895. public final void set_ValueOfColumn(String columnName, Object value)
  896. {
  897. set_ValueOfColumnReturningBoolean(columnName, value);
  898. }
  899. /**
  900. * Set value of Column returning boolean
  901. * @param columnName
  902. * @param value
  903. * @returns boolean indicating success or failure
  904. */
  905. public final boolean set_ValueOfColumnReturningBoolean(String columnName, Object value)
  906. {
  907. int AD_Column_ID = p_info.getAD_Column_ID(columnName);
  908. if (AD_Column_ID > 0)
  909. return set_ValueOfColumnReturningBoolean(AD_Column_ID, value);
  910. else
  911. return false;
  912. }
  913. /**
  914. * Set Value of Column
  915. * @param AD_Column_ID column
  916. * @param value value
  917. */
  918. public final void set_ValueOfColumn (int AD_Column_ID, Object value)
  919. {
  920. set_ValueOfColumnReturningBoolean (AD_Column_ID, value);
  921. } // setValueOfColumn
  922. /**
  923. * Set Value of Column
  924. * @param AD_Column_ID column
  925. * @param value value
  926. * @returns boolean indicating success or failure
  927. */
  928. public final boolean set_ValueOfColumnReturningBoolean (int AD_Column_ID, Object value)
  929. {
  930. int index = p_info.getColumnIndex(AD_Column_ID);
  931. if (index < 0)
  932. log.log(Level.SEVERE, "Not found - AD_Column_ID=" + AD_Column_ID);
  933. String ColumnName = p_info.getColumnName(index);
  934. if (ColumnName.equals("IsApproved"))
  935. return set_ValueNoCheck(ColumnName, value);
  936. else
  937. return set_Value (index, value);
  938. } // setValueOfColumn
  939. /**
  940. * Set Custom Column
  941. * @param columnName column
  942. * @param value value
  943. */
  944. public final void set_CustomColumn (String columnName, Object value)
  945. {
  946. set_CustomColumnReturningBoolean (columnName, value);
  947. } // set_CustomColumn
  948. /**
  949. * Set Custom Column returning boolean
  950. * @param columnName column
  951. * @param value value
  952. * @returns boolean indicating success or failure
  953. */
  954. public final boolean set_CustomColumnReturningBoolean (String columnName, Object value)
  955. {
  956. // [ 1845793 ] PO.set_CustomColumn not updating correctly m_newValues
  957. // this is for columns not in PO - verify and call proper method if exists
  958. int poIndex = get_ColumnIndex(columnName);
  959. if (poIndex > 0) {
  960. // is not custom column - it exists in the PO
  961. return set_Value(columnName, value);
  962. }
  963. if (m_custom == null)
  964. m_custom = new HashMap<String,String>();
  965. String valueString = "NULL";
  966. if (value == null)
  967. ;
  968. else if (value instanceof Number)
  969. valueString = value.toString();
  970. else if (value instanceof Boolean)
  971. valueString = ((Boolean)value).booleanValue() ? "'Y'" : "'N'";
  972. else if (value instanceof Timestamp)
  973. valueString = DB.TO_DATE((Timestamp)value, false);
  974. else // if (value instanceof String)
  975. valueString = DB.TO_STRING(value.toString());
  976. // Save it
  977. if (log.isLoggable(Level.INFO))log.log(Level.INFO, columnName + "=" + valueString);
  978. m_custom.put(columnName, valueString);
  979. return true;
  980. } // set_CustomColumn
  981. /**
  982. * Set (numeric) Key Value
  983. * @param ColumnName column name
  984. * @param value value
  985. */
  986. private void set_Keys (String ColumnName, Object value)
  987. {
  988. // Update if KeyColumn
  989. for (int i = 0; i < m_IDs.length; i++)
  990. {
  991. if (ColumnName.equals (m_KeyColumns[i]))
  992. {
  993. m_IDs[i] = value;
  994. }
  995. } // for all key columns
  996. } // setKeys
  997. /**************************************************************************
  998. * Get Column Count
  999. * @return column count
  1000. */
  1001. public int get_ColumnCount()
  1002. {
  1003. return p_info.getColumnCount();
  1004. } // getColumnCount
  1005. /**
  1006. * Get Column Name
  1007. * @param index index
  1008. * @return ColumnName
  1009. */
  1010. public String get_ColumnName (int index)
  1011. {
  1012. return p_info.getColumnName (index);
  1013. } // getColumnName
  1014. /**
  1015. * Get Column Label
  1016. * @param index index
  1017. * @return Column Label
  1018. */
  1019. protected String get_ColumnLabel (int index)
  1020. {
  1021. return p_info.getColumnLabel (index);
  1022. } // getColumnLabel
  1023. /**
  1024. * Get Column Description
  1025. * @param index index
  1026. * @return column description
  1027. */
  1028. protected String get_ColumnDescription (int index)
  1029. {
  1030. return p_info.getColumnDescription (index);
  1031. } // getColumnDescription
  1032. /**
  1033. * Is Column Mandatory
  1034. * @param index index
  1035. * @return true if column mandatory
  1036. */
  1037. protected boolean isColumnMandatory (int index)
  1038. {
  1039. return p_info.isColumnMandatory(index);
  1040. } // isColumnNandatory
  1041. /**
  1042. * Is Column Updateable
  1043. * @param index index
  1044. * @return true if column updateable
  1045. */
  1046. protected boolean isColumnUpdateable (int index)
  1047. {
  1048. return p_info.isColumnUpdateable(index);
  1049. } // isColumnUpdateable
  1050. /**
  1051. * Set Column Updateable
  1052. * @param index index
  1053. * @param updateable column updateable
  1054. */
  1055. protected void set_ColumnUpdateable (int index, boolean updateable)
  1056. {
  1057. p_info.setColumnUpdateable(index, updateable);
  1058. } // setColumnUpdateable
  1059. /**
  1060. * Set all columns updateable
  1061. * @param updateable updateable
  1062. */
  1063. protected void setUpdateable (boolean updateable)
  1064. {
  1065. p_info.setUpdateable (updateable);
  1066. } // setUpdateable
  1067. /**
  1068. * Get Column DisplayType
  1069. * @param index index
  1070. * @return display type
  1071. */
  1072. protected int get_ColumnDisplayType (int index)
  1073. {
  1074. return p_info.getColumnDisplayType(index);
  1075. } // getColumnDisplayType
  1076. /**
  1077. * Get Lookup
  1078. * @param index index
  1079. * @return Lookup or null
  1080. */
  1081. protected Lookup get_ColumnLookup(int index)
  1082. {
  1083. return p_info.getColumnLookup(index);
  1084. } // getColumnLookup
  1085. /**
  1086. * Get Column Index
  1087. * @param columnName column name
  1088. * @return index of column with ColumnName or -1 if not found
  1089. */
  1090. public final int get_ColumnIndex (String columnName)
  1091. {
  1092. return p_info.getColumnIndex(columnName);
  1093. } // getColumnIndex
  1094. /**
  1095. * Get Display Value of value
  1096. * @param columnName columnName
  1097. * @param currentValue current value
  1098. * @return String value with "./." as null
  1099. */
  1100. public String get_DisplayValue(String columnName, boolean currentValue)
  1101. {
  1102. Object value = currentValue ? get_Value(columnName) : get_ValueOld(columnName);
  1103. if (value == null)
  1104. return "./.";
  1105. String retValue = value.toString();
  1106. int index = get_ColumnIndex(columnName);
  1107. if (index < 0)
  1108. return retValue;
  1109. int dt = get_ColumnDisplayType(index);
  1110. if (DisplayType.isText(dt) || DisplayType.YesNo == dt)
  1111. return retValue;
  1112. // Lookup
  1113. Lookup lookup = get_ColumnLookup(index);
  1114. if (lookup != null)
  1115. return lookup.getDisplay(value);
  1116. // Other
  1117. return retValue;
  1118. } // get_DisplayValue
  1119. /**
  1120. * Copy old values of From to new values of To.
  1121. * Does not copy Keys
  1122. * @param from old, existing & unchanged PO
  1123. * @param to new, not saved PO
  1124. * @param AD_Client_ID client
  1125. * @param AD_Org_ID org
  1126. */
  1127. protected static void copyValues (PO from, PO to, int AD_Client_ID, int AD_Org_ID)
  1128. {
  1129. copyValues (from, to);
  1130. to.setAD_Client_ID(AD_Client_ID);
  1131. to.setAD_Org_ID(AD_Org_ID);
  1132. } // copyValues
  1133. /**
  1134. * Copy old values of From to new values of To.
  1135. * Does not copy Keys and AD_Client_ID/AD_Org_ID
  1136. * @param from old, existing & unchanged PO
  1137. * @param to new, not saved PO
  1138. */
  1139. public static void copyValues (PO from, PO to)
  1140. {
  1141. if (s_log.isLoggable(Level.FINE)) s_log.fine("From ID=" + from.get_ID() + " - To ID=" + to.get_ID());
  1142. // Different Classes
  1143. if (from.getClass() != to.getClass())
  1144. {
  1145. for (int i1 = 0; i1 < from.m_oldValues.length; i1++)
  1146. {
  1147. String colName = from.p_info.getColumnName(i1);
  1148. MColumn column = MColumn.get(from.getCtx(), from.p_info.getAD_Column_ID(colName));
  1149. if ( column.isVirtualColumn()
  1150. || column.isKey() // KeyColumn
  1151. || column.isUUIDColumn() // IDEMPIERE-67
  1152. || column.isStandardColumn()
  1153. || ! column.isAllowCopy())
  1154. continue;
  1155. for (int i2 = 0; i2 < to.m_oldValues.length; i2++)
  1156. {
  1157. if (to.p_info.getColumnName(i2).equals(colName))
  1158. {
  1159. to.m_newValues[i2] = from.m_oldValues[i1];
  1160. break;
  1161. }
  1162. }
  1163. } // from loop
  1164. }
  1165. else // same class
  1166. {
  1167. for (int i = 0; i < from.m_oldValues.length; i++)
  1168. {
  1169. String colName = from.p_info.getColumnName(i);
  1170. MColumn column = MColumn.get(from.getCtx(), from.p_info.getAD_Column_ID(colName));
  1171. if ( column.isVirtualColumn()
  1172. || column.isKey() // KeyColumn
  1173. || column.isUUIDColumn()
  1174. || column.isStandardColumn()
  1175. || ! column.isAllowCopy())
  1176. continue;
  1177. to.m_newValues[i] = from.m_oldValues[i];
  1178. }
  1179. } // same class
  1180. } // copy
  1181. /**************************************************************************
  1182. * Load record with ID
  1183. * @param ID ID
  1184. * @param trxName transaction name
  1185. */
  1186. protected void load (int ID, String trxName)
  1187. {
  1188. if (log.isLoggable(Level.FINEST)) log.finest("ID=" + ID);
  1189. if (ID > 0)
  1190. {
  1191. setKeyInfo();
  1192. m_IDs = new Object[] {new Integer(ID)};
  1193. //m_KeyColumns = new String[] {p_info.getTableName() + "_ID"};
  1194. load(trxName);
  1195. }
  1196. else // new
  1197. {
  1198. loadDefaults();
  1199. m_createNew = true;
  1200. setKeyInfo(); // sets m_IDs
  1201. loadComplete(true);
  1202. }
  1203. } // load
  1204. /**
  1205. * (re)Load record with m_ID[*]
  1206. * @param trxName transaction
  1207. * @return true if loaded
  1208. */
  1209. public boolean load (String trxName)
  1210. {
  1211. m_trxName = trxName;
  1212. boolean success = true;
  1213. StringBuilder sql = new StringBuilder("SELECT ");
  1214. int size = get_ColumnCount();
  1215. for (int i = 0; i < size; i++)
  1216. {
  1217. if (i != 0)
  1218. sql.append(",");
  1219. sql.append(p_info.getColumnSQL(i)); // Normal and Virtual Column
  1220. }
  1221. sql.append(" FROM ").append(p_info.getTableName())
  1222. .append(" WHERE ")
  1223. .append(get_WhereClause(false));
  1224. //
  1225. // int index = -1;
  1226. if (log.isLoggable(Level.FINEST)) log.finest(get_WhereClause(true));
  1227. PreparedStatement pstmt = null;
  1228. ResultSet rs = null;
  1229. try
  1230. {
  1231. pstmt = DB.prepareStatement(sql.toString(), m_trxName); // local trx only
  1232. for (int i = 0; i < m_IDs.length; i++)
  1233. {
  1234. Object oo = m_IDs[i];
  1235. if (oo instanceof Integer)
  1236. pstmt.setInt(i+1, ((Integer)m_IDs[i]).intValue());
  1237. else if (oo instanceof Boolean)
  1238. pstmt.setString(i+1, ((Boolean) m_IDs[i] ? "Y" : "N"));
  1239. else if (oo instanceof Timestamp)
  1240. pstmt.setTimestamp(i+1, (Timestamp)m_IDs[i]);
  1241. else
  1242. pstmt.setString(i+1, m_IDs[i].toString());
  1243. }
  1244. rs = pstmt.executeQuery();
  1245. if (rs.next())
  1246. {
  1247. success = load(rs);
  1248. }
  1249. else
  1250. {
  1251. log.log(Level.SEVERE, "NO Data found for " + get_WhereClause(true), new Exception());
  1252. m_IDs = new Object[] {I_ZERO};
  1253. success = false;
  1254. // throw new DBException("NO Data found for " + get_WhereClause(true));
  1255. }
  1256. m_createNew = false;
  1257. // reset new values
  1258. m_newValues = new Object[size];
  1259. }
  1260. catch (Exception e)
  1261. {
  1262. String msg = "";
  1263. if (m_trxName != null)
  1264. msg = "[" + m_trxName + "] - ";
  1265. msg += get_WhereClause(true)
  1266. // + ", Index=" + index
  1267. // + ", Column=" + get_ColumnName(index)
  1268. // + ", " + p_info.toString(index)
  1269. + ", SQL=" + sql.toString();
  1270. success = false;
  1271. m_IDs = new Object[] {I_ZERO};
  1272. log.log(Level.SEVERE, msg, e);
  1273. // throw new DBException(e);
  1274. }
  1275. // Finish
  1276. finally {
  1277. DB.close(rs, pstmt);
  1278. rs = null; pstmt = null;
  1279. }
  1280. loadComplete(success);
  1281. return success;
  1282. } // load
  1283. /**
  1284. * Load from the current position of a ResultSet
  1285. * @param rs result set
  1286. * @return true if loaded
  1287. */
  1288. protected boolean load (ResultSet rs)
  1289. {
  1290. int size = get_ColumnCount();
  1291. boolean success = true;
  1292. int index = 0;
  1293. log.finest("(rs)");
  1294. // load column values
  1295. for (index = 0; index < size; index++)
  1296. {
  1297. String columnName = p_info.getColumnName(index);
  1298. Class<?> clazz = p_info.getColumnClass(index);
  1299. int dt = p_info.getColumnDisplayType(index);
  1300. try
  1301. {
  1302. if (clazz == Integer.class)
  1303. m_oldValues[index] = decrypt(index, new Integer(rs.getInt(columnName)));
  1304. else if (clazz == BigDecimal.class)
  1305. m_oldValues[index] = decrypt(index, rs.getBigDecimal(columnName));
  1306. else if (clazz == Boolean.class)
  1307. m_oldValues[index] = new Boolean ("Y".equals(decrypt(index, rs.getString(columnName))));
  1308. else if (clazz == Timestamp.class)
  1309. m_oldValues[index] = decrypt(index, rs.getTimestamp(columnName));
  1310. else if (DisplayType.isLOB(dt))
  1311. m_oldValues[index] = get_LOB (rs.getObject(columnName));
  1312. else if (clazz == String.class)
  1313. {
  1314. String value = (String)decrypt(index, rs.getString(columnName));
  1315. if (value != null)
  1316. {
  1317. if (get_Table_ID() == I_AD_Column.Table_ID || get_Table_ID() == I_AD_Element.Table_ID
  1318. || get_Table_ID() == I_AD_Field.Table_ID)
  1319. {
  1320. if ("Description".equals(columnName) || "Help".equals(columnName))
  1321. {
  1322. value = value.intern();
  1323. }
  1324. }
  1325. }
  1326. m_oldValues[index] = value;
  1327. }
  1328. else
  1329. m_oldValues[index] = loadSpecial(rs, index);
  1330. // NULL
  1331. if (rs.wasNull() && m_oldValues[index] != null)
  1332. m_oldValues[index] = null;
  1333. //
  1334. if (CLogMgt.isLevelAll())
  1335. log.finest(String.valueOf(index) + ": " + p_info.getColumnName(index)
  1336. + "(" + p_info.getColumnClass(index) + ") = " + m_oldValues[index]);
  1337. }
  1338. catch (SQLException e)
  1339. {
  1340. if (p_info.isVirtualColumn(index)) { // if rs constructor used
  1341. if (log.isLoggable(Level.FINER))log.log(Level.FINER, "Virtual Column not loaded: " + columnName);
  1342. } else {
  1343. log.log(Level.SEVERE, "(rs) - " + String.valueOf(index)
  1344. + ": " + p_info.getTableName() + "." + p_info.getColumnName(index)
  1345. + " (" + p_info.getColumnClass(index) + ") - " + e);
  1346. success = false;
  1347. }
  1348. }
  1349. }
  1350. m_createNew = false;
  1351. setKeyInfo();
  1352. loadComplete(success);
  1353. return success;
  1354. } // load
  1355. /**
  1356. * Load from HashMap
  1357. * @param hmIn hash map
  1358. * @return true if loaded
  1359. */
  1360. protected boolean load (HashMap<String,String> hmIn)
  1361. {
  1362. int size = get_ColumnCount();
  1363. boolean success = true;
  1364. int index = 0;
  1365. log.finest("(hm)");
  1366. // load column values
  1367. for (index = 0; index < size; index++)
  1368. {
  1369. String columnName = p_info.getColumnName(index);
  1370. String value = (String)hmIn.get(columnName);
  1371. if (value == null)
  1372. continue;
  1373. Class<?> clazz = p_info.getColumnClass(index);
  1374. int dt = p_info.getColumnDisplayType(index);
  1375. try
  1376. {
  1377. if (clazz == Integer.class)
  1378. m_oldValues[index] = new Integer(value);
  1379. else if (clazz == BigDecimal.class)
  1380. m_oldValues[index] = new BigDecimal(value);
  1381. else if (clazz == Boolean.class)
  1382. m_oldValues[index] = new Boolean ("Y".equals(value));
  1383. else if (clazz == Timestamp.class)
  1384. m_oldValues[index] = Timestamp.valueOf(value);
  1385. else if (DisplayType.isLOB(dt))
  1386. m_oldValues[index] = null; // get_LOB (rs.getObject(columnName));
  1387. else if (clazz == String.class)
  1388. m_oldValues[index] = value;
  1389. else
  1390. m_oldValues[index] = null; // loadSpecial(rs, index);
  1391. //
  1392. if (CLogMgt.isLevelAll())
  1393. log.finest(String.valueOf(index) + ": " + p_info.getColumnName(index)
  1394. + "(" + p_info.getColumnClass(index) + ") = " + m_oldValues[index]);
  1395. }
  1396. catch (Exception e)
  1397. {
  1398. if (p_info.isVirtualColumn(index)) { // if rs constructor used
  1399. if (log.isLoggable(Level.FINER))log.log(Level.FINER, "Virtual Column not loaded: " + columnName);
  1400. } else {
  1401. log.log(Level.SEVERE, "(ht) - " + String.valueOf(index)
  1402. + ": " + p_info.getTableName() + "." + p_info.getColumnName(index)
  1403. + " (" + p_info.getColumnClass(index) + ") - " + e);
  1404. success = false;
  1405. }
  1406. }
  1407. }
  1408. m_createNew = false;
  1409. // Overwrite
  1410. setStandardDefaults();
  1411. setKeyInfo();
  1412. loadComplete(success);
  1413. return success;
  1414. } // load
  1415. /**
  1416. * Create Hashmap with data as Strings
  1417. * @return HashMap
  1418. */
  1419. protected HashMap<String,String> get_HashMap()
  1420. {
  1421. HashMap<String,String> hmOut = new HashMap<String,String>();
  1422. int size = get_ColumnCount();
  1423. for (int i = 0; i < size; i++)
  1424. {
  1425. Object value = get_Value(i);
  1426. // Don't insert NULL values (allows Database defaults)
  1427. if (value == null
  1428. || p_info.isVirtualColumn(i))
  1429. continue;
  1430. // Display Type
  1431. int dt = p_info.getColumnDisplayType(i);
  1432. // Based on class of definition, not class of value
  1433. Class<?> c = p_info.getColumnClass(i);
  1434. String stringValue = null;
  1435. if (c == Object.class)
  1436. ; // saveNewSpecial (value, i));
  1437. else if (value == null || value.equals (Null.NULL))
  1438. ;
  1439. else if (value instanceof Integer || value instanceof BigDecimal)
  1440. stringValue = value.toString();
  1441. else if (c == Boolean.class)
  1442. {
  1443. boolean bValue = false;
  1444. if (value instanceof Boolean)
  1445. bValue = ((Boolean)value).booleanValue();
  1446. else
  1447. bValue = "Y".equals(value);
  1448. stringValue = bValue ? "Y" : "N";
  1449. }
  1450. else if (value instanceof Timestamp)
  1451. stringValue = value.toString();
  1452. else if (c == String.class)
  1453. stringValue = (String)value;
  1454. else if (DisplayType.isLOB(dt))
  1455. ;
  1456. else
  1457. ; // saveNewSpecial (value, i));
  1458. //
  1459. if (stringValue != null)
  1460. hmOut.put(p_info.getColumnName(i), stringValue);
  1461. }
  1462. // Custom Columns
  1463. if (m_custom != null)
  1464. {
  1465. Iterator<String> it = m_custom.keySet().iterator();
  1466. while (it.hasNext())
  1467. {
  1468. String column = (String)it.next();
  1469. // int index = p_info.getColumnIndex(column);
  1470. String value = (String)m_custom.get(column);
  1471. if (value != null)
  1472. hmOut.put(column, value);
  1473. }
  1474. m_custom = null;
  1475. }
  1476. return hmOut;
  1477. } // get_HashMap
  1478. /**
  1479. * Load Special data (images, ..).
  1480. * To be extended by sub-classes
  1481. * @param rs result set
  1482. * @param index zero based index
  1483. * @return value value
  1484. * @throws SQLException
  1485. */
  1486. protected Object loadSpecial (ResultSet rs, int index) throws SQLException
  1487. {
  1488. if (log.isLoggable(Level.FINEST)) log.finest("(NOP) - " + p_info.getColumnName(index));
  1489. return null;
  1490. } // loadSpecial
  1491. /**
  1492. * Load is complete
  1493. * @param success success
  1494. * To be extended by sub-classes
  1495. */
  1496. protected void loadComplete (boolean success)
  1497. {
  1498. } // loadComplete
  1499. /**
  1500. * Load Defaults
  1501. */
  1502. protected void loadDefaults()
  1503. {
  1504. setStandardDefaults();
  1505. //
  1506. /** @todo defaults from Field */
  1507. // MField.getDefault(p_info.getDefaultLogic(i));
  1508. } // loadDefaults
  1509. /**
  1510. * Set Default values.
  1511. * Client, Org, Created/Updated, *By, IsActive
  1512. */
  1513. protected void setStandardDefaults()
  1514. {
  1515. int size = get_ColumnCount();
  1516. for (int i = 0; i < size; i++)
  1517. {
  1518. if (p_info.isVirtualColumn(i))
  1519. continue;
  1520. String colName = p_info.getColumnName(i);
  1521. // Set Standard Values
  1522. if (colName.endsWith("tedBy"))
  1523. m_newValues[i] = new Integer (Env.getContextAsInt(p_ctx, "#AD_User_ID"));
  1524. else if (colName.equals("Created") || colName.equals("Updated"))
  1525. m_newValues[i] = new Timestamp (System.currentTimeMillis());
  1526. else if (colName.equals(p_info.getTableName() + "_ID")) // KeyColumn
  1527. m_newValues[i] = I_ZERO;
  1528. else if (colName.equals("IsActive"))
  1529. m_newValues[i] = new Boolean(true);
  1530. else if (colName.equals("AD_Client_ID"))
  1531. m_newValues[i] = new Integer(Env.getAD_Client_ID(p_ctx));
  1532. else if (colName.equals("AD_Org_ID"))
  1533. m_newValues[i] = new Integer(Env.getAD_Org_ID(p_ctx));
  1534. else if (colName.equals("Processed"))
  1535. m_newValues[i] = new Boolean(false);
  1536. else if (colName.equals("Processing"))
  1537. m_newValues[i] = new Boolean(false);
  1538. else if (colName.equals("Posted"))
  1539. m_newValues[i] = new Boolean(false);
  1540. }
  1541. } // setDefaults
  1542. /**
  1543. * Set Key Info (IDs and KeyColumns).
  1544. */
  1545. private void setKeyInfo()
  1546. {
  1547. // Search for Primary Key
  1548. for (int i = 0; i < p_info.getColumnCount(); i++)
  1549. {
  1550. if (p_info.isKey(i))
  1551. {
  1552. String ColumnName = p_info.getColumnName(i);
  1553. m_KeyColumns = new String[] {ColumnName};
  1554. if (p_info.getColumnName(i).endsWith("_ID"))
  1555. {
  1556. Integer ii = (Integer)get_Value(i);
  1557. if (ii == null)
  1558. m_IDs = new Object[] {I_ZERO};
  1559. else
  1560. m_IDs = new Object[] {ii};
  1561. if (log.isLoggable(Level.FINEST)) log.finest("(PK) " + ColumnName + "=" + ii);
  1562. }
  1563. else
  1564. {
  1565. Object oo = get_Value(i);
  1566. if (oo == null)
  1567. m_IDs = new Object[] {null};
  1568. else
  1569. m_IDs = new Object[] {oo};
  1570. if (log.isLoggable(Level.FINEST)) log.finest("(PK) " + ColumnName + "=" + oo);
  1571. }
  1572. return;
  1573. }
  1574. } // primary key search
  1575. // Search for Parents
  1576. ArrayList<String> columnNames = new ArrayList<String>();
  1577. for (int i = 0; i < p_info.getColumnCount(); i++)
  1578. {
  1579. if (p_info.isColumnParent(i))
  1580. columnNames.add(p_info.getColumnName(i));
  1581. }
  1582. // Set FKs
  1583. int size = columnNames.size();
  1584. if (size == 0)
  1585. throw new IllegalStateException("No PK nor FK - " + p_info.getTableName());
  1586. m_IDs = new Object[size];
  1587. m_KeyColumns = new String[size];
  1588. for (int i = 0; i < size; i++)
  1589. {
  1590. m_KeyColumns[i] = (String)columnNames.get(i);
  1591. if (m_KeyColumns[i].endsWith("_ID"))
  1592. {
  1593. Integer ii = null;
  1594. try
  1595. {
  1596. ii = (Integer)get_Value(m_KeyColumns[i]);
  1597. }
  1598. catch (Exception e)
  1599. {
  1600. log.log(Level.SEVERE, "", e);
  1601. }
  1602. if (ii != null)
  1603. m_IDs[i] = ii;
  1604. }
  1605. else
  1606. m_IDs[i] = get_Value(m_KeyColumns[i]);
  1607. if (log.isLoggable(Level.FINEST)) log.finest("(FK) " + m_KeyColumns[i] + "=" + m_IDs[i]);
  1608. }
  1609. } // setKeyInfo
  1610. /**************************************************************************
  1611. * Are all mandatory Fields filled (i.e. can we save)?.
  1612. * Stops at first null mandatory field
  1613. * @return true if all mandatory fields are ok
  1614. */
  1615. protected boolean isMandatoryOK()
  1616. {
  1617. int size = get_ColumnCount();
  1618. for (int i = 0; i < size; i++)
  1619. {
  1620. if (p_info.isColumnMandatory(i))
  1621. {
  1622. if (p_info.isVirtualColumn(i))
  1623. continue;
  1624. if (get_Value(i) == null || get_Value(i).equals(Null.NULL))
  1625. {
  1626. if (log.isLoggable(Level.INFO)) log.info(p_info.getColumnName(i));
  1627. return false;
  1628. }
  1629. }
  1630. }
  1631. return true;
  1632. } // isMandatoryOK
  1633. /**************************************************************************
  1634. * Set AD_Client
  1635. * @param AD_Client_ID client
  1636. */
  1637. final protected void setAD_Client_ID (int AD_Client_ID)
  1638. {
  1639. set_ValueNoCheck ("AD_Client_ID", new Integer(AD_Client_ID));
  1640. } // setAD_Client_ID
  1641. /**
  1642. * Get AD_Client
  1643. * @return AD_Client_ID
  1644. */
  1645. public final int getAD_Client_ID()
  1646. {
  1647. Integer ii = (Integer)get_Value("AD_Client_ID");
  1648. if (ii == null)
  1649. return 0;
  1650. return ii.intValue();
  1651. } // getAD_Client_ID
  1652. /**
  1653. * Set AD_Org
  1654. * @param AD_Org_ID org
  1655. */
  1656. final public void setAD_Org_ID (int AD_Org_ID)
  1657. {
  1658. set_ValueNoCheck ("AD_Org_ID", new Integer(AD_Org_ID));
  1659. } // setAD_Org_ID
  1660. /**
  1661. * Get AD_Org
  1662. * @return AD_Org_ID
  1663. */
  1664. public int getAD_Org_ID()
  1665. {
  1666. Integer ii = (Integer)get_Value("AD_Org_ID");
  1667. if (ii == null)
  1668. return 0;
  1669. return ii.intValue();
  1670. } // getAD_Org_ID
  1671. /**
  1672. * Overwrite Client Org if different
  1673. * @param AD_Client_ID client
  1674. * @param AD_Org_ID org
  1675. */
  1676. protected void setClientOrg (int AD_Client_ID, int AD_Org_ID)
  1677. {
  1678. if (AD_Client_ID != getAD_Client_ID())
  1679. setAD_Client_ID(AD_Client_ID);
  1680. if (AD_Org_ID != getAD_Org_ID())
  1681. setAD_Org_ID(AD_Org_ID);
  1682. } // setClientOrg
  1683. /**
  1684. * Overwrite Client Org if different
  1685. * @param po persistent object
  1686. */
  1687. protected void setClientOrg (PO po)
  1688. {
  1689. setClientOrg(po.getAD_Client_ID(), po.getAD_Org_ID());
  1690. } // setClientOrg
  1691. /**
  1692. * Set Active
  1693. * @param active active
  1694. */
  1695. public final void setIsActive (boolean active)
  1696. {
  1697. set_Value("IsActive", new Boolean(active));
  1698. } // setActive
  1699. /**
  1700. * Is Active
  1701. * @return is active
  1702. */
  1703. public final boolean isActive()
  1704. {
  1705. Boolean bb = (Boolean)get_Value("IsActive");
  1706. if (bb != null)
  1707. return bb.booleanValue();
  1708. return false;
  1709. } // isActive
  1710. /**
  1711. * Get Created
  1712. * @return created
  1713. */
  1714. final public Timestamp getCreated()
  1715. {
  1716. return (Timestamp)get_Value("Created");
  1717. } // getCreated
  1718. /**
  1719. * Get Updated
  1720. * @return updated
  1721. */
  1722. final public Timestamp getUpdated()
  1723. {
  1724. return (Timestamp)get_Value("Updated");
  1725. } // getUpdated
  1726. /**
  1727. * Get CreatedBy
  1728. * @return AD_User_ID
  1729. */
  1730. final public int getCreatedBy()
  1731. {
  1732. Integer ii = (Integer)get_Value("CreatedBy");
  1733. if (ii == null)
  1734. return 0;
  1735. return ii.intValue();
  1736. } // getCreateddBy
  1737. /**
  1738. * Get UpdatedBy
  1739. * @return AD_User_ID
  1740. */
  1741. final public int getUpdatedBy()
  1742. {
  1743. Integer ii = (Integer)get_Value("UpdatedBy");
  1744. if (ii == null)
  1745. return 0;
  1746. return ii.intValue();
  1747. } // getUpdatedBy
  1748. /**
  1749. * Set UpdatedBy
  1750. * @param AD_User_ID user
  1751. */
  1752. final protected void setUpdatedBy (int AD_User_ID)
  1753. {
  1754. set_ValueNoCheck ("UpdatedBy", new Integer(AD_User_ID));
  1755. } // setAD_User_ID
  1756. /** Cache */
  1757. private static CCache<String,String> trl_cache = new CCache<String,String>("po_trl", 5);
  1758. public String get_Translation (String columnName, String AD_Language)
  1759. {
  1760. return get_Translation(columnName, AD_Language, false);
  1761. }
  1762. /**
  1763. * Get Translation of column (if needed).
  1764. * It checks if the base language is used or the column is not translated.
  1765. * If there is no translation then it fallback to original value.
  1766. * @param columnName
  1767. * @param AD_Language
  1768. * @boolean reload
  1769. * @return translated string
  1770. * @throws IllegalArgumentException if columnName or AD_Language is null or model has multiple PK
  1771. */
  1772. public String get_Translation (String columnName, String AD_Language, boolean reload)
  1773. {
  1774. //
  1775. // Check if columnName, AD_Language is valid or table support translation (has 1 PK) => error
  1776. if (columnName == null || AD_Language == null
  1777. || m_IDs.length > 1 || m_IDs[0].equals(I_ZERO)
  1778. || !(m_IDs[0] instanceof Integer))
  1779. {
  1780. throw new IllegalArgumentException("ColumnName=" + columnName
  1781. + ", AD_Language=" + AD_Language
  1782. + ", ID.length=" + m_IDs.length
  1783. + ", ID=" + m_IDs[0]);
  1784. }
  1785. String key = get_TableName() + "." + columnName + "|" + get_ID() + "|" + AD_Language;
  1786. String retValue = null;
  1787. if (! reload && trl_cache.containsKey(key)) {
  1788. retValue = trl_cache.get(key);
  1789. return retValue;
  1790. } else {
  1791. //
  1792. // Check if NOT base language and column is translated => load trl from db
  1793. if (!Env.isBaseLanguage(AD_Language, get_TableName())
  1794. && p_info.isColumnTranslated(p_info.getColumnIndex(columnName))
  1795. )
  1796. {
  1797. // Load translation from database
  1798. int ID = ((Integer)m_IDs[0]).intValue();
  1799. StringBuilder sql = new StringBuilder("SELECT ").append(columnName)
  1800. .append(" FROM ").append(p_info.getTableName()).append("_Trl WHERE ")
  1801. .append(m_KeyColumns[0]).append("=?")
  1802. .append(" AND AD_Language=?");
  1803. retValue = DB.getSQLValueString(get_TrxName(), sql.toString(), ID, AD_Language);
  1804. }
  1805. }
  1806. //
  1807. // If no translation found or not translated, fallback to original:
  1808. if (retValue == null) {
  1809. Object val = get_Value(columnName);
  1810. retValue = (val != null ? val.toString() : null);
  1811. }
  1812. trl_cache.put(key, retValue);
  1813. //
  1814. return retValue;
  1815. } // get_Translation
  1816. /**
  1817. * Get Translation of column
  1818. * @param ctx context
  1819. * @param columnName
  1820. * @return translation
  1821. */
  1822. public String get_Translation (String columnName)
  1823. {
  1824. return get_Translation(columnName, Env.getAD_Language(getCtx()));
  1825. }
  1826. /**
  1827. * Is new record
  1828. * @return true if new
  1829. */
  1830. public boolean is_new()
  1831. {
  1832. if (m_createNew)
  1833. return true;
  1834. //
  1835. for (int i = 0; i < m_IDs.length; i++)
  1836. {
  1837. if (m_IDs[i].equals(I_ZERO) || m_IDs[i] == Null.NULL)
  1838. continue;
  1839. return false; // one value is non-zero
  1840. }
  1841. if (MTable.isZeroIDTable(get_TableName()))
  1842. return false;
  1843. return true;
  1844. } // is_new
  1845. /*
  1846. * Classes which override save() method:
  1847. * org.compiere.process.DocActionTemplate
  1848. * org.compiere.model.MClient
  1849. * org.compiere.model.MClientInfo
  1850. * org.compiere.model.MSystem
  1851. */
  1852. /**************************************************************************
  1853. * Update Value or create new record.
  1854. * To reload call load() - not updated
  1855. * @return true if saved
  1856. */
  1857. public boolean save()
  1858. {
  1859. checkValidContext();
  1860. CLogger.resetLast();
  1861. boolean newRecord = is_new(); // save locally as load resets
  1862. if (!newRecord && !is_Changed())
  1863. {
  1864. if (log.isLoggable(Level.FINE)) log.fine("Nothing changed - " + p_info.getTableName());
  1865. return true;
  1866. }
  1867. for (int i = 0; i < m_setErrors.length; i++) {
  1868. ValueNamePair setError = m_setErrors[i];
  1869. if (setError != null) {
  1870. log.saveError(setError.getValue(), Msg.getElement(getCtx(), p_info.getColumnName(i)) + " - " + setError.getName());
  1871. return false;
  1872. }
  1873. }
  1874. // Organization Check
  1875. if (getAD_Org_ID() == 0
  1876. && (get_AccessLevel() == ACCESSLEVEL_ORG
  1877. || (get_AccessLevel() == ACCESSLEVEL_CLIENTORG
  1878. && MClientShare.isOrgLevelOnly(getAD_Client_ID(), get_Table_ID()))))
  1879. {
  1880. log.saveError("FillMandatory", Msg.getElement(getCtx(), "AD_Org_ID"));
  1881. return false;
  1882. }
  1883. // Should be Org 0
  1884. if (getAD_Org_ID() != 0)
  1885. {
  1886. boolean reset = get_AccessLevel() == ACCESSLEVEL_SYSTEM;
  1887. if (!reset && MClientShare.isClientLevelOnly(getAD_Client_ID(), get_Table_ID()))
  1888. {
  1889. reset = get_AccessLevel() == ACCESSLEVEL_CLIENT
  1890. || get_AccessLevel() == ACCESSLEVEL_SYSTEMCLIENT
  1891. || get_AccessLevel() == ACCESSLEVEL_ALL
  1892. || get_AccessLevel() == ACCESSLEVEL_CLIENTORG;
  1893. }
  1894. if (reset)
  1895. {
  1896. log.warning("Set Org to 0");
  1897. setAD_Org_ID(0);
  1898. }
  1899. }
  1900. Trx localTrx = null;
  1901. Trx trx = null;
  1902. Savepoint savepoint = null;
  1903. if (m_trxName == null)
  1904. {
  1905. StringBuilder l_trxname = new StringBuilder(LOCAL_TRX_PREFIX)
  1906. .append(get_TableName());
  1907. if (l_trxname.length() > 23)
  1908. l_trxname.setLength(23);
  1909. m_trxName = Trx.createTrxName(l_trxname.toString());
  1910. localTrx = Trx.get(m_trxName, true);
  1911. }
  1912. else
  1913. {
  1914. trx = Trx.get(m_trxName, false);
  1915. if (trx == null)
  1916. {
  1917. // Using a trx that was previously closed or never opened
  1918. // Creating and starting the transaction right here, but please note
  1919. // that this is not a good practice
  1920. trx = Trx.get(m_trxName, true);
  1921. log.severe("Transaction closed or never opened ("+m_trxName+") => starting now --> " + toString());
  1922. }
  1923. }
  1924. // Before Save
  1925. try
  1926. {
  1927. // If not a localTrx we need to set a savepoint for rollback
  1928. if (localTrx == null)
  1929. savepoint = trx.setSavepoint(null);
  1930. if (!beforeSave(newRecord))
  1931. {
  1932. log.warning("beforeSave failed - " + toString());
  1933. if (localTrx != null)
  1934. {
  1935. localTrx.rollback();
  1936. localTrx.close();
  1937. m_trxName = null;
  1938. }
  1939. else
  1940. {
  1941. trx.rollback(savepoint);
  1942. savepoint = null;
  1943. }
  1944. return false;
  1945. }
  1946. }
  1947. catch (Exception e)
  1948. {
  1949. log.log(Level.WARNING, "beforeSave - " + toString(), e);
  1950. String msg = DBException.getDefaultDBExceptionMessage(e);
  1951. log.saveError(msg != null ? msg : "Error", e, false);
  1952. if (localTrx != null)
  1953. {
  1954. localTrx.rollback();
  1955. localTrx.close();
  1956. m_trxName = null;
  1957. }
  1958. else if (savepoint != null)
  1959. {
  1960. try
  1961. {
  1962. trx.rollback(savepoint);
  1963. } catch (SQLException e1){}
  1964. savepoint = null;
  1965. }
  1966. return false;
  1967. }
  1968. try
  1969. {
  1970. // Call ModelValidators TYPE_NEW/TYPE_CHANGE
  1971. String errorMsg = ModelValidationEngine.get().fireModelChange
  1972. (this, newRecord ? ModelValidator.TYPE_NEW : ModelValidator.TYPE_CHANGE);
  1973. if (errorMsg != null)
  1974. {
  1975. log.warning("Validation failed - " + errorMsg);
  1976. log.saveError("Error", errorMsg);
  1977. if (localTrx != null)
  1978. {
  1979. localTrx.rollback();
  1980. m_trxName = null;
  1981. }
  1982. else
  1983. {
  1984. trx.rollback(savepoint);
  1985. }
  1986. return false;
  1987. }
  1988. // Save
  1989. if (newRecord)
  1990. {
  1991. boolean b = saveNew();
  1992. if (b)
  1993. {
  1994. if (localTrx != null)
  1995. return localTrx.commit();
  1996. else
  1997. return b;
  1998. }
  1999. else
  2000. {
  2001. validateUniqueIndex();
  2002. if (localTrx != null)
  2003. localTrx.rollback();
  2004. else
  2005. trx.rollback(savepoint);
  2006. return b;
  2007. }
  2008. }
  2009. else
  2010. {
  2011. boolean b = saveUpdate();
  2012. if (b)
  2013. {
  2014. if (localTrx != null)
  2015. return localTrx.commit();
  2016. else
  2017. return b;
  2018. }
  2019. else
  2020. {
  2021. validateUniqueIndex();
  2022. if (localTrx != null)
  2023. localTrx.rollback();
  2024. else
  2025. trx.rollback(savepoint);
  2026. return b;
  2027. }
  2028. }
  2029. }
  2030. catch (Exception e)
  2031. {
  2032. log.log(Level.WARNING, "afterSave - " + toString(), e);
  2033. String msg = DBException.getDefaultDBExceptionMessage(e);
  2034. log.saveError(msg != null ? msg : "Error", e);
  2035. if (localTrx != null)
  2036. {
  2037. localTrx.rollback();
  2038. }
  2039. else if (savepoint != null)
  2040. {
  2041. try
  2042. {
  2043. trx.rollback(savepoint);
  2044. } catch (SQLException e1){}
  2045. savepoint = null;
  2046. }
  2047. return false;
  2048. }
  2049. finally
  2050. {
  2051. if (localTrx != null)
  2052. {
  2053. localTrx.close();
  2054. m_trxName = null;
  2055. }
  2056. else
  2057. {
  2058. if (savepoint != null)
  2059. {
  2060. try {
  2061. trx.releaseSavepoint(savepoint);
  2062. } catch (SQLException e) {
  2063. e.printStackTrace();
  2064. }
  2065. }
  2066. savepoint = null;
  2067. trx = null;
  2068. }
  2069. }
  2070. } // save
  2071. /**
  2072. * Update Value or create new record.
  2073. * @throws AdempiereException
  2074. * @see #save()
  2075. */
  2076. public void saveEx() throws AdempiereException
  2077. {
  2078. if (!save()) {
  2079. String msg = null;
  2080. ValueNamePair err = CLogger.retrieveError();
  2081. String val = err != null ? Msg.translate(getCtx(), err.getValue()) : "";
  2082. if (err != null)
  2083. msg = (val != null ? val + ": " : "") + err.getName();
  2084. if (msg == null || msg.length() == 0)
  2085. msg = "SaveError";
  2086. throw new AdempiereException(msg);
  2087. }
  2088. }
  2089. /**
  2090. * Finish Save Process
  2091. * @param newRecord new
  2092. * @param success success
  2093. * @return true if saved
  2094. */
  2095. private boolean saveFinish (boolean newRecord, boolean success)
  2096. {
  2097. // Translations
  2098. if (success)
  2099. {
  2100. if (newRecord)
  2101. insertTranslations();
  2102. else
  2103. updateTranslations();
  2104. // table with potential tree
  2105. if (get_ColumnIndex("IsSummary") >= 0) {
  2106. if (newRecord)
  2107. insert_Tree(MTree_Base.TREETYPE_CustomTable);
  2108. int idxValue = get_ColumnIndex("Value");
  2109. if (newRecord || (idxValue >= 0 && is_ValueChanged(idxValue)))
  2110. update_Tree(MTree_Base.TREETYPE_CustomTable);
  2111. }
  2112. }
  2113. //
  2114. try
  2115. {
  2116. success = afterSave (newRecord, success);
  2117. }
  2118. catch (Exception e)
  2119. {
  2120. log.log(Level.WARNING, "afterSave", e);
  2121. log.saveError("Error", e, false);
  2122. success = false;
  2123. // throw new DBException(e);
  2124. }
  2125. // Call ModelValidators TYPE_AFTER_NEW/TYPE_AFTER_CHANGE - teo_sarca [ 1675490 ]
  2126. if (success) {
  2127. String errorMsg = ModelValidationEngine.get().fireModelChange
  2128. (this, newRecord ?
  2129. (isReplication() ? ModelValidator.TYPE_AFTER_NEW_REPLICATION : ModelValidator.TYPE_AFTER_NEW)
  2130. :
  2131. (isReplication() ? ModelValidator.TYPE_AFTER_CHANGE_REPLICATION : ModelValidator.TYPE_AFTER_CHANGE)
  2132. );
  2133. setReplication(false);
  2134. if (errorMsg != null) {
  2135. log.saveError("Error", errorMsg);
  2136. success = false;
  2137. }
  2138. }
  2139. // OK
  2140. if (success)
  2141. {
  2142. //post osgi event
  2143. String topic = newRecord ? IEventTopics.PO_POST_CREATE : IEventTopics.PO_POST_UPADTE;
  2144. Event event = EventManager.newEvent(topic, this);
  2145. EventManager.getInstance().postEvent(event);
  2146. if (s_docWFMgr == null)
  2147. {
  2148. try
  2149. {
  2150. Class.forName("org.compiere.wf.DocWorkflowManager");
  2151. }
  2152. catch (Exception e)
  2153. {
  2154. }
  2155. }
  2156. if (s_docWFMgr != null)
  2157. s_docWFMgr.process (this, p_info.getAD_Table_ID());
  2158. // Copy to Old values
  2159. int size = p_info.getColumnCount();
  2160. for (int i = 0; i < size; i++)
  2161. {
  2162. if (m_newValues[i] != null)
  2163. {
  2164. if (m_newValues[i] == Null.NULL)
  2165. m_oldValues[i] = null;
  2166. else
  2167. m_oldValues[i] = m_newValues[i];
  2168. }
  2169. }
  2170. m_newValues = new Object[size];
  2171. m_createNew = false;
  2172. }
  2173. if (!newRecord)
  2174. CacheMgt.get().reset(p_info.getTableName());
  2175. else if (get_ID() > 0 && success)
  2176. CacheMgt.get().newRecord(p_info.getTableName(), get_ID());
  2177. return success;
  2178. } // saveFinish
  2179. /**
  2180. * Update Value or create new record.
  2181. * To reload call load() - not updated
  2182. * @param trxName transaction
  2183. * @return true if saved
  2184. */
  2185. public boolean save (String trxName)
  2186. {
  2187. set_TrxName(trxName);
  2188. return save();
  2189. } // save
  2190. public void saveReplica (boolean isFromReplication) throws AdempiereException
  2191. {
  2192. setReplication(isFromReplication);
  2193. saveEx();
  2194. }
  2195. /**
  2196. * Update Value or create new record.
  2197. * @param trxName transaction
  2198. * @throws AdempiereException
  2199. * @see #saveEx(String)
  2200. */
  2201. public void saveEx(String trxName) throws AdempiereException
  2202. {
  2203. set_TrxName(trxName);
  2204. saveEx();
  2205. }
  2206. /**
  2207. * Is there a Change to be saved?
  2208. * @return true if record changed
  2209. */
  2210. public boolean is_Changed()
  2211. {
  2212. int size = get_ColumnCount();
  2213. for (int i = 0; i < size; i++)
  2214. {
  2215. // Test if the column has changed - teo_sarca [ 1704828 ]
  2216. if (is_ValueChanged(i))
  2217. return true;
  2218. }
  2219. if (m_custom != null && m_custom.size() > 0)
  2220. return true; // there are custom columns modified
  2221. return false;
  2222. } // is_Change
  2223. /**
  2224. * Called before Save for Pre-Save Operation
  2225. * @param newRecord new record
  2226. * @return true if record can be saved
  2227. */
  2228. protected boolean beforeSave(boolean newRecord)
  2229. {
  2230. /** Prevents saving
  2231. log.saveError("Error", Msg.parseTranslation(getCtx(), "@C_Currency_ID@ = @C_Currency_ID@"));
  2232. log.saveError("FillMandatory", Msg.getElement(getCtx(), "PriceEntered"));
  2233. /** Issues message
  2234. log.saveWarning(AD_Message, message);
  2235. log.saveInfo (AD_Message, message);
  2236. **/
  2237. return true;
  2238. } // beforeSave
  2239. /**
  2240. * Called after Save for Post-Save Operation
  2241. * @param newRecord new record
  2242. * @param success true if save operation was success
  2243. * @return if save was a success
  2244. */
  2245. protected boolean afterSave (boolean newRecord, boolean success)
  2246. {
  2247. return success;
  2248. } // afterSave
  2249. /**
  2250. * Update Record directly
  2251. * @return true if updated
  2252. */
  2253. protected boolean saveUpdate()
  2254. {
  2255. boolean ok = doUpdate(isLogSQLScript());
  2256. return saveFinish (false, ok);
  2257. } // saveUpdate
  2258. private boolean isLogSQLScript() {
  2259. boolean logMigrationScript = false;
  2260. if (Ini.isClient()) {
  2261. logMigrationScript = Ini.isPropertyBool(Ini.P_LOGMIGRATIONSCRIPT);
  2262. } else {
  2263. String sysProperty = Env.getCtx().getProperty("LogMigrationScript", "N");
  2264. logMigrationScript = "y".equalsIgnoreCase(sysProperty) || "true".equalsIgnoreCase(sysProperty);
  2265. }
  2266. return logMigrationScript;
  2267. }
  2268. private boolean doUpdate(boolean withValues) {
  2269. //params for insert statement
  2270. List<Object> params = new ArrayList<Object>();
  2271. String where = get_WhereClause(true);
  2272. //
  2273. boolean changes = false;
  2274. StringBuilder sql = new StringBuilder ("UPDATE ");
  2275. sql.append(p_info.getTableName()).append( " SET ");
  2276. boolean updated = false;
  2277. boolean updatedBy = false;
  2278. lobReset();
  2279. // Change Log
  2280. MSession session = MSession.get (p_ctx, false);
  2281. if (session == null)
  2282. log.fine("No Session found");
  2283. int AD_ChangeLog_ID = 0;
  2284. int size = get_ColumnCount();
  2285. for (int i = 0; i < size; i++)
  2286. {
  2287. Object value = m_newValues[i];
  2288. if (value == null
  2289. || p_info.isVirtualColumn(i))
  2290. continue;
  2291. // we have a change
  2292. Class<?> c = p_info.getColumnClass(i);
  2293. int dt = p_info.getColumnDisplayType(i);
  2294. String columnName = p_info.getColumnName(i);
  2295. //
  2296. // updated/by
  2297. if (columnName.equals("UpdatedBy"))
  2298. {
  2299. if (updatedBy) // explicit
  2300. continue;
  2301. updatedBy = true;
  2302. }
  2303. else if (columnName.equals("Updated"))
  2304. {
  2305. if (updated)
  2306. continue;
  2307. updated = true;
  2308. }
  2309. if (DisplayType.isLOB(dt))
  2310. {
  2311. lobAdd (value, i, dt);
  2312. // If no changes set UpdatedBy explicitly to ensure commit of lob
  2313. if (!changes && !updatedBy)
  2314. {
  2315. int AD_User_ID = Env.getContextAsInt(p_ctx, "#AD_User_ID");
  2316. set_ValueNoCheck("UpdatedBy", new Integer(AD_User_ID));
  2317. sql.append("UpdatedBy=").append(AD_User_ID);
  2318. changes = true;
  2319. updatedBy = true;
  2320. }
  2321. continue;
  2322. }
  2323. // Update Document No
  2324. if (columnName.equals("DocumentNo"))
  2325. {
  2326. String strValue = (String)value;
  2327. if (strValue.startsWith("<") && strValue.endsWith(">"))
  2328. {
  2329. value = null;
  2330. int AD_Client_ID = getAD_Client_ID();
  2331. int index = p_info.getColumnIndex("C_DocTypeTarget_ID");
  2332. if (index == -1)
  2333. index = p_info.getColumnIndex("C_DocType_ID");
  2334. if (index != -1) // get based on Doc Type (might return null)
  2335. value = DB.getDocumentNo(get_ValueAsInt(index), m_trxName, false, this);
  2336. if (value == null) // not overwritten by DocType and not manually entered
  2337. value = DB.getDocumentNo(AD_Client_ID, p_info.getTableName(), m_trxName, this);
  2338. }
  2339. else
  2340. if (log.isLoggable(Level.INFO)) log.info("DocumentNo updated: " + m_oldValues[i] + " -> " + value);
  2341. }
  2342. if (changes)
  2343. sql.append(", ");
  2344. changes = true;
  2345. sql.append(columnName).append("=");
  2346. if (withValues)
  2347. {
  2348. // values
  2349. if (value == Null.NULL)
  2350. sql.append("NULL");
  2351. else if (value instanceof Integer || value instanceof BigDecimal)
  2352. sql.append(value);
  2353. else if (c == Boolean.class)
  2354. {
  2355. boolean bValue = false;
  2356. if (value instanceof Boolean)
  2357. bValue = ((Boolean)value).booleanValue();
  2358. else
  2359. bValue = "Y".equals(value);
  2360. sql.append(encrypt(i,bValue ? "'Y'" : "'N'"));
  2361. }
  2362. else if (value instanceof Timestamp)
  2363. sql.append(DB.TO_DATE((Timestamp)encrypt(i,value),p_info.getColumnDisplayType(i) == DisplayType.Date));
  2364. else {
  2365. if (value.toString().length() == 0) {
  2366. // [ 1722057 ] Encrypted columns throw error if saved as null
  2367. // don't encrypt NULL
  2368. sql.append(DB.TO_STRING(value.toString()));
  2369. } else {
  2370. sql.append(encrypt(i,DB.TO_STRING(value.toString())));
  2371. }
  2372. }
  2373. }
  2374. else
  2375. {
  2376. if (value instanceof Timestamp && dt == DisplayType.Date)
  2377. sql.append("trunc(cast(? as date))");
  2378. else
  2379. sql.append("?");
  2380. if (value == Null.NULL)
  2381. {
  2382. params.add(null);
  2383. }
  2384. else if (c == Boolean.class)
  2385. {
  2386. boolean bValue = false;
  2387. if (value instanceof Boolean)
  2388. bValue = ((Boolean)value).booleanValue();
  2389. else
  2390. bValue = "Y".equals(value);
  2391. params.add(encrypt(i,bValue ? "Y" : "N"));
  2392. }
  2393. else if (c == String.class)
  2394. {
  2395. if (value.toString().length() == 0) {
  2396. // [ 1722057 ] Encrypted columns throw error if saved as null
  2397. // don't encrypt NULL
  2398. params.add(null);
  2399. } else {
  2400. params.add(encrypt(i,value));
  2401. }
  2402. }
  2403. else
  2404. {
  2405. params.add(value);
  2406. }
  2407. }
  2408. // Change Log - Only
  2409. if (session != null
  2410. && m_IDs.length == 1
  2411. && p_info.isAllowLogging(i) // logging allowed
  2412. && !p_info.isEncrypted(i) // not encrypted
  2413. && !p_info.isVirtualColumn(i) // no virtual column
  2414. && !"Password".equals(columnName)
  2415. )
  2416. {
  2417. Object oldV = m_oldValues[i];
  2418. Object newV = value;
  2419. if (oldV != null && oldV == Null.NULL)
  2420. oldV = null;
  2421. if (newV != null && newV == Null.NULL)
  2422. newV = null;
  2423. // change log on update
  2424. MChangeLog cLog = session.changeLog (
  2425. m_trxName, AD_ChangeLog_ID,
  2426. p_info.getAD_Table_ID(), p_info.getColumn(i).AD_Column_ID,
  2427. get_ID(), getAD_Client_ID(), getAD_Org_ID(), oldV, newV, MChangeLog.EVENTCHANGELOG_Update);
  2428. if (cLog != null)
  2429. AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID();
  2430. }
  2431. } // for all fields
  2432. // Custom Columns (cannot be logged as no column)
  2433. if (m_custom != null)
  2434. {
  2435. Iterator<String> it = m_custom.keySet().iterator();
  2436. while (it.hasNext())
  2437. {
  2438. if (changes)
  2439. sql.append(", ");
  2440. changes = true;
  2441. //
  2442. String column = (String)it.next();
  2443. String value = (String)m_custom.get(column);
  2444. int index = p_info.getColumnIndex(column);
  2445. if (withValues)
  2446. {
  2447. sql.append(column).append("=").append(encrypt(index,value));
  2448. }
  2449. else
  2450. {
  2451. sql.append(column).append("=?");
  2452. if (value == null || value.toString().length() == 0)
  2453. {
  2454. params.add(null);
  2455. }
  2456. else
  2457. {
  2458. params.add(encrypt(index,value));
  2459. }
  2460. }
  2461. }
  2462. m_custom = null;
  2463. }
  2464. // Something changed
  2465. if (changes)
  2466. {
  2467. if (m_trxName == null) {
  2468. if (log.isLoggable(Level.FINE)) log.fine(p_info.getTableName() + "." + where);
  2469. } else {
  2470. if (log.isLoggable(Level.FINE)) log.fine("[" + m_trxName + "] - " + p_info.getTableName() + "." + where);
  2471. }
  2472. if (!updated) // Updated not explicitly set
  2473. {
  2474. Timestamp now = new Timestamp(System.currentTimeMillis());
  2475. set_ValueNoCheck("Updated", now);
  2476. if (withValues)
  2477. {
  2478. sql.append(",Updated=").append(DB.TO_DATE(now, false));
  2479. }
  2480. else
  2481. {
  2482. sql.append(",Updated=?");
  2483. params.add(now);
  2484. }
  2485. }
  2486. if (!updatedBy) // UpdatedBy not explicitly set
  2487. {
  2488. int AD_User_ID = Env.getContextAsInt(p_ctx, "#AD_User_ID");
  2489. set_ValueNoCheck("UpdatedBy", new Integer(AD_User_ID));
  2490. if (withValues)
  2491. {
  2492. sql.append(",UpdatedBy=").append(AD_User_ID);
  2493. }
  2494. else
  2495. {
  2496. sql.append(",UpdatedBy=?");
  2497. params.add(AD_User_ID);
  2498. }
  2499. }
  2500. sql.append(" WHERE ").append(where);
  2501. /** @todo status locking goes here */
  2502. if (log.isLoggable(Level.FINEST)) log.finest(sql.toString());
  2503. int no = 0;
  2504. if (isUseTimeoutForUpdate())
  2505. no = withValues ? DB.executeUpdateEx(sql.toString(), m_trxName, QUERY_TIME_OUT)
  2506. : DB.executeUpdateEx(sql.toString(), params.toArray(), m_trxName, QUERY_TIME_OUT);
  2507. else
  2508. no = withValues ? DB.executeUpdate(sql.toString(), m_trxName)
  2509. : DB.executeUpdate(sql.toString(), params.toArray(), false, m_trxName);
  2510. boolean ok = no == 1;
  2511. if (ok)
  2512. ok = lobSave();
  2513. else
  2514. {
  2515. if (m_trxName == null)
  2516. log.saveError("SaveError", "Update return " + no + " instead of 1"
  2517. + " - " + p_info.getTableName() + "." + where);
  2518. else
  2519. log.saveError("SaveError", "Update return " + no + " instead of 1"
  2520. + " - [" + m_trxName + "] - " + p_info.getTableName() + "." + where);
  2521. }
  2522. return ok;
  2523. }
  2524. else
  2525. {
  2526. // nothing changed, so OK
  2527. return true;
  2528. }
  2529. }
  2530. private boolean isUseTimeoutForUpdate() {
  2531. return "true".equalsIgnoreCase(System.getProperty(USE_TIMEOUT_FOR_UPDATE, "false"))
  2532. && DB.getDatabase().isQueryTimeoutSupported();
  2533. }
  2534. /**
  2535. * Create New Record
  2536. * @return true if new record inserted
  2537. */
  2538. private boolean saveNew()
  2539. {
  2540. // Set ID for single key - Multi-Key values need explicitly be set previously
  2541. if (m_IDs.length == 1 && p_info.hasKeyColumn()
  2542. && m_KeyColumns[0].endsWith("_ID")) // AD_Language, EntityType
  2543. {
  2544. int no = saveNew_getID();
  2545. if (no <= 0)
  2546. no = DB.getNextID(getAD_Client_ID(), p_info.getTableName(), m_trxName);
  2547. // the primary key is not overwrite with the local sequence
  2548. if (isReplication())
  2549. {
  2550. if (get_ID() > 0)
  2551. {
  2552. no = get_ID();
  2553. }
  2554. }
  2555. if (no <= 0)
  2556. {
  2557. log.severe("No NextID (" + no + ")");
  2558. return saveFinish (true, false);
  2559. }
  2560. m_IDs[0] = new Integer(no);
  2561. set_ValueNoCheck(m_KeyColumns[0], m_IDs[0]);
  2562. }
  2563. //uuid secondary key
  2564. int uuidIndex = p_info.getColumnIndex(getUUIDColumnName());
  2565. if (uuidIndex >= 0)
  2566. {
  2567. String value = (String)get_Value(uuidIndex);
  2568. if (p_info.getColumn(uuidIndex).FieldLength == 36 && (value == null || value.length() == 0))
  2569. {
  2570. UUID uuid = UUID.randomUUID();
  2571. set_ValueNoCheck(p_info.getColumnName(uuidIndex), uuid.toString());
  2572. }
  2573. }
  2574. if (m_trxName == null) {
  2575. if (log.isLoggable(Level.FINE)) log.fine(p_info.getTableName() + " - " + get_WhereClause(true));
  2576. } else {
  2577. if (log.isLoggable(Level.FINE)) log.fine("[" + m_trxName + "] - " + p_info.getTableName() + " - " + get_WhereClause(true));
  2578. }
  2579. // Set new DocumentNo
  2580. String columnName = "DocumentNo";
  2581. int index = p_info.getColumnIndex(columnName);
  2582. if (index != -1)
  2583. {
  2584. String value = (String)get_Value(index);
  2585. if (value != null && value.startsWith("<") && value.endsWith(">"))
  2586. value = null;
  2587. if (value == null || value.length() == 0)
  2588. {
  2589. int dt = p_info.getColumnIndex("C_DocTypeTarget_ID");
  2590. if (dt == -1)
  2591. dt = p_info.getColumnIndex("C_DocType_ID");
  2592. if (dt != -1) // get based on Doc Type (might return null)
  2593. value = DB.getDocumentNo(get_ValueAsInt(dt), m_trxName, false, this);
  2594. if (value == null) // not overwritten by DocType and not manually entered
  2595. value = DB.getDocumentNo(getAD_Client_ID(), p_info.getTableName(), m_trxName, this);
  2596. set_ValueNoCheck(columnName, value);
  2597. }
  2598. }
  2599. // Set empty Value
  2600. columnName = "Value";
  2601. index = p_info.getColumnIndex(columnName);
  2602. if (index != -1)
  2603. {
  2604. String value = (String)get_Value(index);
  2605. if (value == null || value.length() == 0)
  2606. {
  2607. value = DB.getDocumentNo (getAD_Client_ID(), p_info.getTableName(), m_trxName, this);
  2608. set_ValueNoCheck(columnName, value);
  2609. }
  2610. }
  2611. boolean ok = doInsert(isLogSQLScript());
  2612. return saveFinish (true, ok);
  2613. } // saveNew
  2614. private boolean doInsert(boolean withValues) {
  2615. int index;
  2616. lobReset();
  2617. // Change Log
  2618. MSession session = MSession.get (p_ctx, false);
  2619. if (session == null)
  2620. log.fine("No Session found");
  2621. int AD_ChangeLog_ID = 0;
  2622. //params for insert statement
  2623. List<Object> params = new ArrayList<Object>();
  2624. // SQL
  2625. StringBuilder sqlInsert = new StringBuilder("INSERT INTO ");
  2626. sqlInsert.append(p_info.getTableName()).append(" (");
  2627. StringBuilder sqlValues = new StringBuilder(") VALUES (");
  2628. int size = get_ColumnCount();
  2629. boolean doComma = false;
  2630. for (int i = 0; i < size; i++)
  2631. {
  2632. Object value = get_Value(i);
  2633. // Don't insert NULL values (allows Database defaults)
  2634. if (value == null
  2635. || p_info.isVirtualColumn(i))
  2636. continue;
  2637. // Display Type
  2638. int dt = p_info.getColumnDisplayType(i);
  2639. if (DisplayType.isLOB(dt))
  2640. {
  2641. lobAdd (value, i, dt);
  2642. continue;
  2643. }
  2644. // ** add column **
  2645. if (doComma)
  2646. {
  2647. sqlInsert.append(",");
  2648. sqlValues.append(",");
  2649. }
  2650. else
  2651. doComma = true;
  2652. sqlInsert.append(p_info.getColumnName(i));
  2653. //
  2654. // Based on class of definition, not class of value
  2655. Class<?> c = p_info.getColumnClass(i);
  2656. if (withValues)
  2657. {
  2658. try
  2659. {
  2660. if (c == Object.class) // may have need to deal with null values differently
  2661. sqlValues.append (saveNewSpecial (value, i));
  2662. else if (value == null || value.equals (Null.NULL))
  2663. sqlValues.append ("NULL");
  2664. else if (value instanceof Integer || value instanceof BigDecimal)
  2665. sqlValues.append (value);
  2666. else if (c == Boolean.class)
  2667. {
  2668. boolean bValue = false;
  2669. if (value instanceof Boolean)
  2670. bValue = ((Boolean)value).booleanValue();
  2671. else
  2672. bValue = "Y".equals(value);
  2673. sqlValues.append (encrypt(i,bValue ? "'Y'" : "'N'"));
  2674. }
  2675. else if (value instanceof Timestamp)
  2676. sqlValues.append (DB.TO_DATE ((Timestamp)encrypt(i,value), p_info.getColumnDisplayType (i) == DisplayType.Date));
  2677. else if (c == String.class)
  2678. sqlValues.append (encrypt(i,DB.TO_STRING ((String)value)));
  2679. else if (DisplayType.isLOB(dt))
  2680. sqlValues.append("null"); // no db dependent stuff here
  2681. else
  2682. sqlValues.append (saveNewSpecial (value, i));
  2683. }
  2684. catch (Exception e)
  2685. {
  2686. String msg = "";
  2687. if (m_trxName != null)
  2688. msg = "[" + m_trxName + "] - ";
  2689. msg += p_info.toString(i)
  2690. + " - Value=" + value
  2691. + "(" + (value==null ? "null" : value.getClass().getName()) + ")";
  2692. log.log(Level.SEVERE, msg, e);
  2693. throw new DBException(e); // fini
  2694. }
  2695. }
  2696. else
  2697. {
  2698. if (value instanceof Timestamp && dt == DisplayType.Date)
  2699. sqlValues.append("trunc(cast(? as date))");
  2700. else
  2701. sqlValues.append("?");
  2702. if (DisplayType.isLOB(dt))
  2703. {
  2704. params.add(null);
  2705. }
  2706. else if (value == null || value.equals (Null.NULL))
  2707. {
  2708. params.add(null);
  2709. }
  2710. else if (c == Boolean.class)
  2711. {
  2712. boolean bValue = false;
  2713. if (value instanceof Boolean)
  2714. bValue = ((Boolean)value).booleanValue();
  2715. else
  2716. bValue = "Y".equals(value);
  2717. params.add(encrypt(i,bValue ? "Y" : "N"));
  2718. }
  2719. else if (c == String.class)
  2720. {
  2721. if (value.toString().length() == 0)
  2722. {
  2723. params.add(null);
  2724. }
  2725. else
  2726. {
  2727. params.add(encrypt(i,value));
  2728. }
  2729. }
  2730. else
  2731. {
  2732. params.add(value);
  2733. }
  2734. }
  2735. // Change Log - Only
  2736. String insertLog = MSysConfig.getValue(MSysConfig.SYSTEM_INSERT_CHANGELOG, "Y", getAD_Client_ID());
  2737. if ( session != null
  2738. && m_IDs.length == 1
  2739. && p_info.isAllowLogging(i) // logging allowed
  2740. && !p_info.isEncrypted(i) // not encrypted
  2741. && !p_info.isVirtualColumn(i) // no virtual column
  2742. && !"Password".equals(p_info.getColumnName(i))
  2743. && (insertLog.equalsIgnoreCase("Y")
  2744. || (insertLog.equalsIgnoreCase("K") && p_info.getColumn(i).IsKey))
  2745. )
  2746. {
  2747. // change log on new
  2748. MChangeLog cLog = session.changeLog (
  2749. m_trxName, AD_ChangeLog_ID,
  2750. p_info.getAD_Table_ID(), p_info.getColumn(i).AD_Column_ID,
  2751. get_ID(), getAD_Client_ID(), getAD_Org_ID(), null, value, MChangeLog.EVENTCHANGELOG_Insert);
  2752. if (cLog != null)
  2753. AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID();
  2754. }
  2755. }
  2756. // Custom Columns
  2757. if (m_custom != null)
  2758. {
  2759. Iterator<String> it = m_custom.keySet().iterator();
  2760. while (it.hasNext())
  2761. {
  2762. String column = (String)it.next();
  2763. index = p_info.getColumnIndex(column);
  2764. String value = (String)m_custom.get(column);
  2765. if (value == null)
  2766. continue;
  2767. if (doComma)
  2768. {
  2769. sqlInsert.append(",");
  2770. sqlValues.append(",");
  2771. }
  2772. else
  2773. doComma = true;
  2774. sqlInsert.append(column);
  2775. if (withValues)
  2776. {
  2777. sqlValues.append(encrypt(index, value));
  2778. }
  2779. else
  2780. {
  2781. sqlValues.append("?");
  2782. if (value == null || value.toString().length() == 0)
  2783. {
  2784. params.add(null);
  2785. }
  2786. else
  2787. {
  2788. params.add(encrypt(index, value));
  2789. }
  2790. }
  2791. }
  2792. m_custom = null;
  2793. }
  2794. sqlInsert.append(sqlValues)
  2795. .append(")");
  2796. //
  2797. int no = withValues ? DB.executeUpdate(sqlInsert.toString(), m_trxName)
  2798. : DB.executeUpdate(sqlInsert.toString(), params.toArray(), false, m_trxName);
  2799. boolean ok = no == 1;
  2800. if (ok)
  2801. {
  2802. ok = lobSave();
  2803. if (!load(m_trxName)) // re-read Info
  2804. {
  2805. if (m_trxName == null)
  2806. log.log(Level.SEVERE, "reloading");
  2807. else
  2808. log.log(Level.SEVERE, "[" + m_trxName + "] - reloading");
  2809. ok = false;;
  2810. }
  2811. }
  2812. else
  2813. {
  2814. String msg = "Not inserted - ";
  2815. if (CLogMgt.isLevelFiner())
  2816. msg += sqlInsert.toString();
  2817. else
  2818. msg += get_TableName();
  2819. if (m_trxName == null)
  2820. log.log(Level.WARNING, msg);
  2821. else
  2822. log.log(Level.WARNING, "[" + m_trxName + "]" + msg);
  2823. }
  2824. return ok;
  2825. }
  2826. /**
  2827. * Get ID for new record during save.
  2828. * You can overwrite this to explicitly set the ID
  2829. * @return ID to be used or 0 for default logic
  2830. */
  2831. protected int saveNew_getID()
  2832. {
  2833. if (get_ID() > 0 && get_ID() < 999999) // 2Pack assigns official ID's when importing
  2834. return get_ID();
  2835. return 0;
  2836. } // saveNew_getID
  2837. /**
  2838. * Create Single/Multi Key Where Clause
  2839. * @param withValues if true uses actual values otherwise ?
  2840. * @return where clause
  2841. */
  2842. public String get_WhereClause (boolean withValues)
  2843. {
  2844. StringBuilder sb = new StringBuilder();
  2845. for (int i = 0; i < m_IDs.length; i++)
  2846. {
  2847. if (i != 0)
  2848. sb.append(" AND ");
  2849. sb.append(m_KeyColumns[i]).append("=");
  2850. if (withValues)
  2851. {
  2852. if (m_KeyColumns[i].endsWith("_ID"))
  2853. sb.append(m_IDs[i]);
  2854. else if(m_IDs[i] instanceof Timestamp)
  2855. sb.append(DB.TO_DATE((Timestamp)m_IDs[i], false));
  2856. else {
  2857. sb.append("'");
  2858. if (m_IDs[i] instanceof Boolean) {
  2859. if ((Boolean) m_IDs[i]) {
  2860. sb.append("Y");
  2861. } else {
  2862. sb.append("N");
  2863. }
  2864. } else {
  2865. sb.append(m_IDs[i]);
  2866. }
  2867. sb.append("'");
  2868. }
  2869. }
  2870. else
  2871. sb.append("?");
  2872. }
  2873. return sb.toString();
  2874. } // getWhereClause
  2875. /**
  2876. * Save Special Data.
  2877. * To be extended by sub-classes
  2878. * @param value value
  2879. * @param index index
  2880. * @return SQL code for INSERT VALUES clause
  2881. */
  2882. protected String saveNewSpecial (Object value, int index)
  2883. {
  2884. String colName = p_info.getColumnName(index);
  2885. String colClass = p_info.getColumnClass(index).toString();
  2886. String colValue = value == null ? "null" : value.getClass().toString();
  2887. // int dt = p_info.getColumnDisplayType(index);
  2888. log.log(Level.SEVERE, "Unknown class for column " + colName
  2889. + " (" + colClass + ") - Value=" + colValue);
  2890. if (value == null)
  2891. return "NULL";
  2892. return value.toString();
  2893. } // saveNewSpecial
  2894. /**
  2895. * Encrypt data.
  2896. * Not: LOB, special values/Objects
  2897. * @param index index
  2898. * @param xx data
  2899. * @return xx
  2900. */
  2901. private Object encrypt (int index, Object xx)
  2902. {
  2903. if (xx == null)
  2904. return null;
  2905. if (index != -1 && p_info.isEncrypted(index)) {
  2906. return SecureEngine.encrypt(xx, getAD_Client_ID());
  2907. }
  2908. return xx;
  2909. } // encrypt
  2910. /**
  2911. * Decrypt data
  2912. * @param index index
  2913. * @param yy data
  2914. * @return yy
  2915. */
  2916. private Object decrypt (int index, Object yy)
  2917. {
  2918. if (yy == null)
  2919. return null;
  2920. if (index != -1 && p_info.isEncrypted(index)) {
  2921. return SecureEngine.decrypt(yy, getAD_Client_ID());
  2922. }
  2923. return yy;
  2924. } // decrypt
  2925. /**************************************************************************
  2926. * Delete Current Record
  2927. * @param force delete also processed records
  2928. * @return true if deleted
  2929. */
  2930. public boolean delete (boolean force)
  2931. {
  2932. checkValidContext();
  2933. CLogger.resetLast();
  2934. if (is_new())
  2935. return true;
  2936. int AD_Table_ID = p_info.getAD_Table_ID();
  2937. int Record_ID = get_ID();
  2938. if (!force)
  2939. {
  2940. int iProcessed = get_ColumnIndex("Processed");
  2941. if (iProcessed != -1)
  2942. {
  2943. Boolean processed = (Boolean)get_Value(iProcessed);
  2944. if (processed != null && processed.booleanValue())
  2945. {
  2946. log.warning("Record processed"); // CannotDeleteTrx
  2947. log.saveError("Processed", "Processed", false);
  2948. return false;
  2949. }
  2950. } // processed
  2951. } // force
  2952. // Carlos Ruiz - globalqss - IDEMPIERE-111
  2953. // Check if the role has access to this client
  2954. // Don't check role System as webstore works with this role - see IDEMPIERE-401
  2955. if ((Env.getAD_Role_ID(getCtx()) != 0) && !MRole.getDefault().isClientAccess(getAD_Client_ID(), true))
  2956. {
  2957. log.warning("You cannot delete this record, role doesn't have access");
  2958. log.saveError("AccessCannotDelete", "", false);
  2959. return false;
  2960. }
  2961. Trx localTrx = null;
  2962. Trx trx = null;
  2963. Savepoint savepoint = null;
  2964. boolean success = false;
  2965. try
  2966. {
  2967. String localTrxName = m_trxName;
  2968. if (localTrxName == null)
  2969. {
  2970. localTrxName = Trx.createTrxName("POdel");
  2971. localTrx = Trx.get(localTrxName, true);
  2972. m_trxName = localTrxName;
  2973. }
  2974. else
  2975. {
  2976. trx = Trx.get(m_trxName, false);
  2977. if (trx == null)
  2978. {
  2979. // Using a trx that was previously closed or never opened
  2980. // Creating and starting the transaction right here, but please note
  2981. // that this is not a good practice
  2982. trx = Trx.get(m_trxName, true);
  2983. log.severe("Transaction closed or never opened ("+m_trxName+") => starting now --> " + toString());
  2984. }
  2985. }
  2986. try
  2987. {
  2988. // If not a localTrx we need to set a savepoint for rollback
  2989. if (localTrx == null)
  2990. savepoint = trx.setSavepoint(null);
  2991. if (!beforeDelete())
  2992. {
  2993. log.warning("beforeDelete failed");
  2994. if (localTrx != null)
  2995. {
  2996. localTrx.rollback();
  2997. }
  2998. else if (savepoint != null)
  2999. {
  3000. try {
  3001. trx.rollback(savepoint);
  3002. } catch (SQLException e) {}
  3003. savepoint = null;
  3004. }
  3005. return false;
  3006. }
  3007. }
  3008. catch (Exception e)
  3009. {
  3010. log.log(Level.WARNING, "beforeDelete", e);
  3011. String msg = DBException.getDefaultDBExceptionMessage(e);
  3012. log.saveError(msg != null ? msg : "Error", e, false);
  3013. if (localTrx != null)
  3014. {
  3015. localTrx.rollback();
  3016. }
  3017. else if (savepoint != null)
  3018. {
  3019. try {
  3020. trx.rollback(savepoint);
  3021. } catch (SQLException e1) {}
  3022. savepoint = null;
  3023. }
  3024. return false;
  3025. }
  3026. // Delete Restrict AD_Table_ID/Record_ID (Requests, ..)
  3027. String errorMsg = PO_Record.exists(AD_Table_ID, Record_ID, m_trxName);
  3028. if (errorMsg != null)
  3029. {
  3030. log.saveError("CannotDelete", errorMsg);
  3031. if (localTrx != null)
  3032. {
  3033. localTrx.rollback();
  3034. }
  3035. else if (savepoint != null)
  3036. {
  3037. try {
  3038. trx.rollback(savepoint);
  3039. } catch (SQLException e) {}
  3040. savepoint = null;
  3041. }
  3042. return false;
  3043. }
  3044. // Call ModelValidators TYPE_DELETE
  3045. errorMsg = ModelValidationEngine.get().fireModelChange
  3046. (this, isReplication() ? ModelValidator.TYPE_BEFORE_DELETE_REPLICATION : ModelValidator.TYPE_DELETE);
  3047. setReplication(false); // @Trifon
  3048. if (errorMsg != null)
  3049. {
  3050. log.saveError("Error", errorMsg);
  3051. if (localTrx != null)
  3052. {
  3053. localTrx.rollback();
  3054. }
  3055. else if (savepoint != null)
  3056. {
  3057. try {
  3058. trx.rollback(savepoint);
  3059. } catch (SQLException e) {}
  3060. savepoint = null;
  3061. }
  3062. return false;
  3063. }
  3064. try
  3065. {
  3066. //
  3067. deleteTranslations(localTrxName);
  3068. if (get_ColumnIndex("IsSummary") >= 0) {
  3069. delete_Tree(MTree_Base.TREETYPE_CustomTable);
  3070. }
  3071. // Delete Cascade AD_Table_ID/Record_ID (Attachments, ..)
  3072. PO_Record.deleteCascade(AD_Table_ID, Record_ID, localTrxName);
  3073. //delete cascade only for single key column record
  3074. if (m_KeyColumns != null && m_KeyColumns.length == 1) {
  3075. PO_Record.deleteModelCascade(p_info.getTableName(), Record_ID, localTrxName);
  3076. }
  3077. // The Delete Statement
  3078. StringBuilder sql = new StringBuilder ("DELETE FROM ") //jz why no FROM??
  3079. .append(p_info.getTableName())
  3080. .append(" WHERE ")
  3081. .append(get_WhereClause(true));
  3082. int no = 0;
  3083. if (isUseTimeoutForUpdate())
  3084. no = DB.executeUpdateEx(sql.toString(), localTrxName, QUERY_TIME_OUT);
  3085. else
  3086. no = DB.executeUpdate(sql.toString(), localTrxName);
  3087. success = no == 1;
  3088. }
  3089. catch (Exception e)
  3090. {
  3091. String msg = DBException.getDefaultDBExceptionMessage(e);
  3092. log.saveError(msg != null ? msg : e.getLocalizedMessage(), e);
  3093. success = false;
  3094. }
  3095. // Save ID
  3096. m_idOld = get_ID();
  3097. //
  3098. if (!success)
  3099. {
  3100. log.warning("Not deleted");
  3101. if (localTrx != null)
  3102. {
  3103. localTrx.rollback();
  3104. }
  3105. else if (savepoint != null)
  3106. {
  3107. try {
  3108. trx.rollback(savepoint);
  3109. } catch (SQLException e) {}
  3110. savepoint = null;
  3111. }
  3112. }
  3113. else
  3114. {
  3115. if (success)
  3116. {
  3117. if( p_info.isChangeLog())
  3118. {
  3119. // Change Log
  3120. MSession session = MSession.get (p_ctx, false);
  3121. if (session == null)
  3122. log.fine("No Session found");
  3123. else if (m_IDs.length == 1)
  3124. {
  3125. int AD_ChangeLog_ID = 0;
  3126. int size = get_ColumnCount();
  3127. for (int i = 0; i < size; i++)
  3128. {
  3129. Object value = m_oldValues[i];
  3130. if (value != null
  3131. && p_info.isAllowLogging(i) // logging allowed
  3132. && !p_info.isEncrypted(i) // not encrypted
  3133. && !p_info.isVirtualColumn(i) // no virtual column
  3134. && !"Password".equals(p_info.getColumnName(i))
  3135. )
  3136. {
  3137. // change log on delete
  3138. MChangeLog cLog = session.changeLog (
  3139. m_trxName != null ? m_trxName : localTrxName, AD_ChangeLog_ID,
  3140. AD_Table_ID, p_info.getColumn(i).AD_Column_ID,
  3141. Record_ID, getAD_Client_ID(), getAD_Org_ID(), value, null, MChangeLog.EVENTCHANGELOG_Delete);
  3142. if (cLog != null)
  3143. AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID();
  3144. }
  3145. } // for all fields
  3146. }
  3147. // Housekeeping
  3148. m_IDs[0] = I_ZERO;
  3149. if (m_trxName == null)
  3150. log.fine("complete");
  3151. else
  3152. if (log.isLoggable(Level.FINE)) log.fine("[" + m_trxName + "] - complete");
  3153. m_attachment = null;
  3154. }
  3155. }
  3156. else
  3157. {
  3158. log.warning("Not deleted");
  3159. }
  3160. }
  3161. try
  3162. {
  3163. success = afterDelete (success);
  3164. }
  3165. catch (Exception e)
  3166. {
  3167. log.log(Level.WARNING, "afterDelete", e);
  3168. String msg = DBException.getDefaultDBExceptionMessage(e);
  3169. log.saveError(msg != null ? msg : "Error", e, false);
  3170. success = false;
  3171. // throw new DBException(e);
  3172. }
  3173. // Call ModelValidators TYPE_AFTER_DELETE - teo_sarca [ 1675490 ]
  3174. if (success) {
  3175. errorMsg = ModelValidationEngine.get().fireModelChange(this, ModelValidator.TYPE_AFTER_DELETE);
  3176. if (errorMsg != null) {
  3177. log.saveError("Error", errorMsg);
  3178. success = false;
  3179. }
  3180. }
  3181. if (!success)
  3182. {
  3183. if (localTrx != null)
  3184. {
  3185. localTrx.rollback();
  3186. }
  3187. else if (savepoint != null)
  3188. {
  3189. try {
  3190. trx.rollback(savepoint);
  3191. } catch (SQLException e) {}
  3192. savepoint = null;
  3193. }
  3194. }
  3195. else
  3196. {
  3197. if (localTrx != null)
  3198. {
  3199. try {
  3200. localTrx.commit(true);
  3201. } catch (SQLException e) {
  3202. String msg = DBException.getDefaultDBExceptionMessage(e);
  3203. log.saveError(msg != null ? msg : "Error", e);
  3204. success = false;
  3205. }
  3206. }
  3207. }
  3208. // Reset
  3209. if (success)
  3210. {
  3211. //osgi event handler
  3212. Event event = EventManager.newEvent(IEventTopics.PO_POST_DELETE, this);
  3213. EventManager.getInstance().postEvent(event);
  3214. m_idOld = 0;
  3215. int size = p_info.getColumnCount();
  3216. m_oldValues = new Object[size];
  3217. m_newValues = new Object[size];
  3218. CacheMgt.get().reset(p_info.getTableName());
  3219. }
  3220. }
  3221. finally
  3222. {
  3223. if (localTrx != null)
  3224. {
  3225. localTrx.close();
  3226. m_trxName = null;
  3227. }
  3228. else
  3229. {
  3230. if (savepoint != null)
  3231. {
  3232. try {
  3233. trx.releaseSavepoint(savepoint);
  3234. } catch (SQLException e) {
  3235. e.printStackTrace();
  3236. }
  3237. }
  3238. savepoint = null;
  3239. trx = null;
  3240. }
  3241. }
  3242. return success;
  3243. } // delete
  3244. /**
  3245. * Delete Current Record
  3246. * @param force delete also processed records
  3247. * @throws AdempiereException
  3248. * @see #delete(boolean)
  3249. */
  3250. public void deleteEx(boolean force) throws AdempiereException
  3251. {
  3252. if (!delete(force)) {
  3253. String msg = null;
  3254. ValueNamePair err = CLogger.retrieveError();
  3255. if (err != null)
  3256. msg = err.getName();
  3257. if (msg == null || msg.length() == 0)
  3258. msg = "DeleteError";
  3259. throw new AdempiereException(msg);
  3260. }
  3261. }
  3262. /**
  3263. * Delete Current Record
  3264. * @param force delete also processed records
  3265. * @param trxName transaction
  3266. * @return true if deleted
  3267. */
  3268. public boolean delete (boolean force, String trxName)
  3269. {
  3270. set_TrxName(trxName);
  3271. return delete (force);
  3272. } // delete
  3273. /**
  3274. * Delete Current Record
  3275. * @param force delete also processed records
  3276. * @param trxName transaction
  3277. * @throws AdempiereException
  3278. * @see {@link #deleteEx(boolean)}
  3279. */
  3280. public void deleteEx(boolean force, String trxName) throws AdempiereException
  3281. {
  3282. set_TrxName(trxName);
  3283. deleteEx(force);
  3284. }
  3285. /**
  3286. * Executed before Delete operation.
  3287. * @return true if record can be deleted
  3288. */
  3289. protected boolean beforeDelete ()
  3290. {
  3291. // log.saveError("Error", Msg.getMsg(getCtx(), "CannotDelete"));
  3292. return true;
  3293. } // beforeDelete
  3294. /**
  3295. * Executed after Delete operation.
  3296. * @param success true if record deleted
  3297. * @return true if delete is a success
  3298. */
  3299. protected boolean afterDelete (boolean success)
  3300. {
  3301. return success;
  3302. } // afterDelete
  3303. /**
  3304. * Insert (missing) Translation Records
  3305. * @return false if error (true if no translation or success)
  3306. */
  3307. private boolean insertTranslations()
  3308. {
  3309. // Not a translation table
  3310. if (m_IDs.length > 1
  3311. || m_IDs[0].equals(I_ZERO)
  3312. || !(m_IDs[0] instanceof Integer)
  3313. || !p_info.isTranslated())
  3314. return true;
  3315. //
  3316. StringBuilder iColumns = new StringBuilder();
  3317. StringBuilder sColumns = new StringBuilder();
  3318. for (int i = 0; i < p_info.getColumnCount(); i++)
  3319. {
  3320. if (p_info.isColumnTranslated(i))
  3321. {
  3322. iColumns.append(p_info.getColumnName(i))
  3323. .append(",");
  3324. sColumns.append("t.")
  3325. .append(p_info.getColumnName(i))
  3326. .append(",");
  3327. }
  3328. }
  3329. if (iColumns.length() == 0)
  3330. return true;
  3331. String tableName = p_info.getTableName();
  3332. String keyColumn = m_KeyColumns[0];
  3333. //check whether db have working generate_uuid function.
  3334. boolean uuidFunction = DB.isGenerateUUIDSupported();
  3335. //uuid column
  3336. int uuidColumnId = DB.getSQLValue(get_TrxName(), "SELECT col.AD_Column_ID FROM AD_Column col INNER JOIN AD_Table tbl ON col.AD_Table_ID = tbl.AD_Table_ID WHERE tbl.TableName=? AND col.ColumnName=?",
  3337. tableName+"_Trl", PO.getUUIDColumnName(tableName+"_Trl"));
  3338. StringBuilder sql = new StringBuilder ("INSERT INTO ")
  3339. .append(tableName).append("_Trl (AD_Language,")
  3340. .append(keyColumn).append(", ")
  3341. .append(iColumns)
  3342. .append(" IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy");
  3343. if (uuidColumnId > 0 && uuidFunction)
  3344. sql.append(",").append(PO.getUUIDColumnName(tableName+"_Trl")).append(" ) ");
  3345. else
  3346. sql.append(" ) ");
  3347. sql.append("SELECT l.AD_Language,t.")
  3348. .append(keyColumn).append(", ")
  3349. .append(sColumns)
  3350. .append(" 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy");
  3351. if (uuidColumnId > 0 && uuidFunction)
  3352. sql.append(",Generate_UUID() ");
  3353. else
  3354. sql.append(" ");
  3355. sql.append("FROM AD_Language l, ").append(tableName).append(" t ")
  3356. .append("WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.")
  3357. .append(keyColumn).append("=").append(get_ID())
  3358. .append(" AND NOT EXISTS (SELECT * FROM ").append(tableName)
  3359. .append("_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.")
  3360. .append(keyColumn).append("=t.").append(keyColumn).append(")");
  3361. int no = DB.executeUpdate(sql.toString(), m_trxName);
  3362. if (uuidColumnId > 0 && !uuidFunction) {
  3363. MColumn column = new MColumn(getCtx(), uuidColumnId, get_TrxName());
  3364. UUIDGenerator.updateUUID(column, get_TrxName());
  3365. }
  3366. if (log.isLoggable(Level.FINE)) log.fine("#" + no);
  3367. return no > 0;
  3368. } // insertTranslations
  3369. /**
  3370. * Update Translations.
  3371. * @return false if error (true if no translation or success)
  3372. */
  3373. private boolean updateTranslations()
  3374. {
  3375. // Not a translation table
  3376. if (m_IDs.length > 1
  3377. || m_IDs[0].equals(I_ZERO)
  3378. || !(m_IDs[0] instanceof Integer)
  3379. || !p_info.isTranslated())
  3380. return true;
  3381. String tableName = p_info.getTableName();
  3382. if (tableName.startsWith("AD") && getAD_Client_ID() == 0)
  3383. return true;
  3384. //
  3385. boolean trlColumnChanged = false;
  3386. for (int i = 0; i < p_info.getColumnCount(); i++)
  3387. {
  3388. if (p_info.isColumnTranslated(i)
  3389. && is_ValueChanged(p_info.getColumnName(i)))
  3390. {
  3391. trlColumnChanged = true;
  3392. break;
  3393. }
  3394. }
  3395. if (!trlColumnChanged)
  3396. return true;
  3397. //
  3398. MClient client = MClient.get(getCtx());
  3399. //
  3400. String keyColumn = m_KeyColumns[0];
  3401. StringBuilder sqlupdate = new StringBuilder("UPDATE ")
  3402. .append(tableName).append("_Trl SET ");
  3403. //
  3404. StringBuilder sqlcols = new StringBuilder();
  3405. for (int i = 0; i < p_info.getColumnCount(); i++)
  3406. {
  3407. String columnName = p_info.getColumnName(i);
  3408. if (p_info.isColumnTranslated(i)
  3409. && is_ValueChanged(columnName))
  3410. {
  3411. sqlcols.append(columnName).append("=");
  3412. Object value = get_Value(columnName);
  3413. if (value == null)
  3414. sqlcols.append("NULL");
  3415. else if (value instanceof String)
  3416. sqlcols.append(DB.TO_STRING((String)value));
  3417. else if (value instanceof Boolean)
  3418. sqlcols.append(((Boolean)value).booleanValue() ? "'Y'" : "'N'");
  3419. else if (value instanceof Timestamp)
  3420. sqlcols.append(DB.TO_DATE((Timestamp)value));
  3421. else
  3422. sqlcols.append(value.toString());
  3423. sqlcols.append(",");
  3424. }
  3425. }
  3426. StringBuilder whereid = new StringBuilder(" WHERE ").append(keyColumn).append("=").append(get_ID());
  3427. StringBuilder andlang = new StringBuilder(" AND AD_Language=").append(DB.TO_STRING(client.getAD_Language()));
  3428. StringBuilder andnotlang = new StringBuilder(" AND AD_Language!=").append(DB.TO_STRING(client.getAD_Language()));
  3429. int no = -1;
  3430. if (client.isMultiLingualDocument()) {
  3431. String baselang = Language.getBaseAD_Language();
  3432. if (client.getAD_Language().equals(baselang)) {
  3433. // tenant language = base language
  3434. // set all translations as untranslated
  3435. StringBuilder sqlexec = new StringBuilder()
  3436. .append(sqlupdate)
  3437. .append("IsTranslated='N'")
  3438. .append(whereid);
  3439. no = DB.executeUpdate(sqlexec.toString(), m_trxName);
  3440. if (log.isLoggable(Level.FINE)) log.fine("#" + no);
  3441. } else {
  3442. // tenant language <> base language
  3443. // auto update translation for tenant language
  3444. StringBuilder sqlexec = new StringBuilder()
  3445. .append(sqlupdate)
  3446. .append(sqlcols)
  3447. .append("IsTranslated='Y'")
  3448. .append(whereid)
  3449. .append(andlang);
  3450. no = DB.executeUpdate(sqlexec.toString(), m_trxName);
  3451. if (log.isLoggable(Level.FINE)) log.fine("#" + no);
  3452. if (no >= 0) {
  3453. // set other translations as untranslated
  3454. sqlexec = new StringBuilder()
  3455. .append(sqlupdate)
  3456. .append("IsTranslated='N'")
  3457. .append(whereid)
  3458. .append(andnotlang);
  3459. no = DB.executeUpdate(sqlexec.toString(), m_trxName);
  3460. if (log.isLoggable(Level.FINE)) log.fine("#" + no);
  3461. }
  3462. }
  3463. } else {
  3464. // auto update all translations
  3465. StringBuilder sqlexec = new StringBuilder()
  3466. .append(sqlupdate)
  3467. .append(sqlcols)
  3468. .append("IsTranslated='Y'")
  3469. .append(whereid);
  3470. no = DB.executeUpdate(sqlexec.toString(), m_trxName);
  3471. if (log.isLoggable(Level.FINE)) log.fine("#" + no);
  3472. }
  3473. return no >= 0;
  3474. } // updateTranslations
  3475. /**
  3476. * Delete Translation Records
  3477. * @param trxName transaction
  3478. * @return false if error (true if no translation or success)
  3479. */
  3480. private boolean deleteTranslations(String trxName)
  3481. {
  3482. // Not a translation table
  3483. if (m_IDs.length > 1
  3484. || m_IDs[0].equals(I_ZERO)
  3485. || !(m_IDs[0] instanceof Integer)
  3486. || !p_info.isTranslated())
  3487. return true;
  3488. //
  3489. String tableName = p_info.getTableName();
  3490. String keyColumn = m_KeyColumns[0];
  3491. StringBuilder sql = new StringBuilder ("DELETE FROM ")
  3492. .append(tableName).append("_Trl WHERE ")
  3493. .append(keyColumn).append("=").append(get_ID());
  3494. int no = DB.executeUpdate(sql.toString(), trxName);
  3495. if (log.isLoggable(Level.FINE)) log.fine("#" + no);
  3496. return no >= 0;
  3497. } // deleteTranslations
  3498. /**
  3499. * Insert Accounting Records
  3500. * @param acctTable accounting sub table
  3501. * @param acctBaseTable acct table to get data from
  3502. * @param whereClause optional where clause with alias "p" for acctBaseTable
  3503. * @return true if records inserted
  3504. */
  3505. protected boolean insert_Accounting (String acctTable,
  3506. String acctBaseTable, String whereClause)
  3507. {
  3508. if (s_acctColumns == null // cannot cache C_BP_*_Acct as there are 3
  3509. || acctTable.startsWith("C_BP_"))
  3510. {
  3511. s_acctColumns = new ArrayList<String>();
  3512. String sql = "SELECT c.ColumnName "
  3513. + "FROM AD_Column c INNER JOIN AD_Table t ON (c.AD_Table_ID=t.AD_Table_ID) "
  3514. + "WHERE t.TableName=? AND c.IsActive='Y' AND c.AD_Reference_ID=25 ORDER BY c.ColumnName";
  3515. PreparedStatement pstmt = null;
  3516. ResultSet rs = null;
  3517. try
  3518. {
  3519. pstmt = DB.prepareStatement (sql, null);
  3520. pstmt.setString (1, acctTable);
  3521. rs = pstmt.executeQuery ();
  3522. while (rs.next ())
  3523. s_acctColumns.add (rs.getString(1));
  3524. }
  3525. catch (Exception e)
  3526. {
  3527. log.log(Level.SEVERE, acctTable, e);
  3528. }
  3529. finally {
  3530. DB.close(rs, pstmt);
  3531. rs = null; pstmt = null;
  3532. }
  3533. if (s_acctColumns.size() == 0)
  3534. {
  3535. log.severe ("No Columns for " + acctTable);
  3536. return false;
  3537. }
  3538. }
  3539. // Create SQL Statement - INSERT
  3540. StringBuilder sb = new StringBuilder("INSERT INTO ")
  3541. .append(acctTable)
  3542. .append(" (").append(get_TableName())
  3543. .append("_ID, C_AcctSchema_ID, AD_Client_ID,AD_Org_ID,IsActive, Created,CreatedBy,Updated,UpdatedBy ");
  3544. for (int i = 0; i < s_acctColumns.size(); i++)
  3545. sb.append(",").append(s_acctColumns.get(i));
  3546. //check whether db have working generate_uuid function.
  3547. boolean uuidFunction = DB.isGenerateUUIDSupported();
  3548. //uuid column
  3549. int uuidColumnId = DB.getSQLValue(get_TrxName(), "SELECT col.AD_Column_ID FROM AD_Column col INNER JOIN AD_Table tbl ON col.AD_Table_ID = tbl.AD_Table_ID WHERE tbl.TableName=? AND col.ColumnName=?",
  3550. acctTable, PO.getUUIDColumnName(acctTable));
  3551. if (uuidColumnId > 0 && uuidFunction)
  3552. sb.append(",").append(PO.getUUIDColumnName(acctTable));
  3553. // .. SELECT
  3554. sb.append(") SELECT ").append(get_ID())
  3555. .append(", p.C_AcctSchema_ID, p.AD_Client_ID,0,'Y', SysDate,")
  3556. .append(getUpdatedBy()).append(",SysDate,").append(getUpdatedBy());
  3557. for (int i = 0; i < s_acctColumns.size(); i++)
  3558. sb.append(",p.").append(s_acctColumns.get(i));
  3559. //uuid column
  3560. if (uuidColumnId > 0 && uuidFunction)
  3561. sb.append(",generate_uuid()");
  3562. // .. FROM
  3563. sb.append(" FROM ").append(acctBaseTable)
  3564. .append(" p WHERE p.AD_Client_ID=").append(getAD_Client_ID());
  3565. if (whereClause != null && whereClause.length() > 0)
  3566. sb.append (" AND ").append(whereClause);
  3567. sb.append(" AND NOT EXISTS (SELECT * FROM ").append(acctTable)
  3568. .append(" e WHERE e.C_AcctSchema_ID=p.C_AcctSchema_ID AND e.")
  3569. .append(get_TableName()).append("_ID=").append(get_ID()).append(")");
  3570. //
  3571. int no = DB.executeUpdate(sb.toString(), get_TrxName());
  3572. if (no > 0) {
  3573. if (log.isLoggable(Level.FINE)) log.fine("#" + no);
  3574. } else {
  3575. log.warning("#" + no
  3576. + " - Table=" + acctTable + " from " + acctBaseTable);
  3577. }
  3578. //fall back to the slow java client update code
  3579. if (uuidColumnId > 0 && !uuidFunction) {
  3580. MColumn column = new MColumn(getCtx(), uuidColumnId, get_TrxName());
  3581. UUIDGenerator.updateUUID(column, get_TrxName());
  3582. }
  3583. return no > 0;
  3584. } // insert_Accounting
  3585. /**
  3586. * Delete Accounting records.
  3587. * NOP - done by database constraints
  3588. * @param acctTable accounting sub table
  3589. * @return true
  3590. */
  3591. @Deprecated // see IDEMPIERE-2088
  3592. protected boolean delete_Accounting(String acctTable)
  3593. {
  3594. return true;
  3595. } // delete_Accounting
  3596. /**
  3597. * Insert id data into Tree
  3598. * @param treeType MTree TREETYPE_*
  3599. * @return true if inserted
  3600. */
  3601. protected boolean insert_Tree (String treeType)
  3602. {
  3603. return insert_Tree (treeType, 0);
  3604. } // insert_Tree
  3605. /**
  3606. * Insert id data into Tree
  3607. * @param treeType MTree TREETYPE_*
  3608. * @param C_Element_ID element for accounting element values
  3609. * @return true if inserted
  3610. */
  3611. protected boolean insert_Tree (String treeType, int C_Element_ID)
  3612. {
  3613. String tableName = MTree_Base.getNodeTableName(treeType);
  3614. //check whether db have working generate_uuid function.
  3615. boolean uuidFunction = DB.isGenerateUUIDSupported();
  3616. //uuid column
  3617. int uuidColumnId = DB.getSQLValue(get_TrxName(), "SELECT col.AD_Column_ID FROM AD_Column col INNER JOIN AD_Table tbl ON col.AD_Table_ID = tbl.AD_Table_ID WHERE tbl.TableName=? AND col.ColumnName=?",
  3618. tableName, PO.getUUIDColumnName(tableName));
  3619. StringBuilder sb = new StringBuilder ("INSERT INTO ")
  3620. .append(tableName)
  3621. .append(" (AD_Client_ID,AD_Org_ID, IsActive,Created,CreatedBy,Updated,UpdatedBy, "
  3622. + "AD_Tree_ID, Node_ID, Parent_ID, SeqNo");
  3623. if (uuidColumnId > 0 && uuidFunction)
  3624. sb.append(", ").append(PO.getUUIDColumnName(tableName)).append(") ");
  3625. else
  3626. sb.append(") ");
  3627. sb.append("SELECT t.AD_Client_ID, 0, 'Y', SysDate, "+getUpdatedBy()+", SysDate, "+getUpdatedBy()+","
  3628. + "t.AD_Tree_ID, ").append(get_ID()).append(", 0, 999");
  3629. if (uuidColumnId > 0 && uuidFunction)
  3630. sb.append(", Generate_UUID() ");
  3631. else
  3632. sb.append(" ");
  3633. sb.append("FROM AD_Tree t "
  3634. + "WHERE t.AD_Client_ID=").append(getAD_Client_ID()).append(" AND t.IsActive='Y'");
  3635. // Account Element Value handling
  3636. if (C_Element_ID != 0)
  3637. sb.append(" AND EXISTS (SELECT * FROM C_Element ae WHERE ae.C_Element_ID=")
  3638. .append(C_Element_ID).append(" AND t.AD_Tree_ID=ae.AD_Tree_ID)");
  3639. else // std trees
  3640. sb.append(" AND t.IsAllNodes='Y' AND t.TreeType='").append(treeType).append("'");
  3641. if (MTree_Base.TREETYPE_CustomTable.equals(treeType))
  3642. sb.append(" AND t.AD_Table_ID=").append(get_Table_ID());
  3643. // Duplicate Check
  3644. sb.append(" AND NOT EXISTS (SELECT * FROM " + MTree_Base.getNodeTableName(treeType) + " e "
  3645. + "WHERE e.AD_Tree_ID=t.AD_Tree_ID AND Node_ID=").append(get_ID()).append(")");
  3646. int no = DB.executeUpdate(sb.toString(), get_TrxName());
  3647. if (no > 0) {
  3648. if (log.isLoggable(Level.FINE)) log.fine("#" + no + " - TreeType=" + treeType);
  3649. } else {
  3650. if (! MTree_Base.TREETYPE_CustomTable.equals(treeType))
  3651. log.warning("#" + no + " - TreeType=" + treeType);
  3652. }
  3653. if (uuidColumnId > 0 && !uuidFunction )
  3654. {
  3655. MColumn column = new MColumn(getCtx(), uuidColumnId, get_TrxName());
  3656. UUIDGenerator.updateUUID(column, get_TrxName());
  3657. }
  3658. return no > 0;
  3659. } // insert_Tree
  3660. /**
  3661. * Update parent key and seqno based on value if the tree is driven by value
  3662. * @param treeType MTree TREETYPE_*
  3663. * @return true if inserted
  3664. */
  3665. public void update_Tree (String treeType)
  3666. {
  3667. int idxValueCol = get_ColumnIndex("Value");
  3668. if (idxValueCol < 0)
  3669. return;
  3670. int idxValueIsSummary = get_ColumnIndex("IsSummary");
  3671. if (idxValueIsSummary < 0)
  3672. return;
  3673. String value = get_Value(idxValueCol).toString();
  3674. if (value == null)
  3675. return;
  3676. String tableName = MTree_Base.getNodeTableName(treeType);
  3677. String sourceTableName;
  3678. String whereTree;
  3679. Object[] parameters;
  3680. if (MTree_Base.TREETYPE_CustomTable.equals(treeType)) {
  3681. sourceTableName = this.get_TableName();
  3682. whereTree = "TreeType=? AND AD_Table_ID=?";
  3683. parameters = new Object[]{treeType, this.get_Table_ID()};
  3684. } else {
  3685. sourceTableName = MTree_Base.getSourceTableName(treeType);
  3686. if (MTree_Base.TREETYPE_ElementValue.equals(treeType) && this instanceof I_C_ElementValue) {
  3687. whereTree = "TreeType=? AND AD_Tree_ID=?";
  3688. parameters = new Object[]{treeType, ((I_C_ElementValue)this).getC_Element().getAD_Tree_ID()};
  3689. } else {
  3690. whereTree = "TreeType=?";
  3691. parameters = new Object[]{treeType};
  3692. }
  3693. }
  3694. String updateSeqNo = "UPDATE " + tableName + " SET SeqNo=SeqNo+1 WHERE Parent_ID=? AND SeqNo>=? AND AD_Tree_ID=?";
  3695. String update = "UPDATE " + tableName + " SET SeqNo=?, Parent_ID=? WHERE Node_ID=? AND AD_Tree_ID=?";
  3696. String selMinSeqNo = "SELECT COALESCE(MIN(tn.SeqNo),-1) FROM AD_TreeNode tn JOIN " + sourceTableName + " n ON (tn.Node_ID=n." + sourceTableName + "_ID) WHERE tn.Parent_ID=? AND tn.AD_Tree_ID=? AND n.Value>?";
  3697. String selMaxSeqNo = "SELECT COALESCE(MAX(tn.SeqNo)+1,999) FROM AD_TreeNode tn JOIN " + sourceTableName + " n ON (tn.Node_ID=n." + sourceTableName + "_ID) WHERE tn.Parent_ID=? AND tn.AD_Tree_ID=? AND n.Value<?";
  3698. List<MTree_Base> trees = new Query(getCtx(), MTree_Base.Table_Name, whereTree, get_TrxName())
  3699. .setClient_ID()
  3700. .setOnlyActiveRecords(true)
  3701. .setParameters(parameters)
  3702. .list();
  3703. for (MTree_Base tree : trees) {
  3704. if (tree.isTreeDrivenByValue()) {
  3705. int newParentID = -1;
  3706. if (I_C_ElementValue.Table_Name.equals(sourceTableName)) {
  3707. newParentID = retrieveIdOfElementValue(value, getAD_Client_ID(), ((I_C_ElementValue)this).getC_Element().getC_Element_ID(), get_TrxName());
  3708. } else {
  3709. newParentID = retrieveIdOfParentValue(value, sourceTableName, getAD_Client_ID(), get_TrxName());
  3710. }
  3711. int seqNo = DB.getSQLValueEx(get_TrxName(), selMinSeqNo, newParentID, tree.getAD_Tree_ID(), value);
  3712. if (seqNo == -1)
  3713. seqNo = DB.getSQLValueEx(get_TrxName(), selMaxSeqNo, newParentID, tree.getAD_Tree_ID(), value);
  3714. DB.executeUpdateEx(updateSeqNo, new Object[] {newParentID, seqNo, tree.getAD_Tree_ID()}, get_TrxName());
  3715. DB.executeUpdateEx(update, new Object[] {seqNo, newParentID, get_ID(), tree.getAD_Tree_ID()}, get_TrxName());
  3716. }
  3717. }
  3718. } // update_Tree
  3719. /** Returns the summary node from C_ElementValue with the corresponding value */
  3720. private int retrieveIdOfElementValue(String value, int clientID, int elementID, String trxName)
  3721. {
  3722. String sql = "SELECT C_ElementValue_ID FROM C_ElementValue WHERE IsSummary='Y' AND AD_Client_ID=? AND C_Element_ID=? AND Value=?";
  3723. int pos = value.length()-1;
  3724. while (pos > 0) {
  3725. String testParentValue = value.substring(0, pos);
  3726. int parentID = DB.getSQLValueEx(trxName, sql, clientID, elementID, testParentValue);
  3727. if (parentID > 0)
  3728. return parentID;
  3729. pos--;
  3730. }
  3731. return 0; // rootID
  3732. }
  3733. /** Returns the summary node with the corresponding value */
  3734. public static int retrieveIdOfParentValue(String value, String tableName, int clientID, String trxName)
  3735. {
  3736. String sql = "SELECT " + tableName + "_ID FROM " + tableName + " WHERE IsSummary='Y' AND AD_Client_ID=? AND Value=?";
  3737. int pos = value.length()-1;
  3738. while (pos > 0) {
  3739. String testParentValue = value.substring(0, pos);
  3740. int parentID = DB.getSQLValueEx(trxName, sql, clientID, testParentValue);
  3741. if (parentID > 0)
  3742. return parentID;
  3743. pos--;
  3744. }
  3745. return 0; // rootID
  3746. }
  3747. /**
  3748. * Delete ID Tree Nodes
  3749. * @param treeType MTree TREETYPE_*
  3750. * @return true if deleted
  3751. */
  3752. protected boolean delete_Tree (String treeType)
  3753. {
  3754. int id = get_ID();
  3755. if (id == 0)
  3756. id = get_IDOld();
  3757. // IDEMPIERE-2453
  3758. StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM ")
  3759. .append(MTree_Base.getNodeTableName(treeType))
  3760. .append(" n JOIN AD_Tree t ON n.AD_Tree_ID=t.AD_Tree_ID")
  3761. .append(" WHERE Parent_ID=? AND t.TreeType=?");
  3762. if (MTree_Base.TREETYPE_CustomTable.equals(treeType))
  3763. countSql.append(" AND t.AD_Table_ID=").append(get_Table_ID());
  3764. int cnt = DB.getSQLValueEx( get_TrxName(), countSql.toString(), id, treeType);
  3765. if (cnt > 0)
  3766. throw new AdempiereException(Msg.getMsg(Env.getCtx(),"NoParentDelete", new Object[] {cnt}));
  3767. StringBuilder sb = new StringBuilder ("DELETE FROM ")
  3768. .append(MTree_Base.getNodeTableName(treeType))
  3769. .append(" n WHERE Node_ID=").append(id)
  3770. .append(" AND EXISTS (SELECT * FROM AD_Tree t "
  3771. + "WHERE t.AD_Tree_ID=n.AD_Tree_ID AND t.TreeType='")
  3772. .append(treeType).append("'");
  3773. if (MTree_Base.TREETYPE_CustomTable.equals(treeType))
  3774. sb.append(" AND t.AD_Table_ID=").append(get_Table_ID());
  3775. sb.append(")");
  3776. int no = DB.executeUpdate(sb.toString(), get_TrxName());
  3777. if (no > 0) {
  3778. if (log.isLoggable(Level.FINE)) log.fine("#" + no + " - TreeType=" + treeType);
  3779. } else {
  3780. if (! MTree_Base.TREETYPE_CustomTable.equals(treeType))
  3781. log.warning("#" + no + " - TreeType=" + treeType);
  3782. }
  3783. return no > 0;
  3784. } // delete_Tree
  3785. /**************************************************************************
  3786. * Lock it.
  3787. * @return true if locked
  3788. */
  3789. public boolean lock()
  3790. {
  3791. int index = get_ProcessingIndex();
  3792. if (index != -1)
  3793. {
  3794. m_newValues[index] = Boolean.TRUE; // direct
  3795. String sql = "UPDATE " + p_info.getTableName()
  3796. + " SET Processing='Y' WHERE (Processing='N' OR Processing IS NULL) AND "
  3797. + get_WhereClause(true);
  3798. boolean success = false;
  3799. if (isUseTimeoutForUpdate())
  3800. success = DB.executeUpdateEx(sql, null, QUERY_TIME_OUT) == 1; // outside trx
  3801. else
  3802. success = DB.executeUpdate(sql, null) == 1; // outside trx
  3803. if (success)
  3804. log.fine("success");
  3805. else
  3806. log.log(Level.WARNING, "failed");
  3807. return success;
  3808. }
  3809. return false;
  3810. } // lock
  3811. /**
  3812. * Get the Column Processing index
  3813. * @return index or -1
  3814. */
  3815. private int get_ProcessingIndex()
  3816. {
  3817. return p_info.getColumnIndex("Processing");
  3818. } // getProcessingIndex
  3819. /**
  3820. * UnLock it
  3821. * @param trxName transaction
  3822. * @return true if unlocked (false only if unlock fails)
  3823. */
  3824. public boolean unlock (String trxName)
  3825. {
  3826. // log.warning(trxName);
  3827. int index = get_ProcessingIndex();
  3828. if (index != -1)
  3829. {
  3830. m_newValues[index] = Boolean.FALSE; // direct
  3831. String sql = "UPDATE " + p_info.getTableName()
  3832. + " SET Processing='N' WHERE " + get_WhereClause(true);
  3833. boolean success = false;
  3834. if (isUseTimeoutForUpdate())
  3835. success = DB.executeUpdateEx(sql, trxName, QUERY_TIME_OUT) == 1;
  3836. else
  3837. success = DB.executeUpdate(sql, trxName) == 1;
  3838. if (success) {
  3839. if (log.isLoggable(Level.FINE)) log.fine("success" + (trxName == null ? "" : "[" + trxName + "]"));
  3840. } else {
  3841. log.log(Level.WARNING, "failed" + (trxName == null ? "" : " [" + trxName + "]"));
  3842. }
  3843. return success;
  3844. }
  3845. return true;
  3846. } // unlock
  3847. /** Optional Transaction */
  3848. private String m_trxName = null;
  3849. /**
  3850. * Set Trx
  3851. * @param trxName transaction
  3852. */
  3853. public void set_TrxName (String trxName)
  3854. {
  3855. m_trxName = trxName;
  3856. } // setTrx
  3857. /**
  3858. * Get Trx
  3859. * @return transaction
  3860. */
  3861. public String get_TrxName()
  3862. {
  3863. return m_trxName;
  3864. } // getTrx
  3865. /**************************************************************************
  3866. * Get Attachments.
  3867. * An attachment may have multiple entries
  3868. * @return Attachment or null
  3869. */
  3870. public MAttachment getAttachment ()
  3871. {
  3872. return getAttachment(false);
  3873. } // getAttachment
  3874. /**
  3875. * Get Attachments
  3876. * @param requery requery
  3877. * @return Attachment or null
  3878. */
  3879. public MAttachment getAttachment (boolean requery)
  3880. {
  3881. if (m_attachment == null || requery)
  3882. m_attachment = MAttachment.get (getCtx(), p_info.getAD_Table_ID(), get_ID());
  3883. return m_attachment;
  3884. } // getAttachment
  3885. /**
  3886. * Create/return Attachment for PO.
  3887. * If not exist, create new
  3888. * @return attachment
  3889. */
  3890. public MAttachment createAttachment()
  3891. {
  3892. getAttachment (false);
  3893. if (m_attachment == null)
  3894. m_attachment = new MAttachment (getCtx(), p_info.getAD_Table_ID(), get_ID(), null);
  3895. return m_attachment;
  3896. } // createAttachment
  3897. /**
  3898. * Do we have a Attachment of type
  3899. * @param extension extension e.g. .pdf
  3900. * @return true if there is a attachment of type
  3901. */
  3902. public boolean isAttachment (String extension)
  3903. {
  3904. getAttachment (false);
  3905. if (m_attachment == null)
  3906. return false;
  3907. for (int i = 0; i < m_attachment.getEntryCount(); i++)
  3908. {
  3909. if (m_attachment.getEntryName(i).endsWith(extension))
  3910. {
  3911. if (log.isLoggable(Level.FINE)) log.fine("#" + i + ": " + m_attachment.getEntryName(i));
  3912. return true;
  3913. }
  3914. }
  3915. return false;
  3916. } // isAttachment
  3917. /**
  3918. * Get Attachment Data of type
  3919. * @param extension extension e.g. .pdf
  3920. * @return data or null
  3921. */
  3922. public byte[] getAttachmentData (String extension)
  3923. {
  3924. getAttachment(false);
  3925. if (m_attachment == null)
  3926. return null;
  3927. for (int i = 0; i < m_attachment.getEntryCount(); i++)
  3928. {
  3929. if (m_attachment.getEntryName(i).endsWith(extension))
  3930. {
  3931. if (log.isLoggable(Level.FINE)) log.fine("#" + i + ": " + m_attachment.getEntryName(i));
  3932. return m_attachment.getEntryData(i);
  3933. }
  3934. }
  3935. return null;
  3936. } // getAttachmentData
  3937. /**
  3938. * Do we have a PDF Attachment
  3939. * @return true if there is a PDF attachment
  3940. */
  3941. public boolean isPdfAttachment()
  3942. {
  3943. return isAttachment(".pdf");
  3944. } // isPdfAttachment
  3945. /**
  3946. * Get PDF Attachment Data
  3947. * @return data or null
  3948. */
  3949. public byte[] getPdfAttachment()
  3950. {
  3951. return getAttachmentData(".pdf");
  3952. } // getPDFAttachment
  3953. /**************************************************************************
  3954. * Dump Record
  3955. */
  3956. public void dump ()
  3957. {
  3958. if (CLogMgt.isLevelFinest())
  3959. {
  3960. log.finer(get_WhereClause (true));
  3961. for (int i = 0; i < get_ColumnCount (); i++)
  3962. dump (i);
  3963. }
  3964. } // dump
  3965. /**
  3966. * Dump column
  3967. * @param index index
  3968. */
  3969. public void dump (int index)
  3970. {
  3971. StringBuilder sb = new StringBuilder(" ").append(index);
  3972. if (index < 0 || index >= get_ColumnCount())
  3973. {
  3974. if (log.isLoggable(Level.FINEST)) log.finest(sb.append(": invalid").toString());
  3975. return;
  3976. }
  3977. sb.append(": ").append(get_ColumnName(index))
  3978. .append(" = ").append(m_oldValues[index])
  3979. .append(" (").append(m_newValues[index]).append(")");
  3980. if (log.isLoggable(Level.FINEST)) log.finest(sb.toString());
  3981. } // dump
  3982. /*************************************************************************
  3983. * Get All IDs of Table.
  3984. * Used for listing all Entities
  3985. * <code>
  3986. int[] IDs = PO.getAllIDs ("AD_PrintFont", null);
  3987. for (int i = 0; i < IDs.length; i++)
  3988. {
  3989. pf = new MPrintFont(Env.getCtx(), IDs[i]);
  3990. System.out.println(IDs[i] + " = " + pf.getFont());
  3991. }
  3992. * </code>
  3993. * @param TableName table name (key column with _ID)
  3994. * @param WhereClause optional where clause
  3995. * @return array of IDs or null
  3996. * @param trxName transaction
  3997. */
  3998. public static int[] getAllIDs (String TableName, String WhereClause, String trxName)
  3999. {
  4000. ArrayList<Integer> list = new ArrayList<Integer>();
  4001. StringBuilder sql = new StringBuilder("SELECT ");
  4002. sql.append(TableName).append("_ID FROM ").append(TableName);
  4003. if (WhereClause != null && WhereClause.length() > 0)
  4004. sql.append(" WHERE ").append(WhereClause);
  4005. PreparedStatement pstmt = null;
  4006. ResultSet rs = null;
  4007. try
  4008. {
  4009. pstmt = DB.prepareStatement(sql.toString(), trxName);
  4010. rs = pstmt.executeQuery();
  4011. while (rs.next())
  4012. list.add(new Integer(rs.getInt(1)));
  4013. }
  4014. catch (SQLException e)
  4015. {
  4016. s_log.log(Level.SEVERE, sql.toString(), e);
  4017. return null;
  4018. }
  4019. finally {
  4020. DB.close(rs, pstmt);
  4021. rs = null; pstmt = null;
  4022. }
  4023. // Convert to array
  4024. int[] retValue = new int[list.size()];
  4025. for (int i = 0; i < retValue.length; i++)
  4026. retValue[i] = ((Integer)list.get(i)).intValue();
  4027. return retValue;
  4028. } // getAllIDs
  4029. /**
  4030. * Get Find parameter.
  4031. * Convert to upper case and add % at the end
  4032. * @param query in string
  4033. * @return out string
  4034. */
  4035. protected static String getFindParameter (String query)
  4036. {
  4037. if (query == null)
  4038. return null;
  4039. if (query.length() == 0 || query.equals("%"))
  4040. return null;
  4041. if (!query.endsWith("%"))
  4042. query += "%";
  4043. return query.toUpperCase();
  4044. } // getFindParameter
  4045. /**************************************************************************
  4046. * Load LOB
  4047. * @param value LOB
  4048. * @return object
  4049. */
  4050. private Object get_LOB (Object value)
  4051. {
  4052. if (log.isLoggable(Level.FINE)) log.fine("Value=" + value);
  4053. if (value == null)
  4054. return null;
  4055. //
  4056. Object retValue = null;
  4057. long length = -99;
  4058. try
  4059. {
  4060. //[ 1643996 ] Chat not working in postgres port
  4061. if (value instanceof String ||
  4062. value instanceof byte[])
  4063. retValue = value;
  4064. else if (value instanceof Clob) // returns String
  4065. {
  4066. Clob clob = (Clob)value;
  4067. length = clob.length();
  4068. retValue = clob.getSubString(1, (int)length);
  4069. }
  4070. else if (value instanceof Blob) // returns byte[]
  4071. {
  4072. Blob blob = (Blob)value;
  4073. length = blob.length();
  4074. int index = 1; // correct
  4075. if (blob.getClass().getName().equals("oracle.jdbc.rowset.OracleSerialBlob"))
  4076. index = 0; // Oracle Bug Invalid Arguments
  4077. // at oracle.jdbc.rowset.OracleSerialBlob.getBytes(OracleSerialBlob.java:130)
  4078. retValue = blob.getBytes(index, (int)length);
  4079. }
  4080. else
  4081. log.log(Level.SEVERE, "Unknown: " + value);
  4082. }
  4083. catch (Exception e)
  4084. {
  4085. log.log(Level.SEVERE, "Length=" + length, e);
  4086. }
  4087. return retValue;
  4088. } // getLOB
  4089. /** LOB Info */
  4090. private ArrayList<PO_LOB> m_lobInfo = null;
  4091. /**
  4092. * Reset LOB info
  4093. */
  4094. private void lobReset()
  4095. {
  4096. m_lobInfo = null;
  4097. } // resetLOB
  4098. /**
  4099. * Prepare LOB save
  4100. * @param value value
  4101. * @param index index
  4102. * @param displayType display type
  4103. */
  4104. private void lobAdd (Object value, int index, int displayType)
  4105. {
  4106. if (log.isLoggable(Level.FINEST)) log.finest("Value=" + value);
  4107. PO_LOB lob = new PO_LOB (p_info.getTableName(), get_ColumnName(index),
  4108. get_WhereClause(true), displayType, value);
  4109. if (m_lobInfo == null)
  4110. m_lobInfo = new ArrayList<PO_LOB>();
  4111. m_lobInfo.add(lob);
  4112. } // lobAdd
  4113. /**
  4114. * Save LOB
  4115. * @return true if saved or ok
  4116. */
  4117. private boolean lobSave ()
  4118. {
  4119. if (m_lobInfo == null)
  4120. return true;
  4121. boolean retValue = true;
  4122. for (int i = 0; i < m_lobInfo.size(); i++)
  4123. {
  4124. PO_LOB lob = (PO_LOB)m_lobInfo.get(i);
  4125. if (!lob.save(get_TrxName()))
  4126. {
  4127. retValue = false;
  4128. break;
  4129. }
  4130. } // for all LOBs
  4131. lobReset();
  4132. return retValue;
  4133. } // saveLOB
  4134. /**
  4135. * Get Object xml representation as string
  4136. * @param xml optional string buffer
  4137. * @return updated/new string buffer header is only added once
  4138. */
  4139. public StringBuffer get_xmlString (StringBuffer xml)
  4140. {
  4141. if (xml == null)
  4142. xml = new StringBuffer();
  4143. else
  4144. xml.append(Env.NL);
  4145. //
  4146. try
  4147. {
  4148. StringWriter writer = new StringWriter();
  4149. StreamResult result = new StreamResult(writer);
  4150. DOMSource source = new DOMSource(get_xmlDocument(xml.length()!=0));
  4151. TransformerFactory tFactory = TransformerFactory.newInstance();
  4152. Transformer transformer = tFactory.newTransformer();
  4153. transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes");
  4154. transformer.transform (source, result);
  4155. StringBuffer newXML = writer.getBuffer();
  4156. //
  4157. if (xml.length() != 0)
  4158. { // // <?xml version="1.0" encoding="UTF-8"?>
  4159. int tagIndex = newXML.indexOf("?>");
  4160. if (tagIndex != -1)
  4161. xml.append(newXML.substring(tagIndex+2));
  4162. else
  4163. xml.append(newXML);
  4164. }
  4165. else
  4166. xml.append(newXML);
  4167. }
  4168. catch (Exception e)
  4169. {
  4170. log.log(Level.SEVERE, "", e);
  4171. }
  4172. return xml;
  4173. } // get_xmlString
  4174. /** Table ID Attribute */
  4175. protected final static String XML_ATTRIBUTE_AD_Table_ID = "AD_Table_ID";
  4176. /** Record ID Attribute */
  4177. protected final static String XML_ATTRIBUTE_Record_ID = "Record_ID";
  4178. /**
  4179. * Get XML Document representation
  4180. * @param noComment do not add comment
  4181. * @return XML document
  4182. */
  4183. public Document get_xmlDocument(boolean noComment)
  4184. {
  4185. Document document = null;
  4186. try
  4187. {
  4188. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  4189. DocumentBuilder builder = factory.newDocumentBuilder();
  4190. document = builder.newDocument();
  4191. if (!noComment)
  4192. document.appendChild(document.createComment(Adempiere.getSummaryAscii()));
  4193. }
  4194. catch (Exception e)
  4195. {
  4196. log.log(Level.SEVERE, "", e);
  4197. }
  4198. // Root
  4199. Element root = document.createElement(get_TableName());
  4200. root.setAttribute(XML_ATTRIBUTE_AD_Table_ID, String.valueOf(get_Table_ID()));
  4201. root.setAttribute(XML_ATTRIBUTE_Record_ID, String.valueOf(get_ID()));
  4202. document.appendChild(root);
  4203. // Columns
  4204. int size = get_ColumnCount();
  4205. for (int i = 0; i < size; i++)
  4206. {
  4207. if (p_info.isVirtualColumn(i))
  4208. continue;
  4209. Element col = document.createElement(p_info.getColumnName(i));
  4210. //
  4211. Object value = get_Value(i);
  4212. // Display Type
  4213. int dt = p_info.getColumnDisplayType(i);
  4214. // Based on class of definition, not class of value
  4215. Class<?> c = p_info.getColumnClass(i);
  4216. if (value == null || value.equals (Null.NULL))
  4217. ;
  4218. else if (c == Object.class)
  4219. col.appendChild(document.createCDATASection(value.toString()));
  4220. else if (value instanceof Integer || value instanceof BigDecimal)
  4221. col.appendChild(document.createTextNode(value.toString()));
  4222. else if (c == Boolean.class)
  4223. {
  4224. boolean bValue = false;
  4225. if (value instanceof Boolean)
  4226. bValue = ((Boolean)value).booleanValue();
  4227. else
  4228. bValue = "Y".equals(value);
  4229. col.appendChild(document.createTextNode(bValue ? "Y" : "N"));
  4230. }
  4231. else if (value instanceof Timestamp)
  4232. col.appendChild(document.createTextNode(value.toString()));
  4233. else if (c == String.class)
  4234. col.appendChild(document.createCDATASection((String)value));
  4235. else if (DisplayType.isLOB(dt))
  4236. col.appendChild(document.createCDATASection(value.toString()));
  4237. else
  4238. col.appendChild(document.createCDATASection(value.toString()));
  4239. //
  4240. root.appendChild(col);
  4241. }
  4242. // Custom Columns
  4243. if (m_custom != null)
  4244. {
  4245. Iterator<String> it = m_custom.keySet().iterator();
  4246. while (it.hasNext())
  4247. {
  4248. String columnName = (String)it.next();
  4249. // int index = p_info.getColumnIndex(columnName);
  4250. String value = (String)m_custom.get(columnName);
  4251. //
  4252. Element col = document.createElement(columnName);
  4253. if (value != null)
  4254. col.appendChild(document.createTextNode(value));
  4255. root.appendChild(col);
  4256. }
  4257. m_custom = null;
  4258. }
  4259. return document;
  4260. } // getDocument
  4261. /* Doc - To be used on ModelValidator to get the corresponding Doc from the PO */
  4262. private Doc m_doc;
  4263. /**
  4264. * Set the accounting document associated to the PO - for use in POST ModelValidator
  4265. * @param doc Document
  4266. */
  4267. public void setDoc(Doc doc) {
  4268. m_doc = doc;
  4269. }
  4270. public void setReplication(boolean isFromReplication)
  4271. {
  4272. m_isReplication = isFromReplication;
  4273. }
  4274. public boolean isReplication()
  4275. {
  4276. return m_isReplication;
  4277. }
  4278. /**
  4279. * Set the accounting document associated to the PO - for use in POST ModelValidator
  4280. * @return Doc Document
  4281. */
  4282. public Doc getDoc() {
  4283. return m_doc;
  4284. }
  4285. /**
  4286. * PO.setTrxName - set given trxName to an array of POs
  4287. * As suggested by teo in [ 1854603 ]
  4288. */
  4289. public static void set_TrxName(PO[] lines, String trxName) {
  4290. for (PO line : lines)
  4291. line.set_TrxName(trxName);
  4292. }
  4293. /**
  4294. * Get Integer Value
  4295. * @param columnName
  4296. * @return int value
  4297. */
  4298. public int get_ValueAsInt (String columnName)
  4299. {
  4300. int idx = get_ColumnIndex(columnName);
  4301. if (idx < 0)
  4302. {
  4303. return 0;
  4304. }
  4305. return get_ValueAsInt(idx);
  4306. }
  4307. /**
  4308. * Get value as Boolean
  4309. * @param columnName
  4310. * @return boolean value
  4311. */
  4312. public boolean get_ValueAsBoolean(String columnName)
  4313. {
  4314. Object oo = get_Value(columnName);
  4315. if (oo != null)
  4316. {
  4317. if (oo instanceof Boolean)
  4318. return ((Boolean)oo).booleanValue();
  4319. return "Y".equals(oo);
  4320. }
  4321. return false;
  4322. }
  4323. /**
  4324. * @return uuid column name
  4325. */
  4326. public String getUUIDColumnName() {
  4327. return PO.getUUIDColumnName(get_TableName());
  4328. }
  4329. /**
  4330. *
  4331. * @param tableName
  4332. * @return uuid column name
  4333. */
  4334. public static String getUUIDColumnName(String tableName) {
  4335. String columnName = tableName + "_UU";
  4336. if (columnName.length() > 30) {
  4337. int i = columnName.length() - 30;
  4338. columnName = tableName.substring(0, tableName.length() - i) + "_UU";
  4339. }
  4340. return columnName;
  4341. }
  4342. @Override
  4343. protected Object clone() throws CloneNotSupportedException {
  4344. PO clone = (PO) super.clone();
  4345. clone.m_trxName = null;
  4346. if (m_custom != null)
  4347. {
  4348. clone.m_custom = new HashMap<String, String>();
  4349. clone.m_custom.putAll(m_custom);
  4350. }
  4351. if (m_newValues != null)
  4352. {
  4353. clone.m_newValues = new Object[m_newValues.length];
  4354. for(int i = 0; i < m_newValues.length; i++)
  4355. {
  4356. clone.m_newValues[i] = m_newValues[i];
  4357. }
  4358. }
  4359. if (m_oldValues != null)
  4360. {
  4361. clone.m_oldValues = new Object[m_oldValues.length];
  4362. for(int i = 0; i < m_oldValues.length; i++)
  4363. {
  4364. clone.m_oldValues[i] = m_oldValues[i];
  4365. }
  4366. }
  4367. if (m_IDs != null)
  4368. {
  4369. clone.m_IDs = new Object[m_IDs.length];
  4370. for(int i = 0; i < m_IDs.length; i++)
  4371. {
  4372. clone.m_IDs[i] = m_IDs[i];
  4373. }
  4374. }
  4375. clone.p_ctx = Env.getCtx();
  4376. clone.m_doc = null;
  4377. clone.m_lobInfo = null;
  4378. clone.m_attachment = null;
  4379. clone.m_isReplication = false;
  4380. return clone;
  4381. }
  4382. private void readObject(ObjectInputStream ois)
  4383. throws ClassNotFoundException, IOException {
  4384. // default deserialization
  4385. ois.defaultReadObject();
  4386. log = CLogger.getCLogger(getClass());
  4387. }
  4388. public void set_Attribute(String columnName, Object value) {
  4389. if (m_attributes == null)
  4390. m_attributes = new HashMap<String, Object>();
  4391. m_attributes.put(columnName, value);
  4392. }
  4393. public Object get_Attribute(String columnName) {
  4394. if (m_attributes != null)
  4395. return m_attributes.get(columnName);
  4396. return null;
  4397. }
  4398. public HashMap<String,Object> get_Attributes() {
  4399. return m_attributes;
  4400. }
  4401. private void validateUniqueIndex()
  4402. {
  4403. ValueNamePair ppE = CLogger.retrieveError();
  4404. if (ppE != null)
  4405. {
  4406. String msg = ppE.getValue();
  4407. String info = ppE.getName();
  4408. if ("DBExecuteError".equals(msg))
  4409. info = "DBExecuteError:" + info;
  4410. // Unique Constraint
  4411. Exception e = CLogger.retrieveException();
  4412. if (DBException.isUniqueContraintError(e))
  4413. {
  4414. boolean found = false;
  4415. String dbIndexName = DB.getDatabase().getNameOfUniqueConstraintError(e);
  4416. if (log.isLoggable(Level.FINE)) log.fine("dbIndexName=" + dbIndexName);
  4417. MTableIndex[] indexes = MTableIndex.get(MTable.get(getCtx(), get_Table_ID()));
  4418. for (MTableIndex index : indexes)
  4419. {
  4420. if (dbIndexName.equalsIgnoreCase(index.getName()))
  4421. {
  4422. if (index.getAD_Message_ID() > 0)
  4423. {
  4424. MMessage message = MMessage.get(getCtx(), index.getAD_Message_ID());
  4425. log.saveError("SaveError", Msg.getMsg(getCtx(), message.getValue()));
  4426. found = true;
  4427. }
  4428. break;
  4429. }
  4430. }
  4431. if (!found)
  4432. log.saveError(msg, info);
  4433. }
  4434. else
  4435. log.saveError(msg, info);
  4436. }
  4437. }
  4438. private void checkValidContext() {
  4439. if (getCtx().isEmpty() && getCtx().getProperty("#AD_Client_ID") == null)
  4440. throw new AdempiereException("Context lost");
  4441. }
  4442. } // PO