diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..a14f254b877f28adbae9d417b4b95cd0d9aca6a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +# sphinx build directories +_build/ + +# dotfiles +.* +!.gitignore +!.github +!.mailmap +# compiled python files +*.py[co] +__pycache__/ +# setup.py egg_info +*.egg-info +# emacs backup files +*~ +# hg stuff +*.orig +status +# odoo filestore +odoo/filestore +# maintenance migration scripts +odoo/addons/base/maintenance + +# generated for windows installer? +install/win32/*.bat +install/win32/meta.py + +# needed only when building for win32 +setup/win32/static/less/ +setup/win32/static/wkhtmltopdf/ +setup/win32/static/postgresql*.exe + +# js tooling +node_modules +jsconfig.json +tsconfig.json +package-lock.json +package.json +.husky + +# various virtualenv +/bin/ +/build/ +/dist/ +/include/ +/lib/ +/man/ +/share/ +/src/ diff --git a/s_license/__init__.py b/s_license/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..09a4456971f06d44387f749bd18ff12a94e2bb93 --- /dev/null +++ b/s_license/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +from . import models +from . import controllers +from . import wizards diff --git a/s_license/__manifest__.py b/s_license/__manifest__.py new file mode 100644 index 0000000000000000000000000000000000000000..ddf00a90aaf5c86be4b847846e44b6bd80229cd0 --- /dev/null +++ b/s_license/__manifest__.py @@ -0,0 +1,22 @@ +{ + 'name': 'Custom License', + 'version': '1.0', + 'depends': ['base', 'mail', 'website'], + 'author': 'HngQun', + 'website': 'https://snine.vn/', + 'category': 'License Module', + 'description': """ + A module to demonstrate custom license in Odoo 16. + """, + 'data': [ + 'security/access_groups.xml', + 'security/ir.model.access.csv', + 'wizards/popup_wizards.xml', + 'views/license_views.xml', + 'views/license_register.xml', + 'views/license_menu.xml', + ], + 'license': 'LGPL-3', + 'installable': True, + 'application': True, +} diff --git a/s_license/controllers/__init__.py b/s_license/controllers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9e9b01988d5742109830d90b3c5608a653b37220 --- /dev/null +++ b/s_license/controllers/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import license_controller diff --git a/s_license/controllers/license_controller.py b/s_license/controllers/license_controller.py new file mode 100644 index 0000000000000000000000000000000000000000..07a1cf3fa109d77da14d7cec8f4448b3ff0ecc6d --- /dev/null +++ b/s_license/controllers/license_controller.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from odoo import http +from odoo.http import request +import json + +class LicenseController(http.Controller): + + @http.route('/viewlicense', type='http', auth='public', website=True) + def register_license_page(self, **kwargs): + return request.render('s_license.license_register') + + @http.route('/register_license', type='http', method=['POST'], auth='public', website=True) + def register_license_submit(self, **kwargs): + request.env['license.management'].sudo().create({ + 'name': kwargs.get('name'), + 'start_date': kwargs.get('start_date'), + 'end_date': kwargs.get('end_date'), + 'key': kwargs.get('uuid'), + 'note': '', + }) + return request.render('s_license.license_register') diff --git a/s_license/models/__init__.py b/s_license/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c531a69c327ff3160725f5c7054711644f05cfe0 --- /dev/null +++ b/s_license/models/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import license diff --git a/s_license/models/license.py b/s_license/models/license.py new file mode 100644 index 0000000000000000000000000000000000000000..984f1d49d817ad69f96fc1b5fad6947302ac1901 --- /dev/null +++ b/s_license/models/license.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +from odoo import api, models, fields, _ +from odoo.exceptions import UserError, ValidationError +from datetime import datetime + +class LicenseManager(models.Model): + _name = 'license.management' + _description = 'License Management' + _inherit = ['mail.thread', 'mail.activity.mixin'] + + name = fields.Char(string='Name', required=True, tracking=True) + start_date = fields.Date(string='Start Date', required=True, tracking=True) + end_date = fields.Date(string='End Date', required=True, tracking=True) + key = fields.Char(string='Key', required=True, tracking=True) + note = fields.Text(string='Note') + status = fields.Selection([ + ('pending', 'Pending'), + ('active', 'Active'), + ('deactivate', 'Deactivate'), + ('cancelled', 'Cancelled'), + ], string='Status', default='pending', tracking=True, readonly=True) + + def action_show_popup(self, action): + return { + 'name': _('Reason'), + 'res_model': 'license.wizard', + 'view_mode': 'form', + 'target': 'new', + 'type': 'ir.actions.act_window', + 'context': { + 'default_state': action, + 'active_id': self.id, + } + } + + @api.constrains('name', 'start_date', 'end_date', 'key') + def check_status(self): + for rec in self: + if rec.status != 'pending': + rec.status = 'pending' + + def action_active(self): + return self.action_show_popup('active') + + def action_cancel(self): + return self.action_show_popup('cancelled') + + def action_deactivate(self): + return self.action_show_popup('deactivate') diff --git a/s_license/security/access_groups.xml b/s_license/security/access_groups.xml new file mode 100644 index 0000000000000000000000000000000000000000..7605035eadde4b32c21995a4e1df24f57ffc92cc --- /dev/null +++ b/s_license/security/access_groups.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <record model="ir.module.category" id="ls_category"> + <field name="name">Security for license</field> + <field name="sequence">45</field> + </record> + + <record id="group_license" model="res.groups"> + <field name="name">User</field> + <field name="category_id" ref="s_license.ls_category"/> + </record> + +</odoo> diff --git a/s_license/security/ir.model.access.csv b/s_license/security/ir.model.access.csv new file mode 100644 index 0000000000000000000000000000000000000000..d34ca02b11fd86d7fb010b1a61d1ccd1ff7e30a6 --- /dev/null +++ b/s_license/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_license,access_license,model_license_management,group_license,1,1,1,1 +access_license_wizard,access_license_wizard,model_license_wizard,,1,1,1,1 diff --git a/s_license/views/license_menu.xml b/s_license/views/license_menu.xml new file mode 100644 index 0000000000000000000000000000000000000000..da23bae18b0b7f88bc798efd870153780a8505ba --- /dev/null +++ b/s_license/views/license_menu.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8" ?> +<odoo> + <menuitem id="menu_license_root" name="Licenses" sequence="10"/> + <menuitem id="menu_license" name="Licenses" parent="menu_license_root" action="action_license" sequence="10"/> +</odoo> diff --git a/s_license/views/license_register.xml b/s_license/views/license_register.xml new file mode 100644 index 0000000000000000000000000000000000000000..f93716b9a47b6a44326ba97a0923308f02696e35 --- /dev/null +++ b/s_license/views/license_register.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<odoo> + <template id="license_register" name="License Template"> + <t t-call="website.layout"> + <div class="container mt16 mb16"> + <h1>Register License</h1> + <br/> + <form id="register_form" action="/register_license" method="post"> + <input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" /> + + <div class="form-group"> + <label for="name">Name:</label> + <input type="text" class="form-control" id="name" name="name"/> + </div> + + <div class="form-group"> + <label for="uuid">UUID:</label> + <input type="text" class="form-control" id="uuid" name="uuid" required="required"/> + </div> + + <div class="form-group"> + <label for="date_start">Start Date:</label> + <input type="date" class="form-control" id="start_date" name="start_date" required="required"/> + </div> + + <div class="form-group"> + <label for="date_end">End Date:</label> + <input type="date" class="form-control" id="end_date" name="end_date" required="required"/> + </div> + <br/> + <button type="submit" class="btn btn-primary">Register</button> + </form> + </div> + </t> + </template> +</odoo> diff --git a/s_license/views/license_views.xml b/s_license/views/license_views.xml new file mode 100644 index 0000000000000000000000000000000000000000..b98d1ab7fc6d34f3b03c02c0dfd51b41ba9b6ed6 --- /dev/null +++ b/s_license/views/license_views.xml @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="UTF-8"?> +<odoo> + <data> + <record id="view_license_form" model="ir.ui.view"> + <field name="name">license.management.form</field> + <field name="model">license.management</field> + <field name="arch" type="xml"> + <form string="License"> + <header> + + <button name="action_active" string="Confirm" + attrs="{'invisible': [('status', 'in', ['active','deactivate','cancelled'])]}" + type="object" class="oe_highlight"/> + + <button name="action_cancel" string="Cancel" + attrs="{'invisible': [('status', 'in', ['active','cancelled','deactivate'])]}" + type="object" class="oe_highlight"/> + + <button name="action_deactivate" string="Deactivate" + attrs="{'invisible': [('status', 'in', ['pending','cancelled','deactivate'])]}" + type="object" class="oe_highlight"/> + + <field name="status" widget="statusbar" + statusbar_visible="pending, active, cancelled, deactivate" + class="btn btn_primary"/> + + </header> + + <sheet> + <group> + <group> + <field name="name"/> + <field name="start_date"/> + <field name="end_date"/> + </group> + <group> + <field name="key"/> + <field name="note"/> + </group> + </group> + </sheet> + + <notebook> + <page name="public" string="Work Information"> + + </page> + <page name="public" string="Demo ĂŒnormation"> + + </page> + </notebook> + + <div class="oe_chatter"> + <field name="message_follower_ids" groups="base.group_user" options="{'post_refresh': 'recipients'}"/> + <field name="activity_ids"/> + <field name="message_ids"/> + </div> + </form> + </field> + </record> + + <record id="view_license_search" model="ir.ui.view"> + <field name="name">license.management.search</field> + <field name="model">license.management</field> + <field eval="10" name="priority"/> + <field name="arch" type="xml"> + <search string="Student Search"> + <field name="name"/> + <field name="start_date"/> + <field name="end_date"/> + <field name="key"/> + <field name="note"/> + <field name="status"/> + + <group expand='0' string='Filters'> + <separator/> + <filter name="pending_filter" string="pending" domain="[('status', '=', 'pending')]"/> + <filter name="cancelled_filter" string="cancelled" domain="[('status', '=', 'cancelled')]"/> + <filter name="active_filter" string="active" domain="[('status', '=', 'active')]"/> + <filter name="inactive_filter" string="inactive" domain="[('status', '=', 'inactive')]"/> + </group> + + <group expand='0' string='Group by...'> + <filter string='Status' name="status_group" context="{'group_by': 'status'}"/> + </group> + </search> + </field> + </record> + + <record id="view_license_tree" model="ir.ui.view"> + <field name="name">license.management.tree</field> + <field name="model">license.management</field> + <field name="arch" type="xml"> + <tree string="License"> + <field name="name"/> + <field name="start_date"/> + <field name="end_date"/> + <field name="key"/> + <field name="status"/> + </tree> + </field> + </record> + + <record id="view_license_kanban" model="ir.ui.view"> + <field name="name">license.management.kanban</field> + <field name="model">license.management</field> + <field name="arch" type="xml"> + <kanban default_group_by="status"> + <field name="name"/> + <field name="start_date"/> + <field name="end_date"/> + <field name="key"/> + <field name="status"/> + + <templates> + <t t-name="kanban-box"> + <div t-att-data-id="record.id" class="oe_kanban_card oe_kanban_global_click"> + <div class="oe_kanban_details"> + + <div class="o_kanban_record_title"> + <t t-esc="record.name.value" style="font-weight: bold; font-size: large;"/> + </div> + + <ul class="o_kanban_ul_details"> + <li t-if="record.start_date.value"> + <i class="fa fa-calendar-check-o" aria-hidden="true"></i> + <strong> Start Date:</strong> <t t-esc="record.start_date.value"/> + </li> + <li t-if="record.end_date.value"> + <i class="fa fa-calendar-times-o" aria-hidden="true"></i> + <strong> End Date:</strong> <t t-esc="record.end_date.value"/> + </li> + <li t-if="record.key.value"> + <i class="fa fa-key" aria-hidden="true"></i> + <strong> Key:</strong> <t t-esc="record.key.value"/> + </li> + </ul> + + <div class="o_kanban_badge" t-attf-class="o_kanban_badge_#{record.status.value}"> + <span t-esc="record.status.value"/> + </div> + </div> + </div> + </t> + </templates> + </kanban> + </field> + </record> + + <record id="action_license" model="ir.actions.act_window"> + <field name="name">Licenses</field> + <field name="res_model">license.management</field> + <field name="view_mode">tree,kanban,form</field> + <field name="view_id" ref="view_license_tree"></field> + </record> + </data> +</odoo> diff --git a/s_license/wizards/__init__.py b/s_license/wizards/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2fcecdcafd3b354a38bd23d1b18d4455c93947bd --- /dev/null +++ b/s_license/wizards/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import popup_wizard diff --git a/s_license/wizards/popup_wizard.py b/s_license/wizards/popup_wizard.py new file mode 100644 index 0000000000000000000000000000000000000000..f9df1ea4d2d3a05ef82d7e2291ebe2cdbe7c34f1 --- /dev/null +++ b/s_license/wizards/popup_wizard.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from odoo import models, fields, api, _ + +class LicensePopupWizard(models.TransientModel): + _name = 'license.wizard' + _description = 'License Wizard' + + reason = fields.Text(string='Reason', required=True) + + def action_confirm(self): + active_id = self.env.context.get('active_id') + if active_id: + license_record = self.env['license.management'].browse(active_id) + today_str = fields.Date.today().strftime('%d/%m/%Y') + license_record.write({ + 'status': self.env.context.get('default_state'), + 'note': f"{today_str} - {self.reason}\n{license_record.note or ''}" + }) + return {'type': 'ir.actions.act_window_close'} diff --git a/s_license/wizards/popup_wizards.xml b/s_license/wizards/popup_wizards.xml new file mode 100644 index 0000000000000000000000000000000000000000..54006758371806566dabf1750779f84c5e0d30a2 --- /dev/null +++ b/s_license/wizards/popup_wizards.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<odoo> + <record id="view_license_wizard_form" model="ir.ui.view"> + <field name="name">license.wizard.form</field> + <field name="model">license.wizard</field> + <field name="arch" type="xml"> + <form string="License Wizard"> + <group> + <field name="reason"/> + </group> + <footer> + <button string="Confirm" type="object" name="action_confirm" class="btn-primary"/> + <button string="Cancel" class="btn-secondary" special="cancel"/> + </footer> + </form> + </field> + </record> +</odoo>