Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions addons/project/migrations/8.0.1.1/noupdate_changes.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version='1.0' encoding='utf-8'?>
<openerp>
<data>
<record id="project_tt_merge" model="project.task.type"><field name="state"/><field name="sequence">14</field>
</record>
<record id="project_tt_cancel" model="project.task.type"><field name="state"/><field name="sequence">30</field>
</record>
<record id="task_visibility_rule" model="ir.rule"><field name="domain_force">['|',
('project_id.privacy_visibility', 'in', ['public', 'employees']),
'&amp;',
('project_id.privacy_visibility', '=', 'followers'),
('message_follower_ids', 'in', [user.partner_id.id]),
]</field>
<field name="name">Project/Task: employees: public or employee or (followers and following)</field>
</record>
<record id="project_tt_specification" model="project.task.type"><field name="state"/><field name="sequence">10</field>
</record>
<record id="project_tt_analysis" model="project.task.type"><field name="case_default" eval="True"/>
<field name="state"/></record>
<record id="all_projects_account" model="account.analytic.account"><field name="code">PP001</field>
</record>
<record id="project_tt_testing" model="project.task.type"><field name="state"/><field name="sequence">13</field>
</record>
<record id="project_tt_design" model="project.task.type"><field name="state"/><field name="sequence">11</field>
</record>
<record id="project_tt_development" model="project.task.type"><field name="state"/><field name="sequence">12</field>
</record>
<record id="project_tt_deployment" model="project.task.type"><field name="state"/><field name="sequence">20</field>
</record>
</data>
</openerp>

76 changes: 76 additions & 0 deletions addons/project/migrations/8.0.1.1/openupgrade_analysis_work.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---Fields in module 'project'---
# NTD cosmetic change
project / project.project / doc_count (int) : type is now 'integer' ('int')

# Apply set_message_last_post
project / project.project / message_last_post (datetime) : NEW
project / project.task / message_last_post (datetime) : NEW

# NTD Reverse relation of existing many2one on task model
project / project.project / task_ids (one2many) : NEW relation: project.task
# NTD Tracking a write on the task stage_id. No meaningful value to apply during migration.
project / project.task / date_last_stage_update (datetime): NEW

# Adapt to new semantics and range
project / project.task / priority (selection) : selection_keys is now '['0', '1', '2']' ('['0', '1', '2', '3', '4']')

# NTD Existing table column now referred in the model
project / project.task / write_date (datetime) : NEW

# NTD State is no longer available on task, therefore neither in the history.
2project / project.task.history / state (selection) : DEL selection_keys: ['cancelled', 'done', 'draft', 'open', 'pending']
project / project.task.history.cumulative / state (selection) : DEL selection_keys: ['cancelled', 'done', 'draft', 'open', 'pending']
project / project.task.type / state (selection) : DEL required: required, selection_keys: ['cancelled', 'done', 'draft', 'open', 'pending'], req_default: open

# NTD this report is a view, not table
project / report.project.task.user / date_last_stage_update (date) : NEW
project / report.project.task.user / day (char) : DEL
project / report.project.task.user / month (selection) : DEL selection_keys: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']
project / report.project.task.user / priority (selection) : selection_keys is now '['0', '1', '2']' ('['0', '1', '2', '3', '4']')
project / report.project.task.user / stage_id (many2one) : NEW relation: project.task.type
project / report.project.task.user / year (char) : DEL

# Additionally, take into account how project aliases are now configured
# in the project's create() method. Adapt existing aliases.

---XML records in module 'project'---
# Regular updated data
DEL ir.actions.act_window: project.action_project_task_reevaluate
DEL ir.actions.act_window: project.my_open_tasks_action
DEL ir.actions.act_window: project.open_board_project
NEW ir.model.access: project.access_mail_alias
NEW ir.model.access: project.access_project_task_type_manager
NEW ir.model.access: project.access_resource_calendar_leaves_user
DEL ir.ui.menu: project.menu_project_dashboard
NEW ir.ui.view: project.assets_backend
DEL ir.ui.view: project.board_project_form
DEL ir.ui.view: project.view_project_task_reevaluate
DEL ir.ui.view: project.view_task_history_tree
DEL ir.ui.view: project.view_task_project_user_tree
DEL ir.ui.view: project.view_task_tree

# Rename functionally similar mail subtype ids
DEL mail.message.subtype: project.mt_project_task_started
NEW mail.message.subtype: project.mt_project_task_assigned
DEL mail.message.subtype: project.mt_task_closed
NEW mail.message.subtype: project.mt_task_assigned
DEL mail.message.subtype: project.mt_task_started
NEW mail.message.subtype: project.mt_task_ready

# No more mail subtype for a project's task end of life
DEL mail.message.subtype: project.mt_project_task_closed

