0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OdooのXMLデータファイルをExcelファイルで管理する

Last updated at Posted at 2025-07-21

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>

  1. data=もしくはdemo=で始める
  2. data=<name>の場合、XMLデータファイル名は、<name>_data.xmlとする
  3. 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値
コメント <任意の文字列> #[<任意の文字列>] 任意の値  コメントや元データ
プロジェクトのデモデータ
demo=project
#,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 サブフォルダです。

🔁 処理の流れ

  1. 指定された Excel ファイルを読み取り専用で開く。
  2. xml フォルダが存在しない場合は作成する。
  3. 各シートを順に処理:
    • 名前が 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形式のデータファイルであれば、レコードの追加・更新を制御できたり、レコードの削除や関数の呼び出しも可能です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?