├── .gitignore ├── custom-addons └── owl │ ├── __init__.py │ ├── __manifest__.py │ ├── controllers │ ├── __init__.py │ ├── res_partner.py │ └── simple_mail_service.py │ ├── models │ ├── __init__.py │ ├── product_product.py │ ├── res_parnter.py │ └── todo_list.py │ ├── security │ └── ir.model.access.csv │ ├── static │ └── src │ │ ├── components │ │ ├── dashboard │ │ │ ├── basic_services.js │ │ │ ├── dashboard.js │ │ │ ├── dashboard.xml │ │ │ └── dashboard_services.js │ │ ├── range_field │ │ │ ├── range_field.js │ │ │ └── range_field.xml │ │ ├── services │ │ │ ├── odoo_services.js │ │ │ └── odoo_services.xml │ │ ├── simple_mail_service │ │ │ ├── simple_mail.xml │ │ │ ├── simple_mail_container.js │ │ │ ├── simple_mail_service.js │ │ │ └── simple_mail_services.scss │ │ ├── todo_list │ │ │ ├── todo_list.js │ │ │ ├── todo_list.scss │ │ │ └── todo_list.xml │ │ ├── username_field │ │ │ ├── username_field.js │ │ │ ├── username_field.scss │ │ │ └── username_field.xml │ │ ├── valid_email_field │ │ │ ├── valid_email_field.js │ │ │ └── valid_email_field.xml │ │ └── veiw_inheritance │ │ │ ├── res_parnter_kanban_view.js │ │ │ ├── res_partner_form_view.js │ │ │ ├── res_partner_form_view.xml │ │ │ ├── res_partner_kanban_view.scss │ │ │ ├── res_partner_kanban_view.xml │ │ │ ├── res_partner_list_view.js │ │ │ └── res_partner_list_view.xml │ │ ├── img │ │ └── ajscript-media-logo.png │ │ └── pos │ │ ├── chrome.xml │ │ ├── favorite_products │ │ ├── favorite_products.js │ │ ├── favorite_products.scss │ │ └── favorite_products.xml │ │ ├── favorite_screen │ │ ├── buttons │ │ │ ├── favorite_screen_button │ │ │ │ ├── favorite_screen_button.js │ │ │ │ └── favorite_screen_button.xml │ │ │ └── message_popup_button │ │ │ │ ├── message_popup_button.js │ │ │ │ └── message_popup_button.xml │ │ ├── favorite_screen.js │ │ ├── favorite_screen.scss │ │ └── favorite_screen.xml │ │ ├── payment_screen │ │ ├── payment_screen.js │ │ └── payment_screen.xml │ │ ├── popup │ │ ├── message_popup.js │ │ └── message_popup.xml │ │ ├── pos.scss │ │ └── pos_global_state.js │ └── views │ ├── odoo_services.xml │ ├── res_partner.xml │ └── todo_list.xml └── odoo.conf /.gitignore: -------------------------------------------------------------------------------- 1 | # sphinx build directories 2 | _build/ 3 | 4 | # dotfiles 5 | .* 6 | !.gitignore 7 | !.github 8 | !.mailmap 9 | # compiled python files 10 | *.py[co] 11 | __pycache__/ 12 | # setup.py egg_info 13 | *.egg-info 14 | # emacs backup files 15 | *~ 16 | # hg stuff 17 | *.orig 18 | status 19 | # odoo filestore 20 | odoo/filestore 21 | # maintenance migration scripts 22 | odoo/addons/base/maintenance 23 | 24 | # generated for windows installer? 25 | install/win32/*.bat 26 | install/win32/meta.py 27 | 28 | # needed only when building for win32 29 | setup/win32/static/less/ 30 | setup/win32/static/wkhtmltopdf/ 31 | setup/win32/static/postgresql*.exe 32 | 33 | # js tooling 34 | node_modules 35 | jsconfig.json 36 | tsconfig.json 37 | package-lock.json 38 | package.json 39 | .husky 40 | 41 | # various virtualenv 42 | /bin/ 43 | /build/ 44 | /dist/ 45 | /include/ 46 | /lib/ 47 | /man/ 48 | /share/ 49 | /src/ 50 | -------------------------------------------------------------------------------- /custom-addons/owl/__init__.py: -------------------------------------------------------------------------------- 1 | from . import models 2 | from . import controllers 3 | -------------------------------------------------------------------------------- /custom-addons/owl/__manifest__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | { 3 | 'name' : 'OWL Tutorial', 4 | 'version' : '1.0', 5 | 'summary': 'OWL Tutorial', 6 | 'sequence': -1, 7 | 'description': """OWL Tutorial""", 8 | 'category': 'OWL', 9 | 'depends' : ['base', 'web', 'point_of_sale'], 10 | 'data': [ 11 | 'security/ir.model.access.csv', 12 | 'views/todo_list.xml', 13 | 'views/res_partner.xml', 14 | 'views/odoo_services.xml', 15 | ], 16 | 'demo': [ 17 | ], 18 | 'installable': True, 19 | 'application': True, 20 | 'assets': { 21 | 'web.assets_backend': [ 22 | 'owl/static/src/components/*/*.js', 23 | 'owl/static/src/components/*/*.xml', 24 | 'owl/static/src/components/*/*.scss', 25 | ], 26 | 'point_of_sale.assets': [ 27 | 'owl/static/src/pos/**/*.js', 28 | 'owl/static/src/pos/**/*.xml', 29 | 'owl/static/src/pos/**/*.scss', 30 | ] 31 | }, 32 | } -------------------------------------------------------------------------------- /custom-addons/owl/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | from . import res_partner 2 | from . import simple_mail_service 3 | -------------------------------------------------------------------------------- /custom-addons/owl/controllers/res_partner.py: -------------------------------------------------------------------------------- 1 | from odoo import http 2 | 3 | 4 | class ResPartner(http.Controller): 5 | @http.route('/owl/rpc_service', type='json', auth='user') 6 | def get_customers(self, limit): 7 | return http.request.env['res.partner'].search_read([], ['name', 'email'], limit=limit) 8 | 9 | @http.route('/owl/dashboard_service', type='json', auth='user') 10 | def dashboard_service(self): 11 | partner = http.request.env['res.partner'] 12 | return { 13 | "partners": partner.search_count([]), 14 | "customers": partner.search_count([('is_company', '=', True)]), 15 | "individuals": partner.search_count([('is_company', '=', False)]), 16 | "locations": len(partner.read_group([], ['state_id'], ['state_id'])), 17 | } 18 | -------------------------------------------------------------------------------- /custom-addons/owl/controllers/simple_mail_service.py: -------------------------------------------------------------------------------- 1 | from odoo import http 2 | 3 | 4 | class SimpleMailService(http.Controller): 5 | @http.route('/owl/simple_mail', type='json', auth='user') 6 | def send_simple_mail(self, **mail_data): 7 | mail = http.request.env['mail.mail'] 8 | new_email = mail.create({ 9 | 'email_from': mail_data['email_from'], 10 | 'email_to': mail_data['email_to'], 11 | 'subject': mail_data['subject'], 12 | 'body_html': mail_data['message'], 13 | }).send() 14 | 15 | if not new_email: 16 | return False 17 | 18 | return True 19 | -------------------------------------------------------------------------------- /custom-addons/owl/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import todo_list 2 | from . import res_parnter 3 | from . import product_product 4 | -------------------------------------------------------------------------------- /custom-addons/owl/models/product_product.py: -------------------------------------------------------------------------------- 1 | from odoo import models 2 | 3 | 4 | class ProductProduct(models.Model): 5 | _inherit = 'product.product' 6 | 7 | def getFavoriteProducts(self): 8 | products = self.search([('available_in_pos', '=', True), ('product_tag_ids.name', '=', 'Favorite')]) 9 | return products.ids 10 | 11 | def mark_favorite(self): 12 | print("self ==>", self) 13 | tags = self.env['product.tag'].search([('name', '=', 'Favorite')], limit=1) 14 | 15 | self.product_tag_ids = [(4, tags.id)] 16 | 17 | def mark_not_favorite(self): 18 | print("self ==>", self) 19 | tags = self.env['product.tag'].search([('name', '=', 'Favorite')], limit=1) 20 | 21 | self.product_tag_ids = [(3, tags.id)] 22 | -------------------------------------------------------------------------------- /custom-addons/owl/models/res_parnter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from odoo import api, fields, models 4 | 5 | 6 | class ResPartner(models.Model): 7 | _inherit = 'res.partner' 8 | 9 | username = fields.Char() 10 | expected_salary = fields.Integer() 11 | -------------------------------------------------------------------------------- /custom-addons/owl/models/todo_list.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from odoo import api, fields, models 3 | 4 | 5 | class OwlTodo(models.Model): 6 | _name = 'owl.todo.list' 7 | _description = 'OWL Todo List App' 8 | 9 | name = fields.Char(string="Task Name") 10 | completed = fields.Boolean() 11 | color = fields.Char() 12 | -------------------------------------------------------------------------------- /custom-addons/owl/security/ir.model.access.csv: -------------------------------------------------------------------------------- 1 | id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink 2 | access_owl_todo_list,owl_todo_list,model_owl_todo_list,base.group_user,1,1,1,1 -------------------------------------------------------------------------------- /custom-addons/owl/static/src/components/dashboard/basic_services.js: -------------------------------------------------------------------------------- 1 | /** @odoo-module */ 2 | 3 | import { registry } from "@web/core/registry" 4 | const { markup } = owl 5 | 6 | export const basicService = { 7 | start(){ 8 | 9 | function normalFunction(){ 10 | return "This is a normal function" 11 | } 12 | 13 | const arrowFunction = ()=>{ 14 | return "This is an arrow function" 15 | } 16 | 17 | return { 18 | string:"Basic Service", 19 | boolean:true, 20 | integer:1, 21 | float:0.5, 22 | array:[1,2,3], 23 | object:{"key": "value"}, 24 | "function": ()=>{ 25 | console.log("This function has been called") 26 | return "Service function has been called." 27 | }, 28 | normal_function: normalFunction(), 29 | arrow_function: arrowFunction(), 30 | html: markup(""), 31 | } 32 | } 33 | } 34 | 35 | registry.category('services').add("basicService", basicService) -------------------------------------------------------------------------------- /custom-addons/owl/static/src/components/dashboard/dashboard.js: -------------------------------------------------------------------------------- 1 | /** @odoo-module */ 2 | 3 | import { registry } from "@web/core/registry" 4 | import { Layout } from "@web/search/layout" 5 | import { getDefaultConfig } from "@web/views/view" 6 | import { useService } from "@web/core/utils/hooks" 7 | 8 | const { Component, useSubEnv, useState, onWillStart } = owl 9 | 10 | export class OwlDashboard extends Component { 11 | setup(){ 12 | console.log("Owl Odoo Dasboard") 13 | this.display = { 14 | controlPanel: {"top-right": false, "bottom-right": false} 15 | } 16 | 17 | useSubEnv({ 18 | config: { 19 | ...getDefaultConfig(), 20 | ...this.env.config, 21 | } 22 | }) 23 | 24 | this.dashboard_service = useService("owlDashboardService") 25 | console.log(this.dashboard_service) 26 | this.dashboard_data = useState(this.dashboard_service.dashboard_data) 27 | this.simple_mail = useService("simpleMailService") 28 | // this.dashboard_data = useState({}) 29 | 30 | // onWillStart(async ()=>{ 31 | // this.dashboard_data = await this.dashboard_service.getDashboardData() 32 | // console.log("dashboard data == ", this.dashboard_data) 33 | // }) 34 | } 35 | 36 | get owlBasicService(){ 37 | const basicService = this.env.services.basicService 38 | return basicService 39 | } 40 | 41 | openSimpleMail(){ 42 | this.simple_mail.open() 43 | } 44 | } 45 | 46 | OwlDashboard.template = "owl.Dashboard" 47 | OwlDashboard.components = { Layout } 48 | 49 | registry.category("actions").add("owl.Dashboard", OwlDashboard) -------------------------------------------------------------------------------- /custom-addons/owl/static/src/components/dashboard/dashboard.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 |
7 |

