User:Trifonnt

From ADempiere
Revision as of 15:15, 3 September 2007 by Trifonnt (Talk) (Q.24 How to display only label in view?)

Jump to: navigation, search
This Wiki is read-only for reference purposes to avoid broken links.
Name This user real name is : Trifon Nikolaev Trifonov
Sourceforge logo.png This user has a Sourceforge account.
Wikipedia logo.png This user has a Wikipedia account.


Contents

About Me

I started my own company last year providing Adempiere and Compiere consultancy and development. I like developing business systems. This makes me headache from time to time, but also makes me very happy when i see system working.

  • I'm SUN Certified Programmer 1.4.
  • Graduated High School of Economics and Computer Science
  • Bachelor degree in Mathematics

As a member of ADempiere Council i like to review patches and SVN commits in order to be sure that code remains stable and system functional.

My Blogs

Trifon's Paid Services

  • Need urgent advice for your Adempiere implementation or need to talk with experienced Adempiere developer - call now to get friendly help. Ask for Trifon.
  • Price is 0.80 euro per minute

Use Skype Prime service to call now. More info about Skype Prime

  • 1500 EURO / week + ticket + hotel

  • If you need training or support for EDI in Adempiere, please do not hesitate to contact me.
  • Prices start from 30 Euro per hour.

  • Leading Open Source Enterprise Portal
  • JSR-168 compliant
  • Business Friendly Open Source License: MIT License


  • Suitable for:
    • Small applications which need to be developed from scratch
    • Development of applications which must be part of a Web Portal.
    • Applications based on OpenXava can run on any DB which is supported by Hibernate.
    • Licensed under LGPL. You can develop commercial application using OpenXava.
    • Applications based on OpenXava can run on any application server (Tomcat, JBoss, WebSphere, etc).
    • Applications based on Openxava have easy integration of reports made with JasperReports.

Trifon's contributions to OpenXava

  • Please contact me for quotation.

  • Development of java applications.
  • Please contact me for quotation.

Trifon's Recent thoughts

OpenBravo Green?

Recently OpenBravo Green came into my mind(Jordi Mash told me about OpenBravo Green at Adempiere conference in Belrin) and i decided to review it and to check status.

First of all they have good document with intentions and requirements. Good number of frameworks and technologies are listed at this page: Design principles for Openbravo Green

They have a prototype and online demo, but i failed to login into demo page. OpenBravo Green demo

Source code of prototye was more interesting for me and it was the reason that made me write this post.

  • I expected to see more commnents and more TODOs. It is prototype and important is to see what developer intend to do than what has been done. Visibility of ideas and intentions is more important than actual work finished. This is not a minus it is just a note and wish from my side.
  • Source code dissapointed me, because:
    • I saw one regular web based application. No trace from the idea of Model Driven Architecture or Application dictionary. Probably model classes were generated by some tool, but no evidence or comment in the code or in the wiki pages.
    • Only one author, no participataion from community.
    • Author is Adrian Romero, who is very good POS developer(Tina POS), but i doubt about his experience in ERP systems and Application Driven systems. It looks that he is the only active visible OpenBravo developer.


Adempiere: Which is first Data or Code?

Recently i modified very deeply core of Adempiere in order to simplify development and to speed it up. I wasn't sure that such modification is possible and i was very happy when it happened and when at the end it even worked. After some tests i decided to revert to old version of persistent classes (X_xxx) and i deleted newly generated classes and started well known old GenerateModel, but... It failed because GenerateModel needs persistent classes in order to connect to Database. But developer can't generate persistent classes because GenerateModel can't connect to DB. So we got cycle... And here comes my queastion: Which is first Data or Code? I think that design of GenerateModel can be improved:

  • In order to connect to DB it should not need Adempiere Client.
  • Data from DB can be moved to external location like (CSV or xml file), but this could creation duplication of data(stored in DB and in file). Not so good idea.

Trifonnt 12:55, 19 August 2007 (EDT)

Does Community Open Source project need roadmap?

  • What does Roadmap means for a project?
  • Goolge define:Roadmap says few things, but i like mostly one of them:
A program for future development indicating what will be developed and when.

My opinion is that it must be more like:

A program for future development indicating what will be developed, by who will be developed and when.
  • Three very important conditions in order to have Roadmap:
    • Functionality

This is easy to be done. All we need functionality and all we with pleasure advice what to be done. So i think that here ANY project can produce good number of items.

    • Who will develop it?

UPS, here the problems come. In order to develop something WE need knowledge, so from the big list of ALL only developers can participate in development. All this means that in Roadmap, Open Source project must identify developers or companies that will create new functionality. Ok, let's say that i would like to build this new functionality and i write my name against the feature.

    • When functionality will be developed?

Who knows? Who can answer? Who is responsible? It looks that WHEN brings more question than it answers. In Open Source project if no one commit his time to develop given functionality no one can answer the questions. Moreover if someone commit his free time does anyone has rights to push him for specific date? I do not think so... It is developers free will to participate and create functionality. And as you guess in most of the time it is hard to find free time for building big functionality.

My personal opinion is that spending time to create Roadmap without committed developers/companies against specific functionality is just good wast of time. Even worst it is confusing for the end user who read Roadmap and wait for new version and new functionality.

My big excuse to the users who will be frustrated from ssch Roadmaps. Trifonnt 19:31, 8 August 2007 (EDT)

To be Adempiere Developer or Adempiere Businessman(Seller)

I work with Adempiere/Compiere since more than 3 years. I must say that i like the product and area very much. I like to develop extensions for Adempiere and i do it everyday for 10-12 hours a day and sometimes more. I know it is not healthy, but... But last weeks i started to feel tired from all the rules for Developers and all fights in forums and in email lists. Also i realized that Business people around Adempiere do not have such limitations as Developers. For example:

  • On www.adempierebusiness.com there are 30 registered Implementation Partners. Good number.
    • What does this Implementation Partners give to Adempiere project?

Some of them nothing. Well business people say that if we limit and ask for money then this will stop add option of Adempiere. But may i ask how developers can survive?

    • What they get?

FREE Advertisement. Perfect... I think that Adempiere is the only project that allows such thing. Most of other Open Source projects take money for advertisement and put money back into the project.

  • I have read that some of Adempiere Implementation Partners do not work for less than 50 000 USD. Amount which i as developer do not had chance to see till now. Who knows someday Clients may find that contracting developer is much faster and cheaper...
  • User expect developers to provide FREE help in Adempiere forums, which gives additional workload to developers.
  • Developers are expected to be open and expose all their information to community.

So:

  • I found that i had made mistake.

It looks that to be Businessman and make money around Adempiere is much easy and painless than to be Adempiere developer. :(

  • I think to reconsider my role in Adempiere project. Role of Implementation Partner looks much safer and giving more incomes.
  • I should start making advertisements from now... So here it is: I can provide Adempiere on Site Implementation for 1500 EURO / week + expenses(hotel + ticket) in case i need to move to other location.

Adempiere trackers

Submitted by Trifon and Assigned to Trifon

Assigned to Trifon

Adempiere contributions

Functionality Contributions

EDI @ Adempiere - Sponsored Development: EDI Import/Export

Replication @ Adempiere - Sponsored Development: Replication

  • Links:

Resources: http://www.afceurope.com/JMS.html http://www.manageability.org/blog/stuff/open-source-jms-java

Example the Replication with JMS http://www-128.ibm.com/developerworks/library/i-jms/

Use the JMS Synchrony and Asynchrony http://openjms.sourceforge.net/usersguide/using.html

http://www.javalobby.org/articles/distributed-jms/

JMS @ Salesforce.com:

Simplify and Speedup Adempiere development

SF.NET forum post here

Feature Requests:

This enhancement allows Adempiere developers to:

  • Speedup thier day to day work and to have more Object friendly code.
  • Work with Java Interfaces instead of Java Classes.

For example:

    • Adempiere - Old code
Trx trx = Trx.get(Trx.createTrxName("Test"), true);
trx.start();

MAlert alertOldWay = new MAlert(Env.getCtx(), 100, trx.getTrxName());
log.info(alertOldWay.toString());
		
X_AD_AlertProcessor alertProcessorOldWay = new X_AD_AlertProcessor(Env.getCtx(), alertOldWay.getAD_AlertProcessor_ID(), trx.getTrxName());
log.info("alertProcessorOldWay.getAD_AlertProcessor_ID = " + alertProcessorOldWay.getAD_AlertProcessor_ID());

alertOldWay.setDescription("Trifon test");
//--- Save; 
resultSave = alertOldWay.save();
log.info("resultSave = " + resultSave);
		
System.out.println("New value of Description = " + alertOldWay.getDescription());

trx.commit();
trx.close();
    • Adempiere - New code
Trx trx = Trx.get(Trx.createTrxName("Test"), true);
trx.start();

I_AD_Alert alert = new MAlert(Env.getCtx(), 100, trx.getTrxName());
log.info(alert.toString());

I_AD_AlertProcessor alertProcessor = alert.getI_AD_AlertProcessor();
log.info("I_AD_AlertProcessor.getAD_AlertProcessor_ID = " + alertProcessor.getAD_AlertProcessor_ID());

alert.setDescription("Trifon Description modified!");
//--- Save; PO.save(PO) must be static method!!! Two way of usage: PO.save(Object) or PO.save(PO)
//resultSave = PO.save((PO)alert); 
resultSave = PO.save(alert);  // Overloaded save method; For simple usage!
log.info("resultSave = " + resultSave);
				
System.out.println("New value of Description = " + alert.getDescription());

trx.commit();
trx.close();

This modificationas make Adempiere code closer to Hibernate

    • Hibernate code. Example code from here.
Session session = HibernateUtil.getSessionFactory().getCurrentSession();

session.beginTransaction();

Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);

