Fixes Issue 0020623, Fixes Issue 24289: 32K limit for attachments
authorShankar Balachandran <shankar.balachandran@openbravo.com>
Thu, 11 Jul 2013 11:00:28 +0530
changeset 20726 bed326b6ddf6
parent 20725 b0cfc1e49543
child 20727 7f9e668d264c
Fixes Issue 0020623, Fixes Issue 24289: 32K limit for attachments

The attachment model has been changed as explained in the wiki[1].
A new ant task has been added to migrate attachments from old model to new model.

[1] http://wiki.openbravo.com/wiki/Attachments
build.xml
modules/org.openbravo.client.application/src/org/openbravo/client/application/window/AttachmentsAH.java
src-db/database/model/tables/C_FILE.xml
src-db/database/sourcedata/AD_COLUMN.xml
src-db/database/sourcedata/AD_ELEMENT.xml
src-db/database/sourcedata/AD_REF_LIST.xml
src/org/openbravo/erpCommon/businessUtility/TabAttachments.java
src/org/openbravo/erpCommon/businessUtility/TabAttachments_data.xsql
src/org/openbravo/erpCommon/utility/MigrateAttachments.java
src/org/openbravo/erpCommon/utility/reporting/ReportManager.java
--- a/build.xml	Fri Jul 12 15:03:06 2013 +0200
+++ b/build.xml	Thu Jul 11 11:00:28 2013 +0530
@@ -12,7 +12,7 @@
 * under the License.
 * The Original Code is Openbravo ERP.
 * The Initial Developer of the Original Code is Openbravo SLU
-* All portions are Copyright (C) 2001-2012 Openbravo SLU
+* All portions are Copyright (C) 2001-2013 Openbravo SLU
 * All Rights Reserved.
 * Contributor(s):  ______________________________________.
 ************************************************************************
@@ -371,6 +371,12 @@
     <ant dir="${base.src}" target="compile.complete" inheritAll="true" inheritRefs="true" />
   </target>
 
+  <target name="migrate.attachments" depends="init">
+    <java classname="org.openbravo.erpCommon.utility.MigrateAttachments" fork="true" failonerror="yes" jvm="${env.JAVA_HOME}/bin/java">
+      <classpath refid="project.class.path" />
+    </java>
+  </target>
+
   <target name="smartbuild" depends="init">
     <property name="smart.mode" value="true" />
     <property name="onlyIfModified" value="true" />
--- a/modules/org.openbravo.client.application/src/org/openbravo/client/application/window/AttachmentsAH.java	Fri Jul 12 15:03:06 2013 +0200
+++ b/modules/org.openbravo.client.application/src/org/openbravo/client/application/window/AttachmentsAH.java	Thu Jul 11 11:00:28 2013 +0530
@@ -10,8 +10,8 @@
  * License for the specific  language  governing  rights  and  limitations
  * under the License. 
  * The Original Code is Openbravo ERP. 
- * The Initial Developer of the Original Code is Openbravo SLU 
- * All portions are Copyright (C) 2011 Openbravo SLU 
+ * The Initial Developer of the Original Code is Openbravo SLU
+ * All portions are Copyright (C) 2011-2013 Openbravo SLU
  * All Rights Reserved. 
  * Contributor(s):  ______________________________________.
  ************************************************************************
@@ -37,6 +37,7 @@
 import org.openbravo.dal.service.OBCriteria;
 import org.openbravo.dal.service.OBDal;
 import org.openbravo.dal.service.OBDao;
+import org.openbravo.erpCommon.businessUtility.TabAttachments;
 import org.openbravo.model.ad.ui.Tab;
 import org.openbravo.model.ad.utility.Attachment;
 import org.openbravo.utils.FileUtility;
