電子署名サービスを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エージェントに移行を任せるときの発注仕様 — に割いています。
移行パイプラインは次の通りです。
接続は外部API(XML-RPC)のみで、Odoo.shへのSSHもカスタムモジュールも不要でした。スクリプトはすべてClaude Codeに書かせ、人間の作業は認証情報の準備と承認、そして受け入れテストです。
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として全部存在していたのです。よく調べもせずにすすめた当然の結果でした。
移行元に構造化データがあるなら、推測せず取得する。
アンカー検出のような推測手法は、元データが本当に存在しない場合の最終手段です。
当たり前のことですが、あるなら使いましょう。
XML-RPCで authenticate し、Signモジュールの導入状態と既存テンプレートを確認します。
common = xmlrpc.client.ServerProxy(f"{URL}/xmlrpc/2/common")
uid = common.authenticate(DB, LOGIN, API_KEY, {}) 認証はログインパスワードではなくAPIキー(マイプロフィール → アカウントセキュリティ)を使います。
ここがOdoo 19最大の仕様ポイントです。
Odoo 19では sign.template に attachment_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経由の作成でもこの自動生成分が混ざるため、移行スクリプトは「自動生成分を掃除してから正しい定義で作る」設計にする必要がありました。
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 はマスタデータなので自作できます。最終的なマッピング方針はこうです:
日付: 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)の「印鑑」型を自作して付け替えることで解決しています。
3回の手戻りを経て、検証も3層に強化しました。
1. データ検証: DocuSign座標→Odoo保存値の突き合わせ(全48件、許容誤差ページ比±1%)
2. レンダリング検証: 実PDFにフィールド枠をオーバーレイ描画し、下線・空欄に乗っているかを確認。座標値が一致していても、視覚目標に乗っているかは別問題です(実際、初期の数値検証はここを見ておらず「PASSなのにズレて見える」状態を見逃しました)。
3. 機能検証: テスト署名リクエストを実際に送り、署名画面で全フィールドが入力可能なことを確認(Odooでは署名リクエストが1件でも紐づくとテンプレートが凍結・編集不可になる has_sign_requests の仕様があるため、テスト後のリクエスト削除まで含めて後始末)。
テンプレートが入っても、実運用には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(Claude Code)が書きましたが、手戻りの原因はAIの能力ではなくこちらの発注(プロンプト)の物足りなさでした。
同じことをする方のために、発注時に明示すべき事項をチェックリスト化します。
テンプレート6本であれば、DocuSignからPDFを落としてOdooにアップし、フィールドを手で置くほうが確実に早い(半日以内)です。API移行はDocuSignのGo Live審査だけで実働3〜4時間+審査待ち48時間、フィールド移植の品質を実用レベルに上げるまでの手戻りを含めるとさらにかかりました。
一方で、数十件以上の移行、複数アカウント、定期同期が視野にあるなら、この自動化パイプラインは一気に効きます。完成したスクリプトは冪等・自己修復型で、繰り返し実行に耐えます。そして何より、上の発注仕様書は次の移行で丸ごと再利用できます。判断の分かれ目は「テンプレート数」と「継続性」です。
DocuSign API本番化の全記録(Go Live審査の地雷マップ)はこちら:
👉 DocuSign API本番化(Go Live審査)の全記録|note
当社はOdooの公式パートナーとして、Signを含むOdooの導入設計・カスタマイズ・外部システムからの移行を支援しています。本記事のような移行の設計段階からのご相談はOdoo導入・開発・業務統合支援をご覧ください。
Odoo側の受け入れではなく、DocuSign側のAPI本番利用許可(Go Live審査)です。審査対応だけで実働3〜4時間+審査待ち48時間を要しました。Odoo側はXML-RPCの外部APIだけで完結し、SSHもカスタムモジュールも不要です。
できますが、方法を誤ると使い物になりません。PDFの文字から位置を推測するアンカー検出は日本語契約書では構造的に機能せず、DocuSign側にTabsとして存在する位置・サイズ・署名者・型の「正解データ」を取得して再構築するのが正解です。それでもサイズ変換・ロール対応・型マッピングで手戻りが起きるため、実PDFへのオーバーレイ描画と実署名フローまで含めた検証を完了条件にすることを推奨します。
Odoo 19ではsign.templateにattachment_idフィールドが無く、ir.attachment→sign.document→sign.templateの3段構造で作成する必要があります(旧バージョン向けのサンプルは通用しません)。バージョン差異のあるモデルはコードを書く前にfields_getでスキーマを実測するのが確実です。
6本程度なら手動移行(半日以内)のほうが早いというのが実測に基づく結論です。自動化が効くのは数十件以上の移行、複数アカウント、定期的な同期が視野にある場合で、判断の分かれ目は「テンプレート数」と「継続性」です。