session.save(theEvent);

session.getTransaction().commit();

Proposed by Hengsin modification:

Add second interface I_Persistent with one method save()

public interface I_Persistent {
	public boolean save();
}


  • Using the coding pattern below instead of the old new MAlert(Env.getCtx(), 100, trx.getTrxName()) approach would ensure your code always using the correct model class.

How to load:

// to load existing record
int AD_Alert_ID = 100;
MTable table = MTable.get(Env.getCtx(), I_AD_Alert.Table_ID);
I_AD_Alert alert = (I_AD_Alert)table.getPO(AD_Alert_ID, trxName);

How to create new record:

// to create new record
MTable table = MTable.get(Env.getCtx(), I_AD_Alert.Table_ID);
I_AD_Alert alert = (I_AD_Alert)table.getPO(0, trxName);
...


  • Classes which override save() method:
org.compiere.process.DocActionTemplate
org.compiere.model.MClient
org.compiere.model.MClientInfo
org.compiere.model.MSystem

Generated Model class must implement both interfaces and extend PO.

Weak sides that need to be improved:

  • Hard coded Document Engine
  • Hard coded lookup of model class
  • Hard coded list of document type for new client
  • Incomplete Security Implementation
  • No plugin/module support. Both ADCK and 2Pack support dynamic deployment of AD changes but doesn't address the need to deploy java code extension ( especially problematic/messy if your extension needs to use additional third party library).
  • Incomplete web client port
  • Incomplete workflow engine implementation
  • Various swing client bug and limitation
Links

Planned Contributions and Tasks

  • Restrict form "Generate Invoices (manual)" to show only public records (hide locked records) BUG - 1713337
  • Restrict form "Generate Shipments (manual)" to show only public records (hide locked records) BUG - 1713317

Interesting bugs/patches/Feature Requests

Feature Requests

My favorite readings

General

Interesting

Mozilla - Firefox Extensions

Project management

  • Project Dune Web-based issue tracker. Integration with subversion or cvs, code inspections in the browser, SCRUM project management.

Open Source articles

We tried once to create an open-source developer out of a normal developer, but it completely failed.
We never tried it again. Truth be told, I had an aversion to it.
An open source developer is a self-starter. 
He's competitive - this is someone that wants to prove that they can do something better than you can. 
As such, it's a great recruitment/qualification vehicle, because you can see their work before you ever think of hiring them. 
You can see if they'll work out for the company. 
We definitely took that approach to hiring.


OSS Etiquette & Culture

My wish list - Books

Ordered by my desire!!!

Articles and posts from Martin Fowler

Financial based

Read and respond to this message at: https://sourceforge.net/forum/message.php?msg_id=4366156 By: drhayderaziz

I think that Adempiere would get a lot of kudos if Adempiere went the XBRL route www.xbrl.org for financial reporting. For listed companies (and even for unlisted) XBRL will be the way to go especially for any international company that has to consolidate accounts from multiple legal entities in multiple countries in a standardized manner. This would be an essential component of an ERP system in the 2008-2009 time frame. If Adempiere would have its COA structured the XBRL way and have XBRL compliant financial statements at company/consolidated level it would be an AMAZING value proposition to so many organizations.

Comparison of Open Source ERP Systems

Non IT Technology


Social networks

Developer oriented social network

ERP articles

Interesting

Adempiere

Adempiere Brazil Localization effort people that are cooperating with Adempiere LBR (LBR = Localization Brazil). Those are their names, followed by their sourceforge username.

Eduardo Montenegro (emontenegro) Alvaro Montenegro (amontenegro) Mario Grigioni (mgrigioni)


Adempiere commit schema

  • Mentor/Commiter approach approved with 11/11 votes, according to this thread.

VERY IMPORTANT

Incoming events

Time: xx:xx GMT Day: Tuesday June 26 Venue: IRC channel #adempiere-team (on irc.freenode.net)

1 - Xxx

2 - Xxx

Possible integrations with external systems

  • care2x Care2x is a hospital information system which has lots of feature but no accounting at all.

Adempiere Application dictionary vs. Axapta Data dictionary

Performance tests & results made by me

This are slower than on my laptop. On laptop result was around 17 products / second.

Processors..........: 2 x Pentium2 800MHz
RAM.................: 2 GB
OS..................: Linux, Fedora Core 5
DB..................: Oracle XE
JDK.................: Sun JDK 1.5.0_11
Adempiere version...: 3.3.0
Note................: Adempiere client on the same machine as DB.

Tables where records are inserted: 
AD_TREENODEPR     1095095
M_PRODUCT         1095095
M_PRODUCT_ACCT    1095095
M_PRODUCT_TRL     1095095

Performance table

Start Time      = Thu Aug 09 04:57:01 EEST 2007
End Time        = Thu Aug 09 05:13:54 EEST 2007
Duration(ms)    = 1012585
Duration(sec.)  = 1012
Duration(min.)  = 16
Products        = 5000
Time(seconds)   = 1012
Produsts/Second = 4.9407115

Start Time      = Thu Aug 09 05:17:04 EEST 2007
End Time        = Thu Aug 09 09:14:23 EEST 2007
Duration(ms)    = 14239657
Duration(sec.)  = 14239
Duration(min.)  = 237
Products        = 144000
Time(seconds)   = 14239
Produsts/Second = 10.11307 


Start Time       = Fri Aug 10 03:43:13 EEST 2007
End Time         = Fri Aug 10 07:42:25 EEST 2007
Duration(ms)     = 14351454
Duration(sec.)   = 14351
Duration(min.)   = 239
Duration(hours.) = 3
Products         = 144000
Time(seconds)    = 239
Produsts/Second  = 10.03
 
Start Time       = Fri Aug 10 13:00:23 EEST 2007
End Time         = Fri Aug 10 16:58:42 EEST 2007
Duration(ms)     = 14298664
Duration(sec.)   = 14298
Duration(min.)   = 238
Duration(hours.) = 3
Products         = 144000
Time(seconds)    = 14298
Produsts/Second  = 10.071339

Start Time       = Fri Aug 10 19:59:38 EEST 2007
End Time         = Sat Aug 11 03:59:55 EEST 2007
Duration(ms)     = 28817002
Duration(sec.)   = 28817
Duration(min.)   = 480
Duration(hours.) = 8
Products         = 288000
Time(seconds)    = 28817
Produsts/Second  = 9.994101

Start Time       = Sat Aug 11 17:53:38 EEST 2007
End Time         = Sun Aug 12 02:18:17 EEST 2007
Duration(ms)     = 30278812
Duration(sec.)   = 30278
Duration(min.)   = 504
Duration(hours.) = 8
Products         = 288000
Time(seconds)    = 30278
Produsts/Second  = 9.511857

Postal Code Web Service

Implementor hints

Show where products are located

Submitted By: Jan Kantert (jab_doa)

Summary: Show where products are located

sf.net tracker: here


Initial Comment: Log in as System Administrator. Select "Application Dictionary" -> "Window, Tab & Field" -> Name = Product

Click on "Tab". Go to the end of the list and add a new row. Zoom in and set Table to "M_Storage_M_Storage", Name to "Located at", Description to "Where are my units located?", Tab Level to "2" and check "read only". Now save and click on "Create Fields". Thats it. Now you can see where your units are when zooming into a product.

Security

Submitted By: Armen (armenrz)

Summary: Security

sf.net tracker: here


1) ADempiere support role based security - Role window. 2) Role Data Access. You can also play around with SQLWhere in Window, Tab & Field.

