App

DocuSignからOdoo SignへテンプレートをClaude Codeで移行をやった。Odoo 19対応/AIエージェントで自動化したよ。

電子署名サービスをDocuSignからOdoo Signに切り替える際、最初の壁になるのが「既存テンプレートの引っ越し」です。
本記事では、DocuSign本番環境のテンプレート6本を、Claude Code(AIコーディングエージェント)に書かせたスクリプトでOdoo.sh上のOdoo 19 EnterpriseのSignモジュールへ移行した手順を、失敗と手戻りも含めて公開します。

先に全体の結論を言うと、受け入れ側(Odoo)は素直です。障壁の大半はDocuSign側(APIの本番利用許可=Go Live審査)にあり、その顛末はOdooとは直接的な関係がないので別で筆者のnoteにまとめました。これからDocuSign APIを本番で使う方は先にご覧ください:
👉 DocusignのAPIの本番化がえぐかった。サブタイトル:DocuSign API本番化(Go Live審査)の全記録|note

そしてもう一つの結論。

移行スクリプト自体は簡単に書けますが、「動いた」と「使える」の間には深い溝があります。本記事の後半は、その溝で学んだこと — AIエージェントに移行を任せるときの発注仕様 — に割いています。

全体像

移行パイプラインは次の通りです。

  1. DocuSign APIでテンプレートPDFとフィールド定義(Tabs)を取得(読み取り専用)
  2. Odooへ接続確認(dry-run)
  3. XML-RPCでテンプレート作成
  4. DocuSignのタブ定義から署名フィールドを再構築
  5. 検証(数値・レンダリング・実署名フロー)

接続は外部API(XML-RPC)のみで、Odoo.shへのSSHもカスタムモジュールも不要でした。スクリプトはすべてClaude Codeに書かせ、人間の作業は認証情報の準備と承認、そして受け入れテストです。

前提

  • 移行元:DocuSign eSignature(本番アカウント、Go Live済みの統合キー+JWT Grant認証)
  • 移行先:Odoo.sh 本番環境、Odoo 19 Enterprise、Signモジュールインストール済み
  • 実行環境:Windows + Python(xmlrpc標準ライブラリ / PyMuPDF / cryptography)
  • 認証情報はすべて .env で管理し、コード・ログに出力しない

Step 1: DocuSignからテンプレートと「フィールド定義」を取得

JWT Grantでアクセストークンを取得し、テンプレートのPDFを保存します。

  • GET /v2.1/accounts/{accountId}/templates で一覧
  • GET /templates/{templateId}/documents/{documentId} でPDF本体
  • GET /templates/{templateId}/recipients?include_tabs=true署名者(ロール)とタブ定義

3つ目が最重要。

実は当初、PDFだけ取得して署名フィールドの位置は「アンカーテキスト検出」(PDF内の「署名」「日付」という文字を探して隣に置く)で推測させましたが
これは失敗でした。」
日本語の契約書は「署名」と書かずに氏名欄+押印で構成されることが多く、アンカー方式は構造的に効きません。しかも位置・サイズ・署名者・フィールド種別という「正解データ」はDocuSign側にTabsとして全部存在していたのです。よく調べもせずにすすめた当然の結果でした。

移行元に構造化データがあるなら、推測せず取得する。
アンカー検出のような推測手法は、元データが本当に存在しない場合の最終手段です。
当たり前のことですが、あるなら使いましょう。

Step 2: Odoo側の接続確認

XML-RPCで authenticate し、Signモジュールの導入状態と既存テンプレートを確認します。

common = xmlrpc.client.ServerProxy(f"{URL}/xmlrpc/2/common")
uid = common.authenticate(DB, LOGIN, API_KEY, {})

認証はログインパスワードではなくAPIキー(マイプロフィール → アカウントセキュリティ)を使います。

移行前のOdoo Signテンプレート一覧(空の状態)

Step 3: テンプレート作成 — Odoo 19の3段構造

ここがOdoo 19最大の仕様ポイントです。
Odoo 19では sign.templateattachment_id フィールドがありません。
旧バージョン向けの「ir.attachmentをsign.templateに直接紐付ける」サンプルは通用せず、次の3段で作ります。

ir.attachment ──< sign.document (attachment_id*, template_id*) >── sign.template
                        └── sign.item (document_id*)
att_id = create("ir.attachment", {"name": f"{name}.pdf", "datas": b64_pdf, ...})
tpl_id = create("sign.template", {"name": name})
doc_id = create("sign.document", {"template_id": tpl_id, "attachment_id": att_id})

この仕様差は、実装前に fields_get でスキーマを実測したことで判明しました。バージョン差異のあるモデルは、コードを書く前に fields_get で実測する — 今回の移行で最も効いた設計判断です。

細かい罠も2つ。XML-RPCの create() は引数の包み方で戻り値の型が変わります([[{...}]] だとlistが返り、後段で TypeError: unhashable type: 'list')。
また、
Odoo 19はPDFアップロード時にフィールドを自動検出して sign.item を勝手に生成します
API経由の作成でもこの自動生成分が混ざるため、移行スクリプトは「自動生成分を掃除してから正しい定義で作る」設計にする必要がありました。

