Skip to content

Configuration Defaults

OpenBoxes uses a configuration file grails-app/conf/application.yml (formerly, Config.groovy) to manage application settings. The application.yml file uses a YAML format, which is hierarchical and structured by indentation. The configuration properties within this file include database connections, server settings, environment-specific configurations, and more.

For more general information about how Grails configuration works, refer to the official Grails documentation.

Default Configuration File

To review our default configuration file, refer to the snippet below. We will document the most relevant configuration properties in the documentation in the following sections.

Note

This is the latest development version, so might contain properties that are not yet available in the latest release. If the contents did not load properly, you can access the file directly in our GitHub repository here: application.yml

application.yml
# Copyright (c) 2012 Partners In Health.  All rights reserved.
# The use and distribution terms for this software are covered by the
# Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
# which can be found in the file epl-v10.html at the root of this distribution.
# By using this software in any fashion, you are agreeing to be bound by
# the terms of this license.
# You must not remove this notice, or any other, from this software.

---
grails:
    profile: react-webpack
    codegen:
        defaultPackage: org
    dbconsole:
        enabled: true
    gorm:
        reactor:
            # Whether to translate GORM events into Reactor events
            # Disabled by default for performance reasons
            events: false
        failOnError: false
    config:
        locations:
            #
            # The first config file has the lowest priority. Its contents can
            # be overwritten by any file following it that sets the same config
            # key. The *last* config file has the final word (highest priority).
            #
            - classpath:META-INF/grails.build.info
            - file:${catalina.base}/.grails/openboxes-config.properties
            - file:${catalina.base}/.grails/openboxes-config.groovy
            - file:${catalina.base}/.grails/openboxes.yml
            - ~/.grails/openboxes-config.properties
            - ~/.grails/openboxes-config.groovy
            - ~/.grails/openboxes.yml
    plugin:
        console:
            enabled: true  # https://github.com/sheehan/grails-console#security

        # Grails Liquibase database migration support. https://grails.github.io/grails-database-migration/latest/
        # Automatic migrations are disabled by default. We run them manually in BootStrap.groovy
        databasemigration:
            changelogFileName: changelog.groovy

info:
    app:
        name: '@info.app.name@'
        version: '@info.app.version@'
        grailsVersion: '@info.app.grailsVersion@'

spring:
    main:
        banner-mode: "off"
    groovy:
        template:
            check-template-location: false

quartz:
    pluginEnabled: true
    jdbcStore: false
    autoStartup: false
    monitor:
        layout: custom
        showTriggerNames: true
        showCountdown: true
        showTickingCloud: true

# Spring Actuator Endpoints are Disabled by Default
endpoints:
    enabled: true
    jmx:
        enabled: true

management:
    info:
        git:
            mode: full

server:
    contextPath: "/openboxes"
    session:
        # The time (in seconds) of inactivity before we invalidate the user's session. (7200 is two hours.)
        # For future reference, this setting has been renamed to "server.servlet.session.timeout" in SpringBoot 2+
        timeout: 7200
