-
2009-12-30
在项目中加入jar包 - [Compiere - case]
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"/>
-
2009-10-24
上传文件到FTP(支持断点续传) - [JAVA]
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;
}
} -
一些非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;
} -
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; -
2009-08-24
1.PL/SQL基础(声明) - [PL/SQL学习笔记]
声明
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值时,我们一定要记住下面几条规则,避免发生一些常见的错误:
- 比较中如果有空值的话,那么计算结果总为NULL
- 对空值应用逻辑操作符NOT,结果还是NULL
- 条件控制语句中,如果条件的运算结果值为NULL的话,与之相关的语句就不会被执行
- 简单CASE语句中对于空值的判断要使用WHEN expression IS NULL
-
2009-08-24
给你一个日期(2008-8-8),问这一天是否是星期几 - [JAVA]
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日应为星期五 -
2009-08-21
Swing布局管理器介绍 - [JAVA]
-
2009-07-24
java实现精确的四舍五入 - [JAVA]
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.創建透視表
-
2009-04-02
Document Sequence and next Sequence - [Compiere - case]
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 Saveseq.save(trxName);
-
2009-04-02
timestamp date format - [Compiere - case]
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);
} -
2009-03-16
financial reports bug - [Compiere bugs]
如选择打印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();
} -
2008-05-23
PrinterJob类显示Print Dialog - [Java Print]
/**************************************************************************
* 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 -
2008-04-12
attention - [compiere - requirements]
cd to grace -- Genereate vendor invoice from shipment --InOutCreateVDInvoice.java -- 未在db加字段.
-
2008-03-28
Create Attribute Set Instance - [Compiere - case]
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); -
2008-03-24
与F讨论出货需求 - [compiere - requirements]
[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 -
2008-03-14
小于一段数的最大数(sequence currenct next) - [问题集合操场 - sql]
更新AD_Sequence所有'AD_'开头表的的currenct next为9,000,000,一星期后,改回currenct next为小于9,000,000的最大current next.
-
2008-03-13
转tab检查header数据的完整性 - [Compiere bugs]
版本: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();
}
} // saveAttachmentToFilee.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); // Imagerow++;
// }
}
rs.close();
pstmt.close();
}
catch (SQLException ex)
{
ex.printStackTrace();
}
//
miniTable.autoSize();
} -
2008-03-07
查询了错误的列Processed - [Compiere bugs]
版本: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;