Step 4: フィールドの再構築 — 「動いた」と「使える」の間

DocuSignのタブをOdooの sign.item に変換します。一見単純ですが、ここで3回作り直しました。手戻りの中身こそが共有価値だと思うので、順に書きます。

手戻り①:件数が合っても署名者が足りない

最初の移植は「タブ48件→sign.item 48件、全件一致」で完了報告が出ました。しかし実物を見ると、2者契約なのに署名者ロールが1つしかない。当初の要件定義(署名者1名の前提)が実物とズレていたためです。DocuSignのrecipientsと同数・同名のロールを作って対応付けるよう作り直しました。

手戻り②:位置は合ってもサイズが違う

次は「フィールドが極端に小さい」。位置(posX/posY)は変換したのに、サイズ(width/height)をDocuSignから取らず固定値で置いていたのが原因です。タブのwidth/height(72dpi基準のピクセル、PDFのポイントとほぼ1:1)をページ寸法で割って相対値にする変換を追加しました。なおOdooは座標を小数3桁でしか保存しないため、「丸めてからページ内にクランプする」順序にしないと微妙にはみ出します。

手戻り③:型の意味を捨てていた

DocuSignのタブ種別をOdooの型に対応させる際、対応する型がないもの(役職・数値など)を安易に「テキスト」に丸めていました。しかし sign.item.typeマスタデータなので自作できます。最終的なマッピング方針はこうです:

  1. Odooに標準型があるなら使う(氏名→名称、会社名→会社、メール→メール)
  2. 標準に無く汎用性のある意味は型を新規作成(役職、数値)
  3. それ以外はテキスト型+placeholderにDocuSignのラベルを引き継ぐ(住所、郵便番号など。欄内に「住所」と表示され、DocuSignと同じ使い勝手になる)

型に関する2つの日本仕様問題

日付: Odoo SignのDate型は、表示書式MM/DD/YYYYがフロントエンドJSにハードコードされており、res.langのdate_format設定を無視します。日本式(YYYY年MM月DD日)にする設定は存在しないため、日付欄はテキスト型+placeholder「YYYY年MM月DD日」で代替しました(自動日付入力は失われますが、表示の正しさを優先)。

押印: Odoo組み込みのStamp型は「会社スタンプ」で、会社の名前・住所・電話を自動表示するフィールドです。日本の押印(署名者本人が印影を適用する)とは別物で、押印欄に住所や電話番号が表示される珍事になりました。item_type='stamp' かつ自動表示なし(auto_field=False)の「印鑑」型を自作して付け替えることで解決しています。

押印欄が印鑑型フィールドに設定された相殺契約書

Step 5: 検証 — 座標の一致と描画の一致は別物

3回の手戻りを経て、検証も3層に強化しました。

1. データ検証: DocuSign座標→Odoo保存値の突き合わせ(全48件、許容誤差ページ比±1%)

2. レンダリング検証: 実PDFにフィールド枠をオーバーレイ描画し、下線・空欄に乗っているかを確認。座標値が一致していても、視覚目標に乗っているかは別問題です(実際、初期の数値検証はここを見ておらず「PASSなのにズレて見える」状態を見逃しました)。

実PDFに署名フィールド枠を重ねたレンダリング検証画像(業務委託契約書)

3. 機能検証: テスト署名リクエストを実際に送り、署名画面で全フィールドが入力可能なことを確認(Odooでは署名リクエストが1件でも紐づくとテンプレートが凍結・編集不可になる has_sign_requests の仕様があるため、テスト後のリクエスト削除まで含めて後始末)。

移行後のOdoo Signテンプレート一覧(6テンプレート)
Odoo Signテンプレート詳細画面

運用に向けた仕上げ
(移行とは直接無関係だが必要だったこと)

テンプレートが入っても、実運用にはOdoo側の周辺設定が要りました。

躓いた順に:

署名依頼メールが英語で届く: Odooのメールは受信者連絡先(res.partner)の言語で描画されます。さらに新規連絡先のデフォルト言語(ir.default)が英語のままだと、取引先を登録するたびに英語メールが飛びます。ja_JPへの変更を推奨します。

送信元が notifications@~.odoo.com になる: Odoo.sh既定では、差出人ドメインがfrom_filterに一致しない場合、Fromがodoo.comの通知アドレスに書き換えられます。mail.alias.domain の default_from を会社代表アドレスに変更することで「株式会社〇〇 <info@~>」名義に統一できます。あわせて自社ドメインのDNSにSPF(include:_spf.odoo.com)とDKIM(odoo._domainkey CNAME)の追加を。

日本語化された署名依頼メール(送信元は会社代表アドレス)

契約書は会社に紐づかない: 署名リクエストは「個人」の連絡先に紐づき、会社連絡先の画面には表示されません(Odoo 19標準)。ただし個人を必ず会社配下(parent_id)に登録しておけば、commercial_partner経由で会社単位の集約・フィルタが可能です。この運用ルールだけは移行初日から徹底することをお勧めします。

