Groovy is a programming language that runs on a functional programming model that executes commands on the JVM (Java Virtual Machine – Java virtual machine). In order to automate business operations in ERP software, as in Apache Ofbiz, there are a lot of programs written in *.groovy
to perform the business automatically in many steps. The syntax for writing the build.gradle
file is to use the syntax of Apache Groovy.
Development environment
- IntelliJ IDEA 2020.2.3 (Ultimate Edition)
- Windows 10 x64 version 2004 – Enterprise edition
- Groovy 3.0.6
- JDK 1.8
For example: File ofbiz-frameworkapplicationsordergroovyScriptsallocationplanCreateAllocationPlan.groovy
(Apache Ofbiz version 17.12.04, copyright Apache Software Foundation)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | import org.apache.ofbiz.entity.condition.EntityOperator import org.apache.ofbiz.entity.condition.EntityCondition import org.apache.ofbiz.order.order.OrderReadHelper import org.apache.ofbiz.party.party.PartyHelper allocationPlanInfo = itemList = [] isPlanAlreadyExists = false productId = parameters.productId planName = parameters.planName if (productId) { orderedQuantityTotal = 0.0 orderedValueTotal = 0.0 reservedQuantityTotal = 0.0 ecl = EntityCondition.makeCondition([ EntityCondition.makeCondition("productId", EntityOperator.EQUALS, productId), EntityCondition.makeCondition("statusId", EntityOperator.IN, ["ALLOC_PLAN_CREATED", "ALLOC_PLAN_APPROVED"]), EntityCondition.makeCondition("planTypeId", EntityOperator.EQUALS, "SALES_ORD_ALLOCATION")], EntityOperator.AND) allocationPlanHeader = from("AllocationPlanHeader").where(ecl).queryFirst() if (allocationPlanHeader == null) { ecl = EntityCondition.makeCondition([ EntityCondition.makeCondition("productId", EntityOperator.EQUALS, productId), EntityCondition.makeCondition("orderStatusId", EntityOperator.EQUALS, "ORDER_APPROVED"), EntityCondition.makeCondition("orderTypeId", EntityOperator.EQUALS, "SALES_ORDER")], EntityOperator.AND) orderAndItemList = from("OrderHeaderAndItems").where(ecl).queryList() orderAndItemList.each { orderAndItem -> itemMap = salesChannelEnumId = orderAndItem.salesChannelEnumId itemMap.salesChannelEnumId = salesChannelEnumId salesChannel = from("Enumeration").where("enumId", salesChannelEnumId).queryOne() if (salesChannel) { itemMap.salesChannel = salesChannel.description } orh = new OrderReadHelper(delegator, orderAndItem.orderId) placingParty = orh.getPlacingParty() if (placingParty != null) { itemMap.partyId = placingParty.partyId itemMap.partyName = PartyHelper.getPartyName(placingParty) } itemMap.orderId = orderAndItem.orderId itemMap.orderItemSeqId = orderAndItem.orderItemSeqId itemMap.estimatedShipDate = orderAndItem.estimatedShipDate unitPrice = orderAndItem.unitPrice cancelQuantity = orderAndItem.cancelQuantity quantity = orderAndItem.quantity if (cancelQuantity != null) { orderedQuantity = quantity.subtract(cancelQuantity) } else { orderedQuantity = quantity } orderedValue = orderedQuantity.multiply(unitPrice) orderedQuantityTotal = orderedQuantityTotal.add(orderedQuantity) orderedValueTotal = orderedValueTotal.add(orderedValue) itemMap.orderedQuantity = orderedQuantity itemMap.orderedValue = orderedValue // Reserved quantity reservedQuantity = 0.0 reservations = from("OrderItemShipGrpInvRes").where("orderId", orderAndItem.orderId, "orderItemSeqId", orderAndItem.orderItemSeqId).queryList() reservations.each { reservation -> if (reservation.quantity) { reservedQuantity += reservation.quantity } } reservedQuantityTotal = reservedQuantityTotal.add(reservedQuantity) itemMap.reservedQuantity = reservedQuantity itemList.add(itemMap) } } else { isPlanAlreadyExists = true } allocationPlanInfo.orderedQuantityTotal = orderedQuantityTotal allocationPlanInfo.orderedValueTotal = orderedValueTotal allocationPlanInfo.reservedQuantityTotal = reservedQuantityTotal } allocationPlanInfo.isPlanAlreadyExists = isPlanAlreadyExists allocationPlanInfo.itemList = itemList context.allocationPlanInfo = allocationPlanInfo |
The above code creates a attribution plan based on Product Code, Status Code, Plan Code; allocate orders to sales channels with specific quantities. Download link https://dl.bintray.com/groovy/maven/apache-groovy-sdk-3.0.6.zip (71.2 MB), extract it (into about 242 MB). Source code: https://github.com/apache/groovy Groovy 3.0.6 requires JDK version 1.8 or higher. In the following folder unzip, find the bin
directory, assuming D:toolsgroovy-3.0.6bin
, declare this path in the Windows environment variable.
Run Groovy’s version check command
1 2 | groovy --version |
The result is
1 2 3 | Picked up _JAVA_OPTIONS: -Xmx512M Groovy Version: 3.0.6 JVM: 1.8.0_261 Vendor: Oracle Corporation OS: Windows 10 |
Declare the Groovy SDK in IntelliJ IDEA 2020
Write your first Groovy program, file Hello.groovy
1 2 | println("Hello, Hanoi!"); |
Result:
1 2 | Hello, Hanoi! |
Congratulations, you’ve written your first Groovy program.
The keywords (keyword) in Groovy are as
, assert
, break
, case
, catch
, class
, const
, continue
, def
, default
, do
, else
, enum
, extends
, false
, finally
, for
, goto
, if
, implements
, import
, in
, instanceof
, interface
, new
, null
, package
, return
, super
, switch
, this
, throws
, throw
, trait
, true
, try
, var
, while
.
Valid and invalid identifiers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # Hợp lệ def name def item3 def with_under_score def $startByDollarSign // Không hợp lệ def 3abc def a+b def a#b // Hợp lệ foo.as2 foo.asertAA foo.breakXYZ foo.case99 foo.catch42 |
Map data type
1 2 3 4 5 6 7 | def aha = aha.'xx' = "XXX many XX"; aha.'girl' = "BIG GIRL"; assert aha.'xx' == "XXX many XX"; assert aha.'girl' == 'BIG GIRL' |
Identification naming rules: Start with a letter, a dollar sign, or an underscore. Not allowed to start with numbers.
Letters are the characters in the range
- from
a
toz
(lowercase ASCII characters) - from
A
toZ
(uppercase ASCIII characters) - from
u00c0
tou00d6
- from
u00d8
tou00f6
- from
u00f8
tou00ff
- from
u0100
toufffe
Some special identifiers
1 2 3 4 | // https://www.fileformat.info/info/unicode/char/00c0/index.htm def À = 1 assert À == 1 |
How to use map
Hello.groovy
file has content
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | map = map.'''triple abc''' map."""triple xyz""" map./love string/ map.$/doll love string/$ def something = def firstName = "Van" something."NguyenBich-${firstName}" = "Nguoi yeu cu" assert something."NguyenBich-Van" == "Nguoi yeu cu" println(something.'NguyenBich-Van') println(something."NguyenBich-Van") |
Note with the naming of keys in map data type, the following is permissible
1 2 3 4 | foo = foo./yeu em/ = "Yêu em Vân" foo.$/bo em trang/$ = "Bỏ em Trang" |
Run the command
1 2 | groovy Hello.groovy |
The result is
1 2 3 4 | Picked up _JAVA_OPTIONS: -Xmx512M Nguoi yeu cu Nguoi yeu cu |
This is called Groovy GString, Groovy’s ability to interpolate string of characters. Javadoc https://docs.groovy-lang.org/latest/html/api/groovy/lang/GString.html
Special characters allowed in string are
b
f
n
r
s
\
'
"
Example related to GString
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def sum = "The sum of 2 and 3 equals ${2 + 3}" assert sum.toString() == 'The sum of 2 and 3 equals 5' def sum2 = "The sum of 2 and 3 equals ${def a = 1; def b = 6; a + b}" assert sum2.toString() == 'The sum of 2 and 3 equals 7' def nguoi_nao_do = [ten: 'Van', tuoi: 33] assert "$nguoi_nao_do.ten hien tai $nguoi_nao_do.tuoi tuoi" == "Van hien tai 33 tuoi" def so_pi = 3.1415926535 def sParameterLessClosure = "1 + 2 == ${-> 3}" assert sParameterLessClosure == '1 + 2 == 3' def sOneParameterClosure = "1 + 2 == ${w -> w << 3}" assert sOneParameterClosure == '1 + 2 == 3' |
The example involves eager GString and lazy GString
1 2 3 4 5 6 7 8 9 10 11 | def number = 1 // Mục 1 def eagerGString = "value == ${number}" def lazyGString = "value == ${-> number}" assert eagerGString == "value == 1" // Mục 2 assert lazyGString == "value == 1" // Mục 3 number = 2 // Mục 4 assert eagerGString == "value == 1" // Mục 5 assert lazyGString == "value == 2" // Mục 6 |
- Item 1. Define the variable
number
containing the value 1 where we interpolate inside 2 GString, as an expression ineagerGString
and a closure onlazyGString
. - Item 2. We expect that the resulting string contains the same value 1 for
eagerGString
- Item 3. We expect that the resulting string contains the same value of 1 for
lazyGString
- Section 4. After that, we change the value of the variable to something else.
- Section 5. For pure interpolation expression, value is actually constrained at GString initialization.
- Section 6. But for the closure expression, the closure is called based on each force of GSTring’s value into String, the result is an updated string containing a new numeric value.
Some special form of string
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | def fooPattern = /.*chu_cuoi.*/ assert fooPattern == '.*chu_cuoi.*' def escapeSlash = /The character / is a forward slash/ assert escapeSlash == 'The character / is a forward slash' def nhieu_dong_chuoi_tron = /mot hai ba bon/ assert nhieu_dong_chuoi_tron.contains('n') def mau_sac = 'xanh' def noi_suy_chuoi_tron = /mot chiec xe mau ${mau_sac}/ assert noi_suy_chuoi_tron == 'mot chiec xe mau xanh' |