[MIG] sql_request_abstract from 15.0 to 16.0
- Add new main entry menu for SQL configuration - Add abstract tree view - Add abstract form view - ADD ace/mode-pgsql.js file to have syntaxic coloration in sql field - move has_grouped_changed from bi_sql_editor to sql_request_abstract - remove main menu entries, replaced by new spreadsheet menu entries in V16 - update translation - IMP : inherit from mail.thread to use chatter - ADD : new note fields - REF : add comment on has_grouped_changed fieldpull/669/head
parent
ba325a5e05
commit
8dfc1a65c4
|
@ -1,3 +1,2 @@
|
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
|
|
|
@ -4,18 +4,19 @@
|
|||
|
||||
{
|
||||
"name": "SQL Request Abstract",
|
||||
"version": "15.0.1.0.0",
|
||||
"version": "16.0.1.0.0",
|
||||
"author": "GRAP,Akretion,Odoo Community Association (OCA)",
|
||||
"maintainers": ["legalsylvain"],
|
||||
"website": "https://github.com/OCA/reporting-engine",
|
||||
"license": "AGPL-3",
|
||||
"category": "Tools",
|
||||
"summary": "Abstract Model to manage SQL Requests",
|
||||
"depends": ["base"],
|
||||
"depends": ["mail"],
|
||||
"data": [
|
||||
"security/ir_module_category.xml",
|
||||
"security/res_groups.xml",
|
||||
"security/ir.model.access.csv",
|
||||
"views/view_sql_request_mixin.xml",
|
||||
],
|
||||
"installable": True,
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
from . import main
|
|
@ -0,0 +1,29 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import werkzeug
|
||||
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
from odoo.tools.misc import file_open
|
||||
|
||||
from odoo.addons.web.controllers.webclient import WebClient
|
||||
|
||||
|
||||
class SqlRequestAbstractWebClient(WebClient):
|
||||
|
||||
# if a field, widget="ace" option="{'mode': 'xxx'}"
|
||||
# is present, The ace lib (odoo/addons/web/static/lib/ace)
|
||||
# will generate a call to /web/static/lib/ace/mode-xxx.js
|
||||
# to load the javascript syntax file.
|
||||
# We catch this call and redirect on the correct path
|
||||
@http.route("/web/static/lib/ace/mode-pgsql.js", type="http", auth="none")
|
||||
def call_mode_pgsql_file(self):
|
||||
return http.Response(
|
||||
werkzeug.wsgi.wrap_file(
|
||||
request.httprequest.environ,
|
||||
file_open("sql_request_abstract/static/lib/ace/mode-pgsql.js", "rb"),
|
||||
),
|
||||
content_type="application/javascript; charset=utf-8",
|
||||
headers=[("Cache-Control", f"max-age={http.STATIC_CACHE}")],
|
||||
direct_passthrough=True,
|
||||
)
|
|
@ -1,48 +1,41 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sql_request_abstract
|
||||
# * sql_request_abstract
|
||||
#
|
||||
# Translators:
|
||||
# OCA Transbot <transbot@odoo-community.org>, 2017
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 10.0\n"
|
||||
"Project-Id-Version: Odoo Server 16.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-12-01 02:11+0000\n"
|
||||
"PO-Revision-Date: 2021-05-14 19:47+0000\n"
|
||||
"Last-Translator: Yves Le Doeuff <yld@alliasys.fr>\n"
|
||||
"Language-Team: French (https://www.transifex.com/oca/teams/23907/fr/)\n"
|
||||
"Language: fr\n"
|
||||
"POT-Creation-Date: 2022-10-26 11:40+0000\n"
|
||||
"PO-Revision-Date: 2022-10-26 11:40+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=n > 1;\n"
|
||||
"X-Generator: Weblate 4.3.2\n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields,field_description:sql_request_abstract.field_sql_request_mixin__group_ids
|
||||
#: model_terms:ir.ui.view,arch_db:sql_request_abstract.view_sql_request_mixin_form
|
||||
msgid "Allowed Groups"
|
||||
msgstr "Groupes autorisés"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields,field_description:sql_request_abstract.field_sql_request_mixin__user_ids
|
||||
#: model_terms:ir.ui.view,arch_db:sql_request_abstract.view_sql_request_mixin_form
|
||||
msgid "Allowed Users"
|
||||
msgstr "Utilisateurs autorisés"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields,field_description:sql_request_abstract.field_sql_request_mixin__display_name
|
||||
msgid "Display Name"
|
||||
msgstr "Nom affiché"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields.selection,name:sql_request_abstract.selection__sql_request_mixin__state__draft
|
||||
msgid "Draft"
|
||||
msgstr "Brouillon"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields,field_description:sql_request_abstract.field_sql_request_mixin__id
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
#: model:ir.model.fields,field_description:sql_request_abstract.field_sql_request_mixin__has_group_changed
|
||||
msgid "Has Group Changed"
|
||||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: code:addons/sql_request_abstract/models/sql_request_mixin.py:0
|
||||
|
@ -50,15 +43,10 @@ msgstr "ID"
|
|||
msgid "It is not allowed to execute a not checked request."
|
||||
msgstr "Il n'est pas permis d'exécuter une demande non vérifiée."
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields,field_description:sql_request_abstract.field_sql_request_mixin____last_update
|
||||
msgid "Last Modified on"
|
||||
msgstr "Dernière modification le"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:res.groups,name:sql_request_abstract.group_sql_request_manager
|
||||
msgid "Manager"
|
||||
msgstr ""
|
||||
msgstr "Responsable"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: code:addons/sql_request_abstract/models/sql_request_mixin.py:0
|
||||
|
@ -67,19 +55,29 @@ msgid ""
|
|||
"Materialized View requires PostgreSQL 9.3 or greater but PostgreSQL %s is "
|
||||
"currently installed."
|
||||
msgstr ""
|
||||
"La vue matérialisée nécessite PostgreSQL 9.3 ou supérieur mais PostgreSQL %s "
|
||||
"est actuellement installé."
|
||||
"La vue matérialisée nécessite PostgreSQL 9.3 ou supérieur mais PostgreSQL %s"
|
||||
" est actuellement installé."
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields,field_description:sql_request_abstract.field_sql_request_mixin__name
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model_terms:ir.ui.view,arch_db:sql_request_abstract.view_sql_request_mixin_form
|
||||
msgid "Preview Results"
|
||||
msgstr "Prévisualiser les résultats"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields,field_description:sql_request_abstract.field_sql_request_mixin__query
|
||||
msgid "Query"
|
||||
msgstr "Requête"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model_terms:ir.ui.view,arch_db:sql_request_abstract.view_sql_request_mixin_form
|
||||
msgid "SQL Query"
|
||||
msgstr "Requête SQL"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.module.category,name:sql_request_abstract.category_sql_abstract
|
||||
msgid "SQL Request"
|
||||
|
@ -88,7 +86,12 @@ msgstr "Requête SQL"
|
|||
#. module: sql_request_abstract
|
||||
#: model:ir.model,name:sql_request_abstract.model_sql_request_mixin
|
||||
msgid "SQL Request Mixin"
|
||||
msgstr ""
|
||||
msgstr "Modèle abstrait de requête SQL"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model_terms:ir.ui.view,arch_db:sql_request_abstract.view_sql_request_mixin_form
|
||||
msgid "SQL Settings"
|
||||
msgstr "Configuration SQL"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields.selection,name:sql_request_abstract.selection__sql_request_mixin__state__sql_valid
|
||||
|
@ -96,9 +99,14 @@ msgid "SQL Valid"
|
|||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields,field_description:sql_request_abstract.field_sql_request_mixin__smart_search
|
||||
msgid "Smart Search"
|
||||
msgstr "Recherche intelligente"
|
||||
#: model_terms:ir.ui.view,arch_db:sql_request_abstract.view_sql_request_mixin_form
|
||||
msgid "Security"
|
||||
msgstr "Sécurité"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model_terms:ir.ui.view,arch_db:sql_request_abstract.view_sql_request_mixin_form
|
||||
msgid "Set to Draft"
|
||||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields,field_description:sql_request_abstract.field_sql_request_mixin__state
|
||||
|
@ -129,7 +137,8 @@ msgstr ""
|
|||
#: code:addons/sql_request_abstract/models/sql_request_mixin.py:0
|
||||
#, python-format
|
||||
msgid "The query is not allowed because it contains unsafe word '%s'"
|
||||
msgstr "La requête n'est pas autorisée car elle contient le mot dangereux '%s'"
|
||||
msgstr ""
|
||||
"La requête n'est pas autorisée car elle contient le mot dangereux '%s'"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: code:addons/sql_request_abstract/models/sql_request_mixin.py:0
|
||||
|
@ -142,11 +151,16 @@ msgstr "Mode non implémenté : '%s'"
|
|||
msgid "User"
|
||||
msgstr "Utilisateur"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model_terms:ir.ui.view,arch_db:sql_request_abstract.view_sql_request_mixin_form
|
||||
msgid "Validate SQL Expression"
|
||||
msgstr "Valider l'expression SQL"
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields,help:sql_request_abstract.field_sql_request_mixin__query
|
||||
msgid ""
|
||||
"You can't use the following words: DELETE, DROP, CREATE, INSERT, ALTER, "
|
||||
"TRUNCATE, EXECUTE, UPDATE."
|
||||
msgstr ""
|
||||
"Vous ne pouvez pas utiliser les mots suivants: DELETE, DROP, CREATE, INSERT, "
|
||||
"ALTER, TRUNCATE, EXECUTE, UPDATE."
|
||||
"Vous ne pouvez pas utiliser les mots suivants: DELETE, DROP, CREATE, INSERT,"
|
||||
" ALTER, TRUNCATE, EXECUTE, UPDATE."
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * sql_request_abstract
|
||||
# * sql_request_abstract
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 15.0\n"
|
||||
"Project-Id-Version: Odoo Server 16.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-10-26 11:39+0000\n"
|
||||
"PO-Revision-Date: 2022-10-26 11:39+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
|
@ -15,11 +17,13 @@ msgstr ""
|
|||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields,field_description:sql_request_abstract.field_sql_request_mixin__group_ids
|
||||
#: model_terms:ir.ui.view,arch_db:sql_request_abstract.view_sql_request_mixin_form
|
||||
msgid "Allowed Groups"
|
||||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields,field_description:sql_request_abstract.field_sql_request_mixin__user_ids
|
||||
#: model_terms:ir.ui.view,arch_db:sql_request_abstract.view_sql_request_mixin_form
|
||||
msgid "Allowed Users"
|
||||
msgstr ""
|
||||
|
||||
|
@ -28,6 +32,11 @@ msgstr ""
|
|||
msgid "Draft"
|
||||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields,field_description:sql_request_abstract.field_sql_request_mixin__has_group_changed
|
||||
msgid "Has Group Changed"
|
||||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: code:addons/sql_request_abstract/models/sql_request_mixin.py:0
|
||||
#, python-format
|
||||
|
@ -52,11 +61,21 @@ msgstr ""
|
|||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model_terms:ir.ui.view,arch_db:sql_request_abstract.view_sql_request_mixin_form
|
||||
msgid "Preview Results"
|
||||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields,field_description:sql_request_abstract.field_sql_request_mixin__query
|
||||
msgid "Query"
|
||||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model_terms:ir.ui.view,arch_db:sql_request_abstract.view_sql_request_mixin_form
|
||||
msgid "SQL Query"
|
||||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.module.category,name:sql_request_abstract.category_sql_abstract
|
||||
msgid "SQL Request"
|
||||
|
@ -67,11 +86,26 @@ msgstr ""
|
|||
msgid "SQL Request Mixin"
|
||||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model_terms:ir.ui.view,arch_db:sql_request_abstract.view_sql_request_mixin_form
|
||||
msgid "SQL Settings"
|
||||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields.selection,name:sql_request_abstract.selection__sql_request_mixin__state__sql_valid
|
||||
msgid "SQL Valid"
|
||||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model_terms:ir.ui.view,arch_db:sql_request_abstract.view_sql_request_mixin_form
|
||||
msgid "Security"
|
||||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model_terms:ir.ui.view,arch_db:sql_request_abstract.view_sql_request_mixin_form
|
||||
msgid "Set to Draft"
|
||||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields,field_description:sql_request_abstract.field_sql_request_mixin__state
|
||||
msgid "State"
|
||||
|
@ -111,9 +145,14 @@ msgstr ""
|
|||
msgid "User"
|
||||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model_terms:ir.ui.view,arch_db:sql_request_abstract.view_sql_request_mixin_form
|
||||
msgid "Validate SQL Expression"
|
||||
msgstr ""
|
||||
|
||||
#. module: sql_request_abstract
|
||||
#: model:ir.model.fields,help:sql_request_abstract.field_sql_request_mixin__query
|
||||
msgid ""
|
||||
"You can't use the following words: DELETE, DROP, CREATE, INSERT, ALTER, "
|
||||
"TRUNCATE, EXECUTE, UPDATE."
|
||||
msgstr ""
|
||||
msgstr ""
|
|
@ -20,6 +20,8 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
class SQLRequestMixin(models.AbstractModel):
|
||||
_name = "sql.request.mixin"
|
||||
_inherit = ["mail.thread"]
|
||||
|
||||
_description = "SQL Request Mixin"
|
||||
|
||||
_clean_query_enabled = True
|
||||
|
@ -61,6 +63,8 @@ class SQLRequestMixin(models.AbstractModel):
|
|||
# Columns Section
|
||||
name = fields.Char(required=True)
|
||||
|
||||
note = fields.Html()
|
||||
|
||||
query = fields.Text(
|
||||
required=True,
|
||||
help="You can't use the following words"
|
||||
|
@ -93,6 +97,19 @@ class SQLRequestMixin(models.AbstractModel):
|
|||
default=_default_user_ids,
|
||||
)
|
||||
|
||||
has_group_changed = fields.Boolean(
|
||||
copy=False,
|
||||
help="Technical fields, used in modules"
|
||||
" that depends on this one to know"
|
||||
" if groups has changed, and that according"
|
||||
" access should be updated.",
|
||||
)
|
||||
|
||||
@api.onchange("group_ids")
|
||||
def onchange_group_ids(self):
|
||||
if self.state not in ("draft", "sql_valid"):
|
||||
self.has_group_changed = True
|
||||
|
||||
# Action Section
|
||||
def button_validate_sql_expression(self):
|
||||
for item in self:
|
||||
|
@ -105,7 +122,12 @@ class SQLRequestMixin(models.AbstractModel):
|
|||
item.state = "sql_valid"
|
||||
|
||||
def button_set_draft(self):
|
||||
self.write({"state": "draft"})
|
||||
self.write(
|
||||
{
|
||||
"has_group_changed": False,
|
||||
"state": "draft",
|
||||
}
|
||||
)
|
||||
|
||||
# API Section
|
||||
def _execute_sql_request(
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
This module add the 'pgsql' mode syntax for the ace widget.
|
||||
(the ace widget is used in odoo web module, but only with
|
||||
the xml and python mode).
|
||||
|
||||
The file is a copy of the file present here
|
||||
(https://github.com/ajaxorg/ace-builds/blob/v1.12.3/src/mode-pgsql.js
|
||||
(Release 18 Oct 2022)
|
|
@ -1,5 +1,7 @@
|
|||
Inherit the model:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from odoo import models
|
||||
|
||||
class MyModel(models.model)
|
||||
|
@ -9,3 +11,6 @@ Inherit the model:
|
|||
_sql_request_groups_relation = 'my_model_groups_rel'
|
||||
|
||||
_sql_request_users_relation = 'my_model_users_rel'
|
||||
|
||||
|
||||
See implementations in the modules ``bi_sql_editor`` and ``sql_export``. (same OCA/reporting-engine repository)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,108 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!--
|
||||
Copyright (C) 2022 - Today: GRAP (http://www.grap.coop)
|
||||
@author Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<record id="view_sql_request_mixin_tree" model="ir.ui.view">
|
||||
<field name="model">sql.request.mixin</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree
|
||||
decoration-info="state=='draft'"
|
||||
decoration-warning="state in ('sql_valid', 'model_valid')"
|
||||
>
|
||||
<field name="name" />
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_sql_request_mixin_form" model="ir.ui.view">
|
||||
<field name="model">sql.request.mixin</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header>
|
||||
<button
|
||||
name="button_validate_sql_expression"
|
||||
type="object"
|
||||
states="draft"
|
||||
string="Validate SQL Expression"
|
||||
class="oe_highlight"
|
||||
groups="sql_request_abstract.group_sql_request_manager"
|
||||
/>
|
||||
<button
|
||||
name="button_set_draft"
|
||||
type="object"
|
||||
states="sql_valid"
|
||||
string="Set to Draft"
|
||||
groups="sql_request_abstract.group_sql_request_manager"
|
||||
/>
|
||||
<button
|
||||
name="button_preview_sql_expression"
|
||||
type="object"
|
||||
states="draft"
|
||||
string="Preview Results"
|
||||
groups="sql_request_abstract.group_sql_request_manager"
|
||||
/>
|
||||
<field name="state" widget="statusbar" />
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box" />
|
||||
<div class="oe_title">
|
||||
<label for="name" />
|
||||
<h1>
|
||||
<div>
|
||||
<field name="name" />
|
||||
</div>
|
||||
</h1>
|
||||
</div>
|
||||
<group name="group_main_info" />
|
||||
<notebook>
|
||||
<page name="page_sql" string="SQL Settings">
|
||||
<group string="SQL Query" name="group_query">
|
||||
<field
|
||||
name="query"
|
||||
nolabel="1"
|
||||
colspan="2"
|
||||
attrs="{'readonly': [('state', '!=', 'draft')]}"
|
||||
widget="ace"
|
||||
options="{'mode': 'pgsql'}"
|
||||
/>
|
||||
</group>
|
||||
</page>
|
||||
<page name="page_security" string="Security">
|
||||
<group string="Allowed Groups" name="group_allowed_groups">
|
||||
<field
|
||||
name="group_ids"
|
||||
nolabel="1"
|
||||
colspan="2"
|
||||
widget="many2many_tags"
|
||||
/>
|
||||
<field name="has_group_changed" invisible="1" />
|
||||
</group>
|
||||
<group string="Allowed Users" name="group_allowed_users">
|
||||
<field
|
||||
name="user_ids"
|
||||
nolabel="1"
|
||||
colspan="2"
|
||||
widget="many2many_tags"
|
||||
/>
|
||||
</group>
|
||||
</page>
|
||||
<page name="page_note" string="Note">
|
||||
<field name="note" nolabel="1" />
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" />
|
||||
<field name="message_ids" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
Loading…
Reference in New Issue