Odooのデータファイル
Odooのデータファイルには、XML形式とCSV形式とがあり、デモデータやマスタデータとして利用できます。
本記事では、デモデータやマスタデータをExcelファイルで管理し、それをXMLデータファイルとして出力できるようにします。
XMLデータファイルの構造
odooのXMLデータファイルの構造は、次の各ページで確認できます。
1. チュートリアル
2. リファレンス
3. スキーマ
XMLデータファイルのルート要素
XMLデータファイルのルート要素は、<odoo/>
です。その子要素に<operation/>
(後述)もしくは<data/>
で構成されます。
通常、<data noupdate="1" />
と記述し、モジュールのインストール時にのみ、その子要素の<operation/>
が実行(更新)され、モジュールのアップグレード時には、実行(更新)されません。
<odoo>
<data noupdate="1">
<!-- Only loaded when installing the module (odoo-bin -i module) -->
<operation/>
</data>
<!-- (Re)Loaded at install and update (odoo-bin -i/-u) -->
<operation/>
</odoo>
XMLデータファイルの<operation />
実際には、<operation/>
という要素はなく、次の要素を指しています(ただし、<field />
を除く)。
要素 | 説明 | 対象 |
---|---|---|
<odoo_openerp_data /> |
<odoo /> (<openerp /> )もしくは<data />
|
〇 |
<record /> |
レコードの追加・更新 | 〇 |
( <field /> ) |
追加・更新するレコードの項目とその値 | 〇 |
<delete /> |
レコードの削除 | 〇 |
<function /> |
関数の呼び出し | 〇 |
<menuitem /> |
メニュー項目 | |
<template /> |
文書化のテンプレート | |
<act_window /> |
アクション | |
<report /> |
帳票定義 |
対象が"〇"の要素をXMLデータファイルとして出力できるようにします。
Excelファイルの設計
Excelシートの名前
XMLデータファイルに出力するExcelシートの名前を次のように命名します。
data=<name>
demo=<name>
-
data=
もしくはdemo=
で始める -
data=<name>
の場合、XMLデータファイル名は、<name>_data.xml
とする -
demo=<name>
の場合、XMLデータファイル名は、<name>_demo.xml
とする
※ これら以外は、出力対象としない。
Excelシートのレイアウト
各列の定義は、次の表に従い、1行目を見出し行、2行目を属性行として、3行目からデータを入力します。
列 | 説明 | 見出し行 (1行目) |
属性行 (2行目) |
入力値 (3行目) |
備考 |
---|---|---|---|---|---|
A列 | 任意の番号 | # | (なし) | 行番号 | 必須入力 |
B列 | モデル名 | モデル名 | (なし) | モデル名 | 必須入力 |
C列 | noupdate="1" | 有効化時のみ | (なし) | 1、またはそれ以外 | |
D列 | 追加・更新, 削除 | 操作 | (なし) | record, delete, function, その他(操作しない) | |
E列 | 外部ID、または、関数名 | 外部ID | (なし) | 任意の文字列 | |
F列以降 | field要素 | <項目名> | search, ref, eval, <type> | 各属性や項目の値 | |
function要素 | <関数名> | eval, function, (空欄) | 関数呼び出しのeval値 | ||
コメント | <任意の文字列> | #[<任意の文字列>] | 任意の値 | コメントや元データ |
プロジェクトのデモデータ
#,Model,NoUpd,Tag,id/name,groups_id,implied_ids,name,plan_id,mail_template_id,sequence,legend_blocked,legend_done,fold,date_start,date,color,user_id,type_ids,partner_id,privacy_visibility,favorite_user_ids,tag_ids,stage_id,analytic_account_id,res_model,res_id,res_model_id,activity_type_id,date_deadline,summary,create_uid,description,is_reached,deadline,reached_date,project_id,planned_hours,user_ids,create_date,milestone_id,planned_hours,priority,model,message_type,subtype_id,author_id,field,field_desc,old_value_char,new_value_char,field_type,old_value_integer,new_value_integer,mail_message_id,kanban_state,body,parent_id,recurrence_left,repeat_unit,repeat_on_month,repeat_type,repeat_number,repeat_day,repeat_weekday,next_recurrence_date,mon,tue,wed,thu,fri,recurring_task,recurrence_id,depend_on_ids,active,active,project_id,access_token,rated_partner_id,rating_template_id,user_id,progress,status
0,,,,,eval,eval,,ref,ref,,,,eval,eval,eval,,ref,eval,ref,,eval,eval,ref,ref,,ref,ref,ref,eval,,ref,,eval,eval,eval,ref,,eval,eval,ref,eval,,,,ref,ref,eval,,,,,,,ref,,,ref,,,,,,,,eval,eval,eval,eval,eval,eval,eval,ref,eval,eval,,,,ref,ref,eval,eval,
1,res.users,1,record,base.user_demo,[Command.link(ref('group_project_user'))],,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2,res.groups,1,record,base.group_user,,[Command.link(ref('group_project_milestone'))],,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
3,res.groups,1,record,base.group_portal,,[Command.link(ref('group_project_milestone'))],,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
4,project.tags,1,record,project_tags_00,,,Bug,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
5,project.tags,1,record,project_tags_01,,,New Feature,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
6,project.tags,1,record,project_tags_02,,,Experiment,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
7,project.tags,1,record,project_tags_03,,,Usability,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
8,project.tags,1,record,project_tags_04,,,Internal,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
9,project.tags,1,record,project_tags_05,,,External,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
10,account.analytic.account,1,record,analytic_office_design,,,Office Design,analytic.analytic_plan_projects,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
11,account.analytic.account,1,record,analytic_research_development,,,Research & Development,analytic.analytic_plan_projects,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
12,account.analytic.account,1,record,analytic_renovations,,,Renovations,analytic.analytic_plan_projects,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
13,project.project.stage,1,record,project.project_project_stage_2,,,,,project.project_done_email_template,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
14,project.task.type,1,record,project_stage_0,,,New,,project.mail_template_data_project_task,1,Blocked,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
15,project.task.type,1,record,project_stage_1,,,In Progress,,,10,Need functional or technical help,Buzz or set as done,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
16,project.task.type,1,record,project_stage_2,,,Done,,,20,,,TRUE,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
17,project.task.type,1,record,project_stage_3,,,Canceled,,,30,,Ready to reopen,TRUE,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
18,project.project,1,record,project_project_1,,,Office Design,,,,,,,DateTime.today() - relativedelta(weeks=9),"DateTime.today() + relativedelta(weekday=4,weeks=1)",3,base.user_demo,"[Command.link(ref('project_stage_0')), Command.link(ref('project_stage_1')), Command.link(ref('project_stage_2')), Command.link(ref('project_stage_3'))]",base.partner_demo_portal,portal,[Command.link(ref('base.user_admin'))],[Command.link(ref('project.project_tags_05'))],project.project_project_stage_1,project.analytic_office_design,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
19,mail.followers,1,record,project_1_follower_admin,,,,,,,,,,,,,,,base.partner_admin,,,,,,project.project,project_project_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
20,project.project,1,record,project_project_2,,,Research & Development,,,,,,,,,,base.user_admin,"[Command.link(ref('project_stage_0')), Command.link(ref('project_stage_1')), Command.link(ref('project_stage_2')), Command.link(ref('project_stage_3'))]",,followers,[Command.link(ref('base.user_admin'))],[Command.link(ref('project.project_tags_04'))],project.project_project_stage_1,project.analytic_research_development,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
21,mail.activity,1,record,project_2_activity_1,,,,,,,,,,,,,base.user_admin,,,,,,,,,project_project_2,project.model_project_project,mail.mail_activity_data_meeting,(DateTime.today() + relativedelta(days=13)).strftime('%Y-%m-%d %H:%M'),Examine project status,base.user_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
22,project.project,1,record,project_project_3,,,Renovations,,,,,,,(DateTime.today() + relativedelta(months=-2)).strftime('%Y-%m-%d 10:00:00'),(DateTime.today() + relativedelta(days=-5)).strftime('%Y-%m-%d 17:00:00'),4,base.user_admin,"[Command.link(ref('project_stage_0')), Command.link(ref('project_stage_1')), Command.link(ref('project_stage_2')), Command.link(ref('project_stage_3'))]",,employees,[Command.link(ref('base.user_admin'))],"[Command.link(ref('project_tags_04')), Command.link(ref('project_tags_02'))]",project.project_project_stage_2,project.analytic_renovations,,,,,,,,Renovation work at the YourCompany headquarters.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
23,project.task.type,1,record,project_personal_stage_admin_0,,,Inbox,,,1,,,,,,,base.user_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
24,project.task.type,1,record,project_personal_stage_admin_1,,,Today,,,2,,,,,,,base.user_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
25,project.task.type,1,record,project_personal_stage_admin_2,,,This Week,,,3,,,,,,,base.user_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
26,project.task.type,1,record,project_personal_stage_admin_3,,,This Month,,,4,,,,,,,base.user_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
27,project.task.type,1,record,project_personal_stage_admin_4,,,Later,,,5,,,,,,,base.user_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
28,project.task.type,1,record,project_personal_stage_admin_5,,,Done,,,6,,,TRUE,,,,base.user_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
29,project.task.type,1,record,project_personal_stage_admin_6,,,Canceled,,,7,,,TRUE,,,,base.user_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
30,project.task.type,1,record,project_personal_stage_demo_0,,,Inbox,,,1,,,,,,,base.user_demo,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
31,project.task.type,1,record,project_personal_stage_demo_1,,,Today,,,2,,,,,,,base.user_demo,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
32,project.task.type,1,record,project_personal_stage_demo_2,,,This Week,,,3,,,,,,,base.user_demo,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
33,project.task.type,1,record,project_personal_stage_demo_3,,,This Month,,,4,,,,,,,base.user_demo,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
34,project.task.type,1,record,project_personal_stage_demo_4,,,Later,,,5,,,,,,,base.user_demo,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
35,project.task.type,1,record,project_personal_stage_demo_5,,,Done,,,6,,,TRUE,,,,base.user_demo,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
36,project.task.type,1,record,project_personal_stage_demo_6,,,Canceled,,,7,,,TRUE,,,,base.user_demo,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
37,project.milestone,1,record,project_1_milestone_1,,,First Phase,,,,,,,,,,,,,,,,,,,,,,,,,,TRUE,time.strftime('%Y-%m-10'),time.strftime('%Y-%m-10'),project.project_project_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
38,project.milestone,1,record,project_1_milestone_2,,,Second Phase,,,,,,,,,,,,,,,,,,,,,,,,,,FALSE,(DateTime.now() + relativedelta(years=1)).strftime('%Y-%m-15'),,project.project_project_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
39,project.milestone,1,record,project_1_milestone_3,,,Final Phase,,,,,,,,,,,,,,,,,,,,,,,,,,FALSE,(DateTime.now() + relativedelta(years=2)).strftime('%Y-%m-%d'),,project.project_project_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
40,project.task,1,record,project_1_task_1,,,Office planning,,,20,,,,,,7,,,,,,,project_stage_2,,,,,,,,,,,,,project.project_project_1,20,FALSE,DateTime.now() - relativedelta(months=5),project.project_1_milestone_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
41,project.task,1,record,project_1_task_2,,,Lunch Room: kitchen,,,,,,,,,,,,,,,,project_stage_2,,,,,,,,,,,,,project.project_project_1,,[Command.link(ref('base.user_demo'))],DateTime.now() - relativedelta(months=5),project.project_1_milestone_1,32,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
42,mail.message,1,record,project_1_task_2_mail_message_1,,,,,,,,,,,DateTime.now() - relativedelta(months=2),,,,,,,,,,,project.project_1_task_2,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
43,mail.message,1,record,project_1_task_2_mail_message_2,,,,,,,,,,,DateTime.now() - relativedelta(months=1),,,,,,,,,,,project.project_1_task_2,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
44,mail.tracking.value,1,record,project_1_task_2_mail_message_1_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,New,In Progress,many2one,1,2,project_1_task_2_mail_message_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
45,mail.tracking.value,1,record,project_1_task_2_mail_message_2_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,In Progress,Done,many2one,2,3,project_1_task_2_mail_message_2,,,,,,,,,,,,,,,,,,,,,,,,,,,,
46,project.task,1,record,project_1_task_3,,,Noise Reduction,,,,,,,,,4,,,,,,,project_stage_2,,,,,,time.strftime('%Y-%m-24'),,,Installation of acoustic ceiling clouds and wall panels.,,,,project.project_project_1,,[Command.link(ref('base.user_admin'))],DateTime.now() - relativedelta(months=5),project.project_1_milestone_2,10,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
47,mail.message,1,record,project_1_task_3_mail_message_1,,,,,,,,,,,DateTime.now() - relativedelta(months=4),,,,,,,,,,,project.project_1_task_3,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
48,mail.message,1,record,project_1_task_3_mail_message_2,,,,,,,,,,,DateTime.now() - relativedelta(months=4),,,,,,,,,,,project.project_1_task_3,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
49,mail.tracking.value,1,record,project_1_task_3_mail_message_1_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,New,In Progress,many2one,1,2,project_1_task_3_mail_message_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
50,mail.tracking.value,1,record,project_1_task_3_mail_message_2_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,In Progress,Done,many2one,2,3,project_1_task_3_mail_message_2,,,,,,,,,,,,,,,,,,,,,,,,,,,,
51,project.task,1,record,project_1_task_4,,,Modifications asked by the customer,,,17,,,,,,,,,,,,[Command.set([ref('project_tags_00')])],project_stage_2,,,,,,,,,Modifications to the kitchen of the lunch room,,,,project.project_project_1,8,FALSE,DateTime.now() - relativedelta(months=5),project.project_1_milestone_2,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
52,mail.message,1,record,project_1_task_4_mail_message_1,,,,,,,,,,,DateTime.now() - relativedelta(months=4),,,,,,,,,,,project.project_1_task_4,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
53,mail.message,1,record,project_1_task_4_mail_message_2,,,,,,,,,,,DateTime.now() - relativedelta(months=3),,,,,,,,,,,project.project_1_task_4,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
54,mail.tracking.value,1,record,project_1_task_4_mail_message_1_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,New,In Progress,many2one,1,2,project_1_task_4_mail_message_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
55,mail.tracking.value,1,record,project_1_task_4_mail_message_2_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,In Progress,Done,many2one,2,3,project_1_task_4_mail_message_2,,,,,,,,,,,,,,,,,,,,,,,,,,,,
56,project.task,1,record,project_1_task_5,,,Energy Certificate,,,,,,,,,1,,,,,,,project_stage_1,,,,,,DateTime.now() + relativedelta(days=2),,,,,,,project.project_project_1,,[Command.link(ref('base.user_admin'))],DateTime.now() - relativedelta(months=5),project.project_1_milestone_3,15,1,,,,,,,,,,,,,blocked,,,,,,,,,,,,,,,,,,,,,,,,,,,
57,mail.message,1,record,project_1_task_5_mail_message_1,,,,,,,,,,,DateTime.now() - relativedelta(months=4),,,,,,,,,,,project.project_1_task_5,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
58,mail.tracking.value,1,record,project_1_task_5_mail_message_1_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,New,In Progress,many2one,1,2,project_1_task_5_mail_message_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
59,mail.activity,1,record,project_1_task_5_activity_1,,,,,,,,,,,,,base.user_admin,,,,,,,,,project_1_task_5,project.model_project_task,mail.mail_activity_data_email,(DateTime.today() + relativedelta(hours=3)).strftime('%Y-%m-%d %H:%M'),Follow-up email,base.user_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
60,project.task,1,record,project_1_task_6,,,Room 1: Decoration,,,,,,,,,11,,,,,,[Command.set([ref('project_tags_01')])],project_stage_1,,,,,,time.strftime('%Y-%m-%d'),,,,,,,project.project_project_1,,[Command.link(ref('base.user_admin'))],DateTime.now() - relativedelta(months=5),project.project_1_milestone_3,76,0,,,,,,,,,,,,,done,,,,,,,,,,,,,,,,,,,,,,,,,,,
61,mail.message,1,record,project_1_task_6_mail_message_1,,,,,,,,,,,DateTime.now() - relativedelta(months=2),,,,,,,,,,,project.project_1_task_6,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
62,mail.tracking.value,1,record,project_1_task_6_mail_message_1_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,New,In Progress,many2one,1,2,project_1_task_6_mail_message_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
63,mail.activity,1,record,project_1_task_6_activity_1,,,,,,,,,,,,,base.user_admin,,,,,,,,,project_1_task_6,project.model_project_task,mail.mail_activity_data_call,(DateTime.today() + relativedelta(hours=2)).strftime('%Y-%m-%d %H:%M'),Call Joel Willis,base.user_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
64,project.task,1,record,project_1_task_7,,,Room 2: Decoration,,,,,,,,,9,,,,,,,project_stage_1,,,,,,DateTime.now() + relativedelta(days=6),,,,,,,project.project_project_1,,[Command.link(ref('base.user_admin'))],DateTime.now() - relativedelta(months=5),project.project_1_milestone_3,24,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
65,mail.message,1,record,project_1_task_7_mail_message_1,,,,,,,,,,,DateTime.now() - relativedelta(months=3),,,,,,,,,,,project.project_1_task_7,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
66,mail.tracking.value,1,record,project_1_task_7_mail_message_1_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,New,In Progress,many2one,1,2,project_1_task_7_mail_message_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
67,project.task,1,record,project_1_task_8,,,Black Chairs for managers,,,,,,,,,5,,,,,,[Command.set([ref('project_tags_01')])],project_stage_1,,,,,,time.strftime('%Y-%m-19'),,,Use the account_budget module,,,,project.project_project_1,,[Command.link(ref('base.user_demo'))],DateTime.now() - relativedelta(months=5),project.project_1_milestone_3,60,0,,,,,,,,,,,,,blocked,,,,,,,,,,,,,,,,,,,,,,,,,,,
68,mail.message,1,record,project_1_task_8_mail_message_1,,,,,,,,,,,DateTime.now() - relativedelta(months=1),,,,,,,,,,,project.project_1_task_8,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
69,mail.tracking.value,1,record,project_1_task_8_mail_message_1_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,New,In Progress,many2one,1,2,project_1_task_8_mail_message_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
70,mail.message,1,record,project_1_task_8_message_1,,,,,,,,,,,(DateTime.now() - relativedelta(days=3)).strftime('%Y-%m-%d 09:43:27'),,,,,,,,,,,project_1_task_8,,,,,,,,,,,,,,,,,project.task,comment,,base.partner_demo,,,,,,,,,,"Hello Admin,
Can we discuss this? Having nicer chairs for managers doesn't sit right with me.",,,,,,,,,,,,,,,,,,,,,,,,,,
71,mail.message,1,record,project_1_task_8_message_2,,,,,,,,,,,(DateTime.now() - relativedelta(days=3)).strftime('%Y-%m-%d 11:52:03'),,,,,,,,,,,project_1_task_8,,,,,,,,,,,,,,,,,project.task,comment,,base.partner_admin,,,,,,,,,,"We have already discussed, and I stand by my decision.",project_1_task_8_message_1,,,,,,,,,,,,,,,,,,,,,,,,,
72,project.task,1,record,project_1_task_9,,,Meeting Room Furnitures,,,,,,,,,3,,,,,,,project_stage_1,,,,,,,,,,,,,project.project_project_1,,[Command.link(ref('base.user_demo'))],DateTime.now() - relativedelta(months=5),project.project_1_milestone_3,40,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
73,mail.activity,1,record,project_1_task_9_activity_1,,,,,,,,,,,,,base.user_demo,,,,,,,,,project_1_task_9,project.model_project_task,mail.mail_activity_data_todo,(DateTime.today() - relativedelta(days=2)).strftime('%Y-%m-%d %H:%M'),Check furniture,base.user_demo,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
74,project.task.recurrence,1,record,project_task_recurrence_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,DateTime.now() + relativedelta(weeks=-2),,,,,,,,,,,,,,,,,,,3,month,date,after,4,1,mon,,,,,,,,,,,,,,,,,,
75,project.task.recurrence,1,record,project_task_recurrence_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,DateTime.now() + relativedelta(weeks=-2),,,,,,,,,,,,,,,,,
76,project.task.recurrence,1,record,project_task_recurrence_2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,20,,,after,20,,,,TRUE,TRUE,TRUE,TRUE,TRUE,,,,,,,,,,,,
77,project.task.recurrence,1,record,project_task_recurrence_2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,DateTime.now() + relativedelta(weeks=-2),,,,,,,,,,,,,,,,,
78,project.task,1,record,project_1_task_10,,,Customer review,,,20,,,,,,,,,,,,,project_stage_1,,,,,,,,,,,,,project.project_project_1,20,FALSE,DateTime.now() + relativedelta(weeks=-2),project.project_1_milestone_3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,TRUE,project_task_recurrence_1,,,,,,,,,,
79,project.task,1,record,project_1_task_11,,,Daily stand-up meeting - Send minutes,,,20,,,,,,,,,,,,,project_stage_1,,,,,,,,,,,,,project.project_project_1,0.25,FALSE,DateTime.now() - relativedelta(weeks=-1),,,,,,,,,,,,,,,,,,project.project_1_task_10,,,,,,,,,,,,,,TRUE,project_task_recurrence_2,,,,,,,,,,
80,project.task,1,record,project_1_task_12,,,Customer Meeting,,,20,,,,,,,,,,,,,project_stage_1,,,,,,,,,,,,,project.project_project_1,8,FALSE,,,,,,,,,,,,,,,,,,,project.project_1_task_10,,,,,,,,,,,,,,,,,,,,,,,,,
81,project.task,1,record,project_1_task_13,,,Daily Meetings summary,,,10,,,,,,,,,,,,,project_stage_1,,,,,,,,,,,,,project.project_project_1,2,FALSE,,,,,,,,,,,,,,,,,,,project.project_1_task_12,,,,,,,,,,,,,,,,,,,,,,,,,
82,project.task,1,record,project_1_task_14,,,Preparation,,,20,,,,,,,,,,,,,project_stage_1,,,,,,,,,,,,,project.project_project_1,2,FALSE,,,,,,,,,,,,,,,,,,,project.project_1_task_12,,,,,,,,,,,,,,,,,,,,,,,,,
83,project.task,1,record,project_1_task_15,,,Minutes,,,30,,,,,,,,,,,,,project_stage_1,,,,,,,,,,,,,project.project_project_1,2,FALSE,,,,,,,,,,,,,,,,,,,project.project_1_task_12,,,,,,,,,,,,,,,,,,,,,,,,,
84,project.task,1,record,project_2_task_1,,,Customer analysis + Architecture,,,,,,,,,7,,,,,,,project_stage_2,,,,,,,,,,,,,project.project_project_2,12,"[Command.link(ref('base.user_admin')), Command.link(ref('base.user_demo'))]",DateTime.now() - relativedelta(months=5),,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
85,mail.message,1,record,project_2_task_1_mail_message_1,,,,,,,,,,,DateTime.now() - relativedelta(weeks=16),,,,,,,,,,,project.project_2_task_1,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
86,mail.tracking.value,1,record,project_2_task_1_mail_message_1_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,New,In Progress,many2one,1,2,project_2_task_1_mail_message_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
87,mail.message,1,record,project_2_task_1_mail_message_2,,,,,,,,,,,"DateTime.now() + relativedelta(weeks=-16, days=4)",,,,,,,,,,,project.project_2_task_1,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
88,mail.tracking.value,1,record,project_2_task_1_mail_message_2_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,In Progress,Done,many2one,2,3,project_2_task_1_mail_message_2,,,,,,,,,,,,,,,,,,,,,,,,,,,,
89,project.task,1,record,project_2_task_2,,,Basic outline,,,,,,,,,,,,,,,[Command.set([ref('project_tags_02')])],project_stage_2,,,,,,,,,,,,,project.project_project_2,24,[Command.link(ref('base.user_admin'))],DateTime.now() - relativedelta(months=5),,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,[Command.link(ref('project.project_2_task_1'))],,,,,,,,,
90,mail.message,1,record,project_2_task_2_mail_message_1,,,,,,,,,,,DateTime.now() - relativedelta(weeks=15),,,,,,,,,,,project.project_2_task_2,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
91,mail.tracking.value,1,record,project_2_task_2_mail_message_1_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,New,In Progress,many2one,1,2,project_2_task_2_mail_message_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
92,mail.message,1,record,project_2_task_2_mail_message_2,,,,,,,,,,,DateTime.now() - relativedelta(weeks=14),,,,,,,,,,,project.project_2_task_2,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
93,mail.tracking.value,1,record,project_2_task_2_mail_message_2_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,In Progress,Done,many2one,2,3,project_2_task_2_mail_message_2,,,,,,,,,,,,,,,,,,,,,,,,,,,,
94,project.task,1,record,project_2_task_3,,,Planning and budget,,,,,,,,,6,,,,,,,project_stage_1,,,,,,DateTime.now() - relativedelta(days=4),,,,,,,project.project_project_2,,"[Command.link(ref('base.user_admin')), Command.link(ref('base.user_demo'))]",DateTime.now() - relativedelta(months=5),,40,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,[Command.link(ref('project.project_2_task_2'))],,,,,,,,,
95,mail.message,1,record,project_2_task_3_mail_message_1,,,,,,,,,,,"DateTime.now() + relativedelta(weeks=-14, days=1)",,,,,,,,,,,project.project_2_task_3,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
96,mail.tracking.value,1,record,project_2_task_3_mail_message_1_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,New,In Progress,many2one,1,2,project_2_task_3_mail_message_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
97,mail.message,1,record,project_2_task_3_message_1,,,,,,,,,,,"(DateTime.now() + relativedelta(weeks=-9, days=2)).strftime('%Y-%m-%d 11:23:17')",,,,,,,,,,,project_2_task_3,,,,,,,,,,,,,,,,,project.task,comment,,base.partner_root,,,,,,,,,,"Hello Demo,
There is a change in customer requirement.
Can you check the document from customer again.
Thanks,",,,,,,,,,,,,,,,,,,,,,,,,,,
98,mail.message,1,record,project_2_task_3_message_2,,,,,,,,,,,"(DateTime.now() + relativedelta(weeks=-9, days=2)).strftime('%Y-%m-%d 12:04:58')",,,,,,,,,,,project_2_task_3,,,,,,,,,,,,,,,,,project.task,comment,,base.partner_demo,,,,,,,,,,"Ok, I have checked the mail,
I will update the document and let you know.",project_2_task_3_message_1,,,,,,,,,,,,,,,,,,,,,,,,,
99,mail.message,1,record,project_2_task_3_message_3,,,,,,,,,,,"(DateTime.now() + relativedelta(weeks=-9, days=2)).strftime('%Y-%m-%d 12:15:26')",,,,,,,,,,,project_2_task_3,,,,,,,,,,,,,,,,,project.task,comment,,base.partner_root,,,,,,,,,,"Fine!
Send it ASAP, its urgent.",project_2_task_3_message_2,,,,,,,,,,,,,,,,,,,,,,,,,
100,project.task,1,record,project_2_task_4,,,User interface improvements,,,,,,,,,2,,,,,,"[Command.set([
ref('project.project_tags_01'),
ref('project.project_tags_03')])]",project_stage_1,,,,,,DateTime.now() + relativedelta(days=1),,,,,,,project.project_project_2,,[Command.link(ref('base.user_admin'))],DateTime.now() - relativedelta(months=5),,16,1,,,,,,,,,,,,,done,,,,,,,,,,,,,,,,,,[Command.link(ref('project.project_2_task_3'))],,,,,,,,,
101,mail.message,1,record,project_2_task_4_mail_message_1,,,,,,,,,,,DateTime.now() + relativedelta(weeks=-11),,,,,,,,,,,project.project_2_task_4,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
102,mail.tracking.value,1,record,project_2_task_4_mail_message_1_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,New,In Progress,many2one,1,2,project_2_task_4_mail_message_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
103,project.task,1,record,project_2_task_5,,,Social network integration,,,,,,,,,2,,,,,,,project_stage_1,,,,,,DateTime.now() + relativedelta(days=5),,,Facebook and Twitter integration,,,,project.project_project_2,,[Command.link(ref('base.user_demo'))],DateTime.now() - relativedelta(months=5),,38,1,,,,,,,,,,,,,blocked,,,,,,,,,,,,,,,,,,[Command.link(ref('project.project_2_task_3'))],,,,,,,,,
104,mail.message,1,record,project_2_task_5_mail_message_1,,,,,,,,,,,"DateTime.now() + relativedelta(weeks=-12, days=6)",,,,,,,,,,,project.project_2_task_5,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
105,mail.tracking.value,1,record,project_2_task_5_mail_message_1_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,New,In Progress,many2one,1,2,project_2_task_5_mail_message_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
106,project.task,1,record,project_2_task_6,,,Create new components,,,,,,,,,11,,,,,,,project_stage_1,,,,,,DateTime.now() + relativedelta(days=4),,,,,,,project.project_project_2,42,FALSE,DateTime.now() - relativedelta(months=5),,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,[Command.link(ref('project.project_2_task_3'))],,,,,,,,,
107,mail.message,1,record,project_2_task_6_mail_message_1,,,,,,,,,,,"DateTime.now() + relativedelta(weeks=-11, days=3)",,,,,,,,,,,project.project_2_task_6,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
108,mail.tracking.value,1,record,project_2_task_6_mail_message_1_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,New,In Progress,many2one,1,2,project_2_task_6_mail_message_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
109,project.task,1,record,project_2_task_7,,,New portal system,,,,,,,,,,,,,,,[Command.set([ref('project.project_tags_02')])],project_stage_2,,,,,,,,,,,,,project.project_project_2,,"[Command.link(ref('base.user_admin')), Command.link(ref('base.user_demo'))]",DateTime.now() - relativedelta(months=5),,22,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,[Command.link(ref('project.project_2_task_3'))],,,,,,,,,
110,mail.message,1,record,project_2_task_7_mail_message_1,,,,,,,,,,,"DateTime.now() + relativedelta(weeks=-12, days=5)",,,,,,,,,,,project.project_2_task_7,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
111,mail.tracking.value,1,record,project_2_task_7_mail_message_1_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,New,In Progress,many2one,1,2,project_2_task_7_mail_message_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
112,mail.message,1,record,project_2_task_7_mail_message_2,,,,,,,,,,,"DateTime.now() + relativedelta(weeks=-9, days=3)",,,,,,,,,,,project.project_2_task_7,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
113,mail.tracking.value,1,record,project_2_task_7_mail_message_2_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,In Progress,Done,many2one,2,3,project_2_task_7_mail_message_2,,,,,,,,,,,,,,,,,,,,,,,,,,,,
114,project.task,1,record,project_2_task_8,,,Usability review,,,,,,,,,7,,,,,,[Command.set([ref('project_tags_03')])],project_stage_0,,,,,,DateTime.now() + relativedelta(days=9),,,,,,,project.project_project_2,14,"[Command.link(ref('base.user_admin')), Command.link(ref('base.user_demo'))]",DateTime.now() - relativedelta(months=5),,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"[Command.link(ref('project.project_2_task_7')), Command.link(ref('project.project_2_task_5')), Command.link(ref('project.project_2_task_4')), Command.link(ref('project.project_2_task_6'))]",,,,,,,,,
115,mail.message,1,record,project_2_task_8_mail_message_1,,,,,,,,,,,DateTime.now() - relativedelta(weeks=10),,,,,,,,,,,project.project_2_task_8,,,,,,,,,,,,,,,,,project.task,notification,mt_task_stage,base.partner_admin,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
116,mail.tracking.value,1,record,project_2_task_8_mail_message_1_track_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])",Stage,New,In Progress,many2one,1,2,project_2_task_8_mail_message_1,,,,,,,,,,,,,,,,,,,,,,,,,,,,
117,project.task,1,record,project_2_task_9,,,Document management,,,,,,,,,4,,,,,,,project_stage_0,,,,,,DateTime.now() + relativedelta(days=15),,,,,,,project.project_project_2,,"[Command.link(ref('base.user_admin')), Command.link(ref('base.user_demo'))]",DateTime.now() - relativedelta(months=5),,18,1,,,,,,,,,,,,,done,,,,,,,,,,,,,,,,,,[Command.link(ref('project.project_2_task_8'))],,,,,,,,,
118,project.task,1,record,project_2_task_10,,,Unit Testing,,,20,,,,,,5,,,,,,,project_stage_0,,,,,,DateTime.now() + relativedelta(days=15),,,The most important part!,,,,project.project_project_2,35,FALSE,DateTime.now() - relativedelta(months=5),,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,[Command.link(ref('project.project_2_task_8'))],,,,,,,,,
119,project.task,1,record,project_2_task_11,,,Code Documentation,,,,,,,,,,,,,,,,project_stage_3,,,,,,,,,,,,,project.project_project_2,,[Command.link(ref('base.user_admin'))],DateTime.now() - relativedelta(months=5),,20,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,FALSE,,,,,,,,
120,project.task,1,record,project_3_task_1,,,Entry Hall,,,,,,,,,3,,,,,,,project_stage_2,,,,,,,,,,,,,project.project_project_3,,[Command.link(ref('base.user_demo'))],,,40,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
121,project.task,1,record,project_3_task_2,,,Check Lift,,,,,,,,,4,,,,,,,project_stage_2,,,,,,DateTime.today() + relativedelta(days=-10),,,,,,,project.project_project_3,,[Command.link(ref('base.user_admin'))],,,10,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
122,mail.message,1,record,project_3_task_2_message_1,,,,,,,,,,,(DateTime.now() - relativedelta(weeks=3)).strftime('%Y-%m-%d 09:42:13'),,,,,,,,,,,project_3_task_2,,,,,,,,,,,,,,,,,project.task,comment,,base.partner_admin,,,,,,,,,,"The elevator's state leaves much to be desired on many levels, we might need to take steps to repair it.",,,,,,,,,,,,,,,,,,,,,,,,,,
123,mail.message,1,record,project_3_task_2_message_2,,,,,,,,,,,(DateTime.now() - relativedelta(weeks=3)).strftime('%Y-%m-%d 10:23:48'),,,,,,,,,,,project_3_task_2,,,,,,,,,,,,,,,,,project.task,comment,,base.partner_demo,,,,,,,,,,"This is not very uplifting, it would probably raise the expenses by a lot. ??",project_3_task_2_message_1,,,,,,,,,,,,,,,,,,,,,,,,,
124,mail.message,1,record,project_3_task_2_message_3,,,,,,,,,,,(DateTime.now() - relativedelta(weeks=3)).strftime('%Y-%m-%d 10:57:04'),,,,,,,,,,,project_3_task_2,,,,,,,,,,,,,,,,,project.task,comment,,base.partner_admin,,,,,,,,,,"I know, it's driving me up the wall.",project_3_task_2_message_2,,,,,,,,,,,,,,,,,,,,,,,,,
125,project.task,1,record,project_3_task_3,,,Room 1: Paint,,,,,,,,,9,,,,,,[Command.set([ref('project_tags_01')])],project_stage_2,,,,,,DateTime.today() - relativedelta(days=5),,,Repaint the walls with the hex color #0FF1CE,,,,project.project_project_3,,[Command.link(ref('base.user_admin'))],,,24,0,,,,,,,,,,,,,done,,,,,,,,,,,,,,,,,,,,,,,,,,,
126,project.task,1,record,project_3_task_4,,,Bathroom,,,,,,,,,,,,,,,,project_stage_2,,,,,,,,,,,,,project.project_project_3,,[Command.link(ref('base.user_admin'))],,,76,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
127,project.task,1,record,project_3_task_5,,,Room 2: Paint,,,,,,,,,,,,,,,,project_stage_3,,,,,,,,,,,,,project.project_project_3,,[Command.link(ref('base.user_demo'))],,,40,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,FALSE,,,,,,,
128,project.task,1,record,project_private_task_1,,,Buy a gift for Marc Demo's birthday,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,[Command.link(ref('base.user_admin'))],,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
129,project.task,1,record,project_private_task_2,,,Change left screen cable,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,[Command.link(ref('base.user_admin'))],,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
130,project.task,1,record,project_private_task_3,,,Clean kitchen fridge,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,[Command.link(ref('base.user_demo'))],,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
131,project.task,1,record,project_private_task_4,,,Check employees lunch accounts,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,[Command.link(ref('base.user_demo'))],,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
132,rating.rating,1,record,rating_task_1,,,,,,,,,,,,,,,base.partner_demo_portal,,,,,,,project.project_1_task_3,project.model_project_task,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,PROJECT_1,base.partner_root,,,,
133,rating.rating,1,record,rating_task_2,,,,,,,,,,,,,,,base.partner_demo,,,,,,,project.project_2_task_7,project.model_project_task,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,PROJECT_2,base.partner_demo,,,,
134,rating.rating,1,record,rating_task_3,,,,,,,,,,,,,,,base.partner_demo_portal,,,,,,,project.project_1_task_4,project.model_project_task,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,PROJECT_3,base.partner_root,,,,
135,rating.rating,1,record,rating_task_4,,,,,,,,,,,,,,,base.partner_demo_portal,,,,,,,project.project_1_task_2,project.model_project_task,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,PROJECT_4,base.partner_root,,,,
136,project.task.type,1,record,project.project_stage_2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,rating_project_request_email_template,,,
137,project.update,1,record,project_update_1,,,Review of the situation,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ref('base.user_demo'),15,at_risk
138,project.update,1,record,project_update_2,,,Weekly review,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ref('base.user_admin'),35,at_risk
Excel VBA 標準モジュール
GenerateOdooXmlDataFiles
関数にExcelファイル名を指定して呼び出すと、xml
フィルダー内にXMLデータファイルが出力されます。
解説
🧾 関数名: GenerateOdooXmlDataFiles
📌 概要
この関数は、指定された Excel ファイルを読み込み、Odoo にインポート可能な XML データファイルを生成します。
各ワークシートの名前に応じて、適切な XML ファイルとして保存されます。
🎯 主な目的
- Excel ワークブック内の「data=」または「demo=」で始まるシートを対象に、
- その内容を XML 形式に変換し、
- 現在のブックと同じフォルダ内の
xml
サブフォルダに保存します。
🧩 引数
-
sourceFilename As String
読み込む Excel ファイルのフルパス。対象となるワークブックを指定します。
📂 出力
- 各対象シートに対して、以下のようなファイルが生成されます:
-
data=商品
→商品_data.xml
-
demo=顧客
→顧客_demo.xml
-
- 出力先は、現在のブックと同じフォルダ内の
xml
サブフォルダです。
🔁 処理の流れ
- 指定された Excel ファイルを読み取り専用で開く。
-
xml
フォルダが存在しない場合は作成する。 - 各シートを順に処理:
- 名前が
data=
またはdemo=
で始まるかを判定。 - 対象シートの内容を XML に変換(
GenerateOdooXmlData
を使用)。 - SAX パーサーと XML ライターを使って整形(インデント付き)。
- DOM を使って XML 宣言(
<?xml version="1.0" encoding="UTF-8"?>
)を追加。 - ファイルとして保存。
- 名前が
🛠 使用技術
- MSXML2.DOMDocument60:XML の読み書きと整形。
- SAXXMLReader60 / MXXMLWriter60:XML の整形(インデント処理)。
- VBA FileSystem:フォルダの作成とファイル保存。
ソースコード
Option Explicit
' Column offsets for metadata fields in the Excel sheet
Private Const COLUMN_OFFSET_LINE_NUMBER = 0
Private Const COLUMN_OFFSET_MODEL = 1
Private Const COLUMN_OFFSET_NOUPDATE = 2
Private Const COLUMN_OFFSET_TAG = 3
Private Const COLUMN_OFFSET_ID = 4
Private Const COLUMN_OFFSET_FIELDS = 5
' Structure representing each field's metadata
Private Type FieldDefinition
Name As String ' Field name in XML
Type As String ' Field type (e.g., string, eval, ref, html)
IsDisabled As Boolean ' True if field is marked as disabled (type starts with "#")
End Type
'------------------------------------------------------------------------------
' ExtractFieldDefinitions
'
' Parses the header rows of the Excel table to extract field definitions.
' Each field is defined by a name (row 1) and a type (row 2).
' Fields with types starting with "#" are considered disabled and skipped.
'
' Parameters:
' headerRange - The top-left cell of the table (typically A1)
'
' Returns:
' An array of FieldDefinition structures
'------------------------------------------------------------------------------
Private Function ExtractFieldDefinitions(headerRange As Range) As FieldDefinition()
Dim fields() As FieldDefinition
Dim colIndex As Long
colIndex = COLUMN_OFFSET_FIELDS
Do Until headerRange.Offset(0, colIndex).value = ""
ReDim Preserve fields(colIndex)
With fields(colIndex)
.Name = headerRange.Offset(0, colIndex).value
.Type = headerRange.Offset(1, colIndex).value
.IsDisabled = Left(.Type, 1) = "#"
End With
colIndex = colIndex + 1
Loop
ExtractFieldDefinitions = fields
End Function
'------------------------------------------------------------------------------
' AppendXmlChildrenFromString
'
' Parses a raw XML string and appends its child nodes to a given parent element.
' Used for embedding HTML or custom XML fragments inside a field.
'
' Parameters:
' parentElement - The XML element to append children to
' rawXml - A string containing raw XML content
'
' Returns:
' The updated parentElement with children appended
'------------------------------------------------------------------------------
Private Function AppendXmlChildrenFromString(parentElement As IXMLDOMElement, rawXml As String) As IXMLDOMElement
Dim tempDoc As IXMLDOMDocument2
Dim childNode As IXMLDOMNode
Set tempDoc = New DOMDocument60
tempDoc.async = False
tempDoc.preserveWhiteSpace = True
tempDoc.validateOnParse = False
tempDoc.LoadXML "<wrapper>" & vbCrLf & rawXml & vbCrLf & "</wrapper>"
For Each childNode In tempDoc.FirstChild.ChildNodes
parentElement.appendChild childNode
Next childNode
Set AppendXmlChildrenFromString = parentElement
End Function
'------------------------------------------------------------------------------
' CreateRecordElement
'
' Creates an XML <record> element from a row of Excel data.
' Each field is mapped to a <field> child element with appropriate attributes.
'
' Parameters:
' dataRow - The Excel row containing record data
' recordId - The ID of the record
' modelName - The model name for the record
' fields - Array of field definitions
' xmlDoc - The XML document to create elements from
'
' Returns:
' An IXMLDOMElement representing the <record>
'------------------------------------------------------------------------------
Private Function CreateRecordElement(dataRow As Range, recordId As String, modelName As String, fields() As FieldDefinition, xmlDoc As IXMLDOMDocument2) As IXMLDOMElement
Dim i As Long
Dim field As FieldDefinition
Dim value As String
Dim fieldElement As IXMLDOMElement
Set CreateRecordElement = xmlDoc.createElement("record")
With CreateRecordElement
.setAttribute "id", recordId
.setAttribute "model", modelName
For i = COLUMN_OFFSET_FIELDS To UBound(fields)
field = fields(i)
If field.IsDisabled Then GoTo SkipField
value = dataRow.Offset(0, i).value
If value = "" Then GoTo SkipField
Set fieldElement = .appendChild(xmlDoc.createElement("field"))
fieldElement.setAttribute "name", field.Name
Select Case LCase(field.Type)
Case "search"
fieldElement.setAttribute "search", value
Case "ref"
fieldElement.setAttribute "ref", value
Case "eval"
fieldElement.setAttribute "eval", value
Case "html"
fieldElement.setAttribute "type", "html"
AppendXmlChildrenFromString fieldElement, value
Case Else
If field.Type <> "" Then
fieldElement.setAttribute "type", field.Type
End If
fieldElement.Text = value
End Select
SkipField:
Next i
End With
End Function
'------------------------------------------------------------------------------
' CreateDeleteElement
'
' Creates an XML <delete> element for removing records.
'
' Parameters:
' dataRow - The Excel row containing delete metadata
' recordId - The ID of the record to delete
' modelName - The model name
' fields - Array of field definitions (unused here)
' xmlDoc - The XML document to create elements from
'
' Returns:
' An IXMLDOMElement representing the <delete>
'------------------------------------------------------------------------------
Private Function CreateDeleteElement(dataRow As Range, recordId As String, modelName As String, fields() As FieldDefinition, xmlDoc As IXMLDOMDocument2) As IXMLDOMElement
Set CreateDeleteElement = xmlDoc.createElement("delete")
With CreateDeleteElement
.setAttribute "id", recordId
.setAttribute "model", modelName
End With
End Function
'------------------------------------------------------------------------------
' CreateFunctionElement
'
' Creates an XML <function> element to invoke a method on a model.
'
' Parameters:
' dataRow - The Excel row containing function metadata
' functionName - The name of the function to call
' modelName - The model name
' fields - Array of field definitions
' xmlDoc - The XML document to create elements from
'
' Returns:
' An IXMLDOMElement representing the <function>
'------------------------------------------------------------------------------
Private Function CreateFunctionElement(dataRow As Range, functionName As String, modelName As String, fields() As FieldDefinition, xmlDoc As IXMLDOMDocument2) As IXMLDOMElement
Dim i As Long
Dim value As String
Dim field As FieldDefinition
Set CreateFunctionElement = xmlDoc.createElement("function")
With CreateFunctionElement
.setAttribute "model", modelName
.setAttribute "name", functionName
For i = COLUMN_OFFSET_FIELDS To UBound(fields)
field = fields(i)
If field.IsDisabled Then GoTo SkipField
value = dataRow.Offset(0, i).value
If value = "" Then GoTo SkipField
Select Case LCase(field.Type)
Case "eval"
.setAttribute "eval", value
Case "call"
AppendXmlChildrenFromString .ParentNode, value
Case Else
.Text = value
End Select
SkipField:
Next i
End With
End Function
'------------------------------------------------------------------------------
' GenerateOdooXmlData
'
' Main function to generate a complete Odoo-compatible XML document
' from a structured Excel table. Each row is converted into a <record>,
' <delete>, or <function> element depending on the tag.
'
' Parameters:
' tableRange - The top-left cell of the table (typically A1)
'
' Returns:
' An IXMLDOMDocument2 representing the full <odoo> XML structure
'------------------------------------------------------------------------------
Public Function GenerateOdooXmlData(tableRange As Range) As IXMLDOMDocument2
Dim fields() As FieldDefinition
Dim xmlDoc As IXMLDOMDocument2
Dim odooElement As IXMLDOMElement
Dim dataElement As IXMLDOMElement
Dim recordElement As IXMLDOMElement
Dim rowOffset As Long
Dim modelName As String
Dim isNoUpdate As Boolean
Dim tagName As String
Dim recordId As String
Dim lastNoUpdateFlag As Boolean
fields = ExtractFieldDefinitions(tableRange)
With tableRange.Offset(2, 0)
Set xmlDoc = New DOMDocument60
Set odooElement = xmlDoc.createElement("odoo")
xmlDoc.appendChild odooElement
rowOffset = 0
Do Until .Offset(rowOffset, COLUMN_OFFSET_LINE_NUMBER).value = ""
With .Offset(rowOffset, 0)
modelName = .Offset(0, COLUMN_OFFSET_MODEL).value
isNoUpdate = .Offset(0, COLUMN_OFFSET_NOUPDATE).value = "1"
tagName = .Offset(0, COLUMN_OFFSET_TAG).value
recordId = .Offset(0, COLUMN_OFFSET_ID).value
Select Case LCase(tagName)
Case "record"
Set recordElement = CreateRecordElement(.Offset(0, 0), recordId, modelName, fields, xmlDoc)
Case "delete"
Set recordElement = CreateDeleteElement(.Offset(0, 0), recordId, modelName, fields, xmlDoc)
Case "function"
Set recordElement = CreateFunctionElement(.Offset(0, 0), recordId, modelName, fields, xmlDoc)
Case Else
Set recordElement = Nothing
Debug.Assert False
GoTo SkipRow
End Select
If lastNoUpdateFlag <> isNoUpdate Then
lastNoUpdateFlag = isNoUpdate
Set dataElement = Nothing
End If
If dataElement Is Nothing Then
Set dataElement = odooElement.appendChild(xmlDoc.createElement("data"))
If isNoUpdate Then dataElement.setAttribute "noupdate", "1"
End If
dataElement.appendChild recordElement
SkipRow:
rowOffset = rowOffset + 1
End With
Loop
End With
Set GenerateOdooXmlData = xmlDoc
End Function
'------------------------------------------------------------------------------
' GenerateOdooXmlDataFiles
'
' Reads an Excel workbook and generates formatted XML data files for Odoo.
' Each worksheet whose name starts with "data=" or "demo=" is converted into
' an XML file. The output files are saved in a subfolder named "xml" located
' in the same directory as the current workbook.
'
' Sheet name prefixes determine the type of data:
' - "data=" → saved as "<sheetname>_data.xml"
' - "demo=" → saved as "<sheetname>_demo.xml"
'
' XML formatting is handled using SAX and DOM to ensure proper indentation
' and inclusion of the XML declaration.
'------------------------------------------------------------------------------
Sub GenerateOdooXmlDataFiles(sourceFilename As String)
Dim sourceWorkbook As Workbook
Dim sheet As Worksheet
Dim rawXmlDoc As IXMLDOMDocument2
Dim formattedXmlDoc As IXMLDOMDocument2
Dim saxReader As IVBSAXXMLReader
Dim xmlWriter As IMXWriter
Dim xmlFolderPath As String
Dim outputFilename As String
' Open the source Excel workbook in read-only mode
Debug.Print "Excel file: " & sourceFilename
Set sourceWorkbook = Workbooks.Open(ReadOnly:=True, Filename:=sourceFilename)
' Ensure the output folder for XML files exists
xmlFolderPath = ThisWorkbook.Path & "\xml"
If Dir(xmlFolderPath, vbDirectory) = "" Then
MkDir xmlFolderPath
Debug.Print "Created folder: " & xmlFolderPath
End If
' Iterate through each worksheet in the source workbook
For Each sheet In sourceWorkbook.Sheets
' Determine output filename based on sheet name prefix
If Left(sheet.Name, 5) = "data=" Then
outputFilename = xmlFolderPath & "\" & Mid(sheet.Name, 6) & "_data.xml"
Debug.Print "data: " & outputFilename
ElseIf Left(sheet.Name, 5) = "demo=" Then
outputFilename = xmlFolderPath & "\" & Mid(sheet.Name, 6) & "_demo.xml"
Debug.Print "demo: " & outputFilename
Else
' Skip sheets that do not match expected prefixes
Debug.Print "skip: [" & sheet.Name & "]"
GoTo SkipSheet
End If
' Generate raw XML from the sheet's data starting at cell A1
Set rawXmlDoc = BuildXmlData.GenerateOdooXmlData(sheet.Range("A1"))
' Format the raw XML using SAX parser and writer
Set saxReader = New SAXXMLReader60
Set xmlWriter = New MXXMLWriter60
xmlWriter.indent = True ' Enable indentation for readability
xmlWriter.omitXMLDeclaration = True ' Omit declaration temporarily
Set saxReader.contentHandler = xmlWriter
saxReader.Parse rawXmlDoc.XML ' Parse and format the raw XML
' Load formatted XML into DOM and insert XML declaration
Set formattedXmlDoc = New DOMDocument60
With formattedXmlDoc
.LoadXML xmlWriter.output
.InsertBefore .createProcessingInstruction("xml", "version='1.0' encoding='UTF-8'"), .FirstChild
.Save outputFilename ' Save final XML to file
End With
SkipSheet:
Next sheet
Debug.Print "XML generation complete."
End Sub
まとめ
Odooのデータファイルには、XML形式とCSV形式とがあり、Excelシートの内容を元に、XML形式のデータファイルを生成できるように設計・実装しました。
XML形式のデータファイルであれば、レコードの追加・更新を制御できたり、レコードの削除や関数の呼び出しも可能です。