Example: (instr('@#User_Level@','C')<>0 OR C_BPartner.CreatedBy=@#AD_user_ID@ OR C_BPartner.SalesRep_ID=@#AD_User_ID@)

For pricelist, there's one place you can't hide price from user, which is Product Info. You need to modify some codes to do this. What I did is add IsCanViewPrice option in Role so now each role can be set either they can view price or not.


Bank Transfer tips

Submitted By: usman88

Summary: Bank Transfer tips

sf.net tracker: here

Small tips to handle 1 source Bank to 1 recipient Bank transfer transaction.

1. Create new cash journal.

2. Select appropriate cash book ( in my case i use dummy cash book to serve this transaction)

3. Next go to cash line and create a new line and select cash type as bank transfer.

4. Then select source bank account and type in transfer amount

5. Create 2nd line as in step 3.

6. This time select recipient bank account and type in transfer amount but in negative sign.

7. Back to cash journal tab, complete and post it.

8. To finalize it create, complete and post Bank statement for each bank account.


PS. Above tips can be use for more complex scenario such as transfer from 2 bank to 1 recipient.

Listing a BOM within a BOM

Submitted By: stingreye

Summary: Listing a BOM within a BOM

sf.net tracker: here

Here is the view in Oracle... Because we have so many parts we ended up doing a materialized view that we update with a process in Compiere everytime we

change

a BOM. We did that so we could report off of this view faster.

I am sure someone out here could write a much better query than this but here is what I did. Basically, created a view on top of itself so that the parent records would null. Then used the connect by clause to generate the hierarchy. You will also notice the use of connect_by_root = Top level of bill. Connect_by_ISLEAF = bottom level of bill. Level = what level of the hierarchy.

CREATE OR REPLACE VIEW BOM_INDENTED
(TOPLEVELID, TOPLEVEL, TOPLEVEL_DESC, PRODUCTNUM, PRODDEC, 
 M_PRODUCTBOM_ID, BOM_LEVEL, BOMQTY, DESCRIPTION, AD_CLIENT_ID, 
 AD_ORG_ID, ROWNUMBER, BOM_LEVEL_NUM, BOTTOMCOMPONENT)
AS 

SELECT   CONNECT_BY_ROOT M_ProductBom_ID AS TOPLEVELID,CONNECT_BY_ROOT Value
AS TOPLEVEL,CONNECT_BY_ROOT b.DESCRIPTION AS TOPLEVEL_DESC, Value AS
 ProductNum,
b.DESCRIPTION AS ProdDesc, M_PRODUCTBOM_ID,LPAD(' ',2*(LEVEL-1)) ||
 TO_CHAR(LEVEL)
BOM_Level, BOMQTY, a.DESCRIPTION,a.AD_CLIENT_ID, a.AD_ORG_ID, ROWNUM, LEVEL,
Connect_By_ISLEAF
  FROM 
  
-- part 2 subquerry combining top level and bom to create null to start from
( SELECT
 "M_PRODUCT_BOM_ID","AD_CLIENT_ID","AD_ORG_ID","ISACTIVE",--"CREATED","C
REATEDBY","UPDATED","UPDATEDBY",  (remove becuase unnnecessarry)
"LINE","M_PRODUCT_ID","M_PRODUCTBOM_ID","BOMQTY","DESCRIPTION","BOMTYPE"
FROM M_PRODUCT_BOM

UNION
-- part 1 the tope level querry creating the nulls that is combined in part 2
SELECT M_PRODUCT_BOM_1.M_PRODUCT_BOM_ID, M_PRODUCT_BOM.AD_CLIENT_ID,
M_PRODUCT_BOM.AD_ORG_ID, M_PRODUCT_BOM_1.ISACTIVE,-- M_PRODUCT_BOM_1.CREATED,
M_PRODUCT_BOM_1.CREATEDBY, M_PRODUCT_BOM_1.UPDATED, M_PRODUCT_BOM_1.UPDATEDBY,
(removed unnecessary)
 M_PRODUCT_BOM_1.LINE,M_PRODUCT_BOM_1.M_PRODUCTBOM_ID AS M_PRODUCT_ID,
M_PRODUCT_BOM.M_PRODUCT_ID AS M_PRODUCTBOM_ID,  M_PRODUCT_BOM_1.BOMQTY,
M_PRODUCT_BOM_1.DESCRIPTION, M_PRODUCT_BOM_1.BOMTYPE
FROM M_PRODUCT_BOM LEFT JOIN M_PRODUCT_BOM  M_PRODUCT_BOM_1 ON
M_PRODUCT_BOM.M_PRODUCT_ID = M_PRODUCT_BOM_1.M_PRODUCTBOM_ID
GROUP BY M_PRODUCT_BOM_1.M_PRODUCT_BOM_ID, M_PRODUCT_BOM.AD_CLIENT_ID,
M_PRODUCT_BOM.AD_ORG_ID, M_PRODUCT_BOM_1.ISACTIVE, M_PRODUCT_BOM_1.CREATED,
M_PRODUCT_BOM_1.CREATEDBY, M_PRODUCT_BOM_1.UPDATED, M_PRODUCT_BOM_1.UPDATEDBY,
M_PRODUCT_BOM_1.LINE, M_PRODUCT_BOM.M_PRODUCT_ID,
 M_PRODUCT_BOM_1.M_PRODUCTBOM_ID,
M_PRODUCT_BOM_1.BOMQTY, M_PRODUCT_BOM_1.DESCRIPTION, M_PRODUCT_BOM_1.BOMTYPE
HAVING (((M_PRODUCT_BOM_1.M_PRODUCTBOM_ID) IS NULL))) a
-- part 3 continued
   INNER JOIN M_PRODUCT b
   ON a.M_PRODUCTBOM_ID = b.M_PRODUCT_ID
  START WITH a.M_PRODUCT_ID IS NULL
  CONNECT BY PRIOR  M_PRODUCTBOM_ID = a.M_PRODUCT_ID
  ORDER SIBLINGS BY b.Value
/

If you want to use materialized views... we did it the following way (I really know very little about them, so if someone has a better idea of how to use them for this, it would be appreciated) The materialized view would be

CREATE MATERIALIZED VIEW BOM_INDENTED_MV
(PTX_Path,TOPLEVELID, TOPLEVEL, TOPLEVEL_DESC, PRODUCTNUM, PRODDEC, 
 M_PRODUCTBOM_ID, BOM_LEVEL, BOMQTY, DESCRIPTION, AD_CLIENT_ID, 
 AD_ORG_ID, BOM_LEVEL_NUM, BOTTOMCOMPONENT,TOPLEVEL_ISACTIVE, COMP_ISACTIVE,
TOP_Client)
 
 Build IMMEDIATE
 REFRESH ON DEMAND 
 DISABLE QUERY REWRITE
 
AS 
SELECT Path, TOPLEVELID,TOPLEVEL,TOPLEVEL_DESC, ProductNum, ProdDesc,
M_PRODUCTBOM_ID, BOM_Level, BOMQTY, f1.DESCRIPTION,mp2.AD_CLIENT_ID,
 mp2.AD_ORG_ID,
BomLevelNum, BottomComponent, MP.ISACTIVE AS TOPLEV_ISACTIVE, MP2.ISACTIVE AS
COMP_ISACTIVE, mp.AD_CLIENT_ID AS TOPLEVEL_Client
  FROM (
SELECT DISTINCT SYS_CONNECT_BY_PATH (M_PRODUCTBOM_ID,'/') AS Path,
 CONNECT_BY_ROOT
M_ProductBom_ID AS TOPLEVELID,CONNECT_BY_ROOT Value AS TOPLEVEL,CONNECT_BY_ROOT
b.DESCRIPTION AS TOPLEVEL_DESC, Value AS ProductNum, b.DESCRIPTION AS ProdDesc,
M_PRODUCTBOM_ID,LPAD(' ',2*(LEVEL-1)) || TO_CHAR(LEVEL) BOM_Level, BOMQTY,
a.DESCRIPTION,a.AD_CLIENT_ID, a.AD_ORG_ID, LEVEL AS BomLevelNum,
 Connect_By_ISLEAF
AS BottomComponent
  FROM 
  
-- part 2 subquerry combining top level and bom to create null to start from
( SELECT
 "M_PRODUCT_BOM_ID","AD_CLIENT_ID","AD_ORG_ID","ISACTIVE",--"CREATED","C
REATEDBY","UPDATED","UPDATEDBY",  (remove becuase unnnecessarry)
"LINE","M_PRODUCT_ID","M_PRODUCTBOM_ID","BOMQTY","DESCRIPTION","BOMTYPE"
FROM M_PRODUCT_BOM

UNION
-- part 1 the tope level querry creating the nulls that is combined in part 2
SELECT M_PRODUCT_BOM_1.M_PRODUCT_BOM_ID, M_PRODUCT_BOM.AD_CLIENT_ID,
M_PRODUCT_BOM.AD_ORG_ID, M_PRODUCT_BOM_1.ISACTIVE,-- M_PRODUCT_BOM_1.CREATED,
M_PRODUCT_BOM_1.CREATEDBY, M_PRODUCT_BOM_1.UPDATED, M_PRODUCT_BOM_1.UPDATEDBY,
(removed unnecessary)
 M_PRODUCT_BOM_1.LINE,M_PRODUCT_BOM_1.M_PRODUCTBOM_ID AS M_PRODUCT_ID,
M_PRODUCT_BOM.M_PRODUCT_ID AS M_PRODUCTBOM_ID,  M_PRODUCT_BOM_1.BOMQTY,
M_PRODUCT_BOM_1.DESCRIPTION, M_PRODUCT_BOM_1.BOMTYPE
FROM M_PRODUCT_BOM LEFT JOIN M_PRODUCT_BOM  M_PRODUCT_BOM_1 ON
M_PRODUCT_BOM.M_PRODUCT_ID = M_PRODUCT_BOM_1.M_PRODUCTBOM_ID
GROUP BY M_PRODUCT_BOM_1.M_PRODUCT_BOM_ID, M_PRODUCT_BOM.AD_CLIENT_ID,
M_PRODUCT_BOM.AD_ORG_ID, M_PRODUCT_BOM_1.ISACTIVE, M_PRODUCT_BOM_1.CREATED,
M_PRODUCT_BOM_1.CREATEDBY, M_PRODUCT_BOM_1.UPDATED, M_PRODUCT_BOM_1.UPDATEDBY,
M_PRODUCT_BOM_1.LINE, M_PRODUCT_BOM.M_PRODUCT_ID,
 M_PRODUCT_BOM_1.M_PRODUCTBOM_ID,
M_PRODUCT_BOM_1.BOMQTY, M_PRODUCT_BOM_1.DESCRIPTION, M_PRODUCT_BOM_1.BOMTYPE
HAVING (((M_PRODUCT_BOM_1.M_PRODUCTBOM_ID) IS NULL))) a
-- part 3 continued
   INNER JOIN M_PRODUCT b
   ON a.M_PRODUCTBOM_ID = b.M_PRODUCT_ID
  START WITH a.M_PRODUCT_ID IS NULL
  CONNECT BY PRIOR  M_PRODUCTBOM_ID = a.M_PRODUCT_ID
  ORDER SIBLINGS BY b.Value
) f1
INNER JOIN M_PRODUCT mp ON TOPLEVELID=mp.M_PRODUCT_ID
INNER JOIN M_PRODUCT mp2 ON M_PRODUCTBOM_ID = mp2.M_PRODUCT_ID
ORDER BY Path
/

Then to refresh the materialize view we used the following database procedure where we used a parameter in the "report process" window to identify which MV to refresh:

CREATE OR REPLACE PROCEDURE Indented_Bom_Mv_Refresh
(
	PInstance_ID    		IN NUMBER	
)
AS
--	Logistice
	ResultStr						VARCHAR2(2000);
	Message							VARCHAR2(2000);
	Record_ID						NUMBER;
	--	Parameter
	CURSOR Cur_Parameter (PInstance NUMBER) IS
		SELECT i.Record_ID, p.ParameterName, p.P_String, p.P_Number, p.P_Date
		FROM AD_PINSTANCE i, AD_PINSTANCE_PARA p
		WHERE i.AD_PInstance_ID=PInstance
		AND i.AD_PInstance_ID=p.AD_PInstance_ID(+)
		ORDER BY p.SeqNo;
	--	Parameter Variables
	p_MVIEW						VARCHAR2(2000);


BEGIN
	DBMS_OUTPUT.PUT_LINE('Updating PInstance - Processing ' || PInstance_ID);
    ResultStr := 'PInstanceNotFound';
    UPDATE AD_PINSTANCE
    SET Created = SYSDATE,
        IsProcessing = 'Y'
    WHERE AD_PInstance_ID=PInstance_ID;
    COMMIT;
--	Get Parameters
	ResultStr := 'ReadingParameters';
	FOR p IN Cur_Parameter (PInstance_ID) LOOP
		Record_ID := p.Record_ID;
		IF (p.ParameterName = 'MVIEW') THEN
 			P_PTX_MVIEW := p.P_String;
			DBMS_OUTPUT.PUT_LINE('  MVIEW=' || p_MVIEW);
		ELSE
			DBMS_OUTPUT.PUT_LINE('*** Unknown Parameter=' || p.ParameterName);
	 	END IF;
	END LOOP;	--	Get Parameter
	DBMS_OUTPUT.PUT_LINE('  Record_ID=' || Record_ID);

		
DBMS_MVIEW.REFRESH (p_PTX_MVIEW, 'c');


<<FINISH_PROCESS>>
	--  Update AD_PInstance
	DBMS_OUTPUT.PUT_LINE('Updating PInstance - Finished ' || Message);
    UPDATE  AD_PINSTANCE
    SET Updated = SYSDATE,
        IsProcessing = 'N',
        Result = 1,                 -- success
        ErrorMsg = Message
    WHERE   AD_PInstance_ID=PInstance_ID;
    COMMIT;
    RETURN;

EXCEPTION
    WHEN  OTHERS THEN
		ResultStr := ResultStr || ': ' || SQLERRM || ' - ' || Message;
		DBMS_OUTPUT.PUT_LINE(ResultStr);
        UPDATE  AD_PINSTANCE
        SET Updated = SYSDATE,
            IsProcessing = 'N',
            Result = 0,             -- failure
            ErrorMsg = ResultStr
        WHERE   AD_PInstance_ID=PInstance_ID;
        COMMIT;
        RETURN;
END ;
/


User authentication in Adempiere using LDAP

Submitted By: gemmiti

Summary: User authentication in Adempiere using LDAP

sf.net tracker: here

Enabling LDAP Functionality:

1. Log in as SysAdmin 2. Go to Menu>System Admin>System 3. In Field "Ldap URL" fill in your LDAP URL, i.e. "LDAP://YourLdapServer.com" 4. In field "LDAP Domain" Your Domain, i.e., "YourLdapServer.com" 5. Log Out as SysAdmin 6. Log In as SuperUser/Admin 7. GoTo Menu>General Rules>Security>User 8. In tab "User Contact" and "Internal" you will find a field for LDAP User Name -- Here (for a particular user) fill in the LDAP use name.

Upon logging in -- the user can enter his/her LDAP user name and password and it will associate the correct credentials to the user you have set up in Compiere.


Add OnHandQty In Product window

Submitted By: stingreye

Summary: Add OnHandQty In Product window

sf.net tracker: here

First the SQL 
1)	You want On Hand Qty -  Product can be stored in multiple locators in M_Storage table. I assume you want total of all locators
2)	So SQL = SELECT SUM (QTYONHAND) FROM M_STORAGE s
3)	Now you want to sync with product table,  so  we add WHERE s.M_PRODUCT_ID = M_PRODUCT.M_PRODUCT_ID
4)	So the final SQL we need is (SELECT SUM(QTYONHAND) FROM M_STORAGE s WHERE s.M_PRODUCT_ID = M_PRODUCT.M_PRODUCT_ID)