アクセス制限: Signアプリの利用者グループと、テンプレートごとの権限(誰がどの契約書を送れるか)も忘れずに。なお、sign.itemの書き込みには管理者権限だけでは足りず「Sign / User: Own Templates」グループが必要という、権限の2段構えにも注意。

AIエージェントに移行を任せるときの発注仕様書

今回、スクリプトはすべてAI(Claude Code)が書きましたが、手戻りの原因はAIの能力ではなくこちらの発注(プロンプト)の物足りなさでした。
同じことをする方のために、発注時に明示すべき事項をチェックリスト化します。

  1. ユーザーをテスターにしない: 「完了=検証を通過した」と定義し、データ検証・レンダリング検証・機能検証の3層を通ってから報告させる。発見したNGは報告前に自分で直させる
  2. 元データがあるなら推測させない: アンカー検出などの推測手法は、構造化データが存在しない場合の最終手段と明示する
  3. 位置だけでなくサイズも移植対象と明示する(width/height/scale)
  4. 型マッピング表を発注側が渡す。対応先がない型は「汎用型に丸めず、①標準型→②型の新規作成→③テキスト+ラベル引き継ぎ、の順で判断」と指示する
  5. 署名者(ロール)は移行元と同数・同名。「署名者は1名」のような前提を勝手に置かない・置かせない
  6. 検収条件を件数の一致にしない: 実PDF上のレンダリング比較と、実際の署名フロー1本の通し確認まで完了条件に入れる
  7. 実行アカウントの権限とレコードロック(Odooなら has_sign_requests)を事前確認させる
  8. テストの副作用を管理させる: メール送信を伴うテストは事前予告・事後報告・後始末(リクエスト削除)までをルール化する。OdooはORMの create() だけでメールを送ることがあります
  9. ロケール依存の表示(日付書式・言語)は、移行先の実装を検証してから型と設定を決める

正直な結論:この規模なら手動のほうが圧倒的に早い。それでも得たものはある

テンプレート6本であれば、DocuSignからPDFを落としてOdooにアップし、フィールドを手で置くほうが確実に早い(半日以内)です。API移行はDocuSignのGo Live審査だけで実働3〜4時間+審査待ち48時間、フィールド移植の品質を実用レベルに上げるまでの手戻りを含めるとさらにかかりました。

一方で、数十件以上の移行、複数アカウント、定期同期が視野にあるなら、この自動化パイプラインは一気に効きます。完成したスクリプトは冪等・自己修復型で、繰り返し実行に耐えます。そして何より、上の発注仕様書は次の移行で丸ごと再利用できます。判断の分かれ目は「テンプレート数」と「継続性」です。

DocuSign API本番化の全記録(Go Live審査の地雷マップ)はこちら:
👉 DocuSign API本番化(Go Live審査)の全記録|note

当社はOdooの公式パートナーとして、Signを含むOdooの導入設計・カスタマイズ・外部システムからの移行を支援しています。本記事のような移行の設計段階からのご相談はOdoo導入・開発・業務統合支援をご覧ください。

よくある質問

DocuSignからOdoo Signへの移行で一番大変なのは何ですか?

Odoo側の受け入れではなく、DocuSign側のAPI本番利用許可(Go Live審査)です。審査対応だけで実働3〜4時間+審査待ち48時間を要しました。Odoo側はXML-RPCの外部APIだけで完結し、SSHもカスタムモジュールも不要です。

署名フィールドは自動で移行できますか?

できますが、方法を誤ると使い物になりません。PDFの文字から位置を推測するアンカー検出は日本語契約書では構造的に機能せず、DocuSign側にTabsとして存在する位置・サイズ・署名者・型の「正解データ」を取得して再構築するのが正解です。それでもサイズ変換・ロール対応・型マッピングで手戻りが起きるため、実PDFへのオーバーレイ描画と実署名フローまで含めた検証を完了条件にすることを推奨します。

Odoo 19でテンプレートをAPI作成する際の注意点は?

Odoo 19ではsign.templateにattachment_idフィールドが無く、ir.attachment→sign.document→sign.templateの3段構造で作成する必要があります(旧バージョン向けのサンプルは通用しません)。バージョン差異のあるモデルはコードを書く前にfields_getでスキーマを実測するのが確実です。

テンプレート数が少なくても自動化すべきですか?

6本程度なら手動移行(半日以内)のほうが早いというのが実測に基づく結論です。自動化が効くのは数十件以上の移行、複数アカウント、定期的な同期が視野にある場合で、判断の分かれ目は「テンプレート数」と「継続性」です。

弊社ではOdoo(オドゥ)を利用した業務システムの一元管理・オールインワンな仕組みの構築・開発をして企業様をサポートしています。
ご興味ある方は是非お問い合わせください。

※注意

本ブログの投稿内容に関するお問い合わせをして頂いてもご返信できませんので、何卒ご了承ください。