@@ -79,8 +80,9 @@
   private void deleteFile(Attachment attachment) {
     String attachmentFolder = OBPropertiesProvider.getInstance().getOpenbravoProperties()
         .getProperty("attach.path");
-    String fileDirPath = attachmentFolder + "/" + DalUtil.getId(attachment.getTable()) + "-"
-        + attachment.getRecord();
+    String fileDir = TabAttachments.getAttachmentDirectory(attachment.getTable().getId(),
+        attachment.getRecord(), attachment.getName());
+    String fileDirPath = attachmentFolder + "/" + fileDir;
     FileUtility f = new FileUtility();
     final File file = new File(fileDirPath, attachment.getName());
     if (file.exists()) {
--- a/src-db/database/model/tables/C_FILE.xml	Fri Jul 12 15:03:06 2013 +0200
+++ b/src-db/database/model/tables/C_FILE.xml	Thu Jul 11 11:00:28 2013 +0530
@@ -57,6 +57,10 @@
         <default/>
         <onCreateDefault/>
       </column>
+      <column name="PATH" primaryKey="false" required="false" type="VARCHAR" size="2000" autoIncrement="false">
+        <default/>
+        <onCreateDefault/>
+      </column>
       <foreign-key foreignTable="AD_CLIENT" name="C_FILE_AD_CLIENT">
         <reference local="AD_CLIENT_ID" foreign="AD_CLIENT_ID"/>
       </foreign-key>
--- a/src-db/database/sourcedata/AD_COLUMN.xml	Fri Jul 12 15:03:06 2013 +0200
+++ b/src-db/database/sourcedata/AD_COLUMN.xml	Thu Jul 11 11:00:28 2013 +0530
@@ -309439,6 +309439,41 @@
 <!--D1BA6C3D01714981AE40690180B364AC-->  <ISUSEDSEQUENCE><![CDATA[N]]></ISUSEDSEQUENCE>
 <!--D1BA6C3D01714981AE40690180B364AC--></AD_COLUMN>
 
+<!--D243A6CF3B804A92A59633006740DE1D--><AD_COLUMN>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <AD_COLUMN_ID><![CDATA[D243A6CF3B804A92A59633006740DE1D]]></AD_COLUMN_ID>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <AD_CLIENT_ID><![CDATA[0]]></AD_CLIENT_ID>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <AD_ORG_ID><![CDATA[0]]></AD_ORG_ID>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <ISACTIVE><![CDATA[Y]]></ISACTIVE>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <NAME><![CDATA[Path]]></NAME>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <DESCRIPTION><![CDATA[Location of the file relative to the attachments folder]]></DESCRIPTION>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <HELP><![CDATA[Location of the file relative to the attachments folder]]></HELP>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <COLUMNNAME><![CDATA[Path]]></COLUMNNAME>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <AD_TABLE_ID><![CDATA[800028]]></AD_TABLE_ID>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <AD_REFERENCE_ID><![CDATA[10]]></AD_REFERENCE_ID>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <FIELDLENGTH><![CDATA[2000]]></FIELDLENGTH>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <ISKEY><![CDATA[N]]></ISKEY>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <ISPARENT><![CDATA[N]]></ISPARENT>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <ISMANDATORY><![CDATA[N]]></ISMANDATORY>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <ISUPDATEABLE><![CDATA[Y]]></ISUPDATEABLE>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <ISIDENTIFIER><![CDATA[N]]></ISIDENTIFIER>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <SEQNO><![CDATA[170]]></SEQNO>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <ISTRANSLATED><![CDATA[N]]></ISTRANSLATED>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <ISENCRYPTED><![CDATA[N]]></ISENCRYPTED>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <ISSELECTIONCOLUMN><![CDATA[N]]></ISSELECTIONCOLUMN>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <AD_ELEMENT_ID><![CDATA[026C0C35D730459B865E7242955C87F2]]></AD_ELEMENT_ID>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <ISSESSIONATTR><![CDATA[N]]></ISSESSIONATTR>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <ISSECONDARYKEY><![CDATA[N]]></ISSECONDARYKEY>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <ISDESENCRYPTABLE><![CDATA[N]]></ISDESENCRYPTABLE>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <DEVELOPMENTSTATUS><![CDATA[RE]]></DEVELOPMENTSTATUS>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <AD_MODULE_ID><![CDATA[0]]></AD_MODULE_ID>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <POSITION><![CDATA[15]]></POSITION>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <ISTRANSIENT><![CDATA[N]]></ISTRANSIENT>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <ISAUTOSAVE><![CDATA[Y]]></ISAUTOSAVE>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <VALIDATEONNEW><![CDATA[Y]]></VALIDATEONNEW>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <IMAGESIZEVALUESACTION><![CDATA[N]]></IMAGESIZEVALUESACTION>
+<!--D243A6CF3B804A92A59633006740DE1D-->  <ISUSEDSEQUENCE><![CDATA[N]]></ISUSEDSEQUENCE>
+<!--D243A6CF3B804A92A59633006740DE1D--></AD_COLUMN>
+
 <!--D270E0FBA1074C439DC12CF8939EB830--><AD_COLUMN>
 <!--D270E0FBA1074C439DC12CF8939EB830-->  <AD_COLUMN_ID><![CDATA[D270E0FBA1074C439DC12CF8939EB830]]></AD_COLUMN_ID>
 <!--D270E0FBA1074C439DC12CF8939EB830-->  <AD_CLIENT_ID><![CDATA[0]]></AD_CLIENT_ID>
--- a/src-db/database/sourcedata/AD_ELEMENT.xml	Fri Jul 12 15:03:06 2013 +0200
+++ b/src-db/database/sourcedata/AD_ELEMENT.xml	Thu Jul 11 11:00:28 2013 +0530
@@ -20629,6 +20629,20 @@
 <!--01A78AEEF3C411DDB34B001D09C4A2FE-->  <ISGLOSSARY><![CDATA[N]]></ISGLOSSARY>
 <!--01A78AEEF3C411DDB34B001D09C4A2FE--></AD_ELEMENT>
 
+<!--026C0C35D730459B865E7242955C87F2--><AD_ELEMENT>
+<!--026C0C35D730459B865E7242955C87F2-->  <AD_ELEMENT_ID><![CDATA[026C0C35D730459B865E7242955C87F2]]></AD_ELEMENT_ID>
+<!--026C0C35D730459B865E7242955C87F2-->  <AD_CLIENT_ID><![CDATA[0]]></AD_CLIENT_ID>
+<!--026C0C35D730459B865E7242955C87F2-->  <AD_ORG_ID><![CDATA[0]]></AD_ORG_ID>
+<!--026C0C35D730459B865E7242955C87F2-->  <ISACTIVE><![CDATA[Y]]></ISACTIVE>
+<!--026C0C35D730459B865E7242955C87F2-->  <COLUMNNAME><![CDATA[Path]]></COLUMNNAME>
+<!--026C0C35D730459B865E7242955C87F2-->  <NAME><![CDATA[Path]]></NAME>
+<!--026C0C35D730459B865E7242955C87F2-->  <PRINTNAME><![CDATA[Path]]></PRINTNAME>
+<!--026C0C35D730459B865E7242955C87F2-->  <DESCRIPTION><![CDATA[Location of the file relative to the attachments folder]]></DESCRIPTION>
+<!--026C0C35D730459B865E7242955C87F2-->  <HELP><![CDATA[Location of the file relative to the attachments folder]]></HELP>
+<!--026C0C35D730459B865E7242955C87F2-->  <AD_MODULE_ID><![CDATA[0]]></AD_MODULE_ID>
+<!--026C0C35D730459B865E7242955C87F2-->  <ISGLOSSARY><![CDATA[N]]></ISGLOSSARY>
+<!--026C0C35D730459B865E7242955C87F2--></AD_ELEMENT>
+
 <!--035BE38188FC44538E3DDDA769800CFB--><AD_ELEMENT>
 <!--035BE38188FC44538E3DDDA769800CFB-->  <AD_ELEMENT_ID><![CDATA[035BE38188FC44538E3DDDA769800CFB]]></AD_ELEMENT_ID>
 <!--035BE38188FC44538E3DDDA769800CFB-->  <AD_CLIENT_ID><![CDATA[0]]></AD_CLIENT_ID>
--- a/src-db/database/sourcedata/AD_REF_LIST.xml	Fri Jul 12 15:03:06 2013 +0200
+++ b/src-db/database/sourcedata/AD_REF_LIST.xml	Thu Jul 11 11:00:28 2013 +0530
@@ -8807,6 +8807,18 @@
 <!--3D4C02EB56AA4380AF128782BDA72C3D-->  <SEQNO><![CDATA[10]]></SEQNO>
 <!--3D4C02EB56AA4380AF128782BDA72C3D--></AD_REF_LIST>
 
+<!--3E3B66241E984CD19E9CF195DB72638B--><AD_REF_LIST>
+<!--3E3B66241E984CD19E9CF195DB72638B-->  <AD_REF_LIST_ID><![CDATA[3E3B66241E984CD19E9CF195DB72638B]]></AD_REF_LIST_ID>
+<!--3E3B66241E984CD19E9CF195DB72638B-->  <AD_CLIENT_ID><![CDATA[0]]></AD_CLIENT_ID>
+<!--3E3B66241E984CD19E9CF195DB72638B-->  <AD_ORG_ID><![CDATA[0]]></AD_ORG_ID>
+<!--3E3B66241E984CD19E9CF195DB72638B-->  <ISACTIVE><![CDATA[Y]]></ISACTIVE>
+<!--3E3B66241E984CD19E9CF195DB72638B-->  <VALUE><![CDATA[SaveAttachmentsOldWay]]></VALUE>
+<!--3E3B66241E984CD19E9CF195DB72638B-->  <NAME><![CDATA[Save Attachments in old way]]></NAME>
+<!--3E3B66241E984CD19E9CF195DB72638B-->  <DESCRIPTION><![CDATA[Saves the attachments using the earlier folder structure]]></DESCRIPTION>
+<!--3E3B66241E984CD19E9CF195DB72638B-->  <AD_REFERENCE_ID><![CDATA[A26BA480E2014707B47257024C3CBFF7]]></AD_REFERENCE_ID>
+<!--3E3B66241E984CD19E9CF195DB72638B-->  <AD_MODULE_ID><![CDATA[0]]></AD_MODULE_ID>
+<!--3E3B66241E984CD19E9CF195DB72638B--></AD_REF_LIST>
+
 <!--3E6F1F271AE44931BE140ECECE5C86A2--><AD_REF_LIST>
 <!--3E6F1F271AE44931BE140ECECE5C86A2-->  <AD_REF_LIST_ID><![CDATA[3E6F1F271AE44931BE140ECECE5C86A2]]></AD_REF_LIST_ID>
 <!--3E6F1F271AE44931BE140ECECE5C86A2-->  <AD_CLIENT_ID><![CDATA[0]]></AD_CLIENT_ID>
--- a/src/org/openbravo/erpCommon/businessUtility/TabAttachments.java	Fri Jul 12 15:03:06 2013 +0200
+++ b/src/org/openbravo/erpCommon/businessUtility/TabAttachments.java	Thu Jul 11 11:00:28 2013 +0530
@@ -11,7 +11,7 @@
  * under the License.
  * The Original Code is Openbravo ERP.
  * The Initial Developer of the Original Code is Openbravo SLU
- * All portions are Copyright (C) 2001-2011 Openbravo SLU
+ * All portions are Copyright (C) 2001-2013 Openbravo SLU
  * All Rights Reserved.
  * Contributor(s):  ______________________________________.
  ************************************************************************
@@ -34,6 +34,7 @@
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.commons.fileupload.FileItem;
+import org.apache.log4j.Logger;
 import org.codehaus.jettison.json.JSONObject;
 import org.hibernate.criterion.Restrictions;
 import org.openbravo.base.secureApp.HttpSecureAppServlet;
@@ -45,8 +46,10 @@
 import org.openbravo.dal.service.OBDal;
 import org.openbravo.dal.service.OBDao;
 import org.openbravo.erpCommon.utility.OBError;
+import org.openbravo.erpCommon.utility.PropertyException;
 import org.openbravo.erpCommon.utility.SequenceIdData;
 import org.openbravo.erpCommon.utility.Utility;
+import org.openbravo.model.ad.datamodel.Table;
 import org.openbravo.model.ad.ui.Tab;
 import org.openbravo.model.ad.utility.Attachment;
 import org.openbravo.utils.FileUtility;
@@ -54,6 +57,7 @@
 
 public class TabAttachments extends HttpSecureAppServlet {
   private static final long serialVersionUID = 1L;
+  private static Logger log = Logger.getLogger(TabAttachments.class);
 
   @Override
   public void init(ServletConfig config) {
@@ -282,7 +286,7 @@
   private OBError insert(VariablesSecureApp vars, String strFileReference, String tableId,
       String key, String strDataType, String strText) throws IOException, ServletException {
 
-    String cFileId = strFileReference;
+    String cFileId = strFileReference, fileDir = null, path = null;
     OBError myMessage = null;
     myMessage = new OBError();
     myMessage.setTitle("");
@@ -290,6 +294,7 @@
     if (log4j.isDebugEnabled())
       log4j.debug("Deleting records");
     Connection conn = null;
+    fileDir = getAttachmentDirectoryForNewAttachments(tableId, key);
     try {
       conn = this.getTransactionConnection();
       final String inpName = "inpname";
@@ -316,17 +321,18 @@
         }
       }
       if (!fileExists) {
+        path = getPath(fileDir);
         // We only insert a new record if there is no record for this file
         TabAttachmentsData.insert(conn, this, cFileId, vars.getClient(), vars.getOrg(),
-            vars.getUser(), tableId, key, strDataType, strText, strName);
+            vars.getUser(), tableId, key, strDataType, strText, strName, path);
       } else {
         // We update the existing record
         TabAttachmentsData.update(this, vars.getUser(), strDataType, strText, cFileId);
       }
       try {
         // FIXME: Get the directory separator from Java runtime
-        final File uploadedDir = new File(globalParameters.strFTPDirectory + "/" + tableId + "-"
-            + key);
+
+        final File uploadedDir = new File(globalParameters.strFTPDirectory + "/" + fileDir);
         if (!uploadedDir.exists())
           uploadedDir.mkdirs();
         final File uploadedFile = new File(uploadedDir, strName);
@@ -543,16 +549,17 @@
 
   private void printPageFile(HttpServletResponse response, VariablesSecureApp vars,
       String strFileReference) throws IOException, ServletException {
+    String fileDir = null;
     final TabAttachmentsData[] data = TabAttachmentsData.selectEdit(this, strFileReference);
     if (data == null || data.length == 0)
       throw new ServletException("Missing file");
     FileUtility f = new FileUtility();
+    fileDir = getAttachmentDirectory(data[0].adTableId, data[0].adRecordId, data[0].name);
     // FIXME: Get the directory separator from Java runtime
-    final File file = new File(globalParameters.strFTPDirectory + "/" + data[0].adTableId + "-"
-        + data[0].adRecordId, data[0].name);
+    final File file = new File(globalParameters.strFTPDirectory + "/" + fileDir, data[0].name);
     if (file.exists())
-      f = new FileUtility(globalParameters.strFTPDirectory + "/" + data[0].adTableId + "-"
-          + data[0].adRecordId, data[0].name, false, true);
+      f = new FileUtility(globalParameters.strFTPDirectory + "/" + fileDir, data[0].name, false,
+          true);
     else
       f = new FileUtility(globalParameters.strFTPDirectory, strFileReference, false, true);
     if (data[0].datatypeContent.equals(""))
@@ -575,6 +582,122 @@
     out.close();
   }
 
+  /**
+   * Provides the directory in which the attachment has to be stored. For example for tableId "259",
+   * recordId "0F3A10E019754BACA5844387FB37B0D5", the file directory returned is
+   * "259/0F3/A10/E01/975/4BA/CA5/844/387/FB3/7B0/D5". In case 'SaveAttachmentsOldWay' preference is
+   * enabled then the file directory returned is "259-0F3A10E019754BACA5844387FB37B0D5"
+   * 
+   * @param tableId
+   *          UUID of the table
+   * 
+   * @param recordId
+   *          UUID of the record
+   * 
+   * @return file directory to save the attachment
+   */
+  public static String getAttachmentDirectoryForNewAttachments(String tableID, String recordID) {
+    String fileDir = tableID + "-" + recordID;
+    String saveAttachmentsOldWay = null;
+    try {
+      saveAttachmentsOldWay = Preferences.getPreferenceValue("SaveAttachmentsOldWay", true,
+          OBContext.getOBContext().getCurrentClient(), OBContext.getOBContext()
+              .getCurrentOrganization(), OBContext.getOBContext().getUser(), OBContext
+              .getOBContext().getRole(), null);
+    } catch (PropertyException e) {
+      // if property not found, save attachments the new way
+      saveAttachmentsOldWay = "N";
+    }
+
+    if ("Y".equals(saveAttachmentsOldWay)) {
+      return fileDir;
+    } else {
+      fileDir = tableID + "/" + splitPath(recordID);
+    }
+    return fileDir;
+  }
+
+  /**
+   * Provides the directory in which the attachment is stored. For example for tableId "259",
+   * recordId "0F3A10E019754BACA5844387FB37B0D5", and fileName "test.txt" the file directory
+   * returned is "259/0F3/A10/E01/975/4BA/CA5/844/387/FB3/7B0/D5". In case 'SaveAttachmentsOldWay'
+   * preference is enabled then the file directory returned is
+   * "259-0F3A10E019754BACA5844387FB37B0D5"
+   * 
+   * @param tableId
+   *          UUID of the table
+   * 
+   * @param recordId
+   *          UUID of the record
+   * 
+   * @param fileName
+   *          Name of the file
+   * 
+   * @return file directory in which the attachment is stored
+   */
+  public static String getAttachmentDirectory(String tableID, String recordID, String fileName) {
+    String fileDir = tableID + "-" + recordID;
+    Table attachmentTable = null;
+    try {
+      OBContext.setAdminMode();
+      attachmentTable = OBDal.getInstance().get(Table.class, tableID);
+      OBCriteria<Attachment> attachmentCriteria = OBDal.getInstance().createCriteria(
+          Attachment.class);
+      attachmentCriteria.add(Restrictions.eq(Attachment.PROPERTY_RECORD, recordID));
+      attachmentCriteria.add(Restrictions.eq(Attachment.PROPERTY_TABLE, attachmentTable));
+      attachmentCriteria.add(Restrictions.eq(Attachment.PROPERTY_NAME, fileName));
+
+      if (attachmentCriteria.count() > 0) {
+        Attachment attachment = attachmentCriteria.list().get(0);
+        if (attachment.getPath() != null) {
+          fileDir = attachment.getPath();
+        }
+      }
+    } catch (Exception e) {
+      log.error(e.getMessage(), e);
+    } finally {
+      OBContext.restorePreviousMode();
+    }
+    return fileDir;
+  }
+
+  /**
+   * Provides the value to be saved in path field in c_file. The path field is used to get the
+   * location of the attachment. For example 259/0F3/A10/E01/975/4BA/CA5/844/387/FB3/7B0/D5. This
+   * path is relative to the attachments folder
+   * 
+   * @param fileDirectory
+   *          the directory that is retrieved from getFileDirectory()
+   * 
+   * @return value to be saved in path in c_file
+   */
+  public static String getPath(String fileDirectory) {
+    if (fileDirectory != null && fileDirectory.contains("-")) {
+      return null;
+    } else {
+      return fileDirectory;
+    }
+  }
+
+  /**
+   * Splits the path name component so that the resulting path name is 3 characters long sub
+   * directories. For example 12345 is splitted to 123/45
+   * 
+   * @param origname
+   *          Original name
+   * @return splitted name.
+   */
+  public static String splitPath(final String origname) {
+    String newname = "";
+    for (int i = 0; i < origname.length(); i += 3) {
+      if (i != 0) {
+        newname += "/";
+      }
+      newname += origname.substring(i, Math.min(i + 3, origname.length()));
+    }
+    return newname;
+  }
+
   @Override
   public String getServletInfo() {
     return "Servlet that presents the attachments";
--- a/src/org/openbravo/erpCommon/businessUtility/TabAttachments_data.xsql	Fri Jul 12 15:03:06 2013 +0200
+++ b/src/org/openbravo/erpCommon/businessUtility/TabAttachments_data.xsql	Thu Jul 11 11:00:28 2013 +0530
@@ -99,9 +99,9 @@
       <SqlMethodComment></SqlMethodComment>
       <Sql> <![CDATA[
         INSERT INTO C_FILE (C_FILE_ID, AD_CLIENT_ID, AD_ORG_ID, ISACTIVE, CREATED, CREATEDBY, 
-        UPDATED, UPDATEDBY, AD_TABLE_ID, AD_RECORD_ID, C_DATATYPE_ID, TEXT, NAME, SEQNO)
+        UPDATED, UPDATEDBY, AD_TABLE_ID, AD_RECORD_ID, C_DATATYPE_ID, TEXT, NAME, SEQNO,PATH)
         VALUES(?, ?, ?, 'Y', now(), ?, 
-        now(), ?, ?, ?, ?, ?, ?, (SELECT COALESCE(MAX(SEQNO), 0) + 10 FROM C_FILE WHERE AD_TABLE_ID = ? AND AD_RECORD_ID=?))
+        now(), ?, ?, ?, ?, ?, ?, (SELECT COALESCE(MAX(SEQNO), 0) + 10 FROM C_FILE WHERE AD_TABLE_ID = ? AND AD_RECORD_ID=?),?)
       ]]></Sql>
         <Parameter name="cFileId"/>
         <Parameter name="adClientId"/>
@@ -115,6 +115,7 @@
         <Parameter name="name"/>
         <Parameter name="adTableId"/>
         <Parameter name="adRecordId"/>
+        <Parameter name="path"/>
    </SqlMethod>
    <SqlMethod name="update" type="preparedStatement" return="rowCount">
       <SqlMethodComment></SqlMethodComment>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/openbravo/erpCommon/utility/MigrateAttachments.java	Thu Jul 11 11:00:28 2013 +0530
@@ -0,0 +1,151 @@
+/*
+ *************************************************************************
+ * The contents of this file are subject to the Openbravo  Public  License
+ * Version  1.1  (the  "License"),  being   the  Mozilla   Public  License
+ * Version 1.1  with a permitted attribution clause; you may not  use this
+ * file except in compliance with the License. You  may  obtain  a copy of
+ * the License at http://www.openbravo.com/legal/license.html 
+ * Software distributed under the License  is  distributed  on  an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific  language  governing  rights  and  limitations
+ * under the License. 
+ * The Original Code is Openbravo ERP. 
+ * The Initial Developer of the Original Code is Openbravo SLU 
+ * All portions are Copyright (C) 2013 Openbravo SLU 
+ * All Rights Reserved. 
+ * Contributor(s):  ______________________________________.
+ ************************************************************************
+ */
+
+package org.openbravo.erpCommon.utility;
+
+import java.io.File;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+
+import org.apache.tools.ant.Task;
+import org.openbravo.base.session.OBPropertiesProvider;
+import org.openbravo.database.ConnectionProviderImpl;
+import org.openbravo.erpCommon.businessUtility.TabAttachments;
+import org.openbravo.utils.FileUtility;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Migration of attachments based on the new attachment model
+ * 
+ * @author Shankar Balachandran
+ */
+
+public class MigrateAttachments extends Task {
+
+  private static Logger log = LoggerFactory.getLogger(MigrateAttachments.class);
+
+  public static void execute(String[] args) throws Exception {
+    String attachPath = OBPropertiesProvider.getInstance().getOpenbravoProperties()
+        .getProperty("attach.path");
+    log.info("Migrating Attachments");
+    migrateAttachments(attachPath);
+    log.info("Migration Successful");
+  }
+
+  /**
+   * Delete the directory and all the sub files recursively.Extended from FileUtility as source
+   * directory is not removed in FileUtility.delete method.
+   * 
+   * @param source
+   *          Path of the file to be deleted
+   */
+  public static void delete(File source) throws Exception {
+    FileUtility.delete(source);
+    // delete the source directory
+    source.delete();
+  }
+
+  /**
+   * Migrates the attachments to new attachment model
+   * 
+   * @param attachPath
+   *          The path of the attachments folder specified in Openbravo.properties
+   * 
+   */
+  public static void migrateAttachments(String attachPath) throws Exception {
+    int fileCount = 0;
+    String[] names = null;
+    String tableId = null, recordId = null, attachmentDirectory = null, newDirectoryStructure = null;
+    Connection connection = null;
+    PreparedStatement statement = null;
+    try {
+      connection = (new ConnectionProviderImpl(OBPropertiesProvider.getInstance()
+          .getOpenbravoProperties())).getConnection();
+      boolean createDirectory = true;
+      File files = new File(attachPath);
+      if (files.isDirectory()) {
+        for (File directory : files.listFiles()) {
+          tableId = null;
+          recordId = null;
+          if (directory.isDirectory()) {
+            if (directory.getName().contains("-")) {
+              names = directory.getName().split("-");
+              if (names.length == 2) {
+                tableId = names[0];
+                recordId = names[1];
+                newDirectoryStructure = tableId + "/" + TabAttachments.splitPath(recordId);
+                attachmentDirectory = attachPath + "/" + newDirectoryStructure;
+                if (!new File(attachmentDirectory).exists()) {
+                  createDirectory = new File(attachmentDirectory).mkdirs();
+                } else {
+                  createDirectory = true;
+                }
+                if (createDirectory) {
+                  for (File file : directory.listFiles()) {
+                    File destination = new File(attachmentDirectory + "/" + file.getName());
+                    FileUtility.copyFile(file.getAbsoluteFile(), destination);
+                    if (destination.exists()) {
+                      fileCount++;
+                      // update path in c_file
+                      if (connection != null) {
+                        statement = null;
+                        try {
+                          String query = "UPDATE C_FILE SET PATH=? WHERE AD_TABLE_ID=? AND AD_RECORD_ID=? AND NAME=?";
+                          statement = connection.prepareStatement(query);
+                          statement.setString(1, newDirectoryStructure);
+                          statement.setString(2, tableId);
+                          statement.setString(3, recordId);
+                          statement.setString(4, file.getName());
+                          statement.executeUpdate();
+                        } finally {
+                          if (statement != null && !statement.isClosed()) {
+                            statement.close();
+                          }
+                        }
+                      } else {
+                        log.error("Connection Failed!");
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+      for (File directory : files.listFiles()) {
+        if (directory.getName().contains("-") && directory.isDirectory()) {
+          delete(directory);
+        }
+      }
+      log.info("Number of files migrated: " + fileCount);
+    } catch (Exception e) {
+      log.error(e.getCause().getMessage(), e);
+    } finally {
+      try {
+        if (connection != null) {
+          connection.close();
+        }
+      } catch (Exception e) {
+        log.error(e.getMessage(), e);
+      }
+    }
+  }
+}
--- a/src/org/openbravo/erpCommon/utility/reporting/ReportManager.java	Fri Jul 12 15:03:06 2013 +0200
+++ b/src/org/openbravo/erpCommon/utility/reporting/ReportManager.java	Thu Jul 11 11:00:28 2013 +0530
@@ -11,7 +11,7 @@
  * under the License.
  * The Original Code is Openbravo ERP.
  * The Initial Developer of the Original Code is Business Momentum b.v.
- * All portions are Copyright (C) 2007-2011 Openbravo SLU 
+ * All portions are Copyright (C) 2007-2013 Openbravo SLU 
  * All Rights Reserved. 
  * Contributor(s):  Business Momentum b.v. (http://www.businessmomentum.eu).
  *************************************************************************
@@ -42,6 +42,7 @@
 import org.apache.log4j.Logger;
 import org.openbravo.base.secureApp.VariablesSecureApp;
 import org.openbravo.database.ConnectionProvider;
+import org.openbravo.erpCommon.businessUtility.TabAttachments;
 import org.openbravo.erpCommon.businessUtility.TabAttachmentsData;
 import org.openbravo.erpCommon.utility.JRFormatFactory;
 import org.openbravo.erpCommon.utility.SequenceIdData;
@@ -230,11 +231,13 @@
 
   public File createAttachmentForReport(ConnectionProvider connectionProvider, Report report,
       String tableId, VariablesSecureApp vars) throws ReportingException, IOException {
+    String path = null;
     if (report.isAttached())
       throw new ReportingException(Utility.messageBD(connectionProvider, "AttachmentExists",
           vars.getLanguage()));
 
-    final String destination = tableId + "-" + report.getDocumentId();
+    final String destination = TabAttachments.getAttachmentDirectoryForNewAttachments(tableId,
+        report.getDocumentId());
 
     // First move the file to the correct destination
     final File destinationFolder = new File(_strAttachmentPath + "/" + destination);
@@ -258,14 +261,14 @@
     Connection conn = null;
     try {
       conn = _connectionProvider.getTransactionConnection();
-
+      path = TabAttachments.getPath(destination);
       final String newFileId = SequenceIdData.getUUID();
       log4j.debug("New file id: " + newFileId);
       // The 103 in the following insert specifies the document type: in
       // this case PDF
       TabAttachmentsData.insert(conn, _connectionProvider, newFileId, vars.getClient(),
           vars.getOrg(), vars.getUser(), tableId, report.getDocumentId(), "103",
-          "Generated by printing ", destinationFile.getName());
+          "Generated by printing ", destinationFile.getName(), path);
 
       _connectionProvider.releaseCommitConnection(conn);
     } catch (final Exception exception) {