---
grails:
    #
    # From the asset-pipeline manual: "If you want settings to apply to
    # both development runtime and build time the properties have to be
    # duplicated in your application's application.yml [and build.gradle]."
    #
    # http://www.asset-pipeline.com/manual/index.html#configuration
    # http://www.asset-pipeline.com/manual/index.html#configuration-2
    #
    # Every field except `bundle` should also be set in build.gradle.
    #
    # Because we use webpack to bundle .css and .js files, most of
    # asset-pipeline's advanced features are unnecessary.
    #
    assets:
        bundle: false
        developmentRuntime: false
        enableDigests: false
        enableGzip: false
        enableSourceMaps: false
        maxThreads: 1
        minifyCss: false
        minifyJs: false
        skipNonDigests: false
    cache:
        clearAtStartup: true
    doc:
        title: "OpenBoxes"
        subtitle: ""
        authors: "Justin Miranda"
        license: "Eclipse Public License - Version 1.0"
        copyright: ""
        footer: ""
    mail:
        enabled: false
        from: "info@openboxes.com"
        prefix: "[OpenBoxes]"
        host: "localhost"
        port: "25"

        # Authentication disabled by default
        username: null
        password: null

        # Disable debug mode by default
        debug: false
    mime:
        disable:
            accept:
                header:
                    userAgents:
                        - Gecko
                        - WebKit
                        - Presto
                        - Trident
        types:
            all: '*/*'
            atom: application/atom+xml
            css: text/css
            csv: text/csv
            form: application/x-www-form-urlencoded
            html:
              - text/html
              - application/xhtml+xml
            js: text/javascript
            json:
              - application/json
              - text/json
            multipartForm: multipart/form-data
            pdf: application/pdf
            rss: application/rss+xml
            text: text/plain
            hal:
              - application/hal+json
              - application/hal+xml
            xml:
              - text/xml
              - application/xml
        file:
            extensions: true
        use:
            accept:
                header: false
    resources:
        #
        # Set Cache-Control on non-asset-pipeline-managed static assets.
        # See https://gsp.grails.org/latest/guide/resources.html
        #
        cachePeriod: 86400
    urlmapping:
        cache:
            maxsize: 1000
    controllers:
        upload:
            maxFileSize: 2097152
            maxRequestSize: 2097152
        defaultScope: singleton
    converters:
        encoding: UTF-8
    views:
        default:
            codec: 'html'
        enable:
            jsessionid: false
        gsp:
            encoding: UTF-8
            htmlcodec: xml
            codecs:
                expression: html
                scriptlets: html
                taglib: none
                staticparts: none
            sitemesh:
                preprocess: true
        javascript:
            library: jquery
    databinding:
        # The formats that Grails will attempt to use when binding input Strings to java.util.Date fields.
        # We now use the java.time classes instead (ex: Instant, LocalDate). Those fields define their own
        # ValueConverter classes and so don't use the formats defined here.
        dateFormats:
            # Old date formats, don't use going forward! Kept first in the list to maintain backwards compatability.
            - 'MM/dd/yyyy HH:mm:ss XXX'
            - 'MM/dd/yyyy HH:mm:ss XX'
            - 'MM/dd/yyyy HH:mm:ss X'
            - 'MM/dd/yyyy HH:mm XXX'
            - 'MM/dd/yyyy HH:mm XX'
            - 'MM/dd/yyyy HH:mm X'
            # Note that if this format is given, Grails will treat it as midnight in the local server timezone, which
            # is ahead of UTC, can cause the date to actually be the next day. It can also cause inconsistencies across
            # hosts/environments if they're configured for different local timezones. Avoid this format going forward!
            - 'MM/dd/yyyy'
            # ISO-8601 formats. These are better than the old formats, but new APIs should still use Instant instead
            # of Date whenever possible. Note that a format without time and timezone is NOT allowed here because that
            # would be ambiguous and would lead to Date conversion weirdness. If you need a date only, use LocalDate.
            - "yyyy-MM-dd'T'HH:mm:ssXXX"
            - "yyyy-MM-dd'T'HH:mm:ssXX"
            - "yyyy-MM-dd'T'HH:mm:ssX"
            - "yyyy-MM-dd'T'HH:mmXXX"
            - "yyyy-MM-dd'T'HH:mmXX"
            - "yyyy-MM-dd'T'HH:mmX"
        convertEmptyStringsToNull: false
endpoints:
    jmx:
        unique-names: true

org.eclipse.jetty.util.log.class: org.eclipse.jetty.util.log.Slf4jLog
org.jboss.logging.provider: slf4j
server.tomcat.accesslog.enabled: true
slf4j.detectLoggerNameMismatch: true

---
hibernate:
    show_sql: false  # Logs Hibernate queries to the console. We configure this via Logback instead.
    format_sql: false
    allow_update_outside_transaction: false
    cache:
        queries: false
        provider_class: org.hibernate.cache.EhCacheProvider
        region:
            factory_class: org.hibernate.cache.ehcache.EhCacheRegionFactory
        use_query_cache: false
        use_second_level_cache: false