# Data for abandoned process module
DEL process.node: project.process_node_donetask0
DEL process.node: project.process_node_drafttask0
DEL process.node: project.process_node_opentask0
DEL process.node: project.process_node_taskbydelegate0
DEL process.process: project.process_process_tasksprocess0
DEL process.transition: project.process_transition_delegate0
DEL process.transition: project.process_transition_draftopentask0
DEL process.transition: project.process_transition_opendonetask0
DEL process.transition.action: project.process_transition_action_draftcanceltask0
DEL process.transition.action: project.process_transition_action_draftopentask0
DEL process.transition.action: project.process_transition_action_opencanceltask0
DEL process.transition.action: project.process_transition_action_opendrafttask0
DEL process.transition.action: project.process_transition_action_openpendingtask0
48 changes: 48 additions & 0 deletions addons/project/migrations/8.0.1.1/post-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Odoo, a suite of business apps
# This module Copyright (C) 2014 Therp BV (<http://therp.nl>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

from openerp.modules.registry import RegistryManager
from openerp import SUPERUSER_ID as uid
from openerp.openupgrade import openupgrade, openupgrade_80


@openupgrade.migrate()
def migrate(cr, version):
registry = RegistryManager.get(cr.dbname)

openupgrade.map_values(
cr,
openupgrade.get_legacy_name('priority'),
'priority',
[('4', '0'), ('3', '0'), ('2', '1'), ('1', '2'), ('0', '2')],
table='project_task', write='sql')

openupgrade_80.update_aliases(
cr, registry, 'project.project',
set_parent_thread_id=True,
defaults_id_key='project_id')

openupgrade_80.set_message_last_post(
cr, uid, registry, ['project.project', 'project.task']
)

openupgrade.load_data(
cr, 'project', 'migrations/8.0.1.1/noupdate_changes.xml')
38 changes: 38 additions & 0 deletions addons/project/migrations/8.0.1.1/pre-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Odoo, a suite of business apps
# This module Copyright (C) 2014 Therp BV (<http://therp.nl>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

from openerp.openupgrade import openupgrade


column_renames = {
'project_task': [('priority', None)]}

xmlid_renames = [
('project.mt_project_task_started', 'project.mt_project_task_assigned'),
('project.mt_task_started', 'project.mt_task_assigned'),
('project.mt_task_closed', 'project.mt_task_ready'),
]


@openupgrade.migrate()
def migrate(cr, version):
openupgrade.rename_columns(cr, column_renames)
openupgrade.rename_xmlids(cr, xmlid_renames)
2 changes: 1 addition & 1 deletion openerp/openupgrade/doc/source/modules70-80.rst
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ Status :
+-----------------------------------+-----------------------------------+
|product_visible_discount | Nothing to do |
+-----------------------------------+-----------------------------------+
|project | |
|project | Done |
+-----------------------------------+-----------------------------------+
|project_issue | Done |
+-----------------------------------+-----------------------------------+
Expand Down
75 changes: 72 additions & 3 deletions openerp/openupgrade/openupgrade.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
'check_values_selection_field',
'move_field_m2o',
'convert_field_to_html',
'map_values',
]


Expand All @@ -67,6 +68,8 @@ def check_values_selection_field(cr, table_name, field_name, allowed_values):
has only the values 'allowed_values'.
If not return False and log an error.
If yes, return True.

.. versionadded:: 8.0
"""
res = True
cr.execute("SELECT %s, count(*) FROM %s GROUP BY %s;" %
Expand Down Expand Up @@ -197,6 +200,8 @@ def rename_xmlids(cr, xmlids_spec):
One usage example is when an ID changes module. In OpenERP 6 for example,
a number of res_groups IDs moved to module base from other modules (
although they were still being defined in their respective module).

:param xmlids_spec: a list of tuples (old module.xmlid, new module.xmlid).
"""
for (old, new) in xmlids_spec:
if not old.split('.') or not new.split('.'):
Expand Down Expand Up @@ -401,12 +406,17 @@ def write_value(ids, field, value):

def logged_query(cr, query, args=None):
"""
Logs query and affected rows at level DEBUG
Logs query and affected rows at level DEBUG.

:param query: a query string suitable to pass to cursor.execute()
:param args: a list, tuple or dictionary passed as substitution values \
to cursor.execute().
"""
if args is None:
args = []
args = ()
args = tuple(args) if type(args) == list else args
cr.execute(query, args)
logger.debug('Running %s', query % tuple(args))
logger.debug('Running %s', query % args)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will cause a regression on a bug when args was passed as a list.
cr.execute was ok with lists IIRC
EDIT, just look at L373:

args = []

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this is not very pretty but it is trivial of course:

'dfsa' % []
'dfsa'

