Roberto Vormittag
Virgiliu Ratoi
v2018.11, 26-11-2018
About
The ESPD service is a web-based solution which allows the Member States and European Institutions to exchange structured e-procurement information in a harmonised manner. The system is not a persistent storage but rather a transformation service which enables the management of XML based documents for each procurement submitted.
The ESPD service is provided as a web-based system and the output of it provided as XML files. In addition, the ESPD service will become part of the Connecting European Facility (CEF) eProcurement DSI (Digital Service Infrastructure). The CEF will help the Member States to implement an ESPD service in their own country and provide additional services to their users.
System requirements
Installation
Configure the Git repository
-
Fork the ESPD Git repository by clicking on the
Fork
button on the top-right corner of the repository page. Forking a repository allows you to freely experiment with changes without affecting the original project. Most commonly, forks are used to either propose changes to someone else’s project or to use someone else’s project as a starting point for your own idea. -
Create a local clone of your fork
-
On GitHub, navigate to your fork of the ESPD Git repository.
-
Under your repository name, click the
Copy to clipboard
button to copy the URL for the repository. -
Run the
git clone <URL>
command on your computer, after pasting the URL you copied in the previous step. Now, you have a local copy of your fork of the ESPD Git repository.
-
-
Configure Git to sync your fork with the original ESPD Git repository. When you fork a project in order to propose changes to the original repository, you can configure Git to pull changes from the original, or upstream, repository into the local clone of your fork.
-
On GitHub, navigate to the ESPD Git repository repository.
-
Under the repository name, click the
Copy to clipboard
button to copy the URL for the repository. -
Change directories to the location of the fork you cloned in the previous step.
-
Execute
git remote -v
to see the current configured remote repositories for your fork. -
Type
git remote add upstream <URL>
by pasting the URL you copied in Step b. -
To verify the new upstream repository you’ve specified for your fork, execute
git remote -v
again. You should see the URL for your fork asorigin
, and the URL for the original repository asupstream
.
-
$ git remote -v
origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (fetch)
origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (push)
upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git (fetch)
upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git (push)
Now you can keep your fork synced with the upstream
repository with a few Git commands. For more information,
please see the Syncing a fork page.
Quite often people working in a team and using the same repository or upstream prefer different operating systems.
This may result in problems with line endings because Unix, Linux, and OS X use LF and Windows uses CRLF to denote
the end of a line. To have Git solve such problems automatically, you need to set the core.autocrlf attribute to
true on Windows and to input on Linux and OS X. For more details on the meaning of the core.autocrlf attribute,
see the article Dealing with Line Endings. You can change the configuration manually by running
git config --global core.autocrlf true on Windows or git config --global core.autocrlf input on Linux and OS X.
|
Each major application version will have a dedicated branch so you can checkout the code for a particular version. The name convention adopted for release branches is YYYY.MM. The master branch contains the version currently deployed in production. The develop branch contains the code for the next version of ESPD. The gh-pages branch is reserved for documentation. If you want to be isolated from changes you should branch from master or from a specific release branch. |
Building and running the ESPD application
This section goes into detail about how you can build and run the ESPD application.
Building the application
After getting access to the source code and cloning it on your computer, you can now build the project with Maven.
-
Change directories to the location of the cloned Git repository on your machine.
-
Perform a
mvn clean package
and the project dependencies should be downloaded and theespd-web
andespd-docs
modules should be built.
Running the application
The easiest way to run the application is with an embedded container (default is Tomcat) by invoking the main
method
from the eu.europa.ec.grow.espd.config.EspdApplication
class in the same way as you would start a normal Java application.
-
Perform a
mvn clean package
from the root folder of the project:-
The build should generate a WAR file at
${baseDir}/espd-web/target/espd-web.war
-
-
Add the following startup parameters to you server:
-
-Dspring.profiles.active=${your desired profile}
specifies the Spring Boot profile to be used -
-Dted.api.user=${your TED user}
where${your TED user}
is replaced by the TED API username -
-Dted.api.password=${your TED password}
where${your TED password}
must be replaced by the TED API password
-
The application can be started with a shell script that can contain the following minimum configuration:
java -Dspring.profiles.active=prod -Xms768m -Xmx768m -jar espd-web.war
Other application parameters can also be specified in exactly the same way, i.e. by prefixing them with a -D
followed
by the name of the parameter. You can consult the application.properties
files for finding out the available options
but please note that the ones specified at start-up have higher precedence than the ones specified in the .properties
files.
It is recommended to set up the context path of the application when running in the embedded mode. This can be done in the application.properties file located in the espd-web project resources folder. |
# Context path of the application
server.context-path=/espd
Deploying the web application on a servlet container
The application can be deployed as a WAR file on a Servlet 3 compliant container such as Tomcat. For this, you need to package it as a WAR for the
non-embedded
Maven profile and provide some startup parameters on your server.
-
Perform a
mvn clean package -Pnon-embedded
from the root folder of the project:-
The build should generate a WAR file at
${baseDir}/espd-web/target/espd-web.war
-
-
The other steps are similar to the embedded server deployment mode
Using an IDE
You can also run the ESPD application from your favourite IDE by importing the project as a multi-module Maven project first.
You will need to install the Lombok plugin for your particular IDE. Lombok is a library designed to reduce Java boilerplate code through the use of annotations. |
If you have problems importing the project in Eclipse try running the Maven command mvn eclipse:eclipse before the import.
If after the import the Problems view display errors of type Plugin execution not covered by lifecycle configutation right-click on the error and set Eclipse to ignore the execution of the plugin.
|
Overview
-
espd-docs contains the documentation of the application
-
espd-web contains the source code to generate the deployable web archive (WAR) of the application
The Java library eu.europa.ec.grow.espd.exchange-model
contains the JAXB classes of the ESPD Exchange Data Model.
The ESPD web application is quite simple in nature and can be deployed as an executable JAR file (with an embedded Tomcat server) or as a standard WAR file on a compliant Servlet 3 container like Tomcat, Jetty, Weblogic, Wildfly etc.
The main responsibilities of the application are handled as follows:
-
Infrastructural support at the application level and dependency injection via the Spring framework
-
Configuration of the application via Spring Boot
-
The view part uses Spring MVC, JSP files, Apache Tiles, Javascript, jQuery and Twitter Bootstrap
-
The data model uses JAXB to convert Java objects to XML and vice versa
-
REST calls to external services are handled with the Spring Rest Template client
-
Monitoring and analytics via Java Melody and Piwik
-
Optimization of static resources with Spring asset pipeline and WRO4J
Java packages overview
The main Java package of the web application is 'eu.europa.ec.grow.espd` found in the espd-web
module. It is further
subdivided into the following important packages:
-
config
-
controller
-
domain
-
ted
-
util
-
xml
Config package
The config
package contains the Java classes which provide the configuration for the global project
EspdApplication
, Spring MVC web configuration WebConfiguration
, the JAXB configuration etc. You will
notice that these classes are annotated with @Configuration
and they generally define Spring beans that can be
injected in other Spring managed classes of the application.
Controller package
The controller
package consists of the Spring MVC controllers which are responsible for processing user requests,
building an appropriate model and passing it to the view for rendering. Some controllers are REST controllers.
The most important one is EspdController
and its responsibilities include dealing with the generation of the ESPD
requests and responses, uploading files, printing and error handling. The main flow of the application is handled by
this class.
Other controllers which are present in this package are the AboutController
that is used to populate the /about
page of the application and MessageSourceController
which is responsible for loading the i18n messages needed
by the client-side i18n engine.
Domain package
The domain
package contains the elements involved in the representation of an ESPD request or response in the model
(the M
part of the MVC architectural pattern) of the web application. An ESPD request/response is represented
internally as an EspdDocument
which in turn contains fields for all the possible criteria, authority and economic
operator information or procurement procedure data.
In the domain.enums
sub-package you can find the enumerations holding the criteria, requirement groups, requirements,
countries, languages, currencies etc which are used by the application. The criteria definitions load metadata coming
from some JSON configuration files under src/main/resources/criteria
and store the information dynamically in the
criterion enumerations.
TED package
The ted
package is responsible for the interaction with the TED REST service which provides the procurement
procedure information for an ESPD request or response.
TED is the electronic version of the EU Official Journal dedicated to public procurement. |
Util package
The util
package contains various utility classes, for example, some of the application properties that are needed
by the user interface are loaded by Spring in the EspdConfiguration
class. The I18NFunc
class loads the i18n
messages needed by the client-side internationalization engine.
XML package
The xml
package takes care of the marshalling/unmarshalling of the ESPD request and response XML files. It is further
divided into a package for request
, one for response
and a common
package with shared functionality between the
other two. The classes from this package make use of the Template method design pattern to share common
behaviour and design.
The request
and response
packages contain each an exporting
and an importing
sub-package. The exporting
package
is responsible for generating the ESPD request/response XML files while the importing
package knows how to parse XML
files representing ESPD request or response and convert them into the internal ESPD domain objects.
The EspdExchangeMarshaller
is the main class that aggregates all of the classes from this package together and is
injected in the EspdController
to perform the marshalling/unmarshalling logic.
CriteriaTemplates
serves as configuration for the user interface components of the criteria and requirements that
are defined in the JSP and Apache Tiles files.
Web overview
The web static resources can be found in the standard Maven location, i.e. src/main/webapp
. The static
folder contains the
CSS and Javascript files referenced by the web application. The JSP files are located in the WEB-INF/views
folder where
they are subsequently grouped in sub-folders. The src/main/resources
folder contains the configuration files relating
to internationalization, logging, web resource optimizations and general application properties.
Configuration
This section will describe the basic configuration for the most important components of the application.
Configuration files
Most of the configuration properties of the web application are specified in properties
files which can be found
inside the src/main/resources
folder of the espd-web
Maven module. There are different files for each profile
under which the application can be started, for example the application-prod.properties
file will be used for the
prod
profile which must be specified as a startup parameter as -Dspring.profiles.active=prod
. If no profile is
specified, then a default
one will be used by Spring Boot and the corresponding configuration file would be just
application.properties
. Here you can find more information about properties and configuration with Spring Boot.
Spring
The ESPD application is built from the ground-up with Spring Boot, allowing us to simplify the project configuration and the management of third party libraries.
It does so by specifying in the main pom.xml
of the project a parent POM pointing to the Spring Boot starter.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.2.RELEASE</version>
</parent>
Since the application is using Spring Boot extensively by inheriting from spring-boot-starter-parent
, this provides it
with the following features:
-
Default Java compiler level
-
UTF-8 source encoding
-
A Dependency Management section, allowing you to omit
<version>
tags for common dependencies, inherited from thespring-boot-dependencies
POM. -
Sensible resource filtering, including
application.properties
andapplication.yml
type of files -
Sensible plugin configuration
For more information about the Spring Boot project, you can check the official documentation.
The eu.europa.ec.grow.espd.config
package contains the Spring configuration classes. The main class of the application
is EspdApplication
which makes use of the @SpringBootApplication
annotation in order to enable the Spring Boot
auto configuration. The package also contains the web, JAXB and web resources configuration files.
Spring MVC
The web configuration part is found in the WebConfiguration
class in conjunction with the following Spring Boot properties:
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
# Default locale to use
spring.mvc.locale=en
The WebConfiguration
contains the definition of various Spring beans: view resolvers, tiles configuration, locale
interceptors, monitoring filter.
@Bean
UrlBasedViewResolver viewResolver() {
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
viewResolver.setViewClass(TilesView.class);
return viewResolver;
}
@Bean
TilesConfigurer tilesConfigurer() {
TilesConfigurer tilesConfig = new TilesConfigurer();
tilesConfig.setDefinitions("/WEB-INF/tiles.xml");
return tilesConfig;
}
Internationalization (i18n)
Concerning internationalization (i18n), the ESPD application takes advantage of the auto-configuration provided
by Spring Boot in the MessageSourceAutoConfiguration
class.
# The location to the resource bundles needed by i18n
spring.messages.basename=i18n/messages
# Loaded resource bundle files cache expiration, in seconds. When set to -1, bundles are cached forever.
spring.messages.cache-seconds=3
The message files are located in the src/main/resources/i18n
folder and follow a very simple naming convention of
messages_${locale language}
.
The application.properties file which is used only for local development is able to
refresh potential changes in the message files without requiring a server restart by specifying the
spring.messages.cache-seconds property.
|
The corresponding Java configuration is summarized below.
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
@Bean
LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}
@Bean
LocaleResolver localeResolver() {
CookieLocaleResolver resolver = new CookieLocaleResolver();
resolver.setCookieName("ESPD_LOCALE");
resolver.setDefaultLocale(Locale.ENGLISH);
return resolver;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
The LocaleResolver
enables the application to automatically resolve messages files using the client’s locale which
is stored in a ESPD_LOCALE
cookie
.
The LocaleChangeInterceptor
uses an HTTP request parameter named lang
to detect the language changes on the server side.
Web Resources Optimization
Static resources
ESPD uses advanced resource handling features provided by Spring MVC and Spring Boot. We have chosen a path that relies on optimizing resources at build-time using WRO4J and leveraging Spring MVC Resolvers and Transformers and WRO4J filter at run-time.
The static resources of the application (Javascript and CSS files) are versioned using a content-based hashing strategy
and handled with the idea of cache busting
where resources are served with aggressive HTTP cache directives
(e.g. 1 year into the future) and relying on version-related changes in the URL to "bust" the cache when necessary.
The content-based hash version changes whenever the content of the file changes and this happens at build time.
# Cache period for the resources served by the resource handler, in seconds (1 year).
spring.resources.cache-period=31622400
# Enable the Spring Resource Handling chain.
spring.resources.chain.enabled=true
# Enable the content Version Strategy.
spring.resources.chain.strategy.content.enabled=true
# Comma-separated list of patterns to apply to the Version Strategy.
spring.resources.chain.strategy.content.paths=/static/bundle/**
Links to resources are rewritten at run-time using a ResourceUrlEncodingFilter
.
import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;
import org.springframework.context.annotation.Bean;
/**
* If the template engine you are using calls the response encodeURL() method, the version information
* will be automatically added to the URL of the static resources that will be cached.
* This will work in JSPs in conjunction with spring:url tag.
* <p>It needs to be mapped on '/*'.</p>
*
* @return
*/
@Bean
ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
return new ResourceUrlEncodingFilter();
}
And this is how the static resources are referenced in the view part:
<link rel="stylesheet" type="text/css" href="<s:url value="/static/bundle/all.css"/>">
<script src="<s:url value="/static/bundle/all.js"/>"></script>
For example, a request made to the all.js
file would be translated into a request made to a Javascript file with a hash:
https://ec.europa.eu/espdstatic/bundle/all-60d9cd4aee2d53a2a4bd69a5546a9d18.js
.
WRO4J
Another set of static resources optimizations are handled with WRO4J using a simple Java filter at run-time and the Maven plugin at build-time. WRO4J concatenates and minifies the static resources like Javascript or CSS files into a single file per each type of resource so that the number of HTTP requests made by the clients that load the application is reduced drastically.
Run-time solution with a Servlet filter
The Java filter configuration makes use of Spring Boot auto-configuration provided by the wro4j-spring-boot-starter library.
This is only used by the default Spring Boot profile which should be active only at development time.
|
import ac.simons.spring.boot.wro4j.Wro4jAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Profile("default")
@Configuration
class Wro4jConfig extends Wro4jAutoConfiguration {
// only used for development ('default' profile) when we need the Wro4J Filter
}
The run-time properties are defined in the application.properties
file.
# Integer value for specifying how often (in seconds) the resource changes should be checked. When this value is 0,
# the cache is never refreshed. When a resource change is detected, the cached group containing changed resource
# will be invalidated. This is useful during development, when resources are changed often.
wro4j.resourceWatcherUpdatePeriod=3
# Integer value for specifying how often (in seconds) the cache should be refreshed.
# When this value is 0, the cache is never refreshed.
wro4j.cacheUpdatePeriod=3
wro4j.disableCache=true
wro4j.debug=true
wro4j.filterUrl=/static/bundle
# A comma separated values describing pre processor aliases to be used during processing.
wro4j.managerFactory.preProcessors=fallbackCssDataUri, cssUrlRewriting, cssImport, semicolonAppender, cssMinJawr, jsMin
Build-time solution with Maven
The build-time solution uses a Maven plugin and needs two WRO4J configuration files placed under the
src/main/resources
folder.
<groups xmlns="http://www.isdc.ro/wro">
<group name="all">
<css minimize="false">/static/bootstrap-3.2.0/css/bootstrap.min.css</css>
<css minimize="true">/static/css/espd.css</css>
<js minimize="false">/static/jquery/jquery.min.js</js>
<js minimize="false">/static/bootstrap-3.2.0/js/bootstrap.min.js</js>
<js minimize="true">/static/js/init.js</js>
</group>
</groups>
###############################################################################
##### THIS FILE IS USED AT BUILD TIME BY THE WRO4J MAVEN PLUGIN ######
###############################################################################
#If true, it is DEVELOPMENT mode, by default this value is true.
debug=false
# A comma separated values describing pre processor aliases to be used during processing.
preProcessors=fallbackCssDataUri,cssUrlRewriting,cssImport,semicolonAppender
postProcessors=cssVariables,cssMinJawr,jsMin
# The alias of the HashStrategy used to compute ETags & cache keys.
hashStrategy=MD5
# The alias of the NamingStrategy used to rename bundles.
namingStrategy=noOp
The Maven plugin bundles all the Javascript and CSS files into the src/main/webapp/static/bundle
folder,
applying minimization where necessary and creating a all.js
and a all.css
file.
<configuration>
<wroFile>${basedir}/src/main/resources/wro.xml</wroFile>
<extraConfigFile>${basedir}/src/main/resources/wro.properties</extraConfigFile>
<cssDestinationFolder>${basedir}/src/main/webapp/static/bundle/</cssDestinationFolder>
<jsDestinationFolder>${basedir}/src/main/webapp/static/bundle/</jsDestinationFolder>
<wroManagerFactory>ro.isdc...factory.ConfigurableWroManagerFactory</wroManagerFactory>
<ignoreMissingResources>false</ignoreMissingResources>
<incrementalBuildEnabled>true</incrementalBuildEnabled>
</configuration>
Lombok
To reduce some of the boilerplate
code inherent to the Java language, the project uses the Lombok library which leverages Java annotations.
The library can be used in Eclipse by double clicking the lombok.jar
and in IntelliJ by installing the Lombok plugin.
If you do not like the basic idea behind Lombok you can delombok the source code and go back to standard Java source code.
import lombok.Builder;
import lombok.Getter;
@Builder
@Getter
public class TedRequest {
private String receptionId;
}
public TedRequest prepare() {
TedRequest request = TedRequest.builder()
.receptionId("16-000136-001")
.build();
log.debug("This is the reception id: '{}'.", request.getReceptionId());
return request;
}
Logging
Logging in the application is handled using the SLF4J API and the chosen implementation is provided by the Logback library.
Since the espd-web module depends on spring-boot-starter-web
and this one transitively depends on spring-boot-starter-logging
,
the default logging implementation configured by Spring Boot is Logback.
The application-${profile}.properties
files declare the path to the Logback configuration. The logging configuration
files are stored in the espd-web/src/main/resources/logback
folder.
# The path to the logback configuration file depending on the profile
logging.config=classpath:logback/logback-dev.xml
To use logging in the code, you can take advantage of the facilities provided by Lombok.
import lombok.extern.slf4j.Slf4j;
@Slf4j
class Logging {
void logSomething(String parameter) {
log.info("Logging the following value '{}'.", parameter);
}
}
Monitoring and analytics
Java Melody
Basic monitoring of the ESPD application is handled using the Java Melody library by registering a Java filter inside the Spring application context.
import org.springframework.context.annotation.Bean;
import net.bull.javamelody.MonitoringFilter;
import net.bull.javamelody.Parameter;
@Bean
MonitoringFilter melodyMonitoringFilter() {
return new MonitoringFilter();
}
@Bean
FilterRegistrationBean melodyFilterRegistration(MonitoringFilter melodyFilter) {
FilterRegistrationBean frb = new FilterRegistrationBean(melodyFilter);
frb.addInitParameter(Parameter.NO_DATABASE.getCode(), "true");
frb.addInitParameter(Parameter.ALLOWED_ADDR_PATTERN.getCode(),
"(158\\.16[6-8]\\..*)|(127\\.0\\.0\\.1)|(localhost)");
frb.addInitParameter(Parameter.URL_EXCLUDE_PATTERN.getCode(), "(/img/.*)|(/js/.*)|(/css/.*)|(.*/.woff)");
return frb;
}
The filter configuration makes the monitoring accessible only to a certain range of IP addresses, excludes requests pointing to static resources and specifies that no database monitoring should be active.
Additional monitoring capabilities could be added by activating the Spring Boot actuator features.
Piwik
Analytics capabilities for the application are provided via the Piwik server of DG Growth.
# Enable or disable the Piwik integration
piwik.enabled=false
# Piwik id for ESPD project
piwik.id=2
# Piwik server for ESPD project
piwik.server=https://webgate.ec.europa.eu/pwar/piwik.php
[NOTE] You might want to disable the integration with the Piwik server of DG Growth by setting the piwik.enabled
parameter to false
in the corresponding application.properties
file.
Criteria definitions
With the 2016.06
version, the criteria requirement groups have been restructured with regards to interoperability with
the VCD application and three JSON files have been added under src/main/resources/criteria
. These files contain the
definitions for the exclusion, selection and other criteria used by the static version of ESPD.
{
"name": "Conflict of interest due to its participation in the procurement procedure",
"uuid": "b1b5ac18-f393-4280-9659-1367943c1a2e",
"shortName": "Conflict of interest due to its participation in the procurement procedure",
"description": "Is the economic operator aware of any conflict of interest, as indicated in national law, the relevant notice or the procurement documents due to its participation in the procurement procedure?",
"criterionType": {
"description": "Grounds for exclusion relating to possible conflicts of interests",
"espdType": "CONFLICT_OF_INTEREST",
"code": "CRITERION.EXCLUSION.CONFLICT_OF_INTEREST.PROCEDURE_PARTICIPATION"
},
"legislationReference": {
"title": "DIRECTIVE 2014/24/EU OF THE EUROPEAN PARLIAMENT AND OF THE COUNCIL of 26 February 2014 on public procurement and repealing Directive 2004/18/EC",
"description": "Directive 2014/24/EU",
"url": "http://eur-lex.europa.eu/legal-content/EN/TXT/?uri=celex:32014L0024",
"article": "57(4)"
},
"groups": [
{
"name": "G1",
"id": "30450436-f559-4dfa-98ba-f0842ed9d2a0",
"requirements": [
{
"id": "974c8196-9d1c-419c-9ca9-45bb9f5fd59a",
"description": "Your answer?",
"responseType": "INDICATOR",
"espdCriterionFields": [
"answer"
]
}
]
}
],
"espdDocumentField": "conflictInterest"
}
ESPD Exchange Data Model
The ESPD-EDM is a separate project containing the XML schemas used to generate the JAXB annotated Java classes.
<dependency>
<groupId>eu.europa.ec.grow.espd</groupId>
<artifactId>exchange-model</artifactId>
<version>${espd-exchange-model.version}</version>
</dependency>
The marshalling and unmarshalling of the XML files produced by the application is handled by a Spring Jaxb2Marshaller.
import grow.names.specification.ubl.schema.xsd.espdrequest_1.ESPDRequestType;
import grow.names.specification.ubl.schema.xsd.espdresponse_1.ESPDResponseType;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import javax.xml.bind.Marshaller;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class JaxbConfiguration {
@Bean
public Jaxb2Marshaller jaxb2Marshaller() {
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
jaxb2Marshaller.setPackagesToScan(ESPDRequestType.class.getPackage().getName(),
ESPDResponseType.class.getPackage().getName());
Map<String, Object> map = new HashMap<>(2);
map.put(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxb2Marshaller.setMarshallerProperties(map);
return jaxb2Marshaller;
}
}
Date and time
Date and time objects are handled with the Joda-Time library. There are two adapters that are used to populate the Date
objects inside the JAXB POJOs. These adapters convert and parse String
objects into LocalDate
or LocalTime
Joda-Time objects.
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
public final class LocalDateAdapter {
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormat.forPattern("YYYY-MM-dd");
private LocalDateAdapter() {
}
public static LocalDate unmarshal(String v) {
return LocalDate.parse(v, DATE_FORMAT);
}
public static String marshal(LocalDate v) {
return v.toString(DATE_FORMAT);
}
}
REST template
The interaction with external RESTful APIs (e.g. TED) is done with the Spring RestTemplate.
There is one global Spring bean of type RestTemplate
defined in the application.
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@ComponentScan("eu.europa.ec.grow.espd")
public class EspdApplication extends SpringBootServletInitializer implements WebApplicationInitializer {
@Value("${http.client.connect.timeout.millis:30000}")
private int connectTimeout;
@Bean
ObjectMapper objectMapper() {
return new ObjectMapper();
}
@Bean
RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
SimpleClientHttpRequestFactory rf = (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory();
rf.setReadTimeout(connectTimeout);
rf.setConnectTimeout(connectTimeout);
return restTemplate;
}
}
You can find an example of how to use the RestTemplate
in the eu.europa.ec.grow.espd.ted.TedService
class.
TED REST service
Information about the procurement procedure can be provided by the publication office via the TED REST service. In order to be able to retrieve the information from their remote service we need to provide four parameters.
# The base URL of the TED contract notice REST service
ted.api.base.url=https://esentool.ted.europa.eu/api/espd/v1.0/notice
# Timeout in milliseconds for the Spring RestTemplate client
rest.template.connect.timeout.millis=30000
# user for TED API
ted.api.user=passed as server startup parameter
# Password for TED API
ted.api.password=passed as server startup parameter
The part of the code that handles the TED service can be found in the eu.europa.ec.grow.espd.ted
package.
PDF printing
Printing the ESPD Request
and ESPD Response
to PDF files is achieved via Apache FOP. To produce a PDF file, we start
from the HTML content of the ESPD
entity which we want to print and use an XSLT stylesheet that converts the HTML
to XSL-FO. This is the first step in the processing chain. The second step will be done by Apache FOP when it reads the
generated XSL-FO document and formats it to a PDF document.
XSL-FO is an XML vocabulary that is used to specify a pagination and other styling for page layout output.
The acronym FO
stands for Formatting Objects. XSL-FO can be used in conjunction with XSLT to convert from any XML
format into a paginated layout ready for printing or displaying. The XSLT files taking care of the HTML transformation
can be found in src/main/resources/tenderned/pdfrendering/xslt
.
The printing implementation resides inside the eu.europa.ec.grow.espd.tenderned.HtmlToPdfTransformer
class while
the infrastructure setup of Apache FOP is defined in the eu.europa.ec.grow.espd.config.ApacheFopConfig
class.
Apache FOP requires a configuration file whose location can be configured via the apache.fop.xml.configuration.location
application parameter. The default locations point to files belonging to the src/main/resources/grow/fop/
folder.
In order to display the PDF correctly across all European languages we need to use a font which contains all the glyphs
for these languages. Otherwise, if no glyph can be found for a given character, Apache FOP will issue a warning and use
the glyph for "#" (if available) instead. The font also needs to be embedded in the PDF so that the document is
correctly displayed on all clients which are viewing the generated files. For these reasons we are using a custom
font called DejaVu
.
Inside the fop-config.xml
file we need to make sure that the font files are correctly configured and then properly
loaded by Apache FOP across multiple Servlet containers. Please notice the ember-url
attribute.
<font metrics-url="fonts/DejaVuSans/ttf/DejaVuSans.xml"
embed-url="fonts/DejaVuSans/ttf/DejaVuSans.ttf">
<font-triplet name="DejaVuSans" style="normal" weight="normal"/>
</font>
The application parameter apache.fop.defaultBaseUri
can be specified to load the font files via different strategies.
Embedded fonts can be loaded via absolute (Weblogic in Production mode) or relative paths or via classpath depending on
the chosen strategy. When using an embedded server it is recommended to use the classpath approach.
The fonts are loaded by Apache FOP by using a custom org.apache.xmlgraphics.io.ResourceResolver
which looks for them
via the Spring ResourceLoader
mechanism in a portable and consistent way across different Serlvet containers.
@Override
public Resource getResource(URI uri) throws IOException {
log.debug("--- Fop resource resolver get resource: '{}'.", uri);
InputStream is = resourceLoader.getResource(uri.toASCIIString()).getInputStream();
return new Resource(is);
}
To be able to load the fonts when running the application with an embedded server we need to copy them in a location relative to the application context root.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<warName>${project.artifactId}</warName>
<webResources>
<resource>
<directory>src/main/resources/fonts</directory>
<targetPath>fonts</targetPath>
<filtering>false</filtering>
</resource>
</webResources>
</configuration>
</plugin>
Web Page Initialization Parameters
Certain HTTP parameters can be passed to the /initialization
URL of the ESPD application to
initialize specific fields. When issuing such a request, the application redirects the client to the /filter
page.
-
lang
is used to specify the language to be used by the application. It consists of a two letter code from the supported languages of the application. Example: -
agent
is used to choose between acontracting authority
,contracting entity
andeconomic operator
. The only accepted values are:-
ca
for contracting authority -
ce
for contracting entity -
eo
for economic operator
-
-
action
defines what the user would like to do. The accepted values are:-
ca_create_espd_request
for selecting theCreate a new ESPD
option as a contracting authority or contracting entity -
ca_reuse_espd_request
for selecting theReuse an existing ESPD
option as a contracting authority or contracting entity -
ca_review_espd_response
for selecting theReview ESPD
option as a contracting authority or contracting entity -
eo_import_espd
for selecting theImport ESPD
option as an economic operator -
eo_merge_espds
for selecting theMerge two ESPDs
option as an economic operator -
eo_create_espd_response
for selecting theCreate response
option as an economic operator
-
-
country
for selecting the desired country of the authority or economic operator. It must be the two letter code of the country in uppercase.
When the agent
is a contracting authority or contracting entity, the following parameters are supported for
filling in the fields belonging to Part I - Information about the procurement procedure section
.
-
officialName
-
procurerCountry
- must be a two letter country code in uppercase -
title
-
description
-
fileRefByCA
-
tedReceptionId
specifies the received notice number
When the agent is economic operator, the parameters below can additionally be configured to initialize
Part II Information concerning the economic operator - Section A
:
-
name
-
vatNumber
-
anotherNationalId
-
website
-
street
-
postalCode
-
city
-
country
- must be a two letter country code in uppercase -
contactName
-
contactEmail
-
contactPhone
HTTP POST @ espd/initialization?country=RO&city=Drobeta&agent=eo&action=eo_create_espd_response&procurerCountry=FR&lang=ro
All the parameters described above are defined in the EspdInitializationParameters
class.
It is recommended to use HTTP POST requests but HTTP GET requests are also supported.
View
The client interface is based on the Spring MVC integration with Apache Tiles working as a template system for the JSP files. All of the HTML forms are CSS styled with Twitter Bootstrap and use Spring form tags to do the display and form binding.
Every JSP implements a responsive Twitter Bootstrap grid layout and some Twitter Bootstrap styles to make it look better.
Apache Tiles and the corresponding view resolvers
are configured in the WebConfiguration.java
file while tiles.xml
contains the configuration for the Apache Tiles template definitions.
Forms
Client web form submission and validation is performed by Spring MVC Form Binding.
@InitBinder
private void dateBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
CustomDateEditor editor = new CustomDateEditor(dateFormat, true);
binder.registerCustomEditor(Date.class, editor);
}
The HTML form is implemented by the spring:form
tag to bind the controller object via @ModelAttribute
. An
EspdDocument
object will be attached to each HTTP session via the @SessionAttributes("espd")
declaration and is
reset on each page access to the /welcome
page.
@ModelAttribute("espd")
public EspdDocument newDocument() {
return new EspdDocument();
}
Internationalization (i18n)
A Web application with a worldwide user base needs to be easily adapted to support several human languages without impacting its design, through internationalization techniques (i18n).
ESPD i18n is implemented through the use of Spring interceptors
, locale resolvers
and resource bundles
for different locales.
LocaleChangeInterceptor
and LocaleResolver
are configured in the WebConfiguration
class.
A MessageSource
bean is provided by Spring Boot and configured in properties file to enable i18n for the ESPD application.
# The location to the resource bundles needed by i18n
spring.messages.basename=i18n/messages
Translated messages could be retrieved with the given code by the Jsp tag spring:message
.
<%@ taglib prefix="s" uri="http://www.springframework.org/tags" %>
<s:message code="createca_postcode"/><%-- by static code --%>
<s:message code="${cty.i18nCode}"/><%-- code from variable --%>
It is worth noting the use of custom classes providing more efficient use of the MessageSource
bean in JSP.
The class I18NFunc
provides HTML code generating methods to use in JSPs for translated fields.
<%-- Initialization of request objects --%>
eu.europa.ec.grow.espd.util.I18NFunc inst =
new eu.europa.ec.grow.espd.util.I18NFunc(pageContext);
request.setAttribute("i18n", inst.message());
request.setAttribute("div18n", inst.div());
request.setAttribute("span18n", inst.span());
Using these classes is more concise and the generated code supports translation without page reload.
${i18n['createca_procurer_name']}
${div18n['createca_procurer_name']}
${span18n['createca_procurer_name']}
The JSP code above would generate the following HTML output:
Title:
<div data-i18n="createca_procurer_name">Title:</div>
<span data-i18n="createca_procurer_name">Title:</span>
Different combinations of custom i18n code generators provide translation labels, tooltips and placeholders without page reload.
<!-- label example -->
<label class="control-label">
${span18n['crit_year']
</label>
<!-- tooltip example -->
<span data-toggle="tooltip"data-i18n="${tooltip_code}" title="${tooltip_text}">
</span>
<!-- placeholder example -->
<form:input path="field"
cssClass="form-control"
data-i18n="crit_ratio_placeholder"
placeholder="${i18n['crit_ratio_placeholder']}"/>
Dynamic web page translation
The ESPD Web content is translated to another language automatically without page refresh. The dynamic translation is
implemented in Javascript with jQuery and the translation routine is found in the init.js
file. The Javascript client
makes an HTTP call to the /translate
mapping defined inside the MessageSourceController
class to fetch the required
message labels and afterwards updates the DOM elements containing the data-i18n
attribute.
Testing
Making sure that an application behaves correctly is a difficult job and ESPD is no exception from this rule, that’s why we have an extensive test suite of more than 600 automated unit tests. These tests try to focus on the domain model marshalling/unmarshalling logic of ESPD Requests and Responses and go into very detailed checks for each criterion, requirement or other domain entity of the application.
The unit tests are written using the Spock Framework in the Groovy programming language and we take advantage of its beautiful and highly expressive specification language.
The Maven configuration for running the tests at build time is defined in the espd-web/pom.xml
file. It uses the
gmavenplus-plugin
to compile the Groovy test files and the maven-surefire-plugin
to specify the file naming pattern
of the tests that are supposed to be run.
<plugin>
<!-- The gmavenplus plugin is used to compile Groovy code. To learn more about this plugin,
visit https://github.com/groovy/GMavenPlus/wiki -->
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Optional plugins for using Spock -->
<!-- Only required if names of spec classes don't match default Surefire patterns (`*Test` etc.) -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<useFile>false</useFile>
<includes>
<include>**/*Spec.java</include>
<include>**/*Test.java</include>
</includes>
</configuration>
</plugin>
The tests can be run by invoking a mvn clean package
or mvn clean verify
command or from the IDE of your choice.
If you are using Eclipse you will have to install the Groovy Eclipse plugin. |
If you are using IntelliJ you need to enable the Groovy plugin and it might also be useful to install the IntelliJ Spock plugin for improved working experience. |
The location of the tests is under the espd-web/src/test/groovy
folder. Inside the eu.europa.ec.grow.espd.xml
package you will find the following sub-packages:
-
the base package contains some basic infrastructure for the unit tests
-
the request package contains the tests that handle an ESPD Request
-
the response package contains the tests that handle an ESPD Response
-
the samples folder contains the XML sample files that are used by the tests that handle the importing of an ESPD Request or Response
Anatomy of a test specification
Spock specification classes are derived from spock.lang.Specification
. A concrete specification class might consist
of fields, fixture methods, features methods and helper methods.
class ListSpec extends Specification {
def "adding an element to a list leads to size increase"() { (1)
given: "a new list instance is created" (2)
def lst = new ArrayList()
when:
lst.add(666) (3)
then:
lst.size() == 1 (4)
}
}
1 | Feature method, is by convention named with a descriptive String literal. |
2 | Given block, here is where any setup work for this feature needs to be done. |
3 | When block describes a stimulus, a certain action under target by this feature specification. |
4 | Then block contains any expressions that can be used to validate the result of the code that
was triggered by the when block. |
Spock feature specifications are defined as methods inside a spock.lang.Specification
class. They describe the
feature by using a String
literal instead of a method name.
A feature method holds multiple blocks, in our example we used given
, when
and then
. The given
block is special
in that it is optional and allows us to configure local variables visible inside the feature method. The when
block
defines the stimulus and is a companion of the then
block which describes the response to the stimulus.
The given block is just an alias for the setup block.
|
Note that the given
method in the SpockSpec above additionally has a description String
. Description Strings are
optional and can be added after block labels (like given
, when
, then
).
Testing the generation of an XML file
The code sample below represents a unit test that checks the XML output of an ESPD Response generation, more
specifically the Reason
requirement of the Fraud
exclusion criterion.
class FraudResponseTest extends AbstractExclusionCriteriaFixture {
def "check the 'Reason' requirement response"() {
given:
def espd = new EspdDocument(fraud: new CriminalConvictionsCriterion(exists: true, reason: "Reason_03 here")) (1)
when:
def response = parseResponseXml(espd) (2)
def idx = getResponseCriterionIndex(ExclusionCriterion.FRAUD)
then:
def subGroup = response.Criterion[idx].RequirementGroup[0] (3)
def req = subGroup.Requirement[2] (4)
checkRequirement(req, "7d35fb7c-da5b-4830-b598-4f347a04dceb", "Reason", "DESCRIPTION") (5)
req.Response.size() == 1 (6)
req.Response[0].Description.text() == "Reason_03 here" (7)
}
}
1 | The given clause initializes the ESPD domain object with the Fraud criterion which contains a Reason with the
text Reason_03 here because the exists flag is set to true on the criterion. |
2 | The when clause delegates to the parseResponseXml method from the AbstractEspdXmlMarshalling class which
generates the XML representation of an ESPD Response. |
3 | We get the first requirement group of the Fraud criterion |
4 | The Reason requirement is the third requirement of the group |
5 | Check the requirement id, description text and response type |
6 | We should have only a Response XML element |
7 | Make sure that the description text of the response has the actual value that was supplied in the given clause |
The most common way of querying XML in Groovy is using GPath
which is similar to XPath
expressions but you can use
it not only with XML but also with POJO classes. More information and examples of using GPath
can be found in the
Groovy programming language documentation on XML processing.
The parseResponseXml
method uses a Groovy XmlSlurper object which returns GPathResult instances when parsing XML.
The then
clause makes the business assertions about the expected requirement id, description text, response type
and finally .
Testing the parsing of an XML file
The following example will show a test that reads an ESPD Response
XML file and converts it into an ESPD domain
object that is used by the user interface code. The XML sample contains the Setup of economic operator
criterion
with the Year
requirement value set in the response.
class SetupEconomicOperatorImportTest extends AbstractXmlFileImport {
def "10. should import all fields of 'Set up of economic operator'"() {
when:
def espd = parseXmlResponseFile("selection/setup_economic_operator_import.xml") (1)
then:
espd.setupEconomicOperator.exists == true (2)
then: "selection criteria with no answer have a default value of true"
espd.setupEconomicOperator.year1 == 2016 (3)
}
}
1 | Load the XML sample from selection/setup_economic_operator_import.xml and parse it into an EspdDocument
domain object |
2 | Check that the exists indicator has been set for this criterion |
3 | Verify that the Quantity requirement response value stored in year1 is 2016 |
The XML file contains the Setup of economic operator
criterion with the Quantity
requirement (some of the data
has been removed for clarity):
<ccv:Criterion>
<ccv:RequirementGroup>
<cbc:ID schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">e9aa7763-c167-4352-8060-1a3d7d3e2662</cbc:ID>
<ccv:Requirement responseDataType="QUANTITY">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">a18b2c98-8552-45ca-9751-d4c94c05847a</cbc:ID>
<cbc:Description>Please specify</cbc:Description>
<ccv:Response>
<cbc:Quantity unitCode="YEAR">2016</cbc:Quantity>
</ccv:Response>
</ccv:Requirement>
</ccv:RequirementGroup>
</ccv:Criterion>
Data model
The ESPD-EDM model was designed to implement the data requirements expressed in the Annex 2
of the COMMISSION IMPLEMENTING
REGULATION (EU) 2016/7 of 5 January 2016
, establishing the standard form for the European Single Procurement Document
.
Additionally to these requirements, the model also took into account the Information Requirements Model
specified in the
CEN/BII-Workshops (namely Workshop 3), and the latest developments relating to the Virtual Company
Dossier (VCD)
in e-Sens.
The full data model documentation can be found at: ESPD Exchange Data Model documentation
The ESPD Request XML document overview
The UML Diagram below provides a simplified view of the ESPD Request
document. Notice that the classes herein represented
belong to 4 different data-packages. Consequently, each class name is preceded by one of the following prefixes representing
different namespaces: espd
, ccv
, cev
, and cac
.
The figure above provides a high level view of the main concepts used in the ESPD Request relating to the Criterion
entity. The UML diagram below, in turn, shows in detail the classes and attributes of the Core Criterion Data Model
used in the ESPD Request
XML document.
The UML diagram can be read as follows:
-
One Criterion contains one or more Groups of Requirements
-
One Criterion MAY have sub-Criteria
-
One Criterion MAY be linked to a specific Legislation.
-
One Group of Requirements contains one or more Requirements
In the ESPD documents a Criterion
takes the form of
a question or statement about a specific subject that may lead to the exclusion or selection of an
Economic Operator
in aProcurement Project
.
Thus, in the case of grounds for exclusion related to possible convictions, the question
Has the economic operator itself or any person who is a member of its administrative, management or supervisory body or has powers of representation, decision or control therein been the subject of a conviction by final judgement for participation in a criminal organisation, by a conviction rendered at the most five years ago or in which an exclusion period set out directly in the conviction continues to be applicable?
is to be considered a Criterion
.
Requirements
, on the other hand, relates to the way the Economic Operator
has to answer one specific Criterion
.
In the case of the exclusion Criterion
mentioned above, the Contracting Authority
requires the Economic Operator (EO)
to answer YES
or NO
, and if the EO
answers YES
, he is further required to provide additional details about
the conviction.
Java packages
The eu.europa.ec.grow.espd.exchange-model
Java library contains the XML schemas used to generate the JAXB annotated Java classes.
To include the ESPD Exchange Model classes inside your Java project just declare the following Maven dependency in your POM file:
<dependency>
<groupId>eu.europa.ec.grow.espd</groupId>
<artifactId>exchange-model</artifactId>
<version>1.0.2</version>
</dependency>
XML changes
Version 2017.05
Part V should not be shown in an open procedure #67
In case Part I: Information concerning the procurement procedure and the contracting authority or contracting entity
,
section Information about the procurement procedure
receives an additional member called Type of procedure
.
List of values and its impact on Part V specified below: * Not specified (because it may not be selected) * Open procedure * Restricted procedure (Part V will be shown) * Competitive procedure with negotiation (Part V will be shown) * Competitive dialogue (Part V will be shown) * Innovation partnership (Part V will be shown)
Code list for these values are taken from version 1.0.2 and specified below:
Values of Procedure Type:
Title | Value |
---|---|
Not specified |
null |
Open procedure |
1 |
Restricted procedure |
2 |
Competitive procedure with negotiation |
10 |
Competitive dialogue |
C |
Innovation partnership |
11 |
Procedure Type is not stored in XML.
In case "Part III: Exclusion Grounds", section "A: Grounds relating to criminal convictions", applying for all grounds regarding criminal convictions, there is no option to add (conviction) information for multiple representatives, in case of a conviction.
Requirement subgroups with id "f5276600-a2b6-4ff6-a90e-b31fe19dae41" became "unbounded" using functionality implemented for unbounded groups Able to add multiple references in selection criteria.
"subgroups": [
{
"name": "G1.1",
"id": "f5276600-a2b6-4ff6-a90e-b31fe19dae41",
"fulfillmentIndicator": "true",
"unbounded": "true"
}
]
This change provides possibility to implement multiple fields group: - Date of conviction - Reason - Who has been convicted - Length of the period of exclusion - Have you taken measures to demonstrate your reliability ("Self-Cleaning") - Description
<ccv:RequirementGroup>
<cbc:ID schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">7c637c0c-7703-4389-ba52-02997a055bd7</cbc:ID>
<ccv:Requirement responseDataType="INDICATOR">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">974c8196-9d1c-419c-9ca9-45bb9f5fd59a</cbc:ID>
<cbc:Description>Your answer?</cbc:Description>
<ccv:Response>
<ccv-cbc:Indicator>true</ccv-cbc:Indicator>
</ccv:Response>
</ccv:Requirement>
<ccv:RequirementGroup pi="GROUP_FULFILLED.ON_TRUE">
<cbc:ID schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">f5276600-a2b6-4ff6-a90e-b31fe19dae41</cbc:ID>
<ccv:Requirement responseDataType="DATE">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">ecf40999-7b64-4e10-b960-7f8ff8674cf6</cbc:ID>
<cbc:Description>Date of conviction</cbc:Description>
<ccv:Response>
<cbc:Date>2017-05-04</cbc:Date>
</ccv:Response>
</ccv:Requirement>
<ccv:Requirement responseDataType="DESCRIPTION">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">7d35fb7c-da5b-4830-b598-4f347a04dceb</cbc:ID>
<cbc:Description>Reason</cbc:Description>
<ccv:Response>
<cbc:Description>ert</cbc:Description>
</ccv:Response>
</ccv:Requirement>
<ccv:Requirement responseDataType="DESCRIPTION">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">c5012430-14da-454c-9d01-34cedc6a7ded</cbc:ID>
<cbc:Description>Who has been convicted</cbc:Description>
<ccv:Response>
<cbc:Description>rt</cbc:Description>
</ccv:Response>
</ccv:Requirement>
<ccv:Requirement responseDataType="PERIOD">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">9ca9096f-edd2-4f19-b6b1-b55c83a2d5c8</cbc:ID>
<cbc:Description>Length of the period of exclusion</cbc:Description>
<ccv:Response>
<cac:Period>
<cbc:Description>ert</cbc:Description>
</cac:Period>
</ccv:Response>
</ccv:Requirement>
<ccv:RequirementGroup>
<cbc:ID schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">5f9f09f7-f701-432c-9fdc-c22c124a74c9</cbc:ID>
<ccv:Requirement responseDataType="INDICATOR">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">20c5361b-7599-4ee6-b030-7f8323174d1e</cbc:ID>
<cbc:Description>Have you taken measures to demonstrate your reliability ("Self-Cleaning")?</cbc:Description>
<ccv:Response>
<ccv-cbc:Indicator>false</ccv-cbc:Indicator>
</ccv:Response>
</ccv:Requirement>
<ccv:RequirementGroup pi="GROUP_FULFILLED.ON_TRUE">
<cbc:ID schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">74e6c7b4-757b-4b40-ada6-fad6a997c310</cbc:ID>
<ccv:Requirement responseDataType="DESCRIPTION">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">7b07904f-e080-401a-a3a1-9a3efeeda54b</cbc:ID>
<cbc:Description>Please describe them</cbc:Description>
<ccv:Response/>
</ccv:Requirement>
</ccv:RequirementGroup>
</ccv:RequirementGroup>
</ccv:RequirementGroup>
<ccv:RequirementGroup pi="GROUP_FULFILLED.ON_TRUE">
<cbc:ID schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">f5276600-a2b6-4ff6-a90e-b31fe19dae41</cbc:ID>
<ccv:Requirement responseDataType="DATE">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">ecf40999-7b64-4e10-b960-7f8ff8674cf6</cbc:ID>
<cbc:Description>Date of conviction</cbc:Description>
<ccv:Response>
<cbc:Date>2017-05-04</cbc:Date>
</ccv:Response>
</ccv:Requirement>
<ccv:Requirement responseDataType="DESCRIPTION">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">7d35fb7c-da5b-4830-b598-4f347a04dceb</cbc:ID>
<cbc:Description>Reason</cbc:Description>
<ccv:Response>
<cbc:Description>ert</cbc:Description>
</ccv:Response>
</ccv:Requirement>
<ccv:Requirement responseDataType="DESCRIPTION">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">c5012430-14da-454c-9d01-34cedc6a7ded</cbc:ID>
<cbc:Description>Who has been convicted</cbc:Description>
<ccv:Response>
<cbc:Description>rt</cbc:Description>
</ccv:Response>
</ccv:Requirement>
<ccv:Requirement responseDataType="PERIOD">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">9ca9096f-edd2-4f19-b6b1-b55c83a2d5c8</cbc:ID>
<cbc:Description>Length of the period of exclusion</cbc:Description>
<ccv:Response>
<cac:Period>
<cbc:Description>ert</cbc:Description>
</cac:Period>
</ccv:Response>
</ccv:Requirement>
<ccv:RequirementGroup>
<cbc:ID schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">5f9f09f7-f701-432c-9fdc-c22c124a74c9</cbc:ID>
<ccv:Requirement responseDataType="INDICATOR">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">20c5361b-7599-4ee6-b030-7f8323174d1e</cbc:ID>
<cbc:Description>Have you taken measures to demonstrate your reliability ("Self-Cleaning")?</cbc:Description>
<ccv:Response>
<ccv-cbc:Indicator>false</ccv-cbc:Indicator>
</ccv:Response>
</ccv:Requirement>
<ccv:RequirementGroup pi="GROUP_FULFILLED.ON_TRUE">
<cbc:ID schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">74e6c7b4-757b-4b40-ada6-fad6a997c310</cbc:ID>
<ccv:Requirement responseDataType="DESCRIPTION">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">7b07904f-e080-401a-a3a1-9a3efeeda54b</cbc:ID>
<cbc:Description>Please describe them</cbc:Description>
<ccv:Response/>
</ccv:Requirement>
</ccv:RequirementGroup>
</ccv:RequirementGroup>
</ccv:RequirementGroup>
</ccv:RequirementGroup>
In Part I (Information About Publication section) we need to provide the possibility to add a national reference number to help economic operators in a country to identify it on a national portal.
To fulfill this requirement we need to add a new XML element as in the following example:
<cac:AdditionalDocumentReference>
<cbc:ID schemeAgencyID="EU-COM-GROW" schemeAgencyName="DG GROW (European Commission)" schemeVersionID="1.1">The national number here</cbc:ID>
<cbc:DocumentTypeCode listID="ReferencesTypeCodes" listAgencyID="EU-COM-GROW" listVersionID="1.0">NATIONAL_NUMBER</cbc:DocumentTypeCode>
<cac:Attachment>
<cac:ExternalReference>
<cbc:Description>-</cbc:Description>
</cac:ExternalReference>
</cac:Attachment>
</cac:AdditionalDocumentReference>
In Part IV (Selection Criteria) Section B (Economic and Financial Standing), currently the economic operator can only specify a Year for the following requirements:
1 | General Yearly Turnover and |
2 | Specific Yearly Turnover |
This change is about allowing the EO to specify instead the fiscal year by selecting a Start Date and an End Date. In the new definition the QUANTITY_YEAR field will be replaced by a START_DATE and END_DATE fields as follows:
"groups": [
{
"name": "G1",
"id": "c0cd9c1c-e90a-4ff9-bce3-ac0fe31abf16",
"unbounded": "true",
"requirements": [
{
"id": "42ec8116-31a7-4118-8612-5b04f5c8bde7",
"description": "Start Date",
"responseType": "DATE",
"espdCriterionFields": [
"startDate"
]
},
{
"id": "3641b897-f9f0-4d90-909a-b6d4c4b1d645",
"description": "End Date",
"responseType": "DATE",
"espdCriterionFields": [
"endDate"
]
},
{
"id": "42db0eaa-d2dd-48cb-83ac-38d73cab9b50",
"description": "Amount",
"responseType": "AMOUNT",
"espdCriterionFields": [
"amount",
"currency"
]
}
]
},
For both the EO and the representatives of the EO a postcode can be specified. In case of the EO, this information is represented by the XML element espd-cac:ContractingParty.Party.PostalAddress.Postbox, in case of the EO representative the element espd-cac:EconomicOperatorParty.Party.Person.ResidenceAddress.Postbox is used.
As usage of the Postbox element in semantically wrong in both cases (i.e. a postbox number refers to a post office box registered for postal delivery by a postal service provider). The element […].PostalZone will be used instead.
Example:
<espd:ESPDResponse>
.....
<espd-cac:EconomicOperatorParty>
<espd-cbc:SMEIndicator>false</espd-cbc:SMEIndicator>
<espd-cac:RepresentativeNaturalPerson>
<cac:PowerOfAttorney>
<cac:AgentParty>
<cac:Person>
<cac:Contact/>
<cac:ResidenceAddress/>
</cac:Person>
</cac:AgentParty>
</cac:PowerOfAttorney>
</espd-cac:RepresentativeNaturalPerson>
<cac:Party>
<cac:PartyName>
<cbc:Name></cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:PostalZone>1050</cbc:PostalZone>(1)
<cac:Country>
<cbc:IdentificationCode listID="CountryCodeIdentifier" listAgencyID="EU-COM-GROW" listName="CountryCodeIdentifier" listVersionID="1.0.2">GR</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:Contact/>
</cac:Party>
</espd-cac:EconomicOperatorParty>
...
</espd:ESPDResponse>
1 | XML element Postbox has been replaced by PostalZone |
The xml element cbc:VersionID until now contains ESPD Service version. This will be modified so that xml element cbc:VersionID will contain ESPD Data Exchange Model version.
Example:
<espd:ESPDResponse>
.....
<cbc:VersionID schemeAgencyID="EU-COM-GROW">1.0.2</cbc:VersionID> (1)
.....
</espd:ESPDResponse>
1 | XML element cbc:VersionID contains the ESPD Data Exchange Model version. |
Version 2016.12
The possibility of adding an unlimited number of criterion references for certain criteria introduces a new way of
defining them. Each requirement group that can be repeated as many times as desired (called henceforth unbounded
requirement group
) will be defined only once in the criterion definition and its definition will be cloned as many
times as necessary. This means that the requirement group definition will appear only once in the ESPD Request
while
on the ESPD Response
it can be present multiple times.
The new definition of the selection criteria from part C (For works contracts: performance of works of the specified
type
, For supply contracts: performance of deliveries of the specified type
, For service contracts: performance of
services of the specified type
will contain only two groups, as follows:
"groups": [
{
"name": "G1",
"id": "96f00020-0a25-402e-b850-2378e83b5695",
"unbounded": "true",
"requirements": [
{
"id": "ab05ff3b-f3e1-4441-9b43-ee9912e29e92",
"description": "Description",
"responseType": "DESCRIPTION"
},
{
"id": "42db0eaa-d2dd-48cb-83ac-38d73cab9b50",
"description": "Amount",
"responseType": "AMOUNT"
},
{
"id": "42ec8116-31a7-4118-8612-5b04f5c8bde7",
"description": "Start Date",
"responseType": "DATE"
},
{
"id": "3641b897-f9f0-4d90-909a-b6d4c4b1d645",
"description": "End Date",
"responseType": "DATE
},
{
"id": "a92536ab-6783-40bb-a037-5d31f421fd85",
"description": "Recipients",
"responseType": "DESCRIPTION"
}
]
},
{
"name": "G2",
"id": "9026e403-3eb6-4705-a9e9-e21a1efc867d",
"requirements": [
{
"id": "9dae5670-cb75-4c97-901b-96ddac5a633a",
"description": "Is this information available electronically?",
"responseType": "INDICATOR"
}
],
"subgroups": [
{
"name": "G2.1",
"id": "0a166f0a-0c5f-42b0-81e9-0fc9fa598a48",
"fulfillmentIndicator": "true",
"requirements": [
{
"id": "03bb1954-13ae-47d8-8ef8-b7fe0f22d700",
"description": "URL",
"responseType": "EVIDENCE_URL"
},
{
"id": "e2d863a0-60cb-4e58-8c14-4c1595af48b7",
"description": "Code",
"responseType": "CODE"
},
{
"id": "5cbf74d9-a1e2-4233-921d-8b298842ee7d",
"description": "Issuer",
"responseType": "DESCRIPTION"
}
]
}
]
}
}
Please notice that we don’t have 5 groups with the same structure but different ids anymore, but only one, G1
, and
this group has a new property, unbounded
set to true
. This property will not be saved in the XML files but is used
internally by the ESPD application
to handle this scenario.
In order to support the ESPD Responses
generated by versions prior to 2016.12
we need to define some mappings so
that the old requirement group and requirement ids point only to one primary group or requirement. Whenever we encounter
one of the ids from idsToBeReplaced
we will use the entity referenced by the replacementId
.
"requirementGroupMappings": [
{
"replacementId": "96f00020-0a25-402e-b850-2378e83b5695", // use only this requirement group from now on
"idsToBeReplaced": [ // whenever we see these requirement groups we will use the new one above
"c48572f9-47bf-423a-9885-2c78ae9ca718",
"2c7a3581-2954-4142-8c1b-5c52d7c7e9b7",
"d67a6126-dd6d-4ed2-bda7-214a19e13a63",
"159fc086-cf34-48a4-a41b-afed62661383"
]
}
],
"requirementMappings": [
{ // mapping for 'Description'
"replacementId": "ab05ff3b-f3e1-4441-9b43-ee9912e29e92", // use only this requirement from now on
"idsToBeReplaced": [ // whenever we see these requirements we will use the new one above
"927def36-1fa3-4018-8b45-7ee2c5b1e0af",
"e6ca4034-cfee-499a-9a47-c4f2862ef4d0",
"b1640c24-b405-443e-bf5e-d7771f66aab6",
"587129bc-a5e1-43be-94ac-6e5366d30c67"
]
},
{ // mapping for 'Amount'
"replacementId": "42db0eaa-d2dd-48cb-83ac-38d73cab9b50",
"idsToBeReplaced": [
"4acd0a02-c267-4d05-b456-c0565c2ffd46",
"28fb4b41-5178-4b79-ba24-d9a62fa4a658",
"9f278e42-aa1d-4b2e-97cd-832248aa5393",
"cc1a0b1e-dbfd-4313-a4fb-2e543b05549b"
]
},
{ // mapping for 'Start date'
"replacementId": "42ec8116-31a7-4118-8612-5b04f5c8bde7",
"idsToBeReplaced": [
"8d0e5e16-85ed-4730-a784-d4db8f439c0c",
"c953e635-580b-4d7c-a30c-2edbde3b8fdf",
"9b263b45-fc63-4b01-a3dc-cb9c95dda449",
"056cba1d-986b-4164-92b6-26a1cbdf0690"
]
},
{ // mapping for 'End date'
"replacementId": "3641b897-f9f0-4d90-909a-b6d4c4b1d645",
"idsToBeReplaced": [
"4c842551-fb07-4a13-91e6-5653820f7e80",
"822934ff-da94-40d2-a799-f29ba7bba2b0",
"7a95ddbd-05e8-4af4-973f-1b8d05f71e0f",
"dd71df86-3ad5-42dd-add5-9bd51dc88f05"
]
},
{ // mapping for 'Recipients'
"replacementId": "a92536ab-6783-40bb-a037-5d31f421fd85",
"idsToBeReplaced": [
"c8babafa-b6fa-4e14-8749-d913d8f1d33b",
"5157e1ff-d272-4382-98a9-6953f5a15300",
"a84ea948-cf03-47b5-b4cf-a35f49910d10",
"38a4802f-0b93-4e78-ad4e-2a057e1aa578"
]
}
]
This update will affect the Economic Operator criterion EO registered
with id 9b19e869-6c89-4cc4-bd6c-ac9ca8602165
.
The requirement Not applicable
with id 67fd1dde-2a0a-486e-9469-79c78796fc22
will be removed from the parent
criterion and will not be displayed and understood anymore by the ESPD service.
All the Information is available electronically
requirement groups will contain one extra requirement, called Issuer
.
Please keep in mind that this modification will affect a lot of criteria.
For the exclusion criteria, the requirement will have this definition:
{
"id": "c3ccfa31-0c5e-4e3a-a3fd-db9fb83d78d4",
"description": "Issuer",
"responseType": "DESCRIPTION"
}
And for the selection criteria, the requirement will include the following definition:
{
"id": "5cbf74d9-a1e2-4233-921d-8b298842ee7d",
"description": "Issuer",
"responseType": "DESCRIPTION"
}
The economic operator (other) criteria will contain the requirement below:
{
"id": "d8e1e818-d67b-4bb9-9aeb-4c10943a8342",
"description": "Issuer",
"responseType": "DESCRIPTION"
}
A requirement for Description
has to be added to the Conflict of interest due to its participation in the procurement
procedure
exclusion criterion, inside a requirement group G1.1.
"subgroups": [
{
"name": "G1.1",
"id": "73f0fe4c-4ed9-4343-8096-d898cf200146",
"fulfillmentIndicator": "true",
"requirements": [
{
"id": "e098da8e-4717-4500-965f-f882d5b4e1ad",
"description": "Please describe them",
"responseType": "DESCRIPTION",
}
]
}
]
We need to add the self-cleaning
subgroup to the section D exclusion criterion Purely national grounds
.
Consequently, the G1.1 criterion group will contain the following subgroup structure:
{
"name": "G1.1.1",
"id": "5f9f09f7-f701-432c-9fdc-c22c124a74c9",
"requirements": [
{
"id": "20c5361b-7599-4ee6-b030-7f8323174d1e",
"description": "Have you taken measures to demonstrate your reliability (\"Self-Cleaning\")?",
"responseType": "INDICATOR"
}
],
"subgroups": [
{
"name": "G1.1.1.1",
"id": "74e6c7b4-757b-4b40-ada6-fad6a997c310",
"fulfillmentIndicator": "true",
"requirements": [
{
"id": "7b07904f-e080-401a-a3a1-9a3efeeda54b",
"description": "Please describe them",
"responseType": "DESCRIPTION"
}
]
}
]
}
The structure of the General yearly turnover
and Specific yearly turnover
selection criteria has been simplified.
They don’t need the following requirements:
-
Your answer?
with id15335c12-ad77-4728-b5ad-3c06a60d65a4
-
Please provide the requested data below
with id3a6fefd4-f458-4d43-97fb-0725fce5dce2
Subsequently, the G1 group e1886054-ada4-473c-9afc-2fde82c24cf4
and its internal structure have disappeared.
The new structure looks like this:
"groups": [
{
"name": "G1",
"id": "c0cd9c1c-e90a-4ff9-bce3-ac0fe31abf16",
"requirements": [
{
"id": "5aacceb3-280e-42f1-b2da-3d8ac7877fe9",
"description": "Year",
"responseType": "QUANTITY_YEAR"
},
{
"id": "42db0eaa-d2dd-48cb-83ac-38d73cab9b50",
"description": "Amount",
"responseType": "AMOUNT"
}
]
},
{
"name": "G2",
"id": "99c9d014-d0e1-473d-b6d4-a8549f2b19fa",
"requirements": [
{
"id": "49a57870-7fb8-451f-a7af-fa0e7f8b97e7",
"description": "Year",
"responseType": "QUANTITY_YEAR"
},
{
"id": "4acd0a02-c267-4d05-b456-c0565c2ffd46",
"description": "Amount",
"responseType": "AMOUNT"
}
]
},
{
"name": "G3",
"id": "9f0e291f-05c9-4cb6-bc50-4c2d3b2049b2",
"requirements": [
{
"id": "9d0cf1cb-27bc-4747-8579-47dce4d8d490",
"description": "Year",
"responseType": "QUANTITY_YEAR"
},
{
"id": "28fb4b41-5178-4b79-ba24-d9a62fa4a658",
"description": "Amount",
"responseType": "AMOUNT"
}
]
},
{
"name": "G4",
"id": "67b8d7fa-a0aa-43d6-a30b-e15b95326df2",
"requirements": [
{
"id": "17a7353d-a7a4-43ee-9cc8-b9db83eeafb3",
"description": "Year",
"responseType": "QUANTITY_YEAR"
},
{
"id": "9f278e42-aa1d-4b2e-97cd-832248aa5393",
"description": "Amount",
"responseType": "AMOUNT"
}
]
},
{
"name": "G5",
"id": "c8c09a0c-b7a7-4271-bb6a-80f1c0e988f7",
"requirements": [
{
"id": "34825634-5151-4e31-af1b-7eafadcf15be",
"description": "Year",
"responseType": "QUANTITY_YEAR"
},
{
"id": "cc1a0b1e-dbfd-4313-a4fb-2e543b05549b",
"description": "Amount",
"responseType": "AMOUNT"
}
]
},
{
"name": "G6",
"id": "9026e403-3eb6-4705-a9e9-e21a1efc867d",
"requirements": [
{
"id": "9dae5670-cb75-4c97-901b-96ddac5a633a",
"description": "Is this information available electronically?",
"responseType": "INDICATOR"
}
],
"subgroups": [
{
"name": "G2.1",
"id": "0a166f0a-0c5f-42b0-81e9-0fc9fa598a48",
"fulfillmentIndicator": "true",
"requirements": [
{
"id": "03bb1954-13ae-47d8-8ef8-b7fe0f22d700",
"description": "URL",
"responseType": "EVIDENCE_URL"
},
{
"id": "e2d863a0-60cb-4e58-8c14-4c1595af48b7",
"description": "Code",
"responseType": "CODE"
}
]
}
]
}
]
}
The Average yearly turnover
and Specific average turnover
criteria should match the paper version.
Therefore, the structure of their G1 group with id e1886054-ada4-473c-9afc-2fde82c24cf4
changes completely.
They will contain only two requirements on the first group, Number of years
and Average turnover
.
{
"name": "G1",
"id": "e1886054-ada4-473c-9afc-2fde82c24cf4",
"requirements": [
{
"id": "b98ffd05-6572-4b07-a521-693a1754ed46",
"description": "Number of years",
"responseType": "QUANTITY_INTEGER"
},
{
"id": "217637ba-6bdb-4c73-a38f-27fe0e71d9be",
"description": "Average turnover",
"responseType": "AMOUNT"
}
]
}
The following criteria will need to contain two date requirements instead of one: Start date
and End date
.
-
For works contracts: performance of works of the specified type
; -
For supply contracts: performance of deliveries of the specified type
; -
For service contracts: performance of services of the specified type
.
Please keep in mind that the other requirements will remain, only the Date
requirement will be split into two new ones.
"groups": [
{
"name": "G1",
"id": "96f00020-0a25-402e-b850-2378e83b5695",
"requirements": [
...,
{
"id": "42ec8116-31a7-4118-8612-5b04f5c8bde7",
"description": "Start Date",
"responseType": "DATE"
},
{
"id": "3641b897-f9f0-4d90-909a-b6d4c4b1d645",
"description": "End Date",
"responseType": "DATE"
},
...
]
},
{
"name": "G2",
"id": "c48572f9-47bf-423a-9885-2c78ae9ca718",
"requirements": [
...,
{
"id": "8d0e5e16-85ed-4730-a784-d4db8f439c0c",
"description": "Start Date",
"responseType": "DATE"
},
{
"id": "4c842551-fb07-4a13-91e6-5653820f7e80",
"description": "End Date",
"responseType": "DATE"
},
...
]
},
{
"name": "G3",
"id": "2c7a3581-2954-4142-8c1b-5c52d7c7e9b7",
"requirements": [
...,
{
"id": "c953e635-580b-4d7c-a30c-2edbde3b8fdf",
"description": "Start Date",
"responseType": "DATE"
},
{
"id": "822934ff-da94-40d2-a799-f29ba7bba2b0",
"description": "End Date",
"responseType": "DATE"
},
...
]
},
{
"name": "G4",
"id": "d67a6126-dd6d-4ed2-bda7-214a19e13a63",
"requirements": [
...,
{
"id": "9b263b45-fc63-4b01-a3dc-cb9c95dda449",
"description": "Start Date",
"responseType": "DATE"
},
{
"id": "7a95ddbd-05e8-4af4-973f-1b8d05f71e0f",
"description": "End Date",
"responseType": "DATE"
},
...
]
},
{
"name": "G5",
"id": "159fc086-cf34-48a4-a41b-afed62661383",
"requirements": [
...,
{
"id": "056cba1d-986b-4164-92b6-26a1cbdf0690",
"description": "Start Date",
"responseType": "DATE"
},
{
"id": "dd71df86-3ad5-42dd-add5-9bd51dc88f05",
"description": "End Date",
"responseType": "DATE"
},
...
]
}
Version 2016.08
Part II - Information concerning the economic operator
, section A - Information about the economic operator
contains the VAT number if applicable
and If no VAT number is applicable, please indicate another national
identification number, if required and applicable
fields.
In order to be able to distinguish between the VAT Number
and National Number
in the XML we are now using the
schemeID
attribute of the cac:EconomicOperatorParty.cac:Party.cac:PartyIdentification
element. The two possible
values which should be used are:
-
VAT_Number
-
National_Number
Example:
<espd-cac:EconomicOperatorParty>
<espd-cbc:SMEIndicator>false</espd-cbc:SMEIndicator>
<espd-cac:RepresentativeNaturalPerson>
<cac:PowerOfAttorney>
<cac:AgentParty>
<cac:Person>
<cac:Contact/>
<cac:ResidenceAddress/>
</cac:Person>
</cac:AgentParty>
</cac:PowerOfAttorney>
</espd-cac:RepresentativeNaturalPerson>
<cac:Party>
<cac:PartyIdentification>
<cbc:ID schemeAgencyID="EU-COM-GROW" schemeID="National_Number">123456</cbc:ID> (1)
</cac:PartyIdentification>
<cac:PartyIdentification>
<cbc:ID schemeAgencyID="EU-COM-GROW" schemeID="VAT_Number">BE0999999999</cbc:ID> (2)
</cac:PartyIdentification>
<cac:PartyName>
<cbc:Name>Dell</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cac:Country>
<cbc:IdentificationCode listID="CountryCodeIdentifier" listAgencyID="EU-COM-GROW" listName="CountryCodeIdentifier" listVersionID="1.0.2">AT</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:Contact/>
</cac:Party>
</espd-cac:EconomicOperatorParty>
1 | The national number |
2 | The VAT number |
The id of the G1.1
group belonging to the Part III - Exclusion grounds
, section A - Grounds relating to
criminal convictions
has been changed to f5276600-a2b6-4ff6-a90e-b31fe19dae41
due to a conflict with the
G2.1(URL/Code)
group of the Information available electronically
parent group.
Example:
<ccv:RequirementGroup pi="GROUP_FULFILLED.ON_TRUE">
<cbc:ID schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">f5276600-a2b6-4ff6-a90e-b31fe19dae41</cbc:ID>
<ccv:Requirement responseDataType="DATE">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">ecf40999-7b64-4e10-b960-7f8ff8674cf6</cbc:ID>
<cbc:Description>Date of conviction</cbc:Description>
<ccv:Response>
<cbc:Date>2015-12-09</cbc:Date>
</ccv:Response>
</ccv:Requirement>
...
</ccv:RequirementGroup>
Version 2016.07
A new field called Consortium name
was added in Part II - Information concerning the economic operator
,
Section C - Information about reliance on the capacities of other entities
.
The information is saved inside the <espd-cbc:EconomicOperatorGroupName/>
element on an ESPD Response
.
Example:
<espd:ESPDResponse>
<cbc:UBLVersionID schemeAgencyID="OASIS-UBL-TC">2.1</cbc:UBLVersionID>
<cbc:CustomizationID schemeName="CustomizationID" schemeAgencyID="BII" schemeVersionID="3.0">urn:www.cenbii.eu:transaction:biitrns092:ver3.0</cbc:CustomizationID>
<cbc:ID schemeID="ISO/IEC 9834-8:2008 - 4UUID" schemeAgencyID="EU-COM-GROW" schemeAgencyName="DG GROW (European Commission)" schemeVersionID="1.1">3679123f-de23-4703-8161-cf6c8d9b8ad9</cbc:ID>
<cbc:CopyIndicator>false</cbc:CopyIndicator>
<cbc:VersionID schemeAgencyID="EU-COM-GROW">2016.08.01</cbc:VersionID>
<cbc:IssueDate>2016-05-30</cbc:IssueDate>
<cbc:IssueTime>00:00:00</cbc:IssueTime>
<cbc:ContractFolderID schemeAgencyID="TeD">SMART 2015/0065</cbc:ContractFolderID>
<espd-cbc:EconomicOperatorGroupName>Hodor consortium</espd-cbc:EconomicOperatorGroupName> (1)
...
</espd:ESPDResponse>
1 | The name of the consortium is saved here |
These are some changes affecting only the ESPD
application which fixed some correctness issues regarding the XMLs
generated by the application.
-
Update country codes for version 1.0.2 of the data model, the new values are below.
then: "check address information"
result.ContractingParty.Party.PostalAddress.Country.IdentificationCode.@listAgencyID.text() == "EU-COM-GROW"
result.ContractingParty.Party.PostalAddress.Country.IdentificationCode.@listName.text() == "CountryCodeIdentifier"
result.ContractingParty.Party.PostalAddress.Country.IdentificationCode.@listVersionID.text() == "1.0.2"
-
Update criterion and jurisdiction codes for version 1.0.2 of the data model, the new values are below.
assert request.Criterion[idx].TypeCode.@listVersionID.text() == "1.0.2"
assert ref.JurisdictionLevelCode.@listVersionID.text() == "1.0.2"
-
Update
Study and research facilities
andEducational and professional qualifications
criterion type codes to match version 1.0.2 of the data model, the new values are below.
checkCriterionTypeCode(request, idx,
"CRITERION.SELECTION.TECHNICAL_PROFESSIONAL_ABILITY.TECHNICAL.PROFESSIONAL_QUALIFICATIONS")
checkCriterionTypeCode(request, idx,
"CRITERION.SELECTION.TECHNICAL_PROFESSIONAL_ABILITY.TECHNICAL.FACILITIES_FOR_STUDY_RESEARCH")
Version 2016.06.1
The id of the Please specify
requirement belonging to the G1
group of the Subcontracting proportion
criterion was
changed due to a conflict with other existing ids. The new id is 15778db8-0d84-42ba-931b-774c1b3d3f9f
.
then: "main sub group"
def g1 = response.Criterion[idx].RequirementGroup[0]
g1.ID.text() == "575f7550-8a2d-4bad-b9d8-be07ab570076"
g1.@pi.text() == ""
g1.RequirementGroup.size() == 0
g1.Requirement.size() == 1
checkRequirement(g1.Requirement[0], "15778db8-0d84-42ba-931b-774c1b3d3f9f", "Please specify", "DESCRIPTION")
On Part VI - Concluding statements
we added two new fields, Date
and Place
.
<espd:ESPDResponse>
...
<cbc:IssueDate>2015-11-25</cbc:IssueDate> (1)
<cbc:IssueTime>13:19:20</cbc:IssueTime>
...
<cac:Signature>
<cbc:ID>a47fe139-f2b1-4886-9c01-70033ad82fcb</cbc:ID>
<cac:SignatoryParty>
<cac:PhysicalLocation>
<cbc:Name>Eastwatch by the Sea</cbc:Name> (2)
</cac:PhysicalLocation>
</cac:SignatoryParty>
</cac:Signature>
...
</espd:ESPDResponse>
1 | The Date information is saved here |
2 | The Place information is saved inside the Signature element |
The list of countries used by the ESPD application can be found here: https://github.com/ESPD/ESPD-Service/blob/master/espd-web/src/main/java/eu/europa/ec/grow/espd/domain/enums/other/Country.java
The list of currencies used by the ESPD application can be found here: https://github.com/ESPD/ESPD-Service/blob/master/espd-web/src/main/java/eu/europa/ec/grow/espd/domain/enums/other/Currency.java
Version 2016.06
-
The
Name
andDescription
of theOther criteria
have been swapped.
def response = parseResponseXml(espd)
def idx = getEoCriterionIndex(AwardCriterion.EO_PARTICIPATING_PROCUREMENT_PROCEDURE)
then: "CriterionID element"
checkCriterionId(response, idx, "ee51100f-8e3e-40c9-8f8b-57d5a15be1f2")
then: "CriterionTypeCode element"
checkCriterionTypeCode(response, idx, "DATA_ON_ECONOMIC_OPERATOR")
then: "CriterionName element"
response.Criterion[idx].Name.text() == "EO participating in procurement procedure" (1)
then: "CriterionDescription element"
response.Criterion[idx].Description.text() ==
"Is the economic operator participating in the procurement procedure together with others?" (2)
then: "check all the sub groups"
response.Criterion[idx].RequirementGroup.size() == 1
1 | The name (which used to be empty) is present now and goes here |
2 | The old name is now stored in the Description element |
then: "CriterionID element"
checkCriterionId(response, idx, "9b19e869-6c89-4cc4-bd6c-ac9ca8602165")
then: "CriterionTypeCode element"
checkCriterionTypeCode(response, idx, "DATA_ON_ECONOMIC_OPERATOR")
then: "CriterionName element"
response.Criterion[idx].Name.text() == "EO registered"
then: "first sub group requirements"
def r1_0 = response.Criterion[idx].RequirementGroup[0].Requirement[0]
checkRequirement(r1_0, "67fd1dde-2a0a-486e-9469-79c78796fc22", "Not applicable", "INDICATOR")
def r1_1 = response.Criterion[idx].RequirementGroup[0].Requirement[1]
checkRequirement(r1_1, "7f18c64e-ae09-4646-9400-f3666d50af51", "", "INDICATOR")
-
Restructure the requirement groups according to the VCD proposed solution
All the criteria were restructured into new requirement groups and the <ccv:RequirementGroup pi="GROUP_FULFILLED.ON_TRUE">
logic has been introduced. More information about the criteria and requirement groups instantiation can be found here:
The results of all the changes performed on this issue are reflected in the criterion configuration files below:
Version 2016.05
In case Part I - Information concerning the procurement procedure and the contracting authority or contracting entity
,
section Information about publication
is missing the Notice number in the OJS
field, when we want to save the
ESPD Request/Response
we need to provide an ID
to the parent AdditionalDocumentReference
element because the ID
is mandatory. We will use the value 0000/S 000-000000 to represent the lack of the notice number in the OJS.
The generation of the TED information inside the ESPD Request
in this case should look like the test below.
def "should contain AdditionalDocumentReference with default ID if the TED OJS number is missing"() {
given:
def espd = new EspdDocument(ojsNumber: " ", tedReceptionId: " ", (1)
procedureTitle: "Belgium-Brussels: SMART 2015/0065 — Benchmarking deployment of eHealth among general practitioners 2015",
procedureShortDesc: "Service category No 11: Management consulting services [6] and related services.",
tedUrl: "http://ted.europa.eu/udl?uri=TED:NOTICE:002226-2016:TEXT:ES:HTML")
when:
def result = parseRequestXml(espd)
then:
result.AdditionalDocumentReference.size() == 1
then:
result.AdditionalDocumentReference[0].ID.text() == "0000/S 000-000000" (2)
result.AdditionalDocumentReference[0].ID.@schemeID.text() == "COM-GROW-TEMPORARY-ID" (3)
result.AdditionalDocumentReference[0].ID.@schemeAgencyID.text() == "EU-COM-GROW"
result.AdditionalDocumentReference[0].ID.@schemeAgencyName.text() == "DG GROW (European Commission)"
result.AdditionalDocumentReference[0].ID.@schemeVersionID.text() == "1.1"
then:
result.AdditionalDocumentReference[0].DocumentTypeCode.@listAgencyID.text() == "EU-COM-GROW"
result.AdditionalDocumentReference[0].DocumentTypeCode.@listID.text() == "ReferencesTypeCodes"
result.AdditionalDocumentReference[0].DocumentTypeCode.@listVersionID.text() == "1.0"
result.AdditionalDocumentReference[0].DocumentTypeCode.text() == "TED_CN"
then:
result.AdditionalDocumentReference[0].Attachment.ExternalReference.FileName.text() == "Belgium-Brussels: SMART 2015/0065 — Benchmarking deployment of eHealth among general practitioners 2015"
result.AdditionalDocumentReference[0].Attachment.ExternalReference.Description[0].text() == "Service category No 11: Management consulting services [6] and related services."
result.AdditionalDocumentReference[0].Attachment.ExternalReference.URI.text() == "http://ted.europa.eu/udl?uri=TED:NOTICE:002226-2016:TEXT:ES:HTML"
}
1 | The notice number in the OJS is missing (empty in this case) |
2 | This value should be saved in the ESPD Request |
3 | The schemeID should be COM-GROW-TEMPORARY-ID |
The test below imports an ESPD Request
with such a temporary notice number and checks that the information
is ignored in this case.
</espd-req:ESPDRequest>
...
<cbc:ContractFolderID schemeAgencyID="TeD">SMART 2016/0069</cbc:ContractFolderID>
...
<!-- For procurement projects above the threshold it is compulsory to specify the following data, by means of an AdditionalDocumentReference element, about the Contract Notice published in TeD: the OJEU S number[], date[], page[], Notice number in OJS: YYYY/S [][][]-[][][][][][], Title and Description of the Procurement Project -->
<cac:AdditionalDocumentReference>
<cbc:ID schemeID="COM-GROW-TEMPORARY-ID" schemeAgencyID="EU-COM-GROW" (1)
schemeAgencyName="DG GROW (European Commission)" schemeVersionID="1.1">0000/S 000-000000</cbc:ID> (2)
<cbc:DocumentTypeCode listAgencyID="EU-COM-GROW" listID="ReferencesTypeCodes" listVersionID="1.0">TED_CN</cbc:DocumentTypeCode>
<cac:Attachment>
<cac:ExternalReference>
<cbc:URI>http://ted.europa.eu/udl?uri=TED:NOTICE:373035-2015:TEXT:EN:HTML</cbc:URI>
<!-- Title of the Contract Notice -->
<cbc:FileName>Belgium-Brussels: SMART 2015/0065 — Benchmarking deployment of eHealth among general practitioners 2015</cbc:FileName>
<!-- Short description of the Procurement Project -->
<cbc:Description>Service category No 11: Management consulting services [6] and related services.</cbc:Description>
<cbc:Description>16-000136-001</cbc:Description>
</cac:ExternalReference>
</cac:Attachment>
</cac:AdditionalDocumentReference>
...
</espd-req:ESPDRequest>
1 | The code used in this special case |
2 | The value used to represent a missing notice number |
def "we should not load the ojs number if it is marked as a temporary one"() {
given:
def espdXml = importXmlRequestFile("request_temporary_ojs_number_import.xml")
EspdDocument espd = marshaller.importEspdRequest(IOUtils.toInputStream(espdXml)).get()
expect:
espd.fileRefByCA == "SMART 2016/0069"
espd.ojsNumber == null (1)
espd.procedureTitle == "Belgium-Brussels: SMART 2015/0065 — Benchmarking deployment of eHealth among general practitioners 2015"
espd.procedureShortDesc == "Service category No 11: Management consulting services [6] and related services."
espd.tedUrl == "http://ted.europa.eu/udl?uri=TED:NOTICE:373035-2015:TEXT:EN:HTML"
espd.tedReceptionId == "16-000136-001"
}
1 | The notice number in the OJS should be ignored in this case |
Version 2016.04.01
The exclusion criterion Conflict of interest due to its participation in the procurement procedure
needs only the
Yes/No
requirement and not the rest.
<ccv:Criterion>
<cbc:ID schemeID="CriteriaID" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">b1b5ac18-f393-4280-9659-1367943c1a2e</cbc:ID>
<cbc:TypeCode listID="CriteriaTypeCode" listAgencyID="EU-COM-GROW" listVersionID="1.0.2">CRITERION.EXCLUSION.CONFLICT_OF_INTEREST.PROCEDURE_PARTICIPATION</cbc:TypeCode>
<cbc:Name>Conflict of interest due to its participation in the procurement procedure</cbc:Name>
...
<ccv:RequirementGroup>
<cbc:ID schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">30450436-f559-4dfa-98ba-f0842ed9d2a0</cbc:ID>
<ccv:Requirement responseDataType="INDICATOR">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">974c8196-9d1c-419c-9ca9-45bb9f5fd59a</cbc:ID>
<cbc:Description>Your answer?</cbc:Description>
<ccv:Response>
<ccv-cbc:Indicator>false</ccv-cbc:Indicator>
</ccv:Response>
</ccv:Requirement>
</ccv:RequirementGroup>
</ccv:Criterion>
The Guilty of misinterpretation
exclusion criterion only needs the Yes/No
requirement.
<ccv:Criterion>
<cbc:ID schemeID="CriteriaID" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">696a75b2-6107-428f-8b74-82affb67e184</cbc:ID>
<cbc:TypeCode listID="CriteriaTypeCode" listAgencyID="EU-COM-GROW" listVersionID="1.0.2">CRITERION.EXCLUSION.CONFLICT_OF_INTEREST.MISINTERPRETATION</cbc:TypeCode>
<cbc:Name>Guilty of misinterpretation, withheld information, unable to provide required documents and obtained confidential information of this procedure</cbc:Name>
...
<ccv:RequirementGroup>
<cbc:ID schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">30450436-f559-4dfa-98ba-f0842ed9d2a0</cbc:ID>
<ccv:Requirement responseDataType="INDICATOR">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">974c8196-9d1c-419c-9ca9-45bb9f5fd59a</cbc:ID>
<cbc:Description>Your answer?</cbc:Description>
<ccv:Response>
<ccv-cbc:Indicator>false</ccv-cbc:Indicator>
</ccv:Response>
</ccv:Requirement>
</ccv:RequirementGroup>
</ccv:Criterion>
For the economic operator criterion EO registered
found in Part II - Information concerning the economic operator
,
section A - Information about the economic operator
, the requirement with id 0e71abd3-198e-49c5-8128-5708617bb191
is transformed from a DESCRIPTION
to an INDICATOR
type of requirement.
...
<ccv:RequirementGroup pi="GROUP_FULFILLED.ON_FALSE">
<cbc:ID schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">59e6f3ef-15cd-4e21-82ac-ea497ccd44e2</cbc:ID>
<ccv:Requirement responseDataType="INDICATOR">
<cbc:ID schemeID="CriterionRelatedIDs" schemeAgencyID="EU-COM-GROW" schemeVersionID="1.0">0e71abd3-198e-49c5-8128-5708617bb191</cbc:ID>
<cbc:Description>e) Will the economic operator be able to provide a certificate with regard to the payment of social security contributions and taxes or provide information enabling the contracting authority or contracting entity to obtaining it directly by accessing a national database in any Member State that is available free of charge?</cbc:Description>
<ccv:Response/>
</ccv:Requirement>
...
</ccv:RequirementGroup>
...
Release notes
2018.10 (15th of October 2018)
-
Part VI changing the value of createcafinish_concl_statements_text
-
Reduced session timeout
-
Added session countdown timer
-
Fixed eCertis link bug
2017.03.01 (11th of April 2017)
This release contains a bugfix related to printing issues introduced in version 2017.03
.
2017.02.01 (3rd of March 2017)
The duration of the user session stored on the server has been reduced back to 2 hours.
2017.01 (31st of January 2017)
2016.12 (19th of December 2016)
This release contains mostly changes related to the XML structure of criteria. For a comprehensive history of all the XML changes, please visit this link: http://espd.github.io/ESPD-Service/docs/html/xmlChanges.html.
Bug
Enhancement
-
Contracting entities should be able to deselect exclusion grounds
-
When importing a TED notice, Error message should be different
-
Set up of economic operator has only 5 years… should be (much) more
-
Default values for criteria with requirements of type 'Indicator'
-
Fields missing for "Information is available electronically"
Translation
The full list of issues can be seen at ESPD Milestone 2016.12.
2016.11.01 (2nd of December 2016)
Bug
-
Fix for As entrepreneur reloading xml changes values which broke the printing functionality in version 2016.11
2016.11 (30th of November 2016)
Translation
The full list of issues can be seen at ESPD Milestone 2016.11.
2016.10 (8th of November 2016)
Bug
Translation
-
The release includes fixes related to translation issues for various languages
The full list of issues can be seen at ESPD Milestone 2016.10.
2016.08 (31st of August 2016)
Enhancement
The full list of issues can be seen at ESPD Milestone 2016.08.
2016.07 (29th of July 2016)
2016.06.01 (30th of June 2016)
Bug
-
[ESPD-90] - IE does not allow to copy text with paragraphs in textfield
-
[ESPD-96] - Exclusion ground part C must be preselected
-
[ESPD-104] - We are missing fields for Part VI
-
[ESPD-119] - Incorrect criterion definitions
Improvement
-
[ESPD-6] - Use procedure, exclusion as tabs
-
[ESPD-76] - Countries list
-
[ESPD-97] - Re-assign labels in the Home Page
-
[ESPD-100] - Selection criteria requirements duplicate ids
-
[ESPD-106] - Part V should not be shown to CAs
-
[ESPD-108] - VCD issues (continued from version 2016.06)
-
[ESPD-109] - Update of the text on the start page
-
[ESPD-110] - Remove all placeholders from the interface
2016.06 (2nd of June 2016)
This most important updates of this release are:
-
The restructuring of criterion subgroups in order to achieve a better interoperability with the VCD application
-
Deploy the application as an executable WAR file
2016.05 (12th of May 2016)
The main purpose of this release was to make public the open source version of the ESPD application on Github.
2016.04.01 (25th of April 2016)
Bug
-
[ESPD-73] - Missing translation
-
[ESPD-78] - Modification of exclusion criterion 'Guilty of misinterpretation'
-
[ESPD-80] - Numbers are handled wrongly
-
[ESPD-81] - Number of years should be calculated automatically
-
[ESPD-83] - EO not able to import a previously created ESPD
-
[ESPD-84] - Textfield into Yes/No
-
[ESPD-85] - Review ESPD as CA goes to non existent page
-
[ESPD-88] - Modification of exclusion ground conflict of interest
2016.04 (15th of April 2016)
1.1 (10th of February 2016)
Test Sub-task
-
[ESPD-38] - Test of Tests from Application support
-
[ESPD-39] - Test of Tests from Application support
Improvement
-
[ESPD-37] - Information from TED to make ESPD easier
-
[ESPD-45] - Improve metadata
-
[ESPD-46] - Merge two ESPD files
-
[ESPD-49] - New EN version
License
The ESPD project is licensed under the European Union Public Licence.