Now lets add it to compiere:
1)	Log in as SuperUser or System
2)	Role = System Administrator
3)	Open Table window
4)	Select Column tab
5)	Create new record
6)	Insert a  name for DB Column Name 
7)	Choose a system element in this case most likely Qty_OnHand
8)	Reference = String
9)	Length = 20 should be plenty
10)	Click Save
11)	Click synchronize column, cross fingers
12)	Look on bottom left of window for alter table M_PRODUCTADD name of the field you just made

Lets add it to the M_Product window.

1)	Go to Window Tab Field
2)	Choose Product Table
3)	Goto Tab 
4)	Choose the “tab” we want which is Product (it is for the M_PRODUCT table)
5)	Choose Field Tab
6)	New Record
7)	Name your Field (Qty on Hand)
8)	Column – Choose the new column you created.
9)	Save

Now open the Product window and it should work!

Workflow Simple approval

Workflow Dynamic approval

Changelog functionality

changelog functionality consists of two parts: who & what.

  • Via tables user can identify "what" wish to log.
  • Via roles "who".

By this way is possible to finely control the auditing. This means more tables to be flagged if user wish to log all tables.

Developer hints

  • class dbPort/org.compiere.process.DocActionTemplate shows a template for DocAction, i think that it must be updated.

Hint-1; UPDATE columns which must have default value or have checked constraint

When developer add new column which is NOT NULL or which has checked constrint, please do not forget to execute proper UPDATE TABLE sql statemnts.

Example:

ALTER TABLE EXP_FormatLine
ADD IsPartUniqueIndex CHAR(1) CHECK(IsPartUniqueIndex IN ('Y', 'N'));

UPDATE EXP_FormatLine
SET IsPartUniqueIndex = 'N' 
WHERE IsPartUniqueIndex IS NULL;

Hint-2; Be carefull when creating VIEWS

Use LEFT/RIGHT OUTER JOINS, because in many cases fields do not have values and INNER JOIN will not show all records. In this example C_Invoice.C_Order_ID is not manadatory so view will not show records which do not have Order associated with Invoice!

