Install
$ agentstack add skill-ahmed-lakosha-odoo-plugins-frontend-js ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Odoo Frontend JavaScript Patterns
Critical Rules
- Website themes: Use
publicWidgetframework ONLY — NOT Owl or vanilla JS - JS modules: Start every file with
/** @odoo-module **/ - No inline JS/CSS: Always separate files in
static/src/js/andstatic/src/scss/ - Bootstrap: v5.1.3 for Odoo 16+ (never Tailwind)
- Translations: Use
_t()at DEFINITION TIME for static JS labels
Version Detection
| Odoo | Bootstrap | Owl | JavaScript | |------|-----------|-----|------------| | 14 | 4.x | — | ES6+ | | 15 | 4.x | v1 | ES6+ | | 16 | 5.1.3 | v1 | ES2020+ | | 17 | 5.1.3 | v2 | ES2020+ | | 18-19 | 5.1.3 | v2 | ES2020+ |
Detect from path (odoo17/ → v17), manifest version field, or config file.
publicWidget Pattern (REQUIRED for Themes)
Use for: Website interactions, theme functionality, animations, forms
/** @odoo-module **/
import publicWidget from "@web/legacy/js/public/public_widget";
publicWidget.registry.MyWidget = publicWidget.Widget.extend({
selector: '.my-selector',
disabledInEditableMode: false, // Allow in website builder
events: {
'click .button': '_onClick',
'change input': '_onChange',
'submit form': '_onSubmit',
},
/**
* CRITICAL: Check editableMode for website builder compatibility
*/
start: function () {
if (!this.editableMode) {
this._initializeAnimation();
this._bindExternalEvents();
}
return this._super.apply(this, arguments);
},
_initializeAnimation: function () {
this.$el.addClass('animated');
},
_bindExternalEvents: function () {
$(window).on('scroll.myWidget', this._onScroll.bind(this));
$(window).on('resize.myWidget', this._onResize.bind(this));
},
_onClick: function (ev) {
ev.preventDefault();
if (this.editableMode) return;
// Handler logic
},
/**
* CRITICAL: Clean up event listeners to prevent memory leaks
*/
destroy: function () {
$(window).off('.myWidget'); // Remove namespaced events
this._super.apply(this, arguments);
},
});
export default publicWidget.registry.MyWidget;
Key Points
- ALWAYS check
this.editableModebefore animations/interactions disabledInEditableMode: falsemakes widgets work in website builder- ALWAYS clean up event listeners in
destroy() - NEVER use Owl or vanilla JS for website themes
- Use namespaced events (
.myWidget) for easy cleanup
Include in Manifest
'assets': {
'web.assets_frontend': [
'module_name/static/src/js/my_widget.js',
],
}
Owl Component Pattern
Odoo 17 (Owl v1)
/** @odoo-module **/
import { Component, useState } from "@odoo/owl";
import { registry } from "@web/core/registry";
class MyComponent extends Component {
setup() {
this.state = useState({ items: [], loading: false });
}
async willStart() {
await this.loadData();
}
}
MyComponent.template = "module_name.MyComponentTemplate";
registry.category("public_components").add("MyComponent", MyComponent);
Odoo 18-19 (Owl v2 — Breaking Changes)
/** @odoo-module **/
import { Component, useState } from "@odoo/owl";
class MyComponent extends Component {
static template = "module_name.MyComponentTemplate"; // Static property
static props = {
title: { type: String, optional: true },
items: { type: Array },
};
setup() {
this.state = useState({ selectedId: null });
}
}
XML Template
Translation (_t) Best Practices
CORRECT — Wrap at DEFINITION TIME
/** @odoo-module **/
import { _t } from "@web/core/l10n/translation";
// Static labels wrapped where defined
const MONTHS = [
{value: 1, short: _t("Jan"), full: _t("January")},
{value: 2, short: _t("Feb"), full: _t("February")},
// ...
];
const STATUS_LABELS = {
draft: _t("Draft"),
pending: _t("Pending"),
approved: _t("Approved"),
};
WRONG — Runtime wrappers DON'T WORK
// WRONG: Strings without _t() at definition
const MONTHS = [{value: 1, label: "Jan"}]; // NOT found by PO extractor!
// WRONG: Variable passed to _t() at runtime
translateLabel(key) {
return _t(key); // PO extractor can't find string literals
}
When to use _t()
| Use t() | Don't use t() | |----------|----------------| | Static labels in JS arrays/objects | Static text in XML templates (auto-translated) | | Error messages in JS constants | Dynamic variables passed at runtime | | User-facing strings defined in JS | Hardcoded strings in .xml files |
Bootstrap 4 → 5 Migration (Odoo 14/15 → 16+)
Class Replacements
| Bootstrap 4 | Bootstrap 5 | |-------------|-------------| | ml-* | ms-* (margin-start) | | mr-* | me-* (margin-end) | | pl-* | ps-* (padding-start) | | pr-* | pe-* (padding-end) | | text-left | text-start | | text-right | text-end | | float-left | float-start | | float-right | float-end | | form-group | mb-3 | | custom-select | form-select | | close | btn-close | | badge-* | bg-* | | font-weight-bold | fw-bold | | sr-only | visually-hidden | | no-gutters | g-0 |
Data Attributes
| Bootstrap 4 | Bootstrap 5 | |-------------|-------------| | data-toggle | data-bs-toggle | | data-target | data-bs-target | | data-dismiss | data-bs-dismiss |
Removed Classes (find alternatives)
form-inline→ Use grid/flex utilitiesjumbotron→ Recreate with utilitiesmedia→ Used-flexwith flex utilities
SCSS Bootstrap Overrides
File: static/src/scss/bootstrap_overridden.scss Bundle: web._assets_frontend_helpers
@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";
$spacer: 1rem !default;
$border-radius: 0.25rem !default;
$border-radius-lg: 0.5rem !default;
$box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15) !default;
Use !default flag on all overrides.
Version-Specific Notes
Odoo 17
- Owl v1: template as separate property
- Snippet registration: simple XPath
- Import:
@web/legacy/js/public/public_widget
Odoo 18-19
- Owl v2: static template, props validation
- Snippet groups required
- Website builder: plugin architecture (Odoo 19)
- Breaking:
type='json'→type='jsonrpc'in controllers
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: ahmed-lakosha
- Source: ahmed-lakosha/odoo-plugins
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.