dataSource:
    pooled: true
    jmxExport: true
    driverClassName: com.mysql.cj.jdbc.Driver
    dbCreate: none
    dialect: org.hibernate.dialect.MySQL57InnoDBDialect
    factory: org.apache.tomcat.jdbc.pool.DataSourceFactory
    type: org.apache.tomcat.jdbc.pool.DataSource

    # Settings for database logging
    logger: com.mysql.cj.jdbc.log.StandardLogger
    dumpQueriesOnException: false
    logSql: false  # Logs SQL queries to the console. We configure this via Logback instead.
    formatSql: false
    logSlowQueries: false
    includeInnodbStatusInDeadlockExceptions: true

    properties:
        # from https://docs.grails.org/3.3.16/guide/conf.html#dataSource
        jmxEnabled: true
        initialSize: 5
        maxActive: 50
        minIdle: 5
        maxIdle: 25
        maxWait: 10000
        maxAge: 10 * 60000
        timeBetweenEvictionRunsMillis: 5000
        minEvictableIdleTimeMillis: 60000
        validationQuery: SELECT 1
        validationQueryTimeout: 3
        validationInterval: 15000
        testOnBorrow: false
        testWhileIdle: true
        testOnReturn: false
        # https://tomcat.apache.org/tomcat-8.5-doc/jdbc-pool.html#JDBC_interceptors
        jdbcInterceptors: "ConnectionState;StatementCache(max=200)"
        defaultTransactionIsolation: java.sql.Connection.TRANSACTION_READ_COMMITTED

environments:
    development:
        dataSource:
            dumpQueriesOnException: true
            logAbandoned: true
            logSlowQueries: true
            username: "openboxes"
            password: "openboxes"
            url: jdbc:mysql://localhost:3306/openboxes?serverTimezone=UTC&useSSL=false
    test:
        dataSource:
            dumpQueriesOnException: true
            logAbandoned: true
            # Our integration tests run against a containerized database: https://java.testcontainers.org/modules/databases/jdbc/.
            # Note that testcontainers auto-disable SSL when connecting via jdbc: https://github.com/testcontainers/testcontainers-java/pull/561
            # For more information, see /src/integration-test/README.md
            url: jdbc:tc:${TEST_DATABASE:mysql:5.7.44}:///openboxes?TC_MY_CNF=testcontainers
            driverClassName: org.testcontainers.jdbc.ContainerDatabaseDriver
        grails:
            plugin:
                databasemigration:
                    changelogFileName: changelog-test.xml
                    changelogLocation: src/integration-test/resources/migrations
    production:
        dataSource:
            username: "openboxes"
            password: "openboxes"
            url: jdbc:mysql://localhost:3306/openboxes?serverTimezone=UTC&useSSL=false