FROM C_Invoice i 
    INNER JOIN C_DocType d ON (i.C_DocType_ID=d.C_DocType_ID) 
    LEFT OUTER JOIN C_Order o ON (i.C_Order_ID=o.C_Order_ID) 

Hint-2; Java on Ubuntu

Ubuntu came with default version of java 1.4.2 and installing 1.5.0 didn't change that. So developer have to change it. Exporting the path to your newer java to the .bashrc config file of your environment is a standadrd decision but is not system wide and in some cases is not enough. The decision is. When you type at the prompt:

  $ sudo update-java-alternatives -l

will see the versions of all javas installed into your system. In order to make the latest one default for the whole system type:

  $ sudo update-java-alternatives -s java-newer-ver 

Subversion Access

Adempiere project's SourceForge.net Subversion repository can be checked out through SVN with the following instruction set: svn co https://adempiere.svn.sourceforge.net/svnroot/adempiere adempiere

MFG

sf.net forum post

Important Steps:

1. Apply the following patches: 
 
buildbase.patch.tim 
buildclient.patch.tim 
buildutils_dev.patch.tim 
X_AD_WF_Node.patch 
X_C_DocType.patch 
X_S_Resource.patch 
X_AD_Workflow.patch 
X_M_Cost.patch 
X_M_Transaction.patch 
X_M_Product.patch 
 
2. I copied adempiere/libero/build.xml.tim to adempiere/libero/build.xml 
 
Build your Adempiere and run it.  

