Index: .gitignore
===================================================================
diff -u
--- .gitignore (revision 0)
+++ .gitignore (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,20 @@
+.gradle
+build/
+
+### IDEA Files
+.idea
+*.iml
+*.ipr
+*.iws
+out
+
+### Eclipse files
+.project
+.settings
+.classpath
+bin
+
+mediaPath
+media
+temporaryMediaPath
+dsLocaleImport
Index: build.gradle
===================================================================
diff -u
--- build.gradle (revision 0)
+++ build.gradle (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,180 @@
+buildscript {
+ repositories {
+ mavenLocal()
+ maven { url 'http://workhorse.lemanscorp.com/nexus/content/groups/public/' }
+ }
+ dependencies {
+ classpath "org.grails:grails-gradle-plugin:$grailsVersion"
+ classpath "org.grails.plugins:hibernate5:6.0.5"
+ classpath "org.grails.plugins:views-gradle:1.1.2"
+
+ // Lemans
+ classpath 'net.saliman:gradle-cobertura-plugin:2.3.2'
+ }
+}
+
+//version "0.1" - leave this out to get no version no. in the war filename - kkrebs
+
+group 'com.lemans.services'
+
+defaultTasks 'clean', 'check', 'cobertura'
+
+apply plugin:"eclipse"
+apply plugin:"idea"
+apply plugin:"war"
+apply plugin:"org.grails.grails-web"
+apply plugin:"org.grails.plugins.views-json"
+
+apply plugin: "jacoco"
+
+
+// Lemans
+apply plugin: 'net.saliman.cobertura'
+apply plugin: 'codenarc'
+
+
+repositories {
+ mavenLocal()
+ maven { url 'http://workhorse.lemanscorp.com/nexus/content/groups/public/' }
+}
+
+dependencyManagement {
+ imports {
+ mavenBom "org.grails:grails-bom:$grailsVersion"
+ }
+ applyMavenExclusions false
+}
+
+//Lemans
+configurations {
+ compile.exclude module: 'commons-logging'
+}
+
+dependencies {
+ testCompile "org.springframework.boot:spring-boot-starter-logging" //Lemans
+ compile "org.springframework.boot:spring-boot-autoconfigure"
+ compile "org.grails:grails-core"
+
+ compile "org.springframework.boot:spring-boot-starter-actuator"
+ compile "org.springframework.boot:spring-boot-actuator-docs" //Lemans
+
+ provided "org.springframework.boot:spring-boot-starter-tomcat" //Lemans
+
+ compile "org.grails:grails-plugin-url-mappings"
+ compile "org.grails:grails-plugin-rest"
+ compile "org.grails:grails-plugin-codecs"
+ compile "org.grails:grails-plugin-interceptors"
+ compile "org.grails:grails-plugin-services"
+ compile "org.grails:grails-plugin-datasource"
+ compile "org.grails:grails-plugin-databinding"
+ compile "org.grails:grails-plugin-async"
+ compile "org.grails:grails-web-boot"
+ compile "org.grails:grails-logging"
+ compile "org.grails.plugins:cache"
+ compile "org.grails.plugins:hibernate5"
+ compile "org.hibernate:hibernate-core:5.1.2.Final"
+ compile "org.hibernate:hibernate-ehcache:5.1.2.Final"
+ compile "org.grails.plugins:views-json"
+ compile "org.grails.plugins:views-json-templates"
+ console "org.grails:grails-console"
+ profile "org.grails.profiles:rest-api"
+
+ compile 'org.apache.poi:poi:3.14'
+ compile 'org.apache.poi:poi-ooxml:3.14'
+ compile 'org.apache.poi:poi-ooxml-schemas:3.13'
+ compile 'org.gsheets.kktec:gsheets:0.4.2'
+ compile 'org.grails.plugins:quartz:2.0.12'
+
+
+ provided "org.slf4j:slf4j-api:1.7.20"
+ provided "org.slf4j:jcl-over-slf4j:1.7.21"
+ provided "org.slf4j:jul-to-slf4j:1.7.21"
+ provided "org.slf4j:log4j-over-slf4j:1.7.21"
+ provided "ch.qos.logback:logback-core"
+ provided "ch.qos.logback:logback-classic"
+
+ testCompile "org.grails:grails-plugin-testing"
+// testCompile "org.grails:grails-datastore-rest-client"
+
+ // Lemans
+
+
+ compile 'org.apache.poi:poi-ooxml-schemas:3.13'
+ compile 'org.apache.poi:ooxml-schemas:1.3'
+ compile 'org.apache.poi:poi:3.14'
+
+ compile ('org.apache.poi:poi-ooxml:3.14') {
+ transitive = false
+ }
+ compile 'org.gsheets.kktec:gsheets:0.4.2'
+
+
+ compile 'lemans:xml-json:0.14'
+ compile 'org.apache.httpcomponents:httpclient:4.2.1'
+ compile 'org.codehaus.groovy.modules.http-builder:http-builder:0.7.2'
+
+ compile 'org.apache.tika:tika-core:1.4'
+
+ provided 'net.sourceforge.jtds:jtds:1.3.1'
+
+ compile "org.grails.plugins:rabbitmq-native:3.4.4"
+
+ compile 'com.lemans.grails.plugins:lemans-core:0.1.4c'
+ compile 'com.lemans.grails.plugins:lemans-security:0.1.2c'
+ compile 'com.lemans.grails.plugins:lemans-rest:0.1.0d'
+
+ testCompile 'com.lemans.grails.plugins:lemans-testing:0.1.0'
+}
+
+task wrapper(type: Wrapper) {
+ gradleVersion = gradleWrapperVersion
+}
+
+//Extra Lemans Config Section:
+
+grails {
+ // deals with 'command line too long' issue when running Grails commands in Idea
+ pathingJar = true
+}
+
+tasks.withType(Test) {
+ testLogging {
+ showStandardStreams = true
+ }
+ systemProperties System.properties
+}
+
+tasks.withType(JavaExec) {
+ systemProperties System.properties
+}
+
+codenarc {
+ toolVersion = '1.0'
+ configFile = file("${project.projectDir}/codenarc/rules.groovy")
+}
+
+cobertura {
+ coverageExcludes = ['.*Application.*']
+
+// NOTE: It looks like coverage has false negatives. This may fix it - kkrebs
+ coverageMergeDatafiles = [file("${project.buildDir.path}/cobertura/cobertura.ser"),
+ file("${project.buildDir.path}/cobertura/coberturaInput.ser")]
+}
+
+// Investigating using JaCoCo instead of Cobertura - kkrebs
+// This is experimental. Results leave a lot to be desired at this time, 2016-09-29
+// Execute task jacocoTestReport to try it.
+test {
+ jacoco {
+ includes ['com/lemans/**']
+ }
+}
+jacocoTestReport.dependsOn test, integrationTest
+jacocoTestReport.doFirst{
+ classDirectories = files('build/classes/main/com/lemans')
+}
+jacocoTestReport {
+// executionData files('build/jacoco/unitTest.exec', 'build/jacoco/integrationTest.exec')
+ executionData integrationTest, test
+// sourceSets sourceSets.main
+}
Index: codenarc/rules.groovy
===================================================================
diff -u
--- codenarc/rules.groovy (revision 0)
+++ codenarc/rules.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,75 @@
+ruleset {
+ ruleset('rulesets/basic.xml')
+ ruleset('rulesets/braces.xml')
+ ruleset('rulesets/concurrency.xml')
+ ruleset('rulesets/convention.xml') {
+ exclude 'NoDef'
+ exclude 'NoTabCharacter'
+ exclude 'TrailingComma'
+ }
+ ruleset('rulesets/design.xml') {
+ exclude 'AbstractClassWithoutAbstractMethod'
+ exclude 'EmptyMethodInAbstractClass'
+ }
+ ruleset('rulesets/dry.xml') {
+ exclude 'DuplicateListLiteral'
+ exclude 'DuplicateNumberLiteral'
+ exclude 'DuplicateMapLiteral'
+ exclude 'DuplicateStringLiteral'
+ }
+ ruleset('rulesets/enhanced.xml')
+ ruleset('rulesets/exceptions.xml')
+ ruleset('rulesets/formatting.xml') {
+ exclude 'ClassJavadoc'
+ exclude 'ConsecutiveBlankLines'
+ LineLength {
+ length = 200
+ }
+ SpaceAroundMapEntryColon {
+ characterAfterColonRegex = /\s/
+ }
+ exclude 'TrailingWhitespace'
+ }
+ ruleset('rulesets/generic.xml')
+ ruleset('rulesets/grails.xml') {
+ exclude 'GrailsDomainHasEquals'
+ exclude 'GrailsDomainHasToString'
+ }
+ ruleset('rulesets/groovyism.xml')
+ ruleset('rulesets/imports.xml')
+ ruleset('rulesets/jdbc.xml')
+ ruleset('rulesets/junit.xml')
+ ruleset('rulesets/logging.xml')
+ ruleset('rulesets/naming.xml') {
+ exclude 'FactoryMethodName'
+ MethodName { // helps Spock
+ regex = /[a-zA-Z#][_#\w\s'"\(\)]*/
+ }
+ PropertyName {
+ regex = /[a-z][_a-zA-Z0-9]*/
+ }
+ }
+ ruleset('rulesets/security.xml') {
+ exclude 'JavaIoPackageAccess'
+ }
+ ruleset('rulesets/serialization.xml')
+ ruleset('rulesets/size.xml') {
+ CyclomaticComplexity { // Requires the GMetrics jar
+ maxMethodComplexity = 10
+ }
+ MethodSize {
+ maxLines = 26
+ doNotApplyToClassNames = '*Spec'
+ }
+ MethodSize {
+ maxLines = 41
+ applyToClassNames = '*Spec'
+ }
+ }
+ ruleset('rulesets/unnecessary.xml') {
+ UnnecessaryBooleanExpression {
+ doNotApplyToClassNames = '*Spec*'
+ }
+ }
+ ruleset('rulesets/unused.xml')
+}
\ No newline at end of file
Index: files/3c83a0cf-cac5-4888-8de9-8e9d56b48a11.xlsx
===================================================================
diff -u
Binary files differ
Index: files/ProductExport.xls
===================================================================
diff -u
Binary files differ
Index: files/ProductExportFinal.xlsx
===================================================================
diff -u
Binary files differ
Index: files/bad_file.xlsx
===================================================================
diff -u
Binary files differ
Index: files/fire.jpg
===================================================================
diff -u
Binary files differ
Index: files/sample2.PNG
===================================================================
diff -u
Binary files differ
Index: files/testForNoProductId.xlsx
===================================================================
diff -u
Binary files differ
Index: gradle.properties
===================================================================
diff -u
--- gradle.properties (revision 0)
+++ gradle.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,2 @@
+grailsVersion=3.2.4
+gradleWrapperVersion=3.1
Index: gradle/wrapper/gradle-wrapper.jar
===================================================================
diff -u
Binary files differ
Index: gradle/wrapper/gradle-wrapper.properties
===================================================================
diff -u
--- gradle/wrapper/gradle-wrapper.properties (revision 0)
+++ gradle/wrapper/gradle-wrapper.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,6 @@
+#Tue Nov 01 09:55:11 CDT 2016
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-all.zip
Index: gradlew
===================================================================
diff -u
--- gradlew (revision 0)
+++ gradlew (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,169 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
Index: gradlew.bat
===================================================================
diff -u
--- gradlew.bat (revision 0)
+++ gradlew.bat (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
Index: grails-app/conf/application.groovy
===================================================================
diff -u
--- grails-app/conf/application.groovy (revision 0)
+++ grails-app/conf/application.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,94 @@
+import javax.naming.Context
+import javax.naming.InitialContext
+
+grails.databinding.dateFormats = ["yyyy-MM-dd'T'hh:mm:ss", 'yyyy-MM-dd']
+
+environments {
+ production {
+ rabbitmq {
+ connections = [
+ [
+ name : "main",
+ virtualHost: ((Context) (new InitialContext().lookup("java:comp/env"))).lookup("rabbitMQVirtualHost").toString(),
+ host : ((Context) (new InitialContext().lookup("java:comp/env"))).lookup("rabbitMQHost").toString(),
+ username : ((Context) (new InitialContext().lookup("java:comp/env"))).lookup("rabbitMQUserName").toString(),
+ password : ((Context) (new InitialContext().lookup("java:comp/env"))).lookup("rabbitMQPassword").toString()
+ ]
+ ]
+ queues = [
+ [
+ name : ((Context) (new InitialContext().lookup("java:comp/env"))).lookup("usToEuSyncQueue").toString(),
+ durable : true,
+ exchange: ((Context) (new InitialContext().lookup("java:comp/env"))).lookup("usToEuSyncExchange").toString(),
+ binding : ((Context) (new InitialContext().lookup("java:comp/env"))).lookup("usToEuSyncBinding").toString(),
+ arguments: ['x-message-ttl': 18000000, 'x-dead-letter-exchange': 'lemans-us-eu-dead-letter-exchange', 'x-dead-letter-routing-key': 'lemans-us-eu-dead-letter-routing-key']
+ ]
+ ]
+ exchanges = [
+ [
+ name : ((Context) (new InitialContext().lookup("java:comp/env"))).lookup("usToEuSyncExchange").toString(),
+ type : "direct",
+ durable: true
+ ]
+ ]
+ }
+ }
+ development {
+ rabbitmq {
+ connections = [
+ [
+ name : "main",
+ host : "dev-rabbitmq01vm.lemanscorp.com",
+ username: "lemans",
+ password: "o7GgEz2pA9mA",
+ virtualHost: "/"
+ ]
+ ]
+ queues = [
+ [
+ name : "lemans-us-eu-queue",
+ durable : true,
+ exchange: "lemans-us-eu-exchange",
+ binding : "lemans-us-eu-routing-key",
+ arguments: ['x-message-ttl': 18000000, 'x-dead-letter-exchange': 'lemans-us-eu-dead-letter-exchange', 'x-dead-letter-routing-key': 'lemans-us-eu-dead-letter-routing-key']
+ ]
+ ]
+ exchanges = [
+ [
+ name : "lemans-us-eu-exchange",
+ type : "direct",
+ durable: true
+ ]
+ ]
+ }
+ }
+ test {
+ rabbitmq {
+ connections = [
+ [
+ name : "main",
+ host : "dev-rabbitmq01vm.lemanscorp.com",
+ username: "lemans",
+ password: "o7GgEz2pA9mA",
+ virtualHost: "/"
+ ]
+ ]
+ queues = [
+ [
+ name : "lemans-us-eu-queue",
+ durable : true,
+ exchange: "lemans-us-eu-exchange",
+ binding : "lemans-us-eu-routing-key",
+ arguments: ['x-message-ttl': 18000000, 'x-dead-letter-exchange': 'lemans-us-eu-dead-letter-exchange', 'x-dead-letter-routing-key': 'lemans-us-eu-dead-letter-routing-key']
+ ]
+ ]
+ exchanges = [
+ [
+ name : "lemans-us-eu-exchange",
+ type : "direct",
+ durable: true
+ ]
+ ]
+ }
+ }
+}
Index: grails-app/conf/application.properties
===================================================================
diff -u
--- grails-app/conf/application.properties (revision 0)
+++ grails-app/conf/application.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,2 @@
+# Spring Boot config for jmx to avoid naming conflicts when multiple apps in same Tomcat
+spring.jmx.default-domain=ds-service
Index: grails-app/conf/application.yml
===================================================================
diff -u
--- grails-app/conf/application.yml (revision 0)
+++ grails-app/conf/application.yml (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,109 @@
+---
+grails:
+ profile: rest-api
+ codegen:
+ defaultPackage: com.lemans.ds
+ spring:
+ transactionManagement:
+ proxies: false
+info:
+ app:
+ name: '@info.app.name@'
+ version: '@info.app.version@'
+ grailsVersion: '@info.app.grailsVersion@'
+spring:
+ groovy:
+ template:
+ check-template-location: false
+# Spring Actuator Endpoints are Disabled by Default
+endpoints:
+ enabled: false
+ jmx:
+ enabled: true
+
+---
+grails:
+ mime:
+ disable:
+ accept:
+ header:
+ userAgents:
+ - Gecko
+ - WebKit
+ - Presto
+ - Trident
+ types:
+ json:
+ - application/json
+ - text/json
+ hal:
+ - application/hal+json
+ - application/hal+xml
+ xml:
+ - text/xml
+ - application/xml
+ atom: application/atom+xml
+ css: text/css
+ csv: text/csv
+ js: text/javascript
+ rss: application/rss+xml
+ text: text/plain
+ all: '*/*'
+ urlmapping:
+ cache:
+ maxsize: 1000
+ controllers:
+ defaultScope: singleton
+ converters:
+ encoding: UTF-8
+
+---
+
+grails:
+ controllers:
+ upload:
+ maxFileSize: 2000000
+ maxRequestSize: 2000000
+
+---
+hibernate:
+ naming_strategy: org.hibernate.cfg.DefaultNamingStrategy
+ cache:
+ queries: false
+ use_second_level_cache: false
+ use_query_cache: false
+ region.factory_class: org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
+ format_sql: true
+ dialect: org.hibernate.dialect.SQLServer2008Dialect
+
+dataSource:
+ dbCreate: none
+ pooled: true
+ jmxExport: true
+ driverClassName: net.sourceforge.jtds.jdbc.Driver
+
+environments:
+ development:
+ dataSource:
+ url: jdbc:jtds:sqlserver://dev-dbprod02vm/PartsSource_DS
+ username: partssource_ds_user
+ password: DevPassword1
+ logSql: true
+ test:
+ dataSource:
+ url: jdbc:jtds:sqlserver://dev-dbprod02vm/PartsSource_DS
+ username: partssource_ds_user
+ password: DevPassword1
+ logSql: true
+ production:
+ dataSource:
+ jndiName: java:comp/env/jdbc/PartsSource_DS
+ logSql: false
+---
+---
+server:
+ contextPath: /ds-service
+
+management:
+ context_path: /admin
+
Index: grails-app/conf/logback.groovy
===================================================================
diff -u
--- grails-app/conf/logback.groovy (revision 0)
+++ grails-app/conf/logback.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,34 @@
+import grails.util.BuildSettings
+import grails.util.Environment
+
+// See http://logback.qos.ch/manual/groovy.html for details on configuration
+appender('STDOUT', ConsoleAppender) {
+ encoder(PatternLayoutEncoder) {
+ pattern = "%level %logger - %msg%n"
+ }
+}
+
+if (Environment.TEST ||Environment.DEVELOPMENT) {
+ logger 'org.hibernate.type.descriptor.sql.BasicBinder', TRACE, ['STDOUT']
+ logger 'groovy.sql', ALL, ['STDOUT']
+}
+
+def targetDir = BuildSettings.TARGET_DIR
+if (Environment.isDevelopmentMode() && targetDir != null) {
+ appender("FULL_STACKTRACE", FileAppender) {
+ file = "${targetDir}/stacktrace.log"
+ append = true
+ encoder(PatternLayoutEncoder) {
+ pattern = "%level %logger - %msg%n"
+ }
+ }
+ logger("StackTrace", ERROR, ['FULL_STACKTRACE'], false)
+ root(ERROR, ['STDOUT', 'FULL_STACKTRACE'])
+}
+else {
+ root(ERROR, ['STDOUT'])
+}
+
+
+
+
Index: grails-app/conf/spring/resources.groovy
===================================================================
diff -u
--- grails-app/conf/spring/resources.groovy (revision 0)
+++ grails-app/conf/spring/resources.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,65 @@
+import com.lemans.reporting.SsrReporter
+import grails.util.Environment
+
+// Place your Spring DSL code here
+beans = {
+
+ Environment current = Environment.current
+ if (current != Environment.PRODUCTION) {
+ authServiceContext(String, 'http://services2.dev.lemanscorp.com/auth-service/verifyRequest')
+ temporaryMediaPath(String, 'temporaryMediaPath')
+ sqlServerReportHost(String, 'http://dev-dbprod03vm')
+ reportServerExportFolder(String, '/DigitalServicesExport')
+ mediaPath(String, 'mediaPath/')
+ mediaPrefix(String, 'media')
+ dsLocaleImport(String, 'dsLocaleImport/')
+ sqlServerReportApiAuth(String, 'bGVtYW5zY29ycFxzc3JzaTo0MiFHSDJIWC1mN1Q=')
+ usToEuSyncQueue(String, "lemans-us-eu-queue")
+ usToEuSyncExchange(String, "lemans-us-eu-exchange")
+ usToEuSyncBinding(String, "lemans-us-eu-routing-key")
+ usToEuSyncToggle(Boolean, false)
+ } else {
+ authServiceContext(org.springframework.jndi.JndiObjectFactoryBean) {
+ jndiName = 'java:comp/env/authServiceContext'
+ }
+ temporaryMediaPath(org.springframework.jndi.JndiObjectFactoryBean) {
+ jndiName = 'java:comp/env/temporaryMediaPath'
+ }
+ sqlServerReportHost(org.springframework.jndi.JndiObjectFactoryBean) {
+ jndiName = 'java:comp/env/sqlServerReportHost'
+ }
+ reportServerExportFolder(org.springframework.jndi.JndiObjectFactoryBean) {
+ jndiName = 'java:comp/env/reportServerExportFolder'
+ }
+ mediaPath(org.springframework.jndi.JndiObjectFactoryBean) {
+ jndiName = 'java:comp/env/mediaPath'
+ }
+ mediaPrefix(org.springframework.jndi.JndiObjectFactoryBean) {
+ jndiName = 'java:comp/env/mediaPrefix'
+ }
+ dsLocaleImport(org.springframework.jndi.JndiObjectFactoryBean) {
+ jndiName = 'java:comp/env/dsLocaleImport'
+ }
+ sqlServerReportApiAuth(org.springframework.jndi.JndiObjectFactoryBean) {
+ jndiName = 'java:comp/env/sqlServerReportApiAuth'
+ }
+ usToEuSyncQueue(org.springframework.jndi.JndiObjectFactoryBean) {
+ jndiName = 'java:comp/env/usToEuSyncQueue'
+ }
+ usToEuSyncExchange(org.springframework.jndi.JndiObjectFactoryBean) {
+ jndiName = 'java:comp/env/usToEuSyncExchange'
+ }
+ usToEuSyncBinding(org.springframework.jndi.JndiObjectFactoryBean) {
+ jndiName = 'java:comp/env/usToEuSyncBinding'
+ }
+ usToEuSyncToggle(org.springframework.jndi.JndiObjectFactoryBean) {
+ jndiName = 'java:comp/env/usToEuSyncToggle'
+ }
+ }
+
+ ssrReporter(SsrReporter) {
+ httpClient = ref('httpClient')
+ sqlServerReportApiAuth = ref('sqlServerReportApiAuth')
+ sqlServerReportHost = ref('sqlServerReportHost')
+ }
+}
Index: grails-app/controllers/com/lemans/ApplicationController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ApplicationController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ApplicationController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,15 @@
+package com.lemans
+
+import grails.core.GrailsApplication
+import grails.plugins.GrailsPluginManager
+import grails.plugins.PluginManagerAware
+
+class ApplicationController implements PluginManagerAware {
+
+ GrailsApplication grailsApplication
+ GrailsPluginManager pluginManager
+
+ def index() {
+ [grailsApplication: grailsApplication, pluginManager: pluginManager]
+ }
+}
Index: grails-app/controllers/com/lemans/UrlMappings.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/UrlMappings.groovy (revision 0)
+++ grails-app/controllers/com/lemans/UrlMappings.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,392 @@
+package com.lemans
+
+@SuppressWarnings('LineLength')
+@SuppressWarnings('AbcMetric')
+class UrlMappings {
+
+ static mappings = {
+
+ "/dm/$dm/part/$partNumber/fitment"(controller: 'partFitment') {
+ action = [POST: 'add', GET: 'fitmentsByPart']
+ }
+
+ "/dm/$dm/part/$partNumber/fitment/$fitmentId"(controller: 'partFitment') {
+ action = [DELETE: 'delete']
+ }
+
+ "/dm/$dm/make"(controller: 'make') {
+ action = [GET: 'index', POST: 'add']
+ }
+
+ "/dm/$dm/localeImportJob"(controller: 'bulkLocaleImport') {
+ action = [POST: 'add', GET: 'index']
+ }
+
+ "/dm/$dm/localeImportJob/$importFileId"(controller: 'bulkLocaleImport') {
+ action = [GET: 'show', PUT: 'update', DELETE: 'remove']
+ }
+
+ "/dm/$dm/localeImportJob/$importFileId/file/$fileId/download"(controller: 'bulkLocaleImport') {
+ action = [GET: 'download']
+ }
+
+ "/dm/$dm/report/$reportName/download"(controller: 'bulkLocaleExport') {
+ action = [GET: 'downloadBulkExportFile']
+ }
+
+ "/dm/$dm/make/$makeId"(controller: 'make') {
+ action = [GET: 'show', PUT: 'update', DELETE: 'remove']
+ }
+
+ "/dm/$dm/make/$makeId/model"(controller: 'model') {
+ action = [GET: 'index', POST: 'add']
+ }
+
+ "/dm/$dm/make/$makeId/model/$modelId"(controller: 'model') {
+ action = [GET: 'show', PUT: 'update', DELETE: 'remove']
+ }
+
+ "/dm/$dm/make/$makeId/model/$modelId/segment/$segmentCode"(controller: 'segment') {
+ action = [PUT: 'addUpdateOrDeleteModelSegment']
+ }
+
+ "/dm/$dm/make/$makeId/model/$modelId/year"(controller: 'modelYear') {
+ action = [GET: 'index', POST: 'add']
+ }
+
+ "/dm/$dm/make/$makeId/model/$modelId/year/$yearId"(controller: 'modelYear') {
+ action = [DELETE: 'remove']
+ }
+
+ "/dm/$dm/make/$makeId/segment"(controller: 'segment') {
+ action = [GET: 'segments']
+ }
+
+ "/dm/$dm/make/$makeId/segment/$segmentCode/value"(controller: 'segment') {
+ action = [GET: 'segmentValues']
+ }
+
+ "/dm/$dm/search/$searchType/filter/$filterType"(controller: 'genericSearch') {
+ action = [GET: 'filters']
+ }
+
+ "/dm/$dm/search/$searchType/filter/$filterName/options"(controller: 'genericSearch') {
+ action = [GET: 'filterOptions']
+ }
+
+ "/dm/$dm/search/$searchType"(controller: 'genericSearch') {
+ action = [GET: 'search']
+ }
+
+ "/dm/$dm/mediaClassification"(controller: 'media') {
+ action = [GET: 'classifications']
+ }
+
+ "/dm/$dm/media"(controller: 'media') {
+ action = [GET: 'index', POST: 'add']
+ }
+
+ "/dm/$dm/media/$mediaId"(controller: 'media') {
+ action = [GET: 'show', PUT: 'update', DELETE: 'remove']
+ }
+
+ "/dm/$dm/media/$mediaId/download"(controller: 'media') {
+ action = [GET: 'download']
+ }
+
+ "/dm/$dm/media/entity"(controller: 'mediaEntity') {
+ action = [POST: 'bulkAddOrRemoveMediaEntity']
+ }
+
+ "/dm/$dm/media/$mediaId/entity"(controller: 'mediaEntity') {
+ action = [GET: 'index']
+ }
+
+ "/dm/$dm/media/$mediaId/entity/$entityId"(controller: 'mediaEntity') {
+ action = [PUT: 'updateMediaEntity']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/product/$productId/feature/$productFeatureId/move"(controller: 'productFeature') {
+ action = [PUT: 'move']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/product/$productId/feature"(controller: 'productFeature') {
+ action = [GET: 'index', POST: 'add']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/product/$productId/feature/$productFeatureId"(controller: 'productFeature') {
+ action = [GET: 'show', PUT: 'update', DELETE: 'remove']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/product/$productId/splitValidation"(controller: 'product') {
+ action = [POST: 'splitValidation']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/product"(controller: 'product') {
+ action = [GET: 'index', POST: 'add']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/product/$productId"(controller: 'product') {
+ action = [GET: 'show', PUT: 'update', DELETE: 'remove']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/category/$categoryId/product"(controller: 'categoryProduct') {
+ action = [POST: 'addOrRemoveAssociation']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/product/$productId/media"(controller: 'mediaEntity') {
+ action = [GET: 'getMediaForProduct', POST: 'addMediaToProduct']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/product/$productId/media/$mediaId"(controller: 'mediaEntity') {
+ action = [DELETE: 'removeMediaOnProduct']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/product/$productId/part"(controller: 'productPart') {
+ action = [GET: 'index', POST: 'bulkAddOrRemoveOperation']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/product/$productId/attribute"(controller: 'productMerchandise') {
+ action = [GET: 'getMerchandiseProductAttributeDetails']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/product/$productId/attribute/$categoryAttributeId"(controller: 'productMerchandise') {
+ action = [PUT: 'update']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/product/$productId/part/$partNumber"(controller: 'productPart') {
+ action = [GET: 'show', DELETE: 'remove']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/category/$categoryId/part"(controller: 'categoryPart') {
+ action = [PUT: 'addOrRemove']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/category/$categoryId/subComCode"(controller: 'categorySubComCode') {
+ action = [GET: 'index', POST: 'add']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/category/$categoryId/subComCode/$subComCodeId"(controller: 'categorySubComCode') {
+ action = [GET: 'show', DELETE: 'remove']
+ }
+
+ "/dm/$dm/subComCode"(controller: 'subComCode') {
+ action = [GET: 'index']
+ }
+
+ "/dm/$dm/subComCode/$subComCode"(controller: 'subComCode') {
+ action = [GET: 'show']
+ }
+
+ "/dm/$dm/part"(controller: 'part') {
+ action = [GET: 'index']
+ }
+
+ "/dm/$dm/part/$partNumber"(controller: 'part') {
+ action = [GET: 'show', PUT: 'update']
+ }
+
+ "/dm/$dm/part/$partNumber/media"(controller: 'mediaEntity') {
+ action = [GET: 'getMediaForPart', POST: 'addMediaToPart']
+ }
+
+ "/dm/$dm/part/$partNumber/media/$mediaId"(controller: 'mediaEntity') {
+ action = [DELETE: 'removeMediaOnPart']
+ }
+
+ "/dm/$dm/attributeName"(controller: 'attributeName') {
+ action = [GET: 'index', POST: 'add']
+ }
+
+ "/dm/$dm/attributeName/$id"(controller: 'attributeName') {
+ action = [GET: 'show', PUT: 'update', DELETE: 'remove']
+ }
+
+ "/dm/$dm/attributeValue"(controller: 'attributeValue') {
+ action = [GET: 'index', POST: 'add']
+ }
+
+ "/dm/$dm/attributeValue/$id"(controller: 'attributeValue') {
+ action = [GET: 'show', PUT: 'update', DELETE: 'remove']
+ }
+
+ "/dm/$dm/attributeName/$attributeNameId/attributeValue"(controller: 'attributeValue') {
+ action = [GET: 'attributeValuesByAttributeNameIdInCategoryContext']
+ }
+
+ "/dm/$dm/part/attribute"(controller: 'partAttribute') {
+ action = [POST: 'bulkAddOrRemoveOperation']
+ }
+
+ "/dm/$dm/attributeName/$attributeNameId/part"(controller: 'attributePart') {
+ action = [GET: 'partsByAttributeName']
+ }
+
+ "/dm/$dm/attributeValue/$attributeValueId/part"(controller: 'attributePart') {
+ action = [GET: 'partsByAttributeValue']
+ }
+
+ "/dm/$dm/attributeName/$attributeNameId/category"(controller: 'attributeCategory') {
+ action = [GET: 'categoriesByAttributeName']
+ }
+
+ "/dm/$dm/attributeValue/$attributeValueId/category"(controller: 'attributeCategory') {
+ action = [GET: 'categoriesByAttributeValue']
+ }
+
+ "/dm/$dm/part/$partNumber/attribute/$attributeNameId/value"(controller: 'partAttribute') {
+ action = [POST: 'add', PUT: 'bulkReplaceOperation']
+ }
+
+ "/dm/$dm/part/$partNumber/attribute/$attributeNameId/value/$attributeValueId" (controller: 'partAttribute') {
+ action = [DELETE: 'remove']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/category/$id/move"(controller: 'category') {
+ action = [PUT: 'move']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/category/$id"(controller: 'category') {
+ action = [GET: 'show', PUT: 'update', DELETE: 'remove']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/category"(controller: 'category') {
+ action = [GET: 'index', POST: 'add']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/category/$categoryId/details"(controller: 'category') {
+ action = [GET: 'details']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/category/$categoryId/part/details"(controller: 'partAttribute') {
+ action = [POST: 'categoryPartDetails']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/category/$categoryId/attribute"(controller: 'categoryAttribute') {
+ action = [GET: 'index', POST: 'add']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/category/$categoryId/attribute/$attributeNameId"(controller: 'categoryAttribute') {
+ action = [GET: 'showByCompositeKey', PUT: 'update', DELETE: 'remove']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/category/$categoryId/attribute/$attributeNameId/move"(controller: 'categoryAttribute') {
+ action = [PUT: 'move']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/category/$categoryId/attribute/$attributeNameId/value"(controller: 'categoryAttributeValue') {
+ action = [GET: 'index', POST: 'add']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/category/$categoryId/attribute/$attributeNameId/value/$attributeValueId" (controller: 'categoryAttributeValue') {
+ action = [GET: 'showByCompositeKey', PUT: 'update', DELETE: 'remove']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/category/$categoryId/attribute/$attributeNameId/value/$attributeValueId/move" (controller: 'categoryAttributeValue') {
+ action = [PUT: 'move']
+ }
+
+ "/dm/$dm/brandSearch"(parseRequest: true, controller: 'brandSearch') {
+ action = [GET: 'brandSearch']
+ }
+
+ '/stuff'(controller: 'stuff') {
+ action = [GET: 'index']
+ }
+ //partAssociation
+
+ "/dm/$dm/part/$partNumber/relatedPart/$id"(controller: 'partAssociation') {
+ action = [PUT: 'update', DELETE: 'remove', GET: 'show']
+ }
+
+ "/dm/$dm/part/$partNumber/relatedPart"(controller: 'partAssociation') {
+ action = [GET: 'relatedParts', POST: 'add']
+ }
+
+ "/dm/$dm/part/$partNumber/referralPart"(controller: 'partAssociation') {
+ action = [GET: 'referralParts']
+ }
+
+ //productAssociation
+
+ "/dm/$dm/catalog/$catalogInstanceId/product/$productId/relatedProduct/$id"(controller: 'productAssociation') {
+ action = [PUT: 'update', DELETE: 'remove', GET: 'show']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/product/$productId/relatedProduct"(controller: 'productAssociation') {
+ action = [GET: 'relatedProducts', POST: 'add']
+ }
+
+ "/dm/$dm/catalog/$catalogInstanceId/product/$productId/referralProduct"(controller: 'productAssociation') {
+ action = [GET: 'referralProducts']
+ }
+
+ "/dm/$dm/flag"(controller: 'flag') {
+ action = [GET: 'index']
+ }
+
+ "/dm/$dm/$entityClass/flag"(controller: 'flag') {
+ action = [GET: 'index']
+ }
+
+ "/dm/$dm/$entityClass/$entityId/flag/$flagId"(controller: 'flagValue') {
+ action = [GET: 'show']
+ }
+
+ "/dm/$dm/$entityClass/$entityId/flag"(controller: 'flagValue') {
+ action = [GET: 'index', POST: 'addOrDeleteFlagValues']
+ }
+
+ //publicationcategory
+ "/dm/$dm/publicationCategory"(controller: 'publicationCategory') {
+ action = [GET: 'index', POST: 'add']
+ }
+
+ "/dm/$dm/publicationCategory/$categoryId"(controller: 'publicationCategory') {
+ action = [GET: 'show', PUT: 'update', DELETE: 'delete']
+ }
+
+ "/dm/$dm/publicationCategory/$categoryId/move"(controller: 'publicationCategory') {
+ action = [PUT: 'move']
+ }
+
+ //ProductPublicationCategory
+ "/dm/$dm/publicationCategory/$categoryId/product"(controller: 'productPublicationCategory') {
+ action = [GET: 'byCategoryId', POST: 'add']
+ }
+
+ "/dm/$dm/product/$productId/publicationCategory"(controller: 'productPublicationCategory') {
+ action = [GET: 'byProductId']
+ }
+
+ "/dm/$dm/publicationCategory/$categoryId/product/$productId"(controller: 'productPublicationCategory') {
+ action = [DELETE: 'delete']
+ }
+
+ //Explosion Diagram
+ "/dm/$dm/explosionDiagram"(controller: 'explosionDiagram') {
+ action = [GET: 'index', POST: 'add']
+ }
+
+ "/dm/$dm/explosionDiagram/$explosionDiagramId"(controller: 'explosionDiagram') {
+ action = [GET: 'show', PUT: 'update', DELETE: 'delete']
+ }
+
+ //publicationCategoryAttribute
+ "/dm/$dm/publicationCategory/$categoryId/attribute"(controller: 'publicationCategoryAttribute') {
+ action = [GET: 'index', POST: 'add']
+ }
+
+ "/dm/$dm/publicationCategory/$categoryId/attributeOptions"(controller: 'publicationCategoryAttribute') {
+ action = [GET: 'attributeOptions']
+ }
+
+ "/dm/$dm/publicationCategory/$categoryId/attribute/$attributeNameId"(controller: 'publicationCategoryAttribute') {
+ action = [GET: 'showByCompositeKey', DELETE: 'delete']
+ }
+
+ "/dm/$dm/publicationCategory/$categoryId/attribute/$attributeNameId/move"(controller: 'publicationCategoryAttribute') {
+ action = [PUT: 'move']
+ }
+ }
+}
Index: grails-app/controllers/com/lemans/ds/ReportAndLocaleCommonsController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/ReportAndLocaleCommonsController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/ReportAndLocaleCommonsController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,88 @@
+package com.lemans.ds
+
+import com.lemans.LemansApiController
+import com.lemans.ds.rabbit.QueueRequestType
+import com.lemans.ds.rabbit.RequestPayload
+
+/**
+ * Created by MUmachi on 10/9/2017.
+ */
+class ReportAndLocaleCommonsController extends LemansApiController {
+
+ def rabbitMessageService
+
+ def usToEuSyncToggle
+
+ private static final List VALID_LOCALES = ['de', 'it', 'es', 'fr']
+
+ boolean validateLocale(String locale) {
+ VALID_LOCALES.contains(locale)
+ }
+
+ boolean isDefaultLocale() {
+ locale == Locale.default.toString()
+ }
+
+ def renderLocaleError(String locale) {
+ renderErrors(["locale with value [$locale] is not contained within the list [$VALID_LOCALES]"])
+ }
+
+ @SuppressWarnings(['CouldBeSwitchStatement'])//TODO: remove this, codenarc complaing for some reason
+ protected void renderMQObject(data, QueueRequestType requestType, id = null) {
+ renderObject(data, id) {
+ if (usToEuSyncToggle == true) {
+ rabbitMessageService.publishToUsTOEuQueue(new RequestPayload(requestType: requestType, id: data.id))
+ }
+ }
+ }
+
+ protected void renderMQDelete(data, QueueRequestType requestType, Iterable messages = null) {
+ renderDelete(data, messages) {
+ if (usToEuSyncToggle == true) {
+ rabbitMessageService.publishToUsTOEuQueue(new RequestPayload(requestType: requestType, id: data.id))
+ }
+ }
+ }
+
+ protected void renderMQObjectSpecial(data, QueueRequestType requestType, Object optionalPayload = null, id = null) {
+ renderObject(data, id) {
+ if (usToEuSyncToggle == true) {
+ rabbitMessageService.publishToUsTOEuQueue(new RequestPayload(requestType: requestType, id: data.id, body: optionalPayload))
+ }
+ }
+ }
+
+ void renderObject(data, id = null, Closure closure) {
+ if (data == null) { notFound() }
+ else if (data.hasErrors()) { renderErrors(data) }
+ else {
+ closure()
+ show(id ?: data.id)
+ }
+ }
+
+ void renderDelete(data, Iterable messages = null, Closure closure) {
+ if (data == null) { notFound(messages) }
+ else if (data.hasErrors()) { renderErrors(data) }
+ else {
+ closure()
+ renderEmpty()
+ }
+ }
+
+ @SuppressWarnings(['CatchException'])
+ protected generateReport(Map criteria, response, ssrReporter) {
+ try {
+ ssrReporter.renderSSRSReport("${criteria.url}${criteria.urlParams}", criteria.reportFormat, response)
+ } catch (Exception e) {
+ def rootCause = e.cause.toString()
+ if (rootCause.contains('ConnectTimeoutException')) {
+ renderErrors(['Connection Timed out, Try again'])
+ } else if (rootCause.contains('SocketTimeoutException')) {
+ renderErrors(['Socket Timed out, Try again'])
+ } else {
+ renderErrors(['Error with Reporting Server, Please Contact Helpdesk'])
+ }
+ }
+ }
+}
Index: grails-app/controllers/com/lemans/ds/SubComCodeController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/SubComCodeController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/SubComCodeController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,18 @@
+package com.lemans.ds
+
+import com.lemans.LemansApiController
+
+class SubComCodeController extends LemansApiController {
+
+ def commonService
+
+ def index() {
+ List data = commonService.findSubComCodes(common())
+ renderList data
+ }
+
+ def show(String subComCode) {
+ Map data = commonService.findSubComCode(common() + [subComCode: subComCode])
+ renderOne data
+ }
+}
Index: grails-app/controllers/com/lemans/ds/attribute/AttributeCategoryController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/attribute/AttributeCategoryController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/attribute/AttributeCategoryController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,25 @@
+package com.lemans.ds.attribute
+
+import com.lemans.LemansApiController
+
+/**
+ * Created by vramisetti on 8/18/2017.
+ */
+class AttributeCategoryController extends LemansApiController {
+
+ def attributeService
+
+ def categoriesByAttributeName(Integer attributeNameId) {
+ if (attributeNameId) { renderPaginated(attributeService.categoriesByAttributeName(criteria())) }
+ else { renderErrors(['attributeNameId is required']) }
+ }
+
+ def categoriesByAttributeValue(Integer attributeValueId) {
+ if (attributeValueId) { renderPaginated(attributeService.categoriesByAttributeValue(criteria())) }
+ else { renderErrors(['attributeValueId is required']) }
+ }
+
+ private Map criteria() {
+ filters(['attributeNameId', 'attributeValueId']) + pagination() + common()
+ }
+}
Index: grails-app/controllers/com/lemans/ds/attribute/AttributeNameController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/attribute/AttributeNameController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/attribute/AttributeNameController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,47 @@
+package com.lemans.ds.attribute
+
+import static com.lemans.ds.rabbit.QueueRequestType.ATTRIBUTE_NAME_CREATE
+import static com.lemans.ds.rabbit.QueueRequestType.ATTRIBUTE_NAME_DELETE
+import static com.lemans.ds.rabbit.QueueRequestType.ATTRIBUTE_NAME_UPDATE
+
+import com.lemans.ds.ReportAndLocaleCommonsController
+
+class AttributeNameController extends ReportAndLocaleCommonsController {
+
+ def attributeNameService
+ def attributeNameManagerService
+
+ def index() {
+ Map criteria = common() + pagination()
+ renderPaginated attributeNameService.findAttributeNames(criteria)
+ }
+
+ def show(Integer id) {
+ renderOne attributeNameService.findAttributeNameById([attributeNameId: id, locale: locale])
+ }
+
+ def add() {
+ Map criteria = request.JSON + common()
+ AttributeName attributeName = attributeNameManagerService.createAttributeName(criteria, auditUserName)
+ renderMQObject(attributeName, ATTRIBUTE_NAME_CREATE)
+ }
+
+ def update(Integer id) {
+ Map criteria = request.JSON + [attributeNameId: id, locale: locale]
+ if (isDefaultLocale()) {
+ AttributeName attributeName = attributeNameManagerService.updateAttributeName(criteria, id, auditUserName)
+ renderMQObject(attributeName, ATTRIBUTE_NAME_UPDATE)
+ } else {
+ AttributeNameLocale attributeNameLocale = attributeNameManagerService.updateAttributeNameWithLocale(criteria, auditUserName)
+ renderObject(attributeNameLocale, attributeNameLocale?.attributeNameId)
+ }
+ }
+
+ def remove(Integer id) {
+ AttributeName attributeName
+ if (id != null) {
+ attributeName = attributeNameManagerService.deleteAttributeName(id, auditUserName)
+ }
+ renderMQDelete(attributeName, ATTRIBUTE_NAME_DELETE)
+ }
+}
Index: grails-app/controllers/com/lemans/ds/attribute/AttributePartController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/attribute/AttributePartController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/attribute/AttributePartController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,25 @@
+package com.lemans.ds.attribute
+
+import com.lemans.LemansApiController
+
+/**
+ * Created by vramisetti on 8/18/2017.
+ */
+class AttributePartController extends LemansApiController {
+
+ def attributeService
+
+ def partsByAttributeName(Integer attributeNameId) {
+ if (attributeNameId) { renderPaginated(attributeService.partsByAttributeName(criteria())) }
+ else { renderErrors(['attributeNameId is required']) }
+ }
+
+ def partsByAttributeValue(Integer attributeValueId) {
+ if (attributeValueId) { renderPaginated(attributeService.partsByAttributeValue(criteria())) }
+ else { renderErrors(['attributeValueId is required']) }
+ }
+
+ private Map criteria() {
+ filters(['attributeNameId', 'attributeValueId', 'categoryId']) + pagination()
+ }
+}
Index: grails-app/controllers/com/lemans/ds/attribute/AttributeValueController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/attribute/AttributeValueController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/attribute/AttributeValueController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,56 @@
+package com.lemans.ds.attribute
+
+import static com.lemans.ds.rabbit.QueueRequestType.ATTRIBUTE_VALUE_CREATE
+import static com.lemans.ds.rabbit.QueueRequestType.ATTRIBUTE_VALUE_DELETE
+import static com.lemans.ds.rabbit.QueueRequestType.ATTRIBUTE_VALUE_UPDATE
+
+import com.lemans.ds.ReportAndLocaleCommonsController
+
+class AttributeValueController extends ReportAndLocaleCommonsController {
+
+ def attributeValueService
+ def attributeValueManagerService
+
+ def index() {
+ Map criteria = common() + pagination()
+ renderPaginated attributeValueService.findAttributeValues(criteria)
+ }
+
+ def show(Integer id) {
+ renderOne attributeValueService.findAttributeValueById([attributeValueId: id, locale: locale])
+ }
+
+ def add() {
+ Map criteria = request.JSON + common()
+ AttributeValue attributeValue = attributeValueManagerService.createAttributeValue(criteria, auditUserName)
+ renderMQObject(attributeValue, ATTRIBUTE_VALUE_CREATE)
+ }
+
+ def update(Integer id) {
+ Map criteria = request.JSON + [attributeValueId: id, locale: locale]
+ if (isDefaultLocale()) {
+ AttributeValue attributeValue = attributeValueManagerService.updateAttributeValue(criteria, auditUserName)
+ renderMQObject(attributeValue, ATTRIBUTE_VALUE_UPDATE)
+ } else {
+ AttributeValueLocale attributeValueLocale = attributeValueManagerService.updateAttributeValueWithLocale(criteria, auditUserName)
+ renderObject(attributeValueLocale, attributeValueLocale?.attributeValueId)
+ }
+ }
+
+ def remove(Integer id) {
+ AttributeValue attributeValue
+ if (id != null) {
+ attributeValue = attributeValueManagerService.deleteAttributeValue(id, auditUserName)
+ }
+ renderMQDelete(attributeValue, ATTRIBUTE_VALUE_DELETE)
+ }
+
+ def attributeValuesByAttributeNameIdInCategoryContext(Integer attributeNameId) {
+ Map data = attributeValueService.findAttributeValuesByAttributeNameId(attributeNameId)
+ data.results ? renderOne(data.results) : renderEmptyResults()
+ }
+
+ private void renderEmptyResults() {
+ render(toJson([results: []]))
+ }
+}
Index: grails-app/controllers/com/lemans/ds/category/CategoryAttributeController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/category/CategoryAttributeController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/category/CategoryAttributeController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,74 @@
+package com.lemans.ds.category
+
+
+import static com.lemans.ds.rabbit.QueueRequestType.CATEGORY_ATTRIBUTE_CREATE
+import static com.lemans.ds.rabbit.QueueRequestType.CATEGORY_ATTRIBUTE_DELETE
+import static com.lemans.ds.rabbit.QueueRequestType.CATEGORY_ATTRIBUTE_MOVE
+import static com.lemans.ds.rabbit.QueueRequestType.CATEGORY_ATTRIBUTE_UPDATE
+import org.apache.commons.collections.CollectionUtils
+
+import com.lemans.ds.MoveCommand
+import com.lemans.ds.ReportAndLocaleCommonsController
+
+class CategoryAttributeController extends ReportAndLocaleCommonsController {
+
+ def categoryAttributeService
+ def categoryAttributeManagerService
+
+ private static final List FLAG_FIELDS = ['isRequired', 'allowMultipleValues', 'isDropDown', 'isHidden', 'isKeyAttribute']
+
+ def index(Integer catalogInstanceId, Integer categoryId) {
+ Map criteria = common() + pagination() + [catalogInstanceId: catalogInstanceId, categoryId: categoryId]
+ renderPaginated categoryAttributeService.findCategoryAttributeByCategory(criteria)
+ }
+
+ def showByCompositeKey(Integer attributeNameId) {
+ Map criteria = [locale: locale, attributeNameId: attributeNameId] + filters(['catalogInstanceId', 'categoryId'])
+ renderOne categoryAttributeService.findCategoryAttributeByCompositKey(criteria)
+ }
+
+ def add(Integer catalogInstanceId, Integer categoryId) {
+ Map values = request.JSON + [catalogInstanceId: catalogInstanceId, categoryId: categoryId]
+ CategoryAttribute categoryAttribute = categoryAttributeManagerService.createCategoryAttribute(values, auditUserName)
+ categoryAttributeManagerService.sequence([id: categoryAttribute.id], auditUserName)
+ renderMQObject(categoryAttribute, CATEGORY_ATTRIBUTE_CREATE)
+ }
+
+ def update() {
+ Map values = request.JSON + [locale: locale]
+ Map criteria = filters(['catalogInstanceId', 'categoryId', 'attributeNameId'])
+ if (isDefaultLocale() || CollectionUtils.containsAny(values.keySet(), FLAG_FIELDS)) {
+ CategoryAttribute categoryAttribute = categoryAttributeManagerService.updateCategoryAttribute(values, criteria, auditUserName)
+ renderMQObject(categoryAttribute, CATEGORY_ATTRIBUTE_UPDATE)
+ } else {
+ CategoryAttributeLocale categoryAttributeLocale = categoryAttributeManagerService.updateCategoryAttributeWithLocale(values, criteria, auditUserName)
+ renderObject(categoryAttributeLocale, categoryAttributeLocale?.categoryAttributeId)
+ }
+ }
+
+ def move() {
+ Map values = request.JSON
+ MoveCommand cmd = new MoveCommand(values)
+ if (cmd.validate()) {
+ Map moveCriteria = values + filters(['catalogInstanceId', 'categoryId', 'attributeNameId'])
+ CategoryAttribute categoryAttribute = categoryAttributeManagerService.moveCategoryAttribute(moveCriteria, auditUserName)
+ renderMQObjectSpecial(
+ categoryAttribute,
+ CATEGORY_ATTRIBUTE_MOVE,
+ moveCriteria + [appSecurityUser: auditUserName]
+ )
+ } else {
+ renderObject cmd
+ }
+ }
+
+ def remove(Integer version) {
+ Map criteria = filters(['catalogInstanceId', 'categoryId', 'attributeNameId'])
+ CategoryAttribute categoryAttribute = categoryAttributeManagerService.deleteCategoryAttribute(criteria, auditUserName, version)
+ renderMQDelete(categoryAttribute, CATEGORY_ATTRIBUTE_DELETE)
+ }
+
+ protected show(Integer id) {
+ renderOne categoryAttributeService.findCategoryAttributeById([categoryAttributeId: id, locale: locale])
+ }
+}
Index: grails-app/controllers/com/lemans/ds/category/CategoryAttributeValueController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/category/CategoryAttributeValueController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/category/CategoryAttributeValueController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,93 @@
+package com.lemans.ds.category
+
+import static com.lemans.ds.rabbit.QueueRequestType.CATEGORY_ATTRIBUTE_VALUE_CREATE
+import static com.lemans.ds.rabbit.QueueRequestType.CATEGORY_ATTRIBUTE_VALUE_DELETE
+import static com.lemans.ds.rabbit.QueueRequestType.CATEGORY_ATTRIBUTE_VALUE_MOVE
+import static com.lemans.ds.rabbit.QueueRequestType.CATEGORY_ATTRIBUTE_VALUE_UPDATE
+
+import com.lemans.ds.MoveCommand
+import com.lemans.ds.ReportAndLocaleCommonsController
+import com.lemans.ds.rabbit.RequestPayload
+import grails.transaction.Transactional
+
+class CategoryAttributeValueController extends ReportAndLocaleCommonsController {
+
+ def categoryAttributeValueService
+ def categoryAttributeValueManagerService
+
+ def index() {
+ Map criteria = common() + filters(['catalogInstanceId', 'categoryId', 'attributeNameId'])
+ renderPaginated categoryAttributeValueService.findCategoryAttributeValueByCriteria(criteria)
+ }
+
+ def showByCompositeKey() {
+ Map criteria = common() + filters(['catalogInstanceId', 'categoryId', 'attributeNameId', 'attributeValueId'])
+ renderOne categoryAttributeValueService.findCategoryAttributeValueByCompositKey(criteria)
+ }
+
+ @Transactional
+ def add(Integer catalogInstanceId, Integer categoryId, Integer attributeNameId) {
+ Map values = request.JSON + [catalogInstanceId: catalogInstanceId, categoryId: categoryId, attributeNameId: attributeNameId]
+ CategoryAttributeValue categoryAttributeValue = categoryAttributeValueManagerService.createCategoryAttributeValue(values, auditUserName)
+ categoryAttributeValueManagerService.sequence([sourceId: categoryAttributeValue.id], auditUserName)
+ renderMQObject(categoryAttributeValue, CATEGORY_ATTRIBUTE_VALUE_CREATE)
+ }
+
+ def update(Integer catalogInstanceId, Integer categoryId, Integer attributeNameId, Integer attributeValueId) {
+ Map values = request.JSON + [locale: locale, catalogInstanceId: catalogInstanceId,
+ categoryId: categoryId, attributeNameId: attributeNameId,
+ attributeValueId: attributeValueId]
+ if (isDefaultLocale()) {
+ CategoryAttributeValue categoryAttributeValue = categoryAttributeValueManagerService.updateCategoryAttributeValue(values, auditUserName)
+ renderMQObject(categoryAttributeValue, CATEGORY_ATTRIBUTE_VALUE_UPDATE)
+ } else {
+ CategoryAttributeValueLocale categoryAttributeValueLocale = categoryAttributeValueManagerService.updateCategoryAttributeValueWithLocale(values, auditUserName)
+ renderObject(categoryAttributeValueLocale, categoryAttributeValueLocale?.categoryAttributeValueId)
+ }
+ }
+
+ def remove(Integer version) {
+ Map criteria = [
+ catalogInstanceId: params.catalogInstanceId,
+ categoryId: params.categoryId,
+ attributeNameId: params.attributeNameId,
+ attributeValueId: params.attributeValueId
+ ]
+ CategoryAttributeValue categoryAttributeValue = categoryAttributeValueManagerService.deleteCategoryAttributeValue(criteria, auditUserName, version)
+ renderMQDelete(categoryAttributeValue, CATEGORY_ATTRIBUTE_VALUE_DELETE)
+ }
+
+ def move() {
+ Map values = request.JSON
+ Map criteria = [
+ catalogInstanceId: params.catalogInstanceId,
+ categoryId: params.categoryId,
+ attributeNameId: params.attributeNameId,
+ attributeValueId: params.attributeValueId,
+ ]
+ MoveCommand cmd = new MoveCommand(values)
+ if (cmd.validate()) {
+ Map moveCriteria = values + criteria
+ Map categoryAttributeValue = categoryAttributeValueManagerService.moveCategoryAttributeValue(moveCriteria, auditUserName)
+ if (categoryAttributeValue) {
+ if (usToEuSyncToggle == true) {
+ rabbitMessageService.publishToUsTOEuQueue(
+ new RequestPayload(
+ requestType: CATEGORY_ATTRIBUTE_VALUE_MOVE,
+ id: moveCriteria.sourceId,
+ body: moveCriteria + [appSecurityUser: auditUserName]
+ ))
+ }
+ show(categoryAttributeValue.categoryAttributeValueId)
+ } else {
+ notFound()
+ }
+ } else {
+ renderObject cmd
+ }
+ }
+
+ protected show(Integer id) {
+ renderOne categoryAttributeValueService.findCategoryAttributeValueById([categoryAttributeValueId: id, locale: locale])
+ }
+}
Index: grails-app/controllers/com/lemans/ds/category/CategoryController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/category/CategoryController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/category/CategoryController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,81 @@
+package com.lemans.ds.category
+
+import static com.lemans.ds.rabbit.QueueRequestType.CATEGORY_CREATE
+import static com.lemans.ds.rabbit.QueueRequestType.CATEGORY_DELETE
+import static com.lemans.ds.rabbit.QueueRequestType.CATEGORY_MOVE
+import static com.lemans.ds.rabbit.QueueRequestType.CATEGORY_UPDATE
+
+import com.lemans.ds.MoveCategoryCommand
+import com.lemans.ds.ReportAndLocaleCommonsController
+import groovy.json.JsonSlurper
+import org.json.XML
+
+class CategoryController extends ReportAndLocaleCommonsController {
+
+ def categoryService
+ def categoryManagerService
+
+ def index(Integer catalogInstanceId, String categoryId) {
+ Map criteria = common() + [catalogInstanceId: catalogInstanceId, categoryId: categoryId]
+ List results = categoryService.findCategories(criteria)
+ results ? renderMany(results) : notFound()
+ }
+
+ def show(Integer id) {
+ Map criteria = common() + [catalogInstanceId: params.catalogInstanceId, id: id]
+ renderOne categoryService.findCategoryById(criteria)
+ }
+
+ def details(Integer catalogInstanceId, Integer categoryId) {
+ String detailsXML = categoryService.categoryDetails([catalogInstanceId: catalogInstanceId, id: categoryId, locale: locale])
+ if (detailsXML) {
+ renderDetails(XML.toJSONObject(detailsXML)?.category?.toString())
+ } else {
+ notFound()
+ }
+ }
+
+ def add(Integer catalogInstanceId) {
+ Map values = request.JSON + [catalogInstanceId: catalogInstanceId]
+ Category category = categoryManagerService.createCategory(values, auditUserName)
+ sequence(category)
+ renderMQObject(category, CATEGORY_CREATE)
+ }
+
+ def update(Integer catalogInstanceId, Integer id) {
+ Map values = request.JSON
+ if (isDefaultLocale()) {
+ Category category = categoryManagerService.updateCategory(values, catalogInstanceId, id, auditUserName)
+ sequence(category)
+ renderMQObject(category, CATEGORY_UPDATE)
+ } else {
+ CategoryLocale categoryLocale = categoryManagerService.updateCategoryLocale(values + [locale: locale],
+ catalogInstanceId, id, auditUserName)
+ renderObject(categoryLocale, categoryLocale?.categoryId)
+ }
+ }
+
+ def move(Integer catalogInstanceId, Integer id) {
+ Map values = request.JSON
+ MoveCategoryCommand cmd = new MoveCategoryCommand(values)
+ if (cmd.validate()) {
+ Category category = categoryManagerService.moveCategory(values + [catalogInstanceId: catalogInstanceId, id: id], auditUserName)
+ sequence(category)
+ renderMQObject(category, CATEGORY_MOVE)
+ } else { renderObject cmd }
+ }
+
+ def remove(Integer catalogInstanceId, Integer id, Integer version) {
+ Category category = categoryManagerService.deleteCategory(catalogInstanceId, id, auditUserName, version)
+ sequence(category)
+ renderMQDelete(category, CATEGORY_DELETE)
+ }
+
+ private void sequence(Category category) {
+ if (category && !category.hasErrors()) { categoryManagerService.sequence(category) }
+ }
+
+ private void renderDetails(String categoryDetails) {
+ render toJson([results: new JsonSlurper().parseText(categoryDetails)])
+ }
+}
Index: grails-app/controllers/com/lemans/ds/category/CategoryPartController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/category/CategoryPartController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/category/CategoryPartController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,18 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.ReportAndLocaleCommonsController
+
+class CategoryPartController extends ReportAndLocaleCommonsController {
+
+ def partManagerService
+
+ def addOrRemove(Integer catalogInstanceId, Integer categoryId) {
+ Map values = request.JSON + [catalogInstanceId: catalogInstanceId, categoryId: categoryId]
+ if (values.action == 'add' || values.action == 'remove') {
+ if (values.partNumbers && !values.partNumbers?.isEmpty()) {
+ Map data = partManagerService.addOrRemoveParts(values, auditUserName)
+ data.errors ? renderErrors(data.errors) : render(toJson([results: []]))
+ } else { renderErrors(['No part selected']) }
+ } else { renderErrors(['action must either be add or remove']) }
+ }
+}
Index: grails-app/controllers/com/lemans/ds/category/CategorySubComCodeController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/category/CategorySubComCodeController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/category/CategorySubComCodeController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,35 @@
+package com.lemans.ds.category
+
+import static com.lemans.ds.rabbit.QueueRequestType.CATEGORY_SUB_COM_CODE_CREATE
+import static com.lemans.ds.rabbit.QueueRequestType.CATEGORY_SUB_COM_CODE_DELETE
+
+import com.lemans.ds.ReportAndLocaleCommonsController
+
+class CategorySubComCodeController extends ReportAndLocaleCommonsController {
+
+ def categorySubComCodeService
+ def categorySubComCodeManagerService
+
+ def index() {
+ renderMany categorySubComCodeService.findCategorySubComCodes(common() + category())
+ }
+
+ def show(Integer subComCodeId) {
+ renderOne categorySubComCodeService.findCategorySubComCode(common() + category() + [subComCodeId: subComCodeId])
+ }
+
+ def add() {
+ Map values = request.JSON + category()
+ CategorySubComCode categorySubComCode = categorySubComCodeManagerService.addSubComCode(values, auditUserName)
+ renderMQObject(categorySubComCode, CATEGORY_SUB_COM_CODE_CREATE, categorySubComCode?.subComCodeId)
+ }
+
+ def remove(Integer subComCodeId) {
+ CategorySubComCode subComCode = categorySubComCodeManagerService.removeSubComCode(category(), subComCodeId, auditUserName)
+ renderMQDelete(subComCode, CATEGORY_SUB_COM_CODE_DELETE)
+ }
+
+ private Map category() {
+ [catalogInstanceId: params.int('catalogInstanceId'), categoryId: params.int('categoryId')]
+ }
+}
Index: grails-app/controllers/com/lemans/ds/explosion/ExplosionDiagramController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/explosion/ExplosionDiagramController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/explosion/ExplosionDiagramController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,31 @@
+package com.lemans.ds.explosion
+
+import com.lemans.LemansApiController
+
+class ExplosionDiagramController extends LemansApiController {
+
+ def explosionDiagramService
+
+ def explosionDiagramManagerService
+
+ def index() {
+ Map criteria = common() + pagination()
+ renderPaginated explosionDiagramService.findExplosionDiagrams(criteria)
+ }
+
+ def show(Integer explosionDiagramId) {
+ renderOne explosionDiagramService.findExplosionDiagram(explosionDiagramId)
+ }
+
+ def add() {
+ renderObject explosionDiagramManagerService.addExplosionDiagram(request.JSON, auditUserName)
+ }
+
+ def update(Integer explosionDiagramId) {
+ renderObject explosionDiagramManagerService.updateExplosionDiagram(request.JSON, explosionDiagramId, auditUserName)
+ }
+
+ def delete(Integer explosionDiagramId) {
+ renderDelete explosionDiagramManagerService.deleteExplosionDiagram(explosionDiagramId, auditUserName)
+ }
+}
Index: grails-app/controllers/com/lemans/ds/fitment/MakeController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/fitment/MakeController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/fitment/MakeController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,36 @@
+package com.lemans.ds.fitment
+
+import com.lemans.LemansApiController
+
+class MakeController extends LemansApiController {
+
+ def makeService
+
+ def makeManagerService
+
+ def index() {
+ Map criteria = common() + pagination() + filters(['makeName'])
+ Map data = makeService.findMakes(criteria)
+ renderPaginated data
+ }
+
+ def show(Integer makeId) {
+ Map data = makeService.findMakeById(makeId)
+ renderOne data
+ }
+
+ def add() {
+ Make make = makeManagerService.createMake(request.JSON, auditUserName)
+ renderObject(make)
+ }
+
+ def update(Integer makeId) {
+ Make make = makeManagerService.updateMake(request.JSON, makeId, auditUserName)
+ renderObject(make)
+ }
+
+ def remove(Integer makeId) {
+ Make make = makeManagerService.deleteMake(makeId, auditUserName)
+ renderDelete(make)
+ }
+}
Index: grails-app/controllers/com/lemans/ds/fitment/ModelController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/fitment/ModelController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/fitment/ModelController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,83 @@
+package com.lemans.ds.fitment
+
+import com.lemans.LemansApiController
+import org.json.JSONObject
+import org.json.XML
+
+class ModelController extends LemansApiController {
+
+ def modelService
+
+ def modelManagerService
+
+ def index(Integer makeId) {
+ if (makeId) {
+ renderModelDetails(modelService.findModelDetails(makeId))
+ } else { notFound([[type: 'error', text: 'Make is required']]) }
+ }
+
+ def show(Integer modelId) {
+ String modelDetails = modelService.findModelDetails(params.int('makeId'), modelId)
+ if (modelDetails) {
+ JSONObject json = new JSONObject()
+ json.put('results', XML.toJSONObject(modelDetails).model.getJSONObject(0))
+ render contentType: 'application/json', text: json
+ } else { notFound() }
+ }
+
+ def add() {
+ Map values = values()
+ if (values.operation && values.operation != 'INSERT') { bulkOperation(values) }
+ else {
+ Model model = modelManagerService.createModel(values, auditUserName)
+ renderObject(model)
+ }
+ }
+
+ def update(Integer modelId) {
+ Model model = modelManagerService.updateModel(values(), modelId, auditUserName)
+ renderObject(model)
+ }
+
+ def remove() {
+ Map data = modelManagerService.deleteModel(makeModel(), auditUserName)
+ if (data.errors) {
+ if (data.errors.find { it.startsWith('Invalid Model') }) { notFound() }
+ else { renderErrors(data.errors) }
+ } else { renderEmpty() }
+ }
+
+ private void renderModelDetails(String modelDetails) {
+ if (modelDetails) {
+ JSONObject json = new JSONObject()
+ json.put('results', XML.toJSONObject(modelDetails).model)
+ render contentType: 'application/json', text: json
+ } else { renderEmptyResults() }
+ }
+
+ private bulkOperation(values) {
+ String operation = values.operation
+ if (operation == 'ADDYEAR' || operation == 'DELETEYEAR' || operation == 'DELETE') {
+ Map data = modelManagerService.updateMultipleModels(values, auditUserName)
+ data.errors ? renderErrors(data.errors) : renderEmptyResults()
+ } else { renderErrors(['operation must be INSERT, ADDYEAR, DELETEYEAR or DELETE']) }
+ }
+
+ private Map make() {
+ [makeId: params.int('makeId')]
+ }
+
+ private Map makeModel() {
+ make() + [modelId: params.int('modelId')]
+ }
+
+ private Map values() {
+ Map values = request.JSON
+ values.makeId = params.int('makeId')
+ values
+ }
+
+ private void renderEmptyResults() {
+ render(toJson([results: []]))
+ }
+}
Index: grails-app/controllers/com/lemans/ds/fitment/ModelYearController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/fitment/ModelYearController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/fitment/ModelYearController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,64 @@
+package com.lemans.ds.fitment
+
+import com.lemans.LemansApiController
+
+class ModelYearController extends LemansApiController {
+
+ def modelYearService
+
+ def modelYearManagerService
+
+ def index() {
+ Map criteria = common() + pagination() + makeModel()
+ Map data = modelYearService.findYears(criteria)
+ renderPaginated data
+ }
+
+ def add() {
+ Map values = values()
+ List errors = []
+ yearValidations(values, errors)
+ if (errors) { renderErrors(errors) }
+ else { values.operation == 'DELETE' ? deleteYears(values) : createModelYearOrYears(values) }
+ }
+
+ def remove(Integer yearId) {
+ renderDeleteYear(modelYearManagerService.deleteYear(makeModel() + [yearId: yearId], auditUserName))
+ }
+
+ private void yearValidations(Map values, List errors) {
+ String operation = values.operation
+ if (!values.years && !values.year) { errors << 'year(s) is required' }
+ if (operation && !(['INSERT', 'DELETE'].contains(operation))) { errors << 'operation must be INSERT or DELETE' }
+ }
+
+ private createModelYearOrYears(Map values) {
+ Map data = modelYearManagerService.createMultipleYears(values, auditUserName)
+ if (data.errors) { renderErrors(data.errors) }
+ else {
+ List modelYears = modelYearService.findModelYearsByYears(params.int('modelId'), values.years)
+ values.year ? renderOne(modelYears[0]) : renderList(modelYears)
+ }
+ }
+
+ private deleteYears(Map values) {
+ renderDeleteYear(modelYearManagerService.deleteMultipleYears(values, auditUserName))
+ }
+
+ private renderDeleteYear(Map data) {
+ data.errors ? renderErrors(data.errors) : render(toJson([:]))
+ }
+
+ private Map makeModel() {
+ [makeId: params.int('makeId'), modelId: params.int('modelId')]
+ }
+
+ private Map values() {
+ Map values = request.JSON
+ values.makeId = params.int('makeId')
+ values.modelId = params.int('modelId')
+ Integer year = values.year
+ if (year) { values.years = [year] }
+ values
+ }
+}
Index: grails-app/controllers/com/lemans/ds/fitment/PartFitmentController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/fitment/PartFitmentController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/fitment/PartFitmentController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,75 @@
+package com.lemans.ds.fitment
+
+import com.lemans.LemansApiController
+import org.json.JSONObject
+import org.json.XML
+
+/**
+ * Created by vramisetti on 6/12/2017.
+ */
+class PartFitmentController extends LemansApiController {
+
+ def partFitmentManagerService
+
+ def partFitmentService
+
+ def fitmentsByPart(String partNumber) {
+ Map data = partFitmentService.findFitmentsByPart([partNumber: partNumber] + common() + pagination())
+ if (data.results) {
+ JSONObject json = new JSONObject()
+ json.put('results', XML.toJSONObject(data.results).model)
+ json.put('meta', [totalRecords: data.totalRecords])
+ render contentType: 'application/json', text: json
+ } else { renderEmptyResults() }
+ }
+
+ def add(String partNumber) {
+ Map values = request.JSON
+ values.partNumber = partNumber
+ String operation = values.operation
+ if (operation == 'DELETE') { deleteMultipleFitments(values) }
+ else { createPartFitments(values) }
+ }
+
+ def delete(String partNumber, Integer fitmentId) {
+ renderDeleteYear(partFitmentManagerService.deleteFitment(partNumber, fitmentId, auditUserName))
+ }
+
+ private deleteMultipleFitments(Map values) {
+ if (values.partFitmentIds) { renderDeleteYear(partFitmentManagerService.deleteMultipleFitments(values, auditUserName)) }
+ else { renderErrors(['partFitmentIds are required']) }
+ }
+
+ private createPartFitments(Map values) {
+ List errors = []
+ Integer modelYearId = values.modelYearId
+ if (modelYearId) { values.modelYearIds = [modelYearId] }
+ modelYearValidation(values, errors)
+ if (errors) { renderErrors(errors) }
+ else {
+ Map data = partFitmentManagerService.createFitments(values, auditUserName)
+ if (data.errors) { renderErrors(data.errors) }
+ else {
+ List fitments = partFitmentService.findFitmentsByPartNumberAndModelYearIds(values.partNumber, values.modelYearIds)
+ if (values.positions && fitments) {
+ partFitmentManagerService.assignFitmentPositions(values.positions, fitments*.partFitmentId, auditUserName)
+ }
+ values.modelYearId ? renderOne(fitments[0]) : renderList(fitments)
+ }
+ }
+ }
+
+ private void modelYearValidation(Map values, List errors) {
+ String operation = values.operation
+ if (!values.modelYearId && !values.modelYearIds) { errors << 'modelYears(s) is required' }
+ if (operation && !(['INSERT'].contains(operation))) { errors << 'operation must be INSERT or DELETE' }
+ }
+
+ private renderDeleteYear(Map data) {
+ data.errors ? renderErrors(data.errors) : render(toJson([:]))
+ }
+
+ private void renderEmptyResults() {
+ render(toJson([results: [], meta: [totalRecords: 0]]))
+ }
+}
Index: grails-app/controllers/com/lemans/ds/fitment/SegmentController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/fitment/SegmentController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/fitment/SegmentController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,33 @@
+package com.lemans.ds.fitment
+
+import com.lemans.LemansApiController
+
+class SegmentController extends LemansApiController {
+
+ def segmentService
+
+ def segmentManagerService
+
+ def segments() {
+ Map criteria = common() + pagination()
+ Map data = segmentService.findSegments(criteria)
+ renderPaginated data
+ }
+
+ def segmentValues(Integer makeId, String segmentCode) {
+ Map criteria = common() + pagination() + [segmentCode: segmentCode, value: params.value, makeId: makeId]
+ Map data = segmentService.findSegmentValuesBySegmentCode(criteria)
+ renderPaginated data
+ }
+
+ def addUpdateOrDeleteModelSegment(Integer makeId, Integer modelId, String segmentCode) {
+ Map criteria = [makeId: makeId, modelId: modelId, segmentCode: segmentCode, value: request.JSON?.value]
+ ModelSegment modelSegment = segmentManagerService.updateOrCreateModelSegment(criteria, auditUserName)
+ modelSegment ? (modelSegment.hasErrors() ? renderErrors(modelSegment) : renderModelSegment(modelSegment)) : notFound()
+ }
+
+ private renderModelSegment(ModelSegment modelSegment) {
+ renderOne([modelSegmentId: modelSegment.id, modelId: modelSegment.model.id, segmentId: modelSegment.segment.id,
+ value: modelSegment.dateDeleted ? null : modelSegment.segmentValue])
+ }
+}
Index: grails-app/controllers/com/lemans/ds/flag/FlagController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/flag/FlagController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/flag/FlagController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,13 @@
+package com.lemans.ds.flag
+
+import com.lemans.LemansApiController
+
+class FlagController extends LemansApiController {
+
+ def flagService
+
+ def index() {
+ Map criteria = common() + pagination() + filters(['entityClass'])
+ renderPaginated flagService.findFlags(criteria)
+ }
+}
Index: grails-app/controllers/com/lemans/ds/flag/FlagValueController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/flag/FlagValueController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/flag/FlagValueController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,29 @@
+package com.lemans.ds.flag
+
+import com.lemans.LemansApiController
+
+class FlagValueController extends LemansApiController {
+
+ def flagValueService
+
+ def flagValueManagerService
+
+ def show() {
+ renderOne flagValueService.findById(filters(['entityClass', 'entityId', 'flagId']))
+ }
+
+ def index() {
+ Map criteria = common() + pagination() + filters(['entityClass', 'entityId'])
+ renderPaginated flagValueService.findFlags(criteria)
+ }
+
+ def addOrDeleteFlagValues() {
+ Map criteria = request.JSON + filters(['entityClass', 'entityId'])
+ if (criteria.operation && !(criteria.operation in ['INSERT', 'DELETE'])) {
+ renderErrors(['operation must either be INSERT or DELETE'])
+ } else {
+ Map data = flagValueManagerService.addOrDeleteFlagValues(criteria, auditUserName)
+ data.errors ? renderErrors(data.errors) : render(toJson([results: []]))
+ }
+ }
+}
Index: grails-app/controllers/com/lemans/ds/locale/BulkLocaleExportController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/locale/BulkLocaleExportController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/locale/BulkLocaleExportController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,36 @@
+package com.lemans.ds.locale
+
+import com.lemans.ds.ReportAndLocaleCommonsController
+
+class BulkLocaleExportController extends ReportAndLocaleCommonsController {
+
+ def ssrReporter
+
+ def reportServerExportFolder
+
+ def downloadBulkExportFile(String reportName) {
+ String url
+ List urlKeys
+
+ switch (reportName) {
+ case 'productLocaleExport':
+ url = '/ProductExport'
+ urlKeys = ['productId', 'productName', 'categoryId', 'brandId', 'isDigiActive', 'dashboardUrl']
+ break
+ case 'categoryAttributeLocaleExport':
+ url = '/CategoryAttributeValueExport'
+ urlKeys = ['categoryId', 'dashboardUrl']
+ break
+ case 'partLocaleExport':
+ url = '/PartExport'
+ urlKeys = ['partNumber', 'exportPartList', 'partDescr', 'qPart', 'partStatusId', 'derivedPartStatusId',
+ 'brandId', 'vendorId', 'vendorPartNumber', 'categoryId', 'productId', 'subComCodeId',
+ 'isDigiActive', 'modelId', 'startYear', 'endYear', 'attributeNameId', 'attributeValueId',
+ 'dashboardUrl']
+ break
+ }
+ Map criteria = [reportFormat: params.reportFormat ?: 'Excel', url: reportServerExportFolder + url]
+ criteria.urlParams = ssrReporter.createUrlParams(urlKeys, params + [dashboardUrl: params.urlContext])
+ generateReport(criteria, response, ssrReporter)
+ }
+}
Index: grails-app/controllers/com/lemans/ds/locale/BulkLocaleImportController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/locale/BulkLocaleImportController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/locale/BulkLocaleImportController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,92 @@
+package com.lemans.ds.locale
+
+import com.lemans.LemansApiController
+import com.lemans.ds.importing.BulkImportJob
+import com.lemans.ds.importing.LocaleImportProcess
+
+
+class BulkLocaleImportController extends LemansApiController {
+
+ def bulkImportManagerService
+
+ def bulkImportService
+
+ def index() {
+ Map criteria = common() + pagination() + filters(['importType'])
+ renderPaginated bulkImportService.findBulkImports(criteria)
+ }
+
+ def show(Integer importFileId) {
+ renderOne bulkImportService.findLocaleImportProcessById([localeImportProcessId: importFileId])
+ }
+
+ def add() {
+ Map values = request.JSON
+ List errors = validate(values)
+ if (errors) { renderErrors(errors) }
+ else {
+ LocaleImportProcess localeImportProcess = bulkImportManagerService.addLocaleImportProcessFile(values, auditUserName)
+ renderObject localeImportProcess
+ Integer processId = localeImportProcess.id
+ if (processId) {
+ BulkImportJob.triggerNow(['processId': processId])
+ }
+ }
+ }
+
+ def remove(Integer importFileId) {
+ LocaleImportProcess localeImportProcess = bulkImportManagerService.deleteLocaleImportProcess(importFileId, auditUserName)
+ renderDelete localeImportProcess
+ }
+
+ def download(Integer importFileId, String fileId) {
+ fileId == 'Original' ? downloadOriginal(importFileId) : downloadReport(importFileId)
+ }
+
+ private List validate(Map values) {
+ List errors = []
+ if (!values.tempId) {
+ errors << 'tempId is required'
+ }
+ if (!values.importType) {
+ errors << 'ImportType is required'
+ }
+ if (!values.extension) {
+ errors << 'Extension is required'
+ }
+ errors
+ }
+
+ private downloadOriginal(Integer importFileId) {
+ Closure file = { Map localeImportProcess ->
+ bulkImportService.findLocaleImportProcessOriginalFile(localeImportProcess)
+ }
+ downloadImportFile(importFileId, file)
+ }
+
+ private downloadReport(Integer importFileId) {
+ Closure file = { Map localeImportProcess ->
+ bulkImportService.findLocaleImportProcessReportFile(localeImportProcess)
+ }
+ downloadImportFile(importFileId, file, 'Report.xls')
+ }
+
+ private downloadImportFile(Integer importFileId, Closure fileToDownload, String fileNameToBeOverwritten = null) {
+ Map localeImportProcess = bulkImportService.findLocaleImportProcessById([localeImportProcessId: importFileId])
+ if (canDownloadFile(fileNameToBeOverwritten, localeImportProcess)) {
+ File file = fileToDownload(localeImportProcess)
+ if (file) {
+ response.setHeader 'Content-disposition',
+ "attachment;filename=${fileNameToBeOverwritten ?: "${localeImportProcess.originalFileName}.${localeImportProcess.extension}"}"
+ response.contentType = localeImportProcess?.mimeType
+ response.outputStream << file.newInputStream()
+ response.outputStream.flush()
+ } else { renderErrors(['File could not be found!. Please contact helpdesk']) }
+ } else { notFound() }
+ }
+
+ private boolean canDownloadFile(String fileNameToBeOverwritten, Map localeImportProcess) {
+ localeImportProcess && ((localeImportProcess.originalFileName && !fileNameToBeOverwritten) ||
+ (fileNameToBeOverwritten.equalsIgnoreCase('Report.xls') && localeImportProcess.status == 'Partial Success'))
+ }
+}
Index: grails-app/controllers/com/lemans/ds/part/PartAssociationController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/part/PartAssociationController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/part/PartAssociationController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,46 @@
+package com.lemans.ds.part
+
+import com.lemans.LemansApiController
+
+class PartAssociationController extends LemansApiController {
+
+ def partAssociationService
+
+ def partAssociationManagerService
+
+ def relatedParts(String partNumber) {
+ Map criteria = common() + pagination() + [partNumber: partNumber]
+ Map data = partAssociationService.findRelatedParts(criteria)
+ renderPaginated data
+ }
+
+ def referralParts(String partNumber) {
+ Map criteria = common() + pagination() + ['relatedPartNumber': partNumber]
+ Map data = partAssociationService.findReferralParts(criteria)
+ renderPaginated data
+ }
+
+ def show(Integer id) {
+ Map criteria = common() + pagination() + [partAssociationId: id]
+ Map data = partAssociationService.findPartAssociation(criteria)
+ renderOne data
+ }
+
+ def add(String partNumber) {
+ Map input = request.JSON
+ input.relatedPartNumber = input.relatedPartNumber.toString().replaceAll('[^a-zA-Z0-9]', '')
+ PartAssociation partAssociation = partAssociationManagerService.addPartAssociation(partNumber, input, auditUserName)
+ renderObject partAssociation
+ }
+
+ def update(Integer id) {
+ Map input = request.JSON
+ PartAssociation partAssociation = partAssociationManagerService.updatePartAssociation(id, input, auditUserName)
+ renderObject partAssociation
+ }
+
+ def remove(Integer id) {
+ PartAssociation partAssociation = partAssociationManagerService.deletePartAssociation(id, auditUserName)
+ renderDelete partAssociation
+ }
+}
Index: grails-app/controllers/com/lemans/ds/part/PartAttributeController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/part/PartAttributeController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/part/PartAttributeController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,47 @@
+package com.lemans.ds.part
+
+import com.lemans.ds.ReportAndLocaleCommonsController
+import groovy.json.JsonSlurper
+import org.json.XML
+
+class PartAttributeController extends ReportAndLocaleCommonsController {
+
+ def partAttributeService
+ def partAttributeManagerService
+
+ def add(String partNumber, Integer attributeNameId) {
+ Map criteria = request.JSON + [partNumber: partNumber, attributeNameId: attributeNameId]
+ renderResults partAttributeManagerService.addSinglePartAttributeRelation(criteria, auditUserName)
+ }
+
+ def remove() {
+ Map criteria = filters(['partNumber', 'attributeNameId', 'attributeValueId'])
+ renderResults partAttributeManagerService.removeSinglePartAttributeRelation(criteria, auditUserName)
+ }
+
+ def bulkAddOrRemoveOperation() {
+ renderResults partAttributeManagerService.bulkAddOrRemove(request.JSON, auditUserName)
+ }
+
+ def bulkReplaceOperation(String partNumber, Integer attributeNameId) {
+ Map values = request.JSON + [partNumber: partNumber, attributeNameId: attributeNameId]
+ renderResults partAttributeManagerService.bulkReplaceAttributeValueRelation(values, auditUserName)
+ }
+
+ def categoryPartDetails(Integer catalogInstanceId, Integer categoryId) {
+ List partNumbers = request.JSON?.partNumber
+ Map criteria = [catalogInstanceId: catalogInstanceId, id: categoryId, locale: locale]
+ String detailsXML = isDefaultLocale() ? partAttributeService.partAttributeDetails(criteria, partNumbers)
+ : partAttributeService.partAttributeLocaleDetails(criteria, partNumbers)
+ detailsXML ? render(toJson([results: new JsonSlurper().parseText(XML.toJSONObject(detailsXML)?.category?.toString())])) : notFound()
+ }
+
+ private void renderResults(Map data) {
+ if (data.messages.find { it.type == 'error' }) {
+ response.status = 400
+ render toJson(data)
+ } else {
+ render toJson([results: [:]])
+ }
+ }
+}
Index: grails-app/controllers/com/lemans/ds/part/PartController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/part/PartController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/part/PartController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,37 @@
+package com.lemans.ds.part
+
+import com.lemans.ds.ReportAndLocaleCommonsController
+
+class PartController extends ReportAndLocaleCommonsController {
+
+ def partService
+
+ def partManagerService
+
+ def index(PartSearchCommand cmd) {
+ List errors = []
+ if (!params.pageSize) { errors << [type: 'error', text: 'pageSize is required'] }
+ if (errors) {
+ response.status = 400
+ renderMessages(errors)
+ } else {
+ Map criteria = common() + pagination() + cmd.properties
+ renderPaginated partService.findParts(criteria)
+ }
+ }
+
+ def show(String partNumber) {
+ Map criteria = [partNumber: partNumber, locale: locale]
+ renderOne(isDefaultLocale() ? partService.findPartByPartNumber(criteria) : partService.findPartsByLocale(criteria))
+ }
+
+ def update(String partNumber) {
+ Map input = request.JSON + [locale: locale]
+ if (input.primaryMediaId || isDefaultLocale()) {
+ renderObject partManagerService.updatePart(input, partNumber.toUpperCase(), auditUserName)
+ } else {
+ PartLocale partLocale = partManagerService.updatePartLocale(input, partNumber.toUpperCase(), auditUserName)
+ renderObject(partLocale, partLocale?.partNumber)
+ }
+ }
+}
Index: grails-app/controllers/com/lemans/ds/part/PartSearchCommand.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/part/PartSearchCommand.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/part/PartSearchCommand.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,35 @@
+package com.lemans.ds.part
+
+import grails.validation.Validateable
+
+
+class PartSearchCommand implements Validateable {
+
+ String q
+ String partNumber
+ String partStatusCode
+ String partDescr
+ String brandCode
+ String brandName
+ String vendorId
+ String vendorName
+ String vendorPartNumber
+ String subComCode
+ String productId
+ Integer categoryId
+
+ static constraints = {
+ q nullable: true, minSize: 2
+ partNumber nullable: true, minSize: 2
+ partStatusCode nullable: true, minSize: 1
+ partDescr nullable: true, minSize: 2
+ brandCode nullable: true, minSize: 2
+ brandName nullable: true, minSize: 2
+ vendorId nullable: true, minSize: 2
+ vendorName nullable: true, minSize: 2
+ vendorPartNumber nullable: true, minSize: 2
+ subComCode nullable: true
+ productId nullable: true
+ categoryId nullable: true
+ }
+}
Index: grails-app/controllers/com/lemans/ds/product/CategoryProductController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/product/CategoryProductController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/product/CategoryProductController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,18 @@
+package com.lemans.ds.product
+
+import com.lemans.LemansApiController
+
+class CategoryProductController extends LemansApiController {
+
+ def categoryProductManagerService
+
+ def addOrRemoveAssociation(Integer catalogInstanceId, Integer categoryId) {
+ Map values = request.JSON + [catalogInstanceId: catalogInstanceId, categoryId: categoryId]
+ if (values.operation == 'INSERT' || values.operation == 'DELETE') {
+ Map data = categoryProductManagerService.addOrRemoveCategoryProduct(values, auditUserName)
+ data.errors ? renderErrors(data.errors) : render(toJson([results: [:]]))
+ } else {
+ renderErrors(['action must either be add or remove'])
+ }
+ }
+}
Index: grails-app/controllers/com/lemans/ds/product/ProductAssociationController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/product/ProductAssociationController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/product/ProductAssociationController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,46 @@
+package com.lemans.ds.product
+
+import com.lemans.LemansApiController
+
+class ProductAssociationController extends LemansApiController {
+
+ def productAssociationService
+
+ def productAssociationManagerService
+
+ def relatedProducts(Integer productId) {
+ Map criteria = common() + pagination() + [productId: productId]
+ Map data = productAssociationService.findRelatedProducts(criteria)
+ renderPaginated data
+ }
+
+ def referralProducts(Integer productId) {
+ Map criteria = common() + pagination() + [relatedProductId: productId]
+ Map data = productAssociationService.findReferralProducts(criteria)
+ renderPaginated data
+ }
+
+ def show(Integer id) {
+ Map criteria = common() + pagination() + [productAssociationId: id]
+ Map data = productAssociationService.findProductAssociation(criteria)
+ renderOne data
+ }
+
+ def add(Integer productId) {
+ Map input = request.JSON
+ input.relatedProductId = input.relatedProductId.toString().replaceAll('[^a-zA-Z0-9]', '')
+ ProductAssociation productAssociation = productAssociationManagerService.addProductAssociation(productId, input, auditUserName)
+ renderObject productAssociation
+ }
+
+ def update(Integer id) {
+ Map input = request.JSON
+ ProductAssociation productAssociation = productAssociationManagerService.updateProductAssociation(id, input, auditUserName)
+ renderObject productAssociation
+ }
+
+ def remove(Integer id) {
+ ProductAssociation productAssociation = productAssociationManagerService.deleteProductAssociation(id, auditUserName)
+ renderDelete productAssociation
+ }
+}
Index: grails-app/controllers/com/lemans/ds/product/ProductController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/product/ProductController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/product/ProductController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,49 @@
+package com.lemans.ds.product
+
+import com.lemans.ds.ReportAndLocaleCommonsController
+
+class ProductController extends ReportAndLocaleCommonsController {
+
+ def productService
+
+ def productManagerService
+
+ def index() {
+ Map criteria = common() + pagination() + catalog() + filters(['brandId', 'brandCode', 'isDigiActive',
+ 'productName', 'productNameLocale', 'productId', 'flagId', 'mode'])
+ if (params.categoryId) { criteria.categoryId = params.list('categoryId') }
+ renderPaginated productService.findProducts(criteria)
+ }
+
+ def show(Integer productId) {
+ Map criteria = common() + catalog() + [productId: productId, entityClass: 'Product']
+ renderOne productService.findProductById(criteria)
+ }
+
+ def add() {
+ Map values = request.JSON + catalog() + common()
+ if (!(values.isDigiActive)) { values.put('isDigiActive', '0') }
+ renderObject productManagerService.createProduct(values, auditUserName)
+ }
+
+ def update(Integer catalogInstanceId, Integer productId) {
+ Map values = request.JSON + common() + [productId: productId]
+ if (isDefaultLocale()) {
+ renderObject productManagerService.updateProduct(values, catalogInstanceId, auditUserName)
+ } else {
+ ProductLocale productLocale = productManagerService.updateProductWithLocale(values, catalogInstanceId, auditUserName)
+ renderObject(productLocale, productLocale?.productId)
+ }
+ }
+
+ def remove(Integer catalogInstanceId, Integer productId) {
+ renderDelete productManagerService.deleteProduct(catalogInstanceId, productId, auditUserName)
+ }
+
+ def splitValidation(Integer productId) {
+ Map data = productService.splitValidationForProduct(productId)
+ render toJson([results: data.results])
+ }
+
+ private Map catalog() { [catalogInstanceId: params.int('catalogInstanceId')] }
+}
Index: grails-app/controllers/com/lemans/ds/product/ProductFeatureController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/product/ProductFeatureController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/product/ProductFeatureController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,61 @@
+package com.lemans.ds.product
+
+import com.lemans.ds.MoveCommand
+import com.lemans.ds.ReportAndLocaleCommonsController
+
+class ProductFeatureController extends ReportAndLocaleCommonsController {
+
+ def productFeatureService
+
+ def productFeatureManagerService
+
+ def index() {
+ Map criteria = common() + pagination() + product()
+ renderPaginated productFeatureService.findProductFeatures(criteria)
+ }
+
+ def show(Integer productFeatureId) {
+ Map criteria = product() + [productFeatureId: productFeatureId, locale: locale]
+ renderOne productFeatureService.findProductFeatureById(criteria)
+ }
+
+ def add() {
+ def body = request.JSON
+ List features = body.features ?: [body]
+ Map values = [features: features] + product()
+ Map data = productFeatureManagerService.createProductFeatures(values, auditUserName)
+ List messages = data.messages
+ if (messages) {
+ response.status = 400
+ renderMessages(data.messages)
+ } else {
+ messages = [infoMessage("Created ${features.size()} Product Feature(s)")]
+ }
+ render toJson(messages: messages, results: data.results)
+ }
+
+ def update(Integer productFeatureId) {
+ Map values = request.JSON + common() + product() + [productFeatureId: productFeatureId]
+ if (isDefaultLocale()) {
+ renderObject productFeatureManagerService.updateProductFeature(values, auditUserName)
+ } else {
+ ProductFeatureLocale featureLocale = productFeatureManagerService.updateProductFeatureWithLocale(values, auditUserName)
+ renderObject(featureLocale, featureLocale?.productFeatureId)
+ }
+ }
+
+ def remove(Integer productId, Integer productFeatureId) {
+ renderDelete productFeatureManagerService.deleteProductFeature(productId, productFeatureId, auditUserName)
+ }
+
+ def move(Integer productFeatureId) {
+ Map values = request.JSON
+ MoveCommand cmd = new MoveCommand(values)
+ Map criteria = values + product() + [productFeatureId: productFeatureId]
+ renderObject(cmd.validate() ? productFeatureManagerService.moveProductFeature(criteria, auditUserName) : cmd)
+ }
+
+ private Map product() {
+ [catalogInstanceId: params.int('catalogInstanceId'), productId: params.int('productId')]
+ }
+}
Index: grails-app/controllers/com/lemans/ds/product/ProductMerchandiseController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/product/ProductMerchandiseController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/product/ProductMerchandiseController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,37 @@
+package com.lemans.ds.product
+
+import com.lemans.ds.ReportAndLocaleCommonsController
+import groovy.json.JsonSlurper
+import org.json.XML
+
+class ProductMerchandiseController extends ReportAndLocaleCommonsController {
+
+ def productMerchandiseService
+ def productMerchandiseManagerService
+
+ def show(Integer productCategoryAttributeId) {
+ Map criteria = common() + [productCategoryAttributeId: productCategoryAttributeId]
+ renderOne productMerchandiseService.findCategoryAttributeById(criteria)
+ }
+
+ def getMerchandiseProductAttributeDetails() {
+ Map values = request.JSON + common() + pagination() + product()
+ Map criteria = [catalogInstanceId: values.catalogInstanceId, productId: values.productId, locale: locale]
+ String detailsXML = isDefaultLocale() ? productMerchandiseService.findProductAttributeMerchandiseDetails(criteria) :
+ productMerchandiseService.findProductAttributeMerchandiseLocaleDetails(criteria)
+ if (detailsXML) {
+ render toJson([results: new JsonSlurper().parseText(XML.toJSONObject(detailsXML)?.attribute?.toString())])
+ } else {
+ notFound()
+ }
+ }
+
+ def update(Integer productId, Integer categoryAttributeId) {
+ Map values = request.JSON + common() + [productId: productId, categoryAttributeId: categoryAttributeId]
+ renderObject productMerchandiseManagerService.updateProductCategoryAttribute(values, auditUserName)
+ }
+
+ private Map product() {
+ [catalogInstanceId: params.int('catalogInstanceId'), productId: params.int('productId')]
+ }
+}
Index: grails-app/controllers/com/lemans/ds/product/ProductPartController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/product/ProductPartController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/product/ProductPartController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,68 @@
+package com.lemans.ds.product
+
+import com.lemans.ds.ReportAndLocaleCommonsController
+import groovy.json.JsonSlurper
+import org.json.XML
+
+
+class ProductPartController extends ReportAndLocaleCommonsController {
+
+ def productPartService
+
+ def productPartManagerService
+
+ def productMerchandiseService
+
+ def index(Integer productId) {
+ Map criteria = common() + pagination() + catalog() + [productId: productId, attribute: params.attribute]
+ String detailsXML
+ if (criteria.attribute) {
+ detailsXML = productMerchandiseService.findProductPartMerchandiseDetails(criteria)
+ if (detailsXML) {
+ renderDetails(XML.toJSONObject(detailsXML)?.product?.toString())
+ } else if (criteria.locale == Locale.default || !validateLocale(criteria.locale)) {
+ notFound()
+ }
+ }
+ else {
+ Map data = productPartService.findProductParts(criteria)
+ renderPaginated data
+ }
+ }
+
+ def show(Integer productId, String partNumber) {
+ Map criteria = common() + catalog() + [productId: productId, partNumber: partNumber]
+ Map data = productPartService.findProductPartByComposite(criteria)
+ renderOne data
+ }
+
+ def bulkAddOrRemoveOperation(Integer productId) {
+ Map values = request.JSON
+ values.productId = productId
+ values << catalog()
+ Map data = productPartManagerService.bulkAddOrRemove(values, auditUserName)
+ renderPersistResults(data)
+ }
+
+ private void renderDetails(String categoryDetails) {
+ Map details = new JsonSlurper().parseText(categoryDetails)
+ Map data = [results: details]
+ render toJson(data)
+ }
+
+ def remove() {
+ Map criteria = [
+ catalogInstanceId: params.catalogInstanceId,
+ partNumber: params.partNumber,
+ productId: params.productId
+ ]
+ Map data = productPartManagerService.removeSingleProductPartRelation(criteria, auditUserName)
+ renderPersistResults(data)
+ }
+
+ private void renderPersistResults(Map data) {
+ data.errors ? renderErrors(data.errors) : render(toJson([results: [:]]))
+ }
+
+ private Map catalog() { [catalogInstanceId: params.int('catalogInstanceId')] }
+}
Index: grails-app/controllers/com/lemans/ds/publicationcategory/ProductPublicationCategoryController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/publicationcategory/ProductPublicationCategoryController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/publicationcategory/ProductPublicationCategoryController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,32 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.LemansApiController
+
+class ProductPublicationCategoryController extends LemansApiController {
+
+ def productPublicationCategoryService
+
+ def productPublicationCategoryManagerService
+
+ def byCategoryId(Integer categoryId) {
+ Map criteria = common() + pagination() + [categoryId: categoryId, isDigiActive: 1]
+ renderPaginated productPublicationCategoryService.productPublicationCategoriesByCategoryId(criteria)
+ }
+
+ def show(Integer productPublicationCategoryId) {
+ renderOne productPublicationCategoryService.productPublicationCategory(productPublicationCategoryId)
+ }
+
+ def byProductId(Integer productId) {
+ Map criteria = common() + pagination() + [productId: productId, isDigiActive: 1]
+ renderPaginated productPublicationCategoryService.productPublicationCategoriesByProductId(criteria)
+ }
+
+ def add(Integer categoryId) {
+ renderObject productPublicationCategoryManagerService.add(request.JSON, categoryId, auditUserName)
+ }
+
+ def delete(Integer categoryId, Integer productId) {
+ renderDelete productPublicationCategoryManagerService.deleteProductPublicationCategory(categoryId, productId, auditUserName)
+ }
+}
Index: grails-app/controllers/com/lemans/ds/publicationcategory/PublicationCategoryAttributeController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/publicationcategory/PublicationCategoryAttributeController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/publicationcategory/PublicationCategoryAttributeController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,46 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.LemansApiController
+import com.lemans.ds.MoveCommand
+
+class PublicationCategoryAttributeController extends LemansApiController {
+
+ def publicationCategoryAttributeService
+
+ def publicationCategoryAttributeManagerService
+
+ def index(Integer categoryId) {
+ Map criteria = common() + pagination() + [publicationCategoryId: categoryId]
+ renderPaginated publicationCategoryAttributeService.findAllAttributes(criteria)
+ }
+
+ def attributeOptions(Integer categoryId) {
+ renderPaginated publicationCategoryAttributeService.attributeOptions(categoryId)
+ }
+
+ def showByCompositeKey(Integer categoryId, Integer attributeNameId) {
+ renderOne publicationCategoryAttributeService.findPublicationCategoryAttribute(categoryId, attributeNameId)
+ }
+
+ def add(Integer categoryId) {
+ renderObject publicationCategoryAttributeManagerService.addAttribute(request.JSON + [publicationCategoryId: categoryId], auditUserName)
+ }
+
+ def delete(Integer categoryId, Integer attributeNameId) {
+ renderDelete publicationCategoryAttributeManagerService.deleteAttribute(categoryId, attributeNameId, auditUserName)
+ }
+
+ def show(Integer publicationCategoryAttributeId) {
+ renderOne publicationCategoryAttributeService.findById(publicationCategoryAttributeId)
+ }
+
+ def move(Integer categoryId, Integer attributeNameId) {
+ Map input = request.JSON + [publicationCategoryId: categoryId, attributeNameId: attributeNameId]
+ MoveCommand cmd = new MoveCommand([position: input.position, targetId: input.targetId])
+ if (cmd.validate()) {
+ renderObject publicationCategoryAttributeManagerService.moveAttribute(input, auditUserName)
+ } else {
+ renderObject cmd
+ }
+ }
+}
Index: grails-app/controllers/com/lemans/ds/publicationcategory/PublicationCategoryController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/publicationcategory/PublicationCategoryController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/publicationcategory/PublicationCategoryController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,42 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.LemansApiController
+import com.lemans.ds.MoveCategoryCommand
+
+class PublicationCategoryController extends LemansApiController {
+
+ def publicationCategoryService
+
+ def publicationCategoryManagerService
+
+ def index() {
+ Map criteria = common() + pagination()
+ renderPaginated publicationCategoryService.findPublicationCategories(criteria)
+ }
+
+ def show(Integer categoryId) {
+ renderOne publicationCategoryService.findPublicationCategoryById(categoryId)
+ }
+
+ def add() {
+ renderObject publicationCategoryManagerService.createCategory(request.JSON, auditUserName)
+ }
+
+ def update(Integer categoryId) {
+ renderObject publicationCategoryManagerService.updateCategory(request.JSON, categoryId, auditUserName)
+ }
+
+ def delete(Integer categoryId, Integer version) {
+ renderDelete publicationCategoryManagerService.deleteCategory(categoryId, version, auditUserName)
+ }
+
+ def move(Integer categoryId) {
+ Map input = request.JSON
+ MoveCategoryCommand cmd = new MoveCategoryCommand(input)
+ if (cmd.validate()) {
+ renderObject publicationCategoryManagerService.movePublicationCategory(input, categoryId, auditUserName)
+ } else {
+ renderObject cmd
+ }
+ }
+}
Index: grails-app/controllers/com/lemans/ds/search/BrandSearchController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/search/BrandSearchController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/search/BrandSearchController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,14 @@
+package com.lemans.ds.search
+
+import com.lemans.LemansApiController
+
+class BrandSearchController extends LemansApiController {
+
+ def brandSearchService
+
+ def brandSearch() {
+ Map criteria = common() + pagination() + [query: params.query, sort: params.sort ?: 'brandId']
+ Map data = brandSearchService.searchBrands(criteria)
+ renderPaginated(data)
+ }
+}
Index: grails-app/controllers/com/lemans/ds/search/GenericSearchCommand.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/search/GenericSearchCommand.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/search/GenericSearchCommand.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,72 @@
+package com.lemans.ds.search
+
+import grails.validation.Validateable
+
+/**
+ * Created by vramisetti on 3/9/2017.
+ */
+class GenericSearchCommand implements Validateable {
+
+ String q
+ String partNumber
+ String partStatusCode
+ String partDescr
+ String brandCode
+ String brandName
+ String vendorId
+ String vendorName
+ String vendorPartNumber
+ String subComCode
+ String productId
+ Integer assignableCategoryId
+ Integer brandId
+ Integer subComCodeId
+ Integer attributeNameId
+ Integer attributeValueId
+ String assignableProductId
+ Integer mediaTypeId
+ String productName
+ Integer modelId
+ Integer startYear
+ Integer endYear
+ Boolean isDigiActive
+ String mode
+ Integer flagId
+
+ static constraints = {
+ q nullable: true, minSize: 2
+ partNumber nullable: true, minSize: 2
+ partStatusCode nullable: true, minSize: 1
+ partDescr nullable: true, minSize: 2
+ brandCode nullable: true, minSize: 2
+ brandName nullable: true, minSize: 2
+ vendorId nullable: true, minSize: 2
+ vendorName nullable: true, minSize: 2
+ vendorPartNumber nullable: true, minSize: 2
+ subComCode nullable: true
+ productId nullable: true
+ attributeNameId nullable: true
+ attributeValueId nullable: true
+ modelId nullable: true
+ startYear nullable: true
+ endYear nullable: true
+ assignableCategoryId nullable: true
+ brandId nullable: true
+ subComCodeId nullable: true
+
+ assignableProductId nullable: true
+
+ isDigiActive nullable: true
+
+ mode nullable: true
+ flagId nullable: true
+ }
+
+ String setPartNumber(String partNumber) {
+ this.partNumber = partNumber.replaceAll('[^\\p{Alnum}]', '')
+ }
+
+ String setVendorPartNumber(String vendorPartNumber) {
+ this.vendorPartNumber = vendorPartNumber.replaceAll('[^\\p{Alnum}]', '')
+ }
+}
Index: grails-app/controllers/com/lemans/ds/search/GenericSearchController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/ds/search/GenericSearchController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/ds/search/GenericSearchController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,94 @@
+package com.lemans.ds.search
+
+import com.lemans.LemansApiController
+import org.json.JSONObject
+import org.json.XML
+
+class GenericSearchController extends LemansApiController {
+
+ def genericSearchService
+
+ def filterOptions(String searchType, String filterName, GenericSearchCommand cmd) {
+ String filterType = params.filterType ?: params.searchType
+ List errors = collectFilterOptionErrors(searchType, filterType, filterName)
+ if (errors) { renderErrors(errors) }
+ else {
+ renderPaginated genericSearchService.filterOptions(searchType, filterType, cmd.properties + [filterName: filterName]
+ + multiValuedParameters())
+ }
+ }
+
+ def filters(String searchType, String filterType, GenericSearchCommand cmd) {
+ if (filterType && validFilterType(filterType, searchType)) {
+ renderXMLasJSON genericSearchService.filters(searchType, filterType, cmd.properties)
+ } else { renderErrors(['Invalid Filter Type']) }
+ }
+
+ def search(String searchType, GenericSearchCommand cmd) {
+ String filterType = params.filterType ?: params.searchType
+ List errors = collectErrors(filterType, searchType)
+ if (errors) { renderErrors(errors) }
+ else {
+ Map criteria = pagination() + cmd.properties + multiValuedParameters()
+ if (criteria.mode) {
+ renderXMLasPaginatedJSON genericSearchService.searchResultsByMode(searchType, filterType, criteria)
+ } else {
+ renderPaginated genericSearchService.searchResults(searchType, filterType, criteria)
+ }
+ }
+ }
+
+ private boolean validFilterType(String filterType, String searchType) {
+ Map acceptableFilterTypes = FilterXmlGenerator.SEARCH_FILTER_TYPE_MAPPING[searchType.toUpperCase()]
+ acceptableFilterTypes && acceptableFilterTypes[filterType.toUpperCase()]
+ }
+
+ private boolean validFilterName(String filterType, String searchType, String filterName) {
+ Map acceptableFilterTypes = FilterXmlGenerator.SEARCH_FILTER_TYPE_MAPPING[searchType.toUpperCase()]
+ if (acceptableFilterTypes) {
+ filterName in acceptableFilterTypes[filterType.toUpperCase()]
+ }
+ }
+
+ private List collectErrors(String filterType, String searchType) {
+ List errors = []
+ if (!params.pageSize) { errors << 'pageSize is required' }
+ if (!filterType) { errors << 'filterType is required' }
+ if ((filterType && !validFilterType(filterType, searchType))) { errors << 'Invalid Filter Type' }
+ errors
+ }
+
+ private List collectFilterOptionErrors(String searchType, String filterType, String filterName) {
+ List errors = []
+ if (!filterType) { errors << 'filterType is required' }
+ if ((filterType && !validFilterType(filterType, searchType))) { errors << 'Invalid Filter Type' }
+ if (!validFilterName(filterType, searchType, filterName)) { errors << 'Invalid Filter Name' }
+ errors
+ }
+
+ private void renderXMLasPaginatedJSON(Map data) {
+ if (data.totalRecords) {
+ JSONObject json = new JSONObject()
+ json.put('meta', [totalRecords: data.totalRecords])
+ json.put('results', ((JSONObject) XML.toJSONObject(data.resultXML).get('root')).get('part'))
+ render contentType: 'application/json', text: json
+ } else {
+ render toJson([meta: [totalRecords: 0], results: []])
+ }
+ }
+
+ private void renderXMLasJSON(String xml) {
+ if (xml && xml != '') {
+ JSONObject json = new JSONObject()
+ json.put('results', XML.toJSONObject(xml))
+ render contentType: 'application/json', text: json
+ } else { render toJson([results: [:]]) }
+ }
+
+ private Map multiValuedParameters() {
+ Map multiValuedParams = [:]
+ if (params.derivedPartStatusId) { multiValuedParams.derivedPartStatusId = params.list('derivedPartStatusId') }
+ if (params.categoryId) { multiValuedParams.categoryId = params.list('categoryId') }
+ multiValuedParams
+ }
+}
Index: grails-app/controllers/com/lemans/testing/StuffController.groovy
===================================================================
diff -u
--- grails-app/controllers/com/lemans/testing/StuffController.groovy (revision 0)
+++ grails-app/controllers/com/lemans/testing/StuffController.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,25 @@
+package com.lemans.testing
+
+import grails.converters.JSON
+
+@SuppressWarnings('Println')
+class StuffController {
+
+ def index() {
+ String wtf = params.wtf
+ if (wtf) { throw new IllegalArgumentException(wtf) }
+
+ def artefactHandlers = grailsApplication.artefactHandlers*.class.simpleName
+ log.warn "*** artefactHandlers = $artefactHandlers"
+
+ log.warn "*** interceptor artefact info = ${grailsApplication.getArtefactInfo('Interceptor').dump()}"
+
+ def stuff = grailsApplication.allClasses.collect {
+ 'Artefact:' + (it.toString() - 'class')
+ }.sort()
+
+ stuff += artefactHandlers.collect { 'ArtefactHandler: ' + it }
+
+ render (stuff as JSON)
+ }
+}
Index: grails-app/domain/com/lemans/ds/attribute/AttributeName.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/attribute/AttributeName.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/attribute/AttributeName.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,28 @@
+package com.lemans.ds.attribute
+
+import com.lemans.services.Auditable
+
+class AttributeName extends Auditable {
+ Integer id
+ String attributeName
+ String attributeDisplayName
+
+ static constraints = {
+ attributeName maxSize: 100, blank: false
+ attributeDisplayName maxSize: 100, nullable: true
+ }
+
+ static mapping = {
+ table 'attributeName'
+ id column: 'attributeNameId'
+ }
+
+ //implemented when PartAttribute is functional
+ /*boolean hasCategoryOrPartRelation() {
+ boolean categoryCount = Part.createCriteria().count {
+ // ('category', id)
+ isNull("dateDeleted")
+ }
+ }*/
+
+}
Index: grails-app/domain/com/lemans/ds/attribute/AttributeNameLocale.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/attribute/AttributeNameLocale.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/attribute/AttributeNameLocale.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,25 @@
+package com.lemans.ds.attribute
+
+import com.lemans.ds.DSLocale
+
+/**
+ * Created by MUmachi on 9/22/2017.
+ */
+class AttributeNameLocale extends DSLocale {
+ Integer id
+ Integer attributeNameId
+ String attributeName
+ String attributeDisplayName
+
+ static constraints = {
+ attributeName nullable: true, maxSize: 100
+ attributeDisplayName nullable: true, maxSize: 100
+ }
+
+ static mapping = {
+ table 'attributeNameLocale'
+ id column: 'attributeNameLocaleId'
+ attributeNameId updateable: false
+ }
+
+}
Index: grails-app/domain/com/lemans/ds/attribute/AttributeValue.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/attribute/AttributeValue.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/attribute/AttributeValue.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,18 @@
+package com.lemans.ds.attribute
+
+import com.lemans.services.Auditable
+
+class AttributeValue extends Auditable {
+ Integer id
+ String attributeValue
+
+ static constraints = {
+ attributeValue maxSize: 750, blank: false
+
+ }
+
+ static mapping = {
+ table 'attributeValue'
+ id column: 'attributeValueId'
+ }
+}
Index: grails-app/domain/com/lemans/ds/attribute/AttributeValueLocale.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/attribute/AttributeValueLocale.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/attribute/AttributeValueLocale.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,26 @@
+package com.lemans.ds.attribute
+
+import com.lemans.ds.DSLocale
+
+/**
+ * Created by MUmachi on 9/22/2017.
+ */
+class AttributeValueLocale extends DSLocale {
+ Integer id
+ String attributeValue
+ Integer attributeValueId
+ //String locale
+
+ static constraints = {
+ //locale maxSize: 2
+ attributeValue nullable: true, maxSize: 750
+
+ }
+
+ static mapping = {
+ table 'attributeValueLocale'
+ id column: 'attributeValueLocaleId'
+ attributeValueId updateable: false
+ }
+
+}
Index: grails-app/domain/com/lemans/ds/category/Category.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/category/Category.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/category/Category.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,50 @@
+package com.lemans.ds.category
+
+import com.lemans.services.Auditable
+
+
+class Category extends Auditable {
+
+ static final Integer MAX_DIRECT_CHILDREN = 999999
+
+ static final Integer MAX_DEPTH = 4
+
+ private static final Map IMMUTABLE_PROPERTIES = [
+ catalogInstanceId: 'Catalog',
+ parentCategoryId: 'Category Parent',
+ sequence: 'Category Sequence'
+ ].asImmutable()
+
+ Integer id
+
+ String categoryName
+
+ Integer catalogInstanceId
+
+ Integer parentCategoryId
+
+ Integer primaryMediaId
+
+ String description
+
+ Integer sequence
+
+ static constraints = {
+ categoryName blank: false, maxSize: 100
+ description nullable: true, maxSize: 2000
+ parentCategoryId nullable: true
+ primaryMediaId nullable: true
+ sequence nullable: true
+ }
+
+ static mapping = {
+ table 'Category'
+ id column: 'categoryId'
+ catalogInstanceId updateable: false
+ }
+
+ @Override
+ protected Map immutables() {
+ Category.IMMUTABLE_PROPERTIES
+ }
+}
Index: grails-app/domain/com/lemans/ds/category/CategoryAttribute.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/category/CategoryAttribute.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/category/CategoryAttribute.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,36 @@
+package com.lemans.ds.category
+
+import com.lemans.services.Auditable
+
+class CategoryAttribute extends Auditable {
+
+ private static final Map IMMUTABLE_PROPERTIES = [
+ categoryId: 'Category',
+ attributeNameId: 'Attribute Name'
+ ].asImmutable()
+
+ Integer id
+ Integer categoryId
+ Integer attributeNameId
+ Boolean isRequired
+ Boolean isDropdown
+ Boolean isHidden
+ Boolean isGroup
+ Boolean isKeyAttribute
+ Boolean allowMultipleValues
+
+ static constraints = {
+
+ }
+
+ static mapping = {
+ table 'CategoryAttribute'
+ id column: 'categoryAttributeId'
+ }
+
+
+ @Override
+ protected Map immutables() {
+ CategoryAttribute.IMMUTABLE_PROPERTIES
+ }
+}
Index: grails-app/domain/com/lemans/ds/category/CategoryAttributeLocale.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/category/CategoryAttributeLocale.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/category/CategoryAttributeLocale.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,25 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.DSLocale
+
+/**
+ * Created by MUmachi on 10/30/2017.
+ */
+class CategoryAttributeLocale extends DSLocale {
+
+ Integer id
+ Integer categoryAttributeId
+ String attributeName
+ String attributeDisplayName
+
+ static constraints = {
+ attributeName nullable: true, maxSize: 100
+ attributeDisplayName nullable: true, maxSize: 100
+ }
+
+ static mapping = {
+ table 'CategoryAttributeLocale'
+ id column: 'categoryAttributeLocaleId'
+ categoryAttributeId updateable: false
+ }
+}
Index: grails-app/domain/com/lemans/ds/category/CategoryAttributeValue.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/category/CategoryAttributeValue.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/category/CategoryAttributeValue.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,30 @@
+package com.lemans.ds.category
+
+import com.lemans.services.Auditable
+
+class CategoryAttributeValue extends Auditable {
+
+ private static final Map IMMUTABLE_PROPERTIES = [
+ categoryAttributeId: 'Category Attribute',
+ attributeValueId: 'Attribute Value'
+ ].asImmutable()
+
+ Integer id
+ Integer categoryAttributeId
+ Integer attributeValueId
+
+ static constraints = {
+
+ }
+
+ static mapping = {
+ table 'CategoryAttributeValue'
+ id column: 'categoryAttributeValueId'
+ }
+
+
+ @Override
+ protected Map immutables() {
+ CategoryAttributeValue.IMMUTABLE_PROPERTIES
+ }
+}
Index: grails-app/domain/com/lemans/ds/category/CategoryAttributeValueLocale.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/category/CategoryAttributeValueLocale.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/category/CategoryAttributeValueLocale.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,23 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.DSLocale
+
+/**
+ * Created by MUmachi on 10/30/2017.
+ */
+class CategoryAttributeValueLocale extends DSLocale {
+
+ Integer id
+ Integer categoryAttributeValueId
+ String attributeValue
+
+ static constraints = {
+ attributeValue maxSize: 750
+ }
+
+ static mapping = {
+ table 'CategoryAttributeValueLocale'
+ id column: 'categoryAttributeValueLocaleId'
+ categoryAttributeValueId updateable: false
+ }
+}
Index: grails-app/domain/com/lemans/ds/category/CategoryLocale.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/category/CategoryLocale.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/category/CategoryLocale.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,25 @@
+package com.lemans.ds.category
+
+import com.lemans.services.Auditable
+
+class CategoryLocale extends Auditable {
+
+ Integer id
+ Integer categoryId
+ String locale
+ String categoryName
+ String description
+
+ static constraints = {
+ categoryId nullable: false
+ locale nullable: false
+ categoryName nullable: true
+ description nullable: true
+ }
+
+ static mapping = {
+ table 'CategoryLocale'
+ id column: 'categoryLocaleId'
+ categoryId updateable: false
+ }
+}
Index: grails-app/domain/com/lemans/ds/category/CategorySubComCode.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/category/CategorySubComCode.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/category/CategorySubComCode.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,24 @@
+package com.lemans.ds.category
+
+import com.lemans.services.Auditable
+import groovy.transform.ToString
+
+@ToString
+class CategorySubComCode extends Auditable {
+
+ Integer id
+
+ Integer categoryId
+
+ Integer subComCodeId
+
+ static constraints = {
+ }
+
+ static mapping = {
+ table 'CategorySubComCode'
+ id column: 'categorySubComCodeId'
+ categoryId updateable: false
+ subComCodeId updateable: false
+ }
+}
Index: grails-app/domain/com/lemans/ds/explosion/ExplosionDiagram.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/explosion/ExplosionDiagram.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/explosion/ExplosionDiagram.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,22 @@
+package com.lemans.ds.explosion
+
+import com.lemans.services.Auditable
+
+class ExplosionDiagram extends Auditable {
+
+ Integer id
+ String title
+ String jsonData
+ Integer isActive
+
+ static constraints = {
+ title nullable: false
+ jsonData nullable: false
+ isActive nullable: false
+ }
+
+ static mapping = {
+ table 'ExplosionDiagram'
+ id column: 'explosionDiagramId'
+ }
+}
Index: grails-app/domain/com/lemans/ds/fitment/Make.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/fitment/Make.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/fitment/Make.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,28 @@
+package com.lemans.ds.fitment
+
+import com.lemans.services.Auditable
+import groovy.transform.ToString
+
+@ToString
+class Make extends Auditable {
+
+ Integer id
+
+ String makeName
+
+ String modelNameFormat
+
+ static constraints = {
+ makeName maxSize: 100, blank: false
+ modelNameFormat maxSize: 50, blank: false
+ }
+
+ static hasMany = [models: Model]
+
+ static transients = ['models']
+
+ static mapping = {
+ table 'Make'
+ id column: 'makeId'
+ }
+}
Index: grails-app/domain/com/lemans/ds/fitment/Model.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/fitment/Model.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/fitment/Model.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,27 @@
+package com.lemans.ds.fitment
+
+import com.lemans.services.Auditable
+
+class Model extends Auditable {
+
+ Integer id
+ Make make
+ String modelName
+ String modelNameFormat
+ Integer vehicleTypeId
+
+ static constraints = {
+ modelName maxSize: 400, blank: false
+ modelNameFormat nullable: true, blank: false, maxSize: 50
+ }
+
+ static hasMany = [modelYears: ModelYear, modelSegments: ModelSegment]
+
+ static transients = ['modelName']
+
+ static mapping = {
+ table 'Model'
+ id column: 'modelId'
+ make column: 'makeId', updateable: false
+ }
+}
Index: grails-app/domain/com/lemans/ds/fitment/ModelSegment.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/fitment/ModelSegment.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/fitment/ModelSegment.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,27 @@
+package com.lemans.ds.fitment
+
+import com.lemans.services.Auditable
+
+/**
+ * Created by vramisetti on 4/10/2017.
+ */
+class ModelSegment extends Auditable {
+
+ Integer id
+ Segment segment
+ String segmentValue
+
+ static constraints = {
+ segmentValue nullable: true, maxSize: 100
+ }
+
+ static belongsTo = [model: Model]
+
+ static mapping = {
+ table 'ModelSegment'
+ id column: 'modelSegmentId'
+ model column: 'modelId'
+ segment column: 'segmentId'
+ segmentValue column: 'value'
+ }
+}
Index: grails-app/domain/com/lemans/ds/fitment/ModelYear.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/fitment/ModelYear.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/fitment/ModelYear.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,33 @@
+package com.lemans.ds.fitment
+
+import com.lemans.services.Auditable
+import groovy.transform.ToString
+
+import java.time.Year
+
+@ToString
+class ModelYear extends Auditable {
+
+ Integer id
+ Model model
+ @SuppressWarnings(['GrailsDomainReservedSqlKeywordName'])//TODO: remove this if possible
+ Integer year
+
+ static constraints = {
+ year blank: false, validator: {
+ Integer currentYear = Year.now().value
+ if ((it < 1894) || (it > currentYear + 1)) { return ['modelYear.year.rangeExceeded', currentYear + 1] }
+ }
+ }
+
+ static belongsTo = [model: Model]
+
+ static hasMany = [partFitments: PartFitment]
+
+ static mapping = {
+ table 'ModelYear'
+ id column: 'modelYearId'
+ year column: 'year'
+ model column: 'modelId'//, updateable: false
+ }
+}
Index: grails-app/domain/com/lemans/ds/fitment/PartFitment.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/fitment/PartFitment.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/fitment/PartFitment.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,26 @@
+package com.lemans.ds.fitment
+
+import com.lemans.ds.part.Part
+import com.lemans.services.Auditable
+
+/**
+ * Created by VRAMISETTI on 4/15/2017.
+ */
+class PartFitment extends Auditable {
+
+ Integer id
+ String note
+
+ static constraints = {
+ note maxSize: 200, nullable: true
+ }
+
+ static belongsTo = [partNumber: Part, modelYearId: ModelYear]
+
+ static mapping = {
+ table 'PartFitment'
+ id column: 'partFitmentId'
+ partNumber column: 'partNumber'
+ modelYearId column: 'modelYearId'
+ }
+}
Index: grails-app/domain/com/lemans/ds/fitment/Segment.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/fitment/Segment.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/fitment/Segment.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,29 @@
+package com.lemans.ds.fitment
+
+import com.lemans.services.Auditable
+
+/**
+ * Created by vramisetti on 4/10/2017.
+ */
+class Segment extends Auditable {
+
+ Integer id
+ String segmentCode
+ String segmentName
+ Integer sequence
+
+ static constraints = {
+ segmentCode maxSize: 10, blank: false
+ segmentName maxSize: 50, blank: false
+ sequence nullable: true
+ }
+
+ static mapping = {
+ table 'Segment'
+ id column: 'segmentId'
+ }
+
+ static List segmentCodes() {
+ this.executeQuery('SELECT segmentCode FROM Segment WHERE dateDeleted IS NULL')
+ }
+}
Index: grails-app/domain/com/lemans/ds/flag/Flag.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/flag/Flag.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/flag/Flag.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,34 @@
+package com.lemans.ds.flag
+
+import com.lemans.services.Auditable
+
+class Flag extends Auditable {
+
+ private static final Map IMMUTABLE_PROPERTIES = [
+ flagName: 'Flag Name'
+ ].asImmutable()
+
+ Integer id
+ String entityClass
+ String flagName
+ String flagDisplayName
+ String flagTypeId
+
+ static constraints = {
+ entityClass maxSize: 50, blank: false
+ flagName maxSize: 100, blank: false
+ flagDisplayName nullable: true, maxSize: 250
+ }
+
+ static mapping = {
+ table 'Flag'
+ id column: 'flagId'
+ flagName updateable: false
+ flagTypeId updateable: false
+ }
+
+ @Override
+ protected Map immutables() {
+ Flag.IMMUTABLE_PROPERTIES
+ }
+}
Index: grails-app/domain/com/lemans/ds/flag/FlagValue.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/flag/FlagValue.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/flag/FlagValue.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,23 @@
+package com.lemans.ds.flag
+
+import com.lemans.services.Auditable
+import groovy.transform.ToString
+
+@ToString
+class FlagValue extends Auditable {
+
+ Integer id
+ Integer flagId
+ String entityId
+
+ static constraints = {
+ entityId maxSize: 50, blank: false
+ }
+
+ static mapping = {
+ table 'flagValue'
+ id column: 'flagValueId'
+ flagId updateable: false
+ entityId updateable: false
+ }
+}
Index: grails-app/domain/com/lemans/ds/importing/LocaleImportProcess.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/importing/LocaleImportProcess.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/importing/LocaleImportProcess.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,47 @@
+package com.lemans.ds.importing
+
+import com.lemans.services.Auditable
+import groovy.transform.ToString
+
+@ToString(includeNames = true)
+class LocaleImportProcess extends Auditable {
+
+ Integer id
+
+ Integer mimeTypeId
+
+ String extension
+
+ String importType
+
+ String originalFileName
+
+ String status
+
+ String message
+
+ Date startDate
+
+ Date endDate
+
+ static constraints = {
+
+ originalFileName maxSize: 255
+ importType maxSize: 80
+ mimeTypeId nullable: true
+ message nullable: true, maxSize: 255
+ extension nullable: true, maxSize: 7
+ status maxSize: 50
+ startDate nullable: true
+ endDate nullable: true
+ }
+
+
+ static mapping = {
+ table 'LocaleImportProcess'
+ id column: 'localeImportProcessId'
+ originalFileName updateable: false
+ importType updateable: false
+ }
+
+}
Index: grails-app/domain/com/lemans/ds/part/Part.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/part/Part.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/part/Part.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,33 @@
+package com.lemans.ds.part
+
+import com.lemans.ds.fitment.PartFitment
+import com.lemans.services.Auditable
+
+
+/**
+ * Represents additional data for Parts that is used by DS. Actual Parts may only be created or deleted by mainframe.
+ */
+class Part extends Auditable {
+
+ static final int UNASSIGNED_CATEGORY_ID = -1
+ static final String UNASSIGNED_PRODUCT_ID = '-1'
+
+ String id
+
+ String marketingDescr
+ Integer categoryId
+ Integer primaryMediaId
+
+ static constraints = {
+ marketingDescr nullable: true, maxSize: 100
+ categoryId nullable: true
+ primaryMediaId nullable: true
+ }
+
+ static hasMany = [partFitments: PartFitment]
+
+ static mapping = {
+ table 'Part'
+ id column: 'partNumber'
+ }
+}
Index: grails-app/domain/com/lemans/ds/part/PartAssociation.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/part/PartAssociation.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/part/PartAssociation.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,22 @@
+package com.lemans.ds.part
+
+import com.lemans.services.Auditable
+
+class PartAssociation extends Auditable {
+
+ Integer id
+ String partNumber
+ String relatedPartNumber
+ Integer associationTypeId
+
+ static constraints = {
+ partNumber nullable: false
+ relatedPartNumber nullable: false
+ associationTypeId nullable: false
+ }
+
+ static mapping = {
+ table 'PartAssociation'
+ id column: 'partAssociationId'
+ }
+}
Index: grails-app/domain/com/lemans/ds/part/PartLocale.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/part/PartLocale.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/part/PartLocale.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,27 @@
+package com.lemans.ds.part
+
+import com.lemans.ds.DSLocale
+
+class PartLocale extends DSLocale {
+
+ Integer id
+ String partNumber
+ String partDescr
+ String marketingDescr
+ String specialInstructions
+ String partSpecificText
+
+ static constraints = {
+ partNumber nullable: false
+ partDescr nullable: true, maxSize: 50
+ marketingDescr nullable: true, maxSize: 100
+ specialInstructions nullable: true, maxSize: 400
+ partSpecificText nullable: true
+ }
+
+ static mapping = {
+ table 'PartLocale'
+ id column: 'partLocaleId'
+ partNumber updateable: false
+ }
+}
Index: grails-app/domain/com/lemans/ds/part/partmetadata/PartMetadata.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/part/partmetadata/PartMetadata.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/part/partmetadata/PartMetadata.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,36 @@
+package com.lemans.ds.part.partmetadata
+
+import com.lemans.services.Auditable
+import groovy.transform.ToString
+
+
+/**
+ * Represents additional metadata for Parts that is used by DS. Actual Parts may only be created or deleted by mainframe.
+ */
+@ToString
+class PartMetadata extends Auditable {
+
+ String id
+ String partSpecificText
+ String internalNotes
+ String relatedParts
+ String relatedProducts
+ String certificationUS
+ String certificationEU
+ String oemPartNumber
+
+ static constraints = {
+ partSpecificText nullable: true
+ internalNotes nullable: true
+ relatedParts nullable: true
+ relatedProducts nullable: true
+ certificationUS nullable: true
+ certificationEU nullable: true
+ oemPartNumber nullable: true
+ }
+
+ static mapping = {
+ table 'PartMetadata'
+ id column: 'partNumber', generator: 'assigned'
+ }
+}
Index: grails-app/domain/com/lemans/ds/product/Product.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/product/Product.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/product/Product.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,67 @@
+package com.lemans.ds.product
+
+import com.lemans.services.Auditable
+import groovy.transform.ToString
+
+
+@ToString
+class Product extends Auditable {
+
+ static final Integer VIRTUAL_CATALOG_ID = 0
+
+ private static final Map IMMUTABLE_PROPERTIES = [
+ catalogInstanceId: 'Catalog'
+ ].asImmutable()
+
+ Integer id
+
+ Integer catalogInstanceId
+
+ Integer categoryId
+
+ Integer brandId
+
+ String productName
+
+ Boolean isDigiActive
+
+ Integer primaryMediaId
+
+ String description
+
+ String caption
+
+ Date effectiveDate
+
+ static constraints = {
+ categoryId nullable: true, validator: { value, product ->
+ if (value == null && product.catalogInstanceId == VIRTUAL_CATALOG_ID) {
+ return 'nullable'
+ }
+ }
+
+ brandId nullable: true, validator: { value, product ->
+ if (value == null && product.catalogInstanceId == VIRTUAL_CATALOG_ID) {
+ return 'nullable'
+ }
+ }
+
+ productName nullable: false, minSize: 3, maxSize: 150
+ description nullable: true, maxSize: 2000
+ caption nullable: true, maxSize: 150
+ effectiveDate nullable: true
+ primaryMediaId nullable: true
+ isDigiActive nullable: false
+ }
+
+ static mapping = {
+ table 'Product'
+ id column: 'productId'
+ catalogInstanceId updateable: false
+ }
+
+ @Override
+ protected Map immutables() {
+ Product.IMMUTABLE_PROPERTIES
+ }
+}
Index: grails-app/domain/com/lemans/ds/product/ProductAssociation.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/product/ProductAssociation.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/product/ProductAssociation.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,22 @@
+package com.lemans.ds.product
+
+import com.lemans.services.Auditable
+
+class ProductAssociation extends Auditable {
+
+ Integer id
+ String productId
+ String relatedProductId
+ Integer associationTypeId
+
+ static constraints = {
+ productId nullable: false
+ relatedProductId nullable: false
+ associationTypeId nullable: false
+ }
+
+ static mapping = {
+ table 'ProductAssociation'
+ id column: 'productAssociationId'
+ }
+}
Index: grails-app/domain/com/lemans/ds/product/ProductCategoryAttribute.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/product/ProductCategoryAttribute.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/product/ProductCategoryAttribute.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,43 @@
+package com.lemans.ds.product
+
+import com.lemans.services.Auditable
+import groovy.transform.ToString
+
+/**
+ * Created by MUmachi on 10/24/2017.
+ */
+@ToString
+class ProductCategoryAttribute extends Auditable {
+
+
+ Integer id
+
+ Integer productId
+
+ Integer categoryAttributeId
+
+ Boolean isDropdown
+
+ Boolean isHidden
+
+ Boolean isSplit
+
+ Boolean isGroup
+
+ Boolean isKeyAttribute
+
+ static constraints = {
+ isDropdown nullable: true
+ isHidden nullable: true
+ isGroup nullable: true
+ isKeyAttribute nullable: true
+ }
+
+ static mapping = {
+ table 'ProductCategoryAttribute'
+ id column: 'productCategoryAttributeId'
+ categoryAttributeId updateable: false
+ }
+
+
+}
Index: grails-app/domain/com/lemans/ds/product/ProductFeature.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/product/ProductFeature.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/product/ProductFeature.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,26 @@
+package com.lemans.ds.product
+
+import com.lemans.services.Auditable
+import groovy.transform.ToString
+
+@ToString(includeNames = true)
+class ProductFeature extends Auditable {
+
+ Integer id
+
+ Integer productId
+
+ Integer featureTypeId
+
+ String featureText
+
+ static constraints = {
+ featureText blank: false, maxSize: 1500
+ }
+
+ static mapping = {
+ table 'ProductFeature'
+ id column: 'productFeatureId'
+ productId updateable: false
+ }
+}
Index: grails-app/domain/com/lemans/ds/product/ProductFeatureLocale.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/product/ProductFeatureLocale.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/product/ProductFeatureLocale.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,24 @@
+package com.lemans.ds.product
+
+import com.lemans.ds.DSLocale
+
+class ProductFeatureLocale extends DSLocale {
+
+ Integer id
+
+ Integer productFeatureId
+
+ String featureText
+
+
+
+ static constraints = {
+ featureText nullable: true, minSize: 3, maxSize: 1500
+ }
+
+ static mapping = {
+ table 'ProductFeatureLocale'
+ id column: 'productFeatureLocaleId'
+ productFeatureId updateable: false
+ }
+}
Index: grails-app/domain/com/lemans/ds/product/ProductLocale.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/product/ProductLocale.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/product/ProductLocale.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,34 @@
+package com.lemans.ds.product
+
+import com.lemans.ds.DSLocale
+
+/**
+ * Created by MUmachi on 9/21/2017.
+ */
+class ProductLocale extends DSLocale {
+
+ Integer id
+
+ Integer productId
+
+ String productName
+
+ String caption
+
+ String description
+
+
+
+ static constraints = {
+ productName nullable: true, minSize: 3, maxSize: 150
+ description nullable: true, maxSize: 2000
+ caption nullable: true, maxSize: 150
+ }
+
+ static mapping = {
+ table 'ProductLocale'
+ id column: 'productLocaleId'
+ productId updateable: false
+ }
+
+}
Index: grails-app/domain/com/lemans/ds/publicationcategory/ProductPublicationCategory.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/publicationcategory/ProductPublicationCategory.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/publicationcategory/ProductPublicationCategory.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,20 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.services.Auditable
+
+class ProductPublicationCategory extends Auditable {
+
+ Integer id
+ Integer productId
+ Integer categoryId
+
+ static constraints = {
+ productId nullable: false
+ categoryId nullable: false
+ }
+
+ static mapping = {
+ table 'ProductPublicationCategory'
+ id column: 'productPublicationCategoryId'
+ }
+}
Index: grails-app/domain/com/lemans/ds/publicationcategory/PublicationCategory.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/publicationcategory/PublicationCategory.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/publicationcategory/PublicationCategory.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,30 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.services.Auditable
+
+class PublicationCategory extends Auditable {
+
+ static final Integer MAX_DIRECT_CHILDREN = 999999
+
+ Integer id
+ String categoryName
+ String description
+ Integer parentCategoryId
+ Integer primaryMediaId
+ Integer sequence
+ Date effectiveDate
+
+ static constraints = {
+ categoryName nullable: false
+ description nullable: true
+ parentCategoryId nullable: true
+ primaryMediaId nullable: true
+ sequence nullable: false
+ effectiveDate nullable: true
+ }
+
+ static mapping = {
+ table 'PublicationCategory'
+ id column: 'categoryId', updateable: false, insertable: false
+ }
+}
Index: grails-app/domain/com/lemans/ds/publicationcategory/PublicationCategoryAttribute.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/publicationcategory/PublicationCategoryAttribute.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/publicationcategory/PublicationCategoryAttribute.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,28 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.services.Auditable
+
+class PublicationCategoryAttribute extends Auditable {
+
+ Integer id
+ Integer publicationCategoryId
+ Integer attributeNameId
+ Integer sequence
+ Integer source
+ Integer sourceId
+
+ static constraints = {
+ publicationCategoryId nullable: false
+ attributeNameId nullable: false
+ sequence nullable: true
+ source nullable: true
+ sourceId nullable: true
+ }
+
+ static mapping = {
+ table 'PublicationCategoryAttribute'
+ id column: 'publicationCategoryAttributeId'
+ publicationCategoryId updateable: false
+ attributeNameId updateable: false
+ }
+}
Index: grails-app/domain/com/lemans/ds/splitvalidation/JobStatus.groovy
===================================================================
diff -u
--- grails-app/domain/com/lemans/ds/splitvalidation/JobStatus.groovy (revision 0)
+++ grails-app/domain/com/lemans/ds/splitvalidation/JobStatus.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,20 @@
+package com.lemans.ds.splitvalidation
+
+import com.lemans.services.Auditable
+
+class JobStatus extends Auditable {
+
+ Integer id
+ Date startDate
+ Date endDate
+
+ static constraints = {
+ startDate nullable: false
+ endDate nullable: true
+ }
+
+ static mapping = {
+ table 'JobStatus'
+ id column: 'jobStatusId'
+ }
+}
Index: grails-app/i18n/messages.properties
===================================================================
diff -u
--- grails-app/i18n/messages.properties (revision 0)
+++ grails-app/i18n/messages.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,112 @@
+default.doesnt.match.message={0} with value [{2}] does not match the required pattern [{3}]
+default.invalid.url.message={0} with value [{2}] is not a valid URL
+default.invalid.creditCard.message={0} with value [{2}] is not a valid credit card number
+default.invalid.email.message={0} with value [{2}] is not a valid e-mail address
+default.invalid.range.message={0} with value [{2}] does not fall within the valid range from [{3}] to [{4}]
+default.invalid.size.message={0} with value [{2}] does not fall within the valid size range from [{3}] to [{4}]
+default.invalid.max.message={0} with value [{2}] exceeds maximum value [{3}]
+default.invalid.min.message={0} with value [{2}] is less than minimum value [{3}]
+default.invalid.max.size.message={0} with value [{2}] exceeds the maximum size of [{3}]
+default.invalid.min.size.message={0} with value [{2}] is less than the minimum size of [{3}]
+default.invalid.validator.message={0} with value [{2}] does not pass custom validation
+default.not.inlist.message={0} with value [{2}] is not contained within the list [{3}]
+default.blank.message={0} is required
+default.not.equal.message={0} with value [{2}] cannot equal [{3}]
+default.null.message={0} is required
+default.not.unique.message={0} with value [{2}] must be unique
+
+default.paginate.prev=Previous
+default.paginate.next=Next
+default.boolean.true=True
+default.boolean.false=False
+default.date.format=yyyy-MM-dd HH:mm:ss z
+default.number.format=0
+
+default.created.message={0} {1} created
+default.updated.message={0} {1} updated
+default.deleted.message={0} {1} deleted
+default.not.deleted.message={0} {1} could not be deleted
+default.not.found.message={0} not found with id {1}
+default.optimistic.locking.failure=Another user has updated this {0} while you were editing
+
+default.home.label=Home
+default.list.label={0} List
+default.add.label=Add {0}
+default.new.label=New {0}
+default.create.label=Create {0}
+default.show.label=Show {0}
+default.edit.label=Edit {0}
+
+default.button.create.label=Create
+default.button.edit.label=Edit
+default.button.update.label=Update
+default.button.delete.label=Delete
+default.button.delete.confirm.message=Are you sure?
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL={0} must be a valid URL
+typeMismatch.java.net.URI={0} must be a valid URI
+typeMismatch.java.util.Date={0} must be a valid Date
+typeMismatch.java.lang.Double={0} must be a valid number
+typeMismatch.java.lang.Integer={0} must be a valid number
+typeMismatch.java.lang.Long={0} must be a valid number
+typeMismatch.java.lang.Short={0} must be a valid number
+typeMismatch.java.math.BigDecimal={0} must be a valid number
+typeMismatch.java.math.BigInteger={0} must be a valid number
+typeMismatch={0} is type-mismatched
+
+#categoryAttribute
+categoryAttribute.allowMultipleValues.invalid=Cannot alter flag as parts assigned to category has multiple values assigned to this attribute.
+categoryAttribute.partsWithAttributeName.exists=Cannot delete Category Attribute that has parts with the same attribute Name.
+
+#Lemans
+immutable={0} may not be changed
+undeletable={0} may not be deleted
+
+#Category
+category.catalogInstanceId.label=Catalog
+category.categoryName.label=Category Name
+
+
+#Product
+product.productName.label=Product Name
+product.catalogInstanceId.label=Catalog
+product.brandId.label=Brand
+product.categoryId.label=Category
+
+#Product Feature
+productFeature.featureText.label=Product Feature Text
+productFeature.featureTypeId.label=Product Feature Type
+
+#Make
+make.parts.assigned=Cannot delete Make that has {0} parts associated
+make.models.assigned=Cannot delete Make that has {0} models associated
+
+#Model
+model.make.nullable=Make is invalid or not provided
+model.modelName.blank=At least one Segment is required for a Model
+model.yearRangeExceeded=Found Invalid Years {0}. Years should be in between 1894 and {1}
+model.duplicate=Model with following segments and segment Values already exists
+model.emptySegments=At least One Segment is required for a Model
+
+#Model Year
+modelYear.year.rangeExceeded=Year should be in between 1894 and {0}
+
+
+makeModel.modelNameFormat.duplicateSegmentCode=Invalid Model Name Format. Segments can not be repeated.
+makeModel.modelNameFormat.invalidSegmentCode=Invalid Model Name Format. Invalid segment codes present.
+
+modelSegment.segmentValue.label=Value
+
+localeImportProcess.id.stillProcessing=Process can't be deleted, until it is completely processed.
+
+partAssociation.duplicate=PartAssociation for partNumber {0} with associationTypeId {1, number, #} and relatedPartNumber {2} exists.
+productAssociation.duplicate=productAssociation for productId {0} with associationTypeId {1, number, #} and relatedProductId {2} exists.
+
+publicationCategory.notDeletable=Publication Category may not be deleted
+publicationCategory.cyclic=PublicationCategory may not be moved to create a cycle
+productPublicationCategory.duplicate=Product{0, number, #} has already been assigned.
+
+publicationCategory.invalid=Invalid Publication Category
+
+publicationCategoryAttribute.duplicate=Duplicate publicationCategoryAttribute
\ No newline at end of file
Index: grails-app/i18n/messages_cs_CZ.properties
===================================================================
diff -u
--- grails-app/i18n/messages_cs_CZ.properties (revision 0)
+++ grails-app/i18n/messages_cs_CZ.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,55 @@
+default.doesnt.match.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] neodpovídá požadovanému vzoru [{3}]
+default.invalid.url.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není validní URL
+default.invalid.creditCard.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není validní číslo kreditní karty
+default.invalid.email.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není validní emailová adresa
+default.invalid.range.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není v povoleném rozmezí od [{3}] do [{4}]
+default.invalid.size.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není v povoleném rozmezí od [{3}] do [{4}]
+default.invalid.max.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] překračuje maximální povolenou hodnotu [{3}]
+default.invalid.min.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] je menší než minimální povolená hodnota [{3}]
+default.invalid.max.size.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] překračuje maximální velikost [{3}]
+default.invalid.min.size.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] je menší než minimální velikost [{3}]
+default.invalid.validator.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] neprošla validací
+default.not.inlist.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není obsažena v seznamu [{3}]
+default.blank.message=Položka [{0}] třídy [{1}] nemůže být prázdná
+default.not.equal.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] nemůže být stejná jako [{3}]
+default.null.message=Položka [{0}] třídy [{1}] nemůže být prázdná
+default.not.unique.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] musí být unikátní
+
+default.paginate.prev=Předcházející
+default.paginate.next=Následující
+default.boolean.true=Pravda
+default.boolean.false=Nepravda
+default.date.format=dd. MM. yyyy HH:mm:ss z
+default.number.format=0
+
+default.created.message={0} {1} vytvořeno
+default.updated.message={0} {1} aktualizováno
+default.deleted.message={0} {1} smazáno
+default.not.deleted.message={0} {1} nelze smazat
+default.not.found.message={0} nenalezen s id {1}
+default.optimistic.locking.failure=Jiný uživatel aktualizoval záznam {0}, právě když byl vámi editován
+
+default.home.label=Domů
+default.list.label={0} Seznam
+default.add.label=Přidat {0}
+default.new.label=Nový {0}
+default.create.label=Vytvořit {0}
+default.show.label=Ukázat {0}
+default.edit.label=Editovat {0}
+
+default.button.create.label=Vytvoř
+default.button.edit.label=Edituj
+default.button.update.label=Aktualizuj
+default.button.delete.label=Smaž
+default.button.delete.confirm.message=Jste si jistý?
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=Položka {0} musí být validní URL
+typeMismatch.java.net.URI=Položka {0} musí být validní URI
+typeMismatch.java.util.Date=Položka {0} musí být validní datum
+typeMismatch.java.lang.Double=Položka {0} musí být validní desetinné číslo
+typeMismatch.java.lang.Integer=Položka {0} musí být validní číslo
+typeMismatch.java.lang.Long=Položka {0} musí být validní číslo
+typeMismatch.java.lang.Short=Položka {0} musí být validní číslo
+typeMismatch.java.math.BigDecimal=Položka {0} musí být validní číslo
+typeMismatch.java.math.BigInteger=Položka {0} musí být validní číslo
Index: grails-app/i18n/messages_da.properties
===================================================================
diff -u
--- grails-app/i18n/messages_da.properties (revision 0)
+++ grails-app/i18n/messages_da.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,56 @@
+default.doesnt.match.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] overholder ikke mønsteret [{3}]
+default.invalid.url.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] er ikke en gyldig URL
+default.invalid.creditCard.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] er ikke et gyldigt kreditkortnummer
+default.invalid.email.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] er ikke en gyldig e-mail adresse
+default.invalid.range.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] ligger ikke inden for intervallet fra [{3}] til [{4}]
+default.invalid.size.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] ligger ikke inden for størrelsen fra [{3}] til [{4}]
+default.invalid.max.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] overstiger den maksimale værdi [{3}]
+default.invalid.min.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] er under den minimale værdi [{3}]
+default.invalid.max.size.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] overstiger den maksimale størrelse på [{3}]
+default.invalid.min.size.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] er under den minimale størrelse på [{3}]
+default.invalid.validator.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] overholder ikke den brugerdefinerede validering
+default.not.inlist.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] findes ikke i listen [{3}]
+default.blank.message=Feltet [{0}] i klassen [{1}] kan ikke være tom
+default.not.equal.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] må ikke være [{3}]
+default.null.message=Feltet [{0}] i klassen [{1}] kan ikke være null
+default.not.unique.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] skal være unik
+
+default.paginate.prev=Forrige
+default.paginate.next=Næste
+default.boolean.true=Sand
+default.boolean.false=Falsk
+default.date.format=yyyy-MM-dd HH:mm:ss z
+default.number.format=0
+
+default.created.message={0} {1} oprettet
+default.updated.message={0} {1} opdateret
+default.deleted.message={0} {1} slettet
+default.not.deleted.message={0} {1} kunne ikke slettes
+default.not.found.message={0} med id {1} er ikke fundet
+default.optimistic.locking.failure=En anden bruger har opdateret denne {0} imens du har lavet rettelser
+
+default.home.label=Hjem
+default.list.label={0} Liste
+default.add.label=Tilføj {0}
+default.new.label=Ny {0}
+default.create.label=Opret {0}
+default.show.label=Vis {0}
+default.edit.label=Ret {0}
+
+default.button.create.label=Opret
+default.button.edit.label=Ret
+default.button.update.label=Opdater
+default.button.delete.label=Slet
+default.button.delete.confirm.message=Er du sikker?
+
+# Databindingsfejl. Brug "typeMismatch.$className.$propertyName for at passe til en given klasse (f.eks typeMismatch.Book.author)
+typeMismatch.java.net.URL=Feltet {0} skal være en valid URL
+typeMismatch.java.net.URI=Feltet {0} skal være en valid URI
+typeMismatch.java.util.Date=Feltet {0} skal være en valid Dato
+typeMismatch.java.lang.Double=Feltet {0} skal være et valid tal
+typeMismatch.java.lang.Integer=Feltet {0} skal være et valid tal
+typeMismatch.java.lang.Long=Feltet {0} skal være et valid tal
+typeMismatch.java.lang.Short=Feltet {0} skal være et valid tal
+typeMismatch.java.math.BigDecimal=Feltet {0} skal være et valid tal
+typeMismatch.java.math.BigInteger=Feltet {0} skal være et valid tal
+
Index: grails-app/i18n/messages_de.properties
===================================================================
diff -u
--- grails-app/i18n/messages_de.properties (revision 0)
+++ grails-app/i18n/messages_de.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,55 @@
+default.doesnt.match.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] entspricht nicht dem vorgegebenen Muster [{3}]
+default.invalid.url.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist keine gültige URL
+default.invalid.creditCard.message=Das Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist keine gültige Kreditkartennummer
+default.invalid.email.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist keine gültige E-Mail Adresse
+default.invalid.range.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist nicht im Wertebereich von [{3}] bis [{4}]
+default.invalid.size.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist nicht im Wertebereich von [{3}] bis [{4}]
+default.invalid.max.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist größer als der Höchstwert von [{3}]
+default.invalid.min.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist kleiner als der Mindestwert von [{3}]
+default.invalid.max.size.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] übersteigt den Höchstwert von [{3}]
+default.invalid.min.size.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] unterschreitet den Mindestwert von [{3}]
+default.invalid.validator.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist ungültig
+default.not.inlist.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist nicht in der Liste [{3}] enthalten.
+default.blank.message=Die Eigenschaft [{0}] des Typs [{1}] darf nicht leer sein
+default.not.equal.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] darf nicht gleich [{3}] sein
+default.null.message=Die Eigenschaft [{0}] des Typs [{1}] darf nicht null sein
+default.not.unique.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] darf nur einmal vorkommen
+
+default.paginate.prev=Vorherige
+default.paginate.next=Nächste
+default.boolean.true=Wahr
+default.boolean.false=Falsch
+default.date.format=dd.MM.yyyy HH:mm:ss z
+default.number.format=0
+
+default.created.message={0} {1} wurde angelegt
+default.updated.message={0} {1} wurde geändert
+default.deleted.message={0} {1} wurde gelöscht
+default.not.deleted.message={0} {1} konnte nicht gelöscht werden
+default.not.found.message={0} mit der id {1} wurde nicht gefunden
+default.optimistic.locking.failure=Ein anderer Benutzer hat das {0} Object geändert während Sie es bearbeitet haben
+
+default.home.label=Home
+default.list.label={0} Liste
+default.add.label={0} hinzufügen
+default.new.label={0} anlegen
+default.create.label={0} anlegen
+default.show.label={0} anzeigen
+default.edit.label={0} bearbeiten
+
+default.button.create.label=Anlegen
+default.button.edit.label=Bearbeiten
+default.button.update.label=Aktualisieren
+default.button.delete.label=Löschen
+default.button.delete.confirm.message=Sind Sie sicher?
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=Die Eigenschaft {0} muss eine gültige URL sein
+typeMismatch.java.net.URI=Die Eigenschaft {0} muss eine gültige URI sein
+typeMismatch.java.util.Date=Die Eigenschaft {0} muss ein gültiges Datum sein
+typeMismatch.java.lang.Double=Die Eigenschaft {0} muss eine gültige Zahl sein
+typeMismatch.java.lang.Integer=Die Eigenschaft {0} muss eine gültige Zahl sein
+typeMismatch.java.lang.Long=Die Eigenschaft {0} muss eine gültige Zahl sein
+typeMismatch.java.lang.Short=Die Eigenschaft {0} muss eine gültige Zahl sein
+typeMismatch.java.math.BigDecimal=Die Eigenschaft {0} muss eine gültige Zahl sein
+typeMismatch.java.math.BigInteger=Die Eigenschaft {0} muss eine gültige Zahl sein
Index: grails-app/i18n/messages_es.properties
===================================================================
diff -u
--- grails-app/i18n/messages_es.properties (revision 0)
+++ grails-app/i18n/messages_es.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,55 @@
+default.doesnt.match.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no corresponde al patrón [{3}]
+default.invalid.url.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no es una URL válida
+default.invalid.creditCard.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no es un número de tarjeta de crédito válida
+default.invalid.email.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no es una dirección de correo electrónico válida
+default.invalid.range.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no entra en el rango válido de [{3}] a [{4}]
+default.invalid.size.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no entra en el tamaño válido de [{3}] a [{4}]
+default.invalid.max.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] excede el valor máximo [{3}]
+default.invalid.min.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] es menos que el valor mínimo [{3}]
+default.invalid.max.size.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] excede el tamaño máximo de [{3}]
+default.invalid.min.size.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] es menor que el tamaño mínimo de [{3}]
+default.invalid.validator.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no es válido
+default.not.inlist.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no esta contenido dentro de la lista [{3}]
+default.blank.message=La propiedad [{0}] de la clase [{1}] no puede ser vacía
+default.not.equal.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no puede igualar a [{3}]
+default.null.message=La propiedad [{0}] de la clase [{1}] no puede ser nulo
+default.not.unique.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] debe ser única
+
+default.paginate.prev=Anterior
+default.paginate.next=Siguiente
+default.boolean.true=Verdadero
+default.boolean.false=Falso
+default.date.format=yyyy-MM-dd HH:mm:ss z
+default.number.format=0
+
+default.created.message={0} {1} creado
+default.updated.message={0} {1} actualizado
+default.deleted.message={0} {1} eliminado
+default.not.deleted.message={0} {1} no puede eliminarse
+default.not.found.message=No se encuentra {0} con id {1}
+default.optimistic.locking.failure=Mientras usted editaba, otro usuario ha actualizado su {0}
+
+default.home.label=Principal
+default.list.label={0} Lista
+default.add.label=Agregar {0}
+default.new.label=Nuevo {0}
+default.create.label=Crear {0}
+default.show.label=Mostrar {0}
+default.edit.label=Editar {0}
+
+default.button.create.label=Crear
+default.button.edit.label=Editar
+default.button.update.label=Actualizar
+default.button.delete.label=Eliminar
+default.button.delete.confirm.message=¿Está usted seguro?
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=La propiedad {0} debe ser una URL válida
+typeMismatch.java.net.URI=La propiedad {0} debe ser una URI válida
+typeMismatch.java.util.Date=La propiedad {0} debe ser una fecha válida
+typeMismatch.java.lang.Double=La propiedad {0} debe ser un número válido
+typeMismatch.java.lang.Integer=La propiedad {0} debe ser un número válido
+typeMismatch.java.lang.Long=La propiedad {0} debe ser un número válido
+typeMismatch.java.lang.Short=La propiedad {0} debe ser un número válido
+typeMismatch.java.math.BigDecimal=La propiedad {0} debe ser un número válido
+typeMismatch.java.math.BigInteger=La propiedad {0} debe ser un número válido
\ No newline at end of file
Index: grails-app/i18n/messages_fr.properties
===================================================================
diff -u
--- grails-app/i18n/messages_fr.properties (revision 0)
+++ grails-app/i18n/messages_fr.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,19 @@
+default.doesnt.match.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] ne correspond pas au pattern [{3}]
+default.invalid.url.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas une URL valide
+default.invalid.creditCard.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas un numéro de carte de crédit valide
+default.invalid.email.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas une adresse e-mail valide
+default.invalid.range.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas contenue dans l'intervalle [{3}] à [{4}]
+default.invalid.size.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas contenue dans l'intervalle [{3}] à [{4}]
+default.invalid.max.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] est supérieure à la valeur maximum [{3}]
+default.invalid.min.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] est inférieure à la valeur minimum [{3}]
+default.invalid.max.size.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] est supérieure à la valeur maximum [{3}]
+default.invalid.min.size.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] est inférieure à la valeur minimum [{3}]
+default.invalid.validator.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas valide
+default.not.inlist.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] ne fait pas partie de la liste [{3}]
+default.blank.message=La propriété [{0}] de la classe [{1}] ne peut pas être vide
+default.not.equal.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] ne peut pas être égale à [{3}]
+default.null.message=La propriété [{0}] de la classe [{1}] ne peut pas être nulle
+default.not.unique.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] doit être unique
+
+default.paginate.prev=Précédent
+default.paginate.next=Suivant
Index: grails-app/i18n/messages_it.properties
===================================================================
diff -u
--- grails-app/i18n/messages_it.properties (revision 0)
+++ grails-app/i18n/messages_it.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,55 @@
+default.doesnt.match.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non corrisponde al pattern [{3}]
+default.invalid.url.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non è un URL valido
+default.invalid.creditCard.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non è un numero di carta di credito valido
+default.invalid.email.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non è un indirizzo email valido
+default.invalid.range.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non rientra nell'intervallo valido da [{3}] a [{4}]
+default.invalid.size.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non rientra nell'intervallo di dimensioni valide da [{3}] a [{4}]
+default.invalid.max.message=La proprietà [{0}] della classe [{1}] con valore [{2}] è maggiore di [{3}]
+default.invalid.min.message=La proprietà [{0}] della classe [{1}] con valore [{2}] è minore di [{3}]
+default.invalid.max.size.message=La proprietà [{0}] della classe [{1}] con valore [{2}] è maggiore di [{3}]
+default.invalid.min.size.message=La proprietà [{0}] della classe [{1}] con valore [{2}] è minore di [{3}]
+default.invalid.validator.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non è valida
+default.not.inlist.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non è contenuta nella lista [{3}]
+default.blank.message=La proprietà [{0}] della classe [{1}] non può essere vuota
+default.not.equal.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non può essere uguale a [{3}]
+default.null.message=La proprietà [{0}] della classe [{1}] non può essere null
+default.not.unique.message=La proprietà [{0}] della classe [{1}] con valore [{2}] deve essere unica
+
+default.paginate.prev=Precedente
+default.paginate.next=Successivo
+default.boolean.true=Vero
+default.boolean.false=Falso
+default.date.format=dd/MM/yyyy HH:mm:ss z
+default.number.format=0
+
+default.created.message={0} {1} creato
+default.updated.message={0} {1} aggiornato
+default.deleted.message={0} {1} eliminato
+default.not.deleted.message={0} {1} non può essere eliminato
+default.not.found.message={0} non trovato con id {1}
+default.optimistic.locking.failure=Un altro utente ha aggiornato questo {0} mentre si era in modifica
+
+default.home.label=Home
+default.list.label={0} Elenco
+default.add.label=Aggiungi {0}
+default.new.label=Nuovo {0}
+default.create.label=Crea {0}
+default.show.label=Mostra {0}
+default.edit.label=Modifica {0}
+
+default.button.create.label=Crea
+default.button.edit.label=Modifica
+default.button.update.label=Aggiorna
+default.button.delete.label=Elimina
+default.button.delete.confirm.message=Si è sicuri?
+
+# Data binding errors. Usa "typeMismatch.$className.$propertyName per la personalizzazione (es typeMismatch.Book.author)
+typeMismatch.java.net.URL=La proprietà {0} deve essere un URL valido
+typeMismatch.java.net.URI=La proprietà {0} deve essere un URI valido
+typeMismatch.java.util.Date=La proprietà {0} deve essere una data valida
+typeMismatch.java.lang.Double=La proprietà {0} deve essere un numero valido
+typeMismatch.java.lang.Integer=La proprietà {0} deve essere un numero valido
+typeMismatch.java.lang.Long=La proprietà {0} deve essere un numero valido
+typeMismatch.java.lang.Short=La proprietà {0} deve essere un numero valido
+typeMismatch.java.math.BigDecimal=La proprietà {0} deve essere un numero valido
+typeMismatch.java.math.BigInteger=La proprietà {0} deve essere un numero valido
Index: grails-app/i18n/messages_ja.properties
===================================================================
diff -u
--- grails-app/i18n/messages_ja.properties (revision 0)
+++ grails-app/i18n/messages_ja.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,55 @@
+default.doesnt.match.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]パターンと一致していません。
+default.invalid.url.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、有効なURLではありません。
+default.invalid.creditCard.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、有効なクレジットカード番号ではありません。
+default.invalid.email.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、有効なメールアドレスではありません。
+default.invalid.range.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]から[{4}]範囲内を指定してください。
+default.invalid.size.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]から[{4}]以内を指定してください。
+default.invalid.max.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、最大値[{3}]より大きいです。
+default.invalid.min.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、最小値[{3}]より小さいです。
+default.invalid.max.size.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、最大値[{3}]より大きいです。
+default.invalid.min.size.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、最小値[{3}]より小さいです。
+default.invalid.validator.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、カスタムバリデーションを通過できません。
+default.not.inlist.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]リスト内に存在しません。
+default.blank.message=[{1}]クラスのプロパティ[{0}]の空白は許可されません。
+default.not.equal.message=クラス[{1}]プロパティ[{0}]の値[{2}]に[{3}]は許可されません。
+default.null.message=[{1}]クラスのプロパティ[{0}]にnullは許可されません。
+default.not.unique.message=クラス[{1}]プロパティ[{0}]の値[{2}]は既に使用されています。
+
+default.paginate.prev=戻る
+default.paginate.next=次へ
+default.boolean.true=はい
+default.boolean.false=いいえ
+default.date.format=yyyy/MM/dd HH:mm:ss z
+default.number.format=0
+
+default.created.message={0}(id:{1})を作成しました。
+default.updated.message={0}(id:{1})を更新しました。
+default.deleted.message={0}(id:{1})を削除しました。
+default.not.deleted.message={0}(id:{1})は削除できませんでした。
+default.not.found.message={0}(id:{1})は見つかりませんでした。
+default.optimistic.locking.failure=この{0}は編集中に他のユーザによって先に更新されています。
+
+default.home.label=ホーム
+default.list.label={0}リスト
+default.add.label={0}を追加
+default.new.label={0}を新規作成
+default.create.label={0}を作成
+default.show.label={0}詳細
+default.edit.label={0}を編集
+
+default.button.create.label=作成
+default.button.edit.label=編集
+default.button.update.label=更新
+default.button.delete.label=削除
+default.button.delete.confirm.message=本当に削除してよろしいですか?
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL={0}は有効なURLでなければなりません。
+typeMismatch.java.net.URI={0}は有効なURIでなければなりません。
+typeMismatch.java.util.Date={0}は有効な日付でなければなりません。
+typeMismatch.java.lang.Double={0}は有効な数値でなければなりません。
+typeMismatch.java.lang.Integer={0}は有効な数値でなければなりません。
+typeMismatch.java.lang.Long={0}は有効な数値でなければなりません。
+typeMismatch.java.lang.Short={0}は有効な数値でなければなりません。
+typeMismatch.java.math.BigDecimal={0}は有効な数値でなければなりません。
+typeMismatch.java.math.BigInteger={0}は有効な数値でなければなりません。
Index: grails-app/i18n/messages_nb.properties
===================================================================
diff -u
--- grails-app/i18n/messages_nb.properties (revision 0)
+++ grails-app/i18n/messages_nb.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,56 @@
+default.doesnt.match.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] overholder ikke mønsteret [{3}]
+default.invalid.url.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er ikke en gyldig URL
+default.invalid.creditCard.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er ikke et gyldig kredittkortnummer
+default.invalid.email.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er ikke en gyldig epostadresse
+default.invalid.range.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er ikke innenfor intervallet [{3}] til [{4}]
+default.invalid.size.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er ikke innenfor intervallet [{3}] til [{4}]
+default.invalid.max.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] overstiger maksimumsverdien på [{3}]
+default.invalid.min.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er under minimumsverdien på [{3}]
+default.invalid.max.size.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] overstiger maksimumslengden på [{3}]
+default.invalid.min.size.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er kortere enn minimumslengden på [{3}]
+default.invalid.validator.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] overholder ikke den brukerdefinerte valideringen
+default.not.inlist.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] finnes ikke i listen [{3}]
+default.blank.message=Feltet [{0}] i klassen [{1}] kan ikke være tom
+default.not.equal.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] kan ikke være [{3}]
+default.null.message=Feltet [{0}] i klassen [{1}] kan ikke være null
+default.not.unique.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] må være unik
+
+default.paginate.prev=Forrige
+default.paginate.next=Neste
+default.boolean.true=Ja
+default.boolean.false=Nei
+default.date.format=dd.MM.yyyy HH:mm:ss z
+default.number.format=0
+
+default.created.message={0} {1} opprettet
+default.updated.message={0} {1} oppdatert
+default.deleted.message={0} {1} slettet
+default.not.deleted.message={0} {1} kunne ikke slettes
+default.not.found.message={0} med id {1} ble ikke funnet
+default.optimistic.locking.failure=En annen bruker har oppdatert denne {0} mens du redigerte
+
+default.home.label=Hjem
+default.list.label={0}liste
+default.add.label=Legg til {0}
+default.new.label=Ny {0}
+default.create.label=Opprett {0}
+default.show.label=Vis {0}
+default.edit.label=Endre {0}
+
+default.button.create.label=Opprett
+default.button.edit.label=Endre
+default.button.update.label=Oppdater
+default.button.delete.label=Slett
+default.button.delete.confirm.message=Er du sikker?
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=Feltet {0} må være en gyldig URL
+typeMismatch.java.net.URI=Feltet {0} må være en gyldig URI
+typeMismatch.java.util.Date=Feltet {0} må være en gyldig dato
+typeMismatch.java.lang.Double=Feltet {0} må være et gyldig tall
+typeMismatch.java.lang.Integer=Feltet {0} må være et gyldig heltall
+typeMismatch.java.lang.Long=Feltet {0} må være et gyldig heltall
+typeMismatch.java.lang.Short=Feltet {0} må være et gyldig heltall
+typeMismatch.java.math.BigDecimal=Feltet {0} må være et gyldig tall
+typeMismatch.java.math.BigInteger=Feltet {0} må være et gyldig heltall
+
Index: grails-app/i18n/messages_nl.properties
===================================================================
diff -u
--- grails-app/i18n/messages_nl.properties (revision 0)
+++ grails-app/i18n/messages_nl.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,55 @@
+default.doesnt.match.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] komt niet overeen met het vereiste patroon [{3}]
+default.invalid.url.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is geen geldige URL
+default.invalid.creditCard.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is geen geldig credit card nummer
+default.invalid.email.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is geen geldig e-mailadres
+default.invalid.range.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] valt niet in de geldige waardenreeks van [{3}] tot [{4}]
+default.invalid.size.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] valt niet in de geldige grootte van [{3}] tot [{4}]
+default.invalid.max.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] overschrijdt de maximumwaarde [{3}]
+default.invalid.min.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is minder dan de minimumwaarde [{3}]
+default.invalid.max.size.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] overschrijdt de maximumgrootte van [{3}]
+default.invalid.min.size.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is minder dan minimumgrootte van [{3}]
+default.invalid.validator.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is niet geldig
+default.not.inlist.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] komt niet voor in de lijst [{3}]
+default.blank.message=Attribuut [{0}] van entiteit [{1}] mag niet leeg zijn
+default.not.equal.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] mag niet gelijk zijn aan [{3}]
+default.null.message=Attribuut [{0}] van entiteit [{1}] mag niet leeg zijn
+default.not.unique.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] moet uniek zijn
+
+default.paginate.prev=Vorige
+default.paginate.next=Volgende
+default.boolean.true=Ja
+default.boolean.false=Nee
+default.date.format=dd-MM-yyyy HH:mm:ss z
+default.number.format=0
+
+default.created.message={0} {1} ingevoerd
+default.updated.message={0} {1} gewijzigd
+default.deleted.message={0} {1} verwijderd
+default.not.deleted.message={0} {1} kon niet worden verwijderd
+default.not.found.message={0} met id {1} kon niet worden gevonden
+default.optimistic.locking.failure=Een andere gebruiker heeft deze {0} al gewijzigd
+
+default.home.label=Home
+default.list.label={0} Overzicht
+default.add.label=Toevoegen {0}
+default.new.label=Invoeren {0}
+default.create.label=Invoeren {0}
+default.show.label=Details {0}
+default.edit.label=Wijzigen {0}
+
+default.button.create.label=Invoeren
+default.button.edit.label=Wijzigen
+default.button.update.label=Opslaan
+default.button.delete.label=Verwijderen
+default.button.delete.confirm.message=Weet je het zeker?
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=Attribuut {0} is geen geldige URL
+typeMismatch.java.net.URI=Attribuut {0} is geen geldige URI
+typeMismatch.java.util.Date=Attribuut {0} is geen geldige datum
+typeMismatch.java.lang.Double=Attribuut {0} is geen geldig nummer
+typeMismatch.java.lang.Integer=Attribuut {0} is geen geldig nummer
+typeMismatch.java.lang.Long=Attribuut {0} is geen geldig nummer
+typeMismatch.java.lang.Short=Attribuut {0} is geen geldig nummer
+typeMismatch.java.math.BigDecimal=Attribuut {0} is geen geldig nummer
+typeMismatch.java.math.BigInteger=Attribuut {0} is geen geldig nummer
Index: grails-app/i18n/messages_pl.properties
===================================================================
diff -u
--- grails-app/i18n/messages_pl.properties (revision 0)
+++ grails-app/i18n/messages_pl.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,59 @@
+#
+# Translated by Matthias Hryniszak - padcom@gmail.com
+#
+
+default.doesnt.match.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie pasuje do wymaganego wzorca [{3}]
+default.invalid.url.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] jest niepoprawnym adresem URL
+default.invalid.creditCard.message=Właściwość [{0}] klasy [{1}] with value [{2}] nie jest poprawnym numerem karty kredytowej
+default.invalid.email.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie jest poprawnym adresem e-mail
+default.invalid.range.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie zawiera się zakładanym zakresie od [{3}] do [{4}]
+default.invalid.size.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie zawiera się w zakładanym zakresie rozmiarów od [{3}] do [{4}]
+default.invalid.max.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] przekracza maksymalną wartość [{3}]
+default.invalid.min.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] jest mniejsza niż minimalna wartość [{3}]
+default.invalid.max.size.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] przekracza maksymalny rozmiar [{3}]
+default.invalid.min.size.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] jest mniejsza niż minimalny rozmiar [{3}]
+default.invalid.validator.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie spełnia założonych niestandardowych warunków
+default.not.inlist.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie zawiera się w liście [{3}]
+default.blank.message=Właściwość [{0}] klasy [{1}] nie może być pusta
+default.not.equal.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie może równać się [{3}]
+default.null.message=Właściwość [{0}] klasy [{1}] nie może być null
+default.not.unique.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] musi być unikalna
+
+default.paginate.prev=Poprzedni
+default.paginate.next=Następny
+default.boolean.true=Prawda
+default.boolean.false=Fałsz
+default.date.format=yyyy-MM-dd HH:mm:ss z
+default.number.format=0
+
+default.created.message=Utworzono {0} {1}
+default.updated.message=Zaktualizowano {0} {1}
+default.deleted.message=Usunięto {0} {1}
+default.not.deleted.message={0} {1} nie mógł zostać usunięty
+default.not.found.message=Nie znaleziono {0} o id {1}
+default.optimistic.locking.failure=Inny użytkownik zaktualizował ten obiekt {0} w trakcie twoich zmian
+
+default.home.label=Strona domowa
+default.list.label=Lista {0}
+default.add.label=Dodaj {0}
+default.new.label=Utwórz {0}
+default.create.label=Utwórz {0}
+default.show.label=Pokaż {0}
+default.edit.label=Edytuj {0}
+
+default.button.create.label=Utwórz
+default.button.edit.label=Edytuj
+default.button.update.label=Zaktualizuj
+default.button.delete.label=Usuń
+default.button.delete.confirm.message=Czy jesteś pewien?
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=Właściwość {0} musi być poprawnym adresem URL
+typeMismatch.java.net.URI=Właściwość {0} musi być poprawnym adresem URI
+typeMismatch.java.util.Date=Właściwość {0} musi być poprawną datą
+typeMismatch.java.lang.Double=Właściwość {0} musi być poprawnyą liczbą
+typeMismatch.java.lang.Integer=Właściwość {0} musi być poprawnyą liczbą
+typeMismatch.java.lang.Long=Właściwość {0} musi być poprawnyą liczbą
+typeMismatch.java.lang.Short=Właściwość {0} musi być poprawnyą liczbą
+typeMismatch.java.math.BigDecimal=Właściwość {0} musi być poprawnyą liczbą
+typeMismatch.java.math.BigInteger=Właściwość {0} musi być poprawnyą liczbą
Index: grails-app/i18n/messages_pt_BR.properties
===================================================================
diff -u
--- grails-app/i18n/messages_pt_BR.properties (revision 0)
+++ grails-app/i18n/messages_pt_BR.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,59 @@
+#
+# Translated by Lucas Teixeira - lucastex@gmail.com
+#
+
+default.doesnt.match.message=O campo [{0}] da classe [{1}] com o valor [{2}] não atende ao padrão definido [{3}]
+default.invalid.url.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é uma URL válida
+default.invalid.creditCard.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um número válido de cartão de crédito
+default.invalid.email.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um endereço de email válido.
+default.invalid.range.message=O campo [{0}] da classe [{1}] com o valor [{2}] não está entre a faixa de valores válida de [{3}] até [{4}]
+default.invalid.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] não está na faixa de tamanho válida de [{3}] até [{4}]
+default.invalid.max.message=O campo [{0}] da classe [{1}] com o valor [{2}] ultrapassa o valor máximo [{3}]
+default.invalid.min.message=O campo [{0}] da classe [{1}] com o valor [{2}] não atinge o valor mínimo [{3}]
+default.invalid.max.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] ultrapassa o tamanho máximo de [{3}]
+default.invalid.min.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] não atinge o tamanho mínimo de [{3}]
+default.invalid.validator.message=O campo [{0}] da classe [{1}] com o valor [{2}] não passou na validação
+default.not.inlist.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um valor dentre os permitidos na lista [{3}]
+default.blank.message=O campo [{0}] da classe [{1}] não pode ficar em branco
+default.not.equal.message=O campo [{0}] da classe [{1}] com o valor [{2}] não pode ser igual a [{3}]
+default.null.message=O campo [{0}] da classe [{1}] não pode ser vazio
+default.not.unique.message=O campo [{0}] da classe [{1}] com o valor [{2}] deve ser único
+
+default.paginate.prev=Anterior
+default.paginate.next=Próximo
+default.boolean.true=Sim
+default.boolean.false=Não
+default.date.format=dd/MM/yyyy HH:mm:ss z
+default.number.format=0
+
+default.created.message={0} {1} criado
+default.updated.message={0} {1} atualizado
+default.deleted.message={0} {1} removido
+default.not.deleted.message={0} {1} não pode ser removido
+default.not.found.message={0} não foi encontrado com o id {1}
+default.optimistic.locking.failure=Outro usuário atualizou este [{0}] enquanto você tentou salvá-lo
+
+default.home.label=Principal
+default.list.label={0} Listagem
+default.add.label=Adicionar {0}
+default.new.label=Novo {0}
+default.create.label=Criar {0}
+default.show.label=Ver {0}
+default.edit.label=Editar {0}
+
+default.button.create.label=Criar
+default.button.edit.label=Editar
+default.button.update.label=Alterar
+default.button.delete.label=Remover
+default.button.delete.confirm.message=Tem certeza?
+
+# Mensagens de erro em atribuição de valores. Use "typeMismatch.$className.$propertyName" para customizar (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=O campo {0} deve ser uma URL válida.
+typeMismatch.java.net.URI=O campo {0} deve ser uma URI válida.
+typeMismatch.java.util.Date=O campo {0} deve ser uma data válida
+typeMismatch.java.lang.Double=O campo {0} deve ser um número válido.
+typeMismatch.java.lang.Integer=O campo {0} deve ser um número válido.
+typeMismatch.java.lang.Long=O campo {0} deve ser um número válido.
+typeMismatch.java.lang.Short=O campo {0} deve ser um número válido.
+typeMismatch.java.math.BigDecimal=O campo {0} deve ser um número válido.
+typeMismatch.java.math.BigInteger=O campo {0} deve ser um número válido.
Index: grails-app/i18n/messages_pt_PT.properties
===================================================================
diff -u
--- grails-app/i18n/messages_pt_PT.properties (revision 0)
+++ grails-app/i18n/messages_pt_PT.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,34 @@
+#
+# translation by miguel.ping@gmail.com, based on pt_BR translation by Lucas Teixeira - lucastex@gmail.com
+#
+
+default.doesnt.match.message=O campo [{0}] da classe [{1}] com o valor [{2}] não corresponde ao padrão definido [{3}]
+default.invalid.url.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um URL válido
+default.invalid.creditCard.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um número válido de cartão de crédito
+default.invalid.email.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um endereço de email válido.
+default.invalid.range.message=O campo [{0}] da classe [{1}] com o valor [{2}] não está dentro dos limites de valores válidos de [{3}] a [{4}]
+default.invalid.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] está fora dos limites de tamanho válido de [{3}] a [{4}]
+default.invalid.max.message=O campo [{0}] da classe [{1}] com o valor [{2}] ultrapassa o valor máximo [{3}]
+default.invalid.min.message=O campo [{0}] da classe [{1}] com o valor [{2}] não atinge o valor mínimo [{3}]
+default.invalid.max.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] ultrapassa o tamanho máximo de [{3}]
+default.invalid.min.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] não atinge o tamanho mínimo de [{3}]
+default.invalid.validator.message=O campo [{0}] da classe [{1}] com o valor [{2}] não passou na validação
+default.not.inlist.message=O campo [{0}] da classe [{1}] com o valor [{2}] não se encontra nos valores permitidos da lista [{3}]
+default.blank.message=O campo [{0}] da classe [{1}] não pode ser vazio
+default.not.equal.message=O campo [{0}] da classe [{1}] com o valor [{2}] não pode ser igual a [{3}]
+default.null.message=O campo [{0}] da classe [{1}] não pode ser vazio
+default.not.unique.message=O campo [{0}] da classe [{1}] com o valor [{2}] deve ser único
+
+default.paginate.prev=Anterior
+default.paginate.next=Próximo
+
+# Mensagens de erro em atribuição de valores. Use "typeMismatch.$className.$propertyName" para personalizar(eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=O campo {0} deve ser um URL válido.
+typeMismatch.java.net.URI=O campo {0} deve ser um URI válido.
+typeMismatch.java.util.Date=O campo {0} deve ser uma data válida
+typeMismatch.java.lang.Double=O campo {0} deve ser um número válido.
+typeMismatch.java.lang.Integer=O campo {0} deve ser um número válido.
+typeMismatch.java.lang.Long=O campo {0} deve ser um número valido.
+typeMismatch.java.lang.Short=O campo {0} deve ser um número válido.
+typeMismatch.java.math.BigDecimal=O campo {0} deve ser um número válido.
+typeMismatch.java.math.BigInteger=O campo {0} deve ser um número válido.
Index: grails-app/i18n/messages_ru.properties
===================================================================
diff -u
--- grails-app/i18n/messages_ru.properties (revision 0)
+++ grails-app/i18n/messages_ru.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,31 @@
+default.doesnt.match.message=Значение [{2}] поля [{0}] класса [{1}] не соответствует образцу [{3}]
+default.invalid.url.message=Значение [{2}] поля [{0}] класса [{1}] не является допустимым URL-адресом
+default.invalid.creditCard.message=Значение [{2}] поля [{0}] класса [{1}] не является допустимым номером кредитной карты
+default.invalid.email.message=Значение [{2}] поля [{0}] класса [{1}] не является допустимым e-mail адресом
+default.invalid.range.message=Значение [{2}] поля [{0}] класса [{1}] не попадает в допустимый интервал от [{3}] до [{4}]
+default.invalid.size.message=Размер поля [{0}] класса [{1}] (значение: [{2}]) не попадает в допустимый интервал от [{3}] до [{4}]
+default.invalid.max.message=Значение [{2}] поля [{0}] класса [{1}] больше чем максимально допустимое значение [{3}]
+default.invalid.min.message=Значение [{2}] поля [{0}] класса [{1}] меньше чем минимально допустимое значение [{3}]
+default.invalid.max.size.message=Размер поля [{0}] класса [{1}] (значение: [{2}]) больше чем максимально допустимый размер [{3}]
+default.invalid.min.size.message=Размер поля [{0}] класса [{1}] (значение: [{2}]) меньше чем минимально допустимый размер [{3}]
+default.invalid.validator.message=Значение [{2}] поля [{0}] класса [{1}] не допустимо
+default.not.inlist.message=Значение [{2}] поля [{0}] класса [{1}] не попадает в список допустимых значений [{3}]
+default.blank.message=Поле [{0}] класса [{1}] не может быть пустым
+default.not.equal.message=Значение [{2}] поля [{0}] класса [{1}] не может быть равно [{3}]
+default.null.message=Поле [{0}] класса [{1}] не может иметь значение null
+default.not.unique.message=Значение [{2}] поля [{0}] класса [{1}] должно быть уникальным
+
+default.paginate.prev=Предыдушая страница
+default.paginate.next=Следующая страница
+
+# Ошибки при присвоении данных. Для точной настройки для полей классов используйте
+# формат "typeMismatch.$className.$propertyName" (например, typeMismatch.Book.author)
+typeMismatch.java.net.URL=Значение поля {0} не является допустимым URL
+typeMismatch.java.net.URI=Значение поля {0} не является допустимым URI
+typeMismatch.java.util.Date=Значение поля {0} не является допустимой датой
+typeMismatch.java.lang.Double=Значение поля {0} не является допустимым числом
+typeMismatch.java.lang.Integer=Значение поля {0} не является допустимым числом
+typeMismatch.java.lang.Long=Значение поля {0} не является допустимым числом
+typeMismatch.java.lang.Short=Значение поля {0} не является допустимым числом
+typeMismatch.java.math.BigDecimal=Значение поля {0} не является допустимым числом
+typeMismatch.java.math.BigInteger=Значение поля {0} не является допустимым числом
Index: grails-app/i18n/messages_sv.properties
===================================================================
diff -u
--- grails-app/i18n/messages_sv.properties (revision 0)
+++ grails-app/i18n/messages_sv.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,55 @@
+default.doesnt.match.message=Attributet [{0}] för klassen [{1}] med värde [{2}] matchar inte mot uttrycket [{3}]
+default.invalid.url.message=Attributet [{0}] för klassen [{1}] med värde [{2}] är inte en giltig URL
+default.invalid.creditCard.message=Attributet [{0}] för klassen [{1}] med värde [{2}] är inte ett giltigt kreditkortsnummer
+default.invalid.email.message=Attributet [{0}] för klassen [{1}] med värde [{2}] är inte en giltig e-postadress
+default.invalid.range.message=Attributet [{0}] för klassen [{1}] med värde [{2}] är inte inom intervallet [{3}] till [{4}]
+default.invalid.size.message=Attributet [{0}] för klassen [{1}] med värde [{2}] har en storlek som inte är inom [{3}] till [{4}]
+default.invalid.max.message=Attributet [{0}] för klassen [{1}] med värde [{2}] överskrider maxvärdet [{3}]
+default.invalid.min.message=Attributet [{0}] för klassen [{1}] med värde [{2}] är mindre än minimivärdet [{3}]
+default.invalid.max.size.message=Attributet [{0}] för klassen [{1}] med värde [{2}] överskrider maxstorleken [{3}]
+default.invalid.min.size.message=Attributet [{0}] för klassen [{1}] med värde [{2}] är mindre än minimistorleken [{3}]
+default.invalid.validator.message=Attributet [{0}] för klassen [{1}] med värde [{2}] är inte giltigt enligt anpassad regel
+default.not.inlist.message=Attributet [{0}] för klassen [{1}] med värde [{2}] är inte giltigt, måste vara ett av [{3}]
+default.blank.message=Attributet [{0}] för klassen [{1}] får inte vara tomt
+default.not.equal.message=Attributet [{0}] för klassen [{1}] med värde [{2}] får inte vara lika med [{3}]
+default.null.message=Attributet [{0}] för klassen [{1}] får inte vara tomt
+default.not.unique.message=Attributet [{0}] för klassen [{1}] med värde [{2}] måste vara unikt
+
+default.paginate.prev=Föregående
+default.paginate.next=Nästa
+default.boolean.true=Sant
+default.boolean.false=Falskt
+default.date.format=yyyy-MM-dd HH:mm:ss z
+default.number.format=0
+
+default.created.message={0} {1} skapades
+default.updated.message={0} {1} uppdaterades
+default.deleted.message={0} {1} borttagen
+default.not.deleted.message={0} {1} kunde inte tas bort
+default.not.found.message={0} med id {1} kunde inte hittas
+default.optimistic.locking.failure=En annan användare har uppdaterat det här {0} objektet medan du redigerade det
+
+default.home.label=Hem
+default.list.label= {0} - Lista
+default.add.label=Lägg till {0}
+default.new.label=Skapa {0}
+default.create.label=Skapa {0}
+default.show.label=Visa {0}
+default.edit.label=Ändra {0}
+
+default.button.create.label=Skapa
+default.button.edit.label=Ändra
+default.button.update.label=Uppdatera
+default.button.delete.label=Ta bort
+default.button.delete.confirm.message=Är du säker?
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=Värdet för {0} måste vara en giltig URL
+typeMismatch.java.net.URI=Värdet för {0} måste vara en giltig URI
+typeMismatch.java.util.Date=Värdet {0} måste vara ett giltigt datum
+typeMismatch.java.lang.Double=Värdet {0} måste vara ett giltigt nummer
+typeMismatch.java.lang.Integer=Värdet {0} måste vara ett giltigt heltal
+typeMismatch.java.lang.Long=Värdet {0} måste vara ett giltigt heltal
+typeMismatch.java.lang.Short=Värdet {0} måste vara ett giltigt heltal
+typeMismatch.java.math.BigDecimal=Värdet {0} måste vara ett giltigt nummer
+typeMismatch.java.math.BigInteger=Värdet {0} måste vara ett giltigt heltal
\ No newline at end of file
Index: grails-app/i18n/messages_th.properties
===================================================================
diff -u
--- grails-app/i18n/messages_th.properties (revision 0)
+++ grails-app/i18n/messages_th.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,55 @@
+default.doesnt.match.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่ถูกต้องตามรูปแบบที่กำหนดไว้ใน [{3}]
+default.invalid.url.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่ถูกต้องตามรูปแบบ URL
+default.invalid.creditCard.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่ถูกต้องตามรูปแบบหมายเลขบัตรเครดิต
+default.invalid.email.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่ถูกต้องตามรูปแบบอีเมล์
+default.invalid.range.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่ได้มีค่าที่ถูกต้องในช่วงจาก [{3}] ถึง [{4}]
+default.invalid.size.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่ได้มีขนาดที่ถูกต้องในช่วงจาก [{3}] ถึง [{4}]
+default.invalid.max.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] มีค่าเกิดกว่าค่ามากสุด [{3}]
+default.invalid.min.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] มีค่าน้อยกว่าค่าต่ำสุด [{3}]
+default.invalid.max.size.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] มีขนาดเกินกว่าขนาดมากสุดของ [{3}]
+default.invalid.min.size.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] มีขนาดต่ำกว่าขนาดต่ำสุดของ [{3}]
+default.invalid.validator.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่ผ่านการทวนสอบค่าที่ตั้งขึ้น
+default.not.inlist.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่ได้อยู่ในรายการต่อไปนี้ [{3}]
+default.blank.message=คุณสมบัติ [{0}] ของคลาส [{1}] ไม่สามารถเป็นค่าว่างได้
+default.not.equal.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่สามารถเท่ากับ [{3}] ได้
+default.null.message=คุณสมบัติ [{0}] ของคลาส [{1}] ไม่สามารถเป็น null ได้
+default.not.unique.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] จะต้องไม่ซ้ำ (unique)
+
+default.paginate.prev=ก่อนหน้า
+default.paginate.next=ถัดไป
+default.boolean.true=จริง
+default.boolean.false=เท็จ
+default.date.format=dd-MM-yyyy HH:mm:ss z
+default.number.format=0
+
+default.created.message=สร้าง {0} {1} เรียบร้อยแล้ว
+default.updated.message=ปรับปรุง {0} {1} เรียบร้อยแล้ว
+default.deleted.message=ลบ {0} {1} เรียบร้อยแล้ว
+default.not.deleted.message=ไม่สามารถลบ {0} {1}
+default.not.found.message=ไม่พบ {0} ด้วย id {1} นี้
+default.optimistic.locking.failure=มีผู้ใช้ท่านอื่นปรับปรุง {0} ขณะที่คุณกำลังแก้ไขข้อมูลอยู่
+
+default.home.label=หน้าแรก
+default.list.label=รายการ {0}
+default.add.label=เพิ่ม {0}
+default.new.label=สร้าง {0} ใหม่
+default.create.label=สร้าง {0}
+default.show.label=แสดง {0}
+default.edit.label=แก้ไข {0}
+
+default.button.create.label=สร้าง
+default.button.edit.label=แก้ไข
+default.button.update.label=ปรับปรุง
+default.button.delete.label=ลบ
+default.button.delete.confirm.message=คุณแน่ใจหรือไม่ ?
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=คุณสมบัติ '{0}' จะต้องเป็นค่า URL ที่ถูกต้อง
+typeMismatch.java.net.URI=คุณสมบัติ '{0}' จะต้องเป็นค่า URI ที่ถูกต้อง
+typeMismatch.java.util.Date=คุณสมบัติ '{0}' จะต้องมีค่าเป็นวันที่
+typeMismatch.java.lang.Double=คุณสมบัติ '{0}' จะต้องมีค่าเป็นจำนวนประเภท Double
+typeMismatch.java.lang.Integer=คุณสมบัติ '{0}' จะต้องมีค่าเป็นจำนวนประเภท Integer
+typeMismatch.java.lang.Long=คุณสมบัติ '{0}' จะต้องมีค่าเป็นจำนวนประเภท Long
+typeMismatch.java.lang.Short=คุณสมบัติ '{0}' จะต้องมีค่าเป็นจำนวนประเภท Short
+typeMismatch.java.math.BigDecimal=คุณสมบัติ '{0}' จะต้องมีค่าเป็นจำนวนประเภท BigDecimal
+typeMismatch.java.math.BigInteger=คุณสมบัติ '{0}' จะต้องมีค่าเป็นจำนวนประเภท BigInteger
Index: grails-app/i18n/messages_zh_CN.properties
===================================================================
diff -u
--- grails-app/i18n/messages_zh_CN.properties (revision 0)
+++ grails-app/i18n/messages_zh_CN.properties (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,18 @@
+default.blank.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u4E0D\u80FD\u4E3A\u7A7A
+default.doesnt.match.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0E\u5B9A\u4E49\u7684\u6A21\u5F0F [{3}]\u4E0D\u5339\u914D
+default.invalid.creditCard.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0D\u662F\u4E00\u4E2A\u6709\u6548\u7684\u4FE1\u7528\u5361\u53F7
+default.invalid.email.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0D\u662F\u4E00\u4E2A\u5408\u6CD5\u7684\u7535\u5B50\u90AE\u4EF6\u5730\u5740
+default.invalid.max.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u6BD4\u6700\u5927\u503C [{3}]\u8FD8\u5927
+default.invalid.max.size.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u7684\u5927\u5C0F\u6BD4\u6700\u5927\u503C [{3}]\u8FD8\u5927
+default.invalid.min.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u6BD4\u6700\u5C0F\u503C [{3}]\u8FD8\u5C0F
+default.invalid.min.size.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u7684\u5927\u5C0F\u6BD4\u6700\u5C0F\u503C [{3}]\u8FD8\u5C0F
+default.invalid.range.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0D\u5728\u5408\u6CD5\u7684\u8303\u56F4\u5185( [{3}] \uFF5E [{4}] )
+default.invalid.size.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u7684\u5927\u5C0F\u4E0D\u5728\u5408\u6CD5\u7684\u8303\u56F4\u5185( [{3}] \uFF5E [{4}] )
+default.invalid.url.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0D\u662F\u4E00\u4E2A\u5408\u6CD5\u7684URL
+default.invalid.validator.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u672A\u80FD\u901A\u8FC7\u81EA\u5B9A\u4E49\u7684\u9A8C\u8BC1
+default.not.equal.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0E[{3}]\u4E0D\u76F8\u7B49
+default.not.inlist.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0D\u5728\u5217\u8868\u7684\u53D6\u503C\u8303\u56F4\u5185
+default.not.unique.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u5FC5\u987B\u662F\u552F\u4E00\u7684
+default.null.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u4E0D\u80FD\u4E3Anull
+default.paginate.next=\u4E0B\u9875
+default.paginate.prev=\u4E0A\u9875
Index: grails-app/init/BootStrap.groovy
===================================================================
diff -u
--- grails-app/init/BootStrap.groovy (revision 0)
+++ grails-app/init/BootStrap.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,8 @@
+class BootStrap {
+
+ def init = { servletContext ->
+ }
+
+ def destroy = {
+ }
+}
Index: grails-app/init/ds/service/Application.groovy
===================================================================
diff -u
--- grails-app/init/ds/service/Application.groovy (revision 0)
+++ grails-app/init/ds/service/Application.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,10 @@
+package ds.service
+
+import grails.boot.GrailsApp
+import grails.boot.config.GrailsAutoConfiguration
+
+class Application extends GrailsAutoConfiguration {
+ static void main(String[] args) {
+ GrailsApp.run(Application, args)
+ }
+}
Index: grails-app/jobs/com/lemans/ds/importing/BulkImportJob.groovy
===================================================================
diff -u
--- grails-app/jobs/com/lemans/ds/importing/BulkImportJob.groovy (revision 0)
+++ grails-app/jobs/com/lemans/ds/importing/BulkImportJob.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,22 @@
+package com.lemans.ds.importing
+
+import com.lemans.services.LemansManager
+import org.quartz.JobExecutionContext
+
+
+class BulkImportJob extends LemansManager {
+
+ def bulkLocaleImportProcessService
+
+ def bulkImportService
+
+ static triggers = { }
+
+ def execute(JobExecutionContext context) {
+ Integer processId = context.mergedJobDataMap.get('processId')
+ LocaleImportProcess localeImportProcess = bulkImportService.findLocaleImportProcess(processId)
+ if (localeImportProcess) {
+ bulkLocaleImportProcessService.triggerProcess(localeImportProcess)
+ }
+ }
+}
Index: grails-app/jobs/com/lemans/ds/splitvalidation/ProductSplitValidationJob.groovy
===================================================================
diff -u
--- grails-app/jobs/com/lemans/ds/splitvalidation/ProductSplitValidationJob.groovy (revision 0)
+++ grails-app/jobs/com/lemans/ds/splitvalidation/ProductSplitValidationJob.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,21 @@
+package com.lemans.ds.splitvalidation
+
+import com.lemans.services.LemansManager
+import org.quartz.DisallowConcurrentExecution
+
+@DisallowConcurrentExecution
+class ProductSplitValidationJob extends LemansManager {
+
+
+ def jobStatusManagerService
+
+ static triggers = {
+ cron startDelay: 15000, cronExpression: '0 0/30 * * * ?'
+ }
+
+ def concurrent = false
+
+ def execute() {
+ jobStatusManagerService.execute()
+ }
+}
Index: grails-app/services/com/lemans/common/CommonService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/common/CommonService.groovy (revision 0)
+++ grails-app/services/com/lemans/common/CommonService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,36 @@
+package com.lemans.common
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+/**
+ * CommonService provides miscellaneous query operations that are common,
+ * i.e. looking up reference data, etc.
+ *
+ */
+@Transactional(readOnly = true)
+class CommonService extends LemansService {
+
+ /**
+ * Finds a SubComCode by code.
+ *
+ * @param criteria
+ *
+ * @return Map containing SubComCode info
+ */
+ Map findSubComCode(Map criteria) {
+ dqx(criteria).executeOneFrom('dbo.vwSubComCode', ['subComCode = :subComCode']).results[0]
+ }
+
+ /**
+ * Finds SubComCodes, currently All.
+ *
+ * @param criteria
+ *
+ * @return List containing subComCodes
+ */
+ List findSubComCodes(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'subComCode+ASC'
+ dqx(criteria).executeFrom('dbo.vwSubComCode', []).results
+ }
+}
Index: grails-app/services/com/lemans/ds/attribute/AttributeNameManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/attribute/AttributeNameManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/attribute/AttributeNameManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,86 @@
+package com.lemans.ds.attribute
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class AttributeNameManagerService extends LemansManager {
+
+ def attributeNameService
+
+ /**
+ * Updates attributeName.
+
+ * @return AttributeName which may have errors or be null
+ */
+ AttributeName updateAttributeName(Map values, Integer id, String username) {
+ AttributeName attributeName = findDomainObjectById(AttributeName, id)
+ if (attributeName) {
+ applyValuesToDomain(values, attributeName)
+ attributeName.validate()
+ saveOrDiscardDomain(attributeName, username)
+ }
+ }
+
+ /**
+ * Creates a new AttributeName.
+ *
+ * @param values
+ * @param username
+ *
+ * @return AttributeName which may contain errors or be null
+ */
+ AttributeName createAttributeName(Map values, String username) {
+ AttributeName attributeName = new AttributeName(values)
+ attributeName.validate()
+ saveOrDiscardDomain(attributeName, username)
+ }
+
+ /**
+ * Creates a new AttributeNameLocale.
+ *
+ * @param values
+ * @param username
+ *
+ * @return AttributeNameLocale which may contain errors or be null
+ */
+ AttributeNameLocale updateAttributeNameWithLocale(Map values, String username) {
+ AttributeNameLocale attributeNameLocale = AttributeNameLocale.findByAttributeNameIdAndLocale(values.attributeNameId, values.locale)
+ if (!attributeNameLocale) {
+ AttributeName attributeName = AttributeName.findById(values.attributeNameId)
+ if (attributeName) {
+ attributeNameLocale = new AttributeNameLocale([attributeNameId: attributeName.id])
+ } else {
+ return
+ }
+ }
+ applyValuesToDomain(values, attributeNameLocale)
+ attributeNameLocale.validate()
+ saveOrDiscardDomain(attributeNameLocale, username)
+ }
+
+ /**
+ * Soft deletes a AttributeName.
+ *
+ * @param attributeNameId
+ * @param username
+ *
+ * @return DomainForm which may errors or be null
+ */
+ AttributeName deleteAttributeName(Integer id, String username) {
+ AttributeName attributeName
+ Map attributeNameMap = attributeNameService.findAttributeNameById([attributeNameId: id])
+ if (attributeNameMap) {
+ if (attributeNameMap.canDelete) {
+ attributeName = findDomainObjectById(AttributeName, id)
+ if (attributeName) {
+ softDelete(attributeName, username)
+ }
+ } else {
+ attributeName = new AttributeName()
+ attributeName.errors.reject('invalid', 'Cannot delete AttributeName when assigned to Category or Part')
+ }
+ }
+ attributeName
+ }
+}
Index: grails-app/services/com/lemans/ds/attribute/AttributeNameService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/attribute/AttributeNameService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/attribute/AttributeNameService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,41 @@
+package com.lemans.ds.attribute
+
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional
+class AttributeNameService extends LemansService {
+
+ /**
+ * Find a attribute by attributeNameId.
+ *
+ * @param criteria containing attributeNameId
+ *
+ * @return attribute
+ */
+ Map findAttributeNameById(Map criteria) {
+ List clauses = ['attributeNameId = :attributeNameId']
+ dqx(criteria).executeOneFromLocale('dbo.vwAttributeName', clauses)?.results[0]
+ }
+
+ /**
+ * Get collection of Attribute Name
+ *
+ * @param criteria containing pageSize and sorting params
+ *
+ * @return Map containing results and totalRecords
+ */
+ Map findAttributeNames(Map criteria) {
+ criteria.sorting = criteria.sorting ?: localeName(criteria, 'attributeName')
+ dqx(criteria).executeFromLocale('dbo.vwAttributeName')
+ }
+
+ private String localeName(Map criteria, String name) {
+ name + (localeExists(criteria) ? 'Locale' : '')
+ }
+
+ private boolean localeExists(Map criteria) {
+ criteria.locale && criteria.locale != Locale.default.toString()
+ }
+}
Index: grails-app/services/com/lemans/ds/attribute/AttributeService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/attribute/AttributeService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/attribute/AttributeService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,103 @@
+package com.lemans.ds.attribute
+
+import com.lemans.services.LemansService
+import groovy.json.JsonSlurper
+import org.json.XML
+
+/**
+ * Created by vramisetti on 8/18/2017.
+ */
+class AttributeService extends LemansService {
+
+ private static final String ATTR_FROM = 'FROM vwPartAttribute x INNER JOIN vwPart p ON x.partNumber = p.partNumber'
+
+ private static final String ATTR_GROUP_BY = '''p.partNumber, x.partAttributeId, x.attributeNameId, x.attributeName, x.attributeValueId,
+x.attributeValue, x.partNumber, p.punctuatedPartNumber, p.categoryId, p.partDescr, p.categoryName, p.mediaUrl, p.qualifiedCategoryName'''
+
+ /**
+ * Find parts by attribute name.
+ *
+ * @param criteria containing attributeNameId, attributeValueId and categoryId
+ *
+ * @return Map containing results and totalRecords
+ */
+ Map partsByAttributeName(Map criteria) {
+ partsByAttribute(criteria + [attrSelect: 'x.attributeNameId, x.attributeName',
+ groupingAttrSelect: 'pa.attributeValue, pa.attributeValueId', groupingAttrName: 'attributeValues'])
+ }
+
+ /**
+ * Find parts by attribute value.
+ *
+ * @param criteria containing attributeNameId, attributeValueId and categoryId
+ *
+ * @return Map containing results and totalRecords
+ */
+ Map partsByAttributeValue(Map criteria) {
+ partsByAttribute(criteria + [attrSelect: 'x.attributeValueId, x.attributeValue',
+ groupingAttrSelect: 'pa.attributeName, pa.attributeNameId', groupingAttrName: 'attributeNames'])
+ }
+
+ private Map partsByAttribute(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'x.partAttributeId'
+ criteria.groupBy = ATTR_GROUP_BY
+ Map data = dqx(criteria).executeSelectFrom(selectQuery(criteria), ATTR_FROM, partsAttributeClauses(criteria))
+ addGroupingAttr(criteria, data)
+ }
+
+ private String selectQuery(Map criteria) {
+ """SELECT ${criteria.attrSelect}, x.partNumber, p.punctuatedPartNumber, p.categoryId, p.partDescr, p.categoryName, p.mediaUrl,
+ p.qualifiedCategoryName, (
+ SELECT DISTINCT ${criteria.groupingAttrSelect}
+ FROM vwPartAttribute pa
+ WHERE pa.partNumber = p.partNumber AND ${criteria.attributeNameId ?
+ "pa.attributeNameId = ${criteria.attributeNameId}" : "pa.attributeValueId = ${criteria.attributeValueId}"}
+ FOR XML PATH('attributes')) AS attributes"""
+ }
+
+ private Map addGroupingAttr(Map criteria, Map data) {
+ JsonSlurper slurper = new JsonSlurper()
+ data.results.each {
+ it[criteria.groupingAttrName] = slurper.parseText(XML.toJSONArray(it.attributes.characterStream.text).toString()).attributes
+ it.remove('attributes')
+ }
+ data
+ }
+
+ /**
+ * Find categories by attributeValue
+ *
+ * @param criteria containing attributeValueId
+ *
+ * @return Map containing results and totalRecords
+ */
+ Map categoriesByAttributeValue(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'categoryAttributeValueId'
+ dqx(criteria).executeFrom('vwCategoryAttributeValue', attributeClauses(criteria))
+ }
+
+ /**
+ * Find categories by attributeName
+ *
+ * @param criteria containing attributeNameId
+ *
+ * @return Map containing results and totalRecords
+ */
+ Map categoriesByAttributeName(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'categoryAttributeId'
+ dqx(criteria).executeFrom('vwCategoryAttribute', attributeClauses(criteria))
+ }
+
+ private List partsAttributeClauses(Map criteria) {
+ List clauses = attributeClauses(criteria)
+ if (criteria.categoryId) { clauses << 'p.categoryId = :categoryId' }
+ clauses
+ }
+
+ private List attributeClauses(Map criteria) {
+ List clauses = []
+ if (criteria.attributeNameId) { clauses << 'x.attributeNameId = :attributeNameId' }
+ if (criteria.attributeValueId) { clauses << 'x.attributeValueId = :attributeValueId' }
+ clauses
+ }
+}
Index: grails-app/services/com/lemans/ds/attribute/AttributeValueManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/attribute/AttributeValueManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/attribute/AttributeValueManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,86 @@
+package com.lemans.ds.attribute
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class AttributeValueManagerService extends LemansManager {
+
+ def attributeValueService
+
+ /**
+ * Updates attributeValue.
+
+ * @return AttributeValue which may have errors or be null
+ */
+ AttributeValue updateAttributeValue(Map values, String username) {
+ AttributeValue attributeValue = findDomainObjectById(AttributeValue, values.attributeValueId)
+ if (attributeValue) {
+ applyValuesToDomain(values, attributeValue)
+ attributeValue.validate()
+ saveOrDiscardDomain(attributeValue, username)
+ }
+ }
+
+ /**
+ * Creates a new AttributeValue.
+ *
+ * @param values
+ * @param username
+ *
+ * @return AttributeValue which may contain errors or be null
+ */
+ AttributeValue createAttributeValue(Map values, String username) {
+ AttributeValue attributeValue = new AttributeValue(values)
+ attributeValue.validate()
+ saveOrDiscardDomain(attributeValue, username)
+ }
+
+ /**
+ * Creates a new AttributeValue and AttributeValueLocale.
+ *
+ * @param values
+ * @param username
+ *
+ * @return AttributeValueLocale which may contain errors or be null
+ */
+ AttributeValueLocale updateAttributeValueWithLocale(Map values, String username) {
+ AttributeValueLocale attributeValueLocale = AttributeValueLocale.findByAttributeValueIdAndLocale(values.attributeValueId, values.locale)
+ if (!attributeValueLocale) {
+ AttributeValue attributeValue = AttributeValue.findById(values.attributeValueId)
+ if (attributeValue) {
+ attributeValueLocale = new AttributeValueLocale([attributeValueId: attributeValue.id])
+ } else {
+ return
+ }
+ }
+ applyValuesToDomain(values, attributeValueLocale)
+ attributeValueLocale.validate()
+ saveOrDiscardDomain(attributeValueLocale, username)
+ }
+
+ /**
+ * Soft deletes a AttributeValue.
+ *
+ * @param attributeValueId
+ * @param username
+ *
+ * @return DomainForm which may errors or be null
+ */
+ AttributeValue deleteAttributeValue(Integer id, String username) {
+ AttributeValue attributeValue
+ Map attributeValueMap = attributeValueService.findAttributeValueById([attributeValueId: id])
+ if (attributeValueMap) {
+ if (attributeValueMap.canDelete) {
+ attributeValue = findDomainObjectById(AttributeValue, id)
+ if (attributeValue) {
+ softDelete(attributeValue, username)
+ }
+ } else {
+ attributeValue = new AttributeValue()
+ attributeValue.errors.reject('invalid', 'Cannot delete AttributeValue when assigned to Category or Part')
+ }
+ }
+ attributeValue
+ }
+}
Index: grails-app/services/com/lemans/ds/attribute/AttributeValueService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/attribute/AttributeValueService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/attribute/AttributeValueService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,79 @@
+package com.lemans.ds.attribute
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional
+class AttributeValueService extends LemansService {
+
+ private static final String VW_CATEGORY_ATTRIBUTE_VALUE = 'SELECT DISTINCT cav.attributeValueId, cav.attributeValue FROM ' +
+ 'dbo.vwCategoryAttributeValue cav WHERE cav.attributeNameId = ?'
+
+ /**
+ * Find a attribute by attributeValueId.
+ *
+ * @param criteria containing attributeValueId
+ *
+ * @return attribute
+ */
+ def findAttributeValueById(Map criteria) {
+ List clauses = ['attributeValueId = :attributeValueId']
+ dqx(criteria).executeOneFromLocale('dbo.vwAttributeValue', clauses)?.results[0]
+ }
+
+ /**
+ * Get collection of Attribute Name
+ *
+ * @param criteria containing pageSize and sorting params
+ *
+ * @return Map containing results and totalRecords
+ */
+ Map findAttributeValues(Map criteria) {
+ criteria.sorting = criteria.sorting ?: localeName(criteria, 'attributeValue')
+ dqx(criteria).executeFromLocale('dbo.vwAttributeValue', qClauses(criteria))
+ }
+
+ /**
+ * Gets collection of Attribute values by Attribute Name Id
+ *
+ * @param attributeNameId
+ *
+ * @return Map containing results
+ */
+ Map findAttributeValuesByAttributeNameId(Integer attributeNameId) {
+ [results: sql().rows(VW_CATEGORY_ATTRIBUTE_VALUE, [attributeNameId])]
+ }
+
+ private List qClauses(criteria) {
+ List clauses = []
+ if (criteria.q) {
+ expandQ(criteria)
+ criteria.sort == null
+ if (localeExists(criteria)) {
+ criteria.sorting = '''CASE WHEN attributeValueLocale = :q THEN 1
+ WHEN attributeValueLocale LIKE :beginningWithQ THEN 2 ELSE 3 END'''
+ clauses << 'attributeValueLocale LIKE :containingQ'
+ } else {
+ criteria.sorting = '''CASE WHEN attributeValue = :q THEN 1
+ WHEN attributeValue LIKE :beginningWithQ THEN 2 ELSE 3 END'''
+ clauses << 'attributeValue LIKE :containingQ'
+ }
+ }
+ clauses
+ }
+
+ private expandQ(criteria) {
+ criteria.with {
+ beginningWithQ = "$q%".toString()
+ containingQ = "%$q%".toString()
+ }
+ }
+
+ private String localeName(Map criteria, String name) {
+ name + (localeExists(criteria) ? 'Locale' : '')
+ }
+
+ private boolean localeExists(Map criteria) {
+ criteria.locale && criteria.locale != Locale.default.toString()
+ }
+}
Index: grails-app/services/com/lemans/ds/bulk/BulkLocaleImportProcessService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/bulk/BulkLocaleImportProcessService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/bulk/BulkLocaleImportProcessService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,133 @@
+package com.lemans.ds.bulk
+
+import com.lemans.ds.bulk.excel.ExcelBuilderHelper
+import com.lemans.ds.bulk.excel.ExcelParserHelper
+import com.lemans.ds.importing.LocaleImportProcess
+import com.lemans.services.LemansManager
+import org.apache.poi.POIXMLException
+import org.apache.poi.hssf.usermodel.HSSFWorkbook
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException
+import org.apache.poi.ss.usermodel.Workbook
+
+import javax.annotation.Resource
+
+class BulkLocaleImportProcessService extends LemansManager {
+
+ def bulkProductFeatureService
+
+ def bulkProductService
+
+ def bulkCategoryAttributeValueService
+
+ def bulkPartService
+
+ @Resource(name = 'dsLocaleImport')
+ def dsLocaleImport
+
+ private static
+ final List HEADER_IDENTIFIER_FOR_CATEGORY_ATTRIBUTE_VALUE = ['Category Id', 'Attribute Name', 'Attribute Value', 'Locale']
+
+ private static final List HEADER_IDENTIFIER_FOR_PART = ['Part Number', 'Locale']
+
+ private final ExcelBuilderHelper excelBuilderHelper = new ExcelBuilderHelper()
+
+ private final ExcelParserHelper excelParserHelper = new ExcelParserHelper()
+
+ @SuppressWarnings(['CatchException'])
+ void triggerProcess(LocaleImportProcess localeImportProcess) {
+ try {
+ logStart(localeImportProcess)
+ List excelSheets = excelSheetsOfImportProcess(localeImportProcess)
+ if (excelSheets) {
+ HSSFWorkbook outputFile = new HSSFWorkbook()
+ saveSheetsDataAndGenerateExcel(excelSheets, outputFile, localeImportProcess)
+ outputFile.close()
+ logEnd(outputFile.sheets == [] ? 'Success' : 'Partial Success', 'Completed', localeImportProcess)
+ } else { logEnd('No Data', 'No Data Found', localeImportProcess) }
+ } catch (POIXMLException e) {
+ String message = (e.cause.class == InvalidFormatException) ? 'Corrupt: Invalid Format' : 'Invalid Excel'
+ logFailed(message, localeImportProcess, e)
+ } catch (FileNotFoundException e) {
+ logFailed('Internal Error: File not Found', localeImportProcess, e)
+ } catch (Exception e) {
+ logFailed('Internal Error: Please Contact helpDesk', localeImportProcess, e)
+ }
+ }
+
+ private List excelSheetsOfImportProcess(LocaleImportProcess localeImportProcess) {
+ Workbook inputFile = excelParserHelper.workBook(filesLocation(localeImportProcess.id))
+ excelParserHelper.collectSheetsFromExcel(inputFile, localeImportProcess.importType)
+ }
+
+ private void saveSheetsDataAndGenerateExcel(List excelSheets, Workbook reportBook, LocaleImportProcess localeImportProcess) {
+ excelSheets.each { Map sheet ->
+ List errorData = saveSheet(sheet.data, sheet.sheetName, localeImportProcess.createdBy)
+ if (errorData != []) {
+ String reportFilePath = reportFilePath(localeImportProcess.id)
+ generateReportBySheet(errorData, sheet, reportBook, reportFilePath)
+ }
+ }
+ }
+
+ private void generateReportBySheet(List errorData, Map sheet, Workbook reportBook, String reportFilePath) {
+ switch (sheet.sheetName) {
+ case 'CategoryAttributeValue':
+ excelBuilderHelper.reportBySheet(errorData, HEADER_IDENTIFIER_FOR_CATEGORY_ATTRIBUTE_VALUE,
+ reportBook, 'Report', reportFilePath)
+ excelBuilderHelper.reportBySheet(sheet.data, sheet.identifier, reportBook, sheet.sheetName, reportFilePath)
+ break
+ case 'Part':
+ excelBuilderHelper.reportBySheet(errorData, HEADER_IDENTIFIER_FOR_PART,
+ reportBook, 'Report', reportFilePath)
+ excelBuilderHelper.reportBySheet(sheet.data, sheet.identifier, reportBook, sheet.sheetName, reportFilePath)
+ break
+ case 'Product':
+ excelBuilderHelper.reportBySheet(errorData, sheet.identifier, reportBook, sheet.sheetName, reportFilePath)
+ break
+ case 'ProductFeature':
+ excelBuilderHelper.reportBySheet(errorData, sheet.identifier, reportBook, sheet.sheetName, reportFilePath)
+ break
+ }
+ }
+
+ private String reportFilePath(Integer id) { "${filesLocation(id)}/report.xls" }
+
+ private String filesLocation(Integer id) { "$dsLocaleImport$id" }
+
+ List saveSheet(List data, String sheetName, String username) {
+ switch (sheetName) {
+ case 'ProductFeature':
+ bulkProductFeatureService.updateOrCreateProductFeatures(data, username)
+ break
+ case 'Product':
+ bulkProductService.updateOrCreateProducts(data, username)
+ break
+ case 'CategoryAttributeValue':
+ bulkCategoryAttributeValueService.updateOrCreateCategoryAttributeValue(data, username)
+ break
+ case 'Part':
+ bulkPartService.updateOrCreateParts(data, username)
+ break
+ }
+ }
+
+ private void logStart(LocaleImportProcess localeImportProcess) {
+ setStatus(localeImportProcess, [status: 'Processing', startDate: new Date()])
+ }
+
+ private void logFailed(String message, LocaleImportProcess localeImportProcess, Exception e) {
+ logEnd('Failed', message, localeImportProcess, e)
+ }
+
+ private void logEnd(String status, String message, LocaleImportProcess localeImportProcess, Exception e = null) {
+ if (e) { log.error e.message, e }
+ setStatus(localeImportProcess, [status: status, endDate: new Date(), message: message])
+ }
+
+ private void setStatus(LocaleImportProcess localeImportProcess, Map data) {
+ LocaleImportProcess.withTransaction {
+ localeImportProcess.properties = data
+ localeImportProcess.save(flush: true)
+ }
+ }
+}
Index: grails-app/services/com/lemans/ds/bulk/BulkProcessingHelperService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/bulk/BulkProcessingHelperService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/bulk/BulkProcessingHelperService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,20 @@
+package com.lemans.ds.bulk
+
+import com.lemans.services.LemansManager
+
+class BulkProcessingHelperService extends LemansManager {
+
+ def messageSource
+
+ protected static final Map LOCALE_COUNTRY_MAPPING = ['es': 'Spanish', 'de': 'German', 'fr': 'French', 'it': 'Italian']
+
+
+ protected void collectIfErrors(Object object, List errors) {
+ object.errors.fieldErrors.each {
+ errors << "${it.field}: ${messageSource.getMessage(it, Locale.default)}"
+ }
+ object.errors.globalErrors.each {
+ errors << "${it.field}: ${messageSource.getMessage(it, Locale.default)}"
+ }
+ }
+}
Index: grails-app/services/com/lemans/ds/bulk/category/BulkCategoryAttributeValueService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/bulk/category/BulkCategoryAttributeValueService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/bulk/category/BulkCategoryAttributeValueService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,159 @@
+package com.lemans.ds.bulk.category
+
+import com.lemans.ds.bulk.BulkProcessingHelperService
+import com.lemans.ds.category.CategoryAttributeValue
+import com.lemans.ds.category.CategoryAttributeValueLocale
+import org.apache.commons.lang.StringUtils
+import org.apache.poi.ss.usermodel.Cell
+import org.apache.poi.ss.usermodel.DataFormatter
+import org.springframework.dao.DuplicateKeyException
+
+class BulkCategoryAttributeValueService extends BulkProcessingHelperService {
+
+ List updateOrCreateCategoryAttributeValue(List data, String username) {
+ List categoryAttributeValueErrors = []
+ data.each { Map eachRow ->
+ List categoryAttributeValueIds = (categoryAttributeValueIds(eachRow)).findAll { it != null }
+ List validDBIds = (categoryAttributeValueDBIds(categoryAttributeValueIds))*.id
+ Map transposeIdsWithNames = [:]
+ if (validDBIds) {
+ List validDbAttributeNames = getValidDBAttributeNames(validDBIds)
+ transposeIdsWithNames = [validDBIds, validDbAttributeNames].transpose().collectEntries {
+ [(it[0].toString()): it[1]] }
+ }
+ categoryAttributeValueIds.each { Integer id ->
+ if (id in validDBIds) {
+ updateOrCreateCategoryAttributeValueLocales(eachRow, username, id.intValue(),
+ transposeIdsWithNames."$id", categoryAttributeValueErrors)
+ } else {
+ Map errorsInMap = [:]
+ collectErrorsForReport(eachRow, 'Invalid Name for Invalid Id',
+ '', ["Invalid CategoryAttributeValueId: $id"], errorsInMap)
+ errorsInMap?.(categoryAttributeValueErrors << errorsInMap)
+ }
+ }
+
+ }
+ categoryAttributeValueErrors
+ }
+
+ @SuppressWarnings(['CatchException'])
+ private void updateOrCreateCategoryAttributeValueLocales(Map eachRow, String username, Integer id,
+ String attributeName, List categoryAttributeValueErrors) {
+ List explicitLocales = existingLocales(eachRow, attributeName)
+ if (explicitLocales) {
+ CategoryAttributeValueLocale.withTransaction {
+ List categoryAttributeValueLocaleObjects = findAllLocaleObjects(id, explicitLocales)
+ explicitLocales.each { String locale ->
+ List errors = []
+ Map errorsInMap = [:]
+ try {
+ CategoryAttributeValueLocale categoryAttributeValueLocale = categoryAttributeValueLocaleObjects.find {
+ it.locale.equalsIgnoreCase(locale) }
+ Cell attributeValue = eachRow."$attributeName${LOCALE_COUNTRY_MAPPING[locale]}[${locale}]"
+ categoryAttributeValueLocale ? updateCategoryAttributeValueLocale(categoryAttributeValueLocale,
+ username, errors, attributeValue) :
+ addCategoryAttributeValueLocale(eachRow, locale, attributeName, username, errors)
+ if (errors != []) {
+ categoryAttributeValueErrors << collectErrorsForReport(eachRow, attributeName, locale, errors, errorsInMap)
+ }
+ } catch (DuplicateKeyException e) {
+ log.error(e.message)
+ categoryAttributeValueErrors << collectErrorsForReport(eachRow, attributeName, locale,
+ ['duplicate categoryAttributeValue Found'], errorsInMap)
+ } catch (Exception e) {
+ log.error(e.message)
+ categoryAttributeValueErrors << collectErrorsForReport(eachRow, attributeName, locale,
+ ['Exception occurred! If persists, contact HelpDesk'], errorsInMap)
+ }
+ }
+ }
+ }
+ }
+
+ private void updateCategoryAttributeValueLocale(CategoryAttributeValueLocale categoryAttributeValueLocale,
+ String username, List errors, Cell attributeValue) {
+ DataFormatter dataFormatter = new DataFormatter()
+ categoryAttributeValueLocale.attributeValue = dataFormatter.formatCellValue(attributeValue)
+ categoryAttributeValueLocale.validate()
+ saveOrDiscardDomain(categoryAttributeValueLocale, username)
+ if (!categoryAttributeValueLocale.hasErrors()) { categoryAttributeValueLocale.save(flush: true) }
+ collectIfErrors(categoryAttributeValueLocale, errors)
+ }
+
+ private void addCategoryAttributeValueLocale(Map eachRow, String locale, String attributeName,
+ String username, List errors) {
+ CategoryAttributeValueLocale categoryAttributeValueLocale = new CategoryAttributeValueLocale()
+ assignValues(categoryAttributeValueLocale, eachRow, attributeName, locale)
+ categoryAttributeValueLocale.validate()
+ saveOrDiscardDomain(categoryAttributeValueLocale, username)
+ if (!categoryAttributeValueLocale.hasErrors()) { categoryAttributeValueLocale.save(flush: true) }
+ collectIfErrors(categoryAttributeValueLocale, errors)
+ }
+
+ private void assignValues(CategoryAttributeValueLocale categoryAttributeValueLocale, Map eachRow, String attributeName,
+ String locale) {
+ DataFormatter dataFormatter = new DataFormatter()
+ categoryAttributeValueLocale.categoryAttributeValueId = dataFormatter.
+ formatCellValue(eachRow."${attributeName}CategoryAttributeValueId").toInteger()
+ categoryAttributeValueLocale.attributeValue = dataFormatter.
+ formatCellValue(eachRow."$attributeName${LOCALE_COUNTRY_MAPPING[locale]}[${locale}]")
+ categoryAttributeValueLocale.locale = locale.toLowerCase()
+ }
+
+ private List existingLocales(Map eachRow, String attributeName) {
+ List explicitLocales = eachRow.collect { String key, value ->
+ if (key.startsWith("$attributeName")) {
+ if (value.toString()) {
+ StringUtils.substringBetween(key, '[', ']')
+ }
+ }
+ }
+ explicitLocales.findAll { it != null }
+ }
+
+ private List findAllLocaleObjects(Integer categoryAttributeValueId, List locales) {
+ CategoryAttributeValueLocale.findAllByCategoryAttributeValueIdAndLocaleInListAndDateDeletedIsNull(categoryAttributeValueId, locales)
+ }
+
+ private List categoryAttributeValueDBIds(List categoryAttributeValueIds) {
+ categoryAttributeValueIds ? CategoryAttributeValue.findAllByIdInListAndDateDeletedIsNull(categoryAttributeValueIds) : []
+ }
+
+ private List getValidDBAttributeNames(List validDBIds) {
+ if (validDBIds) {
+ List attributeNamesWithSpaces = ((sql().rows('SELECT attributeName FROM vwCategoryAttributeValue WHERE ' +
+ 'categoryAttributeValueId IN (' + validDBIds.join(',') + ')'))*.attributeName).unique()
+ discardSpaces(attributeNamesWithSpaces)
+ }
+ }
+
+ private List discardSpaces(List attributeNames) {
+ List noSpaces = []
+ attributeNames.each { noSpaces << it.toString().replaceAll('\\s', '') }
+ noSpaces
+ }
+
+ private List categoryAttributeValueIds(Map eachRow) {
+ eachRow.collect { String key, value ->
+ if (key.endsWith('CategoryAttributeValueId')) {
+ if (value.toString() != '') {
+ (value.toString() as Double).intValue()
+ }
+ }
+ }
+ }
+
+ private void collectErrorsIntoMap(Map eachRow, String attributeName, String locale, List errors, Map errorsInMap) {
+ errorsInMap.put('Category Id', eachRow.categoryId)
+ errorsInMap.put('Attribute Name', attributeName)
+ errorsInMap.put('Attribute Value', eachRow."$attributeName" ? eachRow."$attributeName" : 'No Value')
+ errorsInMap.put('Locale', locale)
+ errorsInMap.put('report', errors)
+ }
+
+ private Map collectErrorsForReport(Map eachRow, String attributeName, String locale, List errors, Map errorsInMap) {
+ collectErrorsIntoMap(eachRow, attributeName, locale, errors, errorsInMap)
+ if (errorsInMap) { errorsInMap }
+ }
+}
Index: grails-app/services/com/lemans/ds/bulk/part/BulkPartService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/bulk/part/BulkPartService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/bulk/part/BulkPartService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,109 @@
+package com.lemans.ds.bulk.part
+
+import com.lemans.ds.bulk.BulkProcessingHelperService
+import com.lemans.ds.part.Part
+import com.lemans.ds.part.PartLocale
+import org.apache.commons.lang.StringUtils
+import org.springframework.dao.DuplicateKeyException
+
+class BulkPartService extends BulkProcessingHelperService {
+
+ List updateOrCreateParts(List data, String username) {
+ List partsWithErrors = []
+ List partIDs = data*.partNumber
+ List validDBPartNumbers = (Part.findAllByIdInListAndDateDeletedIsNull(partIDs))*.id
+ data.each { Map eachRow ->
+ String partNumber = eachRow.partNumber
+ if (partNumber in validDBPartNumbers) {
+ updateOrCreatePartsLocales(eachRow, username, partNumber, partsWithErrors)
+ } else {
+ Map errorsInMap = [:]
+ collectErrorsForReport(eachRow, '', errorsInMap, ["Invalid Part Number : $partNumber"])
+ errorsInMap ?. (partsWithErrors << errorsInMap)
+ }
+ }
+ partsWithErrors
+ }
+
+ @SuppressWarnings(['CatchException'])
+ private void updateOrCreatePartsLocales(Map eachRow, String username, String partNumber, List partsWithErrors) {
+ List explicitLocales = findLocales(eachRow)
+ if (explicitLocales) {
+ PartLocale.withTransaction {
+ List partLocaleObjects = PartLocale.findAllByPartNumberAndLocaleInListAndDateDeletedIsNull(partNumber, explicitLocales)
+ explicitLocales.each { String locale ->
+ List errors = []
+ Map errorsInMap = [:]
+ try {
+ PartLocale partLocale = partLocaleObjects.find { it.locale.equalsIgnoreCase(locale) }
+ partLocale ? updatePartLocale(eachRow, partLocale, username, locale, errors) :
+ addPartLocale(eachRow, username, locale, partNumber, errors)
+ if (errors != []) {
+ partsWithErrors << collectErrorsForReport(eachRow, locale, errorsInMap, errors)
+ }
+ } catch (DuplicateKeyException e) {
+ log.error(e.message)
+ partsWithErrors << collectErrorsForReport(eachRow, locale, errorsInMap, ['duplicate part found'])
+ } catch (Exception e) {
+ log.error(e.message)
+ partsWithErrors << collectErrorsForReport(eachRow, locale, errorsInMap,
+ ['Exception occurred! If persists, contact HelpDesk'])
+ }
+ }
+ }
+ }
+ partsWithErrors
+ }
+
+ private Map collectErrorsForReport(Map eachRow, String locale, Map errorsInMap, List errors) {
+ collectErrorsIntoMap(eachRow, locale, errorsInMap, errors)
+ if (errorsInMap) { errorsInMap }
+ }
+
+ private void updatePartLocale(Map eachRow, PartLocale partLocale, String username, String locale, List errors) {
+ assignValues(partLocale, eachRow, locale)
+ partLocale.validate()
+ saveOrDiscardDomain(partLocale, username)
+ if (!partLocale.hasErrors()) { partLocale.save(flush: true) }
+ collectIfErrors(partLocale, errors)
+ }
+
+ private void addPartLocale(Map eachRow, String username, String locale, String partNumber, List errors) {
+ PartLocale partLocale = new PartLocale()
+ partLocale.locale = locale
+ partLocale.partNumber = partNumber
+ assignValues(partLocale, eachRow, locale)
+ partLocale.validate()
+ saveOrDiscardDomain(partLocale, username)
+ if (!partLocale.hasErrors()) { partLocale.save(flush: true) }
+ collectIfErrors(partLocale, errors)
+ }
+
+ private void assignValues(PartLocale partLocale, Map eachRow, String locale) {
+ List assignableKeys = eachRow.collect { String key, value ->
+ if (key.endsWith("[$locale]")) {
+ if (value.toString()) { key.replace("${LOCALE_COUNTRY_MAPPING[locale]}[${locale}]", '') }
+ }
+ }
+ List validPartLocaleFields = assignableKeys.findAll { it != null }.unique()
+ validPartLocaleFields.each { String value ->
+ partLocale."$value" = eachRow."$value${LOCALE_COUNTRY_MAPPING[locale]}[${locale}]"
+ }
+ }
+
+ private List findLocales(Map eachRow) {
+ List locales = eachRow.collect { String key, value ->
+ if (key.startsWith('marketingDescr') || key.startsWith('specialInstructions')
+ || key.startsWith('partSpecificText')) {
+ if (value.toString()) { StringUtils.substringBetween(key, '[', ']') }
+ }
+ }
+ locales.findAll { it != null }.unique()
+ }
+
+ private void collectErrorsIntoMap(Map eachRow, String locale, Map errorsInMap, List errors) {
+ errorsInMap.put('Part Number', eachRow.partNumber)
+ errorsInMap.put('Locale', locale)
+ errorsInMap.put('report', errors)
+ }
+}
Index: grails-app/services/com/lemans/ds/bulk/product/BulkProductFeatureService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/bulk/product/BulkProductFeatureService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/bulk/product/BulkProductFeatureService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,102 @@
+package com.lemans.ds.bulk.product
+
+import com.lemans.ds.bulk.BulkProcessingHelperService
+import com.lemans.ds.product.ProductFeatureLocale
+import org.apache.commons.lang.StringUtils
+import org.hibernate.StaleStateException
+import org.springframework.dao.DuplicateKeyException
+
+class BulkProductFeatureService extends BulkProcessingHelperService {
+
+ List updateOrCreateProductFeatures(List productFeatures, String username) {
+ List productFeaturesWithValidProductFeatureId = productFeaturesWithValidProductFeatureId(productFeatures)
+ List validDBProductFeatureIds = validProductFeatureIds(productFeaturesWithValidProductFeatureId*.ProductFeatureIdDummy)
+ List productFeaturesWithErrors = []
+ productFeaturesWithValidProductFeatureId.each { Map productFeature ->
+ List errors = []
+ Integer productFeatureId = productFeature.ProductFeatureIdDummy
+ if (validDBProductFeatureIds.contains(productFeatureId)) {
+ updateOrCreateProductFeatureLocale(productFeature, errors, username)
+ } else { errors << "Invalid productFeatureId: ${productFeatureId}" }
+ if (errors) {
+ productFeature.put('report', errors)
+ productFeature.remove('ProductFeatureIdDummy')
+ productFeaturesWithErrors << productFeature
+ }
+ }
+ productFeaturesWithErrors
+ }
+
+ @SuppressWarnings(['CatchException'])
+ private void updateOrCreateProductFeatureLocale(Map values, List errors, String username) {
+ List existingLocales = getLocalesWithData(values)
+ if (existingLocales) {
+ ProductFeatureLocale.withTransaction {
+ try {
+ List localeObjects = findAllProductFeatureLocales(values.ProductFeatureIdDummy, existingLocales)
+ existingLocales.each { String locale ->
+ ProductFeatureLocale productFeatureLocale = localeObjects.find { it.locale.equalsIgnoreCase(locale) }
+ productFeatureLocale ? updateProductFeatureLocales(productFeatureLocale, locale, values, errors, username)
+ : addProductFeatureLocale(values, locale, errors, username)
+ }
+ } catch (DuplicateKeyException e) {
+ log.error e.message, e
+ errors << 'Duplicate productFeature Found'
+ } catch (StaleStateException e) {
+ log.error e.message, e
+ errors << 'Product FeatureId has already been updated, please try again'
+ } catch (Exception e) {
+ log.error e.message, e
+ errors << 'Exception occurred, please try again. If it persists, please let the development team know about it'
+ }
+ }
+ }
+ }
+
+ private void updateProductFeatureLocales(ProductFeatureLocale productFeatureLocale,
+ String locale, Map values, List errors, String username) {
+ productFeatureLocale.featureText = values["FeatureText${LOCALE_COUNTRY_MAPPING[locale]}[${locale}]"]
+ productFeatureLocale.validate()
+ saveOrDiscardDomain(productFeatureLocale, username)
+ if (!productFeatureLocale.hasErrors()) { productFeatureLocale.save(flush: true) }
+ collectIfErrors(productFeatureLocale, errors)
+ }
+
+ private void addProductFeatureLocale(Map values, String locale, List errors, String username) {
+ ProductFeatureLocale productFeatureLocale = new ProductFeatureLocale()
+ productFeatureLocale.productFeatureId = values.ProductFeatureIdDummy
+ productFeatureLocale.featureText = values["FeatureText${LOCALE_COUNTRY_MAPPING[locale]}[${locale}]"]
+ productFeatureLocale.locale = locale.toLowerCase()
+ productFeatureLocale.validate()
+ saveOrDiscardDomain(productFeatureLocale, username)
+ if (!productFeatureLocale.hasErrors()) { productFeatureLocale.save(flush: true) }
+ collectIfErrors(productFeatureLocale, errors)
+ }
+
+ private List validProductFeatureIds(List productFeatureIds) {
+ sql().rows('SELECT productFeatureId FROM vwProductFeature WHERE productFeatureId IN ('
+ + productFeatureIds.join(',') + ')')?.productFeatureId
+ }
+
+ private List findAllProductFeatureLocales(Integer productFeatureId, List locales) {
+ ProductFeatureLocale.findAllByProductFeatureIdAndLocaleInListAndDateDeletedIsNull(productFeatureId, locales)
+ }
+
+ private List getLocalesWithData(Map data) {
+ List explicitLocales = data.collect { String key, value ->
+ if (key.startsWith('FeatureText')) {
+ if (value.toString()) {
+ StringUtils.substringBetween(key, '[', ']')
+ }
+ }
+ }
+ explicitLocales.findAll { it != null }
+ }
+
+ private List productFeaturesWithValidProductFeatureId(List productFeatures) {
+ productFeatures.findAll { it.ProductFeatureId.toString() != '' }.collect {
+ it.ProductFeatureIdDummy = (it.ProductFeatureId?.toString() as Double)?.intValue()
+ it
+ }
+ }
+}
Index: grails-app/services/com/lemans/ds/bulk/product/BulkProductService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/bulk/product/BulkProductService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/bulk/product/BulkProductService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,113 @@
+package com.lemans.ds.bulk.product
+
+import com.lemans.ds.bulk.BulkProcessingHelperService
+import com.lemans.ds.product.ProductLocale
+import org.apache.commons.lang.StringUtils
+import org.hibernate.StaleStateException
+import org.springframework.dao.DuplicateKeyException
+
+class BulkProductService extends BulkProcessingHelperService {
+
+ List updateOrCreateProducts(List products, String username) {
+ List productsWithValidProductId = productsWithValidProductId(products)
+ List validDBProductIds = validProductIds(productsWithValidProductId*.ProductIdDummy)
+ List productsWithErrors = []
+ productsWithValidProductId.each { Map product ->
+ List errors = []
+ Integer productId = product.ProductIdDummy
+ if (validDBProductIds.contains(productId)) {
+ updateOrCreateProductLocales(product, errors, username)
+ } else { errors << "Invalid productId: ${productId}" }
+ if (errors) {
+ product.put('report', errors)
+ product.remove('ProductIdDummy')
+ productsWithErrors << product
+ }
+ }
+ productsWithErrors
+ }
+
+ @SuppressWarnings(['CatchException'])
+ private void updateOrCreateProductLocales(Map values, List errors, String username) {
+ List existingLocales = getLocalesWithData(values)
+ if (existingLocales) {
+ ProductLocale.withTransaction {
+ try {
+ List productLocaleObjects = findAllProductLocales(values.ProductIdDummy, existingLocales)
+ existingLocales.each { String locale ->
+ ProductLocale productLocale = productLocaleObjects.find { it.locale.equalsIgnoreCase(locale) }
+ productLocale ? updateProductLocale(productLocale, locale, values, errors, username)
+ : addProductLocale(values, locale, errors, username)
+ }
+ } catch (DuplicateKeyException e) {
+ log.error e.message, e
+ errors << 'Duplicate Product Found'
+ } catch (StaleStateException e) {
+ log.error e.message, e
+ errors << 'Product has been updated by different user, please try again'
+ } catch (Exception e) {
+ log.error e.message, e
+ errors << 'Exception occurred, please try again. If it persists, please let the development team know about it'
+ }
+ }
+ }
+ }
+
+ private void updateProductLocale(ProductLocale productLocale, String locale, Map values, List errors, String username) {
+ assignValues(productLocale, values, locale)
+ productLocale.validate()
+ saveOrDiscardDomain(productLocale, username)
+ if (!productLocale.hasErrors()) { productLocale.save(flush: true) }
+ collectIfErrors(productLocale, errors)
+ }
+
+ private void addProductLocale(Map values, String locale, List errors, String username) {
+ ProductLocale productLocale = new ProductLocale()
+ productLocale.locale = locale.toLowerCase()
+ productLocale.productId = values.ProductIdDummy
+ assignValues(productLocale, values, locale)
+ productLocale.validate()
+ saveOrDiscardDomain(productLocale, username)
+ if (!productLocale.hasErrors()) { productLocale.save(flush: true) }
+ collectIfErrors(productLocale, errors)
+ }
+
+ private void assignValues(ProductLocale productLocale, Map values, String locale) {
+ List assignableKeys = values.collect { String key, value ->
+ if (key.endsWith("[$locale]")) {
+ if (value.toString()) { key.replace("${LOCALE_COUNTRY_MAPPING[locale]}[${locale}]", '') }
+ }
+ }
+ List validPartLocaleFields = assignableKeys.findAll { it != null }.unique()
+ validPartLocaleFields.each { String value ->
+ productLocale."$value" = values."$value${LOCALE_COUNTRY_MAPPING[locale]}[${locale}]"
+ }
+ }
+
+ private List findAllProductLocales(Integer productId, List locales) {
+ ProductLocale.findAllByProductIdAndLocaleInListAndDateDeletedIsNull(productId, locales)
+ }
+
+ private List getLocalesWithData(Map data) {
+ List explicitLocales = data.collect { String key, value ->
+ if (key.startsWith('productName') || key.startsWith('caption')
+ || key.startsWith('description')) {
+ if (value.toString()) {
+ StringUtils.substringBetween(key, '[', ']')
+ }
+ }
+ }
+ explicitLocales.findAll { it != null }.unique()
+ }
+
+ private List validProductIds(List productIds) {
+ sql().rows('SELECT productId FROM vwProduct WHERE productId IN (' + productIds.join(',') + ')')?.productId
+ }
+
+ private List productsWithValidProductId(List products) {
+ products.findAll { it.ProductId.toString() != '' }.collect {
+ it.ProductIdDummy = (it.ProductId?.toString() as Double)?.intValue()
+ it
+ }
+ }
+}
Index: grails-app/services/com/lemans/ds/category/CategoryAttributeManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/category/CategoryAttributeManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/category/CategoryAttributeManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,186 @@
+package com.lemans.ds.category
+
+import com.lemans.FeatureToggles
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+@SuppressWarnings('CouldBeElvis')
+class CategoryAttributeManagerService extends LemansManager {
+
+ def categoryAttributeService
+ def categoryService
+
+ private static final String SEQUENCE_SQL =
+ '''EXEC dbo.spMoveCategoryAttribute
+ @sourceId = :id,
+ @targetId = :targetId,
+ @sourceVersion = :sourceVersion,
+ @targetVersion = :targetVersion,
+ @position = :position,
+ @appSecurityUser = :appSecurityUser
+ '''
+
+ private static final String CATEGORY_ATTRIBUTE_MULTIPLE_VALUE =
+ 'SELECT dbo.fnCheckCategoryAttributeMultipleValue (?)'
+
+ private static final int FORCED_OPT_LOCKING_VERSION = -1
+
+ /**
+ * Creates a CategoryAttribute.
+ *
+ * @param values
+ * @param username
+ *
+ * @return CategoryAttribute which may contain errors or be null
+ */
+ CategoryAttribute createCategoryAttribute(Map values, String username) {
+ CategoryAttribute categoryAttribute = new CategoryAttribute(values)
+ categoryAttribute.validate()
+ Map criteria = [catalogInstanceId: values.catalogInstanceId, id: values.categoryId]
+ Map results = categoryService.findCategoryById(criteria)
+ if (!results || results.isEmpty()) {
+ categoryAttribute.errors.reject('invalid', 'Catalog or Category not found')
+ }
+ saveOrDiscardDomain(categoryAttribute, username)
+ }
+
+ /**
+ * Updates a CategoryAttribute.
+ *
+ * @param values - must contain a version entry
+ * @param criteria - catalogInstanceId, categoryId and attributeNameId
+ * @param username
+ * @return CategoryAttribute which may contain errors or be null
+ */
+ CategoryAttribute updateCategoryAttribute(Map values, Map criteria, String username) {
+ CategoryAttribute categoryAttribute = findCategoryAttribute(criteria)
+ if (categoryAttribute) {
+ applyValuesToDomain(values, categoryAttribute)
+ if (FeatureToggles.OPT_LOCKING_FEATURE_TOGGLE) {
+ checkForStaleObject(categoryAttribute, 'CategoryAttribute', values.version ?: FORCED_OPT_LOCKING_VERSION)
+ }
+ validateAllowMultipleValue(categoryAttribute)
+ saveOrDiscardDomain(categoryAttribute, username)
+ }
+ categoryAttribute
+ }
+
+ /**
+ * Updates a CategoryAttribute.
+ *
+ * @param values - must contain a version entry
+ * @param criteria - catalogInstanceId, categoryId and attributeNameId
+ * @param username
+ *
+ * @return CategoryAttribute which may contain errors or be null
+ */
+ CategoryAttributeLocale updateCategoryAttributeWithLocale(Map values, Map criteria, String username) {
+ Map data = categoryAttributeService.findCategoryAttributeLocaleByCompositKey(criteria)
+ CategoryAttributeLocale categoryAttributeLocale
+ if (data) {
+ categoryAttributeLocale = CategoryAttributeLocale.findByCategoryAttributeIdAndLocale(data.categoryAttributeId, values.locale)
+ if (!categoryAttributeLocale) {
+ categoryAttributeLocale = new CategoryAttributeLocale([categoryAttributeId: data.categoryAttributeId])
+ }
+ } else {
+ return
+ }
+ applyValuesToDomain(values, categoryAttributeLocale)
+ categoryAttributeLocale.validate()
+ saveOrDiscardDomain(categoryAttributeLocale, username)
+ }
+
+ /**
+ * Soft deletes a CategoryAttribute.
+ *
+ * @param values - must contain a version entry
+ * @param criteria - catalogInstanceId, categoryId and attributeNameId
+ * @param username
+ * @param version - the client's current version
+ *
+ * @return CategoryAttribute which may contain errors or be null
+ */
+ CategoryAttribute deleteCategoryAttribute(Map criteria, String username, Integer version) {
+ CategoryAttribute categoryAttribute = findCategoryAttribute(criteria)
+ if (categoryAttribute) {
+ if (FeatureToggles.OPT_LOCKING_FEATURE_TOGGLE) {
+ checkForStaleObject(categoryAttribute, 'CategoryAttribute', version ?: FORCED_OPT_LOCKING_VERSION)
+ }
+ if (partAttributesOfCategoryExists(categoryAttribute)) {
+ categoryAttribute.errors.reject('categoryAttribute.partsWithAttributeName.exists', 'Parts with attributeName exists')
+ }
+ softDelete(categoryAttribute, username)
+ }
+ categoryAttribute
+ }
+
+ private boolean partAttributesOfCategoryExists(CategoryAttribute categoryAttribute) {
+ sql().rows('''SELECT * FROM vwPartAttribute WHERE attributeNameId = :attributeNameId AND
+partNumber IN (SELECT partNumber FROM vwPart WHERE categoryId = :categoryId)''',
+ [categoryId: categoryAttribute.categoryId, attributeNameId: categoryAttribute.attributeNameId])
+ }
+
+ private CategoryAttribute findCategoryAttribute(Map criteria) {
+ Map results = categoryAttributeService.findCategoryAttributeByCompositKey(criteria)
+ CategoryAttribute categoryAttribute
+ if (results) {
+ categoryAttribute = findDomainObjectById(CategoryAttribute, results.categoryAttributeId)
+ }
+ categoryAttribute
+ }
+
+ /**
+ * Sequence CategoryAttribute
+ *
+ * @param values - targetAttributeNameId, attributeNameId(source), position, catalogInstanceId, categoryId
+ * @param username
+ * @return sourceCategoryAttribute
+ */
+ CategoryAttribute moveCategoryAttribute(Map values, String username) {
+ CategoryAttribute sourceCategoryAttribute = findCategoryAttribute(values)
+ values.attributeNameId = values.targetId
+ CategoryAttribute targetCategoryAttribute = findCategoryAttribute(values)
+ if (sourceCategoryAttribute && targetCategoryAttribute) {
+ if (FeatureToggles.OPT_LOCKING_FEATURE_TOGGLE) {
+ checkForStaleObject(CategoryAttribute, 'CategoryAttribute', values.sourceVersion ?: FORCED_OPT_LOCKING_VERSION)
+ }
+ values.id = sourceCategoryAttribute.id
+ values.targetId = targetCategoryAttribute.id
+ sequence(values, username)
+ sourceCategoryAttribute
+ }
+ }
+
+ /**
+ * Sequences a CategoryAttribute
+ * @param categoryAttribute
+ */
+ void sequence(Map values, String username) {
+ sql().execute(values + [appSecurityUser: username], SEQUENCE_SQL)
+ }
+
+ /**
+ * validate allowMultipleValue only if it is modified and false
+ */
+ void validateAllowMultipleValue(CategoryAttribute categoryAttribute) {
+ if (!categoryAttribute.allowMultipleValues && categoryAttribute.isDirty('allowMultipleValues')) {
+ if (hasMultipleValueAssociation(categoryAttribute.id)) {
+ categoryAttribute.errors.rejectValue('allowMultipleValues', 'categoryAttribute.allowMultipleValues.invalid')
+ }
+ }
+ }
+
+ /**
+ * return true if CategoryAttribute has multiple values assigned to any of the parts assigned to a category
+ * @param categoryAttributeId
+ */
+ boolean hasMultipleValueAssociation(Integer categoryAttributeId) {
+ boolean flag = false
+ if (categoryAttributeId) {
+ Map results = sql().rows(CATEGORY_ATTRIBUTE_MULTIPLE_VALUE, categoryAttributeId)[0]
+ flag = results[(0)]
+ }
+ flag
+ }
+}
Index: grails-app/services/com/lemans/ds/category/CategoryAttributeService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/category/CategoryAttributeService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/category/CategoryAttributeService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,70 @@
+package com.lemans.ds.category
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional(readOnly = true)
+class CategoryAttributeService extends LemansService {
+
+ def categoryService
+
+
+ /**
+ * Find a categoryAttribute by categoryAttributeId.
+ *
+ * @param criteria containing categoryAttributeId
+ *
+ * @return categoryAttribute
+ */
+ Map findCategoryAttributeById(Map criteria) {
+ List clauses = ['categoryAttributeId = :categoryAttributeId']
+ dqx(criteria).executeOneFromLocale('dbo.vwCategoryAttribute', clauses)?.results[0]
+ }
+
+ /**
+ * Find a CategoryAttribute by catalogId, categoryId and attributeId.
+ *
+ * @param criteria containing catalogInstanceId ,categoryId and attributeId
+ *
+ * @return CategoryAttribute
+ */
+ Map findCategoryAttributeByCompositKey(Map criteria) {
+ List clauses = ['catalogInstanceId = :catalogInstanceId', 'categoryId = :categoryId', 'attributeNameId = :attributeNameId']
+ dqx(criteria).executeOneFromLocale('dbo.vwCategoryAttribute', clauses)?.results[0]
+ }
+
+ /**
+ * Find a CategoryAttribute by catalogId, categoryId and attributeId.
+ *
+ * @param criteria containing catalogInstanceId ,categoryId and attributeId
+ *
+ * @return CategoryAttribute
+ */
+ Map findCategoryAttributeLocaleByCompositKey(Map criteria) {
+ List clauses = [
+ 'catalogInstanceId = :catalogInstanceId',
+ 'categoryId = :categoryId',
+ 'attributeNameId = :attributeNameId'
+ ]
+ dqx(criteria).executeOneFrom('dbo.vwCategoryAttributeLocale', clauses)?.results[0]
+ }
+
+ /**
+ * Find a CategoryAttributes by catalogId and categoryId.
+ *
+ * @param criteria containing catalogInstanceId and categoryId
+ *
+ * @return CategoryAttributes
+ */
+ Map findCategoryAttributeByCategory(Map criteria) {
+ List clauses = ['catalogInstanceId = :catalogInstanceId', 'categoryId = :categoryId']
+ criteria.sorting = criteria.sorting ?: 'sequence+ASC'
+ Map results = dqx(criteria).executeFromLocale('dbo.vwCategoryAttribute', clauses)
+ if (results.results.isEmpty()) { //check if catgoryInstanceId and category exist.
+ Map categoryCriteria = [catalogInstanceId: criteria.catalogInstanceId, id: criteria.categoryId]
+ Map categoryMap = categoryService.findCategoryById(categoryCriteria)
+ return categoryMap ? results : categoryMap
+ }
+ results
+ }
+}
Index: grails-app/services/com/lemans/ds/category/CategoryAttributeValueManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/category/CategoryAttributeValueManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/category/CategoryAttributeValueManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,147 @@
+package com.lemans.ds.category
+
+import com.lemans.FeatureToggles
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class CategoryAttributeValueManagerService extends LemansManager {
+
+ def categoryAttributeValueService
+ def categoryAttributeService
+
+ private static final int FORCED_OPT_LOCKING_VERSION = -1
+
+ private static final String SEQUENCE_SQL =
+ '''EXEC dbo.spMoveCategoryAttributeValue
+ @sourceId = :sourceId,
+ @targetId = :targetId,
+ @sourceVersion = :sourceVersion,
+ @targetVersion = :targetVersion,
+ @position = :position,
+ @appSecurityUser = :appSecurityUser
+ '''
+
+ /**
+ * Creates a CategoryAttributeValue.
+ *
+ * @param values
+ * @param username
+ *
+ * @return CategoryAttributeValue which may contain errors or be null
+ */
+ CategoryAttributeValue createCategoryAttributeValue(Map values, String username) {
+ CategoryAttributeValue categoryAttributeValue = new CategoryAttributeValue(values)
+ Map results = categoryAttributeService.findCategoryAttributeByCompositKey(values)
+ if (!results || results.isEmpty()) {
+ categoryAttributeValue.errors.reject('invalid', 'Catalog -> Category -> attributeName relation not found')
+ } else {
+ categoryAttributeValue.categoryAttributeId = results.categoryAttributeId
+ categoryAttributeValue.validate()
+ }
+ saveOrDiscardDomain(categoryAttributeValue, username)
+ }
+
+ /**
+ * Updates a CategoryAttributeValue.
+ *
+ * @param values - must contain a version entry
+ * @param criteria - catalogInstanceId, categoryId and attributeNameId
+ * @param username
+ * @return CategoryAttribute which may contain errors or be null
+ */
+ CategoryAttributeValue updateCategoryAttributeValue(Map values, String username) {
+ CategoryAttributeValue categoryAttributeValue = findDomainObjectById(CategoryAttributeValue, values.attributeValueId)
+ if (categoryAttributeValue) {
+ applyValuesToDomain(values, categoryAttributeValue)
+ categoryAttributeValue.validate()
+ saveOrDiscardDomain(categoryAttributeValue, username)
+ }
+ categoryAttributeValue
+ }
+
+ /**
+ * Updates a CategoryAttributeValue.
+ *
+ * @param values - must contain a version entry
+ * @param criteria - catalogInstanceId, categoryId and attributeNameId
+ * @param username
+ * @return CategoryAttribute which may contain errors or be null
+ */
+ CategoryAttributeValueLocale updateCategoryAttributeValueWithLocale(Map values, String username) {
+ Map data = categoryAttributeValueService.findCategoryAttributeValueWithLocaleByByCompositKey(values)
+ if (data) {
+ CategoryAttributeValueLocale categoryAttributeValueLocale =
+ categroyAttributeLocale(data, values) ?: new CategoryAttributeValueLocale([categoryAttributeValueId: data.categoryAttributeValueId])
+ applyValuesToDomain(values, categoryAttributeValueLocale)
+ categoryAttributeValueLocale.validate()
+ saveOrDiscardDomain(categoryAttributeValueLocale, username)
+ }
+ }
+
+ private CategoryAttributeValueLocale categroyAttributeLocale(Map data, Map values) {
+ CategoryAttributeValueLocale.findByCategoryAttributeValueIdAndLocale(data.categoryAttributeValueId, values.locale)
+ }
+
+ /**
+ * Soft deletes a CategoryAttributeValue.
+ *
+ * @param values - must contain a version entry
+ * @param criteria - catalogInstanceId, CategoryId, AttributeNameId and attributeValueId
+ * @param username
+ * @param version - the client's current version
+ *
+ * @return CategoryAttributeValue which may contain errors or be null
+ */
+ CategoryAttributeValue deleteCategoryAttributeValue(Map criteria, String username, Integer version) {
+ CategoryAttributeValue categoryAttributeValue = findCategoryAttributeValue(criteria)
+ if (categoryAttributeValue) {
+ if (FeatureToggles.OPT_LOCKING_FEATURE_TOGGLE) {
+ checkForStaleObject(categoryAttributeValue, 'CategoryAttributeValue', version ?: FORCED_OPT_LOCKING_VERSION)
+ }
+ softDelete(categoryAttributeValue, username)
+ }
+ categoryAttributeValue
+ }
+
+ /**
+ * Finds categoryAttributeValue by catalogInstanceId, CategoryId, AttributeNameId and attributeValueId
+ * @param criteria has catalogInstanceId, CategoryId, AttributeNameId and attributeValueId
+ * @return categoryAttributeValue
+ */
+ private CategoryAttributeValue findCategoryAttributeValue(Map criteria) {
+ Map results = categoryAttributeValueService.findCategoryAttributeValueByCompositKey(criteria)
+ CategoryAttributeValue categoryAttributeValue
+ if (results) {
+ categoryAttributeValue = CategoryAttributeValue.findByIdAndDateDeletedIsNull(results.categoryAttributeValueId)
+ }
+ categoryAttributeValue
+ }
+
+ /**
+ * Sequence CategoryAttributeValue
+ *
+ * @param values - targetAttributeValueId, attributeValueId(source), position, catalogInstanceId, categoryId, attributeNameId
+ * @param username
+ * @return sourceCategoryAttributeValue
+ */
+ Map moveCategoryAttributeValue(Map values, String username) {
+ Map sourceCategoryAttributeValue = categoryAttributeValueService.findCategoryAttributeValueByCompositKey(values)
+ values.attributeValueId = values.targetId
+ Map targetCategoryAttributeValue = categoryAttributeValueService.findCategoryAttributeValueByCompositKey(values)
+ if (sourceCategoryAttributeValue && targetCategoryAttributeValue) {
+ values.sourceId = sourceCategoryAttributeValue.categoryAttributeValueId
+ values.targetId = targetCategoryAttributeValue.categoryAttributeValueId
+ sequence(values, username)
+ sourceCategoryAttributeValue
+ }
+ }
+
+ /**
+ * Sequences a CategoryAttributeValue
+ * @param categoryAttributeValue
+ */
+ void sequence(Map values, String username) {
+ sql().execute(values + [appSecurityUser: username], SEQUENCE_SQL)
+ }
+}
Index: grails-app/services/com/lemans/ds/category/CategoryAttributeValueService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/category/CategoryAttributeValueService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/category/CategoryAttributeValueService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,70 @@
+package com.lemans.ds.category
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional
+class CategoryAttributeValueService extends LemansService {
+
+ def categoryAttributeService
+
+
+ /**
+ * Find a categoryAttributeValue by categoryAttributeValueId.
+ *
+ * @param criteria containing categoryAttributeValueId
+ *
+ * @return categoryAttributeValue
+ */
+ Map findCategoryAttributeValueById(Map criteria) {
+ List clauses = ['categoryAttributeValueId = :categoryAttributeValueId']
+ dqx(criteria).executeOneFromLocale('dbo.vwCategoryAttributeValue', clauses)?.results[0]
+ }
+
+ /**
+ * Find a CategoryAttributeValue by catalogId, categoryId, attributeNameId and attributeValueId.
+ *
+ * @param criteria containing catalogInstanceId ,categoryId, attributeNameId and attributeValueId
+ *
+ * @return CategoryAttributeValue
+ */
+ Map findCategoryAttributeValueByCompositKey(Map criteria) {
+ List clauses = ['catalogInstanceId = :catalogInstanceId', 'categoryId = :categoryId', 'attributeNameId = :attributeNameId',
+ 'attributeValueId = :attributeValueId']
+ dqx(criteria).executeOneFromLocale('dbo.vwCategoryAttributeValue', clauses)?.results[0]
+ }
+
+ /**
+ * Find a attribute by attributeValueId.
+ *
+ * @param criteria containing attributeValueId
+ *
+ * @return attribute
+ */
+ def findCategoryAttributeValueWithLocaleByByCompositKey(Map criteria) {
+ List clauses = [
+ 'categoryId = :categoryId',
+ 'attributeNameId = :attributeNameId',
+ 'attributeValueId = :attributeValueId',
+ 'locale = :locale'
+ ]
+ dqx(criteria).executeOneFrom('dbo.vwCategoryAttributeValueLocale', clauses)?.results[0]
+ }
+
+ /**
+ * Find a CategoryAttributeValues by catalogId, categoryId and attributeNameId.
+ *
+ * @param criteria containing catalogInstanceId, categoryId and attributeNameId
+ *
+ * @return CategoryAttributeValues
+ */
+ Map findCategoryAttributeValueByCriteria(Map criteria) {
+ List clauses = ['catalogInstanceId = :catalogInstanceId', 'categoryId = :categoryId', 'attributeNameId = :attributeNameId']
+ Map results = dqx(criteria).executeFromLocale('dbo.vwCategoryAttributeValue', clauses)
+ if (results.results.isEmpty()) { //check if catgoryInstanceId and category exist.
+ Map categoryAttribute = categoryAttributeService.findCategoryAttributeByCompositKey(criteria)
+ return categoryAttribute ? results : categoryAttribute
+ }
+ results
+ }
+}
Index: grails-app/services/com/lemans/ds/category/CategoryManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/category/CategoryManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/category/CategoryManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,188 @@
+package com.lemans.ds.category
+
+import com.lemans.FeatureToggles
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class CategoryManagerService extends LemansManager {
+
+ def categoryService
+
+ private static final String SEQUENCE_SQL = 'EXEC dbo.spCategoryUpdateSequence @categoryId = ?'
+
+ private static final int FORCED_OPT_LOCKING_VERSION = -1
+
+ /**
+ * Creates a Category.
+ *
+ * @param values
+ * @param username
+ *
+ * @return Category which may contain errors or be null
+ */
+ Category createCategory(Map values, String username) {
+ Category category = new Category(values)
+ if (category.parentCategoryId) {
+ Map parent = categoryService.findCategoryTreeInfoById(
+ catalogInstanceId: category.catalogInstanceId, id: category.parentCategoryId
+ )
+ if (!parent) { return null }
+
+ if (parent.level >= Category.MAX_DEPTH) { rejectDepth(category) }
+ }
+ if (!category.hasErrors()) {
+ category.sequence = Category.MAX_DIRECT_CHILDREN
+ saveOrDiscardDomain(category, username)
+ }
+ category
+ }
+
+ /**
+ * Updates a Category.
+ *
+ * @param values - must contain a version entry
+ * @param catalogInstanceId
+ * @param id
+ * @param username
+ *
+ * @return Category which may contain errors or be null
+ */
+ Category updateCategory(Map values, Integer catalogInstanceId, Integer id, String username) {
+ Category category = findCategory(catalogInstanceId, id)
+ if (category) {
+ applyValuesToDomain(values, category)
+ if (FeatureToggles.OPT_LOCKING_FEATURE_TOGGLE) {
+ checkForStaleObject(category, 'Category', values.version ?: FORCED_OPT_LOCKING_VERSION)
+ }
+ saveOrDiscardDomain(category, username)
+ }
+ category
+ }
+
+ /**
+ * Moves a Category relative to another Category for the same Category.
+ *
+ * @param values - must contain the shared catalogInstanceId,
+ * an id (source), A targetId (destination), and A position (BEFORE, AFTER, or INSIDE)
+ * @param username
+ *
+ * @return sourceCategory
+ */
+ Category moveCategory(Map values, String username) {
+ Integer catalogInstanceId = values.catalogInstanceId
+ Category category = findCategory(catalogInstanceId, values.id)
+ if (category) {
+ if (FeatureToggles.OPT_LOCKING_FEATURE_TOGGLE) {
+ checkForStaleObject(category, 'Category', values.version ?: FORCED_OPT_LOCKING_VERSION)
+ }
+ Map source = categoryService.findCategoryTreeInfoById(catalogInstanceId: catalogInstanceId, id: values.id)
+ Map target = categoryService.findCategoryTreeInfoById(catalogInstanceId: catalogInstanceId, id: values.targetId)
+ if (!source || !target) { return null }
+
+ checkTreeConstraints(category, source, target)
+ if (values.position == 'INSIDE') {
+ if (!category.hasErrors()) {
+ category.parentCategoryId = target.categoryId
+ category.sequence = 1
+ }
+ }
+ else {
+ if (!category.hasErrors()) {
+ category.parentCategoryId = target.p1categoryId
+ category.sequence = values.position == 'AFTER' ? target.sequence + 1 : target.sequence
+ }
+ }
+ saveOrDiscardDomain(category, username)
+ category
+ } else { null }
+ }
+
+ private void checkTreeConstraints(Category category, source, target) {
+ if (target.level + source.depth > Category.MAX_DEPTH) { rejectDepth(category) }
+ Set parentIds = [target.p1categoryId, target.p2categoryId, target.p3categoryId]
+ if (category.id in parentIds) { rejectCycle(category) }
+ }
+
+ /**
+ * Soft deletes a Category.
+ *
+ * @param catalogInstanceId
+ * @param id
+ * @param username
+ * @param version - the client's current version
+ *
+ * @return Category which may contain errors or be null
+ */
+ Category deleteCategory(Integer catalogInstanceId, Integer id, String username, Integer version) {
+ Category category = findCategory(catalogInstanceId, id)
+ if (category) {
+ if (FeatureToggles.OPT_LOCKING_FEATURE_TOGGLE) {
+ checkForStaleObject(category, 'Category', version ?: FORCED_OPT_LOCKING_VERSION)
+ }
+ if (children(category)) {
+ category.errors.reject('undeletable', ['Category'] as Object[], 'Category may not be deleted')
+ } else {
+ softDelete(category, username)
+ }
+ }
+ category
+ }
+
+ /**
+ * Finds all non-deleted categories for a catalog.
+ *
+ * @param catalogInstanceId
+ *
+ * @return List of categories
+ */
+ List findCategoriesForCatalog(Integer catalogInstanceId) {
+ Category.findAllByCatalogInstanceIdAndDateDeletedIsNull(catalogInstanceId)
+ }
+
+ /**
+ * Sequences a Category to respect the parentCategoryId and sequence properties.
+ *
+ * @param category
+ */
+ void sequence(Category category) {
+ if (!category.hasErrors()) {
+ sql().execute(SEQUENCE_SQL, [category.id])
+ }
+ }
+
+ private rejectDepth(Category category) {
+ category.errors.reject('maxDepth.exceeded', ['Category'] as Object[], "Category depth of $Category.MAX_DEPTH may not be exceeded")
+ }
+
+ private rejectCycle(Category category) {
+ category.errors.reject('cyclic', ['Category'] as Object[], 'Category may not be moved to create a cycle')
+ }
+
+ private List children(Category category) {
+ Category.findAllByParentCategoryIdAndDateDeletedIsNull(category.id)
+ }
+
+ private Category findCategory(Integer catalogInstanceId, Integer categoryId) {
+ Category.findByCatalogInstanceIdAndIdAndDateDeletedIsNull(catalogInstanceId, categoryId)
+ }
+
+ CategoryLocale updateCategoryLocale(Map values, Integer catalogInstanceId, Integer categoryId, String username) {
+ CategoryLocale categoryLocale = findCategoryLocale(categoryId, values.locale)
+ if (!categoryLocale) {
+ Category category = findCategory(catalogInstanceId, categoryId)
+ if (category) {
+ categoryLocale = new CategoryLocale([categoryId: category.id])
+ } else {
+ return
+ }
+ }
+ applyValuesToDomain(values, categoryLocale)
+ categoryLocale.validate()
+ saveOrDiscardDomain(categoryLocale, username)
+ }
+
+ private CategoryLocale findCategoryLocale(Integer categoryId, String locale) {
+ CategoryLocale.findByCategoryIdAndLocaleAndDateDeletedIsNull(categoryId, locale)
+ }
+}
Index: grails-app/services/com/lemans/ds/category/CategoryService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/category/CategoryService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/category/CategoryService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,82 @@
+package com.lemans.ds.category
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional(readOnly = true)
+class CategoryService extends LemansService {
+
+ static final List TREE_COLUMNS = ['categoryName', 'categoryId', 'parentCategoryId', 'sequence', 'version'].asImmutable()
+
+ static final List DROPDOWN_COLUMNS = ['categoryId', 'qualifiedCategoryName'].asImmutable()
+
+ private static final Map COLUMN_SETS = [_tree: TREE_COLUMNS, _dropdown: DROPDOWN_COLUMNS].asImmutable()
+
+ private static final String CATEGORY_DETAILS_SQL = 'SELECT dbo.fnGetCategoryDetailsJSON(?)'
+
+ private static final String CATEGORY_DETAILS_LOCALE_SQL = 'SELECT dbo.fnGetCategoryDetailsJSONLocale(?,?)'
+
+
+ /**
+ * Find Categories for a Catalog.
+ *
+ * @param criteria containing catalogInstanceId
+ *
+ * @return Categories data
+ */
+ List findCategories(Map criteria) {
+ List clauses = ['catalogInstanceId = :catalogInstanceId']
+ if (criteria.categoryId) {
+ List categoryIds = criteria.categoryId.split(',')
+ clauses << "categoryId IN (${categoryIds.join(',')})"
+ }
+ dqx(criteria).executeFrom('dbo.vwCategory', clauses, COLUMN_SETS).results
+ }
+
+ /**
+ * Find a Category by id.
+ *
+ * @param criteria containing catalogInstanceId and id (categoryId)
+ *
+ * @return Category data
+ */
+ Map findCategoryById(Map criteria) {
+ List clauses = ['catalogInstanceId = :catalogInstanceId', 'categoryId = :id']
+ dqx(criteria).executeOneFromLocale('dbo.vwCategory', clauses)?.results[0]
+ }
+
+ boolean categoryExists(Map criteria) {
+ sql().firstRow(criteria, 'SELECT * FROM dbo.vwCategory WHERE catalogInstanceId = :catalogInstanceId AND categoryId = :categoryId')
+ }
+
+ /**
+ * Find Category hierarchy information by id.
+ *
+ * @param criteria containing catalogInstanceId and categoryId
+ *
+ * @return Category data
+ */
+ Map findCategoryTreeInfoById(Map criteria) {
+ List clauses = ['catalogInstanceId = :catalogInstanceId', 'categoryId = :id']
+ dqx(criteria).executeOneFrom('dbo.vwCategoryLevel', clauses)?.results[0]
+ }
+
+ /**
+ * Find Category details information by id.
+ *
+ * @param criteria containing catalogInstanceId and categoryId
+ *
+ * @return Category details with attribute and attributeValue
+ */
+ String categoryDetails(Map criteria) {
+ if (findCategoryById(criteria)) {
+ if (criteria.locale == Locale.default.toString()) {
+ queryForXmlClob(CATEGORY_DETAILS_SQL, [criteria.id])
+ } else {
+ queryForXmlClob(CATEGORY_DETAILS_LOCALE_SQL, [criteria.id, criteria.locale])
+ }
+ } else {
+ null
+ }
+ }
+}
Index: grails-app/services/com/lemans/ds/category/CategorySubComCodeManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/category/CategorySubComCodeManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/category/CategorySubComCodeManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,47 @@
+package com.lemans.ds.category
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class CategorySubComCodeManagerService extends LemansManager {
+
+ def categoryService
+
+ /**
+ * Adds a SubComCode mapping to a Category.
+ *
+ * @param values Map containing catalogInstanceId, categoryId, and subComCodeId
+ * @param username
+ *
+ * @return CategorySubComCode mapping which may be null or have errors
+ */
+ CategorySubComCode addSubComCode(Map values, String username) {
+ Integer categoryId = values.categoryId
+ Map category = categoryService.findCategoryById([catalogInstanceId: values.catalogInstanceId, id: categoryId])
+ CategorySubComCode categorySubComCode = new CategorySubComCode(categoryId: categoryId, subComCodeId: values.subComCodeId)
+ if (!category) {
+ categorySubComCode.errors.reject('invalid', 'Catalog or Category not found')
+ }
+ saveOrDiscardDomain(categorySubComCode, username)
+ }
+
+ /**
+ * Removes a SubComCode mapping from a Category.
+ *
+ * @param values Map containing catalogInstanceId and categoryId
+ * @param subComCodeId
+ * @param username
+ *
+ * @return CategorySubComCode mapping which may be null or have errors
+ */
+ CategorySubComCode removeSubComCode(Map values, Integer subComCodeId, String username) {
+ Map category = categoryService.findCategoryById([catalogInstanceId: values.catalogInstanceId, id: values.categoryId])
+ CategorySubComCode subComCode
+ if (category) {
+ subComCode = CategorySubComCode.findByCategoryIdAndSubComCodeIdAndDateDeletedIsNull(category.categoryId, subComCodeId)
+ if (subComCode) { softDelete(subComCode, username) }
+ }
+ subComCode
+ }
+}
Index: grails-app/services/com/lemans/ds/category/CategorySubComCodeService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/category/CategorySubComCodeService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/category/CategorySubComCodeService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,33 @@
+package com.lemans.ds.category
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional(readOnly = true)
+class CategorySubComCodeService extends LemansService {
+
+ /**
+ * Finds a CategorySubComCode by categoryId and subComCodeId.
+ *
+ * @param criteria Map containing catalogInstanceId, categoryId, and subComCodeId
+ *
+ * @return Map containing CategorySubComCode data
+ */
+ Map findCategorySubComCode(Map criteria) {
+ List clauses = categoryClauses() + 'subComCodeId = :subComCodeId'
+ dqx(criteria).executeOneFrom('dbo.vwCategorySubComCode', clauses)?.results[0]
+ }
+
+ /**
+ * Finds all CategorySubComCodes for a Category.
+ *
+ * @param criteria Map containing catalogInstanceId and categoryId
+ *
+ * @return List containing CategorySubComCode data
+ */
+ List findCategorySubComCodes(Map criteria) {
+ dqx(criteria).executeFrom('dbo.vwCategorySubComCode', categoryClauses()).results
+ }
+
+ private List categoryClauses() { ['catalogInstanceId = :catalogInstanceId', 'categoryId = :categoryId'] }
+}
Index: grails-app/services/com/lemans/ds/explosion/ExplosionDiagramManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/explosion/ExplosionDiagramManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/explosion/ExplosionDiagramManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,56 @@
+package com.lemans.ds.explosion
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class ExplosionDiagramManagerService extends LemansManager {
+
+ /**
+ * Add an explosion diagram
+ * @param input
+ * @param username
+ * @return Explosion diagram object
+ */
+ ExplosionDiagram addExplosionDiagram(Map input, String username) {
+ ExplosionDiagram explosionDiagram = new ExplosionDiagram()
+ applyValuesToDomain(input, explosionDiagram)
+ explosionDiagram.validate()
+ saveOrDiscardDomain(explosionDiagram, username)
+ }
+
+ /**
+ * Update an explosion diagram
+ * @param input
+ * @param diagramId
+ * @param username
+ * @return Explosion diagram object
+ */
+ ExplosionDiagram updateExplosionDiagram(Map input, Integer diagramId, String username) {
+ ExplosionDiagram explosionDiagram = findExplosionDiagram(diagramId)
+ if (explosionDiagram) {
+ applyValuesToDomain(input, explosionDiagram)
+ explosionDiagram.validate()
+ saveOrDiscardDomain(explosionDiagram, username)
+ }
+ explosionDiagram
+ }
+
+ /**
+ * Delete an explosion diagram
+ * @param diagramId
+ * @param username
+ * @return soft deleted explosion diagram object
+ */
+ ExplosionDiagram deleteExplosionDiagram(Integer diagramId, String username) {
+ ExplosionDiagram explosionDiagram = findExplosionDiagram(diagramId)
+ if (explosionDiagram) {
+ softDelete(explosionDiagram, username)
+ }
+ explosionDiagram
+ }
+
+ private ExplosionDiagram findExplosionDiagram(Integer diagramId) {
+ ExplosionDiagram.findByIdAndDateDeletedIsNull(diagramId)
+ }
+}
Index: grails-app/services/com/lemans/ds/explosion/ExplosionDiagramService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/explosion/ExplosionDiagramService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/explosion/ExplosionDiagramService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,44 @@
+package com.lemans.ds.explosion
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional
+class ExplosionDiagramService extends LemansService {
+
+ /**
+ * Find all explosion diagrams
+ * @param criteria
+ * @return map of explosionDiagrams
+ */
+ Map findExplosionDiagrams(Map criteria) {
+ Map data = dqx(criteria).executeFrom('vwExplosionDiagram')
+ if (data) { [results: transformClobToText(data.results), totalRecords: data.totalRecords] }
+ }
+
+ /**
+ * find an explosion diagram
+ * @param explosionDiagramId
+ * @return map of explosion diagram
+ */
+ Map findExplosionDiagram(Integer explosionDiagramId) {
+ Map data = dqx([explosionDiagramId: explosionDiagramId]).executeOneFrom(
+ 'vwExplosionDiagram', ['explosionDiagramId = :explosionDiagramId'])?.results[0]
+ if (data) { transformClobToText(data) }
+ }
+
+ private Map transformClobToText(Map data) {
+ data.collectEntries { k, v ->
+ if (v?.class?.simpleName == 'ClobImpl') { [k, v.characterStream.text] }
+ else { [k, v] }
+ }
+ }
+
+ private List transformClobToText(List data) {
+ List results = []
+ data.each { Map it ->
+ results << transformClobToText(it)
+ }
+ results
+ }
+}
Index: grails-app/services/com/lemans/ds/fitment/PartFitmentManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/fitment/PartFitmentManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/fitment/PartFitmentManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,128 @@
+package com.lemans.ds.fitment
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+import java.sql.Timestamp
+import java.util.stream.Collectors
+
+/**
+ * Created by vramisetti on 6/12/2017.
+ */
+@Transactional
+class PartFitmentManagerService extends LemansManager {
+
+ def partService
+
+ def partFitmentService
+
+ def modelYearService
+
+ /**
+ * Create part fitments
+ *
+ * @param values
+ * @param username
+ *
+ * @return Map containing errors
+ */
+ Map createFitments(Map values, String username) {
+ List errors = []
+ String partNumber = values.partNumber
+ List modelYearIds = ((List) values.modelYearIds).parallelStream().distinct().collect(Collectors.toList())
+ List validModelYears = modelYearService.findValidModelYearIds(modelYearIds)?.modelYearId
+ List invalidModelYearIds = modelYearIds.parallelStream().filter { s -> !validModelYears.contains(s) }.collect(Collectors.toList())
+ if (invalidModelYearIds) { errors << "Invalid model year's found $invalidModelYearIds" }
+ if (!partService.partWithPartNumberExists(partNumber)) { errors << 'Invalid part' }
+ if (!errors) {
+ List modelYearIdsToBeCreated = modelIdsToBeCreated(partNumber, modelYearIds)
+ Timestamp now = new Timestamp(new Date().time)
+ sql().withBatch(50, FITMENT_INSERT_SQL) { stmt ->
+ modelYearIdsToBeCreated.each { Integer modelYearId ->
+ stmt.addBatch([modelYearId: modelYearId, partNumber: partNumber, now: now, username: username])
+ }
+ }
+ }
+ [errors: errors]
+ }
+
+ private List modelIdsToBeCreated(String partNumber, List modelYearIds) {
+ List existingModelYearIds = partFitmentService.findFitmentsByPartNumberAndModelYearIds(partNumber,
+ modelYearIds, 'modelYearId')*.modelYearId
+ modelYearIds.parallelStream()
+ .filter { s -> !existingModelYearIds.contains(s) }
+ .collect(Collectors.toList())
+ }
+
+ /**
+ * Delete partFitment
+ *
+ * @param partNumber
+ * @param fitmentId
+ * @param username
+ *
+ * @return Map containing errors
+ */
+ Map deleteFitment(String partNumber, Integer fitmentId, String username) {
+ List partFitment = partFitmentService.findFitmentsByPartNumberAndPartFitmentIds(partNumber, [fitmentId])
+ deleteFitments(partFitment ?: [], username)
+ }
+
+ /**
+ * Delete partFitments
+ *
+ * @param values Map containing partNumber and partFitmentIds
+ * @param username
+ *
+ * @return Map containing errors
+ */
+ Map deleteMultipleFitments(Map values, String username) {
+ List partFitment = partFitmentService.findFitmentsByPartNumberAndPartFitmentIds(values.partNumber, values.partFitmentIds)
+ deleteFitments(partFitment ?: [], username)
+ }
+
+ /**
+ * Create Fitment position
+ *
+ * @param positions
+ * @param partFitmentIds
+ * @param username
+ */
+ void assignFitmentPositions(List positions, List partFitmentIds, String username) {
+ Timestamp now = new Timestamp(new Date().time)
+ positions.each { Integer position ->
+ sql().withBatch(50, FITMENT_POSITION_INSERT_SQL) { stmt ->
+ partFitmentIds.each { Integer fitmentId ->
+ stmt.addBatch([partFitmentId: fitmentId, positionTypeId: position, now: now, username: username])
+ }
+ }
+ }
+ }
+
+ private Map deleteFitments(List partFitments, String username) {
+ List partFitmentIds = partFitments*.partFitmentId
+ if (partFitments) {
+ sql().executeUpdate(deleteFitmentsSql(partFitmentIds), [new Timestamp(new Date().time), username])
+ sql().executeUpdate(deleteFitmentPositionSql(partFitmentIds), [new Timestamp(new Date().time), username])
+ }
+ [errors: []]
+ }
+
+ private static final String FITMENT_INSERT_SQL = '''
+INSERT INTO PartFitment (modelYearId, partNumber, note, dateCreated, createdBy, lastUpdated, lastUpdatedBy)
+VALUES (:modelYearId, :partNumber, :note, :now, :username, :now, :username)
+'''
+
+ private static final String FITMENT_POSITION_INSERT_SQL = '''
+INSERT INTO PartFitmentPosition (partFitmentId, positionTypeId, dateCreated, createdBy, lastUpdated, lastUpdatedBy)
+values (:partFitmentId, :positionTypeId, :now, :username, :now, :username)
+'''
+
+ private String deleteFitmentPositionSql(List partFitmentIds) {
+ "UPDATE PartFitmentPosition SET dateDeleted = ?, deletedBy = ? WHERE partFitmentId IN (${partFitmentIds.join(', ')})"
+ }
+
+ private String deleteFitmentsSql(List partFitmentIds) {
+ "UPDATE PartFitment SET dateDeleted = ?, deletedBy = ? WHERE partFitmentId IN (${partFitmentIds.join(', ')})"
+ }
+}
Index: grails-app/services/com/lemans/ds/fitment/PartFitmentService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/fitment/PartFitmentService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/fitment/PartFitmentService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,54 @@
+package com.lemans.ds.fitment
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+/**
+ * Created by vramisetti on 6/13/2017.
+ */
+@Transactional(readOnly = true)
+class PartFitmentService extends LemansManager {
+
+ private static final String FIND_MODEL_SQL = 'EXEC dbo.spGetPartFitment @partNumber = ?, @offset = ?, @pageSize = ?, @sortBy = ?'
+
+ /**
+ * Find partFitment details by partNumber.
+ *
+ * @param criteria containing partNumber and pagination parameters
+ *
+ * @return partFitment details
+ */
+ Map findFitmentsByPart(Map criteria) {
+ List results = sql().callWithAllRows(FIND_MODEL_SQL,
+ [criteria.partNumber, criteria.offset, criteria.pageSize, criteria.sorting]) { }
+ [results: results[0][0][0]?.characterStream?.text, totalRecords: results[1][0]?.totalRecords ?: 0]
+ }
+
+ /**
+ * Find fitments by partNumber and modelYearIds.
+ *
+ * @param partNumber
+ * @param modelYearIds list
+ *
+ * @return List of partFitments
+ */
+ List findFitmentsByPartNumberAndModelYearIds(String partNumber, List modelYearIds, String columns = null) {
+ String query = """SELECT ${columns ?: '*'} FROM dbo.PartFitment WITH(NOLOCK)
+ WHERE partNumber = ? AND modelYearId IN (${modelYearIds.join(', ')}) AND dateDeleted IS NULL"""
+ sql().rows(query, [partNumber])
+ }
+
+ /**
+ * Find fitments by partNumber and partFitmentIds.
+ *
+ * @param partNumber
+ * @param partFitmentIds list
+ *
+ * @return List of partFitments
+ */
+ List findFitmentsByPartNumberAndPartFitmentIds(String partNumber, List partFitmentIds) {
+ String query = """SELECT * FROM dbo.PartFitment WITH(NOLOCK)
+ WHERE partNumber = ? AND partFitmentId IN (${partFitmentIds.join(', ')}) AND dateDeleted IS NULL"""
+ sql().rows(query, [partNumber])
+ }
+}
Index: grails-app/services/com/lemans/ds/fitment/make/MakeManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/fitment/make/MakeManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/fitment/make/MakeManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,119 @@
+package com.lemans.ds.fitment.make
+
+import com.lemans.ds.fitment.Make
+import com.lemans.ds.fitment.Segment
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class MakeManagerService extends LemansManager {
+
+ def makeService
+
+ /**
+ * Creates a new Make.
+ *
+ * @param values
+ * @param username
+ *
+ * @return Make which may contain errors or be null
+ */
+ Make createMake(Map values, String username) {
+ Make make = new Make(values)
+ make.validate()
+ validateModelNameFormat(make)
+ saveOrDiscardDomain(make, username)
+ }
+
+ /**
+ * Updates a Make.
+ *
+ * @param values
+ * @param makeId
+ * @param username
+ *
+ * @return Make which may contain errors or be null
+ */
+ Make updateMake(Map values, Integer makeId, String username) {
+ Make make = findMake(makeId)
+ if (make) {
+ applyValuesToDomain(values, make)
+ validateModelNameFormat(make)
+ saveOrDiscardDomain(make, username)
+ }
+ make
+ }
+
+ /**
+ * Soft deletes a Make.
+ *
+ * @param makeId
+ * @param username
+ *
+ * @return Make which may contain errors or be null
+ */
+ Make deleteMake(Integer makeId, String username) {
+ Map partsAndModels = makeService.modelFitmentCount([makeId: makeId])
+ Integer partCount = partsAndModels.partCount
+ Integer modelCount = partsAndModels.modelCount
+ if (modelCount || partCount) { makeWithErrors(partCount, modelCount) }
+ else {
+ Make make = findMake(makeId)
+ if (make) { softDelete(make, username) }
+ make
+ }
+ }
+
+ /**
+ * Finds Make by makeId
+ * @param makeId
+ *
+ * @return the Make or null
+ */
+ Make findMake(Integer makeId) {
+ Make.findByIdAndDateDeletedIsNull(makeId)
+ }
+
+ void validateModelNameFormat(Object object) {
+ String modelNameFormat = object.modelNameFormat
+ if (modelNameFormat) {
+ List modelNameFormatSegments = modelNameFormat.split('\\P{Alpha}+')
+ if (modelNameFormatSegments - segmentCodes()) {
+ invalidSegmentsExist(object)
+ }
+ if (modelNameFormatSegments.clone().toUnique().size() != modelNameFormatSegments.size()) {
+ duplicateSegmentsExist(object)
+ }
+ }
+ }
+
+ private List segmentCodes() {
+ Segment.withNewSession {
+ Segment.segmentCodes()
+ }
+ }
+
+ private void invalidSegmentsExist(Object object) {
+ object.errors.rejectValue('modelNameFormat', 'makeModel.modelNameFormat.invalidSegmentCode', 'Invalid Segment codes present')
+ }
+
+ private void duplicateSegmentsExist(Object object) {
+ object.errors.rejectValue('modelNameFormat', 'makeModel.modelNameFormat.duplicateSegmentCode',
+ 'Model Name Format has duplicate Segment Codes')
+ }
+
+ private Make makeWithErrors(Integer partCount, Integer modelCount) {
+ Make make = new Make()
+ if (partCount) { partsWithMakeExists(make, partCount) }
+ if (modelCount) { modelsWithMakeExists(make, modelCount) }
+ make
+ }
+
+ private void modelsWithMakeExists(Make make, Integer modelCount) {
+ make.errors.reject('make.models.assigned', [modelCount] as Object[], 'Cannot delete Make that has models associated')
+ }
+
+ private void partsWithMakeExists(Make make, Integer partCount) {
+ make.errors.reject('make.parts.assigned', [partCount] as Object[], 'Cannot delete Make that has parts associated')
+ }
+}
Index: grails-app/services/com/lemans/ds/fitment/make/MakeService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/fitment/make/MakeService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/fitment/make/MakeService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,88 @@
+package com.lemans.ds.fitment.make
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional(readOnly = true)
+class MakeService extends LemansService {
+
+ /**
+ * Finds a Make by id.
+ *
+ * @param makeId
+ *
+ * @return Map containing Make info
+ */
+ Map findMakeById(Integer makeId) {
+ dqx([makeId: makeId]).executeOneFrom('dbo.vwMake', ['makeId = :makeId'])?.results[0]
+ }
+
+ /**
+ * Finds Makes.
+ *
+ * @param criteria
+ *
+ * @return Map containing paginated data
+ */
+ Map findMakes(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'makeId+ASC'
+ dqx(criteria).executeFrom('dbo.vwMake', clauses(criteria))
+ }
+
+ /**
+ * Count of Model's And Part's Assigned to a Make.
+ *
+ * @param criteria containing makeId
+ *
+ * @return Map containing modelCount and partCount
+ */
+ Map modelFitmentCount(Map criteria) {
+ sql().rows(criteria, '''SELECT x.modelCount
+ ,y.partCount
+FROM (
+ SELECT COUNT(*) AS modelCount
+ FROM dbo.Model mo WITH (NOLOCK)
+ WHERE mo.makeId = :makeId
+ AND mo.dateDeleted IS NULL
+ ) x
+ ,(
+ SELECT COUNT(*) AS partCount
+ FROM dbo.Model mo WITH (NOLOCK)
+ INNER JOIN dbo.ModelYear my WITH (NOLOCK) ON my.modelId = mo.modelId
+ AND my.dateDeleted IS NULL
+ INNER JOIN dbo.PartFitment f WITH (NOLOCK) ON f.modelYearId = my.modelYearId
+ AND f.dateDeleted IS NULL
+ WHERE mo.makeId = :makeId
+ AND mo.dateDeleted IS NULL
+ ) y'''
+ )[0]
+ }
+
+ private List clauses(Map criteria) {
+ List clauses = []
+ clauses.addAll wildcardClauses(criteria, ['makeName'])
+ if (criteria.q) {
+ clauses.addAll qClauses(criteria)
+ }
+ clauses
+ }
+
+ private List qClauses(criteria) {
+ List clauses = []
+ if (criteria.q) {
+ expandQ(criteria)
+ String clause = '''
+ makeName LIKE :beginningWithQ
+ OR makeName LIKE :containingQ'''
+ clauses << clause
+ }
+ clauses
+ }
+
+ private expandQ(criteria) {
+ criteria.with {
+ beginningWithQ = "$q%".toString()
+ containingQ = "%$q%".toString()
+ }
+ }
+}
Index: grails-app/services/com/lemans/ds/fitment/model/ModelManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/fitment/model/ModelManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/fitment/model/ModelManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,347 @@
+package com.lemans.ds.fitment
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+import java.sql.Timestamp
+import java.time.Year
+
+@Transactional
+class ModelManagerService extends LemansManager {
+
+ def makeManagerService
+
+ def modelService
+
+ def modelYearService
+
+ /**
+ * Creates a new Model.
+ *
+ * @param values containing makeId
+ * @param username
+ *
+ * @return Model which may contain errors or be null
+ */
+ Model createModel(Map values, String username) {
+ Model model = model(values)
+ model.validate()
+ duplicateModelValidation(model)
+ invalidYearsValidation(values.years, model)
+ makeManagerService.validateModelNameFormat(model)
+ saveOrDiscardModel(model, username)
+ model
+ }
+
+ private Model model(Map values) {
+ Make make = makeManagerService.findMake(values.makeId)
+ Model model = new Model(values)
+ model.make = make
+ addSegmentsAndYears(model, values.segments, values.years)
+ model.modelName = createModelName(model)
+ model
+ }
+
+ String createModelName(Model model) {
+ Map modelSegments = [:]
+ model.modelSegments.findAll { it.dateDeleted == null }.each { modelSegment ->
+ if (modelSegment.segment && modelSegment.segmentValue) {
+ modelSegments << [(modelSegment.segment.sequence): "${modelSegment.segmentValue}[${modelSegment.segment.segmentCode}]"]
+ }
+ }
+ modelSegments ? modelSegments.sort()*.value.join('|') : ''
+ }
+
+ private Model saveOrDiscardModel(Model model, String username) {
+ if (model.hasErrors() || deleted(model)) {
+ model.discard()
+ } else {
+ Set modelSegments = model.modelSegments?.clone()
+ Set modelYears = model.modelYears?.clone()
+ clearSegmentsAndYears(model)
+ auditFields(username, model)
+ model.save()
+ saveSegmentsAndYears(modelSegments, modelYears, model.id, username)
+ }
+ model
+ }
+
+ private void clearSegmentsAndYears(Model model) {
+ model.modelSegments?.clear()
+ model.modelYears?.clear()
+ }
+
+ private void auditFields(String username, Model model) {
+ Date now = new Date()
+ model.lastUpdatedBy = username
+ model.lastUpdated = now
+ if (!model.id) {
+ model.createdBy = username
+ model.dateCreated = now
+ }
+ }
+
+ private void addSegmentsAndYears(Model model, List segments, List years) {
+ Set segmentMaps = Segment.findAllByDateDeletedIsNull()
+ segments?.each { segment ->
+ model.addToModelSegments([segment: segmentMaps.find { it.segmentCode == segment.segmentCode }, segmentValue: segment.value])
+ }
+ years?.each { model.addToModelYears(new ModelYear(year: it)) }
+ }
+
+ private saveSegmentsAndYears(Set modelSegments, Set modelYears, Integer modelId, String username) {
+ Timestamp now = new Timestamp(new Date().time)
+ if (modelSegments) {
+ sql().withBatch(50, MODEL_SEGMENT_INSERT_SQL) { stmt ->
+ modelSegments.each { ModelSegment modelSegment ->
+ stmt.addBatch([modelId: modelId, segmentId: modelSegment.segment.id, value: modelSegment.segmentValue,
+ now: now, username: username])
+ }
+ }
+ }
+ if (modelYears) {
+ sql().withBatch(50, MODEL_YEAR_INSERT_SQL) { stmt ->
+ modelYears.each { ModelYear year ->
+ stmt.addBatch([modelId: modelId, year: year.year, now: now, username: username])
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates a Model.
+ *
+ * @param values containing makeId
+ * @param modelId
+ * @param username
+ *
+ * @return Model which may contain errors or be null
+ */
+ Model updateModel(Map values, Integer modelId, String username) {
+ Model model = findModelByMake([modelId: modelId, makeId: values.makeId])
+ if (model) {
+ applyValuesToDomain(values, model)
+ updateSegmentsAndYears(model, values, username)
+ model.modelName = createModelName(model)
+ model.validate()
+ duplicateModelValidation(model)
+ makeManagerService.validateModelNameFormat(model)
+ invalidYearsValidation(values.years, model)
+ saveOrDiscardDomain(model, username)
+ }
+ model
+ }
+
+ private void updateSegmentsAndYears(Model model, Map values, String username) {
+ updateSegments(model, values.segments, username)
+ updateYears(model, values.years, username)
+ }
+
+ private void updateSegments(Model model, List modelSegments, String username) {
+ if (modelSegments) {
+ Set segments = Segment.findAllByDateDeletedIsNull()
+ Set existingModelSegments = model.modelSegments.findAll { it.dateDeleted == null }
+ modelSegments.each { ms ->
+ Segment segment = segments.find { it.segmentCode == ms.segmentCode }
+ ModelSegment modelSegment = existingModelSegments.find { it.segment == segment }
+ if (modelSegment) { modelSegment.segmentValue = ms.value }
+ else {
+ Date now = new Date()
+ model.addToModelSegments([segmentValue: ms.value, segment: segment, createdBy: username,
+ dateCreated: now, lastUpdatedBy: username, lastUpdated: now])
+ }
+ }
+ }
+ }
+
+ private void updateYears(Model model, List years, String username) {
+ if (years) {
+ if (!invalidYears(years)) {
+ List yearsToInsert = years - model.modelYears.findAll { it.dateDeleted == null }*.year
+ if (yearsToInsert) {
+ Timestamp now = new Timestamp(new Date().time)
+ Integer modelId = model.id
+ sql().withBatch(50, MODEL_YEAR_INSERT_SQL) { stmt ->
+ yearsToInsert.each { Integer year ->
+ stmt.addBatch([modelId: modelId, year: year, now: now, username: username])
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private List invalidYears(List years) {
+ years.findAll { (it < 1894) || (it > Year.now().value + 1) }
+ }
+
+ private void invalidYearsValidation(List years, Model model) {
+ List invalidYears = invalidYears(years)
+ if (invalidYears) {
+ model.errors.reject('model.yearRangeExceeded', [invalidYears, "${Year.now().value + 1}"] as Object[], 'Invalid Years')
+ }
+ }
+
+ /**
+ * Updates Multiple Models.
+ * operation which is either ADDYEAR, DELETEYEAR or DELETE
+ *
+ * @param values
+ * @param username
+ *
+ * @return Model which may contain errors or be null
+ */
+ Map updateMultipleModels(Map values, String username) {
+ switch (values.operation) {
+ case 'DELETE':
+ deleteMultipleModels(values, username)
+ break
+ case 'ADDYEAR':
+ createYearsForModels(values, username)
+ break
+ case 'DELETEYEAR':
+ deleteYearsForModels(values, username)
+ break
+ }
+ }
+
+ /**
+ * Create Years for Models (Ignore already existing Years).
+ *
+ * @param values
+ * @param username
+ *
+ * @return Map containing errors
+ */
+ Map createYearsForModels(Map values, username) {
+ List errors = []
+ modelAndYearsValidation(values, errors)
+ if (!errors) {
+ Map modelYears = modelYearService.verifyModelYears([makeId: values.makeId, modelIds: values.modelIds, years: values.years])
+ Timestamp now = new Timestamp(new Date().time)
+ sql().withBatch(50, MODEL_YEAR_INSERT_SQL) { stmt ->
+ values.modelIds.each { Integer modelId ->
+ List existingYears = modelYears[modelId]*.year
+ values.years.each { Integer year ->
+ if (!(year in existingYears)) {
+ stmt.addBatch([modelId: modelId, year: year, now: now, username: username])
+ }
+ }
+ }
+ }
+ }
+ [errors: errors]
+ }
+
+ private void duplicateModelValidation(Model model) {
+ if (model.modelName && modelService.modelWithModelNameExists(model)) {
+ model.errors.rejectValue('modelName', 'model.duplicate', 'Duplicate Model')
+ }
+ }
+
+ private void modelAndYearsValidation(Map values, List errors) {
+ List modelIds = modelService.verifyModelIds([makeId: values.makeId, modelIds: values.modelIds])
+ List inValidModelIds = values.modelIds - modelIds
+ if (inValidModelIds) { errors << "Invalid Model Ids found $inValidModelIds" }
+ List years = values.years
+ Integer currentYear = Year.now().value
+ List invalidYears = years.findAll { (it < 1894) || (it > currentYear + 1) }
+ if (invalidYears) { errors << "Invalid Years found $invalidYears. Years should be between 1894 and ${currentYear + 1}" }
+ }
+
+ private Map deleteYearsForModels(Map values, username) {
+ List errors = []
+ modelAndYearsValidation(values, errors)
+ if (!errors) {
+ Map modelYears = modelYearService.verifyModelYears([makeId: values.makeId, modelIds: values.modelIds, years: values.years])
+ Timestamp now = new Timestamp(new Date().time)
+ sql().withBatch(50, MODEL_YEAR_DELETE_SQL) { stmt ->
+ values.modelIds.each { Integer modelId ->
+ List existingYears = modelYears[modelId]*.year
+ values.years.each { Integer year ->
+ if ((year in existingYears)) {
+ stmt.addBatch([modelId: modelId, year: year, now: now, username: username])
+ }
+ }
+ }
+ }
+ }
+ [errors: errors]
+ }
+
+ private Map deleteMultipleModels(Map values, username) {
+ List errors = []
+ Map criteria = [makeId: values.makeId, modelIds: values.modelIds]
+ List modelIds = modelService.verifyModelIds(criteria)
+ List invalidModels = values.modelIds - modelIds
+ if (invalidModels) { errors << "Invalid Model Ids found $invalidModels" }
+ if (!errors) {
+ List modelsContainingParts = modelService.modelsContainingParts(criteria)
+ if (modelsContainingParts) {
+ errors << "Found Model(s) $modelsContainingParts associated to parts. Cannot delete models that has parts Associated"
+ } else {
+ batchDeleteModels([modelIds: values.modelIds.join(','), now: new Timestamp(new Date().time), username: username])
+ }
+ }
+ [errors: errors]
+ }
+
+ private void batchDeleteModels(Map criteria) {
+ sql().withBatch(20) { stmt ->
+ stmt.addBatch "UPDATE Model ${modelDeleteSql(criteria)}"
+ stmt.addBatch "UPDATE ModelSegment ${modelDeleteSql(criteria)}"
+ stmt.addBatch "UPDATE ModelYear ${modelDeleteSql(criteria)}"
+ }
+ }
+
+ /**
+ * Soft deletes a Model.
+ *
+ * @param criteria containing modelId and makeId
+ * @param username
+ *
+ * @return Model which may contain errors or be null
+ */
+ Map deleteModel(Map criteria, String username) {
+ deleteMultipleModels([makeId: criteria.makeId, modelIds: [criteria.modelId]], username)
+ }
+
+ /**
+ * Finds Model by modelId
+ * @param modelId
+ *
+ * @return the Model or null
+ */
+ Model findModelByMake(Map criteria) {
+ Make make = makeManagerService.findMake(criteria.makeId)
+ Model.findByIdAndMakeAndDateDeletedIsNull(criteria.modelId, make)
+ }
+
+ private static final String MODEL_YEAR_INSERT_SQL = '''
+INSERT INTO ModelYear (modelId, [year], dateCreated, createdBy, lastUpdated, lastUpdatedBy)
+VALUES (:modelId, :year, :now, :username, :now, :username)
+'''
+
+ private static final String MODEL_YEAR_DELETE_SQL = '''
+UPDATE ModelYear SET
+ dateDeleted = :now,
+ deletedBy = :username
+WHERE modelId = :modelId AND year = :year AND dateDeleted IS NULL
+'''
+
+ private static final String MODEL_SEGMENT_INSERT_SQL = '''
+INSERT INTO ModelSegment (modelId, segmentId, value, dateCreated, createdBy, lastUpdated, lastUpdatedBy)
+VALUES (:modelId, :segmentId, :value, :now, :username, :now, :username)
+'''
+
+ private static String modelDeleteSql(Map criteria) {
+ """
+ SET dateDeleted = '$criteria.now',
+ deletedBy = '$criteria.username',
+ lastUpdated = '$criteria.now',
+ lastUpdatedBy = '$criteria.username'
+ WHERE dateDeleted IS NULL
+ AND modelId IN ($criteria.modelIds)
+ """
+ }
+}
Index: grails-app/services/com/lemans/ds/fitment/model/ModelService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/fitment/model/ModelService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/fitment/model/ModelService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,74 @@
+package com.lemans.ds.fitment.model
+
+import com.lemans.ds.fitment.Model
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional(readOnly = true)
+class ModelService extends LemansService {
+
+ private static final String FIND_MODEL_SQL = 'SELECT dbo.fnGetModelDetails(?, ?)'
+
+ /**
+ * Model with ModelName already Exists.
+ *
+ * @param Model
+ *
+ * @return true if modelName already exits.
+ */
+ boolean modelWithModelNameExists(Model model) {
+ String query = "SELECT modelId FROM dbo.vwModel WHERE modelName = ? AND makeId = ? ${model.id ? 'AND modelId <> ?' : ''}"
+ sql().rows(query, modelNameClauses(model))[0]?.modelId
+ }
+
+ private List modelNameClauses (Model model) {
+ List clauses = [model.modelName, model.makeId]
+ if (model.id) { clauses << model.id }
+ clauses
+ }
+
+ /**
+ * Find modelDetails by makeId and modelId.
+ *
+ * @param makeId, modelId
+ *
+ * @return Model Details String
+ */
+ String findModelDetails(Integer makeId, Integer modelId = null) { queryForXmlClob(FIND_MODEL_SQL, [makeId, modelId]) }
+
+ /**
+ * Verify modelId's by makeId.
+ *
+ * @param criteria containing makeId and modelIds
+ *
+ * @return List of valid modelIds
+ */
+ List verifyModelIds(Map criteria) {
+ String query = "SELECT modelId FROM dbo.Model WHERE makeId = ? AND modelId IN (${criteria.modelIds.join(', ')})"
+ sql().rows(query, [criteria.makeId]).modelId
+ }
+
+ /**
+ * Verify model's containing parts.
+ *
+ * @param criteria containing makeId and modelIds
+ *
+ * @return List of models containing parts
+ */
+ List modelsContainingParts(Map criteria) {
+ String query = """
+ SELECT mo.modelId
+ FROM dbo.Model mo WITH (NOLOCK)
+ INNER JOIN ModelYear my WITH (NOLOCK) ON mo.modelId = my.modelId
+ AND my.dateDeleted IS NULL
+ INNER JOIN PartFitment pf WITH (NOLOCK) ON my.modelYearId = pf.modelYearId
+ AND pf.dateDeleted is NULL
+ WHERE mo.makeId = ?
+ AND mo.modelId IN (${criteria.modelIds.join(', ')})
+ AND mo.dateDeleted IS NULL
+ GROUP BY mo.modelId
+ HAVING COUNT(*) > 0
+ """
+ sql().rows(query, [criteria.makeId])?.modelId
+ }
+}
Index: grails-app/services/com/lemans/ds/fitment/model/segment/SegmentManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/fitment/model/segment/SegmentManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/fitment/model/segment/SegmentManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,71 @@
+package com.lemans.ds.fitment.model.segment
+
+import com.lemans.ds.fitment.Model
+import com.lemans.ds.fitment.ModelSegment
+import com.lemans.ds.fitment.Segment
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+/**
+ * Created by VRAMISETTI on 4/28/2017.
+ */
+@Transactional
+class SegmentManagerService extends LemansManager {
+
+ def modelManagerService
+
+ def modelService
+
+ ModelSegment updateOrCreateModelSegment(Map criteria, String username) {
+ Model model = modelManagerService.findModelByMake(criteria)
+ ModelSegment modelSegment
+ if (model) {
+ Segment segment = findSegment(criteria.segmentCode)
+ modelSegment = findModelSegment(model, segment) ?: new ModelSegment(model: model,
+ segment: segment, segmentValue: criteria.value)
+ if (modelSegment.id && !criteria.value) {
+ String origSegmentValue = modelSegment.segmentValue
+ validateDuplicateModel(modelSegment, model, segment, criteria, username)
+ modelSegment.segmentValue = origSegmentValue
+ modelSegment.hasErrors() ? modelSegment.discard() : softDelete(modelSegment, username)
+ } else {
+ modelSegment.segmentValue = criteria.value
+ modelSegment.validate()
+ validateDuplicateModel(modelSegment, model, segment, criteria, username)
+ saveOrDiscardDomain(modelSegment, username)
+ }
+ }
+ modelSegment
+ }
+
+ private void validateDuplicateModel(ModelSegment ms, Model model, Segment segment, Map criteria, String username) {
+ ModelSegment modelSegment = model.modelSegments.find { it.segment == segment && it.dateDeleted == null }
+ if (modelSegment) {
+ modelSegment.segmentValue = criteria.value
+ } else {
+ Date now = new Date()
+ model.addToModelSegments([segmentValue: criteria.value, segment: segment, createdBy: username,
+ dateCreated: now, lastUpdatedBy: username, lastUpdated: now])
+ }
+ duplicateModelError(model, ms)
+ model.discard()
+ }
+
+ private void duplicateModelError(Model model, ModelSegment ms) {
+ model.modelName = modelManagerService.createModelName(model)
+ if (!model.modelName) {
+ ms.errors.reject('model.emptySegments', 'Empty Segments')
+ }
+ if (model.modelName && model.id && modelService.modelWithModelNameExists(model)) {
+ ms.errors.reject('model.duplicate', 'Duplicate Model')
+ }
+ }
+
+ Segment findSegment(String segmentCode) {
+ Segment.findBySegmentCodeAndDateDeletedIsNull(segmentCode)
+ }
+
+ ModelSegment findModelSegment(Model model, Segment segment) {
+ ModelSegment.findByModelAndSegmentAndDateDeletedIsNull(model, segment)
+ }
+}
Index: grails-app/services/com/lemans/ds/fitment/model/segment/SegmentService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/fitment/model/segment/SegmentService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/fitment/model/segment/SegmentService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,60 @@
+package com.lemans.ds.fitment.model.segment
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional(readOnly = true)
+class SegmentService extends LemansService {
+
+ /**
+ * Find Segments.
+ *
+ * @param criteria
+ *
+ * @return Map containing paginated data
+ */
+ Map findSegments(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'segmentId+ASC'
+ dqx(criteria).executeFrom('dbo.Segment', ['dateDeleted IS NULL'])
+ }
+
+ /**
+ * Finds Segment values by Make and SegmentCode.
+ *
+ * @param criteria containing segmentCode and makeId
+ *
+ * @return Map containing paginated data
+ */
+ Map findSegmentValuesBySegmentCode(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'modelSegmentId+ASC'
+ dqx(criteria).executeFrom('dbo.vwModelSegment', clauses(criteria) + ['segmentCode = :segmentCode', 'makeId = :makeId'])
+ }
+
+ private List clauses(Map criteria) {
+ List clauses = []
+ clauses.addAll wildcardClauses(criteria, ['value'])
+ if (criteria.q) {
+ clauses.addAll qClauses(criteria)
+ }
+ clauses
+ }
+
+ private List qClauses(criteria) {
+ List clauses = []
+ if (criteria.q) {
+ expandQ(criteria)
+ String clause = '''
+ value LIKE :beginningWithQ
+ OR value LIKE :containingQ'''
+ clauses << clause
+ }
+ clauses
+ }
+
+ private expandQ(criteria) {
+ criteria.with {
+ beginningWithQ = "$q%".toString()
+ containingQ = "%$q%".toString()
+ }
+ }
+}
Index: grails-app/services/com/lemans/ds/fitment/model/year/ModelYearManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/fitment/model/year/ModelYearManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/fitment/model/year/ModelYearManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,70 @@
+package com.lemans.ds.fitment.model.year
+
+import com.lemans.ds.fitment.ModelYear
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+import java.sql.Timestamp
+
+@Transactional
+class ModelYearManagerService extends LemansManager {
+
+ def modelYearService
+
+ def modelManagerService
+
+ /**
+ * Creates a new ModelYear.
+ *
+ * @param values containing makeId, modelId
+ * @param username
+ *
+ * @return ModelYear which may contain errors or be null
+ */
+ Map createMultipleYears(Map values, String username) {
+ Map criteria = [makeId: values.makeId, modelIds: [values.modelId], years: values.years]
+ modelManagerService.createYearsForModels(criteria, username)
+ }
+
+ /**
+ * Soft deletes ModelYears.
+ *
+ * @param criteria containing makeId, modelId, years
+ *
+ * @return Map containing errors
+ */
+ Map deleteMultipleYears(Map values, String username) {
+ List years = values.years
+ List modelYears = modelYearService.yearIdsByYears(values.modelId, years)
+ deleteYearsAndFitments(modelYears, username, values)
+ }
+
+ /**
+ * Soft deletes ModelYear.
+ *
+ * @param criteria containing makeId, modelId, yearId
+ *
+ * @return Map containing errors
+ */
+ Map deleteYear(Map values, String username) {
+ Map modelYear = modelYearService.findYearById(values)
+ deleteYearsAndFitments(modelYear ? [modelYear] : [], username, values)
+ }
+
+ private Map deleteYearsAndFitments(List modelYears, String username, Map values) {
+ List modelYearIds = modelYears*.modelYearId
+ if (modelYears) {
+ ModelYear.executeUpdate(deleteYearsSql(modelYearIds), [new Date(), username, values.modelId])
+ sql().executeUpdate(deleteFitmentsSql(modelYearIds), [new Timestamp(new Date().time), username])
+ }
+ [errors: []]
+ }
+
+ private String deleteYearsSql(List modelYearIds) {
+ "UPDATE ModelYear SET dateDeleted = ?, deletedBy = ? WHERE modelId = ? AND modelYearId IN (${modelYearIds.join(', ')})"
+ }
+
+ private String deleteFitmentsSql(List modelYearIds) {
+ "UPDATE PartFitment SET dateDeleted = ?, deletedBy = ? WHERE modelYearId IN (${modelYearIds.join(', ')})"
+ }
+}
Index: grails-app/services/com/lemans/ds/fitment/model/year/ModelYearService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/fitment/model/year/ModelYearService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/fitment/model/year/ModelYearService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,92 @@
+package com.lemans.ds.fitment.model.year
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional(readOnly = true)
+class ModelYearService extends LemansService {
+
+
+ /**
+ * Finds a Year by id.
+ *
+ * @param criteria Map containing yearId, modelId and makeId
+ *
+ * @return Map containing Model Year info
+ */
+ Map findYearById(Map criteria) {
+ dqx(criteria).executeOneFrom('dbo.ModelYear', makeModel() + ['modelYearId = :yearId'] )?.results[0]
+ }
+
+ /**
+ * Finds Years by ModelId.
+ *
+ * @param criteria Map containing yearId, modelId and makeId
+ *
+ * @return Map containing Model Year info
+ */
+ Map verifyModelYears(Map criteria) {
+ String query = """SELECT modelId, [year] FROM dbo.ModelYear WHERE modelId IN (${criteria.modelIds.join(', ')})
+ AND [year] IN (${criteria.years.join(', ')}) AND dateDeleted IS NULL"""
+ sql().rows(query)?.groupBy { it.modelId }
+ }
+
+ /**
+ * Finds ModelYears by years.
+ *
+ * @param modelId
+ * @param years
+ *
+ * @return List containing modelYearId and year
+ */
+ List yearIdsByYears(Integer modelId, List years) {
+ String columns = 'modelYearId, [year]'
+ sql().rows(yearIdsSql(years, columns), [modelId])
+ }
+
+ /**
+ * Finds ModelYears by years.
+ *
+ * @param modelId
+ * @param years
+ *
+ *
+ * @return List containing modelYears
+ */
+ List findModelYearsByYears(Integer modelId, List years) {
+ sql().rows(yearIdsSql(years), [modelId])
+ }
+
+ /**
+ * Finds Years.
+ *
+ * @param criteria
+ *
+ * @return Map containing paginated data
+ */
+ Map findYears(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'modelYearId+ASC'
+ dqx(criteria).executeFrom('dbo.ModelYear', makeModel())
+ }
+
+ /**
+ * Finds valid ModelYearIds.
+ *
+ * @param modelYearIds
+ *
+ * @return List of valid modelYearIds
+ */
+ List findValidModelYearIds(List modelYearIds) {
+ String query = """SELECT modelYearId FROM dbo.ModelYear
+ WHERE modelYearId IN (${modelYearIds.join(', ')}) AND dateDeleted IS NULL"""
+ sql().rows(query)
+ }
+
+ //TODO: to remove datedeleted and modelId back to the clause.
+ private List makeModel() { [/*'makeId = :makeId', */'modelId = :modelId', 'dateDeleted IS NULL'] }
+
+ private String yearIdsSql(List years, String columns = null) {
+ """SELECT ${columns ?: '*'} FROM dbo.ModelYear
+ WHERE modelId = ? AND [year] IN (${years.join(', ')}) AND dateDeleted IS NULL"""
+ }
+}
Index: grails-app/services/com/lemans/ds/flag/FlagService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/flag/FlagService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/flag/FlagService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,38 @@
+package com.lemans.ds.flag
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional(readOnly = true)
+class FlagService extends LemansService {
+
+ /**
+ * Find Flags.
+ *
+ * @param criteria Map
+ *
+ * @return Map containing List of Flags and totalRecords
+ */
+ Map findFlags(Map criteria) {
+ dqx(criteria).executeFrom('dbo.vwFlag', clauses(criteria))
+ }
+
+ /**
+ * Find valid FlagIds by entityClass.
+ *
+ * @param List containing flagIds
+ *
+ * @param entityClass
+ *
+ * @return List containing valid flagIds by entityClass
+ */
+ List validFlagIdsForEntityClass(List flagIds, String entityClass) {
+ sql().rows("SELECT flagId FROM vwFlag WHERE entityClass = $entityClass AND flagId IN (" + flagIds.join(',') + ')')?.flagId
+ }
+
+ private clauses(Map criteria) {
+ List clauses = []
+ if (criteria.entityClass) { clauses << 'entityClass = :entityClass' }
+ clauses
+ }
+}
Index: grails-app/services/com/lemans/ds/flag/FlagValueManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/flag/FlagValueManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/flag/FlagValueManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,113 @@
+package com.lemans.ds.flag
+
+import com.lemans.ds.bulk.BulkProcessingHelperService
+import com.lemans.ds.media.Media
+import grails.transaction.Transactional
+
+
+@Transactional
+class FlagValueManagerService extends BulkProcessingHelperService {
+
+ def flagService
+
+ def flagValueService
+
+ def partService
+
+ /**
+ * Add or delete Flag Value for an entityClass and entityId.
+ *
+ * @param Map containing flagIds, entityClass and entityId
+ *
+ * @param userName
+ *
+ * @return Map containing errors
+ */
+ Map addOrDeleteFlagValues(Map values, String username) {
+ List errors = []
+ List flagIds = values.flagIds
+ String entityClass = values.entityClass
+ String entityId = values.entityId
+
+ validateFlagIds(flagIds, entityClass, errors)
+ customEntityClassValidations(entityClass, entityId, errors)
+
+ if (!errors) {
+ switch (values.operation) {
+ case 'DELETE':
+ deleteFlagValues(flagIds, entityId, errors, username)
+ break
+ case 'INSERT':
+ default:
+ addFlagValues(flagIds, entityId, errors, username)
+ }
+ }
+ [errors: errors]
+ }
+
+ private void customEntityClassValidations(String entityClass, String entityId, List errors) {
+ switch (entityClass) {
+ case 'part':
+ validatePart(entityId, errors)
+ break
+ case 'media':
+ validateMedia(entityId, errors)
+ break
+ }
+ }
+
+ private void validateFlagIds(List flagIds, String entityClass, List errors) {
+ if (flagIds) {
+ List inValidFlagIds = flagIds - flagService.validFlagIdsForEntityClass(flagIds, entityClass)
+ if (inValidFlagIds) {
+ errors << "Invalid FlagIds found $inValidFlagIds"
+ }
+ } else { errors << 'No FlagIds found' }
+ }
+
+ private void validatePart(String partNumber, List errors) {
+ if (!partService.partWithPartNumberExists(partNumber)) {
+ errors << "Part with partNumber $partNumber does not exist"
+ }
+ }
+
+ private void validateMedia(String mediaId, List errors) {
+ if (!(Media.findByIdAndDateDeletedIsNull(mediaId.toInteger()))) {
+ errors << "Media with mediaId $mediaId does not exist"
+ }
+ }
+
+ private void addFlagValues(List flagIds, String entityId, List errors, String username) {
+ FlagValue.withTransaction { status ->
+ flagValuesThatNeedToBeCreated(flagIds, entityId).each { Integer flagId ->
+ FlagValue flagValue = findFlagValue(entityId, flagId) ?: new FlagValue(flagId: flagId)
+ flagValue.entityId = entityId
+ flagValue.validate()
+ saveOrDiscardDomain(flagValue, username)
+ collectIfErrors(flagValue, errors)
+ }
+ if (errors) { status.setRollbackOnly() }
+ }
+ }
+
+ private List flagValuesThatNeedToBeCreated(List flagIds, String entityId) {
+ flagIds - flagValueService.existingFlagIdsForEntityId(flagIds, entityId)
+ }
+
+ private void deleteFlagValues(List flagIds, String entityId, List errors, String username) {
+ FlagValue.withTransaction { status ->
+ flagIds.each { Integer flagId ->
+ FlagValue flagValue = findFlagValue(entityId, flagId)
+ if (flagValue) {
+ softDelete(flagValue, username)
+ collectIfErrors(flagValue, errors)
+ }
+ }
+ if (errors) { status.setRollbackOnly() }
+ }
+ }
+
+ private FlagValue findFlagValue(String partNumber, Integer flagId) {
+ FlagValue.findByEntityIdAndFlagIdAndDateDeletedIsNull(partNumber, flagId)
+ }
+}
Index: grails-app/services/com/lemans/ds/flag/FlagValueService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/flag/FlagValueService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/flag/FlagValueService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,65 @@
+package com.lemans.ds.flag
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional(readOnly = true)
+class FlagValueService extends LemansService {
+
+ /**
+ * Find FlagValue by entityClass, entityId and flagId.
+ *
+ * @param Map containing entityClass, entityId and flagId
+ *
+ * @return FlagValue for an entityClass, entityId and flagId
+ */
+ Map findById(Map criteria) {
+ dqx(criteria).executeOneFrom('dbo.vwFlagValue', clauses(criteria))?.results[0]
+ }
+
+ /**
+ * Find FlagValues by entityClass, entityId.
+ *
+ * @param Map containing entityClass, entityId
+ *
+ * @return Map containing FlagValues for an entityClass and entityId and totalRecords
+ */
+ Map findFlags(Map criteria) {
+ dqx(criteria).executeFrom('dbo.vwFlagValue', clauses(criteria))
+ }
+
+
+ /**
+ * Find Existing FlagIds for an entityId.
+ *
+ * @param List containing flagIds
+ * *
+ * @param entityId
+ *
+ * @return List containing flagIds for an entityId
+ */
+ List existingFlagIdsForEntityId(List flagIds, String entityId) {
+ sql().rows("SELECT flagId FROM vwFlagValue WHERE entityId = $entityId AND flagId IN (" + flagIds.join(',') + ')')?.flagId
+ }
+
+ private List clauses(Map criteria) {
+ List clauses = ['entityClass = :entityClass', 'entityId = :entityId']
+ if (criteria.flagId) { clauses << 'flagId = :flagId' }
+ clauses
+ }
+
+ /**
+ * Find List of FlagValues for a productId
+ *
+ * @param productId and EntityClass
+ *
+ * @return List of FlagValues for a productId
+ */
+ List getListOfFlags(Map criteria) {
+ List clauses = []
+ clauses << 'entityClass = :entityClass'
+ clauses << 'entityId = :entityId'
+ if (criteria.flagId) { clauses << 'flagId = :flagId' }
+ dqx(criteria).executeFrom('dbo.vwflagValue', clauses).results
+ }
+}
Index: grails-app/services/com/lemans/ds/importing/BulkImportManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/importing/BulkImportManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/importing/BulkImportManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,139 @@
+package com.lemans.ds.importing
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+import javax.annotation.Resource
+import java.nio.file.Files
+
+@SuppressWarnings(['UnusedPrivateMethodParameter', 'UnusedPrivateMethod', 'PrintStackTrace'])//TODO: Code narc to be cleaned
+@Transactional
+class BulkImportManagerService extends LemansManager {
+
+ private static final String MIME_TYPE_SQL = '''
+SELECT *
+FROM dbo.vwMimeType
+WHERE mimeType = ?
+AND extension = ?
+'''
+
+ @Resource(name='dsLocaleImport')
+ def dsLocaleImport
+
+ @Resource(name='temporaryMediaPath')
+ def temporaryMediaPath
+
+ def bulkImportService
+
+ def mediaManagerService
+
+ /**
+ * Adds an Excel File.
+ *
+ * @param values Map containing tempId, originalFileName, and optionally description and effectiveDate
+ * @param username
+ *
+ * @return Excel which may be null or contain errors
+ */
+ LocaleImportProcess addLocaleImportProcessFile(Map values, String username) {
+ String tempId = values.tempId
+ values.remove('tempId')
+ LocaleImportProcess localeImportProcess = new LocaleImportProcess(values)
+ localeImportProcess.originalFileName = values.originalFileName
+ String tempFilePath = tempId + '.' + values.extension
+ String pathToTempFile = temporaryMediaPath + '/' + tempFilePath
+ localeImportProcess.status = 'Uploaded'
+
+ File tempFile = new File(pathToTempFile)
+ if (tempFile.exists()) {
+ resolveMimeAndMediaType(tempFile, tempFilePath, values, localeImportProcess)
+ }
+ else { localeImportProcess.errors.reject('file.notFound', 'Uploaded file not found') }
+ saveOrDiscardDomain(localeImportProcess, username)
+ if (!localeImportProcess.hasErrors()) {
+ if (!moveFile(tempFile, localeImportProcess, values)) {
+ softDelete(localeImportProcess, username)
+ localeImportProcess.errors.reject('file.notMoved', 'Could not Move Uploaded File')
+ }
+ }
+ localeImportProcess
+ }
+
+
+ private void resolveMimeAndMediaType(File tempFile, String tempFilePath, Map values, LocaleImportProcess localeImportProcess) {
+ String mimeTypeDescription = mediaManagerService.detectMimeType(tempFile, tempFilePath)
+ Map mimeType = sql().firstRow(MIME_TYPE_SQL, [mimeTypeDescription, values.extension ?: ''])
+ if (mimeType) {
+ localeImportProcess.mimeTypeId = mimeType.mimeTypeId
+ localeImportProcess.validate()
+ }
+ else {
+ String message = "Could not determine the correct file type for mimeType $mimeTypeDescription with extension $values.extension"
+ localeImportProcess.errors.reject('type.notFound', message)
+ }
+ }
+
+ private String completeMediaPath(LocaleImportProcess localeImportProcess) {
+ dsLocaleImport + localeImportProcess.id
+ }
+
+ private String pathToExcel(LocaleImportProcess localeImportProcess, String extension) {
+ completeMediaPath(localeImportProcess) + '/' + absoluteFileName(localeImportProcess, extension)
+ }
+
+ private String absoluteFileName(LocaleImportProcess localeImportProcess, String extension) {
+ "${localeImportProcess.originalFileName}.${extension}"
+ }
+
+ @SuppressWarnings(['CatchException'])
+ private boolean moveFile(File tempFile, LocaleImportProcess localeImportProcess, Map values) {
+ // TODO: subfolders - 2 chars & 2 levels,
+ try {
+ new File(completeMediaPath(localeImportProcess)).mkdir()
+ File movedFile = new File(pathToExcel(localeImportProcess, values.extension))
+ File localePathFolder = new File(dsLocaleImport + localeImportProcess.id)
+ if (!movedFile.exists()) {
+ localePathFolder.mkdirs()
+ }
+ Files.copy(tempFile.toPath(), movedFile.toPath())
+ true
+ } catch (Exception x) {
+ log.error('Unable to move Excel file', x)
+ x.printStackTrace()
+ false
+ }
+ }
+
+ /**
+ * Delete Excel File if appropriate.
+ *
+ * @param values
+ * @param localeImportProcessId
+ * @param username
+ *
+ * @return Media which may be null or contain errors
+ */
+ LocaleImportProcess deleteLocaleImportProcess(Integer importFileId, String username) {
+ Map process = bulkImportService.findLocaleImportProcessById([localeImportProcessId: importFileId])
+ if (process && process.endDate) {
+ LocaleImportProcess localeImportProcess = bulkImportService.findLocaleImportProcess(importFileId)
+ if (localeImportProcess) {
+ File file = bulkImportService.findLocaleImportProcessOriginalFile(localeImportProcess)
+ if (file && file.exists()) {
+ boolean deleted = file.delete()
+ if (!deleted) {
+ log.error('FAILED to delete ' + absoluteFileName(localeImportProcess, localeImportProcess.extension))
+ }
+ softDelete(localeImportProcess, username)
+ } else {
+ localeImportProcess.errors.reject('file.notFound', 'Could not delete Excel file. File Does not Exist')
+ }
+ }
+ localeImportProcess
+ } else {
+ LocaleImportProcess localeImportProcess1 = new LocaleImportProcess()
+ localeImportProcess1.errors.reject('localeImportProcess.id.stillProcessing', 'Still Processing')
+ localeImportProcess1
+ }
+ }
+
+}
Index: grails-app/services/com/lemans/ds/importing/BulkImportService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/importing/BulkImportService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/importing/BulkImportService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,111 @@
+package com.lemans.ds.importing
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+import javax.annotation.Resource
+
+@Transactional(readOnly = true)
+class BulkImportService extends LemansService {
+
+ @Resource(name='mediaPrefix')
+ def mediaPrefix
+
+ @Resource(name='dsLocaleImport')
+ def dsLocaleImport
+
+ private static final String IMPORT_PROCESS_SQL = '''SELECT * FROM dbo.vwLocaleImportProcess WITH(NOLOCK)
+WHERE localeImportProcessId = :localeImportProcessId'''
+
+ /**
+ * Finds Locale Import Process by Id.
+ *
+ * @param criteria containing localeImportProcessId
+ *
+ * @return Map containing Media info
+ */
+ Map findLocaleImportProcessById(Map criteria) {
+ sql().firstRow(criteria, IMPORT_PROCESS_SQL)
+ }
+
+ /**
+ * Finds Locale Import Processes.
+ *
+ * @param criteria
+ *
+ * @return Map containing paginated data
+ */
+ Map findBulkImports(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'localeImportProcessId+ASC'
+ dqx(criteria).executeFrom('dbo.vwLocaleImportProcess', clauses(criteria))
+ }
+
+ /**
+ * Finds a LocaleImportProcess File.
+ *
+ * @param media
+ *
+ * @return
+ */
+ File findLocaleImportProcessOriginalFile(Map localeImportProcess) {
+ File file = new File(originalFilePath(localeImportProcess))
+ file.exists() ? file : null
+ }
+
+ /**
+ * Finds a LocaleImportProcess File.
+ *
+ * @param media
+ * @return
+ */
+ File findLocaleImportProcessOriginalFile(LocaleImportProcess localeImportProcess) {
+ File file = new File(originalFilePath(localeImportProcess))
+ file.exists() ? file : null
+ }
+
+ private String originalFilePath(Map localeImportProcess) {
+ dsLocaleImport + localeImportProcess.localeImportProcessId + '/' +
+ "${localeImportProcess.originalFileName}.${localeImportProcess.extension}"
+ }
+
+ private String reportFilePath(Map localeImportProcess) {
+ dsLocaleImport + localeImportProcess.localeImportProcessId + '/' + 'report.xls'
+ }
+
+ private String originalFilePath(LocaleImportProcess localeImportProcess) {
+ dsLocaleImport + localeImportProcess.id + '/' + absoluteFileName(localeImportProcess)
+ }
+
+ private String absoluteFileName(LocaleImportProcess localeImportProcess) {
+ "${localeImportProcess.originalFileName}.${localeImportProcess.extension}"
+ }
+
+ /**
+ * Finds a LocaleImportProcess File.
+ *
+ * @param media
+ * @return
+ */
+ File findLocaleImportProcessReportFile(Map localeImportProcess) {
+ File file = new File(reportFilePath(localeImportProcess))
+ file.exists() ? file : null
+ }
+
+ /**
+ * Find LocaleImportProcess by importFileId.
+ *
+ * @param importFileId
+ *
+ * @return LocaleImportProcess
+ */
+ LocaleImportProcess findLocaleImportProcess(Integer importFileId) {
+ LocaleImportProcess.findByIdAndDateDeletedIsNull(importFileId)
+ }
+
+
+ private List clauses(Map criteria) {
+ List clauses = []
+ if (criteria.importType) { clauses << 'importType = :importType' }
+ clauses
+ }
+}
Index: grails-app/services/com/lemans/ds/part/PartAssociationManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/part/PartAssociationManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/part/PartAssociationManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,72 @@
+package com.lemans.ds.part
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class PartAssociationManagerService extends LemansManager {
+
+ def partAssociationService
+
+ /**
+ *
+ * Adds PartAssociation
+ *
+ * @param partNumber
+ * @param input
+ * @param username
+ *
+ * @return partAssociation
+ */
+ PartAssociation addPartAssociation(String partNumber, Map input, String username) {
+ PartAssociation partAssociation = new PartAssociation()
+ input.partNumber = partNumber
+ applyValuesToDomain(input, partAssociation)
+ partAssociation.validate()
+ validatePartAssociation(partAssociation)
+ saveOrDiscardDomain(partAssociation, username)
+ }
+
+ /**
+ * Updates PartAssociation
+ *
+ * @param partAssociationId
+ * @param input
+ * @param username
+ *
+ * @return partAssociation
+ */
+ PartAssociation updatePartAssociation(Integer id, Map input, String username) {
+ PartAssociation partAssociation = partAssociationService.getAssociation(id)
+ if (partAssociation) {
+ applyValuesToDomain(input, partAssociation)
+ partAssociation.validate()
+ saveOrDiscardDomain(partAssociation, username)
+ }
+ }
+
+ /**
+ * Deletes a partAssociation
+ *
+ * @param partAssociationId
+ * @param username
+ *
+ * @return partAssociation
+ */
+ PartAssociation deletePartAssociation(Integer id, String username) {
+ PartAssociation partAssociation = partAssociationService.getAssociation(id)
+ if (partAssociation) {
+ softDelete(partAssociation, username)
+ }
+ partAssociation
+ }
+
+ private void validatePartAssociation(PartAssociation partAssociation) {
+ if (PartAssociation.findByPartNumberAndRelatedPartNumberAndAssociationTypeIdAndDateDeletedIsNull(partAssociation.partNumber,
+ partAssociation.relatedPartNumber, partAssociation.associationTypeId)) {
+ partAssociation.errors.reject('partAssociation.duplicate',
+ [partAssociation.partNumber, partAssociation.associationTypeId, partAssociation.relatedPartNumber] as Object[],
+ 'PartAssociation for partNumber with associationTypeId and relatedPartNumber exists.')
+ }
+ }
+}
Index: grails-app/services/com/lemans/ds/part/PartAssociationService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/part/PartAssociationService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/part/PartAssociationService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,61 @@
+package com.lemans.ds.part
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional
+class PartAssociationService extends LemansService {
+
+ /**
+ * Find relatedParts by Part Number
+ *
+ * @param criteria containing partNumber
+ *
+ * @return related parts
+ */
+ Map findRelatedParts(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'partNumber+ASC'
+ dqx(criteria).executeFrom('dbo.vwPartAssociation', clauses(criteria))
+ }
+
+ /**
+ * Find referralParts by related Part Number
+ *
+ * @param criteria containing referral partNumber
+ *
+ * @return referral parts
+ */
+ Map findReferralParts(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'relatedPartNumber+ASC'
+ dqx(criteria).executeFrom('dbo.vwPartAssociation', clauses(criteria))
+ }
+
+ /**
+ * Find PartAssociation by PartAssociationId
+ *
+ * @param criteria contaning partAssociationId
+ *
+ * @return partAssociation
+ */
+ Map findPartAssociation(Map criteria) {
+ dqx(criteria).executeOneFrom('dbo.vwPartAssociation', clauses(criteria))?.results[0]
+ }
+
+ private List clauses(Map criteria) {
+ List clauses = []
+ if (criteria.partNumber) {
+ clauses << 'partNumber = :partNumber'
+ }
+ if (criteria.relatedPartNumber) {
+ clauses << 'relatedPartNumber = :relatedPartNumber'
+ }
+ if (criteria.partAssociationId) {
+ clauses << 'partAssociationId = :partAssociationId'
+ }
+ clauses
+ }
+
+ PartAssociation getAssociation(Integer id) {
+ PartAssociation.findByIdAndDateDeletedIsNull(id)
+ }
+}
Index: grails-app/services/com/lemans/ds/part/PartAttributeManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/part/PartAttributeManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/part/PartAttributeManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,92 @@
+package com.lemans.ds.part
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class PartAttributeManagerService extends LemansManager {
+
+ def productPartManagerService
+ def productPartService
+
+ private final PartAttributeTransformer partAttributeTransformer = new PartAttributeTransformer()
+
+ private static final String UPDATE_PART_ATTRIBUTE_SQL =
+ '''EXEC spUpdatePartAttribute
+ @xml = ?,
+ @appSecurityUser = ?
+ '''
+
+ Map addSinglePartAttributeRelation(Map values, String userName) {
+ values.operation = 'INSERT'
+ values.attributeValueIds = [values.attributeValueId]
+ Map input = buildBulkOperationFormat(values)
+ bulkAddOrRemove(input, userName)
+
+ }
+
+ Map removeSinglePartAttributeRelation(Map values, String userName) {
+ values.operation = 'DELETE'
+ values.attributeValueIds = [values.attributeValueId]
+ Map input = buildBulkOperationFormat(values)
+ bulkAddOrRemove(input, userName)
+ }
+
+ Map bulkReplaceAttributeValueRelation(Map values, String userName) {
+ values.operation = 'REPLACE'
+ Map input = buildBulkOperationFormat(values)
+ bulkAddOrRemove(input, userName)
+ }
+
+ Map buildBulkOperationFormat(Map data) {
+ Map attributeNameMap = [attributeNameId: data.attributeNameId, attributeValueIds: data.attributeValueIds]
+ [
+ operation: data.operation,
+ partNumber: [data.partNumber],
+ attribute: [attributeNameMap]
+ ]
+ }
+
+ Map bulkAddOrRemove(Map input, String userName) {
+ Map error = validateInput(input)
+ if (error) {
+ [messages: [error]]
+ } else {
+ String inputXML = partAttributeTransformer.partAttributeToXML(input)
+ List results = sql().callWithAllRows(UPDATE_PART_ATTRIBUTE_SQL, [inputXML, userName]) { }
+ if (!isErrorFromProc(results)) {
+ Map products = productPartService.findProductByPartNumber(input)
+ Map productIds = [:]
+ List prdIds = []
+ products.results.each { Map result ->
+ prdIds << result.productId
+ }
+ productIds << ['productIds': prdIds]
+ productPartManagerService.processEntityFlag(productIds)
+ }
+ [messages: results[0]]
+ }
+ }
+
+ Boolean isErrorFromProc(List results) {
+ Boolean error = true
+ results.each { List result ->
+ result.each { Map res ->
+ if (res.keySet().contains('type') && res.type == 'error') { error = true }
+ else {
+ error = false
+ }
+ }
+ }
+ error
+ }
+
+ Map validateInput(Map input) {
+ if (!input || !input.partNumber || input.partNumber?.isEmpty()) {
+ return [type: 'error', text: 'No Part Selected']
+ }
+ if (!input.attribute || input.attribute?.isEmpty()) {
+ return [type: 'error', text: 'No attribute change selected']
+ }
+ }
+}
Index: grails-app/services/com/lemans/ds/part/PartAttributeService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/part/PartAttributeService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/part/PartAttributeService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,32 @@
+package com.lemans.ds.part
+
+import com.lemans.ds.ReportAndLocaleCommonsController
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional
+class PartAttributeService extends LemansService {
+
+ private static final String CATEGORY_DETAILS_SQL = 'SELECT dbo.fnGetCategoryPartDetailsJSON(?,?)'
+
+ private static final String CATEGORY_DETAILS_LOCALE_SQL = 'SELECT dbo.fnGetCategoryPartDetailsJSONLocale(?,?,?)'
+
+ def categoryService
+ private final PartAttributeTransformer partAttributeTransformer = new PartAttributeTransformer()
+
+ def partAttributeDetails(Map criteria, List partNumber) {
+ if (categoryService.findCategoryById(criteria)) {
+ String partXML = partAttributeTransformer.partListToXML(partNumber)
+ queryForXmlClob(CATEGORY_DETAILS_SQL, [criteria.id, partXML])
+ } else {
+ null
+ }
+ }
+
+ String partAttributeLocaleDetails(Map criteria, List partNumber) {
+ if (criteria.locale in ReportAndLocaleCommonsController.VALID_LOCALES && categoryService.findCategoryById(criteria)) {
+ String partXML = partAttributeTransformer.partListToXML(partNumber)
+ queryForXmlClob(CATEGORY_DETAILS_LOCALE_SQL, [criteria.id, partXML, criteria.locale])
+ }
+ }
+}
Index: grails-app/services/com/lemans/ds/part/PartManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/part/PartManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/part/PartManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,140 @@
+package com.lemans.ds.part
+
+import com.lemans.ds.category.Category
+import com.lemans.ds.media.Media
+import com.lemans.ds.part.partmetadata.PartMetadata
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+import org.apache.commons.collections.CollectionUtils
+
+import java.sql.Timestamp
+
+@Transactional
+class PartManagerService extends LemansManager {
+
+ def categoryService
+ def productPartManagerService
+ def partService
+
+ private static final List PART_METADATA_FIELDS = ['partSpecificText', 'internalNotes', 'relatedParts',
+ 'relatedProducts', 'certificationUS', 'certificationEU',
+ 'oemPartNumber']
+
+ Map addOrRemoveParts(Map values, String username) {
+ Integer categoryId = values.categoryId
+ List partNumbers = values.partNumbers*.toString()*.toUpperCase()
+ List errors = []
+ if (categoryService.categoryExists([catalogInstanceId: values.catalogInstanceId, categoryId: categoryId])) {
+ List parts = parts(partNumbers)
+ if (partNumbers.size() == parts.size()) {
+ Timestamp now = new Timestamp(new Date().time)
+ Integer cId = (values.action == 'add') ? categoryId : null
+ sql().execute("UPDATE Part SET categoryId = $cId, lastUpdated = $now, lastUpdatedBy = $username WHERE partNumber IN " +
+ "('${parts.join('\',\'')}')")
+ } else { errors << "Invalid parts found ${partNumbers - parts*.toUpperCase()}" }
+ } else { errors << 'Invalid category' }
+ [errors: errors]
+ }
+
+ /**
+ * Updates specific details on existing Part.
+
+ * @return Part which may have errors or be null
+ */
+ Part updatePart(Map values, String partNumber, String username) {
+ Part part = findDomainObjectById(Part, partNumber)
+ if (part) {
+ applyValuesToDomain(values, part)
+ part.validate()
+ validatePart(values, part)
+ //update productPart relation for product and part
+ if (values.containsKey('productId') && !part.hasErrors()) {
+ Map data = updateProductPartRelation(values, partNumber, username)
+ data.errors.each {
+ part.errors.reject('invalid', it)
+ }
+ }
+ saveOrDiscardDomain(part, username)
+ List keys = values.keySet() as String[]
+ if (CollectionUtils.containsAny(keys, PART_METADATA_FIELDS)) {
+ updateorInsertPartMetadata(values, partNumber, username)
+ }
+ }
+ part
+ }
+
+ PartLocale updatePartLocale(Map values, String partNumber, String username) {
+ PartLocale partLocale = getPartLocale(partNumber, values)
+ if (partLocale) {
+ applyValuesToDomain(values, partLocale)
+ partLocale.validate()
+ saveOrDiscardDomain(partLocale, username)
+ }
+ }
+
+ private PartLocale getPartLocale(String partNumber, Map values) {
+ PartLocale partLocale = findPartLocale(partNumber, values.locale)
+ if (!partLocale) {
+ Part part = Part.findByIdAndDateDeletedIsNull(partNumber)
+ if (part) {
+ values.partNumber = partNumber
+ partLocale = new PartLocale()
+ }
+ }
+ partLocale
+ }
+
+ private PartLocale findPartLocale(String partNumber, String locale) {
+ PartLocale.findByPartNumberAndLocaleAndDateDeletedIsNull(partNumber, locale)
+ }
+
+ /**
+ * Updates specific details on existing Part.
+ *
+ * @return Part which may have errors or be null
+ */
+ PartMetadata updateorInsertPartMetadata(Map values, String partNumber, String username) {
+ PartMetadata partMetadata = findDomainObjectById(PartMetadata, partNumber) ?: new PartMetadata(values)
+ Date now = new Date()
+ if (partMetadata) {
+ partMetadata.id = partNumber
+ partMetadata.createdBy = username
+ partMetadata.dateCreated = now
+ applyValuesToDomain(values, partMetadata)
+ partMetadata.validate()
+ saveOrDiscardDomain(partMetadata, username)
+ }
+ }
+
+ /**
+ * On providing productId, add productPart operation shall be performed.
+ * On empty or Null productId, remove productPart operation shall be performed.
+ * @param values
+ * @param partNumber
+ * @param username
+ * @return
+ */
+ Map updateProductPartRelation(Map values, String partNumber, String username) {
+ values.partNumber = partNumber
+ if (values.productId && !values.producId.toString().isEmpty()) {
+ return productPartManagerService.addSingleProductPartRelation(values, username)
+ }
+ //retrieve part details to get primaryProductId since productId not provided in payload
+ Map part = partService.findPartByPartNumber(values)
+ values.productId = part?.primaryProductId
+ productPartManagerService.removeSingleProductPartRelation(values, username)
+ }
+
+ private void validatePart(Map values, Part part) {
+ if (values.categoryId != null && !findDomainObjectById(Category, values.categoryId)) {
+ part.errors.rejectValue('categoryId', 'invalid', "CategoryId with value [$values.categoryId] does not exist")
+ }
+ if (values.primaryMediaId != null && !findDomainObjectById(Media, values.primaryMediaId)) {
+ part.errors.rejectValue('primaryMediaId', 'invalid', "PrimaryMediaId with value [$values.primaryMediaId] does not exist")
+ }
+ }
+
+ private List parts(List partNumbers) {
+ sql().rows("SELECT partNumber FROM Part WHERE partNumber IN ('${partNumbers.join('\',\'')}')")*.partNumber
+ }
+}
Index: grails-app/services/com/lemans/ds/part/PartService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/part/PartService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/part/PartService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,146 @@
+package com.lemans.ds
+
+import com.lemans.ds.part.Part
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional(readOnly = true)
+class PartService extends LemansService {
+
+ static final List SEARCH_COLUMNSET = [
+ 'partNumber', 'punctuatedPartNumber', 'partStatusCode', 'partStatusDescr', 'vendorPartNumber',
+ 'brandName', 'partDescr', 'brandCode', 'vendorName', 'vendorId', 'subComCode', 'categoryId',
+ 'subComCodeId', 'productCode', 'mediaUrl', 'extension', 'version'
+ ].asImmutable()
+
+ private static final List SEARCHABLE_PROPERTIES = [
+ 'partNumber', 'partDescr', 'brandCode', 'brandName',
+ 'vendorId', 'vendorName', 'vendorPartNumber'
+ ].asImmutable()
+
+ private static final List META_DATA = ['partSpecificText', 'internalNotes', 'relatedParts',
+ 'relatedProducts', 'certificationUS', 'certificationEU', 'oemPartNumber']
+
+ private static final Map COLUMN_SETS = [_search: SEARCH_COLUMNSET].asImmutable()
+
+ private static final Integer MAX_SEARCH_RECORDS = 1000
+
+ /**
+ * Find a Part by partNumber.
+ *
+ * @param criteria containing partNumber
+ *
+ * @return part
+ */
+ Map findPartByPartNumber(Map criteria) {
+ Map partData = dqx(criteria).executeOneFrom('dbo.vwPart', ['partNumber = :partNumber'])?.results[0]
+ Map partMetadata = dqx(criteria).executeOneFrom('dbo.vwPartMetadata', ['partNumber = :partNumber'])?.results[0]
+ if (partMetadata) {
+ partMetadata = transformMetadataClobToText(partMetadata)
+ mergeMetaDataWithPart(partData, partMetadata)
+ } else if (partData) {
+ mergeMetaDataAsNull(partData)
+ }
+ partData
+ }
+
+ private void mergeMetaDataWithPart(Map partData, Map partMetaData) {
+ META_DATA.each { String metaData ->
+ partData."$metaData" = partMetaData."$metaData"
+ }
+ }
+
+ private void mergeMetaDataAsNull(Map partData) {
+ partData.identity {
+ partSpecificText = null
+ internalNotes = null
+ relatedParts = null
+ relatedProducts = null
+ certificationUS = null
+ certificationEU = null
+ oemPartNumber = null
+ }
+
+ }
+
+ @SuppressWarnings('ParameterReassignment')
+ Map transformMetadataClobToText(Map partMetadata) {
+ partMetadata.collectEntries { k, v ->
+ if (v?.class?.simpleName == 'ClobImpl') { v = v.characterStream.text }
+ [k, v]
+ }
+ }
+ /**
+ * Find if a Part by partNumber exists.
+ *
+ * @param partNumber
+ *
+ * @return true if part exists
+ */
+ boolean partWithPartNumberExists(String partNumber) {
+ sql().rows('SELECT * FROM vwPart WHERE partNumber = ?', [partNumber])[0]
+ }
+
+ /**
+ * Find Parts matching the filter criteria.
+ *
+ * @param criteria containing filter criteria
+ * filters:
+ * partStatusCode - exact
+ * subComCode - exact/multiple
+ * productId - exact/multiple
+ * partNumber - like
+ * partDescr - like
+ * brandCode - like
+ * brandName - like
+ * vendorId - like
+ * vendorName - like
+ * vendorPartNumber - like
+ * q - like (without client supplied wildcards) on partNumber and partNumber
+ *
+ * @return Map with results containing parts that match the filter criteria
+ */
+ Map findParts(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'partNumber+ASC'
+ criteria.columns = criteria.columns ?: '_search'
+ if (criteria.pageSize > MAX_SEARCH_RECORDS) { criteria.pageSize = MAX_SEARCH_RECORDS }
+ dqx(criteria).executeFrom('dbo.vwPart', searchClauses(criteria), COLUMN_SETS)
+ }
+
+ Map findPartsByLocale(Map criteria) {
+ List clauses = ['partNumber = :partNumber', 'locale = :locale']
+ Map partLocale = dqx(criteria).executeOneFrom('dbo.vwPartLocale', clauses)?.results[0]
+ if (partLocale) {
+ transformMetadataClobToText(partLocale)
+ }
+ }
+
+ private List searchClauses(Map criteria) {
+ List clauses = []
+ if (criteria.partStatusCode) { clauses << 'partStatusCode = :partStatusCode' }
+ Integer categoryId = criteria.categoryId
+ if (categoryId != null) {
+ if (categoryId == Part.UNASSIGNED_CATEGORY_ID) { clauses << 'categoryId IS NULL' }
+ else { clauses << 'categoryId = :categoryId' }
+ }
+ if (criteria.productId) {
+ if (criteria.productId == Part.UNASSIGNED_PRODUCT_ID) { clauses << 'primaryProductId IS NULL' }
+ else {
+ List productIdList = criteria.productId?.split(',')
+ String productIds = productIdList.collect { it }.join(',')
+ clauses << "primaryProductId IN ($productIds)"
+ }
+ }
+ if (criteria.subComCode) {
+ List subComCodeList = criteria.subComCode?.split(',')
+ String subComCodes = subComCodeList.collect { String unquoted -> "'$unquoted'" }.join(',')
+ clauses << "subComCode IN ($subComCodes)"
+ }
+ if (criteria.q) {
+ criteria.qLike = "%$criteria.q%" as String
+ clauses << '(partNumber LIKE :qLike OR partDescr LIKE :qLike)'
+ }
+
+ clauses + wildcardClauses(criteria, SEARCHABLE_PROPERTIES)
+ }
+}
Index: grails-app/services/com/lemans/ds/product/CategoryProductManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/product/CategoryProductManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/product/CategoryProductManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,29 @@
+package com.lemans.ds.product
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class CategoryProductManagerService extends LemansManager {
+
+ private final CategoryProductXmlGenerator categoryProductXmlGenerator = new CategoryProductXmlGenerator()
+
+ def categoryService
+
+ private static final String PART_ATTRIBUTE_PERSIST_SQL =
+ '''EXEC spUpdateProductCategory
+ @xml = ?,
+ @appSecurityUser = ?
+ '''
+
+ Map addOrRemoveCategoryProduct(Map values, String username) {
+ List errors = []
+ if (categoryService.findCategoryById([catalogInstanceId: values.catalogInstanceId, id: values.categoryId])) {
+ String inputXML = categoryProductXmlGenerator.generateProductCategoryXml(values)
+ List results = sql().callWithAllRows(PART_ATTRIBUTE_PERSIST_SQL, [inputXML, username]) { }
+ errors.addAll(results[0]?.text)
+ } else { errors << 'Invalid category or catalog' }
+ [errors: errors]
+ }
+
+}
Index: grails-app/services/com/lemans/ds/product/ProductAssociationManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/product/ProductAssociationManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/product/ProductAssociationManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,71 @@
+package com.lemans.ds.product
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class ProductAssociationManagerService extends LemansManager {
+
+ def productAssociationService
+
+ /**
+ * Adds ProductAssociation
+ *
+ * @param productId
+ * @param input
+ * @param username
+ *
+ * @return productAssociation
+ */
+ ProductAssociation addProductAssociation(Integer productId, Map input, String username) {
+ ProductAssociation productAssociation = new ProductAssociation()
+ input.productId = productId
+ applyValuesToDomain(input, productAssociation)
+ productAssociation.validate()
+ validateProductAssociation(productAssociation)
+ saveOrDiscardDomain(productAssociation, username)
+ }
+
+ /**
+ * Updates ProductAssociation
+ *
+ * @param partAssociationId
+ * @param input
+ * @param username
+ *
+ * @return productAssociation
+ */
+ ProductAssociation updateProductAssociation(Integer id, Map input, String username) {
+ ProductAssociation productAssociation = productAssociationService.getAssociation(id)
+ if (productAssociation) {
+ applyValuesToDomain(input, productAssociation)
+ productAssociation.validate()
+ saveOrDiscardDomain(productAssociation, username)
+ }
+ }
+
+ /**
+ * Deletes a productAssociation
+ *
+ * @param partAssociationId
+ * @param username
+ *
+ * @return productAssociation
+ */
+ ProductAssociation deleteProductAssociation(Integer id, String username) {
+ ProductAssociation productAssociation = productAssociationService.getAssociation(id)
+ if (productAssociation) {
+ softDelete(productAssociation, username)
+ }
+ productAssociation
+ }
+
+ private void validateProductAssociation(ProductAssociation productAssociation) {
+ if (ProductAssociation.findByProductIdAndRelatedProductIdAndAssociationTypeIdAndDateDeletedIsNull(productAssociation.productId,
+ productAssociation.relatedProductId, productAssociation.associationTypeId)) {
+ productAssociation.errors.reject('productAssociation.duplicate',
+ [productAssociation.productId, productAssociation.associationTypeId, productAssociation.relatedProductId] as Object[],
+ 'ProductAssociation for productId with associationTypeId and relatedProductId exists.')
+ }
+ }
+}
Index: grails-app/services/com/lemans/ds/product/ProductAssociationService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/product/ProductAssociationService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/product/ProductAssociationService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,55 @@
+package com.lemans.ds.product
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional
+class ProductAssociationService extends LemansService {
+
+ /**
+ * Find relatedProducts by productId
+ *
+ * @param criteria containing productId
+ *
+ * @return related products
+ */
+ Map findRelatedProducts(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'productId+ASC'
+ dqx(criteria).executeFrom('dbo.vwProductAssociation', clauses(criteria))
+ }
+
+ /**
+ * Find referralProducts by related ProductId
+ *
+ * @param criteria contaning related productId
+ *
+ * @return referral Products
+ */
+ Map findReferralProducts(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'relatedProductId+ASC'
+ dqx(criteria).executeFrom('dbo.vwProductAssociation', clauses(criteria))
+ }
+
+ /**
+ * Find ProductAssociation by ProductAssociationId
+ *
+ * @param criteria contaning productAssociationId
+ *
+ * @return productAssociation
+ */
+ Map findProductAssociation(Map criteria) {
+ dqx(criteria).executeOneFrom('dbo.vwProductAssociation', clauses(criteria))?.results[0]
+ }
+
+ private List clauses(Map criteria) {
+ List clauses = []
+ if (criteria.productId) { clauses << 'productId = :productId' }
+ if (criteria.relatedProductId) { clauses << 'relatedProductId = :relatedProductId' }
+ if (criteria.productAssociationId) { clauses << 'productAssociationId = :productAssociationId' }
+ clauses
+ }
+
+ ProductAssociation getAssociation(Integer id) {
+ ProductAssociation.findByIdAndDateDeletedIsNull(id)
+ }
+}
Index: grails-app/services/com/lemans/ds/product/ProductFeatureManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/product/ProductFeatureManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/product/ProductFeatureManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,150 @@
+package com.lemans.ds.product
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class ProductFeatureManagerService extends LemansManager {
+
+ static final String PRODUCT_FEATURE_SQL = 'EXEC dbo.spUpdateProductFeature @xml = ?, @appSecurityUser = ?'
+
+ static final String PRODUCT_FEATURE_MOVE_SQL = '''
+EXEC dbo.spMoveProductFeature
+ @sourceId = :productFeatureId,
+ @targetId = :targetId,
+ @sourceVersion = 0,
+ @targetVersion = 0,
+ @position = :position,
+ @appSecurityUser = :appSecurityUser
+'''
+
+ def messageSource
+
+ def productService
+
+ /**
+ * Create 1 or more ProductFeatures.
+ *
+ * @param values Map containing features and product info
+ * @param username
+ *
+ * @return Map containing messages which may contain errors and results with feature id's of Product Features created
+ */
+ Map createProductFeatures(Map values, String username) {
+ Integer productId = values.productId
+ List features = values.features.collect { Map featureData ->
+ ProductFeature feature = new ProductFeature(featureData)
+ feature.productId = productId
+ feature
+ }
+ List messages = validateFeatures(features, values)
+ Map data
+ if (messages) {
+ data = [messages: messages, results: []]
+ } else {
+ data = saveFeatures(values, username)
+ }
+ data
+ }
+
+ /**
+ * Updates a Product Feature.
+ *
+ * @param values Map containing catalog/product and feature info
+ * @param username
+ *
+ * @return ProductFeature which may contain errors or be null
+ */
+ ProductFeature updateProductFeature(Map values, String username) {
+ ProductFeature feature = findProductFeature(values.productId, values.productFeatureId)
+ if (feature) {
+ feature.properties = values
+ saveOrDiscardDomain(feature, username)
+ }
+ feature
+ }
+
+ /**
+ * Updates a Product Feature.
+ *
+ * @param values Map containing catalog/product and feature info
+ * @param username
+ *
+ * @return ProductFeatureLocale which may contain errors or be null
+ */
+ ProductFeatureLocale updateProductFeatureWithLocale(Map values, String username) {
+ ProductFeatureLocale productFeatureLocale = ProductFeatureLocale.
+ findByProductFeatureIdAndLocaleAndDateDeletedIsNull(values.productFeatureId, values.locale)
+ if (!productFeatureLocale) {
+ ProductFeature productFeature = ProductFeature.findById(values.productFeatureId)
+ if (productFeature) {
+ productFeatureLocale = new ProductFeatureLocale([productFeatureId: productFeature.id])
+ } else {
+ return
+ }
+ }
+ applyValuesToDomain(values, productFeatureLocale)
+ productFeatureLocale.validate()
+ saveOrDiscardDomain(productFeatureLocale, username)
+
+ }
+
+ /**
+ * Soft deletes a ProductFeature.
+ *
+ * @param productId
+ * @param productFeatureId
+ * @param username
+ *
+ * @return ProductFeature which may contain errors or be null
+ */
+ ProductFeature deleteProductFeature(Integer productId, Integer productFeatureId, String username) {
+ ProductFeature feature = findProductFeature(productId, productFeatureId)
+ if (feature) {
+ softDelete(feature, username)
+ }
+ feature
+ }
+
+ /**
+ * Changes the order of Features for a Product.
+ *
+ * @param values Map containing position and targetId
+ * @param username
+ *
+ * @return source ProductFeature
+ */
+ ProductFeature moveProductFeature(Map values, String username) {
+ ProductFeature feature = findProductFeature(values.productId, values.productFeatureId)
+ if (feature) {
+ sql().execute(values + [appSecurityUser: username], PRODUCT_FEATURE_MOVE_SQL)
+ }
+ feature
+ }
+
+ private ProductFeature findProductFeature(Integer productId, Integer productFeatureId) {
+ ProductFeature.findByProductIdAndIdAndDateDeletedIsNull(productId, productFeatureId)
+ }
+
+ private Map saveFeatures(Map values, String username) {
+ String xml = new ProductFeaturesXmlTransformer().toXml(values)
+ List data = sql().callWithAllRows(PRODUCT_FEATURE_SQL, [xml, username]) { }
+ [messages: data[0], results: data[1]]
+ }
+
+ private List validateFeatures(List features, Map values) {
+ List errors = []
+ if (values.catalogInstanceId == null) {
+ errors << [type: 'error', text: 'Catalog is required']
+ }
+ features.eachWithIndex { ProductFeature feature, int index ->
+ feature.validate()
+ if (feature.hasErrors()) {
+ errors += feature.errors.fieldErrors.collect { error ->
+ [type: 'error', field: error.field, text: messageSource.getMessage(error, Locale.default), index: index + 1]
+ }
+ }
+ }
+ errors
+ }
+}
Index: grails-app/services/com/lemans/ds/product/ProductFeatureService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/product/ProductFeatureService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/product/ProductFeatureService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,50 @@
+package com.lemans.ds.product
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional(readOnly = true)
+class ProductFeatureService extends LemansService {
+ /**
+ * Finds a Feature for a Product by id.
+ *
+ * @param criteria Map containing featureId (productFeatureId)
+ *
+ * @return Map containing Product Feature info
+ */
+ Map findProductFeatureById(Map criteria) {
+ dqx(criteria).executeOneFromLocale('dbo.vwProductFeature', product() + ['productFeatureId = :productFeatureId'])?.results[0]
+ }
+
+ /**
+ * Finds a FeatureLocale for a Product Feature by Locale id.
+ *
+ * @param criteria Map containing featureLocaleId (productFeatureLocaleId)
+ *
+ * @return Map containing Product Feature Locale info
+ */
+ Map findProductFeatureLocaleById(Map criteria) {
+ List clauses = ['productFeatureId = :productFeatureId', 'locale = :locale']
+ dqx(criteria).executeOneFrom('dbo.vwProductFeatureLocale', clauses)?.results[0]
+ }
+
+ /**
+ * Finds Features for a Product. Supports filtering by productId.
+ *
+ * @param criteria
+ *
+ * @return Map containing paginated Product Feature data
+ */
+ Map findProductFeatures(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'sequence+ASC'
+ dqx(criteria).executeFromLocale('dbo.vwProductFeature', clauses(criteria))
+ }
+
+ private List product() { ['catalogInstanceId = :catalogInstanceId', 'productId = :productId'] }
+
+ private List clauses(Map criteria) {
+ List clauses = []
+ if (criteria.productId != null) { clauses.addAll(product()) }
+ clauses
+ }
+}
Index: grails-app/services/com/lemans/ds/product/ProductManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/product/ProductManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/product/ProductManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,104 @@
+package com.lemans.ds.product
+
+import com.lemans.ds.media.Media
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class ProductManagerService extends LemansManager {
+
+ /**
+ * Creates a new Product.
+ *
+ * @param values
+ * @param username
+ *
+ * @return Product which may contain errors or be null
+ */
+ Product createProduct(Map values, String username) {
+ Product product = new Product(values)
+ validateProduct(values, product)
+ saveOrDiscardDomain(product, username)
+ }
+
+
+ /**
+ * Updates a Product.
+ *
+ * @param values
+ * @param catalogInstanceId
+ * @param productId
+ * @param username
+ *
+ * @return Product which may contain errors or be null
+ */
+ Product updateProduct(Map values, Integer catalogInstanceId, String username) {
+ Product product = findProduct(catalogInstanceId, values.productId)
+ if (product) {
+ applyValuesToDomain(values, product)
+ validateProduct(values, product)
+ saveOrDiscardDomain(product, username)
+ }
+ product
+
+ }
+
+ /**
+ * Updates a Product.
+ *
+ * @param values
+ * @param productLocaleId
+ * @param productId
+ * @param username
+ *
+ * @return ProductLocale which may contain errors or be null
+ */
+ ProductLocale updateProductWithLocale(Map values, Integer catalogInstanceId, String username) {
+ ProductLocale productLocale = ProductLocale.findByProductIdAndLocaleAndDateDeletedIsNull(values.productId, values.locale)
+ if (!productLocale) {
+ Product product = findProduct(catalogInstanceId, values.productId)
+ if (product) {
+ productLocale = new ProductLocale([productId: product.id])
+ } else {
+ return
+ }
+ }
+ applyValuesToDomain(values, productLocale)
+ productLocale.validate()
+ saveOrDiscardDomain(productLocale, username)
+ }
+
+ /**
+ * Soft deletes a Product.
+ *
+ * @param catalogInstanceId
+ * @param productId
+ * @param username
+ *
+ * @return Product which may contain errors or be null
+ */
+ Product deleteProduct(Integer catalogInstanceId, Integer productId, String username) {
+ Product product = findProduct(catalogInstanceId, productId)
+ if (product) { softDelete(product, username) }
+ product
+ }
+
+ /**
+ * Finds Product data by catalogInstanceId AND productId
+ * @param catalogInstanceId
+ * @param productId
+ *
+ * @return the Product or null
+ */
+ Product findProduct(Integer catalogInstanceId, Integer productId) {
+ Product.findByCatalogInstanceIdAndIdAndDateDeletedIsNull(catalogInstanceId, productId)
+ }
+
+
+ private void validateProduct(Map values, Product product) {
+ if (!product.hasErrors()) { product.validate() }
+ if (values.primaryMediaId != null && !findDomainObjectById(Media, values.primaryMediaId)) {
+ product.errors.rejectValue('primaryMediaId', 'invalid', "PrimaryMediaId with value [$values.primaryMediaId] does not exist")
+ }
+ }
+}
Index: grails-app/services/com/lemans/ds/product/ProductMerchandiseManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/product/ProductMerchandiseManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/product/ProductMerchandiseManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,38 @@
+package com.lemans.ds.product
+
+import com.lemans.ds.category.CategoryAttribute
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+import org.apache.commons.collections.CollectionUtils
+
+/**
+ * Created by MUmachi on 10/24/2017.
+ */
+@Transactional
+class ProductMerchandiseManagerService extends LemansManager {
+
+ private static final List FLAG_FIELDS = ['isHidden', 'isGroup', 'isDropdown', 'isKeyAttribute']
+
+ ProductCategoryAttribute updateProductCategoryAttribute(Map values, String username) {
+ ProductCategoryAttribute productCategoryAttribute = ProductCategoryAttribute.
+ findByProductIdAndCategoryAttributeIdAndDateDeletedIsNull(
+ values.productId, values.categoryAttributeId)
+ CategoryAttribute categoryAttribute = CategoryAttribute.findById(values.categoryAttributeId)
+ if (categoryAttribute) {
+ List keys = values.keySet() as String[]
+ if (CollectionUtils.containsAny(keys, FLAG_FIELDS)) {
+ FLAG_FIELDS.each { String flag ->
+ if (values."$flag" && categoryAttribute."$flag" == values."$flag") {
+ values."$flag" = null
+ }
+ }
+ }
+ }
+ if (!productCategoryAttribute) {
+ productCategoryAttribute = new ProductCategoryAttribute(values)
+ productCategoryAttribute.isSplit = values.isSplit ?: 0
+ }
+ applyValuesToDomain(values, productCategoryAttribute)
+ saveOrDiscardDomain(productCategoryAttribute, username)
+ }
+}
Index: grails-app/services/com/lemans/ds/product/ProductMerchandiseService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/product/ProductMerchandiseService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/product/ProductMerchandiseService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,45 @@
+package com.lemans.ds.product
+
+import com.lemans.services.LemansService
+
+/**
+ * Created by MUmachi on 10/13/2017.
+ */
+class ProductMerchandiseService extends LemansService {
+
+ private static final String PRODUCT_PART_DETAILS_SQL = 'SELECT dbo.fnGetProductPartAttributeDetailsJSON(?,?,?)'
+
+ private static final String PRODUCT_DETAILS_SQL = 'SELECT dbo.fnGetProductAttributeDetailsJSON(?,?)'
+
+ def productService
+
+
+ def findProductPartMerchandiseDetails(Map criteria) {
+ if (productService.findProductById(criteria)) {
+ queryForXmlClob(PRODUCT_PART_DETAILS_SQL, [criteria.productId, null, null])
+ } else {
+ null
+ }
+ }
+
+ Map findCategoryAttributeById(Map criteria) {
+ List clauses = ['productCategoryAttributeId = :productCategoryAttributeId']
+ dqx(criteria).executeOneFrom('dbo.ProductCategoryAttribute', clauses)?.results[0]
+ }
+
+ def findProductAttributeMerchandiseDetails(Map criteria) {
+ if (productService.findProductById(criteria)) {
+ queryForXmlClob(PRODUCT_DETAILS_SQL, [null, criteria.productId ])
+ } else {
+ null
+ }
+ }
+
+ def findProductAttributeMerchandiseLocaleDetails(Map criteria) {
+ if (productService.findProductById(criteria)) {
+ queryForXmlClob(PRODUCT_DETAILS_SQL, [criteria.locale, criteria.productId ])
+ } else {
+ null
+ }
+ }
+}
Index: grails-app/services/com/lemans/ds/product/ProductPartManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/product/ProductPartManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/product/ProductPartManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,55 @@
+package com.lemans.ds.product
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class ProductPartManagerService extends LemansManager {
+
+ private static final String PART_ATTRIBUTE_PERSIST_SQL =
+ '''EXEC spUpdateProductPart
+ @xml = ?,
+ @appSecurityUser = ?
+ '''
+ private static final String FLAG_PRODUCT_UPDATE_SQL =
+ '''EXEC spUpdateFlagProductAttribute
+ @xmlParameters = ?
+ '''
+
+ Map removeSingleProductPartRelation(Map values, String userName) {
+ values.operation = 'DELETE'
+ bulkAddOrRemove(values, userName)
+ }
+
+ Map addSingleProductPartRelation(Map values, String userName) {
+ values.operation = 'INSERT'
+ bulkAddOrRemove(values, userName)
+ }
+
+ Map bulkAddOrRemove(Map input, String userName) {
+ List errors = []
+ errors.addAll validateInput(input)
+ if (errors.isEmpty()) {
+ String inputXML = new ProductPartTransformer().generateProductPartXml(input)
+ List results = sql().callWithAllRows(PART_ATTRIBUTE_PERSIST_SQL, [inputXML, userName]) { }
+ if (results) {
+ processEntityFlag(input)
+ }
+ errors.addAll(results[0]?.text)
+ }
+ [errors: errors]
+ }
+
+ List validateInput(Map input) {
+ List errors = []
+ if (!input || (!input.partNumber && !input.partNumbers) || input.partNumbers?.isEmpty()) {
+ errors << 'No Part Selected'
+ }
+ errors
+ }
+
+ void processEntityFlag(Map input) {
+ String inputXML = new ProductPartTransformer().generateProductFlagXml(input)
+ sql().execute(FLAG_PRODUCT_UPDATE_SQL, [inputXML]) { }
+ }
+}
Index: grails-app/services/com/lemans/ds/product/ProductPartService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/product/ProductPartService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/product/ProductPartService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,65 @@
+package com.lemans.ds.product
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional
+class ProductPartService extends LemansService {
+
+ def productService
+
+ /**
+ * Finds a ProductPart by productId and PartNumber.
+ *
+ * @param criteria Map containing catalogInstanceId, productId and partNumber
+ *
+ * @return Map containing ProductPartInfo.
+ */
+ Map findProductPartByComposite(Map criteria) {
+ List clause = [
+ 'catalogInstanceId = :catalogInstanceId',
+ 'productId = :productId',
+ 'partNumber = :partNumber'
+ ]
+ dqx(criteria).executeOneFrom('dbo.vwProductPart', clause)?.results[0]
+ }
+
+
+ /**
+ * Finds a Products by PartNumber.
+ *
+ * @param criteria Map containing partNumber
+ *
+ * @return Map containing ProductPartInfo.
+ */
+ Map findProductByPartNumber(Map criteria) {
+ List clauses = []
+ List input = criteria.partNumber
+ List modifiedInput = input.collect { "\'$it\'" }
+ clauses << "partNumber IN (${modifiedInput.join(',')})"
+ Map results = dqx(criteria).executeFrom('dbo.vwProductPart', clauses)
+ results
+ }
+
+ /**
+ * Finds product part relation by product and catalogInstance
+ *
+ * @param criteria containing catalogInstanceId and productId
+ *
+ * @return Map containing paginated data
+ */
+ Map findProductParts(Map criteria) {
+ List clauses = [
+ 'catalogInstanceId = :catalogInstanceId',
+ 'productId = :productId'
+ ]
+ criteria.sorting = criteria.sorting ?: 'sequence+ASC'
+ Map results = dqx(criteria).executeFrom('dbo.vwProductPart', clauses)
+ //check if catalogInstanceId and productId found
+ if (results.results.isEmpty()) {
+ Map product = productService.findProductById(criteria)
+ return product ? results : product
+ }
+ results
+ }
+}
Index: grails-app/services/com/lemans/ds/product/ProductService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/product/ProductService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/product/ProductService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,202 @@
+package com.lemans.ds.product
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+import groovy.json.JsonSlurper
+import groovyx.gpars.GParsPool
+import org.json.XML
+
+import java.sql.SQLException
+
+
+@Transactional(readOnly = true)
+class ProductService extends LemansService {
+
+ def jobStatusManagerService
+
+ private static final String SPLIT_VALIDATION = 'Exec spSplitProductValidation @productId = ?, @bulkValidation = ?'
+ /**
+ * Finds a Product by id.
+ *
+ * @param criteria Map containing catalogInstanceId and productId
+ *
+ * @return Map containing Product info
+ */
+ Map findProductById(Map criteria) {
+ Map productData = dqx(criteria).executeOneFromLocale('dbo.vwProduct', catalog() + ['productId = :productId'])?.results[0]
+ if (productData && !localeExists(criteria)) { augmentProductsWithFlags([productData]) }
+ productData
+ }
+
+ /**
+ * Finds a Product by id.
+ *
+ * @param criteria Map containing catalogInstanceId and productId
+ *
+ * @return Map containing Product info
+ */
+ Map findProductLocaleById(Map criteria) {
+ List clauses = ['productId = :productId', 'locale = :locale']
+ dqx(criteria).executeOneFrom('dbo.vwProductLocale', clauses)?.results[0]
+ }
+
+ /**
+ * Finds Products for a Catalog. Supports filtering by brandCode and categoryId.
+ *
+ * @param criteria
+ *
+ * @return Map containing paginated data
+ */
+ Map findProducts(Map criteria) {
+ boolean localeExists = localeExists(criteria)
+ updateSorting(criteria, localeExists)
+ if (localeExists) {
+ dqx(criteria).executeFromLocale('dbo.vwProduct', clauses(criteria))
+ } else {
+ criteria.mode && criteria.flagId ? productsByFlagId(criteria) : products(criteria)
+ }
+ }
+
+ private void updateSorting(Map criteria, boolean localeExists) {
+ updateSortingBasedOnQ(criteria, localeExists)
+ if (criteria.productName) {
+ criteria.sorting = criteria.sorting ?: 'case when productName ' +
+ 'like ' + "'$criteria.productName%' then 1 when productName like + '%$criteria.productName' " +
+ "then 3 when productName like + '%$criteria.productName%' then 2 end+ASC"
+ }
+ if (localeExists && criteria.productNameLocale) {
+ criteria.sorting = criteria.sorting ?: 'case when productNameLocale ' +
+ 'like ' + "'$criteria.productNameLocale%' then 1 when productNameLocale like + '%$criteria.productNameLocale' " +
+ "then 3 when productNameLocale like + '%$criteria.productNameLocale%' then 2 end+ASC"
+ }
+ if (criteria.productId) {
+ criteria.sorting = criteria.sorting ?: 'case when productId ' +
+ 'like ' + "'$criteria.productId%' then 1 when productId like + '%$criteria.productId' " +
+ "then 3 when productId like + '%$criteria.productId%' then 2 end+ASC"
+ }
+ criteria.sorting = criteria.sorting ?: localeExists ? 'productNameLocale+ASC' : 'productName+ASC'
+ }
+
+ private void updateSortingBasedOnQ(Map criteria, boolean localeExists) {
+ if (criteria.q) {
+ if (localeExists) {
+ criteria.sorting = criteria.sorting ?: 'case when productNameLocale ' +
+ 'like ' + "'$criteria.q%' then 1 when productNameLocale like + '%$criteria.q' " +
+ "then 3 when productNameLocale like + '%$criteria.q%' then 2 end+ASC"
+ } else {
+ criteria.sorting = criteria.sorting ?: 'case when productName ' +
+ 'like ' + "'$criteria.q%' then 1 when productName like + '%$criteria.q' " +
+ "then 3 when productName like + '%$criteria.q%' then 2 end+ASC"
+ }
+ }
+ }
+
+ private Map products(Map criteria) {
+ Map products = dqx(criteria).executeFrom('dbo.vwProduct', clauses(criteria))
+ if (criteria.mode) { augmentProductsWithFlags(products.results) }
+ products
+ }
+
+ private Map productsByFlagId(Map criteria) {
+ Map products = dqx(criteria).executeFromWithOutAlias(
+ '''vwProduct
+ INNER JOIN vwFlagValue
+ ON vwProduct.productId = vwFlagValue.entityId
+ ''',
+ clauses(criteria) + ["vwFlagValue.entityClass = 'Product'", 'vwFlagValue.flagId = :flagId']
+ )
+ augmentProductsWithFlags(products.results)
+ products
+ }
+
+ private List catalog() { ['catalogInstanceId = :catalogInstanceId'] }
+
+ private List clauses(Map criteria) {
+ List clauses = []
+ if (criteria.brandCode != null) { clauses << 'brandCode = :brandCode' }
+ if (criteria.brandId != null) { clauses << 'brandId = :brandId' }
+ if (criteria.isDigiActive != null) { clauses << 'isDigiActive = :isDigiActive' }
+ if (criteria.categoryId != null) { clauses << "categoryId IN (${criteria.categoryId.join(',')})" }
+ if (criteria.productId != null) { clauses << 'productId = :productId' }
+ clauses.addAll wildcardClauses(criteria, [localeName(criteria, 'productName')])
+ if (criteria.q) {
+ clauses.addAll qClauses(criteria)
+ }
+ clauses
+ }
+
+ private List qClauses(criteria) {
+ List clauses = []
+ if (criteria.q) {
+ expandQ(criteria)
+ if (localeExists(criteria)) {
+ String clause = '''
+ productId LIKE :beginningWithQ
+ OR productId LIKE :containingQ
+ OR productNameLocale LIKE :beginningWithQ
+ OR productNameLocale LIKE :containingQ'''
+ clauses << clause
+ } else {
+ String clause = '''
+ productId LIKE :beginningWithQ
+ OR productId LIKE :containingQ
+ OR productName LIKE :beginningWithQ
+ OR productName LIKE :containingQ'''
+ clauses << clause
+ }
+ }
+ clauses
+ }
+
+ private expandQ(criteria) {
+ criteria.with {
+ beginningWithQ = "$q%".toString()
+ containingQ = "%$q%".toString()
+ }
+ }
+
+ private void augmentProductsWithFlags(List products) {
+ if (products) {
+ String query = """
+ SELECT * FROM dbo.vwflagValue fv
+ WHERE fv.entityClass = 'Product'
+ AND fv.entityId IN (${products*.productId.join(',')})
+ """
+ Map flagsByProductId = sql().rows(query).groupBy { it.entityId }
+ products.each { it.flag = flagsByProductId[it.productId.toString()] }
+ }
+ }
+
+ private String localeName(Map criteria, String name) {
+ name + (localeExists(criteria) ? 'Locale' : '')
+ }
+
+ private boolean localeExists(Map criteria) {
+ criteria.locale && criteria.locale != Locale.default.toString()
+ }
+
+ Map splitValidationForProduct(Integer productId) {
+ String xmlResults = queryForXmlClob(SPLIT_VALIDATION, [productId, 0])
+ if (xmlResults) {
+ String validatedResults = XML.toJSONObject(xmlResults)
+ [results: new JsonSlurper().parseText(validatedResults)]
+ } else {
+ [results: [:]]
+ }
+ }
+
+ void bulkSplitValidation() {
+ try {
+ GParsPool.withPool(5) {
+ sql().rows('SELECT productId FROM product WITH(NOLOCK) WHERE dateDeleted IS NULL')?.productId?.eachParallel { Integer productId ->
+ Product.withNewTransaction {
+ sql().execute(SPLIT_VALIDATION, [productId, 1])
+ }
+ }
+ }
+ } catch (SQLException e) {
+ log.error e.message
+ jobStatusManagerService.updateEndDate()
+ }
+ }
+ }
Index: grails-app/services/com/lemans/ds/publicationcategory/ProductPublicationCategoryManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/publicationcategory/ProductPublicationCategoryManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/publicationcategory/ProductPublicationCategoryManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,51 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class ProductPublicationCategoryManagerService extends LemansManager {
+
+ def productPublicationCategoryService
+
+ /**
+ * Create a new ProductPublicationCategory
+ *
+ * @param input
+ * @param publicationCategoryId
+ * @param username
+ *
+ * @return ProductPublicationCategory
+ */
+ ProductPublicationCategory add(Map input, Integer publicationCategoryId, String username) {
+ input.categoryId = publicationCategoryId
+ ProductPublicationCategory productPublicationCategory = productPublicationCategoryService.findProductPublicationCategory(publicationCategoryId, input.productId)
+ if (productPublicationCategory) {
+ productPublicationCategory.errors.reject('productPublicationCategory.duplicate', [productPublicationCategory.productId] as Object[],
+ 'Product has already been assigned')
+ } else {
+ productPublicationCategory = new ProductPublicationCategory()
+ applyValuesToDomain(input, productPublicationCategory)
+ productPublicationCategory.validate()
+ saveOrDiscardDomain(productPublicationCategory, username)
+ }
+ productPublicationCategory
+ }
+
+ /**
+ * Delete a productPublicationCategory by productId and publicationCategoryId
+ *
+ * @param input
+ * @param publicationCategoryId
+ * @param username
+ *
+ * @return ProductPublicationCategory
+ */
+ ProductPublicationCategory deleteProductPublicationCategory(Integer categoryId, Integer productId, String username) {
+ ProductPublicationCategory productPublicationCategory = productPublicationCategoryService.findProductPublicationCategory(categoryId, productId)
+ if (productPublicationCategory) {
+ softDelete(productPublicationCategory, username)
+ }
+ productPublicationCategory
+ }
+}
Index: grails-app/services/com/lemans/ds/publicationcategory/ProductPublicationCategoryService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/publicationcategory/ProductPublicationCategoryService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/publicationcategory/ProductPublicationCategoryService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,47 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional
+class ProductPublicationCategoryService extends LemansService {
+
+ /**
+ * Find all ProductPublicationCategories by categoryId
+ *
+ * @param criteria
+ *
+ * @return ProductPublicationCategories data
+ */
+ Map productPublicationCategoriesByCategoryId(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'categoryId+ASC'
+ dqx(criteria).executeFrom('vwProductPublicationCategory', ['categoryId = :categoryId', 'isDigiActive = :isDigiActive'])
+ }
+
+ /**
+ * Find productPublicationCategory by id
+ *
+ * @param id (productPublicationCategoryId)
+ *
+ * @return ProductPublicationCategory data
+ */
+ Map productPublicationCategory(Integer id) {
+ dqx([id: id]).executeOneFrom('vwProductPublicationCategory', ['productPublicationCategoryId = :id'])?.results[0]
+ }
+
+ /**
+ * Find productPublicationCategories by productId
+ *
+ * @param criteria
+ *
+ * @return ProductPublicationCategories data
+ */
+ Map productPublicationCategoriesByProductId(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'categoryId+ASC'
+ dqx(criteria).executeFrom('vwProductPublicationCategory', ['productId = :productId', 'isDigiActive = :isDigiActive'])
+ }
+
+ ProductPublicationCategory findProductPublicationCategory(Integer categoryId, Integer productId) {
+ ProductPublicationCategory.findByCategoryIdAndProductIdAndDateDeletedIsNull(categoryId, productId)
+ }
+}
Index: grails-app/services/com/lemans/ds/publicationcategory/PublicationCategoryAttributeManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/publicationcategory/PublicationCategoryAttributeManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/publicationcategory/PublicationCategoryAttributeManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,100 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.FeatureToggles
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class PublicationCategoryAttributeManagerService extends LemansManager {
+
+ private static final int FORCED_OPT_LOCKING_VERSION = -1
+
+ private static final String SEQUENCING = '''
+ EXEC dbo.spMovePublicationCategoryAttribute
+ @sourceId = :id,
+ @targetId = :targetId,
+ @sourceVersion = :sourceVersion,
+ @targetVersion = :targetVersion,
+ @position = :position,
+ @appSecurityUser = :appSecurityUser
+ '''
+ /**
+ * Add PublicationCategoryAttribute
+ * @param input
+ * @param username
+ * @return PublicationCategoryAttribute Object
+ */
+ PublicationCategoryAttribute addAttribute(Map input, String username) {
+ PublicationCategoryAttribute publicationCategoryAttribute = new PublicationCategoryAttribute()
+ applyValuesToDomain(input, publicationCategoryAttribute)
+ publicationCategoryAttribute.validate()
+ validatePublicationCategory(input.publicationCategoryId, publicationCategoryAttribute)
+ validatePublicationCategoryAttribute(input.publicationCategoryId, input.attributeNameId, publicationCategoryAttribute)
+ saveOrDiscardDomain(publicationCategoryAttribute, username)
+ flushAndSequence(publicationCategoryAttribute, username)
+ publicationCategoryAttribute
+ }
+
+ /**
+ * Delete PublicationCategoryAttribute
+ * @param input
+ * @param username
+ * @return Soft deleted PublicationCategoryAttribute Object
+ */
+ PublicationCategoryAttribute deleteAttribute(Integer publicationCategoryId, Integer attributeNameId, String username) {
+ PublicationCategoryAttribute publicationCategoryAttribute = findPublicationCategoryAttribute(publicationCategoryId, attributeNameId)
+ if (publicationCategoryAttribute) {
+ softDelete(publicationCategoryAttribute, username)
+ }
+ publicationCategoryAttribute
+ }
+
+ /**
+ * Sequence reset of publicationCategoryAttribute
+ * @param input
+ * @param username
+ * @return PublicationCategoryAttribute Object
+ */
+ PublicationCategoryAttribute moveAttribute(Map values, String username) {
+ PublicationCategoryAttribute source = findPublicationCategoryAttribute(values.publicationCategoryId, values.attributeNameId)
+ values.attributeNameId = values.targetId
+ PublicationCategoryAttribute target = findPublicationCategoryAttribute(values.publicationCategoryId, values.attributeNameId)
+ if (source && target) {
+ if (FeatureToggles.OPT_LOCKING_FEATURE_TOGGLE) {
+ checkForStaleObject(PublicationCategoryAttribute, 'PublicationCategoryAttribute', values.sourceVersion ?: FORCED_OPT_LOCKING_VERSION)
+ }
+ values.id = source.id
+ values.targetId = target.id
+ sequence(values + [appSecurityUser: username])
+ source
+ }
+ }
+
+ private validatePublicationCategory(Integer publicationCategoryId, PublicationCategoryAttribute publicationCategoryAttribute) {
+ PublicationCategory publicationCategory = PublicationCategory.findByIdAndDateDeletedIsNull(publicationCategoryId)
+ if (!publicationCategory) {
+ publicationCategoryAttribute.errors.reject('publicationCategory.invalid', 'Invalid publication Category')
+ }
+ }
+
+ private validatePublicationCategoryAttribute(Integer publicationCategoryId, Integer attributeNameId, PublicationCategoryAttribute publicationCategoryAttribute) {
+ if (findPublicationCategoryAttribute(publicationCategoryId, attributeNameId)) {
+ publicationCategoryAttribute.errors.reject('publicationCategoryAttribute.duplicate', 'Duplicate publicationCategoryAttribute')
+ }
+ }
+
+ private PublicationCategoryAttribute findPublicationCategoryAttribute(Integer publicationCategoryId, Integer attributeNameId) {
+ PublicationCategoryAttribute.findByPublicationCategoryIdAndAttributeNameIdAndDateDeletedIsNull(publicationCategoryId, attributeNameId)
+ }
+
+ private flushAndSequence(PublicationCategoryAttribute publicationCategoryAttribute, String username) {
+ if (!publicationCategoryAttribute.hasErrors()) {
+ publicationCategoryAttribute.save(flush: true)
+ sequence([id: publicationCategoryAttribute.id, appSecurityUser: username])
+ }
+ }
+
+ private void sequence(Map input) {
+ sql().execute(input, SEQUENCING)
+ }
+}
Index: grails-app/services/com/lemans/ds/publicationcategory/PublicationCategoryAttributeService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/publicationcategory/PublicationCategoryAttributeService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/publicationcategory/PublicationCategoryAttributeService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,50 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional
+class PublicationCategoryAttributeService extends LemansService {
+
+ /**
+ * Find all publicationCategoryAttributes
+ * @param id (publicationCategoryId)
+ * @return Map of publicationCategoryAttributes
+ */
+ Map findAllAttributes(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'publicationCategoryId+ASC'
+ dqx(criteria).executeFrom('vwPublicationCategoryAttribute', ['publicationCategoryId = :publicationCategoryId'])
+ }
+
+ /**
+ * Find possible attributeOptions for a publicationCategory
+ * @param id (PublicationCategoryId)
+ * @return Map of results(AttributeNameIds)
+ */
+ Map attributeOptions(Integer id) {
+ List data = sql().callWithAllRows('EXEC dbo.spGetPublicationCategoryAttributes @categoryId = ?', [id]) { }
+ [results: data[0], totalRecords: data[1][0].TotalRecords]
+ }
+
+ /**
+ * Find publicationCategoryId
+ * @param publicationCategoryId
+ * @param attributeNameId
+ * @return Map of publicationCategoryId
+ */
+ Map findPublicationCategoryAttribute(Integer publicationCategoryId, Integer attributeNameId) {
+ List clauses = ['publicationCategoryId = :publicationCategoryId', 'attributeNameId = :attributeNameId']
+ Map criteria = [publicationCategoryId: publicationCategoryId, attributeNameId: attributeNameId]
+ dqx(criteria).executeOneFrom('vwPublicationCategoryAttribute', clauses)?.results[0]
+ }
+
+ /**
+ * Find publicationCategoryAttribute
+ * @param id (publicationCategoryAttributeId)
+ * @return Map of publicationCategoryAttribute
+ */
+ Map findById(Integer id) {
+ dqx([publicationCategoryAttributeId: id]).executeOneFrom('vwPublicationCategoryAttribute',
+ ['publicationCategoryAttributeId = :publicationCategoryAttributeId'])?.results[0]
+ }
+}
Index: grails-app/services/com/lemans/ds/publicationcategory/PublicationCategoryManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/publicationcategory/PublicationCategoryManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/publicationcategory/PublicationCategoryManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,189 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.FeatureToggles
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class PublicationCategoryManagerService extends LemansManager {
+
+ private static final int FORCED_OPT_LOCKING_VERSION = -1
+
+ /**
+ * Create a Publication Category
+ *
+ * @param input
+ * @param username
+ *
+ * @return Publication Category
+ */
+ PublicationCategory createCategory(Map input, String username) {
+ PublicationCategory publicationCategory = new PublicationCategory()
+ applyValuesToDomain(input, publicationCategory)
+ publicationCategory.sequence = PublicationCategory.MAX_DIRECT_CHILDREN
+ publicationCategory.validate()
+ saveOrDiscardDomain(publicationCategory, username)
+ flushAndSequence(publicationCategory)
+ publicationCategory
+ }
+
+ /**
+ * Update a Publication Category
+ *
+ * @param input
+ * @param id
+ * @param username
+ *
+ * @return Publication Category
+ */
+ PublicationCategory updateCategory(Map input, Integer id, String username) {
+ PublicationCategory publicationCategory = findCategory(id)
+ if (publicationCategory) {
+ applyValuesToDomain(input, publicationCategory)
+ if (FeatureToggles.OPT_LOCKING_FEATURE_TOGGLE) {
+ checkForStaleObject(publicationCategory, 'PublicationCategory', input.version ?: FORCED_OPT_LOCKING_VERSION)
+ }
+ saveOrDiscardDomain(publicationCategory, username)
+ flushAndSequence(publicationCategory)
+ }
+ publicationCategory
+ }
+
+ /**
+ * Move the position of a Publication Category
+ *
+ * @param input
+ * @param id (categoryId)
+ * @param username
+ *
+ * @return Publication Category
+ */
+ PublicationCategory movePublicationCategory(Map input, Integer id, String username) {
+ PublicationCategory publicationCategory = findCategory(id)
+ if (publicationCategory) {
+ if (FeatureToggles.OPT_LOCKING_FEATURE_TOGGLE) {
+ checkForStaleObject(publicationCategory, 'PublicationCategory', input.version ?: FORCED_OPT_LOCKING_VERSION)
+ }
+ PublicationCategory targetPublicationCategory = findCategory(input.targetId)
+ if (!targetPublicationCategory) { return null }
+ checkTreeConstraints(publicationCategory, targetPublicationCategory.id)
+ if (input.position == 'INSIDE') {
+ if (!publicationCategory.hasErrors()) {
+ publicationCategory.parentCategoryId = input.targetId
+ publicationCategory.sequence = 1
+ }
+ } else {
+ if (!publicationCategory.hasErrors()) {
+ publicationCategory.parentCategoryId = targetPublicationCategory.parentCategoryId
+ publicationCategory.sequence = input.position == 'AFTER' ? targetPublicationCategory.sequence + 1 : targetPublicationCategory.sequence
+ }
+ }
+ saveOrDiscardDomain(publicationCategory, username)
+ flushAndSequence(publicationCategory)
+ publicationCategory
+ }
+ }
+
+ /**
+ * Delete a publication Category
+ *
+ * @param id (categoryId)
+ * @param version
+ * @param username
+ *
+ * @return Publication Category
+ */
+ PublicationCategory deleteCategory(Integer id, Integer version, String username) {
+ PublicationCategory publicationCategory = findCategory(id)
+ if (publicationCategory) {
+ if (FeatureToggles.OPT_LOCKING_FEATURE_TOGGLE) {
+ checkForStaleObject(publicationCategory, 'publicationCategory', version ?: FORCED_OPT_LOCKING_VERSION)
+ }
+ if (children(publicationCategory)) {
+ publicationCategory.errors.reject('publicationCategory.notDeletable', ['PublicationCategory'] as Object[],
+ 'Publication Category may not be deleted')
+ } else {
+ softDelete(publicationCategory, username)
+ flushAndSequence(publicationCategory)
+ }
+ }
+ publicationCategory
+ }
+
+ void sequence(PublicationCategory publicationCategory) {
+ PublicationCategory.withTransaction {
+ sql().execute("""
+ UPDATE publicationcategory
+ SET sequence = ${publicationCategory.sequence + 1}
+ WHERE
+ ISNULL(parentCategoryId, -1) = ISNULL(${publicationCategory.parentCategoryId},-1) AND
+ sequence >= ${publicationCategory.sequence} AND
+ categoryId <> ${publicationCategory.id} AND
+ dateDeleted is NULL
+ """)
+ resetSequence()
+ }
+ }
+
+ void resetSequence() {
+ sql().execute('''
+ UPDATE c
+ SET sequence = a.sequence
+ FROM publicationCategory c
+ INNER JOIN (
+ SELECT c.categoryId, sequence = ROW_NUMBER() OVER (PARTITION BY parentCategoryId ORDER BY sequence, categoryName)
+ FROM publicationCategory c
+ WHERE c.dateDeleted IS NULL
+ ) a
+ ON a.categoryId = c.categoryId
+ AND a.sequence <> c.sequence
+ WHERE c.dateDeleted IS NULL
+ ''')
+ }
+
+ private PublicationCategory findCategory(Integer id) {
+ PublicationCategory.findByIdAndDateDeletedIsNull(id)
+ }
+
+ private List children(PublicationCategory publicationCategory) {
+ PublicationCategory.findAllByParentCategoryIdAndDateDeletedIsNull(publicationCategory.id)
+ }
+
+ private void flushAndSequence(PublicationCategory publicationCategory) {
+ if (publicationCategory && !publicationCategory.hasErrors()) {
+ publicationCategory.save(flush: true)
+ sequence(publicationCategory)
+ }
+ }
+
+ private void checkTreeConstraints(PublicationCategory publicationCategory, Integer targetId) {
+ List parentIds = sql().rows("""
+ WITH ParentsCTE
+ AS (
+ SELECT c1.categoryId
+ ,c1.parentCategoryId
+ FROM [PartsSource_DS].[dbo].PublicationCategory c1
+ WHERE c1.dateDeleted IS NULL
+ AND c1.categoryId = $targetId
+
+ UNION ALL
+
+ SELECT c2.categoryId
+ ,c2.parentCategoryId
+ FROM [PartsSource_DS].[dbo].PublicationCategory c2
+ INNER JOIN ParentsCTE c ON c.parentCategoryId = c2.categoryId
+ WHERE c2.dateDeleted IS NULL
+ )
+ SELECT parentCategoryId
+ FROM ParentsCTE
+ WHERE parentCategoryId IS NOT NULL;
+ """)*.parentCategoryId
+ if (publicationCategory.id in parentIds) { rejectCycle(publicationCategory) }
+ }
+
+ private rejectCycle(PublicationCategory publicationCategory) {
+ publicationCategory.errors.reject('publicationCategory.cyclic', ['PublicationCategory'] as Object[],
+ 'PublicationCategory may not be moved to create a cycle')
+
+ }
+}
Index: grails-app/services/com/lemans/ds/publicationcategory/PublicationCategoryService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/publicationcategory/PublicationCategoryService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/publicationcategory/PublicationCategoryService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,31 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional
+class PublicationCategoryService extends LemansService {
+
+ /**
+ * Find Publication Categories
+ *
+ * @param criteria
+ *
+ * @return Publication Categories data
+ */
+ Map findPublicationCategories(Map criteria) {
+ criteria.sorting = criteria.sorting ?: 'categoryId+ASC'
+ dqx(criteria).executeFrom('dbo.vwPublicationCategory')
+ }
+
+ /**
+ * Find Publication Category by id
+ *
+ * @param id (categoryId)
+ *
+ * @return Publication Category data
+ */
+ Map findPublicationCategoryById(Integer id) {
+ dqx([id: id]).executeOneFrom('dbo.vwPublicationCategory', ['categoryId = :id'])?.results[0]
+ }
+}
Index: grails-app/services/com/lemans/ds/rabbit/RabbitMessageService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/rabbit/RabbitMessageService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/rabbit/RabbitMessageService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,18 @@
+package com.lemans.ds.rabbit
+
+class RabbitMessageService {
+
+ def rabbitMessagePublisher
+
+ def usToEuSyncExchange
+
+ def usToEuSyncBinding
+
+ void publishToUsTOEuQueue(RequestPayload payload) {
+ rabbitMessagePublisher.send {
+ exchange = usToEuSyncExchange
+ routingKey = usToEuSyncBinding
+ body = [requestType: payload.requestType, id: payload.id, body: payload.body]
+ }
+ }
+}
Index: grails-app/services/com/lemans/ds/rabbit/RequestPayload.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/rabbit/RequestPayload.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/rabbit/RequestPayload.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,8 @@
+package com.lemans.ds.rabbit
+
+class RequestPayload {
+
+ QueueRequestType requestType
+ Integer id
+ Object body
+}
Index: grails-app/services/com/lemans/ds/search/BrandSearchService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/search/BrandSearchService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/search/BrandSearchService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,46 @@
+package com.lemans.ds.search
+
+import com.lemans.services.LemansService
+import grails.transaction.Transactional
+
+@Transactional(readOnly = true)
+class BrandSearchService extends LemansService {
+
+ private static final String BRAND_SELECT = 'SELECT x.brandId, x.brandCode, RTRIM(x.brandName) AS brandName'
+ /**
+ * Searches for Brands supporting * wildcards.
+ *
+ * @param criteria containing brandCode or brandName
+ *
+ * @return Map with List of results
+ */
+ Map searchBrands(Map criteria) {
+ if (criteria.query) {
+ criteria.q = formatWildCardSearch(criteria.query)
+ }
+ Map results = dqx(criteria).executeSelectFrom(BRAND_SELECT, 'FROM dbo.Brand x', clauses(criteria))
+ results
+ }
+
+ private String formatWildCardSearch(String query) {
+ String q = query
+ if (q) {
+ if (q.contains('*')) {
+ q.replace('*', '%')
+ } else {
+ "${q}%"
+ }
+ }
+ }
+
+ private List clauses(Map criteria) {
+ List clauses = []
+
+ if (criteria.q) {
+ clauses << '(x.brandCode LIKE :q OR x.brandName LIKE :q)'
+ }
+
+ clauses
+
+ }
+}
Index: grails-app/services/com/lemans/ds/search/GenericSearchService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/search/GenericSearchService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/search/GenericSearchService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,87 @@
+package com.lemans.ds.search
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+@Transactional
+class GenericSearchService extends LemansManager {
+
+ private static final String FILTER_SQL = 'EXEC dbo.spResponsiveSearchFilters @filterType = ?, @selectedParameters = ?'
+
+ private static final String FILTER_OPTIONS_SQL = 'EXEC dbo.spResponsiveSearchFilters_V1 @filterType = ?, @selectedParameters = ?'
+
+ private static final String FILTER_RESULTS_SQL = 'EXEC dbo.spResponsiveSearchResults @filterType = ?, @selectedParameters = ?, ' +
+ '@offset = ?, @pagesize = ?, @sortBy = ?, @mode = ?'
+
+ private final FilterXmlGenerator filterXmlGenerator = new FilterXmlGenerator()
+
+ /**
+ * Returns Filters and their values.
+ *
+ * @param searchType
+ * @param filterType
+ * @param criteria Map
+ *
+ * @return Map containing results and totalRecords
+ */
+ Map filterOptions(String searchType, String filterType, Map criteria) {
+ String xml = filterXmlGenerator.generateXml(searchType, filterType, criteria)
+ List data = sql().callWithAllRows(FILTER_OPTIONS_SQL, [filterType, xml]) { }
+ [results: data ? data[0] : [], totalRecords: data ? data[0].size() : 0]
+ }
+
+ /**
+ * Returns Filters and their values.
+ * @param searchType
+ * @param filterType
+ * @param criteria Map
+ * @return XML containing filter results
+ */
+ String filters(String searchType, String filterType, Map criteria) {
+ String xml = filterXmlGenerator.generateXml(searchType, filterType, criteria)
+ queryForXml(FILTER_SQL, [filterType, xml])
+ }
+
+ /**
+ * Returns Results with totalRecords.
+ *
+ * @param searchType
+ *
+ * @param filterType
+ *
+ * @param criteria Map
+ *
+ * @return Map containing results and totalRecords
+ */
+ Map searchResults(String searchType, String filterType, Map criteria) {
+ List data = genericSearchResults(searchType, filterType, criteria)
+ [results: data ? data[0] : [], totalRecords: data ? (data[1][0]?.totalRecords ?: 0) : 0]
+ }
+
+ /**
+ * Returns Results with totalRecords.
+ *
+ * @param searchType
+ *
+ * @param filterType
+ *
+ * @param criteria Map
+ *
+ * @return Map containing resultXml and totalRecords
+ */
+ Map searchResultsByMode(String searchType, String filterType, Map criteria) {
+ List data = genericSearchResults(searchType, filterType, criteria)
+ Integer totalRecords = data ? data[1][0].totalRecords : 0
+ [resultXML: totalRecords ? data[0][0][0].characterStream.text : '', totalRecords: totalRecords]
+ }
+
+ private List genericSearchResults(String searchType, String filterType, Map criteria) {
+ List parameters = parameters(searchType, filterType, criteria)
+ sql().callWithAllRows(FILTER_RESULTS_SQL, parameters) { }
+ }
+
+ private List parameters(String searchType, String filterType, Map criteria) {
+ String xml = filterXmlGenerator.generateXml(searchType, filterType, criteria)
+ [filterType, xml, criteria.offset ?: 0, criteria.pageSize ?: 25, criteria.sorting ?: null, criteria.mode]
+ }
+}
Index: grails-app/services/com/lemans/ds/splitvalidation/JobStatusManagerService.groovy
===================================================================
diff -u
--- grails-app/services/com/lemans/ds/splitvalidation/JobStatusManagerService.groovy (revision 0)
+++ grails-app/services/com/lemans/ds/splitvalidation/JobStatusManagerService.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,49 @@
+package com.lemans.ds.splitvalidation
+
+import com.lemans.services.LemansManager
+import grails.transaction.Transactional
+
+import java.sql.Timestamp
+
+@Transactional
+class JobStatusManagerService extends LemansManager {
+
+ def productService
+
+ def execute() {
+ if (jobStatus()) {
+ updateStartDate()
+ productService.bulkSplitValidation()
+ updateEndDate()
+ }
+ }
+
+ Map jobStatus() {
+ JobStatus.withNewTransaction {
+ Map jobStatus = sql().firstRow('SELECT * FROM jobStatus WITH(NOLOCK) WHERE jobStatusId = 1 AND dateDeleted IS NULL')
+ jobStatus
+ }
+ }
+
+ void updateStartDate() {
+ JobStatus.withNewTransaction {
+ sql().execute("""
+ UPDATE jobStatus SET
+ startDate = ${ new Timestamp(new Date().time) },
+ lastUpdated = ${ new Timestamp(new Date().time) }
+ WHERE jobStatusId = 1
+ """)
+ }
+ }
+
+ void updateEndDate() {
+ JobStatus.withNewTransaction {
+ sql().execute("""
+ UPDATE jobStatus SET
+ endDate = ${ new Timestamp(new Date().time) },
+ lastUpdated = ${ new Timestamp(new Date().time) }
+ WHERE jobStatusId = 1
+ """)
+ }
+ }
+}
Index: grails-app/views/index.gson
===================================================================
diff -u
--- grails-app/views/index.gson (revision 0)
+++ grails-app/views/index.gson (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,3 @@
+json {
+ results 'ds-service'
+}
\ No newline at end of file
Index: src/integration-test/groovy/com/lemans/common/SubComCodeFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/common/SubComCodeFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/common/SubComCodeFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,51 @@
+package com.lemans.common
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class SubComCodeFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'subComCode' }
+
+ final static int ALL_COLUMNS_SIZE = 13
+
+ def 'can NOT find a SubComCode that does not exist'() {
+ given:
+ path('-1')
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find a SubComCode'() {
+ given:
+ String subComCode = '0101'
+ path(subComCode)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.subComCode == subComCode
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can find all SubComCodes'() {
+ given:
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.subComCode.every { it != null }
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ payload.results.size() > 350
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/CatalogTestFixture.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/CatalogTestFixture.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/CatalogTestFixture.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,10 @@
+package com.lemans.ds
+
+import com.lemans.ds.product.Product
+
+trait CatalogTestFixture {
+
+ Integer catalogInstanceId = Product.VIRTUAL_CATALOG_ID
+
+ Map catalog = [catalog: catalogInstanceId]
+}
Index: src/integration-test/groovy/com/lemans/ds/attribute/AttributeCategoryFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/attribute/AttributeCategoryFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/attribute/AttributeCategoryFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,85 @@
+package com.lemans.ds.attribute
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class AttributeCategoryFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { '' }
+
+ final static int ALL_COLUMNS_SIZE = 22
+
+ def 'can get all categories filtered by attribute Name'() {
+ given:
+ path('attributeName/1081/category')
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can get all categories filtered by attribute Name and category'() {
+ given:
+ path('attributeName/1081/category')
+ queryParams.categoryId = 592
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT find category attributes with out attributeNameId'() {
+ given:
+ path('attributeName/null/category')
+ invalid()
+
+ when:
+ get()
+
+ then:
+ payload.messages[0].text == 'attributeNameId is required'
+ }
+
+ def 'can get all categories filtered by attribute Value'() {
+ given:
+ path('attributeValue/101059/category')
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results[0].size() == 20
+ }
+
+ def 'can find all categories filtered by attribute Value and category'() {
+ given:
+ path('attributeValue/101059/category')
+ queryParams.categoryId = 6917
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results[0].size() == 20
+ }
+
+ def 'can NOT find category attributes with out attributeValueId'() {
+ given:
+ path('attributeValue/null/category')
+ invalid()
+
+ when:
+ get()
+
+ then:
+ payload.messages[0].text == 'attributeValueId is required'
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/attribute/AttributeNameFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/attribute/AttributeNameFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/attribute/AttributeNameFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,202 @@
+package com.lemans.ds.attribute
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+import spock.lang.Stepwise
+
+@Stepwise
+class AttributeNameFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'attributeName' }
+
+ final static int ALL_COLUMNS_SIZE = 13
+
+ @Shared
+ Integer currentAttributeNameId
+
+ def 'can not create attributeName without blank attributeName field'() {
+ given:
+ String json = '{ "attributeName": "" }'
+ path()
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'attributeName'
+ text.contains 'attributeName is required'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can not create duplicate attributeName'() {
+ given:
+ String attributeName = 'Size'
+ String json = """{ "attributeName": "$attributeName" }"""
+ path()
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ code == 'DUPLICATE_KEY'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can create valid attributeName '() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeName = "$key TestCreate"
+ String json = """{ "attributeName": "$attributeName" }"""
+ path()
+ ok()
+
+ when:
+ post(json)
+ currentAttributeNameId = payload.results.attributeNameId
+ then:
+ payload.results.attributeName == attributeName
+ payload.results.size() == ALL_COLUMNS_SIZE
+
+ }
+
+
+ def 'can NOT update attributeName with invalid attributeNameId '() {
+ given:
+ Integer attributeNameId = -1
+ String key = 'zzz_' + new Date()
+ String attributeName = "$key updated"
+ String json = """{ "attributeName": "$attributeName" }"""
+ path(attributeNameId)
+ notFound()
+
+ when:
+ put(json)
+
+ then:
+ !payload
+ }
+
+ def 'can NOT update attributeName with invalid attributeName'() {
+ given:
+ String attributeName = 'This is long text, This is long text with lots of characters, so its long text with more than 100 text.'
+ String json = """{ "attributeName": "$attributeName" }"""
+ path(currentAttributeNameId)
+ invalid()
+
+ when:
+ put(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'attributeName'
+ text.contains 'exceeds the maximum size'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can update attributeName'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeName = "$key updated"
+ String json = """{ "attributeName": "$attributeName" }"""
+ path(currentAttributeNameId)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.attributeName == attributeName
+ payload.results.attributeNameId == currentAttributeNameId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT find AttributeName for invalid attributeNameId'() {
+ given:
+ path(-1)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can get all AttributeNames'() {
+ given:
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ apiPaginated() > 10
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can find a AttributeName'() {
+ given:
+ path(currentAttributeNameId)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.attributeNameId == currentAttributeNameId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can not delete a AttributeName if related to Category or Part'() {
+ given:
+ path(1089)
+ invalid()
+
+ when:
+ delete()
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text.contains 'Cannot delete AttributeName when assigned to Category or Part'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can Not delete a AttributeName for invalid Id'() {
+ given:
+ path(-1)
+ notFound()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can delete a AttributeName'() {
+ given:
+ path(currentAttributeNameId)
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+}
Index: src/integration-test/groovy/com/lemans/ds/attribute/AttributeNameLocaleFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/attribute/AttributeNameLocaleFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/attribute/AttributeNameLocaleFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,346 @@
+package com.lemans.ds.attribute
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Ignore
+import spock.lang.Stepwise
+
+@Stepwise
+class AttributeNameLocaleFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'attributeName' }
+
+ final static int ALL_COLUMNS_SIZE = 16
+
+
+ Integer validAttributeNameId = 1024
+
+ Integer invalidAttributeNameId = 1183
+
+ String invalidLocale = 'xy'
+
+ def 'can create attributeNameLocale with attributeName id that exist'() {
+ given:
+ String attributeName = 'updated In German'
+ optionalHeaders.locale = 'it'
+ String json = """
+ {
+ "attributeName": "$attributeName",
+ }
+ """
+ path(validAttributeNameId)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.attributeNameLocale == attributeName
+ payload.results.attributeNameId == 1024
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT create attributeNameLocale with attributeName id that exist but with invalid Locale'() {
+ given:
+ String attributeName = 'updated In German'
+ optionalHeaders.locale = invalidLocale
+ String json = """
+ {
+ "attributeNameLocale": "$attributeName",
+ }
+ """
+ path(validAttributeNameId)
+ invalid()
+
+ when:
+ put(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'locale'
+ text == 'locale with value [xy] is not contained within the list [[de, it, es, fr]]'
+ }
+ payload.messages.size() == 1
+ }
+
+
+ def 'can NOT create attributeNameLocale with attributeNameId that does not exist'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeName = "$key updated In German"
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeNameLocale": "$attributeName",
+ }
+ """
+ path(invalidAttributeNameId)
+ notFound()
+
+ when:
+ put(json)
+
+ then:
+ !payload
+ }
+
+ def 'can NOT create attributeNameLocale with invalid Locale'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeName = "$key updated In German"
+ optionalHeaders.locale = invalidLocale
+ String json = """
+ {
+ "attributeNameLocale": "$attributeName",
+ }
+ """
+ path(validAttributeNameId)
+ invalid()
+
+ when:
+ put(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'locale'
+ text == 'locale with value [xy] is not contained within the list [[de, it, es, fr]]'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can NOT update attributeName with invalid Locale'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeName = "$key updated In German"
+ optionalHeaders.locale = invalidLocale
+ String json = """
+ {
+ "attributeNameLocale": "$attributeName",
+ }
+ """
+ path(validAttributeNameId)
+ invalid()
+
+ when:
+ put(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'locale'
+ text == 'locale with value [xy] is not contained within the list [[de, it, es, fr]]'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can NOT update attributeName with invalid AttributeName'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeName = "$key updated In German"
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeNameLocale": "$attributeName",
+ }
+ """
+ path(invalidAttributeNameId)
+ notFound()
+
+ when:
+ put(json)
+
+ then:
+ !payload
+ }
+
+ @Ignore
+ def 'can update attributeName with valid Locale'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeName = "$key updated In German"
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeNameLocale": "$attributeName",
+ }
+ """
+ path(validAttributeNameId)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.attributeNameLocale == attributeName
+ payload.results.attributeNameId == validAttributeNameId
+ payload.results.size() == ALL_COLUMNS_SIZE - 1
+ }
+
+ @Ignore
+ def 'can update attributeDisplayName with valid Locale'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeDisplayName = "$key updated In German in displayname"
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeDisplayNameLocale": "$attributeDisplayName",
+ }
+ """
+ path(validAttributeNameId)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.attributeDisplayNameLocale == attributeDisplayName
+ payload.results.attributeNameId == validAttributeNameId
+ payload.results.size() == ALL_COLUMNS_SIZE - 1
+ }
+
+
+ def 'can update attributeName and attributeDisplayName with NULL values for a valid Locale'() {
+ given:
+ String attributeName = ''
+ String attributeDisplayName = ''
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeName": "$attributeName",
+ "attributeDisplayName": "$attributeDisplayName",
+ }
+ """
+ path(validAttributeNameId)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.attributeDisplayNameLocale == null
+ payload.results.attributeNameId == validAttributeNameId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can update attributeName and attributeDisplayName with valid Locale'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeName = "$key updated Both In German"
+ String attributeDisplayName = "$key updated In German in displayname"
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeName": "$attributeName",
+ "attributeDisplayName": "$attributeDisplayName",
+ }
+ """
+ path(validAttributeNameId)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.attributeDisplayNameLocale == attributeDisplayName
+ payload.results.attributeNameId == validAttributeNameId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT get all AttributeNames for invalid Locale'() {
+ given:
+ optionalHeaders.locale = invalidLocale
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.meta.totalRecords == 0
+ payload.results == []
+ }
+
+ def 'can get all AttributeNames for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ apiPaginated() > 10
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT find AttributeNameLocale for an invalid AttributeName for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path(invalidAttributeNameId)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT find AttributeNameLocale for a valid AttributeName for a invalid Locale'() {
+ given:
+ optionalHeaders.locale = invalidLocale
+ path(validAttributeNameId)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT find AttributeNameLocale for invalid AttributeName for invalid Locale'() {
+ given:
+ optionalHeaders.locale = invalidLocale
+ path(invalidAttributeNameId)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find AttributeNameLocale for a valid AttributeName for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path(validAttributeNameId)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.attributeNameId == validAttributeNameId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can get AttributeNameLocale for a valid AttributeName for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'fr'
+ path(validAttributeNameId)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.attributeNameId == validAttributeNameId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/attribute/AttributePartFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/attribute/AttributePartFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/attribute/AttributePartFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,85 @@
+package com.lemans.ds.attribute
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class AttributePartFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { '' }
+
+ final static int ALL_COLUMNS_SIZE = 10
+
+ def 'can get all parts filtered by attribute Name'() {
+ given:
+ path('attributeName/1081/part')
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can get all parts filtered by attribute Name and category'() {
+ given:
+ path('attributeName/1081/part')
+ queryParams.categoryId = 592
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT find part attributes with out attributeNameId'() {
+ given:
+ path('attributeName/null/part')
+ invalid()
+
+ when:
+ get()
+
+ then:
+ payload.messages[0].text == 'attributeNameId is required'
+ }
+
+ def 'can get all parts filtered by attribute Value'() {
+ given:
+ path('attributeValue/101059/part')
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can find all parts filtered by attribute Value and category'() {
+ given:
+ path('attributeValue/101059/part')
+ queryParams.categoryId = 6917
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT find part attributes with out attributeValueId'() {
+ given:
+ path('attributeValue/null/part')
+ invalid()
+
+ when:
+ get()
+
+ then:
+ payload.messages[0].text == 'attributeValueId is required'
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/attribute/AttributeValueFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/attribute/AttributeValueFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/attribute/AttributeValueFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,245 @@
+package com.lemans.ds.attribute
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+
+class AttributeValueFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'attributeValue' }
+
+ final static int ALL_COLUMNS_SIZE = 12
+
+ @Shared
+ Integer currentAttributeValueId
+
+ def 'can not create attributeValue with blank attributeValue field'() {
+ given:
+ String json = '{ "attributeValue": "" }'
+ path()
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'attributeValue'
+ text.contains 'attributeValue is required'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can not create duplicate attributeValue'() {
+ given:
+ String attributeValue = 'Small'
+ String json = """{ "attributeValue": "$attributeValue" }"""
+ path()
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ code == 'DUPLICATE_KEY'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can create valid attributeValue '() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeValue = "$key TestCreate"
+ String json = """{ "attributeValue": "$attributeValue" }"""
+ path()
+ ok()
+
+ when:
+ post(json)
+ currentAttributeValueId = payload.results.attributeValueId
+ then:
+ payload.results.attributeValue == attributeValue
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+
+ //Test for Editing Attribute Value
+ def 'can NOT update attributeValue with invalid attributeValueId '() {
+ given:
+ Integer attributeValueId = -1
+ String key = 'zzz_' + new Date()
+ String attributeValue = "$key updated"
+ String json = """{ "attributeValue": "$attributeValue" }"""
+ path(attributeValueId)
+ notFound()
+
+ when:
+ put(json)
+
+ then:
+ !payload
+ }
+
+ def 'can NOT update attributeValue with invalid attributeValue'() {
+ given:
+ String longAttributeValue = '*' * (1000)
+ String attributeValue = longAttributeValue
+ String json = """{ "attributeValue": "$attributeValue" }"""
+ path(currentAttributeValueId)
+ invalid()
+
+ when:
+ put(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'attributeValue'
+ text.contains 'exceeds the maximum size'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can update attributeValue'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeValue = "$key updated"
+ String json = """{ "attributeValue": "$attributeValue" }"""
+ path(currentAttributeValueId)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.attributeValue == attributeValue
+ payload.results.attributeValueId == currentAttributeValueId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ def 'can NOT find AttributeValue for invalid attributeValueId'() {
+ given:
+ path(-1)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can get all AttributeValues'() {
+ given:
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ apiPaginated() > 3
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can get all AttributeValues with q params'() {
+ given:
+ String qParam = 'sm'
+ queryParams.q = qParam
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ apiPaginated() > 3
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ payload.results.attributeValue.every { it.toLowerCase().contains(qParam) }
+ }
+
+ def 'can find a AttributeValue'() {
+ given:
+ path(currentAttributeValueId)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.attributeValueId == currentAttributeValueId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can not delete a AttributeValue if related to Category or Part'() {
+ given:
+ path(9)
+ invalid()
+
+ when:
+ delete()
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text.contains 'Cannot delete AttributeValue when assigned to Category or Part'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can NOT delete AttributeValue with invalid AttributeNameId'() {
+ given:
+ path(-1)
+ notFound()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can delete a AttributeValue'() {
+ given:
+ path(currentAttributeValueId)
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can get attributeValues by valid Attribute Name Id'() {
+ given:
+ Integer id = 1
+ path(attributeName: id)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.attributeValueId.contains(3)
+ payload.results.attributeValue.contains('Medium')
+ }
+
+ def 'can NOT get attribute values with invalid attribute name Id'() {
+ given:
+ Integer id = 10
+ path(attributeName: id)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results == []
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/attribute/AttributeValueLocaleFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/attribute/AttributeValueLocaleFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/attribute/AttributeValueLocaleFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,281 @@
+package com.lemans.ds.attribute
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class AttributeValueLocaleFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'attributeValue' }
+
+ final static int ALL_COLUMNS_SIZE = 14
+
+
+ Integer invalidAttributeValueId = 118332
+ Integer validAttributeValueId = 39418
+
+
+ def 'can create AttributeValuesLocale with attributeValueId that exist'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeValue = "$key updated In German"
+ optionalHeaders.locale = 'it'
+ String json = """
+ {
+ "attributeValue": "$attributeValue",
+ }
+ """
+ path(validAttributeValueId)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.attributeValueLocale == attributeValue
+ payload.results.attributeValueId == validAttributeValueId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT create attributeValueLocale with attributeValueId that exist but with invalid Locale'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeValue = "$key updated In German"
+ optionalHeaders.locale = 'xy'
+ String json = """
+ {
+ "attributeValue": "$attributeValue",
+ }
+ """
+ path(validAttributeValueId)
+ invalid()
+
+ when:
+ put(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'locale'
+ text == 'locale with value [xy] is not contained within the list [[de, it, es, fr]]'
+ }
+ payload.messages.size() == 1
+ }
+
+
+ def 'can NOT create attributeValueLocale with attributeValueId that does not exist'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeValue = "$key updated In German"
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeValue": "$attributeValue",
+ }
+ """
+ path(invalidAttributeValueId)
+ notFound()
+
+ when:
+ put(json)
+
+ then:
+ !payload
+ }
+
+ def 'can NOT create attributeValueLocale with invalid Locale'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeValue = "$key updated In German"
+ optionalHeaders.locale = 'xy'
+ String json = """
+ {
+ "attributeValue": "$attributeValue",
+ }
+ """
+ path(validAttributeValueId)
+ invalid()
+
+ when:
+ put(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'locale'
+ text == 'locale with value [xy] is not contained within the list [[de, it, es, fr]]'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can NOT update attributeValue with invalid Locale'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeValue = "$key updated In German"
+ optionalHeaders.locale = 'xy'
+ String json = """
+ {
+ "attributeValue": "$attributeValue",
+ }
+ """
+ path(validAttributeValueId)
+ invalid()
+
+ when:
+ put(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'locale'
+ text == 'locale with value [xy] is not contained within the list [[de, it, es, fr]]'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can NOT update attributeValue with invalid AttributeValueId'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeValue = "$key updated In German"
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeValue": "$attributeValue",
+ }
+ """
+ path(invalidAttributeValueId)
+ notFound()
+
+ when:
+ put(json)
+
+ then:
+ !payload
+ }
+
+ def 'can update attributeValue with valid Locale'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeValue = "$key updated In German"
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeValue": "$attributeValue",
+ }
+ """
+ path(validAttributeValueId)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.attributeValueLocale == attributeValue
+ payload.results.attributeValueId == validAttributeValueId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can update empty attributeValue with valid Locale'() {
+ given:
+ String attributeValue = ''
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeValue": "$attributeValue",
+ }
+ """
+ path(validAttributeValueId)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.attributeValueLocale == null
+ payload.results.attributeValueId == validAttributeValueId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+
+ def 'can NOT get all AttributeValues for invalid Locale'() {
+ given:
+ optionalHeaders.locale = 'xy'
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results == []
+ payload.meta.totalRecords == 0
+ }
+
+ def 'can get all AttributeValues for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ apiPaginated() > 10
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT find AttributeValueLocale for an invalid AttributeValueId for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path(invalidAttributeValueId)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT find AttributeValueLocale for a valid AttributeValue for a invalid Locale'() {
+ given:
+ optionalHeaders.locale = 'xy'
+ path(validAttributeValueId)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT find AttributeValueLocale for invalid AttributeValue for invalid Locale'() {
+ given:
+ optionalHeaders.locale = 'xy'
+ path(invalidAttributeValueId)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find AttributeValueLocale for a valid AttributeValue for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path(validAttributeValueId)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.attributeValueId == validAttributeValueId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,324 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Stepwise
+
+@Stepwise
+class CategoryAttributeFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'attribute' }
+
+ final static int ALL_COLUMNS_SIZE = 22
+
+ final static int CATALOG_INSTANCE_ID = 0
+
+ final static int CATEGORY_ID = 1
+
+ final static int ATTRIBUTE_NAME_ID = 1
+
+
+ def 'clean CategoryAttribute data'() {
+ given:
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], ATTRIBUTE_NAME_ID)
+ ignore()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can not create Category Attribute without required Fields'() {
+ given:
+ String json = """
+{
+"attributeNameId": $ATTRIBUTE_NAME_ID,
+"categoryId": $CATEGORY_ID,
+"isRequired": true,
+ }"""
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID])
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'allowMultipleValues'
+ text.contains 'allowMultipleValues is required'
+ }
+ payload.messages.size() == 5
+ }
+
+ def 'can create valid Category AttributeName relation'() {
+ given:
+ String json = """
+{
+"attributeNameId": $ATTRIBUTE_NAME_ID,
+"isRequired": true,
+"isDropdown": true,
+"isHidden": true,
+"isGroup": true,
+"isKeyAttribute": true,
+"allowMultipleValues": false
+ }"""
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID])
+ ok()
+
+ when:
+ post(json)
+
+ then:
+ with(payload) {
+ results.attributeNameId == ATTRIBUTE_NAME_ID
+ results.isRequired == true
+ results.size() == ALL_COLUMNS_SIZE
+ }
+ }
+
+ def 'can not create duplicate attributeName'() {
+ given:
+ String json = """
+{
+"attributeNameId": $ATTRIBUTE_NAME_ID,
+"categoryId": $CATALOG_INSTANCE_ID,
+"isRequired": true,
+"isDropdown": true,
+"isHidden": true,
+"isGroup": true,
+"isKeyAttribute": true,
+"allowMultipleValues": false
+ }"""
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID])
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ code == 'DUPLICATE_KEY'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can NOT update CategoryAttribute with invalid value'() {
+ given:
+ String json = '''
+{
+"isRequired": true,
+"isDropdown": true,
+"isHidden": true,
+"isGroup": true,
+"allowMultipleValues": null
+ }'''
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], ATTRIBUTE_NAME_ID)
+ invalid()
+
+ when:
+ put(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'allowMultipleValues'
+ text.contains 'allowMultipleValues is required'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can NOT update CategoryAttribute with invalid attributeId'() {
+ given:
+ String json = '''
+{
+"isRequired": true,
+"isDropdown": true,
+"isHidden": true,
+"isGroup": true,
+"allowMultipleValues": null
+ }'''
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], -1)
+ notFound()
+
+ when:
+ put(json)
+
+ then:
+ !payload
+ }
+
+ def 'can update CategoryAttribute with valid value'() {
+ given:
+ String json = '{ "allowMultipleValues": true}'
+ path([catalog: 0, category: CATEGORY_ID], ATTRIBUTE_NAME_ID)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ with(payload) {
+ results.attributeNameId == ATTRIBUTE_NAME_ID
+ results.allowMultipleValues == true
+ results.size() == ALL_COLUMNS_SIZE
+ }
+ }
+
+ def 'can update IsDropDown with valid value'() {
+ given:
+ String json = '{ "isDropdown": true}'
+ path([catalog: 0, category: CATEGORY_ID], ATTRIBUTE_NAME_ID)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ with(payload) {
+ results.attributeNameId == ATTRIBUTE_NAME_ID
+ results.isDropdown == true
+ results.size() == ALL_COLUMNS_SIZE
+ }
+ }
+
+ def 'can NOT update CategoryAttribute allowMultipleValues to false if parts has multiple values assigned '() {
+ given:
+ String json = '{ "allowMultipleValues": false}'
+ path([catalog: 0, category: 626], 1572)
+ invalid()
+
+ when:
+ put(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'allowMultipleValues'
+ text.contains 'Cannot alter'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can NOT find a CategoryAttribute for a Catalog that does not exist'() {
+ given:
+ path([catalog: -1, category: 1], 1)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT find a CategoryAttribute for a Category that does not exist'() {
+ given:
+ path([catalog: 0, category: -1], 1)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find a CategoryAttribute'() {
+ given:
+ int catalogInstanceId = 0
+ int categoryId = 1
+ int attributeNameId = 1
+ path([catalog: catalogInstanceId, category: categoryId], attributeNameId)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.categoryId == categoryId
+ payload.results.catalogInstanceId == catalogInstanceId
+ payload.results.attributeNameId == attributeNameId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT find CategoryAttributes for a Catalog that does not exist'() {
+ given:
+ path([catalog: -1, category: 1])
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT find CategoryAttributes for a Category that does not exist'() {
+ given:
+ path([catalog: 0, category: -1])
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find CategoriesAttributes for a category with all columns'() {
+ given:
+ int catalogInstanceId = 0
+ int categoryId = 1
+ path([catalog: catalogInstanceId, category: categoryId])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.catalogInstanceId.every { it == catalogInstanceId }
+ payload.results.size() >= 1
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+
+
+ def 'can not delete a CategoryAttribute with invalid categoryId'() {
+ given:
+ path([catalog: CATALOG_INSTANCE_ID, category: -1], ATTRIBUTE_NAME_ID)
+ notFound()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT delete a CategoryAttribute that has parts with the same attribute name'() {
+ given:
+ path([catalog: CATALOG_INSTANCE_ID, category: 4542], 510)
+ invalid()
+
+ when:
+ delete()
+
+ then:
+ payload.messages[0].text == 'Cannot delete Category Attribute that has parts with the same attribute Name.'
+ }
+
+ def 'can delete a CategoryAttribute'() {
+ given:
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], ATTRIBUTE_NAME_ID)
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeLocaleFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeLocaleFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeLocaleFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,318 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class CategoryAttributeLocaleFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'attribute' }
+
+ final static int ALL_COLUMNS_SIZE = 25
+ final static int CATALOG_INSTANCE_ID = 0
+ final static int CATEGORY_ID = 4537
+ final static int VALID_ATTRIBUTE_NAME_ID = 1081
+ final static int INVALID_ATTRIBUTE_NAME_ID = 31
+
+
+
+ String invalidLocale = 'xy'
+
+ def 'can create categoryAttributeLocale with categoryAttributeId that exist with valid Locale'() {
+ given:
+ String attributeName = 'updated In German'
+ optionalHeaders.locale = 'it'
+ String json = """
+ {
+ "attributeName": "$attributeName",
+ }
+ """
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], VALID_ATTRIBUTE_NAME_ID)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.attributeNameLocale == attributeName
+ payload.results.attributeNameId == VALID_ATTRIBUTE_NAME_ID
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT create categoryAttributeLocale with categoryAttributeId that exist but with invalid Locale'() {
+ given:
+ String attributeName = 'updated In German'
+ optionalHeaders.locale = invalidLocale
+ String json = """
+ {
+ "attributeName": "$attributeName",
+ }
+ """
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], VALID_ATTRIBUTE_NAME_ID)
+ invalid()
+
+ when:
+ put(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'locale with value [xy] is not contained within the list [[de, it, es, fr]]'
+ }
+ payload.messages.size() == 1
+ }
+
+
+ def 'can NOT create categoryAttributeLocale with categoryAttributeId that does not exist'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeName = "$key updated In German"
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeName": "$attributeName",
+ }
+ """
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], INVALID_ATTRIBUTE_NAME_ID)
+ notFound()
+
+ when:
+ put(json)
+
+ then:
+ !payload
+ }
+
+ def 'can NOT update categoryAttributeLocale with invalid Locale'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeName = "$key updated In German"
+ optionalHeaders.locale = invalidLocale
+ String json = """
+ {
+ "attributeName": "$attributeName",
+ }
+ """
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], VALID_ATTRIBUTE_NAME_ID)
+ invalid()
+
+ when:
+ put(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'locale with value [xy] is not contained within the list [[de, it, es, fr]]'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can NOT update categoryAttributeLocale with invalid categoryAttributeId'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeName = "$key updated In German"
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeName": "$attributeName",
+ }
+ """
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], INVALID_ATTRIBUTE_NAME_ID)
+ notFound()
+
+ when:
+ put(json)
+
+ then:
+ !payload
+ }
+
+ def 'can update categoryAttributeLocale with valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ String json ='''
+ {
+ "isKeyAttribute": true,
+ "isHidden": true,
+ "allowMultipleValues": true,
+ "isGroup": true,
+ "isRequired": false,
+ }
+ '''
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], VALID_ATTRIBUTE_NAME_ID)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.attributeNameId == VALID_ATTRIBUTE_NAME_ID
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can update categoryAttributeDisplayNameLocale with valid Locale'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeDisplayName = "$key updated In German in displayname"
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeDisplayName": "$attributeDisplayName"
+ }
+ """
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], VALID_ATTRIBUTE_NAME_ID)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.attributeDisplayNameLocale == attributeDisplayName
+ payload.results.attributeNameId == VALID_ATTRIBUTE_NAME_ID
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+
+ def 'can update categoryAttributeLocale and categoryAttributeDisplayNameLocale with NULL values for a valid Locale'() {
+ given:
+ String attributeName = ''
+ String attributeDisplayName = ''
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeName": "$attributeName",
+ "attributeDisplayName": "$attributeDisplayName",
+ }
+ """
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], VALID_ATTRIBUTE_NAME_ID)
+ ok()
+
+ when:
+ put(json)
+
+
+ then:
+ with(payload) {
+ //results.attributeDisplayNameLocale == null
+ results.attributeNameId == VALID_ATTRIBUTE_NAME_ID
+ results.size() == ALL_COLUMNS_SIZE
+ }
+ }
+
+ def 'can update categoryAttributeLocale and categoryAttributeDisplayNameLocale with valid Locale'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeName = "$key updated Both In German"
+ String attributeDisplayName = "$key updated In German in displayname"
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeName": "$attributeName",
+ "attributeDisplayName": "$attributeDisplayName",
+ }
+ """
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], VALID_ATTRIBUTE_NAME_ID)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.attributeDisplayNameLocale == attributeDisplayName
+ payload.results.attributeNameId == VALID_ATTRIBUTE_NAME_ID
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT get all categoryAttributeLocale for invalid Locale'() {
+ given:
+ optionalHeaders.locale = invalidLocale
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results == []
+ payload.meta.totalRecords == 0
+ }
+
+ def 'can get all categoryAttributeLocale for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID])
+ ok()
+
+ when:
+ get()
+
+ then:
+ apiPaginated() > 10
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT find categoryAttributeLocale for an invalid categoryAttributeId for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], INVALID_ATTRIBUTE_NAME_ID)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT find categoryAttributeLocale for a valid categoryAttributeId for a invalid Locale'() {
+ given:
+
+ optionalHeaders.locale = invalidLocale
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], VALID_ATTRIBUTE_NAME_ID)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT find categoryAttributeLocale for invalid categoryAttributeId for invalid Locale'() {
+ given:
+ optionalHeaders.locale = invalidLocale
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], INVALID_ATTRIBUTE_NAME_ID)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT find categoryAttributeLocale for a invalid categoryAttributeId for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], INVALID_ATTRIBUTE_NAME_ID)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+ def 'can find categoryAttributeLocale for a valid categoryAttributeId for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], VALID_ATTRIBUTE_NAME_ID)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.attributeNameId == VALID_ATTRIBUTE_NAME_ID
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeMoveFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeMoveFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeMoveFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,95 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+import spock.lang.Stepwise
+
+@Stepwise
+class CategoryAttributeMoveFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'attribute' }
+
+ final static int CATALOG_INSTANCE_ID = 0
+
+ final static int CATEGORY_ID = 1
+
+ final static int SOURCE_ATTRIBUTE_NAME_ID = 1089
+
+ final static int TARGET_ATTRIBUTE_NAME_ID = 103
+
+ @Shared
+ Integer targetSequence
+
+ def 'can find target CategoryAttribute'() {
+ given:
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], TARGET_ATTRIBUTE_NAME_ID)
+ ok()
+
+ when:
+ get()
+ targetSequence = payload.results.sequence
+ then:
+ payload.results.categoryId == CATEGORY_ID
+ payload.results.catalogInstanceId == CATALOG_INSTANCE_ID
+ payload.results.attributeNameId == TARGET_ATTRIBUTE_NAME_ID
+ }
+
+ def 'can re sequence or Move Category Attribute Before'() {
+ given:
+ Integer assertSequence = targetSequence
+ String json = """
+{
+"targetId": $TARGET_ATTRIBUTE_NAME_ID,
+"position": "BEFORE"
+}
+"""
+ path([catalog: 0, category: CATEGORY_ID], SOURCE_ATTRIBUTE_NAME_ID + '/move')
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ with(payload) {
+ results.attributeNameId == SOURCE_ATTRIBUTE_NAME_ID
+ results.sequence <= assertSequence
+ }
+ }
+
+ def 'can find target CategoryAttribute again'() {
+ given:
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID], TARGET_ATTRIBUTE_NAME_ID)
+ ok()
+
+ when:
+ get()
+ targetSequence = payload.results.sequence
+ then:
+ payload.results.categoryId == CATEGORY_ID
+ payload.results.catalogInstanceId == CATALOG_INSTANCE_ID
+ payload.results.attributeNameId == TARGET_ATTRIBUTE_NAME_ID
+ }
+
+ def 'can re sequence or Move Category Attribute After'() {
+ given:
+ Integer assertSequence = targetSequence
+ String json = """
+{
+"targetId": $TARGET_ATTRIBUTE_NAME_ID,
+"position": "AFTER"
+}
+"""
+ path([catalog: 0, category: CATEGORY_ID], SOURCE_ATTRIBUTE_NAME_ID + '/move')
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ with(payload) {
+ results.attributeNameId == SOURCE_ATTRIBUTE_NAME_ID
+ results.sequence >= assertSequence
+ }
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeValueFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeValueFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeValueFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,171 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Stepwise
+
+@Stepwise
+class CategoryAttributeValueFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'value' }
+
+ final static int ALL_COLUMNS_SIZE = 20
+
+ final static int CATALOG_INSTANCE_ID = 0
+
+ final static int CATEGORY_ID = 4537
+
+ final static int ATTRIBUTE_NAME_ID = 1081
+
+ final static int ATTRIBUTE_VALUE_ID = 9
+
+ def 'clean test data'() {
+ given:
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], ATTRIBUTE_VALUE_ID)
+ ignore()
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can not create CategoryAttributeValue with invalid attributeNameId'() {
+ given:
+ String json = """
+{
+"attributeValueId": $ATTRIBUTE_VALUE_ID,
+ }"""
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: -1])
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text.contains 'attributeName relation not found'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can create valid CategoryAttributeValue relation'() {
+ given:
+ String json = """
+{
+"attributeValueId": $ATTRIBUTE_VALUE_ID,
+ }"""
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID])
+ ok()
+
+ when:
+ post(json)
+
+ then:
+ with(payload) {
+ results.attributeValueId == ATTRIBUTE_VALUE_ID
+ results.size() == ALL_COLUMNS_SIZE
+ }
+
+ }
+
+ def 'can not create duplicate CategoryAttributeValue relation'() {
+ given:
+ String json = """
+{
+"attributeValueId": $ATTRIBUTE_VALUE_ID,
+ }"""
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID])
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ code == 'DUPLICATE_KEY'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can NOT find a CategoryAttributeValue for a Catalog that does not exist'() {
+ given:
+ path([catalog: -1, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], ATTRIBUTE_VALUE_ID)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find a CategoryAttributeValue'() {
+ given:
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], ATTRIBUTE_VALUE_ID)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.categoryId == CATEGORY_ID
+ payload.results.catalogInstanceId == CATALOG_INSTANCE_ID
+ payload.results.attributeNameId == ATTRIBUTE_NAME_ID
+ payload.results.attributeValueId == ATTRIBUTE_VALUE_ID
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT find CategoryAttributeValues for a Category that does not exist'() {
+ given:
+ path([catalog: CATALOG_INSTANCE_ID, category: -1, attribute: ATTRIBUTE_NAME_ID], ATTRIBUTE_VALUE_ID)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find CategoryAttributeValues for a valid request with all columns'() {
+ given:
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.catalogInstanceId.every { it == CATALOG_INSTANCE_ID }
+ payload.results.size() >= 1
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+
+
+ def 'can not delete a CategoryAttributeValue with invalid categoryId'() {
+ given:
+ path([catalog: CATALOG_INSTANCE_ID, category: -1, attribute: ATTRIBUTE_NAME_ID], ATTRIBUTE_VALUE_ID)
+ notFound()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can delete a CategoryAttributeValue'() {
+ given:
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], ATTRIBUTE_VALUE_ID)
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeValueLocaleFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeValueLocaleFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeValueLocaleFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,238 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class CategoryAttributeValueLocaleFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'value' }
+
+ final static int ALL_COLUMNS_SIZE = 23
+
+ final static int CATALOG_INSTANCE_ID = 0
+
+ final static int CATEGORY_ID = 4537
+
+ final static int ATTRIBUTE_NAME_ID = 1081
+
+ final static int VALID_CATEGORY_ATTRIBUTE_VALUE_ID = 63296
+
+ final static int INVALID_CATEGORY_ATTRIBUTE_VALUE_ID = 0
+
+
+ def 'can create categoryAttributeValueLocale with categoryAttributeValueId that exist with valid locale'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeValue = "$key updated In German"
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeValue": "$attributeValue",
+ }
+ """
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], VALID_CATEGORY_ATTRIBUTE_VALUE_ID)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.attributeValueId == VALID_CATEGORY_ATTRIBUTE_VALUE_ID
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT create categoryAttributeValueLocale with categoryAttributeValueId that exist but with invalid Locale'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeValue = "$key updated In German"
+ optionalHeaders.locale = 'xy'
+ String json = """
+ {
+ "attributeValue": "$attributeValue",
+ }
+ """
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], VALID_CATEGORY_ATTRIBUTE_VALUE_ID)
+ notFound()
+
+ when:
+ put(json)
+
+ then:
+ !payload
+ }
+
+ def 'can NOT create attributeValueLocale with attributeValueId that does not exist'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeValue = "$key updated In German"
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeValue": "$attributeValue",
+ }
+ """
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], INVALID_CATEGORY_ATTRIBUTE_VALUE_ID)
+ notFound()
+
+ when:
+ put(json)
+
+ then:
+ !payload
+ }
+
+ def 'can NOT update attributeValue with invalid Locale'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeValue = "$key updated In German"
+ optionalHeaders.locale = 'xy'
+ String json = """
+ {
+ "attributeValue": "$attributeValue",
+ }
+ """
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], VALID_CATEGORY_ATTRIBUTE_VALUE_ID)
+ notFound()
+
+ when:
+ put(json)
+
+ then:
+ !payload
+ }
+
+ def 'can NOT update attributeValue with invalid AttributeValueId'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeValue = "$key updated In German"
+ optionalHeaders.locale = 'de'
+ String json = """
+ {
+ "attributeValue": "$attributeValue",
+ }
+ """
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], INVALID_CATEGORY_ATTRIBUTE_VALUE_ID)
+ notFound()
+
+ when:
+ put(json)
+
+ then:
+ !payload
+ }
+
+ def 'can update attributeValue with valid Locale'() {
+ given:
+ String key = 'zzz_' + new Date()
+ String attributeValue = "$key updated In German"
+ optionalHeaders.locale = 'es'
+ String json = """
+ {
+ "attributeValue": "$attributeValue",
+ }
+ """
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], VALID_CATEGORY_ATTRIBUTE_VALUE_ID)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ //payload.results.attributeValueLocale == attributeValue
+ payload.results.attributeValueId == VALID_CATEGORY_ATTRIBUTE_VALUE_ID
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT get all AttributeValues for invalid Locale'() {
+ given:
+ optionalHeaders.locale = 'xy'
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID])
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can get all AttributeValues for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID])
+ ok()
+
+ when:
+ get()
+
+ then:
+ //apiPaginated() > 1
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT find AttributeValueLocale for an invalid AttributeValueId for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], INVALID_CATEGORY_ATTRIBUTE_VALUE_ID)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT find AttributeValueLocale for a valid AttributeValue for a invalid Locale'() {
+ given:
+ optionalHeaders.locale = 'xy'
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], VALID_CATEGORY_ATTRIBUTE_VALUE_ID)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT find AttributeValueLocale for invalid AttributeValue for invalid Locale'() {
+ given:
+ optionalHeaders.locale = 'xy'
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], INVALID_CATEGORY_ATTRIBUTE_VALUE_ID)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find AttributeValueLocale for a valid AttributeValue for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], VALID_CATEGORY_ATTRIBUTE_VALUE_ID)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.attributeValueId == VALID_CATEGORY_ATTRIBUTE_VALUE_ID
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT find AttributeValueLocale for a invalid AttributeValue for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], INVALID_CATEGORY_ATTRIBUTE_VALUE_ID)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeValueMoveFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeValueMoveFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategoryAttributeValueMoveFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,99 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+import spock.lang.Stepwise
+
+@Stepwise
+class CategoryAttributeValueMoveFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'value' }
+
+ final static int CATALOG_INSTANCE_ID = 0
+
+ final static int CATEGORY_ID = 1
+
+ final static int ATTRIBUTE_NAME_ID = 103
+
+ final static int SOURCE_ATTRIBUTE_VALUE_ID = 11
+
+ final static int TARGET_ATTRIBUTE_VALUE_ID = 67036
+
+ @Shared
+ Integer targetSequence
+
+ def 'can find target CategoryAttributeValue'() {
+ given:
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], TARGET_ATTRIBUTE_VALUE_ID)
+ ok()
+
+ when:
+ get()
+ targetSequence = payload.results.sequence
+ then:
+ payload.results.categoryId == CATEGORY_ID
+ payload.results.catalogInstanceId == CATALOG_INSTANCE_ID
+ payload.results.attributeValueId == TARGET_ATTRIBUTE_VALUE_ID
+ }
+
+ def 'can re sequence or Move Category Attribute Value Before'() {
+ given:
+ Integer assertSequence = targetSequence
+ String json = """
+{
+"targetId": $TARGET_ATTRIBUTE_VALUE_ID,
+"position": "BEFORE"
+}
+"""
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], SOURCE_ATTRIBUTE_VALUE_ID + '/move')
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ with(payload) {
+ results.attributeValueId == SOURCE_ATTRIBUTE_VALUE_ID
+ results.sequence
+ results.sequence <= assertSequence
+ }
+ }
+
+ def 'can find target CategoryAttributeValue again'() {
+ given:
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], TARGET_ATTRIBUTE_VALUE_ID)
+ ok()
+
+ when:
+ get()
+ targetSequence = payload.results.sequence
+ then:
+ payload.results.categoryId == CATEGORY_ID
+ payload.results.catalogInstanceId == CATALOG_INSTANCE_ID
+ payload.results.attributeValueId == TARGET_ATTRIBUTE_VALUE_ID
+ }
+
+ def 'can re sequence or Move Category AttributeValue After'() {
+ given:
+ Integer assertSequence = targetSequence
+ String json = """
+{
+"targetId": $TARGET_ATTRIBUTE_VALUE_ID,
+"position": "AFTER"
+}
+"""
+ path([catalog: CATALOG_INSTANCE_ID, category: CATEGORY_ID, attribute: ATTRIBUTE_NAME_ID], SOURCE_ATTRIBUTE_VALUE_ID + '/move')
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ with(payload) {
+ results.attributeValueId == SOURCE_ATTRIBUTE_VALUE_ID
+ results.sequence
+ results.sequence >= assertSequence
+ }
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategoryDetailsFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategoryDetailsFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategoryDetailsFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,69 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class CategoryDetailsFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'details' }
+
+ final static int ALL_COLUMNS_SIZE = 21
+
+ def 'can NOT find a Category for a Catalog that does not exist'() {
+ given:
+ path([catalog: 0, category: -1])
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find a Category details for a valid Catalog for a valid Locale'() {
+ given:
+ int categoryId = 626
+ optionalHeaders.locale = 'de'
+ path([catalog: 0, category: categoryId])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.categoryId == categoryId
+ payload.results.attribute.size() > 1
+ }
+
+ def 'can find a Category details for a valid Catalog'() {
+ given:
+ int categoryId = 626
+ path([catalog: 0, category: categoryId])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.categoryId == categoryId
+ payload.results.attribute.size() > 1
+ }
+
+ def 'can find a Category details for a Locale for valid Catalog'() {
+ given:
+ int categoryId = 626
+ optionalHeaders.locale = 'de'
+ path([catalog: 0, category: categoryId])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.categoryId == categoryId
+ payload.results.attribute.size() > 1
+ }
+
+
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategoryFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategoryFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategoryFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,115 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.IgnoreRest
+
+class CategoryFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'category' }
+
+ final static int ALL_COLUMNS_SIZE = 21
+
+ def 'can NOT find a Category for a Catalog that does not exist'() {
+ given:
+ path([catalog: 0], -1)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find a Category for a Catalog'() {
+ given:
+ int catalogInstanceId = 0
+ int categoryId = 1
+ path([catalog: catalogInstanceId], categoryId)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.categoryId == categoryId
+ payload.results.catalogInstanceId == catalogInstanceId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT find Categories for a Catalog that does not exist'() {
+ given:
+ path([catalog: -1])
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find Categories for a Catalog with ALL columns'() {
+ given:
+ int catalogId = 0
+ path([catalog: catalogId])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.catalogInstanceId.every { it == catalogId }
+ payload.results.size() >= 7
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+
+ @IgnoreRest
+ def 'can find specific Categories for a Catalog with ALL columns'() {
+ given:
+ int catalogId = 0
+ path([catalog: catalogId])
+ queryParams.categoryId = '1,2'
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.catalogInstanceId.every { it == catalogId }
+ payload.results.categoryId == [1, 2]
+ payload.results.size() == 2
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can find Categories for a Catalog with ONLY tree columns'() {
+ given:
+ int catalogId = 0
+ queryParams.columns = '_tree'
+ path([catalog: catalogId])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.size() >= 7
+ payload.results[0].size() == CategoryService.TREE_COLUMNS.size()
+ }
+
+ def 'can find Categories for a Catalog with ONLY dropdown columns'() {
+ given:
+ int catalogId = 0
+ queryParams.columns = '_dropdown'
+ path([catalog: catalogId])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.size() >= 7
+ payload.results[0].size() == CategoryService.DROPDOWN_COLUMNS.size()
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategoryHierarchyPersistenceSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategoryHierarchyPersistenceSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategoryHierarchyPersistenceSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,96 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+import spock.lang.Stepwise
+
+@Stepwise
+class CategoryHierarchyPersistenceSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'category' }
+
+ @Shared Integer childCategoryId
+
+ @Shared Integer parentCategoryId = 2
+
+ @Shared Integer catalogId = 0
+
+ def 'can add a child Category to a parent'() {
+ given:
+ String name = 'testName_' + new Date()
+ Map category = [catalogInstanceId: catalogId, categoryName: name, parentCategoryId: parentCategoryId]
+ path(catalog: catalogId)
+ ok()
+
+ when:
+ post(category)
+ childCategoryId = payload.results.categoryId
+
+ then:
+ with(payload.results) {
+ catalogInstanceId == this.catalogId
+ categoryName == name
+ parentCategoryId == this.parentCategoryId
+ categoryId != null
+ sequence > 1
+ }
+ }
+
+ def 'can NOT update a child Category name making it a duplicate'() {
+ given:
+ int idOfDuplicated = 6
+ String nameOfDuplicated = 'Child Level 3'
+ Map category = [categoryName: nameOfDuplicated, version: -9999]
+ path([catalog: catalogId], idOfDuplicated)
+ invalid()
+
+ when:
+ put(category)
+
+ then:
+ payload.messages.find {
+ it.code == 'DATA_INTEGRITY_VIOLATION' || it.code == 'DUPLICATE_KEY'
+ }
+ }
+
+ def 'can delete the new child Category without children'() {
+ given:
+ queryParams.version = -9999
+ path([catalog: catalogId], childCategoryId)
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT add a child Category to a deleted parent'() {
+ given:
+ String name = 'testName_' + new Date()
+ Map category = [catalogInstanceId: catalogId, categoryName: name, parentCategoryId: 301]
+ path(catalog: catalogId)
+ notFound()
+
+ when:
+ post(category)
+
+ then:
+ !payload
+ }
+
+ def 'can NOT add a child Category to a parent exceeding the depth limitation'() {
+ given:
+ Map category = [catalogInstanceId: catalogId, categoryName: 'testName_' + new Date(), parentCategoryId: 9]
+ path(catalog: catalogId)
+ invalid()
+
+ when:
+ post(category)
+
+ then:
+ payload.messages.find { it.text == "Category depth of $Category.MAX_DEPTH may not be exceeded" }
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategoryIntegrationSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategoryIntegrationSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategoryIntegrationSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,27 @@
+package com.lemans.ds.category
+
+import ds.service.Application
+import grails.test.mixin.integration.Integration
+import spock.lang.Shared
+import spock.lang.Specification
+
+@Integration(applicationClass = Application)
+class CategoryIntegrationSpec extends Specification {
+
+ @Shared def categoryManagerService
+
+ def 'can find Categories for a catalogInstanceId'() {
+ given:
+ int catalogInstanceId = 0
+
+ when:
+ List categories = categoryManagerService.findCategoriesForCatalog(catalogInstanceId)
+ categories.each {
+ }
+
+ then:
+ categories.catalogInstanceId.every { it == catalogInstanceId }
+ categories.dateDeleted.every { it == null }
+ categories.size() > 4
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategoryLocaleFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategoryLocaleFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategoryLocaleFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,41 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class CategoryLocaleFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'category' }
+
+ Integer categoryId = 1
+ String localePresent = 'es'
+
+ def 'can update or create categoryLocale'() {
+ given:
+ Map data = [categoryName: 'Updating testCategoryLocale', description: 'Updating Locale creating']
+ queryParams.locale = localePresent
+ path([catalog: 0], categoryId)
+ ok()
+
+ when:
+ put(data)
+
+ then:
+ payload.results.categoryNameLocale == 'Updating testCategoryLocale'
+ payload.results.descriptionLocale == 'Updating Locale creating'
+ }
+
+ def 'can get locale values for a categoryId'() {
+ given:
+ queryParams.locale = localePresent
+ path([catalog: 0], categoryId)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.categoryId == categoryId
+ payload.results.locale == localePresent
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategoryMoveFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategoryMoveFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategoryMoveFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,159 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Stepwise
+
+@Stepwise
+class CategoryMoveFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'move' }
+
+ def 'can NOT move a Category with invalid targetId or position'() {
+ given:
+ path(catalog: 0, category: 305)
+ invalid()
+
+ when:
+ put()
+
+ then:
+ with(payload) {
+ messages.find { it.text == 'targetId is required' }
+ messages.find { it.text == 'position is required' }
+ }
+ }
+
+ def 'can NOT move a Category INSIDE another Category if it would create a cycle'() {
+ given:
+ int targetId = 3
+ int categoryId = 2
+ Map body = [targetId: targetId, position: 'INSIDE']
+ path(catalog: 0, category: categoryId)
+ invalid()
+
+ when:
+ put(body)
+
+ then:
+ payload.messages.find { it.text == 'Category may not be moved to create a cycle' }
+ }
+
+ def 'can NOT move a Category AFTER another Category if it would create a cycle'() {
+ given:
+ int targetId = 3
+ int categoryId = 2
+ Map body = [targetId: targetId, position: 'AFTER']
+ path(catalog: 0, category: categoryId)
+ invalid()
+
+ when:
+ put(body)
+
+ then:
+ payload.messages.find { it.text == 'Category may not be moved to create a cycle' }
+ }
+
+ def 'can NOT move a Category INSIDE another Category exceeding the depth limitation'() {
+ given:
+ Map body = [targetId: 9, position: 'INSIDE']
+ path(catalog: 0, category: 272)
+ invalid()
+
+ when:
+ put(body)
+
+ then:
+ payload.messages.find { it.text == "Category depth of $Category.MAX_DEPTH may not be exceeded" }
+ }
+
+ def 'can NOT move a Category AFTER another Category exceeding the depth limitation'() {
+ given:
+ Map body = [targetId: 9, position: 'INSIDE']
+ path(catalog: 0, category: 272)
+ invalid()
+
+ when:
+ put(body)
+
+ then:
+ payload.messages.find { it.text == "Category depth of $Category.MAX_DEPTH may not be exceeded" }
+ }
+
+ def 'can NOT move a Category BEFORE another Category exceeding the depth limitation'() {
+ given:
+ Map body = [targetId: 9, position: 'INSIDE']
+ path(catalog: 0, category: 272)
+ invalid()
+
+ when:
+ put(body)
+
+ then:
+ payload.messages.find { it.text == "Category depth of $Category.MAX_DEPTH may not be exceeded" }
+ }
+
+ def 'can move a Category AFTER a SIBLING Category'() {
+ given:
+ int categoryId = 305
+ int targetId = 300
+ Map body = [targetId: targetId, position: 'AFTER']
+ path(catalog: 0, category: categoryId)
+ ok()
+
+ when:
+ put(body)
+
+ then:
+ Thread.sleep(1000)
+ payload.results.sequence == 6
+ }
+
+ def 'can move a Category BEFORE a SIBLING Category'() {
+ given:
+ int categoryId = 305
+ int targetId = 300
+ Map body = [targetId: targetId, position: 'BEFORE']
+ path(catalog: 0, category: categoryId)
+ ok()
+
+ when:
+ put(body)
+
+ then:
+ Thread.sleep(1000)
+ payload.results.sequence == 5
+ }
+
+ def 'can move a Category to the first sibling UNDER another Category'() {
+ given:
+ int targetId = 80
+ int categoryId = 81
+ Map body = [targetId: targetId, position: 'INSIDE']
+ path(catalog: 1, category: categoryId)
+ ok()
+
+ when:
+ put(body)
+
+ then:
+ payload.results.parentCategoryId == targetId
+ payload.results.sequence == 1
+ }
+
+ def 'can move a Category back alongside AFTER its original sibling Category'() {
+ given:
+ int targetId = 80
+ int categoryId = 81
+ Map body = [targetId: targetId, position: 'AFTER']
+ path(catalog: 1, category: categoryId)
+ ok()
+
+ when:
+ put(body)
+
+ then:
+ payload.results.parentCategoryId == null
+ payload.results.sequence == 3
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategoryPartFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategoryPartFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategoryPartFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,95 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Stepwise
+
+@Stepwise
+class CategoryPartFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'part' }
+
+ int categoryId = 434
+ int catalogId = 0
+ Map mapping = [catalog: catalogId, category: categoryId]
+
+ def 'can NOT add Parts to an invalid Category'() {
+ given:
+ List partNumbers = ['B8ES', 'B9ES']
+ String action = 'add'
+ path([catalog: -1, category: 2])
+ invalid()
+
+ when:
+ put([partNumbers: partNumbers, action: action])
+
+ then:
+ with(payload.messages[0]) {
+ text == 'Invalid category'
+ type == 'error'
+ }
+ }
+
+ def 'can NOT add invalid Parts to a Category'() {
+ given:
+ List partNumbers = ['B10ES', '99912345']
+ String action = 'add'
+ path(mapping)
+ invalid()
+
+ when:
+ put([partNumbers: partNumbers, action: action])
+
+ then:
+ with(payload.messages[0]) {
+ text == 'Invalid parts found [99912345]'
+ type == 'error'
+ }
+ }
+
+ def 'can not perform add Parts to a Category without any parts'() {
+ given:
+ String action = 'add'
+ path(mapping)
+ invalid()
+
+ when:
+ put([action: action])
+
+ then:
+ with(payload.messages[0]) {
+ text == 'No part selected'
+ type == 'error'
+ }
+ }
+
+ def 'can add Parts to a Category'() {
+ given:
+ List partNumbers = ['B8ES', 'B9ES']
+ String action = 'add'
+ path(mapping)
+ ok()
+
+ when:
+ put([partNumbers: partNumbers, action: action])
+
+ then:
+ !payload.messages
+ payload.results == []
+ }
+
+ def 'can remove Parts from a Category'() {
+ given:
+ List partNumbers = ['B8ES', 'B9ES']
+ String action = 'remove'
+ path(mapping)
+ ok()
+
+ when:
+ put([partNumbers: partNumbers, action: action])
+
+ then:
+ !payload.messages
+ payload.results == []
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategoryPersistenceFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategoryPersistenceFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategoryPersistenceFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,156 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+import spock.lang.Stepwise
+
+@Stepwise
+class CategoryPersistenceFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'category' }
+
+ @Shared Integer theCategoryId
+
+ @Shared Integer catalogId = 0
+
+ def 'can NOT delete a root Category that does not exist'() {
+ given:
+ path([catalog: catalogId], -1)
+ notFound()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT create an invalid root Category for a Catalog'() {
+ given:
+ Map category = [catalogInstanceId: catalogId]
+ path(catalog: catalogId)
+ invalid()
+
+ when:
+ post(category)
+
+ then:
+ payload.messages[0].field == 'categoryName'
+ payload.messages[0].text == 'Category Name is required'
+ }
+
+ def 'can create a root Category for a Catalog'() {
+ given:
+ String name = 'testName_' + new Date()
+ Map category = [catalogInstanceId: catalogId, categoryName: name]
+ path(catalog: catalogId)
+ ok()
+
+ when:
+ post(category)
+ theCategoryId = payload.results.categoryId
+
+ then:
+ with(payload.results) {
+ catalogInstanceId == this.catalogId
+ categoryName == name
+ parentCategoryId == null
+ sequence
+ categoryId
+ }
+ }
+
+ def 'can NOT create a duplicate root Category for a Catalog'() {
+ given:
+ String name = 'Parent Category 1'
+ Map category = [catalogInstanceId: catalogId, categoryName: name]
+ path(catalog: catalogId)
+ invalid()
+
+ when:
+ post(category)
+
+ then:
+ payload.messages.find { it.code == 'DUPLICATE_KEY' }
+ }
+
+ def 'can update a Category'() {
+ given:
+ String description = 'test_' + new Date()
+ Map category = [description: description, version: -9999]
+ path([catalog: catalogId], theCategoryId)
+ ok()
+
+ when:
+ put(category)
+
+ then:
+ payload.results.description == description
+ }
+
+ def 'can NOT update a Category catalogInstanceId'() {
+ given:
+ Map category = [catalogInstanceId: 1]
+ path([catalog: catalogId], theCategoryId)
+ invalid()
+
+ when:
+ put(category)
+
+ then:
+ payload.messages.find { it.field == 'catalogInstanceId' }.text == 'Catalog may not be changed'
+ }
+
+ def 'can NOT update a Category parentCategoryId directly'() {
+ given:
+ Map category = [parentCategoryId: 1]
+ path([catalog: catalogId], theCategoryId)
+ invalid()
+
+ when:
+ put(category)
+
+ then:
+ payload.messages.find { it.field == 'parentCategoryId' }.text == 'Category Parent may not be changed'
+ }
+
+ def 'can NOT update a Category sequence directly'() {
+ given:
+ Map category = [sequence: 1]
+ path([catalog: catalogId], theCategoryId)
+ invalid()
+
+ when:
+ put(category)
+
+ then:
+ payload.messages.find { it.field == 'sequence' }.text == 'Category Sequence may not be changed'
+ }
+
+ def 'can delete a root Category without children'() {
+ given:
+ queryParams.version = -9999
+ path([catalog: catalogId], theCategoryId)
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT delete a root Category with children'() {
+ given:
+ queryParams.version = -9999
+ path([catalog: catalogId], 2)
+ invalid()
+
+ when:
+ delete()
+
+ then:
+ payload.messages.find { it.text == 'Category may not be deleted' }
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategoryProductFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategoryProductFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategoryProductFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,112 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.testing.DsFuncSpec
+
+
+class CategoryProductFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'product' }
+
+ int categoryId = 434
+ int catalogId = 0
+ int product1 = 343317
+ //int product2 = 343317
+ Map mapping = [catalog: catalogId, category: categoryId]
+
+ def 'can NOT add Product to an invalid Catalog'() {
+ given:
+ List productIds = [product1, 1234]
+ String action = 'INSERT'
+ path([catalog: -1, category: 2])
+ invalid()
+
+ when:
+ post([productIds: productIds, operation: action])
+
+ then:
+ with(payload.messages[0]) {
+ text == 'Invalid category or catalog'
+ type == 'error'
+ }
+ }
+
+ def 'can NOT add Product to an invalid Category'() {
+ given:
+ List productIds = [product1, 1234]
+ String action = 'INSERT'
+ path([catalog: catalogId, category: -1])
+ invalid()
+
+ when:
+ post([productIds: productIds, operation: action])
+
+ then:
+ with(payload.messages[0]) {
+ text == 'Invalid category or catalog'
+ type == 'error'
+ }
+ }
+
+ def 'can NOT add category to Invalid productId'() {
+ given:
+ List productIds = [product1, 1234]
+ String action = 'INSERT'
+ path([catalog: catalogId, category: categoryId])
+ invalid()
+
+ when:
+ post([productIds: productIds, operation: action])
+
+ then:
+ with(payload.messages[0]) {
+ text.contains('Invalid productId')
+ type == 'error'
+ }
+ }
+
+ /*def 'can NOT add category to products with parts of different category '() {
+ given:
+ List productIds = [product1]
+ String action = 'INSERT'
+ path([catalog: catalogId, category: categoryId])
+ invalid()
+
+ when:
+ post([productIds: productIds, operation: action])
+
+ then:
+ with(payload.messages[0]) {
+ text.contains('Invalid productId')
+ type == 'error'
+ }
+ }*/
+
+ def 'can add category to products'() {
+ given:
+ List productIds = [product1]
+ String action = 'INSERT'
+ path([catalog: catalogId, category: categoryId])
+ ok()
+
+ when:
+ post([productIds: productIds, operation: action])
+
+ then:
+ !payload.results
+ }
+
+ def 'can remove Category for product'() {
+ given:
+ List productIds = [product1]
+ String action = 'DELETE'
+ path([catalog: catalogId, category: categoryId])
+ ok()
+
+ when:
+ post([productIds: productIds, operation: action])
+
+ then:
+ !payload.results
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategorySubComCodeFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategorySubComCodeFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategorySubComCodeFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,59 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class CategorySubComCodeFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'subComCode' }
+
+ final static int ALL_COLUMNS_SIZE = 13
+
+ Integer catalogId = 1
+ Integer categoryId = 81
+ Map mapping = [catalog: catalogId, category: categoryId]
+
+ def 'can NOT find a Category SubComCode that does not exist'() {
+ given:
+ path(mapping, -1)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find a Category SubComCode'() {
+ given:
+ Integer subComCodeId = 106
+ path(mapping, subComCodeId)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.subComCodeId == subComCodeId
+ payload.results.categoryId == categoryId
+ payload.results.catalogInstanceId == catalogId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can find Category SubComCodes'() {
+ given:
+ path(mapping)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.catalogInstanceId.every { it == catalogId }
+ payload.results.categoryId.every { it == categoryId }
+ payload.results.subComCode.every { it != null }
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ payload.results.size() > 2
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/category/CategorySubComCodePersistenceFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/category/CategorySubComCodePersistenceFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/category/CategorySubComCodePersistenceFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,71 @@
+package com.lemans.ds.category
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Stepwise
+
+@Stepwise
+class CategorySubComCodePersistenceFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'subComCode' }
+
+ final static int ALL_COLUMNS_SIZE = 11
+
+ Integer catalogId = 1
+ Integer categoryId = 81
+ Integer subComCodeId = 136
+
+ Map mapping = [catalog: catalogId, category: categoryId]
+
+ def 'can NOT add a mapping for a SubComCode that does not exist to a Category'() {
+ given:
+ path(mapping)
+ invalid()
+
+ when:
+ post([subComCodeId: -1])
+
+ then:
+ payload.messages.find { it.code == 'DATA_INTEGRITY_VIOLATION' }
+ }
+
+ def 'can NOT remove a CategorySubComCode mapping that does not exist to a Category'() {
+ given:
+ path(mapping, -1)
+ notFound()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can add add a mapping for a SubComCode to a Category'() {
+ given:
+ path(mapping)
+ ok()
+
+ when:
+ post(subComCodeId: subComCodeId)
+
+ then:
+ with(payload.results) {
+ categorySubComCodeId
+ subComCodeId == this.subComCodeId
+ categoryId == this.categoryId
+ }
+ }
+
+ def 'can remove a mapping for a SubComCode from a Category'() {
+ given:
+ path(mapping, subComCodeId)
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/explosion/ExplosionDiagramFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/explosion/ExplosionDiagramFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/explosion/ExplosionDiagramFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,130 @@
+package com.lemans.ds.explosion
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+import spock.lang.Stepwise
+
+@Stepwise
+class ExplosionDiagramFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'explosionDiagram' }
+
+ @Shared Integer explosionDiagramId
+
+ Integer invalidId = -1
+
+ def 'can not add explosion diagram'() {
+ given:
+ Map input = [title: 'test', jsonData: "results:[description: 'test']"]
+ path()
+ invalid()
+
+ when:
+ post(input)
+
+ then:
+ payload.messages[0].type == 'error'
+ payload.messages[0].text == 'isActive is required'
+ }
+
+ def 'can not get explosion diagram'() {
+ given:
+ path("$invalidId")
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload.results
+ }
+
+ def 'can not update explosion diagram'() {
+ given:
+ Map input = [jsonData: "results:[description: 'test update']", isActive: 1]
+ path("$invalidId")
+ notFound()
+
+ when:
+ put(input)
+
+ then:
+ !payload
+ }
+
+ def 'can not delete explosion diagram'() {
+ given:
+ path("$invalidId")
+ notFound()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can add explosionDiagram'() {
+ given:
+ Map input = [title: 'test', jsonData: "results:[description: 'test']", isActive: 1]
+ path()
+ ok()
+
+ when:
+ post(input)
+ explosionDiagramId = payload.results.explosionDiagramId
+
+ then:
+ payload.results.jsonData == "results:[description: 'test']"
+ }
+
+ def 'can get all explosion diagrams'() {
+ given:
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload
+ }
+
+ def 'can get explosionDiagram'() {
+ given:
+ path("$explosionDiagramId")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload
+ }
+
+ def 'can update explosionDiagram'() {
+ given:
+ Map input = [jsonData: "results:[description: 'test update']", isActive: 1]
+ path("$explosionDiagramId")
+ ok()
+
+ when:
+ put(input)
+
+ then:
+ payload.results.jsonData == "results:[description: 'test update']"
+ }
+
+ def 'can delete explosionDiagram'() {
+ given:
+ path("$explosionDiagramId")
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/exporting/BulkUploadExportReportFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/exporting/BulkUploadExportReportFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/exporting/BulkUploadExportReportFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,64 @@
+package com.lemans.ds.exporting
+
+import com.lemans.ds.testing.DsFuncSpec
+
+
+class BulkUploadExportReportFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'report' }
+
+
+ def 'can download a report for locale bulk upload'() {
+ given:
+ String reportName = 'productLocaleExport'
+ queryParams += [productId: '8945',
+ urlContext: 'https://dashboard4.stage.lemanscorp.com/digital-services/product/$param1/parts']
+ path("$reportName/download")
+
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.response.contentType == 'application/vnd.ms-excel'
+ payload.response.headers.'content-disposition'.contains 'ProductExport.xls'
+ }
+
+ def 'can download a category attribute value report for locale bulk upload'() {
+ given:
+ String reportName = 'categoryAttributeLocaleExport'
+ queryParams += [categoryId: '101',
+ urlContext: 'https://dashboard4.stage.lemanscorp.com/digital-services/category/$param1/parts']
+ path("$reportName/download")
+
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.response.contentType == 'application/vnd.ms-excel'
+ payload.response.headers.'content-disposition'.contains 'CategoryAttributeValueExport.xls'
+ }
+
+ def 'can download a part report for locale bulk upload'() {
+ given:
+ String reportName = 'partLocaleExport'
+ queryParams += [partNumber: '', exportPartList: '0001CRKICK, 0004212535, 0010, 0010401', partStatusId: '', derivedPartStatusId: '',
+ partDescr: '', brandCode: '', vendorId: '', vendorPartNumber: '', subComCodeId: '', qPart: '', productId: '',
+ categoryId: '4537', isDigiActive: '', modelId: '', startYear: '', endYear: '', attributeNameId: '', attributeValueId: '',
+ urlContext: 'https://dashboard4.stage.lemanscorp.com/digital-services/part/$param1/view']
+ path("$reportName/download")
+
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.response.contentType == 'application/vnd.ms-excel'
+ payload.response.headers.'content-disposition'.contains 'PartExport.xls'
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/fitment/MakeFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/fitment/MakeFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/fitment/MakeFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,80 @@
+package com.lemans.ds.fitment
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class MakeFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'make' }
+
+ final static int ALL_COLUMNS_SIZE = 10
+
+ def 'can find a Make'() {
+ given:
+ int id = 1
+ path(id)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.makeId == id
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT find a Make that does not exist'() {
+ given:
+ int id = -1
+ path(id)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can search for a Make by makeName'() {
+ given:
+ queryParams.makeName = 'john*'
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results[0].makeName.startsWith('John')
+ apiPaginated() == 1
+ }
+
+ def 'can search for a Make by q parameter on makeName'() {
+ given:
+ queryParams.q = 'john'
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results[0].makeName.startsWith('John')
+ apiPaginated() == 1
+ }
+
+ def 'can find Makes'() {
+ given:
+ queryParams.columns = 'makeId,makeName'
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.makeId.every { it != null }
+ apiPaginated() > 50
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/fitment/MakePersistenceFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/fitment/MakePersistenceFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/fitment/MakePersistenceFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,201 @@
+package com.lemans.ds.fitment
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+import spock.lang.Stepwise
+import spock.lang.Unroll
+
+@Stepwise
+class MakePersistenceFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'make' }
+
+ @Shared Integer makeId
+
+ def 'can NOT create an invalid Make'() {
+ given:
+ path()
+ invalid()
+
+ when:
+ post()
+ List errors = payload.messages.findAll { it.type = 'error' }
+
+ then:
+ errors.find { it.field == 'makeName' }
+ errors.find { it.field == 'makeName' }.text == 'makeName is required'
+ errors.find { it.field == 'modelNameFormat' }
+ errors.find { it.field == 'modelNameFormat' }.text == 'modelNameFormat is required'
+ }
+
+ @Unroll
+ def 'can NOT create a Make with invalid modelNameFormat #modelNameFormat with repeating segments'() {
+ given:
+ path()
+ invalid()
+ String makeName = 'test_' + new Date()
+ Map make = [makeName: makeName, modelNameFormat: modelNameFormat]
+
+ when:
+ post(make)
+ List errors = payload.messages.findAll { it.type = 'error' }
+
+ then:
+ errors.find { it.field == 'modelNameFormat' }
+ errors.find { it.field == 'modelNameFormat' }.text == 'Invalid Model Name Format. Segments can not be repeated.'
+
+ where:
+ modelNameFormat << ['M-T-T, DT-E-E, L-C-L']
+ }
+
+ @Unroll
+ def 'can NOT create a Make with invalid modelNameFormat #modelNameFormat with invalid segment codes'() {
+ given:
+ path()
+ invalid()
+ String makeName = 'test_' + new Date()
+ Map make = [makeName: makeName, modelNameFormat: modelNameFormat]
+
+ when:
+ post(make)
+ List errors = payload.messages.findAll { it.type = 'error' }
+
+ then:
+ errors.find { it.field == 'modelNameFormat' }
+ errors.find { it.field == 'modelNameFormat' }.text == 'Invalid Model Name Format. Invalid segment codes present.'
+
+ where:
+ modelNameFormat << ['Z-U', 'ZU', 'Z-UU', 'Z-U-U', 'Z-UUU', 'Z-UU']
+ }
+
+ def 'can NOT update an invalid Make'() {
+ given:
+ path(1)
+ invalid()
+
+ when:
+ put([makeName: null])
+ List errors = payload.messages.findAll { it.type = 'error' }
+
+ then:
+ errors.find { it.field == 'makeName' }
+ errors.find { it.field == 'makeName' }.text == 'makeName is required'
+ }
+
+ def 'can NOT delete a Make that does not exist'() {
+ given:
+ path(-1)
+ notFound()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT delete a Make that has parts and models associated'() {
+ given:
+ path(5)
+ invalid()
+
+ when:
+ delete()
+
+ then:
+ with(payload) {
+ with(messages[0]) {
+ type == 'error'
+ text.matches('Cannot delete Make that has \\d+ parts associated')
+ }
+ with(messages[1]) {
+ type == 'error'
+ text.matches('Cannot delete Make that has \\d+ models associated')
+ }
+ }
+ }
+
+ def 'can create a valid Make'() {
+ given:
+ String name = 'test_' + new Date()
+ Map make = [makeName: name, modelNameFormat: 'M-T-D-L-F-G']
+ path()
+ ok()
+
+ when:
+ post(make)
+ makeId = payload.results.makeId
+
+ then:
+ with(payload.results) {
+ makeName == name
+ }
+ }
+
+ def 'can update a valid Make'() {
+ given:
+ String name = 'more_test_' + new Date()
+ Map make = [makeName: name, modelNameFormat: 'M/T/DT/L/F/G']
+ path(makeId)
+ ok()
+
+ when:
+ put(make)
+
+ then:
+ payload.results.makeId == makeId
+ payload.results.makeName == name
+ }
+
+ @Unroll
+ def 'can NOT update a Make with invalid modelNameFormat #modelNameFormat with repeating segments'() {
+ given:
+ path(makeId)
+ invalid()
+ String makeName = 'test_' + new Date()
+ Map make = [makeName: makeName, modelNameFormat: modelNameFormat]
+
+ when:
+ put(make)
+ List errors = payload.messages.findAll { it.type = 'error' }
+
+ then:
+ errors.find { it.field == 'modelNameFormat' }
+ errors.find { it.field == 'modelNameFormat' }.text == 'Invalid Model Name Format. Segments can not be repeated.'
+
+ where:
+ modelNameFormat << ['M-T-T', 'DT-E-E', 'L-C-L']
+ }
+
+ def 'can NOT update a Make with invalid modelNameFormat #modelNameFormat with invalid segment codes'() {
+ given:
+ path(makeId)
+ invalid()
+ String makeName = 'test_' + new Date()
+ Map make = [makeName: makeName, modelNameFormat: modelNameFormat]
+
+ when:
+ put(make)
+ List errors = payload.messages.findAll { it.type = 'error' }
+
+ then:
+ errors.find { it.field == 'modelNameFormat' }
+ errors.find { it.field == 'modelNameFormat' }.text == 'Invalid Model Name Format. Invalid segment codes present.'
+
+ where:
+ modelNameFormat << ['Z-U', 'ZU', 'Z-UU', 'Z-U-U', 'Z-UUU', 'Z-UU']
+ }
+
+ def 'can delete a Make'() {
+ given:
+ path(makeId)
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/fitment/ModelFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/fitment/ModelFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/fitment/ModelFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,71 @@
+package com.lemans.ds.fitment
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class ModelFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'model' }
+
+ def 'can find a Model'() {
+ given:
+ int id = 302
+ path([make: 1], id)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.modelId == id
+ }
+
+ def 'can NOT find a Model that does not exist'() {
+ given:
+ int id = -1
+ path([make: 1], id)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find Models by make'() {
+ given:
+ path([make: 1])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results[0].makeId == 1
+ }
+
+ def 'can Not find Models for invalid make'() {
+ given:
+ path([make: -1])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results == []
+ }
+
+ def 'can Not find Models with out a make'() {
+ given:
+ path([make: null])
+ notFound()
+
+ when:
+ get()
+
+ then:
+ payload.messages[0].text == 'Make is required'
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/fitment/ModelPersistenceFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/fitment/ModelPersistenceFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/fitment/ModelPersistenceFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,373 @@
+package com.lemans.ds.fitment
+
+import com.lemans.ds.testing.DsFuncSpec
+import groovy.sql.Sql
+import spock.lang.Shared
+import spock.lang.Stepwise
+
+import java.time.Year
+
+@Stepwise
+class ModelPersistenceFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'model' }
+
+ @Shared Integer modelId
+
+
+ def dataSource
+
+ def 'can NOT create an invalid Model'() {
+ given:
+ path([make: 1])
+ String segmentValue = 'test_' + new Date()
+ invalid()
+ Map model = [segments: [[segmentCode: 'X', value: segmentValue]]]
+
+ when:
+ post(model)
+
+ then:
+ payload.messages.find { it.text == 'segment is required' }
+ }
+
+ def 'can NOT create Model with invalid Make'() {
+ given:
+ path([make: -1])
+ invalid()
+
+ when:
+ post()
+
+ then:
+ payload.messages.find { it.text == 'Make is invalid or not provided' }
+ }
+
+ def 'can NOT delete a Model that does not exist'() {
+ given:
+ path([make: 1], -1)
+ notFound()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT delete a Model that has parts associated to it'() {
+ given:
+ path([make: 101], 11475)
+ invalid()
+
+ when:
+ delete()
+
+ then:
+ payload.messages[0].text == 'Found Model(s) [11475] associated to parts. Cannot delete models that has parts Associated'
+ }
+
+ def 'can NOT add multiple years to multiple models with invalid modelIds'() {
+ given:
+ Map model = [operation: 'ADDYEAR', modelIds: [32, 47, -1], years: [2013, 2014]]
+ path([make: 7])
+ invalid()
+
+ when:
+ post(model)
+
+ then:
+ payload.messages[0].text == 'Invalid Model Ids found [-1]'
+ }
+
+ def 'can NOT delete multiple models invalid modelIds'() {
+ given:
+ Map model = [operation: 'DELETE', modelIds: [-1, -2]]
+ path([make: 7])
+ invalid()
+
+ when:
+ post(model)
+
+ then:
+ payload.messages[0].text == 'Invalid Model Ids found [-1, -2]'
+ }
+
+ def 'can create a valid Model'() {
+ given:
+ String segmentValue = 'test_' + new Date()
+ Map model = [vehicleTypeId: '18001', modelNameFormat: 'E L',
+ segments: [[segmentCode: 'E', value: segmentValue], [segmentCode: 'L', value: segmentValue]],
+ years: [2001, 2002]]
+
+ path([make: 1])
+ ok()
+
+ when:
+ post(model)
+ modelId = payload.results.modelId
+
+ then:
+ with(payload.results) {
+ makeId == 1
+ year.year == [2001, 2002]
+ segment.value == [segmentValue, segmentValue]
+ segment.segmentCode == ['L', 'E']
+ }
+ }
+
+ def 'can NOT create a valid Model with Empty Segment'() {
+ given:
+ Map model = [vehicleTypeId: '18001', modelNameFormat: 'E L',
+ segments: [],
+ years: [2001, 2002]]
+
+ path([make: 1])
+ invalid()
+
+ when:
+ post(model)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'At least one Segment is required for a Model'
+ }
+ }
+
+ def 'can NOT create a duplicate Model'() {
+ given:
+ String segmentValue = 'test'
+ Map model = [vehicleTypeId: '18001', modelNameFormat: 'M T',
+ segments: [[segmentCode: 'M', value: segmentValue], [segmentCode: 'T', value: segmentValue]],
+ years: [2001, 2002]]
+ path([make: 1])
+ invalid()
+
+ when:
+ post(model)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'Model with following segments and segment Values already exists'
+ }
+ }
+
+ def 'can add segment to a Model by segmentCode'() {
+ given:
+ Map segmentValue = [value: 'segmentValue__']
+ path([make: 1], "$modelId/segment/D")
+ ok()
+
+ when:
+ put(segmentValue)
+
+ then:
+ with(payload.results) {
+ it.modelId == modelId
+ value == 'segmentValue__'
+ }
+ }
+
+ def 'can update segment of a Model by segmentCode'() {
+ given:
+ Map segmentValue = [value: 'segmentValueEdited']
+ path([make: 1], "$modelId/segment/M")
+ ok()
+
+ when:
+ put(segmentValue)
+
+ then:
+ with(payload.results) {
+ it.modelId == modelId
+ value == 'segmentValueEdited'
+ }
+ }
+
+ def 'can NOT update segment of a Model to null by segmentCode for Model Id that one Segment Value is remaining'() {
+ given:
+ Map segmentValue = [value: null]
+ path([make: 13], '104/segment/M')
+ invalid()
+
+ when:
+ put(segmentValue)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'At least One Segment is required for a Model'
+ }
+ }
+
+ def 'can update segment of a Model to null by segmentCode'() {
+ given:
+ Map segmentValue = [value: null]
+ path([make: 1], "$modelId/segment/M")
+ ok()
+
+ when:
+ put(segmentValue)
+
+ then:
+ with(payload.results) {
+ it.modelId == modelId
+ }
+ }
+
+ def 'can NOT add multiple years to multiple models with invalid years'() {
+ given:
+ Map model = [operation: 'ADDYEAR', modelIds: [32, 47], years: [1700, 2013, 2014]]
+ path([make: 7])
+ invalid()
+
+ when:
+ post(model)
+
+ then:
+ payload.messages[0].text == "Invalid Years found [1700]. Years should be between 1894 and ${Year.now().value + 1}"
+ }
+
+ def 'can add multiple years to multiple models'() {
+ given:
+ Map model = [operation: 'ADDYEAR', modelIds: [32, 47], years: [2013, 2014]]
+ path([make: 7])
+ ok()
+
+ when:
+ post(model)
+
+ then:
+ payload.results == []
+ }
+
+ def 'can delete multiple years to multiple models'() {
+ given:
+ Map model = [operation: 'DELETEYEAR', modelIds: [32, 47], years: [2013, 2014]]
+ path([make: 7])
+ ok()
+
+ when:
+ post(model)
+
+ then:
+ payload.results == []
+ }
+
+ def 'can delete multiple models'() {
+ given:
+ Map model = [operation: 'DELETE', modelIds: [169, 2797]]
+ path([make: 9])
+ ok()
+
+ when:
+ post(model)
+
+ then:
+ payload.results == []
+
+ cleanup:
+ new Sql(dataSource).execute('''
+ UPDATE Model
+ SET dateDeleted = NULL
+ ,deletedBy = NULL
+ WHERE modelId IN (218, 223) AND dateDeleted IS NOT NULL''')
+ }
+
+ def 'can NOT delete models containing parts'() {
+ given:
+ Map model = [operation: 'DELETE', modelIds: [302, 488, 603, 606, 1354]]
+ path([make: 1])
+ invalid()
+
+ when:
+ post(model)
+
+ then:
+ payload.messages[0].text == 'Found Model(s) [488, 603, 606] associated to parts. Cannot delete models that has parts Associated'
+ }
+
+ @SuppressWarnings(['LineLength'])
+ def 'can NOT update a valid Model by adding invalid years'() {
+ given:
+ Map model = [vehicleTypeId: '18001', modelNameFormat: 'M T',
+ segments: [[segmentCode: 'M', value: 'test_edited'],
+ [segmentCode: 'T', value: 'test_edited2'], [segmentCode: 'D', value: 'test_edited3']],
+ years: [1800, 9000]]
+ path([make: 1], modelId)
+ invalid()
+
+ when:
+ put(model)
+
+ then:
+ payload.messages.find { it.field != 'modelName' }.text == "Found Invalid Years [1800, 9000]. Years should be in between 1894 and ${Year.now().value + 1}"
+ }
+
+ def 'can NOT update a Model with invalid segmentCodes'() {
+ given:
+ Map model = [vehicleTypeId: '18001', modelNameFormat: 'Z U P',
+ segments: [[segmentCode: 'Z', value: 'test_edited'],
+ [segmentCode: 'U', value: 'test_edited2'], [segmentCode: 'P', value: 'test_edited3']]]
+ path([make: 1], modelId)
+ invalid()
+
+ when:
+ put(model)
+
+ then:
+ payload.messages.find { it.field == 'modelNameFormat' }.text == 'Invalid Model Name Format. Invalid segment codes present.'
+ }
+
+ def 'can update a valid Model'() {
+ given:
+ Map model = [vehicleTypeId: '18001', modelNameFormat: 'M T',
+ segments: [[segmentCode: 'M', value: 'test_edited'],
+ [segmentCode: 'T', value: 'test_edited2'], [segmentCode: 'D', value: 'test_edited3']],
+ years: [2011, 2015]]
+ path([make: 1], modelId)
+ ok()
+
+ when:
+ put(model)
+
+ then:
+ with(payload.results) {
+ makeId == 1
+ year.year == [2001, 2002, 2011, 2015]
+ segment.segmentCode == ['M', 'T', 'D', 'L', 'E']
+ }
+ }
+
+ def 'can update a valid Model with modelNameFormat'() {
+ given:
+ Map model = [modelNameFormat: 'M T']
+ path([make: 1], modelId)
+ ok()
+
+ when:
+ put(model)
+
+ then:
+ with(payload.results) {
+ makeId == 1
+ year.year == [2001, 2002, 2011, 2015]
+ segment.segmentCode == ['M', 'T', 'D', 'L', 'E']
+ }
+ }
+
+ def 'can delete a Model'() {
+ given:
+ path([make: 1], modelId)
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/fitment/ModelYearFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/fitment/ModelYearFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/fitment/ModelYearFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,112 @@
+package com.lemans.ds.fitment
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Ignore
+import spock.lang.Shared
+import spock.lang.Stepwise
+
+@Stepwise
+class ModelYearFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'year' }
+
+ @Shared List yearIds
+
+ @Shared List years
+
+ def 'can NOT create an invalid Year'() {
+ given:
+ path([make: 1, model: 302])
+ invalid()
+
+ when:
+ post()
+
+ then:
+ payload.messages.find { it.text == 'year(s) is required' }
+ }
+
+ @Ignore
+ def 'can NOT delete a Year that does not exist'() {
+ given:
+ path([make: 1, model: 302], -1)
+ notFound()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can create a valid Year'() {
+ given:
+ Integer year = 2017
+ path([make: 1, model: 302])
+ ok()
+
+ when:
+ post([year: year])
+
+ then:
+ payload.results.year == year
+ }
+
+ def 'can add multiple years to a make'() {
+ given:
+ Map year = [years: [2015, 2016]]
+ path([make: 1, model: 302])
+ ok()
+
+ when:
+ post(year)
+
+ then:
+ payload.results.size() == 2
+ with (payload) {
+ results[0].year == 2015
+ results[1].year == 2016
+ }
+ }
+
+ def 'can find Years'() {
+ given:
+ queryParams.columns = 'modelYearId, year'
+ path([make: 1, model: 302])
+ ok()
+
+ when:
+ get()
+ yearIds = payload.results.modelYearId
+ years = payload.results.year
+
+ then:
+ payload.results.modelYearId.every { it != null }
+ apiPaginated() > 2
+ }
+
+ def 'can delete a Year by Id'() {
+ given:
+ path([make: 1, model: 302], yearIds[0])
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can delete multiple years'() {
+ given:
+ path([make: 1, model: 302])
+ ok()
+
+ when:
+ post([operation: 'DELETE', years: years])
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/fitment/PartFitmentFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/fitment/PartFitmentFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/fitment/PartFitmentFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,131 @@
+package com.lemans.ds.fitment
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+import spock.lang.Stepwise
+
+/**
+ * Created by vramisetti on 6/13/2017.
+ */
+@Stepwise
+class PartFitmentFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'fitment' }
+
+ @Shared List partFitmentIds = []
+
+ def 'can NOT find partFitments for invalid part'() {
+ given:
+ String pNum = 'B8ESSS'
+ path([part: pNum])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.meta.totalRecords == 0
+ payload.results == []
+ }
+
+ def 'can add a partFitment'() {
+ given:
+ String pNum = 'B8ES'
+ Integer mYId = 100
+ path([part: pNum])
+ ok()
+ Map json = [modelYearId: mYId, positions: [22001, 22002, 22003, 22004]]
+
+ when:
+ post(json)
+ partFitmentIds << payload.results.partFitmentId
+
+ then:
+ with(payload.results) {
+ partNumber == pNum
+ modelYearId == mYId
+ }
+ }
+
+ def 'can NOT add a partFitment with invalid modelYearId'() {
+ given:
+ String pNum = 'B8ES'
+ Integer mYId = -1
+ path([part: pNum])
+ invalid()
+ Map json = [modelYearId: mYId]
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'Invalid model year\'s found [-1]'
+ }
+ }
+
+ def 'can find partFitments'() {
+ given:
+ String pNum = 'B8ES'
+ path([part: pNum])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload
+ }
+
+ def 'can delete a partFitment'() {
+ given:
+ String partNumber = 'B8ES'
+ Integer partFitmentId = partFitmentIds[0]
+ path([part: partNumber], partFitmentId)
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can add multiple partFitments'() {
+ given:
+ String pNum = 'B8ES'
+ Integer mYId = 96990
+ path([part: pNum])
+ ok()
+ Map json = [modelYearIds: [mYId, 96991, 96992],
+ positions: [22001, 22002]]
+
+ when:
+ post(json)
+ payload.results.each {
+ partFitmentIds << it.partFitmentId
+ }
+
+ then:
+ with(payload) {
+ results.every { it.partNumber == pNum }
+ results.modelYearId == [96990, 96991, 96992]
+ }
+ }
+
+ def 'can delete multiple partFitments'() {
+ given:
+ String partNumber = 'B8ES'
+ path([part: partNumber])
+ ok()
+ Map json = [partFitmentIds: partFitmentIds, operation: 'DELETE']
+
+ when:
+ post(json)
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/fitment/SegmentFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/fitment/SegmentFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/fitment/SegmentFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,66 @@
+package com.lemans.ds.fitment
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class SegmentFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { '' }
+
+ def 'can find Segments'() {
+ given:
+ queryParams.columns = 'segmentId,segmentName,segmentCode'
+ path([make: 1], 'segment')
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.segmentId.every { it != null }
+ apiPaginated() > 3
+ }
+
+ def 'can find Segment values by segmentCodes'() {
+ given:
+ path([make: 1, segment: 'M'], 'value')
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.segmentId.every { it != null }
+ apiPaginated() > 3
+ }
+
+ def 'can search Segment values '() {
+ given:
+ queryParams.value = 'MX*'
+ path([make: 10, segment: 'M'], 'value')
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.segmentId.every { it != null }
+ payload.results.value.every { it.startsWith('MX') }
+ apiPaginated() >= 1
+ }
+
+ def 'can search Segment values by q parameter'() {
+ given:
+ queryParams.q = 'Model'
+ path([make: 1, segment: 'M'], 'value')
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.segmentId.every { it != null }
+ payload.results.value.every { it.contains('Model') }
+ apiPaginated() > 3
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/flag/FlagMediaEntityFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/flag/FlagMediaEntityFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/flag/FlagMediaEntityFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,76 @@
+package com.lemans.ds.flag
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class FlagMediaEntityFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'flag' }
+
+ def 'can create flagValue with mediaId'() {
+ given:
+ Map input = [flagIds: [23], operation: 'INSERT']
+ path([media: 3])
+ queryParams.entityClass = 'Media'
+ ok()
+
+ when:
+ post(input)
+
+ then:
+ payload
+ }
+
+ def 'can not create flagValue with invalid mediaId'() {
+ given:
+ Map input = [flagIds: [23], operation: 'INSERT']
+ path([media: -3])
+ queryParams.entityClass = 'Media'
+ invalid()
+
+ when:
+ post(input)
+
+ then:
+ payload.messages[0].type == 'error'
+ payload.messages[0].text == 'Media with mediaId -3 does not exist'
+ }
+
+ def 'can get flags for media'() {
+ given:
+ path([media: 3])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload
+ }
+
+ def 'can get flagvalue for a media and flag'() {
+ given:
+ path([media: 3], 23)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload
+ }
+
+ def 'can delete flagValue with mediaId'() {
+ given:
+ path([media: 3])
+ Map input = [flagIds: [23], operation: 'DELETE']
+ queryParams.entityClass = 'Media'
+ ok()
+
+ when:
+ post(input)
+
+ then:
+ payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/flag/FlagPartEntityPersistenceFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/flag/FlagPartEntityPersistenceFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/flag/FlagPartEntityPersistenceFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,246 @@
+package com.lemans.ds.flag
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Stepwise
+
+@Stepwise
+class FlagPartEntityPersistenceFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'flag' }
+
+ final static int ALL_COLUMNS_SIZE = 12
+
+
+ String invalidPartNumber = 'xyz'
+ String addedPartNumber = '01040073'
+ Integer flagId1 = 1
+ Integer flagId2 = 2
+ Integer invalidFlagId = 6
+ Integer invalidFlagId1 = 8
+
+ def 'can NOT add invalid Flag to validPart'() {
+ given:
+ path([part: addedPartNumber])
+ invalid()
+ Map input = [
+ flagIds: [invalidFlagId1, invalidFlagId],
+ operation: 'INSERT'
+ ]
+
+ when:
+ post(input)
+
+ then:
+ with(payload) {
+ messages[0].type == 'error'
+ messages[0].text == 'Invalid FlagIds found [8, 6]'
+ }
+ }
+
+ def 'can NOT add Flag to invalidPart'() {
+ given:
+ path([part: invalidPartNumber])
+ invalid()
+ Map input = [
+ flagIds: [flagId1, flagId2],
+ operation: 'INSERT'
+ ]
+
+ when:
+ post(input)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'Part with partNumber xyz does not exist'
+ }
+ }
+
+ def 'can NOT add Invalid Flag to invalid Part'() {
+ given:
+ path([part: invalidPartNumber])
+ invalid()
+ Map input = [
+ flagIds: [flagId1, invalidFlagId],
+ operation: 'INSERT'
+ ]
+
+ when:
+ post(input)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'Invalid FlagIds found [6]'
+ }
+ with(payload.messages[1]) {
+ type == 'error'
+ text == 'Part with partNumber xyz does not exist'
+ }
+ }
+
+ def 'can add flagPart relation '() {
+ given:
+ path([part: addedPartNumber])
+ ok()
+ Map input = [
+ flagIds: [flagId1, flagId2],
+ ]
+
+ when:
+ post(input)
+
+ then:
+ !payload.results
+ }
+
+ def 'can find allowable flags for an entity '() {
+ given:
+ path()
+ queryParams.entityClass = 'part'
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.flagId.contains(flagId1)
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ payload.results.flagId.every { it != null }
+ }
+
+ def 'can find flagPart relation '() {
+ given:
+ path([part: addedPartNumber])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.flagId.contains(flagId1)
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ payload.results.flagId.every { it != null }
+ }
+
+ def 'can find flagPart relation for a part with no flag'() {
+ given:
+ path([part: '004165'], flagId1)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can show flagValue with entityId and flagId'() {
+ given:
+ path([part: addedPartNumber], flagId1)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.flagId == flagId1
+ payload.results.entityId == '01040073'
+ }
+
+ def 'can not show flagValue with invalid entityId'() {
+ given:
+ path([part: invalidPartNumber], flagId1)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can not show flagValue with invalid flagId'() {
+ given:
+ Integer id = -1
+ path([part: 'B8es'], id)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT delete if there is an invalid flag'() {
+ given:
+ String partNumber = addedPartNumber
+ path([part: partNumber])
+ Map input = [
+ flagIds: [flagId1, invalidFlagId],
+ operation: 'DELETE'
+ ]
+ invalid()
+
+ when:
+ post(input)
+
+ then:
+ payload.messages[0].text == 'Invalid FlagIds found [6]'
+ }
+
+ def 'can delete'() {
+ given:
+ path([part: addedPartNumber])
+ Map input = [
+ flagIds: [flagId1, flagId2],
+ operation: 'DELETE'
+ ]
+ ok()
+
+ when:
+ post(input)
+
+ then:
+ payload.results == []
+ }
+
+ def 'can still delete if invalid flag is included'() {
+ given:
+ path([part: '002-05901'])
+ Map input = [
+ flagIds: [invalidFlagId, flagId1],
+ operation: 'DELETE'
+ ]
+ invalid()
+
+ when:
+ post(input)
+
+ then:
+ payload.messages[0].text == 'Invalid FlagIds found [6]'
+ }
+
+ def 'can not delete if no flags are selected'() {
+ given:
+ String partNumber = addedPartNumber
+ path([part: partNumber])
+ Map input = [
+ flagIds: [],
+ operation: 'DELETE'
+ ]
+ invalid()
+
+ when:
+ post(input)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'No FlagIds found'
+ }
+
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/flag/FlagProductEntityFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/flag/FlagProductEntityFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/flag/FlagProductEntityFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,98 @@
+package com.lemans.ds.flag
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Stepwise
+
+@Stepwise
+class FlagProductEntityFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'flag' }
+
+ final static int ALL_COLUMNS_SIZE = 12
+
+
+ String invalidProductNumber = 'xyz'
+ String addedProductNumber = '316931'
+ Integer flagId1 = 11
+
+ def 'can find allowable flags for an entity '() {
+ given:
+ path()
+ queryParams.entityClass = 'Product'
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.flagId.contains(flagId1)
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ payload.results.flagId.every { it != null }
+ }
+
+ def 'can find flagProduct relation '() {
+ given:
+ path([product: addedProductNumber])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.flagId.contains(flagId1)
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ payload.results.flagId.every { it != null }
+ }
+
+ def 'can show flagValue with entityId and flagId'() {
+ given:
+ path([product: addedProductNumber], flagId1)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.flagId == flagId1
+ payload.results.entityId == '316931'
+ }
+
+ def 'can show flag fo finalized product'() {
+ given:
+ path([product: addedProductNumber], flagId1)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.flagId == flagId1
+ payload.results.entityId == '316931'
+ }
+
+ def 'can not show flagValue with invalid entityId'() {
+ given:
+ path([product: invalidProductNumber], flagId1)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can not show flagValue with invalid flagId'() {
+ given:
+ Integer id = -1
+ path([product: 'B8es'], id)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/importing/LocaleBulkFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/importing/LocaleBulkFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/importing/LocaleBulkFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,54 @@
+package com.lemans.ds.importing
+
+import com.lemans.ds.testing.DsFuncSpec
+
+/**
+ * Created by mumachi on 12/6/2017.
+ */
+class LocaleBulkFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'localeImportJob' }
+
+ final static int ALL_COLUMNS_SIZE = 15
+
+ def 'can find Locale Import data by localeImportProcessId'() {
+ given:
+ int importFileId = 6
+ path(importFileId)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.localeImportProcessId == importFileId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT find Locale Import by id that does not exist'() {
+ given:
+ path(-1)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find All Locale Import'() {
+ given:
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.localeImportProcessId.every { it }
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ apiPaginated() > 5
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/importing/LocaleBulkImportFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/importing/LocaleBulkImportFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/importing/LocaleBulkImportFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,217 @@
+package com.lemans.ds.importing
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Ignore
+import spock.lang.Shared
+import spock.lang.Stepwise
+
+import java.nio.file.Files
+
+@Stepwise
+class LocaleBulkImportFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'localeImportJob' }
+
+ private static final String TEMP_MEDIA_FOLDER = 'temporaryMediaPath/'
+
+ @Shared String fileName = 'testForNoProductId'
+ @Shared File temporaryMediaFolder = new File(TEMP_MEDIA_FOLDER)
+ @Shared File testFile = new File('files/' + fileName + '.xlsx')
+ @Shared File temporaryFile = new File(TEMP_MEDIA_FOLDER + fileName + '.xlsx')
+ @Shared Integer importFileId
+
+ def setupSpec() {
+ temporaryMediaFolder.mkdir()
+ assert testFile.exists()
+ if (!temporaryFile.exists()) { Files.copy(testFile.toPath(), temporaryFile.toPath()) }
+ assert temporaryFile.exists()
+ }
+
+ def cleanupSpec() {
+ temporaryFile.delete()
+ assert !temporaryFile.exists()
+ temporaryMediaFolder.delete()
+ assert testFile.exists()
+ }
+
+ def 'can NOT add an Import file without tempId'() {
+ given:
+ path()
+ Map body = [
+ originalFileName: fileName,
+ extension: 'xlsx',
+ importType: 'Product'
+ ]
+ invalid()
+
+ when:
+ post(body)
+
+ then:
+ payload.messages.find { it.text == 'tempId is required' }
+ }
+
+ def 'can NOT add an Import file without the originalFileName'() {
+ given:
+ path()
+ Map body = [
+ extension: 'xlsx',
+ importFileId: importFileId,
+ importType: 'Product'
+ ]
+ invalid()
+
+ when:
+ post(body)
+
+ then:
+ payload.messages.find { it.text == 'tempId is required' }
+ }
+
+ def 'can NOT add an Import File without an extension'() {
+ given:
+ path()
+ invalid()
+
+ when:
+ post([tempId: fileName, importType: 'Product', originalFileName: fileName])
+
+ then:
+ payload.messages.find { it.text == 'Extension is required' }
+ }
+
+ def 'can add an Import File for Product'() {
+ given:
+ Map body = [
+ originalFileName: 'testForNoProductId',
+ extension: 'xlsx',
+ tempId: fileName,
+ importType: 'Product',
+ ]
+ path()
+ ok()
+
+ when:
+ post(body)
+ importFileId = payload.results.localeImportProcessId
+
+ then:
+ importFileId
+ payload.results.originalFileName
+ payload.results.originalFileName == 'testForNoProductId'
+ Thread.sleep(10000) //To Make sure the Job is finished to download those files.
+ }
+
+ @Ignore
+ def 'can add an Import File for CategoryAttribute'() {
+ given:
+ Map body = [
+ originalFileName: 'CategoryAttributeValue',
+ extension: 'xlsx',
+ tempId: fileName,
+ importType: 'CategoryAttribute',
+ ]
+ path()
+ ok()
+
+ when:
+ post(body)
+ importFileId = payload.results.localeImportProcessId
+
+ then:
+ importFileId
+ payload.results.originalFileName
+ payload.results.originalFileName == 'CategoryAttributeValue'
+ Thread.sleep(15000) //To Make sure the Job is finished to download those files.
+ }
+
+ @Ignore
+ def 'can add an Import File for Part'() {
+ given:
+ Map body = [
+ originalFileName: 'PartExport',
+ extension: 'xlsx',
+ tempId: fileName,
+ importType: 'Part',
+ ]
+ path()
+ ok()
+
+ when:
+ post(body)
+ importFileId = payload.results.localeImportProcessId
+
+ then:
+ importFileId
+ payload.results.originalFileName
+ payload.results.originalFileName == 'PartExport'
+ Thread.sleep(10000) //To Make sure the Job is finished to download those files.
+ }
+
+ def 'can NOT delete an Import File that does not exist'() {
+ given:
+ path(-1)
+ invalid()
+
+ when:
+ delete()
+
+ then:
+ payload.messages[0].text == "Process can't be deleted, until it is completely processed."
+ }
+
+ def 'can download Original File'() {
+ given:
+ String fileId = 'Original'
+ path("$importFileId/file/$fileId/download")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.response.headers.'Content-disposition'.endsWith 'testForNoProductId.xlsx'
+ payload.response.contentType == 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
+ }
+
+ def 'can NOT download Report File that does NOT exists'() {
+ given:
+ String fileId = 'Report'
+ path("$importFileId/file/$fileId/download")
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ @Ignore
+ def 'can download Report File if it exists'() {
+ given:
+ String fileId = 'Report'
+ path("$importFileId/file/$fileId/download")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.response.headers.'Content-disposition'.endsWith 'Report.xls'
+ payload.response.contentType == 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
+ }
+
+ def 'can delete an Import File'() {
+ given:
+ path(importFileId)
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/part/BulkPartAttributeFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/part/BulkPartAttributeFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/part/BulkPartAttributeFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,268 @@
+package com.lemans.ds.part
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class BulkPartAttributeFuncSpec extends DsFuncSpec {
+
+
+ final static int ALL_COLUMNS_SIZE = 21
+
+ String partNumber1 = '010110074'
+ String partNumber2 = '25011551'
+ Integer attributeNameId = 5793
+ Integer attributeValueId = 101059
+
+ def 'can NOT perform bulk operation on partAttribute without partNumber'() {
+ given:
+ String json = """
+{
+"operation": "INSERT",
+"attribute":[
+{
+"attributeNameId": $attributeNameId,
+"attributeValueIds": [$attributeValueId]
+}
+]
+}
+"""
+ path('part/attribute')
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text.contains('No Part Selected')
+ }
+ }
+
+ def 'can NOT perform bulk operation on partAttribute without attributeName'() {
+ given:
+ String json = """
+{
+"partNumber": ["$partNumber1", "xxyz"],
+"operation": "INSERT",
+"attribute":[
+]
+}
+"""
+ path('part/attribute')
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text.contains('No attribute change selected')
+ }
+ }
+
+ def 'can NOT perform bulk operation on partAttribute without attributeValue'() {
+ given:
+ String json = """
+{
+"partNumber": ["$partNumber1"],
+"operation": "INSERT",
+"attribute":[
+{
+"attributeNameId": $attributeNameId,
+"attributeValueIds": null
+}
+]
+}
+"""
+ path('part/attribute')
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text.contains('Invalid attribute value')
+ }
+ }
+
+ def 'can NOT perform bulk operation on partAttribute for invalidPart'() {
+ given:
+ String json = """
+{
+"partNumber": ["$partNumber1", "xxyz"],
+"operation": "INSERT",
+"attribute":[
+{
+"attributeNameId": $attributeNameId,
+"attributeValueIds": [$attributeValueId]
+}
+]
+}
+"""
+ path('part/attribute')
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text.contains('Invalid Part')
+ }
+
+ }
+
+
+ def 'can NOT perform bulk add partAttribute for invalid attributeValueId'() {
+ given:
+ String json = """
+{
+"partNumber": ["$partNumber1"],
+"operation": "INSERT",
+"attribute":[
+{
+"attributeNameId": $attributeNameId,
+"attributeValueIds": [-1]
+}
+]
+}
+"""
+ path('part/attribute')
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text.contains('Invalid attribute')
+ }
+
+ }
+
+ def 'can NOT perform bulk add partAttribute for part and attribute not related to same category'() {
+ given:
+ String json = """
+{
+"partNumber": ["012"],
+"operation": "INSERT",
+"attribute":[
+{
+"attributeNameId": $attributeNameId,
+"attributeValueIds": [$attributeValueId]
+}
+]
+}
+"""
+ path('part/attribute')
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[1]) {
+ type == 'error'
+ text.contains('not belong to same category')
+ }
+
+ }
+
+ def 'can NOT perform bulk add partAttribute for part does not assigned to valid category'() {
+ given:
+ String json = """
+{
+"partNumber": ["38070158"],
+"operation": "INSERT",
+"attribute":[
+{
+"attributeNameId": $attributeNameId,
+"attributeValueIds": [$attributeValueId]
+}
+]
+}
+"""
+ path('part/attribute')
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text.contains('Category is not assigned')
+ }
+
+ }
+ def 'can bulk delete partAttribute for Parts'() {
+ given:
+ String json = """
+{
+"partNumber": ["b8es", "b9es"],
+"operation": "DELETE",
+"attribute":[
+{
+"attributeNameId": $attributeNameId,
+"attributeValueIds": [$attributeValueId]
+}
+]
+}
+"""
+ path('part/attribute')
+ ok()
+
+ when:
+ post(json)
+
+ then:
+ !payload.results
+ }
+
+ def 'can bulk add partAttribute for Parts'() {
+ given:
+ String json = """
+{
+"partNumber": ["01104390", "01104391"],
+"operation": "INSERT",
+"attribute":[
+{
+"attributeNameId": $attributeNameId,
+"attributeValueIds": [$attributeValueId]
+}
+]
+}
+"""
+ path('part/attribute')
+ ok()
+
+ when:
+ post(json)
+
+ then:
+ !payload.results
+ }
+
+ def 'can bulk REPLACE partAttribute for Parts'() {
+ given:
+ String json = """
+{
+"attributeValueIds": [$attributeValueId]
+}
+"""
+ path([part: "$partNumber1", attribute: attributeNameId], 'value')
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ !payload.results
+ }
+
+}
Index: src/integration-test/groovy/com/lemans/ds/part/PartAssociationFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/part/PartAssociationFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/part/PartAssociationFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,215 @@
+package com.lemans.ds.part
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+
+class PartAssociationFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'part' }
+
+ private static final String PART_NUMBER_1 = 'b8es'
+
+ private static final String PART_NUMBER_2 = 'b9es'
+
+ private static final String RELATED_PART_1 = '012'
+
+ private static final String RELATED_PART_2 = '0001CRKICK'
+
+ private static final String RELATED_PART_3 = 'b8es'
+
+ private static final Integer ASSOCIATION_TYPE_ID_1 = 20001
+
+ private static final Integer ASSOCIATION_TYPE_ID_2 = 20002
+
+ @Shared
+ Integer partAssociationId1
+
+ @Shared
+ Integer partAssociationId2
+
+ @Shared
+ Integer partAssociationId3
+
+ def 'can not find related part'() {
+ given:
+ path('_invalid_/relatedPart')
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.meta.totalRecords == 0
+ }
+
+ def 'can not find referral part'() {
+ given:
+ path('_invalid_/referralPart')
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.meta.totalRecords == 0
+ }
+
+ def 'can not add partAssociation'() {
+ given:
+ Map inputPayload = [relatedPartNumber: RELATED_PART_1]
+ path("$PART_NUMBER_1/relatedPart")
+ invalid()
+
+ when:
+ post(inputPayload)
+
+ then:
+ payload.messages[0].field == 'associationTypeId'
+ payload.messages[0].text == 'associationTypeId is required'
+ }
+
+ def 'can add partAssociation example1'() {
+ given:
+ Map inputPayload = [associationTypeId: ASSOCIATION_TYPE_ID_1, relatedPartNumber: RELATED_PART_1]
+ path("$PART_NUMBER_1/relatedPart")
+ ok()
+
+ when:
+ post(inputPayload)
+ partAssociationId1 = payload.results.partAssociationId
+
+ then:
+ payload.results.associationTypeId == ASSOCIATION_TYPE_ID_1
+ payload.results.relatedPartNumber == RELATED_PART_1
+ }
+
+ def 'can not add duplicate partAssociation example1'() {
+ given:
+ Map inputPayload = [associationTypeId: ASSOCIATION_TYPE_ID_1, relatedPartNumber: RELATED_PART_1]
+ path("$PART_NUMBER_1/relatedPart")
+ invalid()
+
+ when:
+ post(inputPayload)
+
+ then:
+ payload.messages[0].type == 'error'
+ payload.messages[0].text == "PartAssociation for partNumber $PART_NUMBER_1 with " +
+ "associationTypeId $ASSOCIATION_TYPE_ID_1 and relatedPartNumber $RELATED_PART_1 exists."
+ }
+
+ def 'can add partAssociation example2'() {
+ Map inputPayload = [associationTypeId: ASSOCIATION_TYPE_ID_1, relatedPartNumber: RELATED_PART_2]
+ path("$PART_NUMBER_1/relatedPart")
+ ok()
+
+ when:
+ post(inputPayload)
+ partAssociationId2 = payload.results.partAssociationId
+
+ then:
+ payload.results.associationTypeId == ASSOCIATION_TYPE_ID_1
+ payload.results.relatedPartNumber == RELATED_PART_2
+ }
+
+ def 'can add partAssociation example3'() {
+ Map inputPayload = [associationTypeId: ASSOCIATION_TYPE_ID_1, relatedPartNumber: RELATED_PART_3]
+ path("$PART_NUMBER_2/relatedPart")
+ ok()
+
+ when:
+ post(inputPayload)
+ partAssociationId3 = payload.results.partAssociationId
+
+ then:
+ payload.results.associationTypeId == ASSOCIATION_TYPE_ID_1
+ payload.results.relatedPartNumber == RELATED_PART_3
+ }
+
+ def 'can find related part'() {
+ given:
+ path("$PART_NUMBER_1/relatedPart")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.meta.totalRecords >= 1
+ payload.results.partNumber.every { it == PART_NUMBER_1 }
+ }
+
+ def 'can find referral part'() {
+ given:
+ path("$PART_NUMBER_1/referralPart")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.meta.totalRecords >= 1
+ payload.results[1].relatedPartNumber == PART_NUMBER_1
+ }
+
+ def 'can get a partAssociation'() {
+ path("$PART_NUMBER_1/relatedPart/$partAssociationId1")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.associationTypeId == ASSOCIATION_TYPE_ID_1
+ }
+
+ def 'can update partAssociation'() {
+ given:
+ Map inputPayload = [associationTypeId: ASSOCIATION_TYPE_ID_2]
+ path("$PART_NUMBER_1/relatedPart/$partAssociationId1")
+ ok()
+
+ when:
+ put(inputPayload)
+
+ then:
+ payload.results.associationTypeId == ASSOCIATION_TYPE_ID_2
+ }
+
+ def 'can delete partAssociation example1'() {
+ given:
+ path("$PART_NUMBER_1/relatedPart/$partAssociationId1")
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can delete partAssociation example2'() {
+ given:
+ path("$PART_NUMBER_1/relatedPart/$partAssociationId2")
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can delete partAssociation example3'() {
+ given:
+ path("$PART_NUMBER_2/relatedPart/$partAssociationId3")
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/part/PartAttributeDetailsFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/part/PartAttributeDetailsFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/part/PartAttributeDetailsFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,78 @@
+package com.lemans.ds.part
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class PartAttributeDetailsFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'part/details' }
+
+ final static int ALL_COLUMNS_SIZE = 21
+
+ def 'can NOT find a PartAttribute for a Catalog that does not exist'() {
+ given:
+ path([catalog: 0, category: -1])
+ notFound()
+
+ when:
+ post()
+
+ then:
+ !payload
+ }
+
+ def 'can find a Category details for a valid Catalog'() {
+ given:
+ int categoryId = 592
+ String json = '''
+ {
+ "partNumber":["B10ES", "01000003"]
+ }'''
+ path([catalog: 0, category: categoryId])
+ ok()
+
+ when:
+ post(json)
+
+ then:
+ payload.results.categoryId == categoryId
+ payload.results.part.size() == 2
+ }
+
+ def 'can find a Category details for a valid Catalog for a valid Locale'() {
+ given:
+ int categoryId = 592
+ optionalHeaders.locale = 'de'
+ String json = '''
+ {
+ "partNumber":["B10ES", "01000003"]
+ }'''
+ path([catalog: 0, category: categoryId])
+ ok()
+
+ when:
+ post(json)
+
+ then:
+ payload.results.categoryId == categoryId
+ payload.results.part.size() == 2
+ }
+
+ def 'can NOT find a Category details for a valid Catalog for an invalid Locale'() {
+ given:
+ int categoryId = 592
+ optionalHeaders.locale = 'xy'
+ String json = '''
+ {
+ "partNumber":["B10ES", "01000003"]
+ }'''
+ path([catalog: 0, category: categoryId])
+ notFound()
+
+ when:
+ post(json)
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/part/PartAttributeFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/part/PartAttributeFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/part/PartAttributeFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,62 @@
+package com.lemans.ds.part
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Ignore
+
+
+class PartAttributeFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'value' }
+
+ final static int ALL_COLUMNS_SIZE = 21
+
+ String partNumber = '25011550'
+ Integer attributeNameId = 5793
+ Integer attributeValueId = 101059
+
+ def 'can NOT add attribute for invalidPart'() {
+ given:
+ String json = """{"attributeValueId": $attributeValueId}"""
+ path([part: 'xxyyz', attribute: attributeNameId])
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text.contains('Invalid Part')
+ }
+ }
+
+ @Ignore
+ def 'can add partAttribute relation '() { //TODO: unIgnore
+ given:
+ String partNumber = '06010006'
+ Integer attributeNameId = 5436
+ Integer attributeValueId = 100515
+ String json = """{"attributeValueId": $attributeValueId}"""
+ path([part: "$partNumber", attribute: attributeNameId])
+ ok()
+
+ when:
+ post(json)
+
+ then:
+ !payload.results
+ }
+
+ def 'can delete partAttribute relation '() {
+ given:
+ path([part: "$partNumber", attribute: attributeNameId], attributeValueId)
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload.results
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/part/PartFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/part/PartFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/part/PartFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,382 @@
+package com.lemans.ds.part
+
+import com.lemans.ds.testing.DsFuncSpec
+import com.lemans.ds.PartService
+
+class PartFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'part' }
+
+ final static int ALL_COLUMNS_SIZE = 102
+
+ final static int SEARCH_COLUMNS_SIZE = PartService.SEARCH_COLUMNSET.size()
+
+ def 'can NOT find Parts without a pageSize param'() {
+ given:
+ queryParams.pageSize = null
+ path()
+ invalid()
+
+ when:
+ get()
+
+ then:
+ payload.messages.find { it.text == 'pageSize is required' }
+ }
+
+ def 'can NOT find a Part by partNumber that does not exist'() {
+ given:
+ path('____')
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find Parts containing a partial partNumber'() {
+ given:
+ String partialPartNumber = '8E'
+ String partNumber = "*$partialPartNumber*"
+ queryParams.partNumber = partNumber
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.partNumber.every { it.contains partialPartNumber }
+ apiPaginated() > 23
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ }
+
+ def 'can find Parts containing a partial partNumber OR partDescr'() {
+ given:
+ String q = 'KICK'
+ queryParams.q = q
+ path()
+ ok()
+
+ when:
+ get()
+ List partNumberMatches = payload.results.findAll { it.partNumber.contains(q) }
+ List partDescrMatches = payload.results.findAll { it.partDescr.contains(q) }
+
+ then:
+ payload.results.every {
+ it.partNumber.contains(q) || it.partDescr.contains(q)
+ }
+ partNumberMatches
+ partDescrMatches
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ apiPaginated() > 200
+ apiPaginated() < 1000
+ }
+
+ def 'can find a Part by partNumber'() {
+ given:
+ queryParams.columns = null
+ String partNumber = '02151001'
+ path(partNumber)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.partNumber == partNumber
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can find Standard Parts'() {
+ given:
+ String partStatusCode = 'S'
+ queryParams.partStatusCode = partStatusCode
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.partStatusCode.every { it == partStatusCode }
+ apiPaginated() > 73000
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ }
+
+ def 'can find Parts by Category'() {
+ given:
+ Integer categoryId = 592
+ queryParams.categoryId = categoryId
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.categoryId.every { it == categoryId }
+ apiPaginated() > 1
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ }
+
+ def 'can find Parts not assigned to a Category'() {
+ given:
+ queryParams.categoryId = Part.UNASSIGNED_CATEGORY_ID
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.categoryId.every { it == null }
+ apiPaginated()
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ }
+
+ def 'can find Parts containing a partial partDescription'() {
+ given:
+ String partialPartDescription = 'EBONY'
+ String partDescription = "*$partialPartDescription*"
+ queryParams.partDescr = partDescription
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.partDescr.every { it.contains partialPartDescription }
+ apiPaginated() > 30
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ }
+
+ def 'can find Parts for single subComCode'() {
+ given:
+ String subComCode = '0101'
+ queryParams.subComCode = subComCode
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.subComCode.every { it == subComCode }
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ }
+
+ def 'can find Parts for multiple SubComCodes'() {
+ given:
+ String code1 = '0101'
+ String code2 = '0102'
+ queryParams.subComCode = "$code1,$code2"
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.subComCode.every { it == code1 || it == code2 }
+ apiPaginated() > 0
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ }
+
+ def 'can find Parts for single productId'() {
+ given:
+ String productId = '343229'
+ queryParams.productId = productId
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ apiPaginated() > 1
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ }
+
+ def 'can find Parts for multiple productId'() {
+ given:
+ String productId1 = '343229'
+ String productId2 = '123'
+ queryParams.productId = "$productId1,$productId2"
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ apiPaginated() > 1
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ }
+
+ def 'can find Parts unassigned to a product'() {
+ given:
+ String productId = Part.UNASSIGNED_PRODUCT_ID
+ queryParams.productId = productId
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ apiPaginated() > 1000
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ }
+
+ def 'can find Parts referencing a partial brandCode'() {
+ given:
+ String partialBrandCode = '068'
+ String brandCode = "*$partialBrandCode"
+ queryParams.brandCode = brandCode
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.brandCode.every { it.contains partialBrandCode }
+ apiPaginated() > 530
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ }
+
+ def 'can find Parts referencing a partial brandName'() {
+ given:
+ String partialBrandName = 'MA'
+ String brandName = "*$partialBrandName"
+ queryParams.brandName = brandName
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.brandName.every { it.contains partialBrandName }
+ apiPaginated() > 150
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ }
+
+ def 'can find Parts referencing a partial vendorName'() {
+ given:
+ String partialVendorName = 'AFX'
+ String vendorName = "$partialVendorName*"
+ queryParams.vendorName = vendorName
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.vendorName.every { it.contains partialVendorName }
+ apiPaginated() > 4900
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ }
+
+ def 'can find Parts referencing an exact vendorName'() {
+ given:
+ String vendorName = 'AFX NORTH AMERICA INC'
+ queryParams.vendorName = vendorName
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.vendorName.every { it == vendorName }
+ apiPaginated() > 4900
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ }
+
+ def 'can find Parts referencing a partial vendorId'() {
+ given:
+ String partialVendorId = '00A'
+ String vendorId = "*$partialVendorId"
+ queryParams.vendorId = vendorId
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.vendorId.every { it.contains partialVendorId }
+ apiPaginated() > 300
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ }
+
+ def 'can find Parts containing a partial vendorPartNumber'() {
+ given:
+ String partialVendorPartNumber = '113'
+ String vendorPartNumber = "$partialVendorPartNumber*"
+ queryParams.vendorPartNumber = vendorPartNumber
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.vendorPartNumber.every { it.contains partialVendorPartNumber }
+ apiPaginated() > 500
+ payload.results[0].size() == SEARCH_COLUMNS_SIZE
+ }
+
+ def 'can not find part by invalid partNumber and locale'() {
+ given:
+ String partNumber = '_invalid_'
+ queryParams.locale = 'de'
+ path(partNumber)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can not find part by partNumber with invalid locale'() {
+ given:
+ String partNumber = 'b8es'
+ queryParams.locale = '_invalid_'
+ path(partNumber)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find part by partNumber and locale'() {
+ given:
+ String partNumber = '0001CRKICK'
+ queryParams.locale = 'de'
+ path(partNumber)
+ ok()
+
+ when:
+ get()
+
+ then:
+ with(payload.results) {
+ locale == 'de'
+ partNumber
+ }
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/part/PartPersistenceFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/part/PartPersistenceFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/part/PartPersistenceFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,218 @@
+package com.lemans.ds.part
+
+import com.lemans.ds.testing.DsFuncSpec
+
+
+class PartPersistenceFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'part' }
+
+ final static int ALL_COLUMNS_SIZE = 102
+
+ def 'can NOT update a Part that does not exist'() {
+ given:
+ String partNumber = 'XXYYZZAABB'
+ String json = '{ "marketingDescr": "Updated" }'
+ path(partNumber)
+ notFound()
+
+ when:
+ put(json)
+
+ then:
+ !payload
+ }
+
+ def 'can NOT update an Part with invalid categoryId'() {
+ given:
+ Map part = [categoryId: -1]
+ path('B8ES')
+ invalid()
+
+ when:
+ put(part)
+ List errors = payload.messages.findAll { it.type = 'error' }
+
+ then:
+ errors.find { it.field == 'categoryId' }
+ }
+
+ def 'can not update a part with invalid primaryMediaId'() {
+ given:
+ Map part = [primaryMediaId: -1]
+ path('B8ES')
+ invalid()
+
+ when:
+ put(part)
+ List errors = payload.messages.findAll { it.type = 'error' }
+
+ then:
+ errors.find { it.field == 'primaryMediaId' }
+ }
+
+ def 'can update a Part'() {
+ given:
+ String partNumber = '0001CRKICK'
+ String oem = 'OEM0001CRKICK'
+ Integer mediaId = 228030
+ String partSpecificText = ''
+ String internalNotes = ''
+ String relatedParts = 'NewInsert'
+ String relatedProducts = ''
+ String certificationUS = 'test for US certificate'
+ String certificationEU = 'test for EU certificate'
+ Map part = [oemPartNumber: oem, primaryMediaId: mediaId, partSpecificText: partSpecificText,
+ internalNotes: internalNotes, relatedParts: relatedParts, relatedProducts: relatedProducts,
+ certificationUS: certificationUS, certificationEU: certificationEU]
+ path(partNumber)
+ ok()
+
+ when:
+ put(part)
+
+ then:
+ payload.results.partNumber == partNumber
+ payload.results.certificationUS == certificationUS
+ payload.results.certificationEU == certificationEU
+ payload.results.oemPartNumber == oem
+ payload.results.primaryMediaId == mediaId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can NOT create part and product relation with invalid productId' () {
+ given:
+ Integer productId = -1
+ String partNumber = '02151001'
+ String json = """{
+ "productId": $productId
+ }"""
+ path(partNumber)
+ invalid()
+
+ when:
+ put(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text.contains 'Invalid product'
+ }
+ }
+
+ def 'can create part and product relation' () {
+ given:
+ Integer productId = 343229
+ String partNumber = '02151001'
+ String json = """{
+ "productId": $productId
+ }"""
+ path(partNumber)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.primaryProductId == productId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can remove part and product relation' () {
+ given:
+ String productId = ''
+ String partNumber = '02151001'
+ String json = """{
+ "productId": "$productId"
+ }"""
+ path(partNumber)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can not update partLocale with invalid partNumber'() {
+ given:
+ String partNumber = '_invalid_'
+ queryParams.locale = 'de'
+ String json = '''{
+ "specialInstructions": "testForUpdate"
+ }'''
+ path(partNumber)
+ notFound()
+
+ when:
+ put(json)
+
+ then:
+ !payload
+ }
+
+ def 'can not update partLocale with invalid locale'() {
+ given:
+ String partNumber = '0001CRKICK'
+ queryParams.locale = '_invalid_'
+ String json = '''{
+ "specialInstructions": "testForUpdate"
+ }'''
+ path(partNumber)
+ invalid()
+
+ when:
+ put(json)
+
+ then:
+ with(payload.messages[1]) {
+ type == 'error'
+ text == 'locale with value [_invalid_] is not contained within the list [[de, it, es, fr]]'
+ }
+ }
+
+ def 'can update partLocale'() {
+ given:
+ String partNumber = '0001CRKICK'
+ queryParams.locale = 'es'
+ String json = '''{
+ "specialInstructions": "testForUpdate1",
+ "partSpecificText": "testForUpdate"
+ }'''
+ path(partNumber)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ with(payload.results) {
+ partNumber
+ locale == 'es'
+ specialInstructionsLocale == 'testForUpdate1'
+ }
+ }
+
+ def 'can create partLocale while calling update if partLocale does not exist'() {
+ given:
+ String partNumber = 'b8es'
+ queryParams.locale = 'es'
+ String json = '''{
+ "specialInstructions": "testForUpdate"
+ }'''
+ path(partNumber)
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ with(payload.results) {
+ partNumber
+ locale == 'es'
+ specialInstructionsLocale == 'testForUpdate'
+ }
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/product/ProductAssociationFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/product/ProductAssociationFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/product/ProductAssociationFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,214 @@
+package com.lemans.ds.product
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+
+class ProductAssociationFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'product' }
+
+ private static final Integer PRODUCTID_1 = 316571
+
+ private static final Integer PRODUCTID_2 = 316572
+
+ private static final Integer RELATED_PRODUCTID_1 = 316573
+
+ private static final Integer RELATED_PRODUCTID_2 = 316574
+
+ private static final Integer RELATED_PRODUCTID_3 = 316571
+
+ private static final Integer ASSOCIATION_TYPE_ID_1 = 21001
+
+ private static final Integer ASSOCIATION_TYPE_ID_2 = 21002
+
+ @Shared
+ Integer productAssociationId1
+
+ @Shared
+ Integer productAssociationId2
+
+ @Shared
+ Integer productAssociationId3
+
+ def 'can not find related product'() {
+ given:
+ path([catalog: 0], '_invalid_/relatedProduct')
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.meta.totalRecords >= 1
+ }
+
+ def 'can not find referral product'() {
+ given:
+ path([catalog: 0], '_invalid_/referralProduct')
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.meta.totalRecords >= 1
+ }
+
+ def 'can not add productAssociation'() {
+ given:
+ Map inputPayload = [relatedProductId: RELATED_PRODUCTID_1]
+ path([catalog: 0], "$PRODUCTID_1/relatedProduct")
+ invalid()
+
+ when:
+ post(inputPayload)
+
+ then:
+ payload.messages[0].field == 'associationTypeId'
+ payload.messages[0].text == 'associationTypeId is required'
+ }
+
+ def 'can add productAssociation example1'() {
+ given:
+ Map inputPayload = [associationTypeId: ASSOCIATION_TYPE_ID_1, relatedProductId: RELATED_PRODUCTID_1]
+ path([catalog: 0], "$PRODUCTID_1/relatedProduct")
+ ok()
+
+ when:
+ post(inputPayload)
+ productAssociationId1 = payload.results.productAssociationId
+
+ then:
+ payload.results.associationTypeId == ASSOCIATION_TYPE_ID_1
+ payload.results.relatedProductId == RELATED_PRODUCTID_1
+ }
+
+ def 'can not add duplicate productAssociation example1'() {
+ given:
+ Map inputPayload = [associationTypeId: ASSOCIATION_TYPE_ID_1, relatedProductId: RELATED_PRODUCTID_1]
+ path([catalog: 0], "$PRODUCTID_1/relatedProduct")
+ invalid()
+
+ when:
+ post(inputPayload)
+
+ then:
+ payload.messages[0].type == 'error'
+ payload.messages[0].text == 'productAssociation for productId 316571 with associationTypeId ' +
+ ' 21001 and relatedProductId 316573 exists.'
+ }
+
+ def 'can add productAssociation example2'() {
+ Map inputPayload = [associationTypeId: ASSOCIATION_TYPE_ID_1, relatedProductId: RELATED_PRODUCTID_2]
+ path([catalog: 0], "$PRODUCTID_1/relatedProduct")
+ ok()
+
+ when:
+ post(inputPayload)
+ productAssociationId2 = payload.results.productAssociationId
+
+ then:
+ payload.results.associationTypeId == ASSOCIATION_TYPE_ID_1
+ payload.results.relatedProductId == RELATED_PRODUCTID_2
+ }
+
+ def 'can add productAssociation example3'() {
+ Map inputPayload = [associationTypeId: ASSOCIATION_TYPE_ID_1, relatedProductId: RELATED_PRODUCTID_3]
+ path([catalog: 0], "$PRODUCTID_2/relatedProduct")
+ ok()
+
+ when:
+ post(inputPayload)
+ productAssociationId3 = payload.results.productAssociationId
+
+ then:
+ payload.results.associationTypeId == ASSOCIATION_TYPE_ID_1
+ payload.results.relatedProductId == RELATED_PRODUCTID_3
+ }
+
+ def 'can find related product'() {
+ given:
+ path([catalog: 0], "$PRODUCTID_1/relatedProduct")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productId.every { it == PRODUCTID_1 }
+ }
+
+ def 'can find referral product'() {
+ given:
+ path([catalog: 0], "$PRODUCTID_1/referralProduct")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.relatedProductId.every { it == PRODUCTID_1 }
+ }
+
+ def 'can get productAssociation'() {
+ given:
+ path([catalog: 0], "$PRODUCTID_1/relatedProduct/$productAssociationId1")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.associationTypeId == ASSOCIATION_TYPE_ID_1
+ }
+
+ def 'can update productAssociation'() {
+ given:
+ Map inputPayload = [associationTypeId: ASSOCIATION_TYPE_ID_2]
+ path([catalog: 0], "$PRODUCTID_1/relatedProduct/$productAssociationId1")
+ ok()
+
+ when:
+ put(inputPayload)
+
+ then:
+ payload.results.associationTypeId == ASSOCIATION_TYPE_ID_2
+ }
+
+ def 'can delete productAssociation example1'() {
+ given:
+ path([catalog: 0], "$PRODUCTID_1/relatedProduct/$productAssociationId1")
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can delete productAssociation example2'() {
+ given:
+ path([catalog: 0], "$PRODUCTID_1/relatedProduct/$productAssociationId2")
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can delete productAssociation example3'() {
+ given:
+ path([catalog: 0], "$PRODUCTID_2/relatedProduct/$productAssociationId3")
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/product/ProductFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/product/ProductFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/product/ProductFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,331 @@
+package com.lemans.ds.product
+
+import com.lemans.ds.CatalogTestFixture
+import com.lemans.ds.testing.DsFuncSpec
+
+class ProductFuncSpec extends DsFuncSpec implements CatalogTestFixture {
+
+ @Override
+ String resourceName() { 'product' }
+
+ final static int ALL_COLUMNS_SIZE = 31
+
+ def 'can find a Product'() {
+ given:
+ int id = 316931
+ path(catalog, id)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productId == id
+ payload.results.catalogInstanceId == catalogInstanceId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can search for a Product'() {
+ given:
+ int id = 316931
+ queryParams.productId = id
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results[0].productId == id
+ payload.results[0].catalogInstanceId == catalogInstanceId
+ apiPaginated() == 1
+ }
+
+ def 'can find Products for a Brand by brandCode'() {
+ given:
+ String brand = '0821'
+ queryParams.brandCode = brand
+ queryParams.columns = 'productId,catalogInstanceId,brandCode,brandId'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.brandCode.every { it == brand }
+ payload.results.catalogInstanceId.every { it == catalogInstanceId }
+ apiPaginated() > 720
+ }
+
+ def 'can find Products belonging to single flagId'() {
+ given:
+ queryParams.isDigiActive = true
+ queryParams.flagId = 20
+ queryParams.mode = 'flag'
+ queryParams.columns = 'productId,catalogInstanceId,brandCode,brandId'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.meta.totalRecords == 0
+ }
+
+ def 'can search products by flagId'() {
+ given:
+ queryParams.flagId = 17
+ queryParams.columns = 'productId,catalogInstanceId,brandCode,brandId'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can find Products belonging to a single flagId'() {
+ given:
+ queryParams.isDigiActive = true
+ queryParams.flagId = 19
+ queryParams.brandCode = '0823'
+ queryParams.mode = 'flag'
+ queryParams.columns = 'productId,catalogInstanceId,brandCode,brandId'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results[0].brandCode == '0823'
+ payload.meta.totalRecords >= 1
+ }
+
+ def 'can find Products for a Brand by brandId'() {
+ given:
+ String brand = 821
+ queryParams.brandId = brand
+ queryParams.columns = 'productId,catalogInstanceId,brandCode,brandId'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.brandId.every { brand }
+ payload.results.catalogInstanceId.every { it == catalogInstanceId }
+ apiPaginated() > 720
+ }
+
+ def 'can find Products by isDigiActiveFilter with value 1'() {
+ given:
+ queryParams.isDigiActive = 1
+ queryParams.columns = 'productId, catalogInstanceId, brandCode, productName'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.meta.totalRecords >= 10
+ }
+
+ def 'can find products by IsDigiActiveFilter with value 0'() {
+ given:
+ queryParams.isDigiActive = 0
+ queryParams.columns = 'productId, catalogInstanceId, brandCode, productName'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results[0].productId == 316590
+ payload.meta.totalRecords >= 26770
+ }
+
+ def 'can find Products for a Category'() {
+ given:
+ Integer categoryId = 300
+ queryParams.categoryId = categoryId
+ queryParams.columns = 'productId,catalogInstanceId,categoryId'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.categoryId.every { it == categoryId }
+ payload.results.catalogInstanceId.every { it == catalogInstanceId }
+ apiPaginated() >= 2
+ }
+
+ def 'can find Products'() {
+ given:
+ queryParams.columns = 'productId, catalogInstanceId, productName'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productId.every { it != null }
+ payload.results.catalogInstanceId.every { it == catalogInstanceId }
+ apiPaginated() > 13000
+ }
+
+ def 'can find Products Part Descriptions'() {
+ given:
+ queryParams.columns = 'productId, catalogInstanceId, productName'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productId.every { it != null }
+ payload.results.catalogInstanceId.every { it == catalogInstanceId }
+ apiPaginated() > 13000
+ }
+
+ def 'can find Products like a specific productName'() {
+ given:
+ String name = 'testing'
+ queryParams.productName = name + '*'
+ queryParams.columns = 'productId,catalogInstanceId,productName'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productId.every { it != null }
+ payload.results.catalogInstanceId.every { it == catalogInstanceId }
+ payload.results.productName.every { it.startsWith(name) }
+ apiPaginated() > 9
+ }
+
+ def 'can find Products with generic q param search on productId '() {
+ given:
+ String partialProductId = '32'
+ queryParams.q = partialProductId
+ queryParams.columns = 'productId,catalogInstanceId,productName'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productId.every { it.toString().contains(partialProductId) }
+ payload.results.catalogInstanceId.every { it == catalogInstanceId }
+ apiPaginated() > 1000
+ }
+
+ def 'can find Products with generic q param search on productName '() {
+ given:
+ String partialProductName = 'LOOKY'
+ queryParams.q = partialProductName
+ queryParams.columns = 'productId,catalogInstanceId,productName'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productName.every { it.toString().contains(partialProductName) }
+ payload.results.catalogInstanceId.every { it == catalogInstanceId }
+ apiPaginated() > 0
+ }
+
+ def 'can create a product'() {
+ given:
+ String json ='''
+ {
+ "productName": "dfd",
+ "categoryId": "4537",
+ "brandId": "0649"
+ }
+ '''
+ path(catalog)
+ ok()
+
+ when:
+ post(json)
+
+ then:
+ payload
+ }
+
+ def 'can find products without flag param'() {
+ given:
+ String partialProductId = '32'
+ queryParams.q = partialProductId
+ queryParams.columns = 'productId,catalogInstanceId,productName'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload
+ }
+
+ def 'can find products with flag param'() {
+ given:
+ String partialProductId = '32'
+ queryParams.q = partialProductId
+ queryParams.mode = 'flag'
+ queryParams.columns = 'productId,catalogInstanceId,productName'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload
+ }
+
+ def 'can get split validation'() {
+ given:
+ Map data = [bulkValidation: 0]
+ path([catalog: 0], '316651/splitValidation')
+ ok()
+
+ when:
+ post(data)
+
+ then:
+ payload
+ }
+
+ def 'can get products with multiple categoryIds'() {
+ given:
+ queryParams.categoryId = [4542, 4543]
+ queryParams.isDigiActive = 1
+ queryParams.columns = 'productId,catalogInstanceId,productName'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/product/ProductLocaleFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/product/ProductLocaleFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/product/ProductLocaleFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,332 @@
+package com.lemans.ds.product
+
+import com.lemans.ds.CatalogTestFixture
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Stepwise
+
+import java.text.SimpleDateFormat
+
+@Stepwise
+class ProductLocaleFuncSpec extends DsFuncSpec implements CatalogTestFixture {
+
+ @Override
+ String resourceName() { 'product' }
+
+ final static int ALL_COLUMNS_SIZE = 30
+ String effective = '2016-11-18T08:12:33'
+ SimpleDateFormat dateTimeFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.default)
+ Date effectiveDate = dateTimeFormatter.parse(effective)
+
+ Integer validProductId = 346004
+
+ Integer invalidProductId = 34614
+
+
+
+ def 'can create ProductLocale with productId that exist'() {
+ given:
+ String name = 'German_more_testing'
+ optionalHeaders.locale = 'fr'
+ path(catalog, validProductId)
+ ok()
+
+ when:
+ put([productName: name])
+
+ then:
+ payload.results.productId == validProductId
+ payload.results.productNameLocale == name
+ }
+
+ def 'can add ProductLocale with productId that exist'() {
+ given:
+ String name = 'German_more_testing'
+ optionalHeaders.locale = 'fr'
+ path(catalog, validProductId)
+ ok()
+
+ when:
+ put([productName: name])
+
+ then:
+ payload.results.productId == validProductId
+ payload.results.productNameLocale == name
+ }
+
+ def 'can NOT create ProductLocale with productId that exist but with invalid Locale'() {
+ given:
+ String name = 'German_more_testing'
+ optionalHeaders.locale = 'xy'
+ path(catalog, validProductId)
+ invalid()
+
+ when:
+ put([productName: name])
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'locale'
+ text == 'locale with value [xy] is not contained within the list [[de, it, es, fr]]'
+ }
+ payload.messages.size() == 1
+ }
+
+
+ def 'can NOT create ProductLocale with productId that does not exist'() {
+ given:
+ String name = 'German_more_testing'
+ optionalHeaders.locale = 'de'
+ path(catalog, invalidProductId)
+ notFound()
+
+ when:
+ put([productName: name])
+
+ then:
+ !payload
+ }
+
+ def 'can NOT create ProductLocale with invalid Locale'() {
+ given:
+ String name = 'German_more_testing'
+ optionalHeaders.locale = 'xy'
+ path(catalog, validProductId)
+ invalid()
+
+ when:
+ put([productName: name])
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'locale'
+ text == 'locale with value [xy] is not contained within the list [[de, it, es, fr]]'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can NOT update ProductLocale with invalid Locale'() {
+ given:
+ String name = 'German_more_testing'
+ optionalHeaders.locale = 'xy'
+ path(catalog, validProductId)
+ invalid()
+
+ when:
+ put([productName: name])
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'locale'
+ text == 'locale with value [xy] is not contained within the list [[de, it, es, fr]]'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can NOT update ProductLocale with invalid ProductId'() {
+ given:
+ String name = 'German_more_testing'
+ optionalHeaders.locale = 'de'
+ path(catalog, invalidProductId)
+ notFound()
+
+ when:
+ put([productName: name])
+
+ then:
+ !payload
+ }
+
+ def 'can update ProductLocale with valid Locale'() {
+ given:
+ String name = 'German_more_testing'
+ optionalHeaders.locale = 'de'
+ path(catalog, validProductId)
+ ok()
+
+ when:
+ put([productName: name])
+
+ then:
+ payload.results.productNameLocale == name
+ payload.results.productId == validProductId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can update empty ProductNameLocale with valid Locale'() {
+ given:
+ String name = ''
+ optionalHeaders.locale = 'de'
+ path(catalog, validProductId)
+ ok()
+
+ when:
+ put([productName: name])
+
+ then:
+ payload.results.productNameLocale == null
+ payload.results.productId == validProductId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+
+ def 'can NOT get all ProductLocale for invalid Locale'() {
+ given:
+ queryParams.columns = 'productId, productNameLocale'
+ optionalHeaders.locale = '_xy'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results == []
+ payload.meta.totalRecords == 0
+ }
+
+ def 'can get all ProductLocale for a valid Locale'() {
+ given:
+ queryParams.columns = 'productId, productNameLocale'
+ optionalHeaders.locale = 'de'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ /*payload.results.productId.every { it == catalogInstanceId }
+ payload.results.productLocaleId.every { it == productLocaleId }*/
+ apiPaginated() >= 2
+ }
+
+ def 'can NOT find ProductLocale for an invalid ProductId for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path(catalog, invalidProductId)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT find ProductLocale for a valid ProductId for a invalid Locale'() {
+ given:
+ optionalHeaders.locale = 'xy'
+ path(catalog, validProductId)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT find ProductLocale for invalid ProductId for invalid Locale'() {
+ given:
+ optionalHeaders.locale = 'xy'
+ path(catalog, invalidProductId)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find ProductLocale for a valid ProductId for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path(catalog, validProductId)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productId == validProductId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can get ProductLocale for a valid ProductId for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'fr'
+ path(catalog, validProductId)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productId == validProductId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+
+
+
+ def 'can find Products like a specific productName for a valid Locale'() {
+ given:
+ String name = 'testing'
+ queryParams.productNameLocale = name + '*'
+ queryParams.columns = 'productId,catalogInstanceId,productNameLocale'
+ optionalHeaders.locale = 'de'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productId.every { it != null }
+ payload.results.catalogInstanceId.every { it == catalogInstanceId }
+ payload.results.productNameLocale.every { it.startsWith(name) }
+ //apiPaginated() > 9
+ }
+
+ def 'can find Products with generic q param search on productId '() {
+ given:
+ String partialProductId = '32'
+ queryParams.q = partialProductId
+ queryParams.columns = 'productId,catalogInstanceId,productNameLocale'
+ optionalHeaders.locale = 'fr'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productId.every { it.toString().contains(partialProductId) }
+ payload.results.catalogInstanceId.every { it == catalogInstanceId }
+ apiPaginated() > 1000
+ }
+
+ def 'can find Products with generic q param search on productNameLocale for a valid Locale '() {
+ given:
+ String partialProductName = 'Locale'
+ queryParams.q = partialProductName
+ optionalHeaders.locale = 'fr'
+ queryParams.columns = 'productId,catalogInstanceId,productNameLocale'
+ path(catalog)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productNameLocale.every { it.toString().contains(partialProductName) }
+ payload.results.catalogInstanceId.every { it == catalogInstanceId }
+ apiPaginated() > 0
+ }
+
+
+}
Index: src/integration-test/groovy/com/lemans/ds/product/ProductPartFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/product/ProductPartFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/product/ProductPartFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,243 @@
+package com.lemans.ds.product
+
+import com.lemans.ds.CatalogTestFixture
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Stepwise
+
+@Stepwise
+class ProductPartFuncSpec extends DsFuncSpec implements CatalogTestFixture {
+
+ final static int ALL_COLUMNS_SIZE = 19
+
+ final static int PARTS_COLUMNS_SIZE = 3
+
+ Integer productId = 316931
+ String partNumber1 = '01050001'
+ String partNumber2 = '01050003'
+
+ @Override
+ String resourceName() { 'part' }
+
+ def 'cleanUpData'() {
+ given:
+ String json = """
+{
+"operation": "DELETE",
+"partNumbers": ["$partNumber1", "$partNumber2"]
+}
+"""
+ path(catalog + [product: productId])
+ ok()
+
+ when:
+ post(json)
+
+ then:
+ !payload.results
+ }
+
+ def 'can NOT perform bulk operation on Product Part relation with no partNumber'() {
+ given:
+ String json = '''
+{
+"operation": "INSERT"
+}
+'''
+ path(catalog + [product: productId])
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text.contains('No Part Selected')
+ }
+ }
+
+ def 'can NOT perform bulk operation on Product Part relation with empty partNumbers'() {
+ given:
+ String json = '''
+{
+"operation": "INSERT",
+"partNumbers": []
+}
+'''
+ path(catalog + [product: productId])
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text.contains('No Part Selected')
+ }
+ }
+
+ def 'can NOT perform bulk operation on Product Part relation with invalid productId'() {
+ given:
+ String json = """
+{
+"operation": "INSERT",
+"partNumber": "$partNumber1"
+}
+"""
+ path(catalog + [product: -1])
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text.contains('Invalid product')
+ }
+ }
+
+ def 'can NOT perform bulk operation on Product Part with brand and catalog mismatch of part and product'() {
+ given:
+ String json = '''
+{
+"operation": "INSERT",
+"partNumber": "B8ES"
+}
+'''
+ path(catalog + [product: productId])
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text.contains('doesn\'t match')
+ }
+ }
+
+ def 'can ADD single Product Part relation '() {
+ given:
+ String json = '''
+{
+"operation": "INSERT",
+"partNumber": "b8es"
+}
+'''
+ path(catalog + [product: 316651])
+ ok()
+
+ when:
+ post(json)
+
+ then:
+ !payload.results
+ }
+
+ def 'can perform bulk ADD operation on Product Part relation'() {
+ given:
+ String json = '''
+{
+"operation": "INSERT",
+"partNumbers": ["b8es"]
+}
+'''
+ path(catalog + [product: 316651])
+ ok()
+
+ when:
+ post(json)
+
+ then:
+ !payload.results
+ }
+
+ def 'can NOT find Product Part relation with invalid partNumber'() {
+ given:
+ path(catalog + [product: productId], -1)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+
+ }
+
+ def 'can find Product Part relation by product and Part'() {
+ given:
+ path(catalog + [product: 316931], '0010513')
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productId == 316931
+ payload.results.partNumber == '0010513'
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can find ALL Product Part relation by product'() {
+ given:
+ path(catalog + [product: productId])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.size() >= 2
+ payload.results.every { it.productId == productId }
+ payload.results[0].size() == ALL_COLUMNS_SIZE
+ }
+ def 'can perform single DELETE on Product Part relation'() {
+ given:
+
+ path(catalog + [product: productId], partNumber1)
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload.results
+ }
+
+ def 'can perform bulk DELETE on Product Part relation'() {
+ given:
+ String json = """
+{
+"operation": "DELETE",
+"partNumbers": ["$partNumber2"]
+}
+"""
+ path(catalog + [product: productId])
+ ok()
+
+ when:
+ post(json)
+
+ then:
+ !payload.results
+ }
+
+ def 'can find ALL Product Part Merchandising details by product'() {
+ given:
+ queryParams.attribute = true
+ path(catalog + [product: productId])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.size() >= 1
+ payload.results.productId == productId
+ payload.results.part[0].attribute[0].size() == PARTS_COLUMNS_SIZE
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/product/ProductPersistenceFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/product/ProductPersistenceFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/product/ProductPersistenceFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,125 @@
+package com.lemans.ds.product
+
+import com.lemans.ds.CatalogTestFixture
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+import spock.lang.Stepwise
+
+import java.text.SimpleDateFormat
+
+@Stepwise
+class ProductPersistenceFuncSpec extends DsFuncSpec implements CatalogTestFixture {
+
+ @Override
+ String resourceName() { 'product' }
+
+ @Shared Integer productId
+
+ String effective = '2016-11-18T08:12:33'
+ SimpleDateFormat dateTimeFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.default)
+ Date effectiveDate = dateTimeFormatter.parse(effective)
+
+ def 'can NOT create an invalid Product'() {
+ given:
+ path(catalog: null)
+ invalid()
+
+ when:
+ post()
+
+ then:
+ payload.messages.find { it.text == 'Catalog is required' }
+ }
+
+ def 'can NOT update the Catalog for a Product'() {
+ given:
+ path(catalog, 343170)
+ invalid()
+
+ when:
+ put([catalogInstanceId: 1])
+ List errors = payload.messages.findAll { it.type = 'error' }
+
+ then:
+ errors.find { it.field == 'catalogInstanceId' }.text == 'Catalog may not be changed'
+ }
+
+ def 'can NOT update an invalid Product'() {
+ given:
+ path(catalog, 343170)
+ invalid()
+
+ when:
+ put([productName: null, primaryMediaId: -1])
+ List errors = payload.messages.findAll { it.type = 'error' }
+
+ then:
+ errors.find { it.field == 'productName' }
+ errors.find { it.field == 'primaryMediaId' }.text == 'PrimaryMediaId with value [-1] does not exist'
+ }
+
+ def 'can NOT delete a Product that does not exist'() {
+ given:
+ path(catalog: -1, 343170)
+ notFound()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can create a valid Product for the VirtualCatalog'() {
+ given:
+ Integer brand = 839
+ Integer category = 81
+ String name = 'testing_123'
+ Map product = [brandId: brand, categoryId: category, productName: name, effectiveDate: effective, isDigiActive: '1']
+ path(catalog)
+ ok()
+
+ when:
+ post(product)
+ productId = payload.results.productId
+
+ then:
+ with(payload.results) {
+ brandId == brand
+ categoryId == category
+ productName == name
+ catalogInstanceId == this.catalogInstanceId
+ effectiveDate == this.effective
+ }
+ }
+
+ def 'can update a valid Product'() {
+ given:
+ String name = 'more_testing'
+ Integer mediaId = 228030
+ path(catalog, productId)
+ ok()
+
+ when:
+ put([productName: name, effectiveDate: effective, primaryMediaId: mediaId])
+
+ then:
+ payload.results.productId == productId
+ payload.results.productName == name
+ payload.results.primaryMediaId == mediaId
+ payload.results.effectiveDate == effective
+ }
+
+
+ def 'can delete a Product'() {
+ given:
+ path(catalog, productId)
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeatureFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeatureFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeatureFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,36 @@
+package com.lemans.ds.product.feature
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class ProductFeatureFuncSpec extends DsFuncSpec implements ProductFeatureTestFixture {
+
+ @Override
+ String resourceName() { 'feature' }
+
+ def 'can find a Feature for a Product'() {
+ given:
+ Integer featureId = 54
+ path(catalogProduct, featureId)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productFeatureId == featureId
+ payload.results.productId == productId
+ }
+
+ def 'can find Features for a Product'() {
+ given:
+ path(catalogProduct)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productId.every { it == productId }
+ payload.results.productFeatureId.every { it }
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeatureLocaleFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeatureLocaleFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeatureLocaleFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,234 @@
+package com.lemans.ds.product.feature
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Stepwise
+
+@Stepwise
+class ProductFeatureLocaleFuncSpec extends DsFuncSpec implements ProductFeatureTestFixture {
+
+ @Override
+ String resourceName() { 'feature' }
+
+ Integer validProductFeatureId = 1788329
+
+ Integer invalidProductFeatureId = 11000000
+
+ final static int ALL_COLUMNS_SIZE = 19
+
+
+
+ def 'can create ProductFeatureLocale with productFeatureId that exist'() {
+ given:
+ String name = 'German_more_testing'
+ optionalHeaders.locale = 'de'
+ path(catalogProduct, validProductFeatureId)
+ ok()
+
+ when:
+ put([featureText: name])
+
+ then:
+ payload.results.productFeatureId == validProductFeatureId
+ //payload.results.featureTextLocale == name
+ }
+
+ def 'can NOT create ProductFeatureLocale with productFeatureId that exist but with invalid Locale'() {
+ given:
+ String name = 'German_more_testing'
+ optionalHeaders.locale = 'xy'
+ path(catalogProduct, validProductFeatureId)
+ invalid()
+
+ when:
+ put([featureText: name])
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'locale'
+ text == 'locale with value [xy] is not contained within the list [[de, it, es, fr]]'
+ }
+ payload.messages.size() == 1
+ }
+
+
+ def 'can NOT create ProductFeatureLocale with productFeatureId that does not exist'() {
+ given:
+ String name = 'German_more_testing'
+ optionalHeaders.locale = 'de'
+ path(catalogProduct, invalidProductFeatureId)
+ notFound()
+
+ when:
+ put([featureText: name])
+
+ then:
+ !payload
+ }
+
+ def 'can NOT create ProductFeatureLocale with invalid Locale'() {
+ given:
+ String name = 'German_more_testing'
+ optionalHeaders.locale = 'xy'
+ path(catalogProduct, validProductFeatureId)
+ invalid()
+
+ when:
+ put([featureText: name])
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'locale'
+ text == 'locale with value [xy] is not contained within the list [[de, it, es, fr]]'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can NOT update ProductFeatureLocale with invalid Locale'() {
+ given:
+ String name = 'German_more_testing'
+ optionalHeaders.locale = 'xy'
+ path(catalogProduct, validProductFeatureId)
+ invalid()
+
+ when:
+ put([featureText: name])
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ field == 'locale'
+ text == 'locale with value [xy] is not contained within the list [[de, it, es, fr]]'
+ }
+ payload.messages.size() == 1
+ }
+
+ def 'can NOT update ProductFeatureLocale with invalid productFeatureId'() {
+ given:
+ String name = 'German_more_testing'
+ optionalHeaders.locale = 'de'
+ path(catalogProduct, invalidProductFeatureId)
+ notFound()
+
+ when:
+ put([featureText: name])
+
+ then:
+ !payload
+ }
+
+ def 'can update ProductFeatureLocale with valid Locale'() {
+ given:
+ String name = 'German_more_testing'
+ optionalHeaders.locale = 'de'
+ path(catalogProduct, validProductFeatureId)
+ ok()
+
+ when:
+ put([featureText: name])
+
+ then:
+ payload.results.featureTextLocale == name
+ payload.results.productFeatureId == validProductFeatureId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+ def 'can update with empty ProductFeatureLocale with valid Locale'() {
+ given:
+ String name = ''
+ optionalHeaders.locale = 'de'
+ path(catalogProduct, validProductFeatureId)
+ ok()
+
+ when:
+ put([featureText: name])
+
+ then:
+ payload.results.featureTextLocale == null
+ payload.results.productFeatureId == validProductFeatureId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+
+
+ def 'can NOT get all ProductFeatureLocale for invalid Locale'() {
+ given:
+ optionalHeaders.locale = 'xy'
+ path(catalogProduct)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results == []
+ payload.meta.totalRecords == 0
+ }
+
+ def 'can get all ProductFeatureLocale for a product for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path(catalogProduct)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productId.every { it == productId }
+ payload.results.productFeatureId.every { it }
+ }
+
+ def 'can NOT find ProductFeatureLocale for an invalid ProductFeatureId for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path(catalogProduct, invalidProductFeatureId)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT find ProductFeatureLocale for a valid ProductFeatureId for a invalid Locale'() {
+ given:
+ optionalHeaders.locale = 'xy'
+ path(catalogProduct, validProductFeatureId)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT find ProductFeatureLocale for invalid ProductFeatureId for invalid Locale'() {
+ given:
+ optionalHeaders.locale = 'xy'
+ path(catalogProduct, invalidProductFeatureId)
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can find ProductFeatureLocale for a valid ProductFeatureId for a valid Locale'() {
+ given:
+ optionalHeaders.locale = 'de'
+ path(catalogProduct, validProductFeatureId)
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.productFeatureId == validProductFeatureId
+ payload.results.size() == ALL_COLUMNS_SIZE
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeatureMoveFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeatureMoveFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeatureMoveFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,141 @@
+package com.lemans.ds.product.feature
+
+import com.lemans.ds.MoveCommand
+import com.lemans.ds.product.Product
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+import spock.lang.Stepwise
+
+@Stepwise
+class ProductFeatureMoveFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'feature' }
+
+ @Shared List features
+ @Shared Map feature1
+ @Shared Map feature2
+ @Shared Map feature3
+ @Shared Map feature4
+ @Shared Map feature5
+ @Shared Map feature6
+
+ List initialFeatureOrder = [1778579, 1778580, 1778581, 1778582, 1778583, 1778584]
+
+ Integer productId = 342390
+ Map catalogProduct = [catalog: Product.VIRTUAL_CATALOG_ID, product: productId]
+
+ // TODO: add MoveCommand unit tests
+
+ // TODO: make sure move fails on different products
+// def 'can NOT move a ProductFeature to a different Product'() {
+//
+// }
+
+ def 'load initial Features for a Product'() {
+ given:
+ queryParams.pageSize = 10
+ path(catalogProduct)
+ ok()
+
+ when:
+ get()
+ populateFeatures(payload.results)
+
+ then:
+ assertFeaturesAreOrderedBySequence()
+ assertFeatureOrder(initialFeatureOrder)
+ }
+
+ def 'can move a ProductFeature after another'() {
+ given:
+ Map move = [targetId: 1778581, position: MoveCommand.AFTER]
+ path(catalogProduct, '1778579/move')
+ ok()
+
+ when:
+ put(move)
+
+ then:
+ payload.results.productFeatureId == 1778579
+ payload.results.sequence == 3
+ }
+
+ def 'load Features for a Product after MOVE AFTER'() {
+ given:
+ queryParams.pageSize = 10
+ path(catalogProduct)
+ ok()
+
+ when:
+ get()
+ populateFeatures(payload.results)
+
+ then:
+ assertFeaturesAreOrderedBySequence()
+ assertFeatureOrder([1778580, 1778581, 1778579, 1778582, 1778583, 1778584])
+ }
+
+ def 'can move a ProductFeature BEFORE another'() {
+ given:
+ Map move = [targetId: 1778580, position: MoveCommand.BEFORE]
+ path(catalogProduct, '1778579/move')
+ ok()
+
+ when:
+ put(move)
+
+ then:
+ payload.results.productFeatureId == 1778579
+ }
+
+ def 'load Features for a Product after MOVE BEFORE'() {
+ given:
+ queryParams.pageSize = 10
+ path(catalogProduct)
+ ok()
+
+ when:
+ get()
+ populateFeatures(payload.results)
+
+ then:
+ assertFeaturesAreOrderedBySequence()
+ assertFeatureOrder(initialFeatureOrder)
+ }
+
+ private void assertFeatureOrder(List ids) {
+ with(this) {
+ feature1.productFeatureId == ids[0]
+ feature2.productFeatureId == ids[1]
+ feature3.productFeatureId == ids[2]
+ feature4.productFeatureId == ids[3]
+ feature5.productFeatureId == ids[4]
+ feature6.productFeatureId == ids[5]
+ }
+ true
+ }
+
+ private boolean assertFeaturesAreOrderedBySequence() {
+ with(this) {
+ features.size() == 6
+ feature1.sequence == 1
+ feature2.sequence == 2
+ feature3.sequence == 3
+ feature4.sequence == 4
+ feature5.sequence == 5
+ feature6.sequence == 6
+ }
+ true
+ }
+
+ private void populateFeatures(featureList) {
+ features = featureList
+ feature1 = features[0]
+ feature2 = features[1]
+ feature3 = features[2]
+ feature4 = features[3]
+ feature5 = features[4]
+ feature6 = features[5]
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeaturePersistenceFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeaturePersistenceFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeaturePersistenceFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,75 @@
+package com.lemans.ds.product.feature
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+import spock.lang.Stepwise
+
+@Stepwise
+class ProductFeaturePersistenceFuncSpec extends DsFuncSpec implements ProductFeatureTestFixture {
+
+ @Override
+ String resourceName() { 'feature' }
+
+ @Shared Integer productFeatureId
+
+ def 'can create multiple Product Features'() {
+ given:
+ path(catalogProduct)
+ ok()
+
+ when:
+ post(validFeatures)
+ List errors = errors()
+
+ then:
+ !errors
+ payload.messages.find { it.type == 'info' }.text == 'Created 2 Product Feature(s)'
+ payload.results.size() == 2
+ }
+
+ def 'can create a Product Feature'() {
+ given:
+ path(catalogProduct)
+ ok()
+
+ when:
+ post(validFeature1)
+ List errors = errors()
+ productFeatureId = payload.results[0].productFeatureId
+
+ then:
+ !errors
+ payload.messages.find { it.type == 'info' }.text == 'Created 1 Product Feature(s)'
+ payload.results.size() == 1
+ }
+
+ def 'can update a Product Feature'() {
+ given:
+ String text = 'wtf_' + new Date()
+ path(catalogProduct, productFeatureId)
+ ok()
+
+ when:
+ put([featureText: text])
+
+ then:
+ payload.results.productFeatureId == productFeatureId
+ payload.results.featureText == text
+ }
+
+ def 'can delete a Product Feature'() {
+ given:
+ path(catalogProduct, productFeatureId)
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ private List errors() {
+ payload.messages.findAll { it.type == 'error' }
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeatureTestFixture.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeatureTestFixture.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeatureTestFixture.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,19 @@
+package com.lemans.ds.product.feature
+
+import com.lemans.ds.product.Product
+
+
+trait ProductFeatureTestFixture {
+
+ Integer catalogInstanceId = Product.VIRTUAL_CATALOG_ID
+ Integer productId = 333467
+ Map catalogProduct = [catalog: catalogInstanceId, product: productId]
+
+ Integer featureTypeId = 13001
+ Map invalidFeature1 = [featureText: 'abc', featureTypeId: null]
+ Map invalidFeature2 = [featureText: null, featureTypeId: featureTypeId]
+ Map validFeature1 = [featureText: 'abc', featureTypeId: featureTypeId]
+ Map validFeature2 = [featureText: 'xyz', featureTypeId: featureTypeId]
+ Map invalidFeatures = [features: [invalidFeature1, validFeature1, invalidFeature2, validFeature2]]
+ Map validFeatures = [features: [validFeature1, validFeature2]]
+}
Index: src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeatureValidationFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeatureValidationFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/product/feature/ProductFeatureValidationFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,139 @@
+package com.lemans.ds.product.feature
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class ProductFeatureValidationFuncSpec extends DsFuncSpec implements ProductFeatureTestFixture {
+
+ @Override
+ String resourceName() { 'feature' }
+
+ def 'can NOT create a ProductFeature without a Catalog'() {
+ given:
+ path(catalog: null, product: productId)
+ invalid()
+
+ when:
+ post(validFeature1)
+ List errors = errors()
+
+ then:
+ errors[0].text == 'Catalog is required'
+ errors.size() == 1
+ }
+
+ def 'can NOT create a ProductFeature without a Product'() {
+ given:
+ path(catalog: catalogInstanceId, product: null)
+ invalid()
+
+ when:
+ post(validFeature1)
+ List errors = errors()
+
+
+ then:
+ errors[0].text == 'productId is required'
+ errors.size() == 1
+ }
+
+ def 'can NOT create a ProductFeature with an invalid Product'() {
+ given:
+ path(catalog: catalogInstanceId, product: -1)
+ invalid()
+
+ when:
+ post(validFeature1)
+ List errors = errors()
+
+ then:
+ errors[0].text == 'Invalid productId'
+ errors.size() == 1
+ }
+
+ def 'can NOT create a ProductFeature with an invalid Catalog'() {
+ given:
+ path(catalog: -1, product: productId)
+ invalid()
+
+ when:
+ post(validFeature1)
+ List errors = errors()
+
+ then:
+ errors.size() == 1
+ errors[0].text == 'Invalid catalog instance [-1] for the product'
+ }
+
+ def 'can NOT create an invalid ProductFeature'() {
+ given:
+ path(catalogProduct)
+ invalid()
+
+ when:
+ post(invalidFeature1)
+ List errors = errors()
+
+ then:
+ errors.size() == 1
+ errors[0].text == 'Product Feature Type is required'
+ errors[0].index == 1
+ }
+
+ def 'can NOT create invalid ProductFeatures'() {
+ given:
+ path(catalogProduct)
+ invalid()
+
+ when:
+ post(invalidFeatures)
+ List errors = errors()
+
+ then:
+ errors.size() == 2
+ errors[0].text == 'Product Feature Type is required'
+ errors[0].index == 1
+ errors[1].text == 'Product Feature Text is required'
+ errors[1].index == 3
+ }
+
+ def 'can NOT update an invalid ProductFeature'() {
+ given:
+ path(catalogProduct, 54)
+ invalid()
+
+ when:
+ put([featureText: null, featureTypeId: null])
+
+ then:
+ payload.messages.find { it.text == 'Product Feature Text is required' }
+ payload.messages.find { it.text == 'Product Feature Type is required' }
+ }
+
+ def 'can NOT update a ProductFeature that does not exist'() {
+ given:
+ path(catalogProduct, -1)
+ notFound()
+
+ when:
+ put()
+
+ then:
+ !payload
+ }
+
+ def 'can NOT delete a ProductFeature that does not exist'() {
+ given:
+ path(catalogProduct, -1)
+ notFound()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ private List errors() {
+ payload.messages.findAll { it.type == 'error' }
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/product/merchandising/ProductMerchandisingFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/product/merchandising/ProductMerchandisingFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/product/merchandising/ProductMerchandisingFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,92 @@
+package com.lemans.ds.product.merchandising
+
+import com.lemans.ds.CatalogTestFixture
+import com.lemans.ds.testing.DsFuncSpec
+
+/**
+ * Created by MUmachi on 10/16/2017.
+ */
+class ProductMerchandisingFuncSpec extends DsFuncSpec implements CatalogTestFixture {
+
+ @Override
+ String resourceName() { 'attribute' }
+
+ final static int ALL_COLUMNS_SIZE = 10
+
+ Integer productId = 343229
+ Integer categoryAttributeId = 32177
+
+ def 'can find ALL Product Merchandising details by product'() {
+ given:
+ path(catalog + [product: productId])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.size() >= 1
+ //payload.results.productId == productId
+ //payload.results.attribute[0].size() >= ALL_COLUMNS_SIZE
+ //payload.results.attribute.mediaUrl
+ //payload.results.attribute.partDescr
+ //payload.results.attribute.partNumber
+ //payload.results.attribute.punctuatedPartNumber
+ }
+
+ def 'can create or update isDropdown Attribute by productId and CategoryAttributeId'() {
+ given:
+ path(catalog + [product: 316931], 45891)
+
+ Boolean isDropdown = false
+
+ String json = """
+{
+"isDropdown": ${isDropdown},
+ }"""
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ //payload.results.isDropdown == isDropdown
+ payload.results.size() == 15
+ }
+
+ def 'can create or update isHidden Attribute by productId and CategoryAttributeId'() {
+ given:
+ path(catalog + [product: productId], categoryAttributeId)
+ Boolean isHidden = true
+ String json = """
+{
+"isHidden": $isHidden,
+ }"""
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.isHidden == isHidden
+ payload.results.size() == 15
+ }
+
+ def 'can create or update isSplit Attribute by productId and CategoryAttributeId'() {
+ given:
+ path(catalog + [product: productId], categoryAttributeId)
+ Boolean isSplit = true
+ String json = """
+{
+"isSplit": $isSplit,
+ }"""
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload.results.isSplit == isSplit
+ payload.results.size() == 15
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/publicationcategory/ProductPublicationCategoryFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/publicationcategory/ProductPublicationCategoryFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/publicationcategory/ProductPublicationCategoryFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,65 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Stepwise
+
+@Stepwise
+class ProductPublicationCategoryFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'publicationCategory' }
+
+ def 'can create ProductPublicationCategory'() {
+ Map json = [productId: 316782]
+ Integer categoryId = 15
+ path("$categoryId/product")
+ ok()
+
+ when:
+ post(json)
+
+ then:
+ payload.results.categoryId == 15
+ payload.results.productId == 316782
+ }
+
+ def 'can get publicationCategory'() {
+ given:
+ Integer categoryId = 15
+ path("$categoryId/product")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.every { categoryId == 15 }
+ }
+
+ def 'can get categories'() {
+ given:
+ Integer productId = 316782
+ path([product: productId])
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results.every { productId }
+ }
+
+ def 'can delete productPublicationCategory'() {
+ given:
+ Integer categoryId = 15
+ Integer productId = 316782
+ path("$categoryId/product/$productId")
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/publicationcategory/PublicationCategoryAttributeFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/publicationcategory/PublicationCategoryAttributeFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/publicationcategory/PublicationCategoryAttributeFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,142 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class PublicationCategoryAttributeFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'publicationCategory' }
+
+ Integer publicationCategoryId = 15
+
+ Integer invalidPublicationCategoryId = -1
+
+ Integer attributeNameId = 1
+
+ Integer invalidAttributeNameId = -1
+
+ def 'can not create a publicationCategoryAttribute'() {
+ given:
+ Map input = [:]
+ path("$publicationCategoryId/attribute")
+ invalid()
+
+ when:
+ post(input)
+
+ then:
+ payload.messages[0].field == 'attributeNameId'
+ payload.messages[0].text == 'attributeNameId is required'
+ }
+
+ def 'can not create with invalid publication category'() {
+ given:
+ Map input = [:]
+ path("$invalidPublicationCategoryId/attribute")
+ invalid()
+
+ when:
+ post(input)
+
+ then:
+ payload.messages[0].field == 'attributeNameId'
+ payload.messages[0].text == 'attributeNameId is required'
+ }
+
+ def 'can not find attributes for invalidPublicationCategory'() {
+ given:
+ path("$invalidPublicationCategoryId/attribute")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload
+ }
+
+ def 'can not find invalid publicationCategoryAttribute'() {
+ given:
+ path("$invalidPublicationCategoryId/attribute/$invalidAttributeNameId")
+ notFound()
+
+ when:
+ get()
+
+ then:
+ !payload
+ }
+
+ def 'can create a publicationCategoryAttribute'() {
+ given:
+ Map input = [attributeNameId: 1]
+ path("$publicationCategoryId/attribute")
+ ok()
+
+ when:
+ post(input)
+
+ then:
+ payload
+ }
+
+ def 'can not create publication with same attributeNameId and publicationCategroyId'() {
+ given:
+ Map input = [attributeNameId: 1]
+ path("$publicationCategoryId/attribute")
+ invalid()
+
+ when:
+ post(input)
+
+ then:
+ payload.messages[0].type == 'error'
+ payload.messages[0].text == 'Duplicate publicationCategoryAttribute'
+ }
+
+ def 'can find all publicationCategoryAttributes'() {
+ given:
+ path("$publicationCategoryId/attribute")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload
+ }
+
+ def 'can find a publicationCategoryAttribute'() {
+ given:
+ path("$publicationCategoryId/attribute/$attributeNameId")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload
+ }
+
+ def 'can delete a publicationCategoryAttribute'() {
+ path("$publicationCategoryId/attribute/$attributeNameId")
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can get possible attribute options for a publicationCategory'() {
+ path('369/attributeOptions')
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/publicationcategory/PublicationCategoryAttributeMoveFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/publicationcategory/PublicationCategoryAttributeMoveFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/publicationcategory/PublicationCategoryAttributeMoveFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,99 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+import spock.lang.Stepwise
+
+@Stepwise
+class PublicationCategoryAttributeMoveFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'publicationCategory' }
+
+ Integer publicationCategoryId = 15
+
+ Integer sourceAttributeNameId = 9
+
+ Integer targetAttributeNameId = 1
+
+ @Shared Integer target
+
+ @Shared Integer source
+
+ def 'can create one publicationCategoryAttribute'() {
+ given:
+ Map input = [attributeNameId: 1]
+ path("$publicationCategoryId/attribute")
+ ok()
+
+ when:
+ post(input)
+ target = payload.results.publicationCategoryAttributeId
+
+
+ then:
+ payload
+ }
+
+ def 'can create two publicationCategoryAttribute'() {
+ given:
+ Map input = [attributeNameId: 9]
+ path("$publicationCategoryId/attribute")
+ ok()
+
+ when:
+ post(input)
+ source = payload.results.publicationCategoryAttributeId
+
+ then:
+ payload
+ }
+
+ def 'can move before'() {
+ given:
+ Map input = [targetId: targetAttributeNameId, position: 'BEFORE']
+ path("$publicationCategoryId/attribute/$sourceAttributeNameId/move")
+ ok()
+
+ when:
+ put(input)
+
+ then:
+ payload
+ }
+
+ def 'can move after'() {
+ given:
+ Map input = [targetId: targetAttributeNameId, position: 'AFTER']
+ path("$publicationCategoryId/attribute/$sourceAttributeNameId/move")
+ ok()
+
+ when:
+ put(input)
+
+ then:
+ payload
+ }
+
+ def 'can delete publicationCategoryAttribute1'() {
+ path("$publicationCategoryId/attribute/$sourceAttributeNameId")
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can delete publicationCategoryAttribute2'() {
+ path("$publicationCategoryId/attribute/$targetAttributeNameId")
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/publicationcategory/PublicationCategoryFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/publicationcategory/PublicationCategoryFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/publicationcategory/PublicationCategoryFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,130 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+
+class PublicationCategoryFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'publicationCategory' }
+
+ @Shared
+ Integer parent
+
+ @Shared
+ Integer child
+
+ def 'can not create a parent publicationCategory'() {
+ given:
+ Map json = [description: 'Third']
+ path()
+ invalid()
+
+ when:
+ post(json)
+
+ then:
+ payload.messages[0].text == 'categoryName is required'
+ }
+
+ def 'can create a parent publicationCategory'() {
+ given:
+ Map json = [description: 'Third', categoryName: 'Third Category']
+ path()
+ ok()
+
+ when:
+ post(json)
+ parent = payload.results.categoryId
+
+ then:
+ payload
+ }
+
+ def 'can create a child publicationCategory'() {
+ given:
+ Map json = [description: 'child', categoryName: 'Child of Third Category', parentCategoryId: parent]
+ path()
+ ok()
+
+ when:
+ post(json)
+ child = payload.results.categoryId
+
+ then:
+ payload
+ }
+
+ def 'can get publicationCAtegories'() {
+ given:
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload
+ }
+
+ def 'can get publicationCategory'() {
+ given:
+ path("$parent")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload
+ }
+
+ def 'can update publicationCAtegory'() {
+ given:
+ Map json = [description: 'updated', categoryName: 'Third Category']
+ path("$parent")
+ ok()
+
+ when:
+ put(json)
+
+ then:
+ payload
+ }
+
+ def 'can not delete a parent publicationCAtegory'() {
+ given:
+ path("$parent")
+ invalid()
+
+ when:
+ delete()
+
+ then:
+ payload.messages[0].text == 'Publication Category may not be deleted'
+ }
+
+ def 'can delete a child publicationCAtegory'() {
+ given:
+ path("$child")
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can delete parent publication category'() {
+ given:
+ path("$parent")
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/publicationcategory/PublicationCategoryMoveFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/publicationcategory/PublicationCategoryMoveFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/publicationcategory/PublicationCategoryMoveFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,201 @@
+package com.lemans.ds.publicationcategory
+
+import com.lemans.ds.testing.DsFuncSpec
+import spock.lang.Shared
+
+class PublicationCategoryMoveFuncSpec extends DsFuncSpec {
+
+ @Shared
+ Integer parent
+
+ @Shared
+ Integer parent2
+
+ @Shared
+ Integer child1
+
+ @Shared
+ Integer child2
+
+ def 'can create a parent publicationCategory'() {
+ given:
+ Map json = [description: 'second Parent Category', categoryName: 'category2']
+ path('publicationCategory')
+ ok()
+
+ when:
+ post(json)
+ parent = payload.results.categoryId
+
+ then:
+ payload
+ }
+
+ def 'can create second parent publicationCategory'() {
+ given:
+ Map json = [description: 'second Parent Category', categoryName: 'category2']
+ path('publicationCategory')
+ ok()
+
+ when:
+ post(json)
+ parent2 = payload.results.categoryId
+
+ then:
+ payload
+ }
+
+ def 'can create a child publicationCategory'() {
+ given:
+ Map json = [description: 'First child Category', categoryName: 'child1 category1', parentCategoryId: parent]
+ path('publicationCategory')
+ ok()
+
+ when:
+ post(json)
+ child1 = payload.results.categoryId
+
+ then:
+ payload
+ }
+
+ def 'can create a second child publicationCategory'() {
+ given:
+ Map json = [description: 'second child Category', categoryName: 'child2 category1', parentCategoryId: parent]
+ path('publicationCategory')
+ ok()
+
+ when:
+ post(json)
+ child2 = payload.results.categoryId
+
+ then:
+ payload
+ }
+
+ def 'can NOT move a Category INSIDE another Category if it would create a cycle'() {
+ given:
+ int targetId = child1
+ int categoryId = parent
+ Map body = [targetId: targetId, position: 'INSIDE']
+ path([publicationCategory: categoryId], 'move')
+ invalid()
+
+ when:
+ put(body)
+
+ then:
+ payload.messages.find { it.text == 'PublicationCategory may not be moved to create a cycle' }
+ }
+
+ def 'can move a Category AFTER a SIBLING Category'() {
+ given:
+ int categoryId = child1
+ int targetId = child2
+ Map body = [targetId: targetId, position: 'AFTER']
+ path([publicationCategory: categoryId], 'move')
+ ok()
+
+ when:
+ put(body)
+
+ then:
+ Thread.sleep(1000)
+ payload.results.sequence == 2
+ }
+
+ def 'can move a Category BEFORE a SIBLING Category'() {
+ given:
+ int categoryId = child1
+ int targetId = child2
+ Map body = [targetId: targetId, position: 'BEFORE']
+ path([publicationCategory: categoryId], 'move')
+ ok()
+
+ when:
+ put(body)
+
+ then:
+ Thread.sleep(1000)
+ payload.results.sequence == 1
+ }
+
+ def 'can move a Category to the first sibling UNDER another Category'() {
+ given:
+ int targetId = parent
+ int categoryId = parent2
+ Map body = [targetId: targetId, position: 'INSIDE']
+ path([publicationCategory: categoryId], 'move')
+ ok()
+
+ when:
+ put(body)
+
+ then:
+ payload.results.parentCategoryId == targetId
+ payload.results.sequence == 1
+ }
+
+ def 'can move a Category back alongside AFTER its original sibling Category'() {
+ given:
+ int targetId = parent
+ int categoryId = parent2
+ Map body = [targetId: targetId, position: 'AFTER']
+ path([publicationCategory: categoryId], 'move')
+ ok()
+
+ when:
+ put(body)
+
+ then:
+ payload.results.parentCategoryId == null
+ }
+
+ def 'can delete child1'() {
+ given:
+ path([publicationCategory: child1])
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can delete child2'() {
+ given:
+ path([publicationCategory: child2])
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can delete parent1'() {
+ given:
+ path([publicationCategory: parent])
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+
+ def 'can delete parent2'() {
+ given:
+ path([publicationCategory: parent2])
+ ok()
+
+ when:
+ delete()
+
+ then:
+ !payload
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/search/BrandSearchFunctionalSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/search/BrandSearchFunctionalSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/search/BrandSearchFunctionalSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,95 @@
+package com.lemans.ds.search
+
+import com.lemans.ds.testing.DsFuncSpec
+
+
+class BrandSearchFunctionalSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'brandSearch' }
+
+ def 'can search on brands'() {
+ given:
+ String term = 'cy'
+ queryParams.query = term
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ with(payload) {
+ results[0].brandCode == '0116'
+ results[0].brandId == 116
+ results[0].brandName == 'CYCRA'
+ }
+ }
+
+ def 'can search on brandCode with wildcard characters'() {
+ given:
+ queryParams.pageSize = 50
+ queryParams.query = '0*6'
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ with(payload) {
+ results.brandCode.every { it.startsWith('0') }
+ results.brandCode.every { it.endsWith('6') }
+ }
+ }
+
+ def 'can search on brandName with wildcard characters'() {
+ given:
+ queryParams.pageSize = 50
+ queryParams.query = 'p*t*s'
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ with(payload) {
+ results.brandName.every { it.startsWith('P') }
+ results.brandName.every { it.contains('T') }
+ results.brandName.every { it.endsWith('S') }
+ }
+ }
+
+ def 'can search on brandName without wildcard characters'() {
+ given:
+ queryParams.pageSize = 50
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ with(payload) {
+ results.brandId
+ results.brandCode
+ results.brandName
+ }
+ }
+
+ def 'can NOT find a brand that does not exist'() {
+ given:
+ queryParams.query = '[___]'
+ path()
+ ok()
+
+ when:
+ get()
+
+ then:
+ with(payload) {
+ !results
+ }
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/search/GenericFilterFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/search/GenericFilterFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/search/GenericFilterFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,97 @@
+package com.lemans.ds.search
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class GenericFilterFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'search' }
+
+ def 'can NOT get filters with invalid filterType'() {
+ given:
+ queryParams.brandId = 123
+ queryParams.derivedPartStatusId = 9001
+ String searchType = 'part'
+ String filterType = 'invalid_type'
+ path("$searchType/filter/$filterType")
+ invalid()
+
+ when:
+ get()
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'Invalid Filter Type'
+ }
+ }
+
+ def 'can NOT get filters with a filterType that does not match searchType'() {
+ given:
+ queryParams.brandId = 123
+ queryParams.derivedPartStatusId = 9001
+ String searchType = 'product'
+ String filterType = 'PartsWithoutCategory'
+ path("$searchType/filter/$filterType")
+ invalid()
+
+ when:
+ get()
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'Invalid Filter Type'
+ }
+ }
+
+ def 'can NOT get filters with invalid searchType'() {
+ given:
+ queryParams.brandId = 123
+ queryParams.derivedPartStatusId = 9001
+ String searchType = '_invalid_'
+ String filterType = 'PartsWithoutCategory'
+ path("$searchType/filter/$filterType")
+ invalid()
+
+ when:
+ get()
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'Invalid Filter Type'
+ }
+ }
+
+ def 'can get filters with a filterType'() {
+ given:
+ queryParams.derivedPartStatusId = 9001
+ queryParams.isDigiActive = 1
+ String searchType = 'part'
+ String filterType = 'PartsWithoutCategory'
+ path("$searchType/filter/$filterType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get filters with filterType isDigiActive'() {
+ given:
+ queryParams.isDigiActive = 1
+ String searchType = 'PRODUCT'
+ String filterType = 'PRODUCTSWITHOUTMEDIA'
+ path("$searchType/filter/$filterType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/search/GenericOptionsFilterFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/search/GenericOptionsFilterFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/search/GenericOptionsFilterFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,587 @@
+package com.lemans.ds.search
+
+import com.lemans.ds.testing.DsFuncSpec
+
+@SuppressWarnings(['MethodCount'])
+class GenericOptionsFilterFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'search' }
+
+ def 'can NOT get filter options with invalid filterType'() {
+ given:
+ queryParams.brandId = 123
+ queryParams.derivedPartStatusId = 9001
+ queryParams.filterType = 'invalid_type'
+ String filterName = 'productId'
+ String searchType = 'part'
+ path("$searchType/filter/$filterName/options")
+ invalid()
+
+ when:
+ get()
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'Invalid Filter Type'
+ }
+ }
+
+ def 'can NOT get filter options with a filterType that does not match searchType'() {
+ given:
+ queryParams.brandId = 123
+ queryParams.derivedPartStatusId = 9001
+ String searchType = 'product'
+ queryParams.filterType = 'PartsWithoutCategory'
+ String filterName = 'productId'
+ path("$searchType/filter/$filterName/options")
+ invalid()
+
+ when:
+ get()
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'Invalid Filter Type'
+ }
+ }
+
+ def 'can NOT get filter options with invalid searchType'() {
+ given:
+ queryParams.brandId = 123
+ queryParams.derivedPartStatusId = 9001
+ String searchType = '_invalid_'
+ queryParams.filterType = 'PartsWithoutCategory'
+ String filterName = 'productId'
+ path("$searchType/filter/$filterName/options")
+ invalid()
+
+ when:
+ get()
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'Invalid Filter Type'
+ }
+ }
+
+ def 'can NOT get filter options with invalid filterName'() {
+ given:
+ queryParams.derivedPartStatusId = 9001
+ queryParams.isDigiActive = 1
+ String searchType = 'part'
+ queryParams.filterType = 'PartsWithoutCategory'
+ String filterName = '_Invalid_'
+ path("$searchType/filter/$filterName/options")
+ invalid()
+
+ when:
+ get()
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'Invalid Filter Name'
+ }
+ }
+
+ def 'can NOT get filter options with a filterName that does not match searchType or filterType'() {
+ given:
+ queryParams.derivedPartStatusId = 9001
+ queryParams.isDigiActive = 1
+ String searchType = 'product'
+ queryParams.filterType = 'productsWithOutMedia'
+ String filterName = 'attributeNameId'
+ path("$searchType/filter/$filterName/options")
+ invalid()
+
+ when:
+ get()
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'Invalid Filter Name'
+ }
+ }
+
+ def 'can get filter options with a filterType'() {
+ given:
+ queryParams.derivedPartStatusId = 9001
+ queryParams.isDigiActive = 1
+ String searchType = 'part'
+ queryParams.filterType = 'part'
+ String filterName = 'productId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get filter options with filterType partsWithoutMedia'() {
+ given:
+ queryParams.isDigiActive = 1
+ String searchType = 'part'
+ queryParams.filterType = 'partsWithoutMedia'
+ String filterName = 'productId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get filter options with filterType partsWithoutMedia and filterName brandId'() {
+ given:
+ queryParams.isDigiActive = 1
+ String searchType = 'part'
+ queryParams.filterType = 'partsWithoutMedia'
+ String filterName = 'brandId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can filter by product'() {
+ given:
+ queryParams.isDigiActive = 1
+ String searchType = 'product'
+ String filterName = 'brandId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can filter by filterType product'() {
+ given:
+ queryParams.isDigiActive = 1
+ String searchType = 'product'
+ queryParams.filterType = 'product'
+ String filterName = 'brandId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ // Parts without attribute value
+ def 'can get BRANDID results in PARTSWITHOUTATTRIBUTEVALUE Queue'() {
+ given:
+ queryParams.isDigiActive = 0
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTATTRIBUTEVALUE'
+ String filterName = 'brandId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get CATEGORY results in PARTSWITHOUTATTRIBUTEVALUE Queue'() {
+ given:
+ queryParams.isDigiActive = 0
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTATTRIBUTEVALUE'
+ String filterName = 'categoryId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get PRODUCT results in PARTSWITHOUTATTRIBUTEVALUE Queue'() {
+ given:
+ queryParams.isDigiActive = 0
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTATTRIBUTEVALUE'
+ String filterName = 'productId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get PARTSTATUS results in PARTSWITHOUTATTRIBUTEVALUE Queue'() {
+ given:
+ queryParams.isDigiActive = 0
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTATTRIBUTEVALUE'
+ String filterName = 'derivedPartStatusId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get PARTSTATUS results in PARTSWITHOUTATTRIBUTEVALUE Queue with MULTISELECT'() {
+ given:
+ queryParams.isDigiActive = 0
+ queryParams.derivedPartStatusId = 9002
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTATTRIBUTEVALUE'
+ String filterName = 'categoryId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ // PartsWithoutCategory
+ def 'can get BRANDID results in PARTSWITHOUTCATEGORY Queue'() {
+ given:
+ queryParams.isDigiActive = 0
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTCATEGORY'
+ String filterName = 'brandId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get VENDORID results in PARTSWITHOUTCATEGORY queue'() {
+ given:
+ queryParams.isDigiActive = 0
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTCATEGORY'
+ String filterName = 'vendorId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get ASSIGNABLECATEGORY results in PARTSWITHOUTCATEGORY queue'() {
+ given:
+ queryParams.isDigiActive = 0
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTCATEGORY'
+ String filterName = 'assignableCategoryId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get DERIVEDPARTSTATUS results in PARTSWITHOUTCATEGORY queue'() {
+ given:
+ queryParams.isDigiActive = 0
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTCATEGORY'
+ String filterName = 'derivedPartStatusId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get SUBCOMCODE results in PARTSWITHOUTCATEGORY queue'() {
+ given:
+ queryParams.isDigiActive = 0
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTCATEGORY'
+ String filterName = 'subComCodeId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get BRANDID results in PARTSWITHOUTCATEGORY Queue with MULTISELECT parameter'() {
+ given:
+ queryParams.isDigiActive = 0
+ queryParams.derivedPartStatusId = [9001, 9002] // 9001
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTCATEGORY'
+ String filterName = 'brandId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ // Parts Without Products
+
+ def 'can get BRAND results in PARTSWITHOUTPRODUCT queue'() {
+ given:
+ queryParams.isDigiActive = 0
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTPRODUCT'
+ String filterName = 'brandId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get CATEGORY results in PARTSWITHOUTPRODUCT queue'() {
+ given:
+ queryParams.isDigiActive = 0
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTPRODUCT'
+ String filterName = 'categoryId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get PARTSTATUS results in PARTSWITHOUTPRODUCT queue'() {
+ given:
+ queryParams.isDigiActive = 0
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTPRODUCT'
+ String filterName = 'derivedPartStatusId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get ASSIGNABLEPRODUCT results in PARTSWITHOUTPRODUCT queue'() {
+ given:
+ queryParams.isDigiActive = 1
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTPRODUCT'
+ String filterName = 'assignableProductId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get VENDOR results in PARTSWITHOUTPRODUCT queue'() {
+ given:
+ queryParams.isDigiActive = 0
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTPRODUCT'
+ String filterName = 'vendorId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get VENDOR results in PARTSWITHOUTPRODUCT queue with MULTISELECT'() {
+ given:
+ queryParams.isDigiActive = 0
+ queryParams.derivedPartStatusId = [9001, 9002]
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTPRODUCT'
+ String filterName = 'vendorId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get VENDOR results in PARTSWITHOUTPRODUCT queue with different filters'() {
+ given:
+ queryParams.isDigiActive = 1
+ queryParams.brandId = 7
+ queryParams.categoryId = 4804
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTPRODUCT'
+ String filterName = 'vendorId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ // Products Without Media
+
+ def 'can get PRODUCTID results in PRODUCTSWITHOUTMEDIA queue'() {
+ given:
+ queryParams.isDigiActive = 1
+ String searchType = 'PRODUCT'
+ queryParams.filterType = 'PRODUCTSWITHOUTMEDIA'
+ String filterName = 'productId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get BRANDID results in PRODUCTSWITHOUTMEDIA'() {
+ given:
+ queryParams.isDigiActive = 1
+ String searchType = 'PRODUCT'
+ queryParams.filterType = 'PRODUCTSWITHOUTMEDIA'
+ String filterName = 'brandId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get MEDIATYPE results in PRODUCTWITHOUMEDIA'() {
+ given:
+ queryParams.isDigiActive = 1
+ String searchType = 'PRODUCT'
+ queryParams.filterType = 'PRODUCTSWITHOUTMEDIA'
+ String filterName = 'mediaTypeId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get CATEGORY results in PRODUCTSWITHOUTMEDIA'() {
+ given:
+ queryParams.isDigiActive = 1
+ String searchType = 'PRODUCT'
+ queryParams.filterType = 'PRODUCTSWITHOUTMEDIA'
+ String filterName = 'categoryId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'multiSelect category in partsWithoutAttributeValue'() {
+ given:
+ queryParams.categoryId = [4537, 4545]
+ queryParams.isDigiActive = 1
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTATTRIBUTEVALUE'
+ String filterName = 'brandId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'multi select category and derived part status in partsWithoutAttributeValue'() {
+ given:
+ queryParams.categoryId = [4551, 4552]
+ queryParams.derivedPartStatusId = [9001, 9002]
+ String searchType = 'PART'
+ queryParams.filterType = 'PARTSWITHOUTATTRIBUTEVALUE'
+ String filterName = 'brandId'
+ path("$searchType/filter/$filterName/options")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/search/GenericSearchFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/search/GenericSearchFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/search/GenericSearchFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,392 @@
+package com.lemans.ds.search
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class GenericSearchFuncSpec extends DsFuncSpec {
+
+ @Override
+ String resourceName() { 'search' }
+
+ def 'can get search Results with a filterType'() {
+ given:
+ queryParams.derivedPartStatusId = 9001
+ queryParams.filterType = 'PARTSWITHOUTCATEGORY'
+ String searchType = 'part'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ payload.meta.totalRecords
+ }
+
+ def 'can get search Results with Parts MMY filter'() {
+ given:
+ queryParams.modelId = 5
+ queryParams.startYear = 2000
+ queryParams.endYear = 2003
+ queryParams.filterType = 'PART'
+ String searchType = 'part'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ payload.meta.totalRecords
+ }
+
+ def 'can get partSearchResults by vendorPartNumber'() {
+ given:
+ queryParams.vendorPartNumber = 'FE8301-1-2PK'
+ queryParams.filterType = 'PART'
+ String searchType = 'part'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ payload.meta.totalRecords
+ }
+
+ def 'can get search Results with ProductsWithoutMedia isDigiActive filter'() {
+ given:
+ queryParams.isDigiActive = true
+ queryParams.filterType = 'PRODUCTSWITHOUTMEDIA'
+ String searchType = 'PRODUCT'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ payload.meta.totalRecords
+ }
+
+ def 'can get search results with attribute name and attribute value filter'() {
+ given:
+ queryParams.attributeNameId = 1
+ queryParams.attributeValueId = 3
+ queryParams.filterType = 'PART'
+ String searchType = 'part'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ payload.meta.totalRecords >= 9400
+ }
+
+ def 'can get partsWithMedia results with attributeName and attributeValue Filter'() {
+ given:
+ queryParams.attributeNameId = 910
+ queryParams.attributeValueId = '_NO_VALUE_'
+ queryParams.filterType = 'PARTSWITHOUTMEDIA'
+ String searchType = 'part'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.meta.totalRecords >= 160
+ }
+
+ def 'can get partsWithoutCategory results with partDescr Filter'() {
+ given:
+ queryParams.partDescr = 'helmet'
+ queryParams.filterType = 'PARTSWITHOUTCATEGORY'
+ String searchType = 'part'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.meta.totalRecords >= 7000
+ }
+
+ def 'can get search results filtered by vendorId'() {
+ given:
+ queryParams.vendorId = '1646-A'
+ queryParams.filterType = 'PARTSWITHOUTPRODUCT'
+ String searchType = 'part'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.meta.totalRecords >= 40
+ }
+
+ def 'can get search results filtered by vendorPartNumber'() {
+ given:
+ queryParams.vendorPartNumber = 'KG0025HPK'
+ queryParams.filterType = 'PARTSWITHOUTATTRIBUTEVALUE'
+ String searchType = 'part'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.meta.totalRecords == 0
+ }
+
+ def 'can not get results with invalid Search value'() {
+ given:
+ queryParams.attributeNameId = 1
+ queryParams.attributeValueId = 3
+ queryParams.filterType = 'PARTSWITHOUTMEDIA'
+ String searchType = 'product'
+ path("$searchType")
+ invalid()
+
+ when:
+ get()
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'Invalid Filter Type'
+ }
+ }
+
+ def 'can get search results with attribute name and attribute value with flag tab results'() {
+ given:
+ queryParams.mode = 'Flag'
+ queryParams.attributeNameId = 1
+ queryParams.attributeValueId = 3
+ queryParams.filterType = 'PART'
+ String searchType = 'part'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ payload.meta.totalRecords >= 9400
+ }
+
+ def 'can NOT get search Results with invalid filterType'() {
+ given:
+ queryParams.brandId = 123
+ queryParams.derivedPartStatusId = 9001
+ String searchType = 'part'
+ queryParams.filterType = '_invalid_'
+ path("$searchType")
+ invalid()
+
+ when:
+ get()
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'Invalid Filter Type'
+ }
+ }
+
+ def 'can NOT get search Results with invalid searchType'() {
+ given:
+ queryParams.brandId = 123
+ queryParams.derivedPartStatusId = 9001
+ String searchType = '_invalid_'
+ queryParams.filterType = 'PARTSWITHOUTCATEGORY'
+ path("$searchType")
+ invalid()
+
+ when:
+ get()
+
+ then:
+ with(payload.messages[0]) {
+ type == 'error'
+ text == 'Invalid Filter Type'
+ }
+ }
+
+ def 'can get tab output'() {
+ given:
+ queryParams.mode = 'Flag'
+ String searchType = 'part'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results[0]
+ }
+
+ def 'can get tab output with more queryParams'() {
+ given:
+ queryParams.mode = 'Flag'
+ queryParams.brandId = 123
+ queryParams.derivedPartStatusId = 9001
+ String searchType = 'part'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results[0]
+ }
+
+ def 'can get parts when mode is null'() {
+ given:
+ String searchType = 'part'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ payload.meta
+ }
+
+ def 'can not get Flag tab ed results with invalid searchType'() {
+ given:
+ String searchType = '_invalid_'
+ queryParams.mode = 'Flag'
+ path("$searchType")
+ invalid()
+
+ when:
+ get()
+
+ then:
+ payload.with {
+ messages[0].type == 'error'
+ messages[0].text == 'Invalid Filter Type'
+ }
+ }
+
+ def 'can not get results if no parts available'() {
+ given:
+ queryParams.brandId = 0002
+ queryParams.partNumber = 'sfdfdgfdgfdg'
+ String searchType = 'part'
+ queryParams.mode = 'Flag'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.with {
+ results == []
+ meta.totalRecords == 0
+ }
+ }
+
+ def 'can get results with multiple partStatus in partSearch'() {
+ queryParams.derivedPartStatusId = [9001, 9002, 9003]
+ String searchType = 'part'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get results with multiple partStatus in partsWihoutCategory'() {
+ queryParams.derivedPartStatusId = [9001, 9002, 9003]
+ String searchType = 'part'
+ queryParams.filterType = 'PARTSWITHOUTCATEGORY'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get results with multiple partStatus in partsWithoutProduct'() {
+ queryParams.derivedPartStatusId = [9001, 9002, 9003]
+ String searchType = 'part'
+ queryParams.filterType = 'PARTSWITHOUTPRODUCT'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get results with multiple partStatus in partsWithoutAttributeValue'() {
+ queryParams.derivedPartStatusId = [9001, 9002, 9003]
+ String searchType = 'part'
+ queryParams.filterType = 'PARTSWITHOUTATTRIBUTEVALUE'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'can get results with multiple partStatus in partsWithoutMedia'() {
+ queryParams.derivedPartStatusId = [9001, 9002, 9003]
+ String searchType = 'part'
+ queryParams.filterType = 'PARTSWITHOUTMEDIA'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+
+ def 'multi Select category partsWithoutAttributeValue'() {
+ queryParams.categoryId = [4544, 4545]
+ queryParams.isDigiActive = 1
+ String searchType = 'part'
+ queryParams.filterType = 'PARTSWITHOUTATTRIBUTEVALUE'
+ path("$searchType")
+ ok()
+
+ when:
+ get()
+
+ then:
+ payload.results
+ }
+}
Index: src/integration-test/groovy/com/lemans/ds/testing/DsFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/ds/testing/DsFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/ds/testing/DsFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,18 @@
+package com.lemans.ds.testing
+
+import com.lemans.testing.LemansApiFunctionalSpec
+import ds.service.Application
+import grails.test.mixin.integration.Integration
+import groovyx.net.http.HTTPBuilder
+
+@Integration(applicationClass = Application)
+abstract class DsFuncSpec extends LemansApiFunctionalSpec {
+
+ @Override
+ final String serviceName() { 'ds-service' }
+
+ def setup() {
+ port = serverPort
+ http = new HTTPBuilder(url())
+ }
+}
Index: src/integration-test/groovy/com/lemans/security/TokenVerifierFuncSpec.groovy
===================================================================
diff -u
--- src/integration-test/groovy/com/lemans/security/TokenVerifierFuncSpec.groovy (revision 0)
+++ src/integration-test/groovy/com/lemans/security/TokenVerifierFuncSpec.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,20 @@
+package com.lemans.security
+
+import com.lemans.ds.testing.DsFuncSpec
+
+class TokenVerifierFuncSpec extends DsFuncSpec {
+
+ def 'can NOT act without a valid security token'() {
+ given:
+ System.setProperty('ignoreToken', '')
+ domain = null
+ path()
+ expectedStatusCode = 401
+
+ when:
+ get()
+
+ then:
+ payload.messages[0].text == 'no credentials'
+ }
+}
Index: src/main/groovy/com/lemans/FeatureToggles.groovy
===================================================================
diff -u
--- src/main/groovy/com/lemans/FeatureToggles.groovy (revision 0)
+++ src/main/groovy/com/lemans/FeatureToggles.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,12 @@
+package com.lemans
+
+class FeatureToggles {
+
+ static final Boolean getOPT_LOCKING_FEATURE_TOGGLE() {
+ System.getProperty('OPT_LOCKING')
+ }
+
+ static final Boolean getUS_EU_SYNC_FEATURE_TOGGLE() {
+ System.getProperty('US_EU_SYNC')
+ }
+}
Index: src/main/groovy/com/lemans/ds/DSLocale.groovy
===================================================================
diff -u
--- src/main/groovy/com/lemans/ds/DSLocale.groovy (revision 0)
+++ src/main/groovy/com/lemans/ds/DSLocale.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,20 @@
+package com.lemans.ds
+
+import com.lemans.services.Auditable
+
+/**
+ * Created by MUmachi on 9/28/2017.
+ */
+abstract class DSLocale extends Auditable {
+
+ String locale
+
+ static constraints = {
+
+
+ locale maxSize: 2, inList: ['de', 'it', 'es', 'fr']
+
+ }
+
+}
+
Index: src/main/groovy/com/lemans/ds/MoveCategoryCommand.groovy
===================================================================
diff -u
--- src/main/groovy/com/lemans/ds/MoveCategoryCommand.groovy (revision 0)
+++ src/main/groovy/com/lemans/ds/MoveCategoryCommand.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,26 @@
+package com.lemans.ds
+
+import com.lemans.FeatureToggles
+import grails.validation.Validateable
+import groovy.transform.ToString
+
+@ToString
+class MoveCategoryCommand implements Validateable {
+
+ static final String BEFORE = 'BEFORE'
+ static final String AFTER = 'AFTER'
+ static final String INSIDE = 'INSIDE'
+
+ static final List POSITIONS = [BEFORE, AFTER, INSIDE].asImmutable()
+
+ Integer targetId
+
+ String position
+
+ Integer version
+
+ static constraints = {
+ position blank: false, inList: POSITIONS
+ version nullable: !FeatureToggles.OPT_LOCKING_FEATURE_TOGGLE
+ }
+}
Index: src/main/groovy/com/lemans/ds/MoveCommand.groovy
===================================================================
diff -u
--- src/main/groovy/com/lemans/ds/MoveCommand.groovy (revision 0)
+++ src/main/groovy/com/lemans/ds/MoveCommand.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,28 @@
+package com.lemans.ds
+
+import com.lemans.FeatureToggles
+import grails.validation.Validateable
+
+
+class MoveCommand implements Validateable {
+
+ static final String BEFORE = 'BEFORE'
+ static final String AFTER = 'AFTER'
+ static final String INSIDE = 'INSIDE'
+
+ static final List POSITIONS = [BEFORE, AFTER, INSIDE].asImmutable()
+
+ Integer targetId
+
+ String position
+
+ Integer sourceVersion
+
+ Integer targetVersion
+
+ static constraints = {
+ position blank: false, inList: POSITIONS
+ sourceVersion nullable: !FeatureToggles.OPT_LOCKING_FEATURE_TOGGLE
+ targetVersion nullable: !FeatureToggles.OPT_LOCKING_FEATURE_TOGGLE
+ }
+}
Index: src/main/groovy/com/lemans/ds/bulk/excel/ExcelBuilderHelper.groovy
===================================================================
diff -u
--- src/main/groovy/com/lemans/ds/bulk/excel/ExcelBuilderHelper.groovy (revision 0)
+++ src/main/groovy/com/lemans/ds/bulk/excel/ExcelBuilderHelper.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,146 @@
+package com.lemans.ds.bulk.excel
+
+import groovy.util.logging.Slf4j
+import org.apache.poi.hssf.usermodel.HSSFCell
+import org.apache.poi.hssf.usermodel.HSSFCellStyle
+import org.apache.poi.hssf.usermodel.HSSFFont
+import org.apache.poi.hssf.usermodel.HSSFHyperlink
+import org.apache.poi.hssf.usermodel.HSSFRow
+import org.apache.poi.hssf.usermodel.HSSFSheet
+import org.apache.poi.hssf.usermodel.HSSFWorkbook
+import org.apache.poi.hssf.util.HSSFColor
+import org.apache.poi.ss.usermodel.Cell
+import org.apache.poi.ss.usermodel.Hyperlink
+
+@Slf4j
+class ExcelBuilderHelper {
+
+ void reportBySheet(List results, List identifier, HSSFWorkbook reportBook, String sheetName, String filePath) {
+ if (sheetName in ['CategoryAttributeValue', 'Part']) {
+ generateExcelReport(results, identifier, reportBook, sheetName, filePath)
+ } else {
+ List dataWithReport = results.findAll { it.report != [] }
+ if (dataWithReport) {
+ generateExcelReport(dataWithReport, identifier, reportBook, sheetName, filePath)
+ }
+ }
+ }
+
+ @SuppressWarnings(['CatchException'])
+ void generateExcelReport(List results, List identifier, HSSFWorkbook reportBook, String sheetName, String filePath) {
+ FileOutputStream fileOutputStream = new FileOutputStream(filePath)
+ HSSFCellStyle defaultStyle = defaultStyle(reportBook)
+ try {
+ HSSFSheet reportSheet = reportBook.createSheet(sheetName)
+ addHeaderRow(identifier, reportSheet, defaultStyle, sheetName)
+ addRows(results, reportBook, reportSheet)
+ reportBook.write(fileOutputStream)
+ fileOutputStream.close()
+ } catch (Exception e) {
+ log.error e.message, e
+ }
+ fileOutputStream.flush()
+ fileOutputStream.close()
+ }
+
+ private void addHeaderRow(List identifier, HSSFSheet reportSheet, HSSFCellStyle defaultStyle, String sheetName) {
+ isReportColumnAdded(sheetName, identifier)
+ HSSFRow reportRow = reportSheet.createRow(0)
+ HSSFCell reportCell
+ identifier.eachWithIndex { def entry, int i ->
+ reportCell = reportRow.createCell(i)
+ reportCell.cellValue = entry.toString()
+ reportCell.cellStyle = defaultStyle
+ }
+ }
+
+ private void isReportColumnAdded(String sheetName, List identifier) {
+ if (!(sheetName in ['CategoryAttributeValue', 'Part'])) {
+ if (!('report' in identifier)) { identifier.add('report') }
+ }
+ }
+
+ private void addRows(List errorData, HSSFWorkbook reportBook, HSSFSheet reportSheet) {
+ HSSFCellStyle rowStyle = rowStyle(reportBook)
+ errorData.eachWithIndex { Map data, int index ->
+ HSSFRow reportRow = reportSheet.createRow(index + 1)
+ data.eachWithIndex { def entry, int idx ->
+ HSSFCell reportCell = reportRow.createCell(idx)
+ reportCell.cellStyle = rowStyle
+ addCellValue(entry.value, reportCell, reportBook)
+ reportSheet.autoSizeColumn(idx)
+ }
+ }
+ }
+
+ private void addCellValue(Cell cell, Cell reportCell, HSSFWorkbook reportBook) {
+ copyCell(cell, reportCell, reportBook)
+ }
+
+ @SuppressWarnings(['UnusedPrivateMethodParameter'])
+ private void addCellValue(cellValue, HSSFCell reportCell, HSSFWorkbook reportBook) {
+ if (cellValue.toString() == '') {
+ reportCell.cellValue = ''
+ } else {
+ reportCell.cellValue = cellValue.toString()
+ }
+ }
+
+ private void copyCell(Cell oldCell, Cell newCell, HSSFWorkbook reportBook) {
+ switch (oldCell.cellType) {
+ case Cell.CELL_TYPE_STRING:
+ newCell.cellValue = oldCell.stringCellValue
+ break
+ case Cell.CELL_TYPE_NUMERIC:
+ newCell.cellValue = oldCell.numericCellValue
+ break
+ case Cell.CELL_TYPE_BLANK:
+ newCell.cellType = Cell.CELL_TYPE_BLANK
+ break
+ case Cell.CELL_TYPE_BOOLEAN:
+ newCell.cellValue = oldCell.booleanCellValue
+ break
+ case Cell.CELL_TYPE_ERROR:
+ newCell.cellErrorValue = oldCell.errorCellValue
+ break
+ case Cell.CELL_TYPE_FORMULA:
+ newCell.cellFormula = oldCell.cellFormula
+ break
+ default:
+ break
+ }
+ if (oldCell.hyperlink) {
+ addHyperLink(oldCell, newCell, reportBook)
+ }
+ }
+
+ private void addHyperLink(Cell value, HSSFCell reportCell, HSSFWorkbook reportBook) {
+ HSSFCellStyle hyperLinkStyle = hyperLinkStyle(reportBook)
+ HSSFHyperlink hyperlink = (HSSFHyperlink) reportBook.creationHelper.createHyperlink(Hyperlink.LINK_URL)
+ hyperlink.address = value.hyperlink.address
+ reportCell.hyperlink = (HSSFHyperlink) hyperlink
+ reportCell.cellStyle = hyperLinkStyle
+ }
+
+ private HSSFCellStyle defaultStyle(HSSFWorkbook reportBook) {
+ HSSFCellStyle defaultStyle = reportBook.createCellStyle()
+ HSSFFont defaultFont = reportBook.createFont()
+ defaultFont.bold = true
+ defaultStyle.font = defaultFont
+ defaultStyle
+ }
+
+ private HSSFCellStyle hyperLinkStyle(HSSFWorkbook reportBook) {
+ HSSFCellStyle style = reportBook.createCellStyle()
+ HSSFFont font = reportBook.createFont()
+ font.color = HSSFColor.BLUE.index
+ style.font = font
+ style
+ }
+
+ private HSSFCellStyle rowStyle(HSSFWorkbook reportBook) {
+ HSSFCellStyle style = reportBook.createCellStyle()
+ style.wrapText = true
+ style
+ }
+}
Index: src/main/groovy/com/lemans/ds/bulk/excel/ExcelParserHelper.groovy
===================================================================
diff -u
--- src/main/groovy/com/lemans/ds/bulk/excel/ExcelParserHelper.groovy (revision 0)
+++ src/main/groovy/com/lemans/ds/bulk/excel/ExcelParserHelper.groovy (revision 3fb6c0e8e7068726c84a18eeb122d345e576b67f)
@@ -0,0 +1,68 @@
+package com.lemans.ds.bulk.excel
+
+import org.apache.commons.io.FilenameUtils
+import org.apache.poi.hssf.usermodel.HSSFWorkbook
+import org.apache.poi.ss.usermodel.Row
+import org.apache.poi.ss.usermodel.Sheet
+import org.apache.poi.ss.usermodel.Workbook
+import org.apache.poi.xssf.usermodel.XSSFWorkbook
+
+class ExcelParserHelper {
+
+ private static final Map IMPORT_TYPE_SHEET_NAME_MAPPING = [PRODUCT: ['Product', 'ProductFeature'],
+ CATEGORYATTRIBUTE: ['CategoryAttributeValue'], PART: ['Part']]
+
+
+ Workbook workBook(String filesPath) {
+ File[] files = new File(filesPath).listFiles()
+ String filename = files.first()
+ FilenameUtils.getExtension(filename).equalsIgnoreCase('xls') ?
+ new HSSFWorkbook(files.first().newInputStream()) : new XSSFWorkbook(files.first().newInputStream())
+ }
+
+ List collectSheetsFromExcel(Workbook inputFile, String importType) {
+ List sheets = []
+ inputFile.sheets.each { Sheet sheet ->
+ if (sheet.sheetName in IMPORT_TYPE_SHEET_NAME_MAPPING[importType.toUpperCase()] && sheet.lastRowNum > 0) {
+ sheets << rowsOfSheet(sheet)
+ }
+ }
+ sheets
+ }
+
+ private Map rowsOfSheet(Sheet sheet) {
+ List rows = collectRows(sheet)
+ List header = discardReportHeaderIfPresent((List) rows.get(0))
+ List