---
openboxes:
    ajaxRequest:
        timeout: 120000

    dashboard:
        yearTypes:
            fiscalYear:
                start: "07/01" # format: MM/DD, For PIH and the Govt of Dominica fiscal year start July 1
                end: "06/30" # format: MM/DD
                labelYearPrefix: "FY "
                yearFormat: "yy"
            calendarYear:
                start: "01/01"
                end: "12/31"
                labelYearPrefix: ""
                yearFormat: "yyyy"

    order:
        orderStatusPropertyMap:
            PLACED: ["productCode", "sourceName", "supplierCode", "manufacturer", "manufacturerCode", "quantity", "unitPrice", "unitOfMeasure", "budgetCode"]

    purchaseOrder:
        editableProperties:
            status: "PLACED"
            deny: ["productCode", "sourceName", "supplierCode", "manufacturer", "manufacturerCode", "quantity", "unitOfMeasure"]


    # OpenBoxes default line printer port
    linePrinterTerminal:
        port: "LPT1"

    # OpenBoxes default uploads directory location
    uploads:
        location: "uploads"


    # Fullstory integration
    fullstory:
        enabled: false
        debug: false
        host: "fullstory.com"
        org: ""
        namespace: "FS"

    # Hotjar integration
    hotjar:
        enabled: false
        hjid: 0
        hjsv: 6

    # Forecasting feature
    forecasting:
        enabled: true
        demandPeriod: 365

    # Bill of Materials feature
    bom:
        enabled: false

    # User signup
    signup:
        enabled: true
        recaptcha:
            enabled: false
            v2:
                siteKey: ""
                secretKey: ""
        additionalQuestions:
            enabled: false

    # UserVoice widget
    uservoice:
        widget:
            enabled: false
            position: "right"

    # Zopim widget
    zopim:
        widget:
            enabled: false
            url: "//v2.zopim.com/?2T7RMi7ERqr3s8N20KQ3wOBRudcwosBA"

    # HelpScout beacon
    helpscout:
        widget:
            color: "#3AB4B1"
            enabled: true
            key: "44ee4f01-5334-4b93-ad25-03037903eb80"

    # JIRA Issue Collector
    jira:
        issue:
            collector:
                enabled: false
                url: "https://openboxes.atlassian.net/s/d41d8cd98f00b204e9800998ecf8427e/en_USgc5zl3-1988229788/6318/12/1.4.10/_/download/batch/com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:issuecollector/com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:issuecollector.js?collectorId=fb813fdb"

    # OpenBoxes Feedback
    mail:
        feedback:
            enabled: false
            recipients: ["feedback@openboxes.com"]
        # OpenBoxes Error Emails (bug reports)
        errors:
            enabled: true
            recipients: ["errors@openboxes.com"]

    # Barcode scanner (disabled by default)
    scannerDetection:
        enabled: false

    barcode:
        printer:
            # Print barcode labels via USB
            name: "printer-thermalprinter"
            #  Print barcode labels via RAW
            ipAddress: "127.0.0.1"
            port: 9100

    # Default delay and min length for typeahead components
    typeahead:
        delay: 300
        minLength: 3

    refreshAnalyticsDataOnStartup:
        enabled: true

    jobs:
        # Send stock alerts
        sendStockAlertsJob:
            enabled: true
            skipOnEmpty: true
            daysUntilExpiry: 60
            cronExpression: "0 0 0 * * ?" # every day at midnight

        # Refresh inventory snapshots (triggered manually)
        refreshInventorySnapshotJob:
            enabled: true
            retryOnError: false
            maxRetryAttempts: 3

        # Refresh inventory snapshots after transaction (only for transaction entries)
        refreshInventorySnapshotAfterTransactionJob:
            enabled: true
            retryOnError: false
            maxRetryAttempts: 3

        # Refresh order summary snapshots
        refreshOrderSummaryJob:
            enabled: true
            cronExpression: "0 0 2 * * ?" # at 02:00:00am every day

        # Refresh product availability materialized view
        refreshProductAvailabilityJob:
            enabled: true
            cronExpression: "0 0 0/2 * * ?" # every two hours
            delayStart: true
            delayInMilliseconds: 5000

        # Refresh transaction fact table
        refreshTransactionFactJob:
            enabled: true
            cronExpression: "0 0 0 * * ?" # every day at midnight

        # Refresh stockout data for yesterday
        refreshStockoutDataJob:
            enabled: true
            cronExpression: "0 0 1 * * ?" # at 01:00:00am every day

        # Refresh demand fact table
        refreshDemandDataJob:
            enabled: true
            cronExpression: "0 0 1 * * ?" # at 01:00:00am every day

        # Assign identifier job
        assignIdentifierJob:
            enabled: true
            cronExpression: "0 * * * * ?" # every minute

        # Calculate historical quantity on hand
        calculateHistoricalQuantityJob:
            enabled: false
            cronExpression: "0 0 0 * * ?" # every day at midnight
            daysToProcess: 540 # 18 months

        # Data Cleaning Job
        dataCleaningJob:
            enabled: true
            cronExpression: "0 */5 * * * ?"       # every five minutes

        # Data Migration Job (enabled, but needs to be triggered manually)
        dataMigrationJob:
            enabled: true

        updateExchangeRatesJob:
            enabled: false
            cronExpression: "0 0 * * * ?" # every hour

    # LDAP configuration
    ldap:
        enabled: false
        context:
            managerDn: "cn=read-only-admin,dc=example,dc=com"
            managerPassword: "password"
            server:
                host: "ldap.forumsys.com"
                port: 389

        # LDAP Search
        search:
            base: "dc=example,dc=com"
            filter: "(uid={0})"
            searchSubtree: true
            attributesToReturn: ['mail', 'givenName']

    # Stock Card > Consumption > Reason codes
    # Examples: Stock out, Low stock, Expired, Damaged, Could not locate, Insufficient quantity reconditioned
    stockCard:
        consumption:
            stockout: &stockout 1
            low_stock: &low_stock 2
            expired: &expired 3
            damaged: &damaged 4
            could_not_locate: &could_not_locate 14
            insufficient_quantity_reconditioned: &insufficient_quantity_reconditioned 19
            reasonCodes: [*stockout, *low_stock, *expired, *damaged, *could_not_locate, *insufficient_quantity_reconditioned]

    # Localization configuration - default and supported locales
    locale:
        custom:
            enabled: false
        defaultLocale: 'en'
        localizationModeLocale: 'ach'
        supportedLocales: ['ar', 'ach', 'de', 'en', 'es', 'es_MX', 'fr', 'ht', 'it', 'pt', 'fi', 'zh']
        # Currency configuration
        defaultCurrencyCode: "USD"
        defaultCurrencySymbol: '\$'
        supportedCurrencyCodes: ["USD", "CAD", "EUR", "GBP"]
        currencyApi:
            url: "https://api.exchangeratesapi.io/latest?base=%s"
            apiKey: ""
            password: ""
        translationApi:
            uri: "https://translate.yandex.net/api/v1.5/tr.json/translate?key=%s&text=%s&lang=%s&format=%s"
            apiKey: ""
            format: "plain"

    # Accounting (Budget Code, GL Account)
    accounting:
        enabled: true

    # Inventory snapshot configuration
    inventorySnapshot:
        batchSize: 100

    # Allow users to customize logo image url as well as label
    logo:
        url: "/assets/openboxes_logo_40x40.jpg"
        label: ""
    report:
        logo:
            url: "https://openboxes.com/img/logo_100.png"

    # Allow system to anonymize user data to prevent it from being accessed by unauthorized users
    anonymize:
        enabled: false

    generateName:
        separator: " - "

    # Disable feature during development
    shipping:
        search:
            maxResults: 1000

    # Automatically create temporary receiving locations for shipments
    receiving:
        createReceivingLocation:
            enabled: true
        receivingLocation:
            prefix: "R"

    # Indicates which activities are required for a location to allow logins
    chooseLocation:
        requiredActivities: ["MANAGE_INVENTORY"]

    expirationDate:
        minValue: "01/01/2000"
    cycleCount:
        additionalColumns: {}

    api:
        pagination:
            enabled: true
            pageSize: 10

