| name | odoo_module_documentation |
| description | Guidelines for documenting Odoo modules with screenshots, user guides, and technical documentation. Use when creating module documentation. |
| version | 1.0.0 |
| author | VPCS Team |
| category | documentation |
| odoo_versions | ["17.0","18.0","19.0"] |
| tags | ["odoo","documentation","screenshots","user-guide","technical-docs"] |
Odoo Module Documentation & Screenshot Management Skill
Overview
Comprehensive skill for automating screenshot capture, icon/banner generation, and index.html documentation updates for Odoo custom modules. Integrates browser automation, image generation, and HTML templating for professional module documentation.
Core Components
1. Directory Structure
{module_name}/
โโโ static/
โ โโโ description/
โ โโโ index.html # Module documentation (auto-generated)
โ โโโ icon.png # 180x180 app icon (auto-generated)
โ โโโ banner.png # 1200x320 banner (auto-generated or screenshot)
โ โโโ banner.gif # Optional animated banner
โ โโโ 01_feature_name.png # Feature screenshots (numbered)
โ โโโ 02_feature_name.png
โ โโโ ...
โ โโโ company_logo.png # Company watermark/logo
โ โโโ README.md # Screenshot guide (optional)
2. File Requirements
Icon (icon.png)
- Size: 180x180 pixels
- Format: PNG with transparency
- Content: App logo/symbol representing module functionality
- Generation Methods:
- AI Image Generation (GitHub Copilot + DALL-E/Stable Diffusion)
- Screenshot crop from main UI
- Template-based icon with module name
Banner (banner.png)
- Size: 1200x320 pixels (Odoo standard)
- Format: PNG or GIF
- Content: Module branding with key features
- Generation Methods:
- AI Image Generation with text overlay
- Composite of multiple screenshots
- Design template with module info
Screenshots (01_.png, 02_.png, ...)
- Naming: Sequential numbering (01_, 02_, 03_...)
- Format: PNG
- Content: Live app testing images showing key features
- Capture Strategy:
- Main menu/dashboard
- List views with data
- Form views (create/edit)
- Settings/configuration
- Action results (reports, wizards)
- Special features (charts, kanban, calendar)
3. Screenshot Capture Workflow
This is the verified flow (agent-browser 0.35.x, Odoo 17/18/19, headless
Chrome via CDP). @e1, @e2 โฆ refs are assigned fresh by every
snapshot -i and go stale after any navigation โ re-snapshot each time.
Step 0: One-time setup + own session
npm i -g agent-browser && agent-browser install
export AGENT_BROWSER_SESSION="$(agent-browser session id --scope worktree --prefix docshots)"
AB(){ agent-browser --session "$AGENT_BROWSER_SESSION" "$@"; }
AB set viewport 1600 1000
Step 1: Resolve the Odoo URL + credentials
ODOO_PORT=$(grep '^xmlrpc_port' odoo.conf | tail -1 | awk '{print $NF}')
BASE="http://localhost:${ODOO_PORT}"; DB=$(grep '^db_name' odoo.conf | awk '{print $NF}')
PW="admin"
docker run -d --rm --name "${S}-bridge" --network "${S}_default" \
-p 127.0.0.1:8718:8718 alpine/socat \
-d -d TCP-LISTEN:8718,fork,reuseaddr TCP:"${S}-odoo-1":8069
BASE="http://127.0.0.1:8718"
DB=$(grep '^db_name' ".sandbox/sessions/${S}/config/odoo.conf" | awk '{print $NF}')
PW=$(grep '^ODOO_API_PASSWORD=' ".sandbox/sessions/${S}/runtime.env" | cut -d= -f2)
curl -s -o /dev/null -w 'health %{http_code}\n' "$BASE/web/health"
Step 2: Log in
AB open "$BASE/web/login"
AB snapshot -i
AB fill @e1 "admin"
AB fill @e2 "$PW"
AB click @e3
sleep 4
AB get url
Step 3: Navigate to the module (use the action xmlid, not web#menu_id)
AB open "$BASE/odoo/action-{module_name}.{action_xmlid}"
sleep 4
AB snapshot -i
AB errors
Step 4: Capture screenshots (sequential, numbered)
AB screenshot ./static/description/01_main_menu.png
AB snapshot -i
AB click @e14 ; sleep 3
AB screenshot ./static/description/02_form_view.png
AB click @e3 ; sleep 2
AB snapshot -i
AB fill @e13 "Demo Value"
AB upload "input[type=file]" ./demo.csv ; sleep 2
AB screenshot ./static/description/03_create_form.png
AB click @e12 ; sleep 3
AB snapshot -i | grep -iE 'notification|success|error'
AB screenshot ./static/description/04_action_result.png
Step 5: Cross-check server-side (don't trust pixels alone)
curl -s -c /tmp/ck -H 'Content-Type: application/json' \
-d "{\"jsonrpc\":\"2.0\",\"method\":\"call\",\"params\":{\"db\":\"$DB\",\"login\":\"admin\",\"password\":\"$PW\"}}" \
"$BASE/web/session/authenticate" >/dev/null
curl -s -b /tmp/ck -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"call","params":{"model":"my.model","method":"search_count","args":[[]],"kwargs":{}}}' \
"$BASE/web/dataset/call_kw"
Step 6: Tear down
AB close --all
docker rm -f "${S}-bridge" 2>/dev/null
rm -f /tmp/ck
Gotchas learned the hard way
wait --load networkidle never settles on Odoo (long-poll bus) โ use sleep 3-4.
- The Claude-in-Chrome extension stalls when proxied through the socat bridge;
agent-browser (direct CDP) does not โ always use agent-browser for sandbox sessions.
agent-browser screenshot <path> takes the viewport; there is no --full in 0.35.x.
- Never hard-code
admin/admin โ a sandbox session's password is random and lives
in .sandbox/sessions/<s>/runtime.env (ODOO_API_PASSWORD). .sandbox/ is
git-ignored; never copy that file or its values into tracked files, logs, or docs.
4. Icon & Banner Generation
Method 1: AI Image Generation (GitHub Copilot + Model)
from copilot import CopilotClient
async def generate_icon(module_name: str, description: str) -> str:
"""Generate app icon using AI."""
prompt = f"""
Create a professional app icon for an Odoo module:
- Module: {module_name}
- Description: {description}
- Style: Modern, flat design, Odoo purple (#875A7B) accent
- Size: 180x180 pixels
- Format: PNG with transparency
- Icon should be simple, recognizable, professional
"""
client = CopilotClient()
response = await client.generate_image(prompt=prompt, size="180x180")
return response.image_path
async def generate_banner(module_name: str, features: list) -> str:
"""Generate banner with module info."""
prompt = f"""
Create a professional banner for an Odoo module:
- Module: {module_name}
- Key Features: {', '.join(features[:3])}
- Style: Modern, gradient background, Odoo branding
- Size: 1200x320 pixels
- Include: Module icon, name, tagline, feature icons
- Colors: Odoo purple (#875A7B), white text, professional gradient
"""
client = CopilotClient()
response = await client.generate_image(prompt=prompt, size="1200x320")
return response.image_path
Method 2: Screenshot-Based Icon
AB open "$BASE/odoo/apps"
AB snapshot -i | grep -i "{module_title}"
AB screenshot @e<ref> ./icon_temp.png
convert ./icon_temp.png -resize 180x180 ./static/description/icon.png
Method 3: Template-Based Generation
from PIL import Image, ImageDraw, ImageFont
def create_icon_from_template(module_name: str, color: str = "#875A7B") -> str:
"""Create simple icon with module initials."""
img = Image.new('RGBA', (180, 180), (135, 90, 123, 255))
draw = ImageDraw.Draw(img)
initials = ''.join([word[0].upper() for word in module_name.split('_')[:3]])
font = ImageFont.truetype("/System/Library/Fonts/Helvetica.ttc", 60)
bbox = draw.textbbox((0, 0), initials, font=font)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
position = ((180 - text_width) / 2, (180 - text_height) / 2)
draw.text(position, initials, fill="white", font=font)
output_path = f"./static/description/icon.png"
img.save(output_path)
return output_path
5. index.html Template Generation
Template Structure
<section class="oe_container">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan" style="color:#875A7B;">How to access a trial on VPCS cloud platform</h2>
<h3 class="oe_slogan">Watch this video for a step-by-step guide on the trial process.<br/>You can try out the module on our cloud platform.</h3>
<a class="text-center" style="font-size: 20px; margin-left: 50%;" target="_blank" href="{trial_video_url}">Trial Guide</a>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan" style="color:#875A7B;">{module_title}</>
{module_description}
Key Features
{features_list}
Benefits
{benefits_list}
{for each screenshot in screenshots}
{screenshot.title}
{screenshot.description}
{end for}
{{COMPANY_TAGLINE}}
About us
Website |
Blog |
Contact us |
Request New Feature
Python Template Generator
def generate_index_html(module_data: dict) -> str:
"""Generate index.html from module data and screenshots."""
template = """
<section class="oe_container">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan" style="color:#875A7B;">How to access a trial on VPCS cloud platform</h2>
<h3 class="oe_slogan">Watch this video for a step-by-step guide on the trial process.<br/>You can try out the module on our cloud platform.</h3>
<a class="text-center" style="font-size: 20px; margin-left: 50%;" target="_blank" href="{trial_url}">Trial Guide</a>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan" style="color:#875A7B;">{module_title}</h2>
<h3 class="oe_slogan">{module_description}</h3>
<div style="margin-left: 20%; width: 60%; font-size: 18px">
<h2>Key Features</h2>
<ul style="font-size: 15px;">
{features}
</ul>
</div>
</div>
</section>
"""
features_html = "\n".join([f" <li>{f}</li>" for f in module_data['features']])
screenshots_html = ""
for i, screenshot in enumerate(module_data['screenshots'], 1):
screenshots_html += f"""
<section class="oe_container">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan" style="color:#875A7B; margin-top: 100px;">{screenshot['title']}</h2>
<h3 class="oe_slogan">{screenshot['description']}</h3>
<div class="oe_demo oe_picture oe_screenshot mx-auto">
<img src="{screenshot['filename']}" style="max-width: 100%; height: auto;">
</div>
</div>
</section>
"""
company_name = module_data.get(, )
company_url = module_data.get(, )
company_email = module_data.get(, )
company_tagline = module_data.get(, )
footer_html =
html = template.(
trial_url=module_data.get(, ),
module_title=module_data[],
module_description=module_data[],
features=features_html
)
html += screenshots_html + footer_html
html
6. Complete Automation Workflow
Task Completion Documentation Update
async def update_module_documentation(
module_name: str,
module_path: str,
base_url: str,
odoo_password: str,
action_xmlid: str,
progress_data: dict = None,
) -> dict:
"""
Complete documentation update on task completion.
Args:
module_name: Technical name of the module
module_path: Path to module directory
base_url: Odoo base URL resolved in Step 1 (local port or sandbox bridge)
odoo_password: admin password resolved in Step 1 (never a literal)
action_xmlid: xmlid of the module's main act_window
progress_data: Progress JSON with completed tasks
Returns:
dict: Summary of generated files
"""
import os
from pathlib import Path
desc_path = Path(module_path) / "static" / "description"
desc_path.mkdir(parents=True, exist_ok=True)
manifest = read_manifest(module_path)
module_title = manifest.get('summary', module_name.replace('_', ' ').title())
module_description = manifest.get('description', '')
print("๐จ Generating icon.png...")
icon_path = await generate_icon(module_name, module_description)
()
screenshots = capture_module_screenshots(
module_name=module_name,
base_url=base_url,
odoo_password=odoo_password,
action_xmlid=action_xmlid,
output_dir=(desc_path),
progress_data=progress_data,
)
()
banner_path = generate_banner(
module_name=module_title,
features=[t[] t progress_data.get(, [])[:]]
)
()
module_data = {
: module_title,
: module_description,
: [task[] task progress_data.get(, [])],
: screenshots,
:
}
html_content = generate_index_html(module_data)
html_path = desc_path /
html_path.write_text(html_content)
logo_src = Path(__file__).parent / /
logo_dst = desc_path /
logo_src.exists() logo_dst.exists():
shutil
shutil.copy(logo_src, logo_dst)
{
: (desc_path / ),
: (desc_path / ),
: [s[] s screenshots],
: (html_path),
: (screenshots) +
}
() -> :
screenshots = []
sess = os.environ[]
ab =
run_bash()
run_bash()
run_bash()
run_bash()
run_bash()
run_bash()
run_bash()
run_bash()
run_bash()
screenshot_num =
filename =
run_bash()
screenshots.append({
: filename,
: ,
:
})
task progress_data.get(, []):
task.get() == :
screenshot_num +=
filename =
run_bash()
run_bash()
task[].lower() task[].lower():
run_bash()
run_bash()
run_bash()
screenshots.append({
: filename,
: task[],
: task.get(, )
})
run_bash()
screenshots
() -> :
words = task_title.lower().split()
words:
idx = words.index()
idx > :
words[idx - ]
() -> :
subprocess
result = subprocess.run(command, shell=, capture_output=, text=)
{: result.stdout, : result.stderr, : result.returncode}
7. Integration with Progress Tracking
Update progress JSON to trigger documentation
{
"module_name": "car_sales",
"status": "done",
"tasks": [
{
"id": "task_1",
"title": "Implement Car Registration Model",
"status": "done",
"screenshot": "02_car_registration_list.png"
},
{
"id": "task_2",
"title": "Create Car Form View",
"status": "done",
"screenshot": "03_car_form_view.png"
}
],
"documentation": {
"icon_generated": true,
Trigger in Agent Loop
if all(task['status'] == 'done' for task in progress_data['tasks']):
print("\nโ
All tasks complete. Generating documentation...")
doc_result = await update_module_documentation(
module_name=progress_data['module_name'],
module_path=config['custom_addons'] + '/' + progress_data['module_name'],
base_url=base_url,
odoo_password=odoo_password,
action_xmlid=progress_data['action_xmlid'],
progress_data=progress_data,
)
progress_data['documentation'] = {
'icon_generated': True,
'banner_generated': True,
'screenshots_captured': len(doc_result['screenshots']),
'index_html_updated': True,
'last_update': datetime.now().isoformat()
}
print(f"๐ธ Documentation complete: {doc_result['total_files']} files generated")
Best Practices
Screenshot Quality
- Set the viewport once (
AB set viewport 1600 1000) and keep every capture at it
- Re-snapshot after every navigation โ refs are stale otherwise
AB errors must be empty before you accept a screenshot as passing
- Clean Data: use demo data that's readable and professional
- Cross-check with JSON-RPC (Step 5) โ a screenshot proves rendering, not that the action worked
Icon/Banner Design
- Branding Consistency: Use Odoo purple (#875A7B) as primary color
- Simple Symbols: Icons should be recognizable at small sizes
- Professional: Avoid clipart, use modern flat design
- Readable Text: Banner text should be large enough to read in app store
index.html Structure
- Trial Link First: Always include trial/demo access prominently
- Features Before Screenshots: Explain before showing
- Progressive Detail: Start with overview, drill down to specifics
- Call to Action: End with company info and contact links
Automation Timing
- Task Completion: Generate docs when all tasks marked 'done'
- Manual Trigger: Provide option to regenerate docs anytime
- Incremental Updates: Allow adding screenshots without regenerating all
- Version Control: Commit generated files to git for tracking
Error Handling
try:
doc_result = await update_module_documentation(...)
except Exception as e:
print(f"โ ๏ธ Screenshot capture failed: {e}")
icon_path = create_icon_from_template(module_name)
html = generate_index_html({**module_data, "screenshots": []})
Common concrete failures and fixes:
| Symptom | Cause | Fix |
|---|
agent-browser: command not found | not installed | npm i -g agent-browser && agent-browser install |
stuck on /web/login after submit | wrong password | sandbox: read ODOO_API_PASSWORD from runtime.env, don't assume admin |
blank page after open .../action-... | client still booting / JS error | sleep longer, then AB errors; if errors, the module has a real frontend bug |
health 000 / connection refused | sandbox has no published port | start the alpine/socat bridge (Step 1) |
Node is not a file input element on upload @eN | Odoo binary widget ref is a <label> | AB upload "input[type=file]" <path> |
Summary
This skill enables:
- โ
Screenshot capture via
agent-browser (headless Chrome/CDP) โ the single
browser path for local Odoo and Docker Sandbox sessions (via a socat bridge)
- โ
Server-side JSON-RPC cross-checks so a passing screenshot also means the action worked
- โ
AI / screenshot / template icon + banner generation
- โ
Professional
index.html generation with configurable branding
- โ
Secret hygiene: session passwords are read from git-ignored
.sandbox/ at
run time and never written into tracked files, logs, or the generated docs
- โ
Version-specific capture (Odoo 17/18/19), progress-tracking integration
Use this skill when: module development or migration is complete and ready
for documentation, or when refreshing existing docs with new features.