3. Create a new client and import accounting etc... 
(If you don't do this you won't see all the menus, you can't just login under GardenAdmin/GardenAdmin that is not enough) 
It looks that 2pack file do not contains proper access records.
Must research!!!

4. Login with your new client Admin login you created in step3 and Libero away!!!


Template Process

package org.adempiere.process;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;

import org.compiere.model.MClient;
import org.compiere.model.MDocType;
import org.compiere.model.X_C_BPartner_EDI;
import org.compiere.model.X_C_EDIFormat;
import org.compiere.model.X_C_EDIProcessor;
import org.compiere.process.ProcessInfoParameter;
import org.compiere.process.SvrProcess;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Msg;

/**
 *  @author Trifon Trifonov
 *  @version $Id:$
 */
public class ModelImporter extends SvrProcess
{
	/** Client Parameter			*/
	protected int	p_AD_Client_ID = 0;
	
	/** Business Partner Parameter */
	protected int p_C_BPartner_ID = 0;
	
	/** Document Type Parameter */
	protected int p_C_DocType_ID = 0;
	
	/** Record ID */
	protected int p_Record_ID = 0;
	
	/** Table ID */
	int AD_Table_ID = 0;
	
	/**
	 * 	Get Parameters
	 */
	protected void prepare ()
	{
		
		p_Record_ID = getRecord_ID();
		if (p_AD_Client_ID == 0)
			p_AD_Client_ID = Env.getAD_Client_ID(getCtx());
		AD_Table_ID = getTable_ID();
		
		StringBuffer sb = new StringBuffer ("AD_Table_ID=").append(AD_Table_ID);
		sb.append("; Record_ID=").append(getRecord_ID());
		//	Parameter
		ProcessInfoParameter[] para = getParameter();
		for (int i = 0; i < para.length; i++)
		{
			String name = para[i].getParameterName();
			if (para[i].getParameter() == null)
				;
			else if (name.equals("C_BPartner_ID"))
				p_C_BPartner_ID = para[i].getParameterAsInt();
			else
				log.log(Level.SEVERE, "Unknown Parameter: " + name);
		}
		
		log.info(sb.toString());
	}

	/**
	 * 	Process 
	 *	@return info
	 */
	protected String doIt () throws Exception
	{
		StringBuffer result = new StringBuffer("");
		
		MClient client = MClient.get (getCtx(), p_AD_Client_ID);
		log.info(client.toString());
		// get proper EDI Format from Document Type
		MDocType docType = MDocType.get(getCtx(), p_C_DocType_ID);
		
		int C_EDIFormat_ID = 0;
		int C_EDIProcessor_ID = 0;
		String sql1 = "SELECT C_EDIFormat_ID, C_EDIProcessor_ID "
			   + "FROM " + X_C_BPartner_EDI.Table_Name + " "
			   + "WHERE AD_Client_ID = ? "
			   + " AND C_BPartner_ID = ? "
			   + " AND C_DocType_ID = ? "
			   + " AND Inbound = 'N' " 
		;
		ResultSet rs1 = null;
		PreparedStatement pstmt1 = null;
		try
		{
			pstmt1 = DB.prepareStatement(sql1, get_TrxName());
			pstmt1.setInt(1, p_AD_Client_ID);
			pstmt1.setInt(2, p_C_BPartner_ID);
			pstmt1.setInt(3, p_C_DocType_ID);
			rs1 = pstmt1.executeQuery();
			if (rs1.next())
			{
				// Found specific C_EDIFormat for given BPartner and C_DocType_ID
				C_EDIFormat_ID = rs1.getInt(X_C_EDIFormat.COLUMNNAME_C_EDIFormat_ID);
				C_EDIProcessor_ID = rs1.getInt(X_C_EDIProcessor.COLUMNNAME_C_EDIProcessor_ID);
			} else {
				// Get C_EDIFormat_ID from Document Type
				C_EDIFormat_ID = docType.getC_EDIFormat_ID();
			}
			
		} finally {
			try {
				if (rs1 != null) rs1.close();
				if (pstmt1 != null) pstmt1.close();
			} catch (SQLException ex) {/*ignored*/}
			rs1 = null;
			pstmt1 = null;
		}
		if (C_EDIFormat_ID == 0) {
			throw new Exception("EDI Format is not set for C_DocType_ID = [" + p_C_DocType_ID + "]");
		}
		if (C_EDIProcessor_ID == 0) {
			throw new Exception("EDI Processor is not set for C_DocType_ID = [" + p_C_DocType_ID + "]");
		}
		
		addLog(0, null, null, Msg.getMsg (getCtx(), "EDISubmitProcessResult") + "\n" + result.toString());
		return result.toString();
	}
}

Migration with SQL statements - AD_Element

INSERT INTO AD_Element
(AD_Element_ID, AD_Client_ID, AD_Org_ID, IsActive,
Created, CreatedBy,
Cpdated, UpdatedBy,
ColumnName, EntityType, Name,
PrintName
)
VALUES (500XX, 0, 0, 'Y',
TO_DATE ('02/26/2007 12:30:00', 'MM/DD/YYYY HH24:MI:SS'), 100,
TO_DATE ('02/26/2007 12:30:00', 'MM/DD/YYYY HH24:MI:SS'), 100,
'[ColumnNameHere]', 'D', '[NameHere]',
'[PrintNameHere]'
);
COMMIT;
UPDATE AD_Sequence
SET CurrentNextSys = (SELECT MAX (AD_Element_ID) + 1
FROM AD_Element
WHERE AD_Element_ID < 1000000)
WHERE NAME = 'AD_Element';

Migration with SQL statements - AD_Column

INSERT INTO AD_Column
(AD_Column_ID, AD_Client_ID, AD_Org_ID, IsActive,
Created,
Updated, CreatedBy,
UpdatedBy, Name, Description, VERSION,
EntityType, ColumnName, AD_Table_ID, AD_Reference_ID, 
FieldLength, IsKey, IsParent, IsMandatory, IsUpdateable,
IsIdentifier, SeqNo, IsTranslated, IsEncrypted,
isselectioncolumn, ad_element_id, callout, issyncdatabase,
isalwaysupdateable
)
VALUES (502XX, 0, 0, 'Y',
TO_DATE ('02/26/2007 12:30:00', 'MM/DD/YYYY HH24:MI:SS'),
TO_DATE ('02/26/2007 12:30:00', 'MM/DD/YYYY HH24:MI:SS'), 100,
100, '[NameHere]', '[DescriptionHere]', 1,
'D', '[ColumnNameHere]', AD_Table_IDHERE, AD_Reference_IDHere,
1, 'N', 'N', 'Y', 'Y',
'N', 0, 'N', 'N',
'N', 50071, '[CalloutHere]', 'N',
'N'
);
COMMIT;
UPDATE AD_Sequence
SET CurrentNextSys = (SELECT MAX (ad_column_id) + 1
FROM AD_Column
WHERE AD_Column_ID < 1000000)
WHERE NAME = 'AD_Column';

Migration with SQL statements - AD_Field

INSERT INTO AD_Field
(ad_field_ID, AD_Client_ID, AD_Org_ID, IsActive,
Created, CreatedBy,
Updated, UpdatedBy,
Name, Description, IsCentrallyMaintained, SeqNo, AD_Tab_ID,
AD_Column_ID, IsDisplayed, DisplayLength, IsReadonly,
IsSameLine, IsHeading, IsFieldOnly, IsEncrypted, EntityType
)
VALUES (50184, 0, 0, 'Y',
TO_DATE ('02/26/2007 12:30:00', 'MM/DD/YYYY HH24:MI:SS'), 100,
TO_DATE ('02/26/2007 12:30:00', 'MM/DD/YYYY HH24:MI:SS'), 100,
'Store Archive On File System', 'Store Archive On File System', 'Y', 250, 145,
50214, 'Y', 1, 'N',
'N', 'N', 'N', 'N', 'D'
);
COMMIT;
UPDATE AD_Sequence
SET currentnextsys = (SELECT MAX (ad_field_id) + 1
FROM AD_Field
WHERE AD_Field_ID < 1000000)
WHERE Name = 'AD_Field';

Migration with SQL statements - AD_Message

INSERT INTO AD_Message
(AD_Message_ID, AD_Client_ID, AD_Org_ID, IsActive,
Created, CreatedBy,
Updated, UpdatedBy,
Value, MsgText, MsgType
)
VALUES (500XX, 0, 0, 'Y',
TO_DATE ('02/26/2007 12:30:00', 'MM/DD/YYYY HH24:MI:SS'), 100,
TO_DATE ('02/26/2007 12:30:00', 'MM/DD/YYYY HH24:MI:SS'), 100,
'[VaueHere]','[MSG Text Here]','I'
);
COMMIT;
UPDATE AD_Sequence
SET CurrentNextSys = (SELECT MAX (AD_Message_ID) + 1
FROM AD_Message
WHERE AD_message_ID < 1000000)
WHERE NAME = 'AD_Message';

Migration with SQL statements - AD_Process_Para

INSERT INTO AD_Process_Para
(AD_Process_Para_ID, AD_Client_ID, AD_Org_ID, IsActive, Created,
CreatedBy, Updated, UpdatedBy, Name,
Description,
Help,
AD_Process_ID, SeqNo, AD_Reference_ID, AD_Reference_Value_ID,
AD_Val_Rule_ID, ColumnName, IsCentrallyMaintained, FieldLength,
IsMandatory, IsRange, AD_Element_ID, EntityType
)
VALUES (50019, 0, 0, 'Y', TO_DATE ('2007-03-03', 'YYYY-MM-DD'),
100, TO_DATE ('2007-03-03', 'YYYY-MM-DD'), 100, 'Shipment Date',
'Date printed on shipment',
'The Shipment Date indicates the date printed on the shipment.',
118, 15, 15, NULL,
NULL, 'MovementDate', 'N', 0,
'Y', 'N', 1037, 'D'
);
COMMIT;
UPDATE AD_SEQUENCE
SET currentnextsys = (SELECT MAX (ad_process_para_id) + 1
FROM AD_Process_Para
WHERE AD_Process_Para_ID < 1000000)
WHERE NAME = 'AD_Process_Para';

License to use for new classes

/**********************************************************************
* This file is part of Adempiere ERP Bazaar                           *
* http://www.adempiere.org                                            *
*                                                                     *
* Copyright (C) Trifon Trifonov.                                      *
* Copyright (C) Contributors                                          *
*                                                                     *
* This program is free software; you can redistribute it and/or       *
* modify it under the terms of the GNU General Public License         *
* as published by the Free Software Foundation; either version 2      *
* of the License, or (at your option) any later version.              *
*                                                                     *
* This program is distributed in the hope that it will be useful,     *
* but WITHOUT ANY WARRANTY; without even the implied warranty of      *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the        *
* GNU General Public License for more details.                        *
*                                                                     *
* You should have received a copy of the GNU General Public License   *
* along with this program; if not, write to the Free Software         *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,          *
* MA 02110-1301, USA.                                                 *
*                                                                     *
* Contributors:                                                       *
* - Trifon Trifonov (trifonnt@users.sourceforge.net)                  *
*                                                                     *
* Sponsors:                                                           *
* - Company (http://www.site.com)                                     *
***********************************************************************/
...
...
/**
 *	@author Trifon Trifonov
 *	@version $Id$
 */

Adempiere Export/Import Tools

  • ADCK can be used in two modes.
    • As Development tool. To model DB design and create/modify tables.

As Development tool ADCK uses Druid which allows developer to generate CREATE TABLE and ALTER table SQL statement for various DBs: Oracle, Postgres, MySQL, ... As Development tool ADCK can generate xml files for various tools and frameworks like: hibernate, torque.

    • As transfer tool for only migrating AD changes from Adempiere instance to instance or from version to version.

As transfer tool ADCK require initial just setting of proper settings in properties files.

2Pack vs. ADCK comparison table.

This table is made with the sole purpose to help me to organize information regarding import/export tools in Adempiere. I do not have any idea to push any of the tools mentiod here. It is user's choice to use or not to use any of them and also users choice to have headache after thier usage.

WARNING: !!!MAKE A BACKUP BEFORE ANY USAGE OF ANY OF THE TOOLS!!!

2PackADCK
menumenu
windowwindow
tabtab
fieldfield
processprocess
processparaprocesspara
tabletable
columncolumn
impformatAD_ImpFormat
impformatrowAD_ImpFormat_Row
printformatAD_PrintFormat
printformatitemAD_PrintFormatItem
reference[[ADCK |]]
referencelist[[ADCK |]]
referencetable[[ADCK |]]
reportview[[ADCK |]]
reportviewcol[[ADCK |]]
task[[ADCK |]]
form[[ADCK |]]
workbench[[ADCK |]]
preference[[ADCK |]]
roleAD_Role
???AD_User
userroleAD_User_Roles
orgroleAD_Role_OrgAccess
windowaccessAD_Window_Access
processaccessAD_Process_Access
formaccessAD_Form_Access
workflowaccessAD_Workflow_Access
taskaccess[[ADCK |]]
???AD_Column_Access
???AD_Language
???AD_Message
???AD_PrintTableFormat
???AD_Scheduler
???C_BP_Group
???M_Product_Category
???PA_ReportColumnSet
???PA_ReportColumn
???PA_Report
???PA_ReportLineSet
???PA_ReportLine
???PA_ReportSource
???AD_Client
2Pack Any TableADCK Any Table
???TEMPLATE

Weak sides of 2pack according me

  • Require integration at source code level with Adempiere, which means that if i need to customize any import functionality i must rebuild whole application or patch it.
    • Carlos pointed that 2Pack can import records into any table, even customized.
    • My comment is regarding main xml elements like: menu, window, tab, field, process, ... First time when i understood that i need to modify main xml elements was for JasperReports integration when i had to add new column into AD_Process table.
    • I think that GenericPO which is responsible to import records into Any Table had one issue (I have to remember the exact reason. It was regarding Sequences or something related. ).
    • Format of general imports is complicated.

  • Main class(org.adempiere.pipo.PackInHandler) which handle import is very big(3800 lines) and complex, which require lot of time for developer to study it and makes modifications hard task.
    • Since version 3.3.0 Low Heng Sin refactored org.adempiere.pipo.PackInHandler and now it is split into multiple classes.
  • 2Pack uses SAX Parser which makes developer job harder as developer can't look forward or backward in XML tree.

Weak sides of ADCK according me

  • It is external program, so user do not have such detailed control over security.
  • It is harder for developer to work with it as do not have automatic changes extraction like 2Pack.

Ideas for extensions

Computerized Maintenance Management System

sf.net post CMMS is Computerized Maintenance Management System that designed specially for preventive maintenance for assets such as light vehicle, heavy equipment etc. so that company can create budgeting for repair and maintenance for their assets. Technicaly the module give alert to user when an asset have to get action such as replace fuel filter, oil filter or other item spareparts. Therefore these module related with inventory (internal use). Inventory will decrease its stock when user CMMS request for repair and maintenance their an asset.


Additional fields in I_Xxx tables

sf.net forum post

By: Carlos (globalqss)

Hi, every time I customize tables for a customer (almost all projects) I need to change the importers and/or make some tricks with not used fields on import. [Struggling today with this case]

Idea!

Not tested, but I think I could make a ModelValidator on udpate of I_ table (i.e. I_BPartner) and process the corresponding fields.

The other thing we could do is to add some free fields to every I_ table as wildcards to save customized or non considered fields and process them properly with the ModelValidator. Maybe just adding i.e. 10 fields FreeField01 through FreeField10 on the tables can make the trick.

Recent Adempiere changes

Add more query method to MTable

Date: 2007-08-21

MTable has new method:

	public Query createQuery(String whereClause, String trxName) 
	{
		return new Query(this, whereClause, trxName);
	}

Jar file in 2pack/lib will be packed into Adempiere.jar

Starting from revision 3261 of trunk, any jar file that is pack of the lib folder of a 2pack package will be packed into the Adempiere.jar by the run_setup process. What this mean is, you just need to compile all libero specific code, pack it in a jar file and place it under the lib folder of the libero 2pack archive. Please look at the latest FAPack006.zip in the svn packages folder for an example.

CRM systems

Comparison of CRM systems

Localized in Bulgarian

Interesting news/Forum post regarding CRM systems

ERP Systems

Tina POS

Weak sides

Compiere

My opinion is that this is very outdated information, but who knows...

Open Compiere

HTML/CSS



Java related

Frameworks

WidgetServer is a component based, server-side, Java/XML rich-client-framework which enables an application to run as either

- an application with a rich Web client based on AJAX (HTML, XML, CSS, Javascript)
- an application with a simple Web client based on HTML and CSS
- a standalone application with a Swing GUI,
- a client/server application with a thin Swing client,
- or a mobile application on different devices like PDAs or Smartphones


Java and IM

Java Content Repository API

Autocompletion

Portal, JCR, ECM, Groupware

Document Management Systems

Content Management Systems

Eclipse IDE and Application build on Eclipse

Eclipse Easy Shell plugin

gnome-terminal --working-directory {1}
  • Seetings for windows
cmd.exe /C start /D{1} cmd.exe /K "rxvt.exe -fn "Console" -vb +sb -sl 1000 -e bash -I"

Script Languages

Eclipse plugin

Java FTP Libraries and Articles

Commercial which support FTPS (FTP Over SSL)

OpenXava Developer hints

Hint.1 Always use different properties names

Let's have component Organization

<component name="Organization">
	<entity>
		<property name="orgId" type="String" key="true" hidden="true" >
			<default-value-calculator class="org.openxava.calculators.UUIDCalculator" on-create="true" />
		</property>
		<property name="orgName" type="String" size="30" required="false"/>
...
</component>

and component Warehouse

<component name="Warehouse">
	<entity>
		<property name="warehouseId" type="String" key="true" hidden="true" >
			<default-value-calculator class="org.openxava.calculators.UUIDCalculator" on-create="true" />
		</property>
		<property name="warehouseName" type="String" size="30" required="false"/>
...
</component>

by this way in Application_en.properties developer can define different names for properties:

organizationId=Organization ID
orgName=Organization Name
warehouseId=Warehouse ID
warehouseName=Warehouse Name

also it helps developer to understand well which exactly property he uses/modify.


OpenXava Q & A

Created an entity ticketClass with a method GenerateTickets. Implemented the calculator using an IModelCalculator class.

Want to create a button/link when ticketClass is displayed so that user click on it the method gets executed.

Q.1 How do I set this up?

  • A1. Have to create an action and include it in the controller of your module.

In this action, you can obtain the object and call to you method, something like this:

public void execute() throws Exception {
  ...
  TicketClass ticketClass = (TicketClass) MapFacade.findEntity(getModelName(), getView().getKeyValues());
  ticketClass.generateTickets();
  getView().findObject(); // This refresh the view (Q2)
  ...
}

Q.2 How can I get the view of the entity refreshed after the method is executed.

  • A2. See the above code

Q.3 How can i reload other modules to reflect the changes made by an action that was performed in one module?

An action in one module (module A) that changes the value of the data contained in another module (module B), when i execute an action button (in module A), it executes successfully and updates the data in the other entity (entity B) in the database, but when i view module B, the module list view (of module B) is still showing the old data!

  • A3. In a Liferay 2.1.3 with OX2.2 it works fine, that is, I modify data in a portlet, I move to another and the data

is refreshed without touch filter nor any other action. Look at your web/WEB-INF/portlet.xml, have you <expiration-cache>0</expiration-cache> for your portlets? Since OX2.1 expiration-cache is 0 in the portlet.xml generated by OpenXava. Hence, if you are using OX2.1 or better you only need to execute the ant target 'redeployPortlets' in order to fix your portlet.xml file.


Q.4 How to avoid warnings that messages are not translated

  • A4.

This messages warn you about the labels that you need to put in your i18n/YourProyectName-labels_en.properties. Developer can avoid this messages putting the next line:

i18nWarnings=false

in the file properties/xava.properties of your project.

Q.5 Example TestCase

public class ExampleTest extends ModuleTestBase { 
 
  public DepartmentsTest(String testName) { 
  
  super(testName,"ExampleManager", "ExampleModlue");
  } 
  
  public void testCreateReadUpdateDelete() throws Exception { 
  
    //Test Create
    execute("CRUD.new");
    setValue("id", "ENG");
    setValue("name", "Example Name");
    setValue("description", "Example description");
    assertNoErrors();
  } 
}

Q.6 How to provide a web API for accessing the OpenXava application?

Just use Web Services. The OpenXava applications are standard Java Web applications, therefore you can use any Java API from your OpenXava application.

In this case you can create a simple Java class, with the services of you application that you want to expose. And use from it MapFacade or the model objects generated by OpenXava, or whatever code of your application you want. Something like this:

public class MyService {

  public Address getCustomerAddress(String customerId) throws Exception {
    // Here you use the POJOs generated by OpenXava
    Customer customer = (Customer) Customer.findById(customerId);
    return customer.getAddress();
  }
}

Now you can create a Web Service from MyService class using AXIS, or the wizard included in Eclipse WTP. But, remember, all this is a Java issue, not an OpenXava one.

Q.7 Magic numbers

It is possible to define some magic numbers in OX? The Environment Variables are not enough. I want to use the magic numbers in components.

  • A7. Use standard techniques for Java and XML.

In Java you can use "public final static" variables:

public class MyMagicNumbers {
  public final static int MY_MAGIC = 24324;
}

In XML you can use XML ENTITIES:

<!ENTITY mymagic "24324">

and then use &mymagic; instead of 34324.

These techniques are not from OpenXava but from Java and XML.

In XML you can use @mymagic@, and the filter of OpenXava ant build changes it from a value of your properties file. This allows you to have a different value of @mymagic@ for each customer or configuration. This is a technique from OpenXava.

Q.8 How to access EJB from OpenXava

  • Reference Guide section 3.14.
  • There is a example of using a EJB inside validator.
  • Developer can access EJB code in any part of OpenXava applications, for example:
Delivery delivery = DeliveryUtil.getHome().findByNumber(33);
delivery.generateInvoice();

Q.9 Access rights depending on the user profile

  • sf.net thread
  • User should only see fields or to execute actions depending on his user profile (or role).
  • Certain users to be able to update certain fields while others may only read them.
1. Option - provided by the container portal
  • Create two groups of users:
    • First with create-delete-edit rights.
    • Second only with read rights.
  • Define two modules:
    • First with standard CRUD controller.
    • Second with Print controller.

The admin may asign First or Second module (portlet) for each group of users with the portal administrative tools.

2. Option

Not completly implemented in OpenXava, but there are some useful tools in OX that can help:

  • Users.getCurrent() return the current user if application is started in portal. This value may be used in any point of the code.
  • Filters: filters the data displayed in the mode list according to the current user.

A full management of users in OX project implies creation of several modules for storing the info about users, interfaces for change rights and create new users, roles and rights, overwrite standard controllers, to write new actions (tipically "save" action, frequently "new" action too), to write specific validators, to write filters for tabs in list mode, and more...

Javier's opinion: I think that part of this stuff can be moved to a new project, instead of be included in OX core. This new project would manage access rights and user profiles, and will be reusable for other projects.

Links

Q.10 Property displayed as Radio button

  • Existing test case: CustomerWithRadioButtons
  • Section 4.7 of reference guide shows how editors are full configurable in OpenXava.
  • /components/SomeComponent.xml file
<entity>
  <property name="gender" >
	<valid-values>
		<valid-value value="Male"/>
		<valid-value value="Female"/>
	</valid-values>
  </property>
</entity>
<view>
  <property-view property="gender" editor="ValidValuesRadioButton"></property-view>
  <members >
    name;
    gender;
  </members >
<view>
  • OpenXava/xava/default-editors.xml contains definition of:
    • ValidValuesRadioButton
<editor name="ValidValuesRadioButton" url="radioButtonEditor.jsp">
	<property name="horizontal" value="true" />
</editor>
    • ValidValuesVerticalRadioButton
<editor name="ValidValuesVerticalRadioButton"
	url="radioButtonEditor.jsp">
	<property name="horizontal" value="true" />
</editor>
    • ValidValuesHorizontalRadioButton
<editor name="ValidValuesHorizontalRadioButton"
	url="radioButtonEditor.jsp">
	<property name="horizontal" value="true" />
</editor>

It looks both last have the same value for horizontal request parameter <property name="horizontal" value="true" />. Probably this is a mistake.

Q.11 OpenXava design

  • All model classes implement org.openxava.model.IModel interface:
/**
 * Interface to be implemented by all model classes. <p>
 * 
 * The model classes may be EntityBeans EJB 2 or POJOs (for JDO, EJB3 or Hibernate).
 * 
 * @author Javier Paniza
 */
public interface IModel {
	
	/**
	 * Returns metadata about object. <p>
	 * 
	 * @return  Not null.
	 * @exception XavaException  Any problem related to OpenXava.
	 * @exception RemoteException  System problem.
	 */
	MetaModel getMetaModel() throws XavaException, RemoteException;

}

Q.12 How to get logger in class

OpenXava uses Apache Commons Loggin

private static Log log = LogFactory.getLog(Users.class);

Q.13 Using the <transient> tag

Section 4.10 of Reference guide says:

"put <transient/> at the end of the component definition (1), just in the part for the mappings"

Make sure you write the following:


<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE component SYSTEM "dtds/component.dtd">
<component name="YourComponent">
     <entity>
         ...
     </entity>
     ...
     <transient/>   <!--If you put it anywhere else you will see error in the
document-->
</component>

Q.14 Search criteria customization in List mode

Requirement: The user writes his search criteria (Type employee salary for example) and then presses the search button.

Create a module that start in detail mode. Use a view that show just the data for filtering.

Define a controller with your "Search" actions, and in this action change to list mode.

In this way you can achieve your goal.

Read the chapters 7 and 8 of Reference Guide.


Q.15 How to change Action view from link to button?

1. Create a new property called 'buttonsForNoImageActions' (or so), for use in xava.properties.

2. Add the property in the class XavaPreferences.

3. Modify the class ActionTag.

By default, buttonsForNoImageActions must be 'false', in order to not change the current OX behavior (by default).

Q.16 How to open/goto custom jsp?

DECLARATIVE jsp navigation, as following:

<action name="goToMyJSP" class="org.openxava.actions.NavigationAction">				
  <set property="customView" value="mypage.jsp"/>
  <set property="nextController" value="MyController"/>
</action>				

In general, action classes can be made reusable, and can be configured in controllers.xml. In this way can do more configuration and less programming.

Q.17 How to make a property as a NOT persistent field in the entity element?

Yes, it's possible.

Two options:

1. Calculated properties (section 3.8.4 of reference guide)

2. View properties (section 4.5 of reference guide)

Q.18 How to implement Wizard form

First consider seriously to use a single view with several sections. This is more flexible in most cases and easy to implement with OpenXava.

Steps: Your action for navigate must implements IChangeControllersAction, this allows you to change the actions to show in each page, and using

getView().setViewName( ... );

inside execute() of your action, you can change the view in each step.

Look at the Reference Guide and OpenXavaTest for IChangeControllersAction, and also for INavigationAction.


Q.19 How to forward to a JSP

Action obtains two values which are important for the JSP.

Create a IForwardAction with a code like:

 
public String getForwardURI() { 
    return "/mypage.jsp?a=" + getA() + "&b=" + getB(); 
}

And put mypage.jsp in the 'web' folder of your project.

Q.20 Change order of error messages

Method 'validate' of MapFacadeBean:

private void validate(Messages errors, MetaModel metaModel, Map values, Map
keyValues, Object containerKey, boolean creating)
		throws XavaException, RemoteException {		
		Iterator it = values.entrySet().iterator();		
		while (it.hasNext()) {
			Map.Entry en = (Map.Entry) it.next();
			String name = (String) en.getKey();
			Object value = en.getValue();
			validate(errors, metaModel, name, value, creating);
		}
		if (metaModel.containsValidadors()) {
			validateWithModelValidator(errors, metaModel, values, keyValues,
 containerKey,
creating);
		}
	}

In this case the loop is over entrySet of a Map, hence the order is unknown. You can modify this method in order to work by the correct order, some method of MetaModel returns the member names in order of declaration.


Q.21 How to disable the icon in the corner most column in column arrangement view?

Modify list.jsp.


Q.22 Is it possible to open custom JSP view in a pop up window?

No. The custom JSP must be inside the module. Use IForwardAction that has a method (inNewWindow()) for create a pop up windows. You can put your JSP in a public folder, and send the parameters for this jsp using OX session objects.


Q.23 How to hide view section depending on user rights?

Some users must be able to see given section from the view while other users must NOT be able to see this section.

At the moment developer can hide (using View.setHidden) members and groups, but not sections.

There are 4 (at least) options:

  • Use setSectionEditable
  • Put your sensible data in a group, and hide/show it.
  • Create 2 views (one with the critical section, another without it)
  • Modify OpenXava to allow to hide programatically sections.


  • Which class should change view or hide section?
    • SEARCH_ACTION and 'new' Action are good places.
    • Other options as in an on-each-request action, or an on-init action.


  • Guide how to develop option 4?
    • Look at View.setHidden method.

Q.24 How to display only label in view?

Use a property of the view with stereotype LABEL.

<view>
  <property name="selectLabel" stereotype="LABEL">
    <!--calculator class="org.openxava.calculators.StringCalculator">
	<set property="string" from="Select at least one:"/>
    </calculator-->
  </property>
  <members>
     selectLabel;
     a;
     b;
  </members>
</view>

File Application_en.properties must contain translation of property:

selectLabel=Select at least one:


Another option may be to have a property with no content and 'Select at least one:' as label.

File: xava/editors.xml

<?xml version = "1.0" encoding = "ISO-8859-1"?>
<!DOCTYPE editors SYSTEM "dtds/editors.dtd">
<editors>
	<editor url="void.jsp">
		<for-stereotype stereotype="VOID" />
	</editor>
</editors>

Create empty file: web/xava/editors/void.jsp

Q.25 How to make property required from Action?

Make a property 'X' required depending on the value of other property 'A'. If property 'A' has value 111 than property 'X' must became required.

<property-view property="A">
    <on-change class="bg.d3soft.openxava.actions.OnChangeA"/> 
</property-view>
  • Use a validator for property X (reference guide 3.8.6), and inject in the validator the property A (using <set />).
  • Use a model level validator (section 3.16).

You cannot change the state of a property from 'required' to 'not required' at runtime (well, you can, but you shouldn't), because required is a feature of the model, not of a view. You can access to the MetaModel at runtime and change the value of required, but if you do it in this way the value will be change for all modules, and all users at the same time, because you are modifying the meta data of the model.

Web Services


Mashups

Liferay

I am trying to get user atributes using the JSR-186 specified way.
Here is the extract from portlet.xml:
<user-attribute>
  <name>user.name.given</name>
</user-attribute>
<user-attribute>
  <name>user.name.family</name>
</user-attribute>
<user-attribute>
  <name>user.business-info.postal.street</name>
</user-attribute>
<user-attribute>
  <name>user.business-info.postal.city</name>
</user-attribute>
<user-attribute>
  <name>user.business-info.postal.country</name>
</user-attribute>
<user-attribute>
  <name>user.business-info.postal.organization</name>
</user-attribute>
<user-attribute>
  <name>user.business-info.telecom.telephone.number</name>
</user-attribute>

Here is the code to get user attributes:

HashMap hmUserInfo = (HashMap)request.getAttribute(PortletRequest.USER_INFO);'
log.debug((String)hmUserInfo.get(UserAttributes.USER_NAME_GIVEN));
log.debug(((String)hmUserInfo.get(UserAttributes.USER_NAME_FAMILY));
log.debug((String)hmUserInfo.get(UserAttributes.USER_BUSINESS_INFO_POSTAL_CITY));
log.debug((String)hmUserInfo.get(UserAttributes.USER_BUSINESS_INFO_POSTAL_COUNTRY));
log.debug((String)hmUserInfo.get(UserAttributes.USER_BUSINESS_INFO_POSTAL_ORGANIZATION));
log.debug((String)hmUserInfo.get(UserAttributes.USER_BUSINESS_INFO_POSTAL_STREET));
log.debug((String)hmUserInfo.get(UserAttributes.USER_BUSINESS_INFO_TELECOM_TELEPHONE_NUMBER));

Liferay return first/last names ONLY in spite of the fact that all other information is presented for this user.
I entered it using Organization Admin portlet.


Web Services

XML

Ant related

HTML, CSS

DB Related

Fun

  • Robocode I love this kind of games where user can develop his own creature with AI.

Fork developers work in their free time! Excuse Me???

What Compiere defenders think.

Can you belive? I work for free? Please do not tell it to my wife... This is top secret.


  • sf.net post It looks that this post was deleted from Compiere forums. Compiere Inc. do not like what friends of Compiere think??? Just to prove that such post exist i posted whole post here

Having been in the ERP game for a very long time, I just find the bazaar a bit too risky for real life scenario and most of the clever developers involved are doing out of free time. I honestly dont even have free time to write this email, but I guess some people have more free time than others. So what people do in their free times is entirely up to them? The product is becoming massive yes, and to expect the couple of clever developers to maintain it within their "free" time would be EXTREMELY risky for any enterprise to put reliance on. Yes it may be more clever and even more transparrent and yes many more extremely clever things will be released out of it. Question though is who is going to maintain it in their free time over the next couple of years and who actually needs to get paid to support it.