mail:
    error:
        enabled: false
        debug: false
        to: 'errors@openboxes.com'
        server: "localhost"
        port: "25"
        from: "info@openboxes.com"
        username: null
        password: null
        prefix: "[OpenBoxes]"

uiperformance:
    enabled: false

# TODO OBGM 1: Compare the loggers in log4j = { } to make sure they exist in logback.groovy
# TODO OBGM 2: Add grails-cache plugin config if will be needed (See OBGM-71)
# TODO OBGM 3: Investigate if Joda-Time plugin is still needed
# TODO OBGM 4: Add resources plugin config if will be needed

grails:
    # request parameters to mask when logging exceptions
    exceptionresolver:
        params:
            exclude:
                - 'password'
                - 'passwordConfirm'
    # enabled native2ascii conversion of i18n properties files
    enable:
        native2ascii: true
    # whether to install the java.util.logging bridge for sl4j. Disable fo AppEngine!
    logging:
        jul:
            usebridge: true

sentry:
    # TODO: For some reason adding the "sentry-spring-boot-starter" dependency causes:
    #       - java.lang.IllegalStateException: Error processing condition on io.sentry.spring.boot.SentryAutoConfiguration$HubConfiguration$SentryWebMvcConfiguration.transactionNameProvider
    #       - Caused by: java.lang.reflect.MalformedParameterizedTypeException: null
    #       https://github.com/getsentry/sentry-java/blob/6.34.0/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java#L268-L272
    #       Find a way to bypass this auto-configuration or provide a default implementation of transactionNameProvider.
    #       Possibly related to: https://github.com/getsentry/sentry-java/issues/1863
    #       Until this is resolved, all sentry related properties must be defined in sentry.properties.
    dsn: ${SENTRY_DSN_BACKEND:${SENTRY_DSN:""}}

    # A custom setting that defines the format of Sentry HTTP transaction names
    httpTransactionNameFormat: CONTROLLER_METHOD

# Application-level logger config. See logback.xml for Logback-specific configuration.
logging:
    # How the logs are formatted. See https://logback.qos.ch/manual/layouts.html for config options.
    pattern:
        console: "%date{ISO8601} %-5level [%thread] %logger{40}: %message%n%xException"
    level:
        root: INFO