Basic Services

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
String
Boolean
Integer
Float
Array
Object
Function
Normal Function
Arrow Function
HTML
40 |
41 |
42 |

Dashboard Service

43 |
44 |
45 |
46 | 47 |
48 |
49 |

Partners

50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | 58 |
59 |
60 |

Customers

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | 69 |
70 |
71 |

Individuals

72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | 80 |
81 |
82 |

Locations

83 |
84 |
85 |
86 |
87 |
88 | 89 |
90 |

Simple Mail Service

91 | 92 |
93 |
94 |
95 |
96 |
-------------------------------------------------------------------------------- /custom-addons/owl/static/src/components/dashboard/dashboard_services.js: -------------------------------------------------------------------------------- 1 | /** @odoo-module */ 2 | 3 | import { registry } from "@web/core/registry" 4 | const { reactive } = owl 5 | 6 | export const owlDashboardService = { 7 | dependencies:["rpc"], 8 | async start(env, { rpc }){ 9 | let dashboard_data = reactive({}) 10 | 11 | Object.assign(dashboard_data, await rpc("/owl/dashboard_service/")) 12 | 13 | // setInterval(async ()=>{ 14 | // Object.assign(dashboard_data, await rpc("/owl/dashboard_service/")) 15 | // }, 5000) 16 | 17 | async function getDashboardData(){ 18 | return await rpc("/owl/dashboard_service/") 19 | } 20 | return { 21 | dashboard_data, 22 | getDashboardData: getDashboardData, 23 | } 24 | } 25 | } 26 | 27 | registry.category("services").add("owlDashboardService", owlDashboardService) -------------------------------------------------------------------------------- /custom-addons/owl/static/src/components/range_field/range_field.js: -------------------------------------------------------------------------------- 1 | /** @odoo-module **/ 2 | 3 | import { registry } from "@web/core/registry"; 4 | import { standardFieldProps } from "@web/views/fields/standard_field_props"; 5 | 6 | import { Component, useState, onWillUpdateProps } from "@odoo/owl"; 7 | 8 | export class RangeField extends Component { 9 | setup() { 10 | this.state = useState({ 11 | range: this.props.value || '', 12 | }); 13 | 14 | const { currency_id } = this.props.record.data 15 | this.currency = currency_id ? currency_id[1] : '' 16 | } 17 | } 18 | 19 | RangeField.template = "owl.RangeField"; 20 | RangeField.props = { 21 | ...standardFieldProps, 22 | }; 23 | 24 | RangeField.supportedTypes = ["integer"]; 25 | 26 | registry.category("fields").add("range", RangeField); 27 | -------------------------------------------------------------------------------- /custom-addons/owl/static/src/components/range_field/range_field.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 |
8 |
9 |
-------------------------------------------------------------------------------- /custom-addons/owl/static/src/components/services/odoo_services.js: -------------------------------------------------------------------------------- 1 | /** @odoo-module */ 2 | 3 | import { registry } from "@web/core/registry" 4 | import { Layout } from "@web/search/layout" 5 | import { getDefaultConfig } from "@web/views/view" 6 | import { useService } from "@web/core/utils/hooks" 7 | import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog" 8 | import { routeToUrl } from "@web/core/browser/router_service" 9 | import { browser } from "@web/core/browser/browser" 10 | 11 | const { Component, useSubEnv, useState } = owl 12 | 13 | export class OwlOdooServices extends Component { 14 | setup(){ 15 | console.log("Owl Odoo Services") 16 | this.display = { 17 | controlPanel: {"top-right": false, "bottom-right": false} 18 | } 19 | 20 | useSubEnv({ 21 | config: { 22 | ...getDefaultConfig(), 23 | ...this.env.config, 24 | } 25 | }) 26 | 27 | this.cookieService = useService("cookie") 28 | console.log(this.cookieService) 29 | 30 | if (this.cookieService.current.dark_theme == undefined){ 31 | this.cookieService.setCookie("dark_theme", false) 32 | } 33 | 34 | const router = this.env.services.router 35 | 36 | this.state = useState({ 37 | dark_theme: this.cookieService.current.dark_theme, 38 | get_http_data: [], 39 | post_http_data: [], 40 | rpc_data: [], 41 | orm_data: [], 42 | bg_success: router.current.search.bg_success, 43 | user_data: null, 44 | company_data: null, 45 | }) 46 | 47 | const titleService = useService("title") 48 | titleService.setParts({zopenerp: "AJScript", odoo: "Media", any:"Tutorials"}) 49 | console.log(titleService.getParts()) 50 | } 51 | 52 | showNotification(){ 53 | const notification = this.env.services.notification 54 | notification.add("This is a sample notification.", { 55 | title: "Odoo Notification Service", 56 | type: "info", //info, warning, danger, success 57 | sticky: true, 58 | className: "p-4", 59 | buttons: [ 60 | { 61 | name: "Notification Action", 62 | onClick: ()=>{ 63 | console.log("This is notification action") 64 | }, 65 | primary: true, 66 | }, 67 | { 68 | name: "Show me again", 69 | onClick: ()=>{ 70 | this.showNotification() 71 | }, 72 | primary: false, 73 | } 74 | ] 75 | }) 76 | } 77 | 78 | showDialog(){ 79 | const dialog = this.env.services.dialog 80 | dialog.add(ConfirmationDialog, { 81 | title: "Dialog Service", 82 | body: "Are you sure you want to continue this action?", 83 | confirm: ()=>{ 84 | console.log("Dialog Confirmed.") 85 | }, 86 | cancel: ()=>{ 87 | console.log("Dialog Cancelled") 88 | } 89 | }, { 90 | onClose: ()=> { 91 | console.log("Dialog service closed....") 92 | } 93 | }) 94 | console.log(dialog) 95 | } 96 | 97 | showEffect(){ 98 | const effect = this.env.services.effect 99 | console.log(effect) 100 | effect.add({ 101 | type: "rainbow_man", 102 | message: "This is an awesome odoo effect service." 103 | }) 104 | } 105 | 106 | setCookieService(){ 107 | if (this.cookieService.current.dark_theme == 'false'){ 108 | this.cookieService.setCookie("dark_theme", true) 109 | } else { 110 | this.cookieService.setCookie("dark_theme", false) 111 | } 112 | 113 | this.state.dark_theme = this.cookieService.current.dark_theme 114 | 115 | this.cookieService.deleteCookie("test") 116 | } 117 | 118 | async getHttpService(){ 119 | const http = this.env.services.http 120 | console.log(http) 121 | const data = await http.get('https://dummyjson.com/products') 122 | console.log(data) 123 | this.state.get_http_data = data.products 124 | } 125 | 126 | async postHttpService(){ 127 | const http = this.env.services.http 128 | console.log(http) 129 | const data = await http.post('https://dummyjson.com/products/add', {title: 'BMW Pencil',}) 130 | console.log(data) 131 | this.state.post_http_data = data 132 | } 133 | 134 | async getRpcService(){ 135 | const rpc = this.env.services.rpc 136 | const data = await rpc("/owl/rpc_service", {limit: 15}) 137 | console.log(data) 138 | this.state.rpc_data = data 139 | } 140 | 141 | async getOrmService(){ 142 | const orm = this.env.services.orm 143 | const data = await orm.searchRead("res.partner", [], ['name', 'email']) 144 | console.log(data) 145 | this.state.orm_data = data 146 | } 147 | 148 | getActionService(){ 149 | const action = this.env.services.action 150 | action.doAction({ 151 | type: "ir.actions.act_window", 152 | name: "Action Service", 153 | res_model: "res.partner", 154 | domain:[], 155 | context:{search_default_type_company: 1}, 156 | views:[ 157 | [false, "list"], 158 | [false, "form"], 159 | [false, "kanban"], 160 | ], 161 | view_mode:"list,form,kanban", 162 | target: "current" 163 | }) 164 | } 165 | 166 | getRouterService(){ 167 | const router = this.env.services.router 168 | console.log(router) 169 | let { search } = router.current 170 | search.bg_success = search.bg_success == "1" ? "0" : "1" 171 | console.log(router.current) 172 | browser.location.href = browser.location.origin + routeToUrl(router.current) 173 | } 174 | 175 | getUserService(){ 176 | const user = this.env.services.user 177 | console.log(user) 178 | this.state.user_data = JSON.stringify(user) 179 | } 180 | 181 | getCompanyService(){ 182 | const company = this.env.services.company 183 | console.log(company) 184 | this.state.company_data = JSON.stringify(company) 185 | } 186 | } 187 | 188 | OwlOdooServices.template = "owl.OdooServices" 189 | OwlOdooServices.components = { Layout } 190 | 191 | registry.category("actions").add("owl.OdooServices", OwlOdooServices) -------------------------------------------------------------------------------- /custom-addons/owl/static/src/components/services/odoo_services.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |
23 |
24 |
25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 |
52 | 53 |
54 |
55 |
56 | 57 |
58 |
59 |
60 |
61 |
62 | 63 |
64 | 65 | -------------------------------------------------------------------------------- /custom-addons/owl/static/src/components/simple_mail_service/simple_mail.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 |
7 |

Simple Mail Service

8 |
9 | 12 | 13 |
14 |
15 | 18 | 19 |
20 |
21 | 24 | 25 |
26 |
27 | 30 |