• Compiere的一些外部包,都是在tools->build.xml整理的。

    外部包统一存放在tools/lib目录下,编译的时候会生成CCTools.jar及CSTools.jar中,这两个包的归类未研究,大概一个是存放常规包,一个存放数据库包,可以在build.xml看出这两个包生成包含的内容:

    <jar jarfile="${dist.dir}/CCTools.jar">
          <fileset dir="${buildClient.dir}"/>

    把jar解压放到CCTools.jar中:

    <unjar src="lib/activation.jar" dest="${buildClient.dir}" />
        <unjar src="lib/mail.jar" dest="${buildClient.dir}" />
        <!-- Bean Shell            -->
        <unjar src="lib/bsh-1.3.0.jar" dest="${buildClient.dir}" />
        <!-- ECS, etc                -->
        <copy todir="${buildClient.dir}/org">
          <fileset dir="${build.dir}/org" />
        </copy>
        <!-- Payment Processors        -->
        <unjar src="lib/Verisign.jar" dest="${buildClient.dir}" />
        <unjar src="lib/jpayment.jar" dest="${buildClient.dir}" />
        <!-- Log4J, jnlp            -->
        <unjar src="lib/log4j.jar" dest="${buildClient.dir}"/>
        <unjar src="lib/jnlp.jar" dest="${buildClient.dir}"/>
        <!-- Barcode, ftp            -->
        <unjar src="lib/barbecue-1.0.6d.jar" dest="${buildClient.dir}"/>
        <unjar src="lib/commons-net-1.4.0.jar" dest="${buildClient.dir}"/>
        <unjar src="lib/poi-3.6-20091214.jar" dest="${buildClient.dir}"/>

    在setup的时候,根据build.xml,会生成CompiereCLib与CompiereSLib,分别包含了CCTools.jar及CSTools.jar的内容。

    下面举例一个添加jar包的过程,如要把poi-3.6-20091214.jar添加到项目中:

    1.将poi-3.6-20091214.jar放到tools/lib目录

    2.tools->build.xml,在tools下解压poi-3.6-20091214.jar

    <property name="buildPoi.dir" value="build/Poi"/>

     

    <!-- Poi Lib        -->
        <unjar src="lib/poi-3.6-20091214.jar" dest="${buildPoi.dir}"/>
        <!-- Clean up            -->
        <delete>
          <fileset dir="${buildPoi.dir}" includes="build.xml"/>
          <fileset dir="${buildPoi.dir}/META-INF" casesensitive="no" includes="*.mf,*.sf,*.rsa,*.dsa"/>
        </delete>

        <!-- Create poi-3.6-20091214.jar file     -->
        <jar jarfile="${dist.dir}/poi-3.6-20091214.jar">
          <fileset dir="${buildPoi.dir}"/>
          <manifest>
            <attribute name="Specification-Title" value="poi"/>
            <attribute name="Specification-Version" value="3.6-20091214"/>
            <attribute name="Specification-Vendor" value="Apache"/>
            <attribute name="Implementation-Title" value="edb ${env.COMPIERE_VERSION}"/>
            <attribute name="Implementation-Version" value="${env.COMPIERE_VERSION} ${DSTAMP}-${TSTAMP}"/>
            <attribute name="Implementation-Vendor" value="${env.COMPIERE_VENDOR}"/>
            <attribute name="Implementation-URL" value="http://www.compiere.org"/>
          </manifest>
        </jar>

    3.将jar添加到CCTools.jar中

    <unjar src="lib/poi-3.6-20091214.jar" dest="${buildClient.dir}"/>

     

    <delete file="${dist.dir}/poi-3.6-20091214.jar" failonerror="false"/>

  • import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.io.RandomAccessFile;
    import java.io.StringWriter;
    import java.util.Properties;
    import java.util.StringTokenizer;

    import org.apache.commons.net.ftp.FTPClient;
    import org.apache.commons.net.ftp.FTPFile;
    import org.apache.commons.net.ftp.FTPReply;

    /**
     *
     * Function Description:FTP 功能类,含put和get方法
     */

    public class AppendFtp extends FTPClient {
     
     private Properties envs=getEnvVars();

     /*private static org.apache.log4j.Logger logger =
         org.apache.log4j.Logger.getLogger(AppendFtp.class);*/
     
     
     
     public void logException(Exception ae){
      StringWriter sw= new StringWriter();
      PrintWriter pw =new PrintWriter(sw);
      ae.printStackTrace(pw);
      //logger.info(sw.getBuffer());
     }

     
      public boolean upload(String remote, String fileName, String local) throws IOException {
         ftp.enterLocalPassiveMode();
    //        if(fileName.endsWith(".rar") || fileName.endsWith(".zip") || fileName.endsWith(".jar")) {
             ftp.setFileType(FTPClient.BINARY_FILE_TYPE); //2进制文件时,防止文件损坏
    //        }
            boolean result;
           
            ftp.changeWorkingDirectory(remote); //进入ftp目录
            boolean isExists = false;
            long fileSize=123;
            FTPFile[] files = ftp.listFiles();
            for (int i = 0; i < files.length; i++) {
                FTPFile file = files[i];
                System.out.println(fileName+"--fileName-------------file.getName()-"+file.getName());
                if(fileName.equals(file.getName())) {
                    isExists = true;
                    fileSize=file.getSize();
                    break;
                }
            }
            if(isExists) {
                //文件存在时, 使用断点上传方式
                System.out.println("fileSize:" + fileSize);
                File fLocal = new File(local);
                OutputStream os=ftp.appendFileStream(fileName);
                RandomAccessFile randomAccessFileLocal = new RandomAccessFile(fLocal, "r");
                randomAccessFileLocal.seek(fileSize);
                int len = 0;
                byte[] bt = new byte[1024];
                while ((len = randomAccessFileLocal.read(bt)) > 0) {
                 os.write(bt, 0, len);
                }
                randomAccessFileLocal.close();
                os.close();
                result = true;
            } else {
             System.out.println("create");
                //不存在时, 直接上传
                File f = new File(local);
                FileInputStream in = new FileInputStream(f);
                result = ftp.storeFile(fileName, in);
                in.close();
            }
            if(fileName.indexOf("ExpDat")>-1){
       boolean deleted=new File(local).delete();
       if(deleted)
        System.out.println("delete file ok!");
       else
        System.out.println("delete not ok!");
      }
            System.out.println("upload ok");
            return result;
        }

        /**
         * 建立FTP连接
         */
        public boolean connect(String hostname, int port, String username,
       String password) throws IOException {
         ftp.connect(hostname, port);
      ftp.login(username, password);
      if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
       disconnect();
       return false;
      }
      return true;
     }
       
        /**
         * 关闭FTP连接
         */
        public void disconnect() throws IOException {
      if (ftp.isConnected()) {
       ftp.disconnect();
      }
     }
        public FTPClient ftp;
        public void sss(){
         ftp=new FTPClient();
         File fExpDat=new File(envs.getProperty("COMPIERE_HOME")+"/data/ExpDat.jar");
         File fCompiereJar=new File(envs.getProperty("COMPIERE_HOME")+"/lib/Compiere.jar");  
         String ExpDatPath=fExpDat.getPath();
         String CompiereJarPath=fCompiereJar.getPath();
         System.out.println("==ExpDatPath=="+ExpDatPath);
         System.out.println("==CompiereJarPath=="+CompiereJarPath);
         try {
           if(connect("www.zoapcon.com",21,"zoap","zoapfaxftp"))
            System.out.println("login ok!");
           upload("Alison","ExpDat.jar",ExpDatPath);
        upload("Alison","Compiere.jar",CompiereJarPath);
      } catch (IOException e) {
       e.printStackTrace();
      }finally{
       try {
        disconnect();
       } catch (IOException e) {
        e.printStackTrace();
       }
      }
        }
       
      public static void main(String[] args){
       AppendFtp ftpClient = new AppendFtp();
       ftpClient.sss();
      }
      
      public static Properties getEnvVars() {
       Process p = null;
       Properties envVars = new Properties();
       try {
        
        Runtime r = Runtime.getRuntime();
        String OS = System.getProperty("os.name").toLowerCase();
        // System.out.println(OS);
        if (OS.indexOf("windows 9") > -1) {
         p = r.exec("command.com /c set");
        } else if ((OS.indexOf("nt") > -1)
          || (OS.indexOf("windows 20") > -1)
          || (OS.indexOf("windows xp") > -1)) {
         // thanks to JuanFran for the xp fix!
         p = r.exec("cmd.exe /c set");
        } else {
         // our last hope, we assume Unix (thanks to H. Ware for the fix)
         p = r.exec("env");
        }
        BufferedReader br = new BufferedReader(new InputStreamReader(p
          .getInputStream()));
        String line;
        while ((line = br.readLine()) != null) {
         int idx = line.indexOf('=');
         String key = line.substring(0, idx);
         String value = line.substring(idx + 1);
         envVars.setProperty(key, value);
         // System.out.println( key + " = " + value );
        }
       } catch (IOException ioe) {
        ioe.printStackTrace();
       }
       return envVars;
      }
    }

  • 2009-10-21

    获得环境变量 - [JAVA]

    一些非JAVA相关的环境变量可以通过下面这个方法来获取。

    public static Properties getEnvVars() {
      Process p = null;
      Properties envVars = new Properties();
      try {
       
       Runtime r = Runtime.getRuntime();
       String OS = System.getProperty("os.name").toLowerCase();
       // System.out.println(OS);
       if (OS.indexOf("windows 9") > -1) {
        p = r.exec("command.com /c set");
       } else if ((OS.indexOf("nt") > -1)
         || (OS.indexOf("windows 20") > -1)
         || (OS.indexOf("windows xp") > -1)) {
        // thanks to JuanFran for the xp fix!
        p = r.exec("cmd.exe /c set");
       } else {
        // our last hope, we assume Unix (thanks to H. Ware for the fix)
        p = r.exec("env");
       }
       BufferedReader br = new BufferedReader(new InputStreamReader(p
         .getInputStream()));
       String line;
       while ((line = br.readLine()) != null) {
        int idx = line.indexOf('=');
        String key = line.substring(0, idx);
        String value = line.substring(idx + 1);
        envVars.setProperty(key, value);
        // System.out.println( key + " = " + value );
       }
      } catch (IOException ioe) {
       ioe.printStackTrace();
      }
      return envVars;
     }

  • 2009-09-17

    拆分字符串 - [oracle]

    CREATE OR REPLACE PROCEDURE splitstr (
      srcstr     IN   VARCHAR2
    , splitstr   IN   VARCHAR2)
    AS
      ln_pos   NUMBER;
      lv_srcstr VARCHAR2(2000);
    BEGIN
      ln_pos := 1;
      lv_srcstr := srcstr || splitstr;
      FOR i IN 1 .. (LENGTH (lv_srcstr) - LENGTH (REPLACE (lv_srcstr, splitstr, '')))
      LOOP
        DBMS_OUTPUT.PUT_LINE (NVL (SUBSTR (lv_srcstr, ln_pos, INSTR (lv_srcstr, splitstr, 1, i) - ln_pos), 'null'));
     ln_pos := INSTR (lv_srcstr, splitstr, 1, i) + 1;
      END LOOP;
      DBMS_OUTPUT.PUT_LINE ('end');
    END;

  • 声明

    1.默认情况下,变量是被初始化为NULL的。

    birtdday    DATE;
    emp_count   SMALLINT := 0;

    2.对于常量声明要多加一个CONSTANT关键字:credit_limit   CONSTANT REAL := 5000.00;

    我们可以使用关键字DEFAULT来替换赋值操作符为变量初始化。

    blood_type   CHAR DEFAULT 'o';

    3.除了在声明中做初始化操作外,还可以使用NOT NULL进行约束:

    %ROWTYPE属性提供数据表(或视图)中一整行数据的类型信息。记录可以完整地保存从游标或游标变量中取出的当前行的信息。下面例子中,我们声明了两个记录,第一个保存emp表的行信息,第二个保存从游标c1取出的行信息。

    DECLARE
      emp_rec emp%ROWTYPE;
      CURSOR c1 IS 
        SELECT
     deptno, dname, loc FROM dept;
      dept_rec c1%ROWTYPE;

    我们还可以为指定的域进行赋值操作,如下例:

    acct_id INTEGER(4) NOT NULL := 9999;

    4.NATURALN和POSITIVEN是PL/SQL提供的两个不可为空的预定义子数据类型。下面这两个声明是等价的:

    emp_count NATURAL NOT NULL := 0;
    emp_count NATURALN         := 0;

    line_items POSITIVEN;   -- not allowed; not initialized

    5.使用%TYPE

    %TYPE属性能够为我们提供变量或数据库字段的数据类型。在下面的例子中,%TYPE提供了变量credit的数据类型:

    credit   REAL(7, 2);
    debit    credit%TYPE;

    在引用数据库中某个字段的数据类型时,%TYPE显得更加有用。我们可以通过表名加字段来引用,或是使用所有者加表名加字段来引用:

    my_dname scott.dept.dname%TYPE;

    6. 使用%ROWTYPE

    我们还可以为指定的域进行赋值操作,如下例:

    emp_rec.ename := 'JOHNSON';
    emp_rec.sal   := emp_rec.sal * 1.15;

    DECLARE
      CURSOR my_cursor IS
        SELECT sal + NVL(comm, 0) wages, ename FROM emp;
      my_rec my_cursor%ROWTYPE;
    BEGIN
      OPEN my_cursor;
      LOOP
        FETCH my_cursor INTO my_rec;
        EXIT WHEN my_cursor%NOTFOUND;
        IF my_rec.wages > 2000 tdEN
          INSERT INTO temp VALUES (NULL, my_rec.wages, my_rec.ename);
        END IF;
      END LOOP;
      CLOSE my_cursor;
    END;

    7.PL/SQL命名规范

    raise_salary(...);   -- simple
    emp_actions.raise_salary(...);   -- qualified
    raise_salary@newyork(...);   -- remote
    emp_actions.raise_salary@newyork(...);   -- qualified and remote

    第一种情况,我们只是简单的使用程序名称。第二种情况,我们必须使用点标志(dot notation)来引用过程,因为它是保存在emp_actions包中的。第三种情况,使用远程访问指示符,就能引用数据库连接newyork,因为过程是存放在远程数据库的。第四中情况,我们在过程名称加上限定修饰词并引用数据库连接。

    8.命名解析

    在SQL语句中,数据库字段名称的优先级要高于本地变量和形式参数。例如,下面的DELETE语句会从emp表删除所有的雇员信息,而不只是名字为"KING"的雇员:

    DECLARE
      ename   VARCHAR2 (10) := 'KING';
    BEGIN
      DELETE FROM emp
            WHERE ename = ename;
      ...

    在这种情况下,为了避免产生歧义,可以像下面这样在本地变量和形式参数的前面加上类似于"my_"这样的前缀:

    DECLARE
      my_ename VARCHAR2(10);

    或是使用块标签来进行引用限定:

    <<main>>
    DECLARE
      ename   VARCHAR2 (10) := 'KING';
    BEGIN
      DELETE FROM emp
            WHERE ename = main.ename;
      ...

    9.标识符的作用域(scope)和可见度(visiblity)

    PL/SQL块中声明的标识符对于其所在块来说是本地的,对于子块来说是全局的。如果全局标识符在子块中被重新声明,那么,全局和本地声明的标识符在子块的作用域都是存在的,但是,只有本地标识符是可见的,这时如果想引用全局标识符,就需要添加限定修饰词。

    虽然我们不能在同一块中两次声明同一标识符,但可以在两个不同的块中声明同一标识符。这两个标识符是互相独立的,对其中任何一个的改变都不会影响到另一个。但是,一个块不能引用同一级别中另外一个块中的变量,因为对于它来说,同级块中标识符即不是本地的,又不是全局的。

    如果子块中重新声明了全局标识符,本地标识符优先权高于全局标识符,我们就不能再引用全局标识符,除非使用限定名(qualified name)。修饰词可以是封闭块的标签,如下例所示:

    <<outer>>
    DECLARE
      birtddate   DATE;
    BEGIN
      DECLARE
        birtddate   DATE;
      BEGIN
       ...
        IF birtddate = OUTER.birtddate tdEN
          ...
        END IF;
        ...
      END;
      ...
    END;

    10.CASE表达式

    一个CASE表达式从一个或多个供选方案中选择一个返回结果。CASE表达式使用一个选择器来决定返回哪一个分支的结果。具体的语法形式如下:

    DECLARE
      grade       CHAR(1)      := 'B';
      appraisal   VARCHAR2(20);
    BEGIN
      appraisal    := CASE grade
                       WHEN 'A' tdEN 'Excellent'
                       WHEN 'B' tdEN 'Very Good'
                       WHEN 'C' tdEN 'Good'
                       WHEN 'D' tdEN 'Fair'
                       WHEN 'F' tdEN 'Poor'
                       ELSE 'No such grade'
                     END;
    END;

    11.在比较和条件语句中处理NULL值

    在使用NULL值时,我们一定要记住下面几条规则,避免发生一些常见的错误:

    1. 比较中如果有空值的话,那么计算结果总为NULL
    2. 对空值应用逻辑操作符NOT,结果还是NULL
    3. 条件控制语句中,如果条件的运算结果值为NULL的话,与之相关的语句就不会被执行
    4. 简单CASE语句中对于空值的判断要使用WHEN expression IS NULL

     

  • Calendar c = new GregorianCalendar(2008, 7, 8);
    System.out.println("测试结果:" + c.get(Calendar.DAY_OF_WEEK));


    国外算一周从周日开始

    一月:Calendar.JANUARY = 0

    public class ttt {

       
    public static void getWeek() throws Exception{
            SimpleDateFormat format
    = new SimpleDateFormat("yyyy-MM-dd"); 
                Calendar c
    = Calendar.getInstance(); 
                c.setTime(format.parse(
    "2008-08-08")); 
               
    int dayForWeek = 0
               
    if(c.get(Calendar.DAY_OF_WEEK) == 1){ 
                 dayForWeek
    = 7
                }
    else
                 dayForWeek
    = c.get(Calendar.DAY_OF_WEEK) - 1
                } 
                System.out.print(dayForWeek);
            }
       
    public static void main(String[] args) throws Exception {
           getWeek();
        }
    }

    输出结果是5,所以2008年8月8日应为星期五

  • http://zhangjunhd.blog.51cto.com/113473/128174

  • BigDecimal.ROUND_HALF_DOWN 如果最后一位<=5则舍弃,如果>5, 向前进一位。

    如7.5->7;7.6->8;-7.5->-7          

    BigDecimal.ROUND_HALF_UP 如果最后一位<5则舍弃,如果>=5, 向前进一位。反之舍弃。

    如7.5->8;7.4->7;-7.5->-8            

    BigDecimal.ROUND_UP       最后一位如果大于0,则向前进一位,正负数都如此。           BigDecimal.ROUND_DOWN 最后一位不管是什么都会被舍弃。          

    BigDecimal.ROUND_CEILING 如果是正数,按ROUND_UP处理,如果是负数,按照ROUND_DOWN处理。

    例如7.1->8; -7.1->-7;

    所以这种近似的结果都会>=实际值。          

    BigDecimal.ROUND_FLOOR 跟BigDecimal_ROUND_CEILING相反。例如7.1->7;-7.1->-8。

    这种处理的结果<=实际值。          

    BigDecimal.ROUND_HALF_EVEN 如果倒数第二位是奇数,按照BigDecimal.ROUND_HALF_UP处理,如果是偶数,按照 BigDecimal.ROUND_HALF_DOWN来处理。

    如7.5->8;8.5->8;7.4->7;-7.5->-8 

  • 2009-04-15

    EXCEL 創建透視表 - [EXCEL]

    1.控制面板-〉管理工具-〉數據源(ODBC)-〉用戶dsn->添加
    選擇驅動程序:oracle in oradb10g_home1
    輸入Data Source Name
    選擇TNS Service Name
    輸入User ID (compiere)
    Test connection測試通過

    建立文件DSN
    添加-〉選擇一個文件存放位置並取一個名字


    對於已經做好的透視表,只需要進行第一步。

    2.新建excel
    數據-〉導入外部數據-〉導入數據
    選擇剛剛建立的數據源
    選擇一個表
    點 創建透視表 鏈接

    3.創建透視表

  • String sqlSEQ = "SELECT s.AD_Sequence_ID FROM AD_Sequence s WHERE s.Name='AlisonCode' AND s.AD_Client_ID=?";

    if (seqID == -1) {
         log .warning("Document Sequence [AlisonCode] not exists,"
             + " Create Product fail,"
             + " Please create the Document Sequence and Named [AlisonCode]");
         return false;

        }

    MSequence seq = new MSequence(Env.getCtx(), seqID, trxName);
         int runno = seq.getNextID(); // Auto Increment No, Need Save

    seq.save(trxName);

  • java.util.Date day = new java.util.Date(System.currentTimeMillis());
       String st = "2008-01-22 00:00";
       java.text.DateFormat df = new java.text.SimpleDateFormat(
         "yyyy-MM-dd hh:mm");
       java.util.Date starttime = null;
       try {
        starttime = df.parse(st);
       } catch (ParseException e1) {
        e1.printStackTrace();
       }

       if (day.after(starttime)) {
        System.exit(0);
       }

  • 如选择打印Period是1月,RelativePeriod=-1 .etc..
    那么1+-1=12,即月份=12,但是compiere算不出这样的结果,使月份等于1.

    修改了两个java,处理这个情况:

    FinReport.java

    boolean january=false;

    if((relativeOffset!=null&&(relativeOffset.add(new BigDecimal(m_reportPeriod))).doubleValue()==0.0)){ //原本系统会做一月处理

        january=true;
       }

    else if (m_columns[col].getAmountType() != null)
       {
        info.append(" - ColumnDateAcct=");
        if (m_columns[col].isPeriod())
        {
         String sql = frp.getPeriodWhere();
         info.append("Period");
         select.append(sql);
        }
        else if (m_columns[col].isYear())
        {
         String sql = frp.getYearWhere();
         info.append("Year");
         select.append(sql);
        }
        else if (m_columns[col].isTotal()&&!january)
        {
         String sql = frp.getTotalWhere();
         info.append("Total");
         select.append(sql);    
        }
        /**
         * Stephy
         * */
        else if(m_columns[col].isTotal() &&january){ //Stephy
         String sql=frp.getTotalWhereInJan();
         info.append("Total");
         select.append(sql);
        }
        /** End **/
        else
        {
         log.log(Level.SEVERE, "No valid Column AmountType");
         select.append("=0"); // valid sql 
        }
       }

    FinReportPeriod.java

    public String getTotalWhereInJan(){
      StringBuffer sql=new StringBuffer("< ");
      sql.append(DB.TO_DATE(m_YearStartDate));
      return sql.toString();
     }

  • /**************************************************************************
      *  Print Report
      */
     public void print ()
     {
      log.info(m_info.toString());
      if (m_layout == null)
       layout();
      
      // Paper Attributes:  media-printable-area, orientation-requested, media

     //通过添加一些Print Dialog的属性,可以设置Print Dialog的选项.
      PrintRequestAttributeSet prats = m_layout.getPaper().getPrintRequestAttributeSet();
      // add:    copies, job-name, priority
      if (m_info.isDocumentCopy() || m_info.getCopies() < 1)
       prats.add (new Copies(1));   //添加属性
      else
       prats.add (new Copies(m_info.getCopies()));
      Locale locale = Language.getLoginLanguage().getLocale();
      prats.add(new JobName(m_printFormat.getName(), locale));
      prats.add(PrintUtil.getJobPriority(m_layout.getNumberOfPages(), m_info.getCopies(), true));
      prats.add(SheetCollate.COLLATED);  //RCP add by Stephy 2008/5/23
      try
      {
       //System.out.println("ReportEngine - m_info.getPrinterName() = "+m_info.getPrinterName()+ " .");
       // PrinterJob
       PrinterJob job = getPrinterJob(m_info.getPrinterName());
      // job.getPrintService().addPrintServiceAttributeListener(this);
       job.setPageable(m_layout.getPageable(false)); // no copy
      // Dialog
       if (m_info.isWithDialog() && !job.printDialog(prats)) //显示Print Dialog,并传入属性集
        return;

       //System.out.println("ReportEngine m_info.getCopies() = "+m_info.getCopies());
      // submit
       boolean printCopy = m_info.isDocumentCopy() && m_info.getCopies() > 1;
       ArchiveEngine.get().archive(m_layout, m_info);
       PrintUtil.print(job, prats, false, printCopy);

       // Document: Print Copies
       if (printCopy)
       {
        //System.out.println("ReportEngine printCopy = true");
        log.info("Copy " + (m_info.getCopies()-1));
        prats.add(new Copies(m_info.getCopies()-1));
        job = getPrinterJob(m_info.getPrinterName());
       // job.getPrintService().addPrintServiceAttributeListener(this);
        job.setPageable (m_layout.getPageable(true));  // Copy
        PrintUtil.print(job, prats, false, false);
        //System.out.println("ReportEngine END.");
       }
      }
      catch (Exception e)
      {
       log.log(Level.SEVERE, "", e);
      }
     } // print

  • cd to grace --  Genereate vendor invoice from shipment --InOutCreateVDInvoice.java -- 未在db加字段.

  • attribute set包含多个attribute use
    attribute use有attribute,而它的主键是attribute set & attribute
    asi出现的attribute就是从attribute set的attribute use的attribute取的,但是只取instance attribute = false的attributes
    attribute instance保存的就是asi的值,因此它的主键是asi & attribute 

     创建一个Attribute Set Instance示例

      //创建新的AttributeSetInstance对象

      MAttributeSetInstance masi = new MAttributeSetInstance(getCtx(), 0,    subcategory.getM_MinAttributeSet_ID(), get_TrxName());
       masi.save();

       int M_AttributeSetInstance_ID = masi.getM_AttributeSetInstance_ID();

      //一个Attribute都是一个Attribute Instance,而Attribute就是在Attribute Set->Attribute Use定义的.
       String sql = "Select M_Attribute_ID from M_AttributeUse where M_AttributeSet_ID ="+subcategory.getM_MinAttributeSet_ID();
       PreparedStatement pstmt = null;
       try
       {
        pstmt = DB.prepareStatement(sql, get_TrxName());
        ResultSet rs = pstmt.executeQuery();
        while (rs.next())
        {
         int M_Attribute_ID = rs.getInt(1);
         MAttribute attribute =new MAttribute(getCtx(),M_Attribute_ID,get_TrxName());

         //根据Attribute创建Attribute Instance.
         MAttributeInstance hai=null;
         String attvalue=null;
         if (attribute.getAttributeValueType().equals("N")) {

         //Attribute type是Number,Attribute Instance的ValueNumber保存value
          BigDecimal value = DB.getSQLValueBD(get_TrxName(),
            "SELECT ValueNumber FROM M_AttributeInstance WHERE M_AttributeSetInstance_ID="
              + getM_AttributeSetInstance_ID()+"AND M_Attribute_ID = ?",M_Attribute_ID);
          if (value == null)
           value = Env.ZERO;
          hai = new MAttributeInstance(getCtx(), M_Attribute_ID,
            M_AttributeSetInstance_ID, value, get_TrxName());
         }

         //Attribute type是String,Attribute Instance的Value保存value
         else if(attribute.getAttributeValueType().equals("S")) {
           attvalue = DB.getSQLValueString(get_TrxName(),
            "SELECT Value FROM M_AttributeInstance WHERE M_AttributeSetInstance_ID="
              + getM_AttributeSetInstance_ID()+"AND M_Attribute_ID = ?",M_Attribute_ID);
          hai = new MAttributeInstance(getCtx(), M_Attribute_ID,
            M_AttributeSetInstance_ID, attvalue, get_TrxName());
         }

         //Attribute type是List,Attribute Instance的M_AttributeValue_ID和Value保存value
         else if(attribute.getAttributeValueType().equals("L")) {
          int M_AttributeValue_ID = DB.getSQLValue(get_TrxName(),
            "SELECT M_AttributeValue_ID FROM M_AttributeInstance WHERE M_AttributeSetInstance_ID="
              + getM_AttributeSetInstance_ID()+"AND M_Attribute_ID = ?",M_Attribute_ID);
          
          hai = new MAttributeInstance(getCtx(), M_Attribute_ID,
            M_AttributeSetInstance_ID, M_AttributeValue_ID,attvalue, get_TrxName());
         }
        masi.setDescription();
        masi.save();
        rs.close();
        pstmt.close();
        pstmt = null;
       }
       catch (Exception e)
       {
        log.log(Level.SEVERE, "getLines - " + sql, e);
       }
       minute.setM_AttributeSetInstance_ID(M_AttributeSetInstance_ID);

     

  • [10:01:50] FUNG 说: 它是存倉公司
    [10:02:35] FUNG 说: 為客戶提供貨倉空間來存貨
    [10:03:08] FUNG 说: 他們只會用到compiere的MR和Shipment的功能
    [10:03:39] FUNG 说: 要清楚記錄貨品的進倉時間和是哪張mr存入的
    [10:04:50] FUNG 说: 例如一張mr內有三款貨,而每款貨來了很多「箱」
    [10:05:48] FUNG 说: 例如產品A來了十五箱,一號箱至十號箱內有100件,十一號至十三號內有150件,十四號至十五號有80件
    [10:06:47] FUNG 说: 而出貨如果要出300件貨,SHIPMENT可以指名要SHIP一號箱至三號相,又或十一號箱至十三號箱
    [10:07:07] FUNG 说: 是十一號箱至十二號箱
    [10:07:28] FUNG 说: 問題:如何實現以上的功能
    [10:08:46] FUNG 说: shipment出哪些箱是用戶選擇,不是系列自動生成
    [10:09:30] FUNG 说: 我們要做的是如何記錄「箱」這概念,以及如何讓客戶輸入和選擇「箱」
    [10:09:46] FUNG 说: 對以上的描述有沒有不明白的地方?
    [10:13:25] Wenrui 说: 每箱相当于一个locator,然后根据locator出货怎么样.
    [10:15:37] FUNG 说: 他們不會為warehouse再分locator,而且箱和locator的意思不一樣,用戶不易明白
    [10:19:12] Wenrui 说: 在mr和shipment多加一个tab,那个tab的level是1,纪录款,mr line和shipment line的tab level是2,纪录箱.
    [10:20:00] FUNG 说: 那用戶是否要在三個tab輸入資料了?
    [10:21:12] Wenrui 说: 是啊,在mr/ship line上输入要入/出的箱.
    [10:21:47] FUNG 说: 那在mr line裏要為每個箱輸入一次資料了?
    [10:26:41] Wenrui 说: 或者想个办法自动生成啊.
    [10:26:50] FUNG 说: 例如呢
    [10:32:24] FUNG 说: 由於這客用要用web,自動生成的功能不好用form來實現
    [10:35:55] Wenrui 说: 我们如何把箱这个概念放到库存.
    [10:36:07] FUNG 说: 可以用attribute
    [10:36:13] FUNG 说: 你認為如何?
    [10:39:47] Wenrui 说: a可以啊.
    [10:41:47] Wenrui 说: 他们每款大概有多少箱啊.
    [10:41:57] FUNG 说: 說不定
    [10:43:07] Wenrui 说: 我觉得不用加多的tab了,如果他们工作量不是很大,就充分利用copy record的功能.
    [10:46:46] FUNG 说: 但很煩複呀,例如剛才的例子,開頭十個箱是一樣的…
    [10:48:35] Wenrui 说: 那来个process,问要copy几条.
    [10:49:19] FUNG 说: 我看可以在你說的新tab的before save做功能
    [10:50:04] FUNG 说: 在新tab中輸入起始箱號和最後箱號、產品和每箱數量
    [10:50:22] FUNG 说: 在aftersave(不是beforesave)生成line
    [10:53:02] Wenrui 说: 但是自动生成后还是要去line改产品的asi啊.
    [10:53:40] FUNG 说: 改asi的甚麼?
    [10:54:27] Wenrui 说: 哦,也可以在新tab上加asi
    [10:54:35] FUNG 说: 是
    [10:55:06] Wenrui 说: 那就这么办吧.
    [10:56:18] FUNG 说: 先設計一下代碼和db結構,有時間再試寫。
    [10:57:27] Wenrui 说: 在261吗?
    [10:57:33] FUNG 说: 是
    [10:57:48] Wenrui 说: 261有个shipment style呢.
    [10:58:12] FUNG 说: 這個項目要把style隱藏
    [10:58:27] Wenrui 说: 哦
    [10:58:54] Wenrui 说: 他们只用到mr和shipment啊.
    [10:59:17] FUNG 说: 應還有invoice,但還沒確實談要
    [10:59:22] FUNG 说: 求
    [10:59:30] Wenrui 说: ok
  • 更新AD_Sequence所有'AD_'开头表的的currenct next为9,000,000,一星期后,改回currenct next为小于9,000,000的最大current next.

  • 版本:Compiere 261

    问题描述:如果没有输入数据立即保存的,head的数据没有保存,直接转入下一个tab,势必给操作带来麻烦。

    关键字:VTabbedPane.setSelectedIndex()方法.

    setSelectedIndex是JTabbedPane的方法,VTabbedPane extends JTabbedPaneT,因此JTabbedPane覆盖了这个方法,这个覆盖方法252d没有,261和300都有了.先来看看261的版本

    public void setSelectedIndex (int index)
     {
      Component newC = getComponentAt(index);
      GridController newGC = null;
      if (newC instanceof GridController)
       newGC = (GridController)newC;
      // Display
      if (newGC != null)
      {
       String logic = newGC.getDisplayLogic();
       if (logic != null && logic.length() > 0)
       {
        boolean display = Evaluator.evaluateLogic(newGC, logic);
        if (!display)
        {
         log.info("Not displayed - " + logic);
         return;
        }
       }
      }

      //
      int oldIndex = getSelectedIndex();
      if (newGC != null && oldIndex >= 0 && index != oldIndex)
      {
       Component oldC = getComponentAt(oldIndex);
       if (oldC != null && oldC instanceof GridController)
       {
        GridController oldGC = (GridController)oldC;
        if (newGC.getTabLevel() > oldGC.getTabLevel()+1)
        {
         // Search for right tab
         for (int i = index-1; i >=0; i--)
         {
          Component rightC = getComponentAt(i);
          GridController rightGC = null;
          if (rightC instanceof GridController)
          {
           rightGC = (GridController)rightC;
           if (rightGC.getTabLevel() == oldGC.getTabLevel()+1) //意思是说,当你有level1的tab转到
           {                                                                  //level3的tab是不被允许的.
            ADialog.warn(0, this, "TabSwitchJumpGo", rightGC.getTitle());
            return;
           }
          }
         }
         ADialog.warn(0, this, "TabSwitchJump");
         return;
        }
        oldGC.setMnemonics(false);
       }
      }
      // Switch
      super.setSelectedIndex (index);
      if (newGC != null)
       newGC.setMnemonics(true);
     } // setSelectedIndex

     而300则做得更完善些,它除了实现了上面的功能,还限制没保存的记录不能转tab

    public void setSelectedIndex (int index)
     {
      System.out.println("==setSelectedIndex==");
      Component newC = getComponentAt(index);
      GridController newGC = null;
      if (newC instanceof GridController)
       newGC = (GridController)newC;
      // Display
      if (newGC != null)
      {
       boolean display = newGC.isDisplayed();
       if (!display)
       {
        log.info("Not displayed - " + newGC.toString());
        return;
       }
      }

      //
      int oldIndex = getSelectedIndex();
      if (newGC != null && oldIndex >= 0 && index != oldIndex)
      {
       Component oldC = getComponentAt(oldIndex);
       if (oldC != null && oldC instanceof GridController)
       {
        GridController oldGC = (GridController)oldC;
        if (newGC.getTabLevel() > oldGC.getTabLevel()+1)
        {
         // Search for right tab
         for (int i = index-1; i >=0; i--)
         {
          Component rightC = getComponentAt(i);
          GridController rightGC = null;
          if (rightC instanceof GridController)
          {
           rightGC = (GridController)rightC;
           if (rightGC.getTabLevel() == oldGC.getTabLevel()+1)
           {
            ADialog.warn(0, this, "TabSwitchJumpGo", rightGC.getTitle());
            return;
           }
          }
         }
         ADialog.warn(0, this, "TabSwitchJump");
         return;
        }else if(newGC.getTabLevel() == oldGC.getTabLevel()+1){
         if(oldGC.getMTab().getRecord_ID()==-1
          && !oldGC.getMTab().getTableName().equals("AD_Language")
          && !oldGC.getMTab().getTableName().equals("AD_EntityType")){
           ADialog.warn(0, this, "TabSwitchJumpGo", oldGC.getTitle());
           return;
         }
        }
        oldGC.setMnemonics(false);
       }
      }
      // Switch
      super.setSelectedIndex (index);
      if (newGC != null)
       newGC.setMnemonics(true);
     } // setSelectedIndex

    其实也没觉得300的做法有多妙:),如果没保存记录有完整就自动保存那该多好;),这不过这种做法更加保险,我主要喜欢这段代码的写法

  • e.g1.将图片存储到byte array

    Object m_imageSaveValue=null;

    ...

    // Show File Open Dialog
      JFileChooser jfc = new JFileChooser();
      jfc.setMultiSelectionEnabled(false);
      jfc.setFileSelectionMode(JFileChooser.FILES_ONLY);
      jfc.showOpenDialog(this);

      // Get File Name

      File imageFile = jfc.getSelectedFile();

    try{
        m_imageValueSave=new byte[(int)imageFile.length()];
        FileInputStream fis = new FileInputStream (imageFile);
        fis.read((byte[])m_imageValueSave);
        fis.close();
                    
       }
       catch(IOException io)
       {
        io.printStackTrace();
       }

    e.g2.将byte array(image)输出到文件

    private void saveAttachmentToFile()
     {
      if(m_imageValueSave==null || !(m_imageValueSave instanceof byte[]))
       return;
      byte[] byteArray = (byte[])m_imageValueSave;
      JFileChooser chooser = new JFileChooser();
      chooser.setDialogType(JFileChooser.SAVE_DIALOG);
      chooser.setDialogTitle(Msg.getMsg(Env.getCtx(), "AttachmentSave"));
      int returnVal = chooser.showSaveDialog(this);
      File saveFile = chooser.getSelectedFile();
      if (saveFile == null)
       return;
      try{
       FileOutputStream output =new FileOutputStream(saveFile);
       output.write(byteArray);
       output.close();

      }catch(FileNotFoundException fe){
       fe.printStackTrace();
      }
      catch (IOException e) {
                e.printStackTrace();
      }
     } // saveAttachmentToFile

     e.g3.从db查询blob(image),在jtable显示图片.

    public void actionPerformed(ActionEvent e) {
         String sql="select i.ad_image_id,i.name,i.binarydata from ad_image i where i.ad_image_id in (100,101)";
         int row=0;
         miniTable.setRowCount(row);
         try
            {
                    PreparedStatement pstmt = DB.prepareStatement(sql.toString());
                    ResultSet rs = pstmt.executeQuery();
                    //
                    while (rs.next())
                    {
                          //  extend table
                          miniTable.setRowCount(row + 1);
                          //  set values
                          miniTable.setValueAt(new IDColumn(rs.getInt(1)), row, 0); // order line id
                          miniTable.setValueAt(rs.getString(2), row, 1); //document no
                          Blob blob  = rs.getBlob(3);
                          InputStream is = blob.getBinaryStream();
                          int length = (int) blob.length();
                          byte[] buffer = new byte[length]; //buffer length等于blob length
                          try{
                           is.read(buffer);  //读取blob到buffer
                           is.close();
                          }catch (IOException ex) {
                           ex.printStackTrace();
                          }
                          ImageIcon icon=new ImageIcon(buffer);
                          miniTable.setRowHeight(row,icon.getImage().getHeight(null));
                         
                          miniTable.setValueAt(icon,row,2); // Image

                          row++;
                       // }
                    }
                    rs.close();
                    pstmt.close();
            }
            catch (SQLException ex)
            {
             ex.printStackTrace();
            }
            //
            miniTable.autoSize();
        }

  • 版本:Compiere 261 changed 

    问题描述:在 product [window],click BOM Create button,弹出BOM [window],定位到product id对应的BOM or new BOM,当再次dick BOM Create Button,发生错误,没有process列.

    关键字:变量m_onlyCurrentRows (boolean),m_onlyCurrentRows的值决定查询条件

    GridTable.createSelectSql()

    if (m_onlyCurrentRows && m_TabNo == 0)
      {
       if (where.toString().indexOf(" WHERE ") == -1)
        where.append(" WHERE ");
       else
        where.append(" AND ");
       // Show only unprocessed or the one updated within x days
       where.append("(Processed='N' OR Updated>");
       if (!DB.isOracle ())
        where.append("addDays(current_timestamp, -1)"); //jz
       else
        where.append("SysDate-1");
       where.append(")");
      }

    m_onlyCurrentRows的值,由什么决定呢?m_onlyCurrentRows的值决定错sql的走向.

    追踪到GridWindowVO.createTabs()发现window type决定m_onlyCurrentRows,去看window的设置,window type=Maintain,因此m_onlyCurrentRows应是false的,即不会生成process=..的sql.

    GridTabVO mTabVO = GridTabVO.create(mWindowVO, TabNo, rs,
         mWindowVO.WindowType.equals(WINDOWTYPE_QUERY),  //  isRO
         mWindowVO.WindowType.equals(WINDOWTYPE_TRX));

    逐想到是否与缓存有关,第一次open window,取信数据,第二次在打开,取缓存的数据,但是却取错了.

    去掉prefrence的cache windows选项,果然运行正常.现在要找到缓存数据取错的原因,即要知道cache windows的功能.

    先去Preference.java找到cache window checkbox,追寻到引用

    AEnv.getMWindowVO()

    if (AD_Window_ID != 0 && Ini.isCacheWindow()) // try cache
      {
       mWindowVO = s_windows.get(AD_Window_ID);
       if (mWindowVO != null)
       {
        mWindowVO = mWindowVO.clone(WindowNo);
        log.info("Cached=" + mWindowVO);
       }
      }

    OK,我发现了关键方法clone(),如果isCacheWindow,则先clone window,再clone tabs

    VGridTabVO.clone()

    clone.IsSortTab = IsSortTab;
      clone.AD_ColumnSortOrder_ID = AD_ColumnSortOrder_ID;
      clone.AD_ColumnSortYesNo_ID = AD_ColumnSortYesNo_ID;
      //  Derived
      clone.onlyCurrentRows = true;

    ----

    所有的数据都是从缓存拿,为什么onlyCurrentRows 要设为true?这就是为什么第二次查询会出问题的原因,因此onlyCurrentRows也应从缓存取  clone.onlyCurrentRows =onlyCurrentRows;