On my initial review I checked that all occurrences I could find in openupgrade-addons/7.0 pass a tuple, not a list. Do you have a concrete example where a list is passed that would trigger a regression? In fact, does that not break cr.execute which I believe does not take a list of arguments very well instead of a tuple?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

>>> "%s, %s" % ["1", "2"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: not enough arguments for format string

I remember running into this bug in 7.0 migration, I was calling logged query with a list.
I am resposible for the tuple being there in the first place.
If we support args as a dict, why not proof it for lists as well.
I don't want this back and forth of args and tuple(args) to go on forever ;)

And the concrete example is right there, two lines above:

     if args is None:
         args = []

It seems really senseless to proof it with None to the cr.execute and then have it fail on the next line.
These four lines should be compatible with eachother.

Edit: I realise that [] is an edge case which will not cause a fail, but I think my point still stands. If I look at the code, it tells me that args is expected to be a list. I will therefore use a args as a list and run into this bug.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I now see that you actually can pass a list to cursor.execute(), so we should settle for
args = tuple(args) if typeof(args) == list else args then

-- EDIT-- Done.

logger.debug('%s rows affected', cr.rowcount)
return cr.rowcount

Expand Down Expand Up @@ -520,6 +530,65 @@ def float_to_integer(cr, table, field):
})


def map_values(
cr, source_column, target_column, mapping,
model=None, table=None, write='sql'):
"""
Map old values to new values within the same model or table. Old values
presumably come from a legacy column.

:param cr: The database cursor
:param source_column: the database column that contains old values to be \
mapped
:param target_column: the database column, or model field (if 'write' is \
'orm') that the new values are written to
:para mapping: list of tuples [(old value, new value)]
:param model: used for writing if 'write' is 'orm', or to retrieve the \
table if 'table' is not given.
:param table: the database table used to query the old values, and write \
the new values (if 'write' is 'sql')
:param write: Either 'orm' or 'sql'. Note that old ids are always \
identified by an sql read.

.. versionadded:: 8.0
"""

if write not in ('sql', 'orm'):
logger.exception(
"map_values is called with unknown value for write param: %s",
write)
if not table:
if not model:
logger.exception("map_values is called with no table and no model")
table = model._table
if source_column == target_column:
logger.exception(
"map_values is called with the same value for source and old"
" columns : %s",
source_column)
for old, new in mapping:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a check for avoiding using the same column as source and target.

values = {
'table': table,
'source': source_column,
'target': target_column,
'old': old,
'new': new,
}
if write == 'sql':
query = """UPDATE %(table)s
SET %(target)s = %%(new)s
WHERE %(source)s = %%(old)s""" % values
else:
query = """SELECT id FROM %(table)s
WHERE %(source)s = %%(old)s""" % values
logged_query(cr, query, values)
if write == 'orm':
model.write(
cr, SUPERUSER_ID,
[row[0] for row in cr.fetchall()],
{target_column: new})


def message(cr, module, table, column,
message, *args, **kwargs):
"""
Expand Down
37 changes: 37 additions & 0 deletions openerp/openupgrade/openupgrade_80.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
# docs in the latest release.


from openerp import SUPERUSER_ID


def get_last_post_for_model(cr, uid, ids, model_pool):
"""
Given a set of ids and a model pool, return a dict of each object ids with
Expand Down Expand Up @@ -72,3 +75,37 @@ def set_message_last_post(cr, uid, pool, models):
last_posts = get_last_post_for_model(cr, uid, obj_ids, model_pool)
for i in obj_ids:
model_pool.write(cr, uid, [i], {'message_last_post': last_posts[i]})


def update_aliases(
cr, registry, model_name, set_parent_thread_id,
alias_defaults=None, defaults_id_key=False):
"""
Update a model's aliases according to how they are configured
in the model's create() method.

:param model_name: The name of the model whose aliases are to be updated. \
The model_id is also set as the aliases' alias_parent_model_id.
:param set_parent_thread_id': When set, set the ids of the resources as \
their alias' alias_parent_thread_id
:param alias_defaults: Static dictionary, recorded as a string on each \
alias
:param defaults_id_key: When defined, add this key to each alias' defaults \
dictionary with the resource id as its value.
"""
model_id = registry['ir.model'].search(
cr, SUPERUSER_ID, [('model', '=', model_name)])[0]
vals = {'alias_parent_model_id': model_id}
if defaults_id_key and alias_defaults is None:
alias_defaults = {}
res_ids = registry[model_name].search(
cr, SUPERUSER_ID, [], context={'active_test': False})
for res in registry[model_name].browse(
cr, SUPERUSER_ID, res_ids):
if set_parent_thread_id:
vals['alias_parent_thread_id'] = res.id
if defaults_id_key:
alias_defaults[defaults_id_key] = res.id
if alias_defaults is not None:
vals['alias_defaults'] = str(alias_defaults)
res.alias_id.write(vals)