Kanban pada Odoo mendukung semua tag HTML sehingga kita dapat merancang tampilan yang interaktif.
Tampilan kanban yang interaktif pada pembuatan modul Odoo adalah seperti berikut ini
- Start SSH file system


- Source code view asrama.hostel
<?xml version="1.0" encoding="utf-8"?> <odoo> <!-- asrama.hostel search view --> <record id="view_hostel_search_view" model="ir.ui.view"> <field name="name">Hostel Search</field> <field name="model">asrama.hostel</field> <field name="arch" type="xml"> <search> <field name="name" /> <field name="hostel_code" /> </search> </field> </record> <!-- asrama.hostel tree view --> <record id="View_hostel_tree_view" model="ir.ui.view"> <field name="name">asrama.hostel.view.tree</field> <field name="model">asrama.hostel</field> <field name="arch" type="xml"> <tree> <field name="name" /> <field name="hostel_code" /> <!-- Add your fields here --> </tree> </field> </record> <!-- asrama.hostel form view --> <record id="view_hostel_form_view" model="ir.ui.view"> <field name="name">asrama.hostel.view.form</field> <field name="model">asrama.hostel</field> <field name="arch" type="xml"> <form string="Hostel"> <sheet> <div class="oe_title"> <h3> <table> <tr> <td style="padding-right:10px;"> <field name="name" required="1" placeholder="Name" /> </td> <td style="padding-right:10px;"> <field name="hostel_code" placeholder="Code" /> </td> </tr> </table> </h3> </div> <field name="image" widget="image" class="oe_avatar"/> <group> <group> <label for="street" string="Address"/> <div class="o_address_format"> <field name="street" placeholder="Street..." class="o_address_street"/> <field name="street2" placeholder="Street 2..." class="o_address_street"/> <field name="city" placeholder="City" class="o_address_city"/> <field name="state_id" class="o_address_state" placeholder="State" options='{"no_open": True}'/> <field name="zip" placeholder="ZIP" class="o_address_zip"/> <field name="country_id" placeholder="Country" class="o_address_country" options='{"no_open": True, "no_create": True}'/> </div> </group> <group> <field name="phone" widget="phone"/> <field name="mobile" widget="phone"/> <field name="email" widget="email" context="{'gravatar_image': True}"/> </group> <group> <field name="hostel_floors"/> <field name="active"/> <field name="type"/> <field name="hostel_rating"/> <field name="other_info"/> </group> </group> <group> <field name="description"/> </group> </sheet> </form> </field> </record> <!-- asrama.hostel action window --> <record id="action_hostel" model="ir.actions.act_window"> <field name="name">Hostel</field> <field name="type">ir.actions.act_window</field> <field name="res_model">asrama.hostel</field> <field name="view_mode">tree,form</field> <field name="help" type="html"> <p class="oe_view_nocontent_create"> Membuat Hostel </p> </field> </record> <menuitem id="hostel_main_menu" name="Hostel" sequence="1"/> <menuitem id="hostel_type_menu" name="Hostel" parent="hostel_main_menu" action="action_hostel" groups="group_hostel_manager" sequence="10"/> <menuitem id="hostel_sub1" name="Menu-01" parent="hostel_main_menu" sequence="1"/> </odoo> - Source code model asrama.amenities
from odoo import fields, models class AsramaAmenities(models.Model): _name = 'asrama.amenities' _description = 'Asrama Amenities' name = fields.Char('Nama',help='Amenities yang disediakan') active = fields.Boolean( 'Active', default=True , help='Activate/Deactivated record' ) color = fields.Integer() - Source code akses keamanan
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_hostel_manager_id,access.hostel.manager,asramaku.model_asrama_hostel,group_hostel_manager,1,1,1,1 access_hostel_user_id,access.hostel.user,asramaku.model_asrama_hostel,group_hostel_user,1,0,0,0 access_hostel_room_manager_id,access.hostel.room.manager,asramaku.model_asrama_room,asramaku.group_hostel_manager,1,1,1,1 access_hostel_room_user_id,access.hostel.room.user,asramaku.model_asrama_room,asramaku.group_hostel_user,1,0,0,0 access_hostel_amenities_manager_id,access.hostel.amenities.manager,asramaku.model_asrama_amenities,asramaku.group_hostel_manager,1,1,1,1 access_hostel_amenities_user_id,access.hostel.amenities.user,asramaku.model_asrama_amenities,asramaku.group_hostel_user,1,0,0,0 access_hostel_student_manager_id,access.hostel.student.manager,asramaku.model_asrama_student,asramaku.group_hostel_manager,1,1,1,1 access_hostel_student_user_id,access.hostel.student.user,asramaku.model_asrama_student,asramaku.group_hostel_user,1,0,0,0 access_hostel_category_manager_id,access.hostel.category.manager,asramaku.model_asrama_category,asramaku.group_hostel_manager,1,1,1,1 access_hostel_category_user_id,access.hostel.category.user,asramaku.model_asrama_category,asramaku.group_hostel_user,1,0,0,0 access_hostel_assign_room_student_wizard_id,access.hostel.assign.room.student.wizard,asramaku.model_assign_room_student_wizard,asramaku.group_hostel_manager,1,1,1,1 access_hostel_asrama_room_availability_id,access.hostel.asrama.room.availability,asramaku.model_asrama_room_availability,asramaku.group_hostel_manager,1,1,1,1 access_hostel_room_stage,hostel_room_stage,model_asrama_room_stage,base.group_user,1,1,1,1
- Model asrama.room
import logging from odoo import fields, models, api from odoo.exceptions import ValidationError, UserError from odoo.tools.translate import _ _logger = logging.getLogger(__name__) class BaseArchive(models.AbstractModel): _name = 'base.archive' active = fields.Boolean(default=True) def do_archive(self): for record in self: record.active = not record.active class AsramaRoom(models.Model): _name = 'asrama.room' _inherit = ['base.archive'] _description = 'Informasi Kamar Asrama' _rec_name = 'room_no' name = fields.Char('Nama kamar',required=True) room_no = fields.Char('No kamar',required=True) floor_no = fields.Integer('No Lantai',default=1, help='No Lantai') currency_id = fields.Many2one('res.currency',string='Mata uang') rent_amount = fields.Monetary('Harga sewa',help='Masukkan harga sewa') hostel_id = fields.Many2one('asrama.hostel','Hostel', help='Nama hostel') student_per_room = fields.Integer('Jml pelajar per kamar',required=True, help='Jml yang dialokasikan per kamar') @api.depends('student_per_room','student_ids') def _compute_check_availability(self): """Mengecek ketersediaan""" for rec in self: rec.availability = rec.student_per_room - len (rec.student_ids.ids) availability = fields.Float(compute='_compute_check_availability',store=True, string='Ketersediaan',help='Ketersediaan kamar pada asrama') student_ids = fields.One2many('asrama.student','room_id','Daftar pelajar', help='Pelajar-pelajar dalam room ini') color = fields.Integer() popularity = fields.Selection([ ('no','No Demand'), ('low','Low Demand'), ('medium','Medium Demand'), ('high','High Demand'), ]) asrama_amenities_ids = fields.Many2many('asrama.amenities', 'asrama_amenities_rel','room_id','amenity_id', string='Amenities', domain="[('active','=',True)]", help='Pilih fasilitas room' ) _sql_constraints = [('room_no_unique','unique(room_no)','Nomor room harus unik!')] @api.constrains('rent_amount') def _check_rent_amount(self): """Constraint pada nilai negatif""" if self.rent_amount < 0: raise ValidationError(_('Nilai rental tidak boleh negatif')) room_rating = fields.Float('Rata-rata Rating dari Room',digits=(14,4)) ''' state = fields.Selection([ ('draft','Unavailable'), ('available','Available'), ('closed','Closed') ], 'State', default='draft' ) ''' remarks = fields.Text('Remarks') @api.model def create(self,values): if not self.user_has_groups('base.group_allow_export'): if values.get('remarks'): raise UserError('Anda tidak diijinkan membuat remark') return super(AsramaRoom, self).create(values) def write(self,values): if not self.user_has_groups('base.group_allow_export'): if values.get('remarks'): raise UserError('Anda tidak diijinkan mengubah remark') return super(AsramaRoom, self).write(values) def is_allowed_transition(self,old_state,new_state): allowed = [ ('draft','available'), ('available','closed'), ('closed','draft') ] return (old_state,new_state) in allowed def change_state(self,new_state): for room in self: if room.is_allowed_transition(room.state, new_state): room.state = new_state else: msg = _('Pindah dari %s ke %s tidak diperbolehkan!') % (room.state,new_state) raise UserError(msg) def make_available(self): self.change_state('available') def make_closed(self): self.change_state('closed') def log_all_room_members(self): asrama_student_obj = self.env['asrama.student'] all_members = asrama_student_obj.search([]) print('Semua anggota',all_members) return True def create_categories(self): categ1 = { 'name': 'Child category 1', 'description': 'Description for child 1' } categ2 = { 'name': 'Child category 2', 'description': 'Description child 2' } parent_category_val = { 'name': 'Parent category', 'description': 'Description for parent category', 'child_ids': [ (0,0,categ1), (0,0,categ2), ] } record = self.env['asrama.category'].create(parent_category_val) return True def update_room_no(self): self.ensure_one() self.room_no = 'RM2772' def find_room(self): domain = [ '|', '&',('name','ilike','bunga'), ('hostel_id.name','=','Kemayoran'), '&',('name','ilike','galo'), ('hostel_id.name','=','BSD') ] rooms = self.search(domain) #_logger.info('Room ditemukan %s',rooms) print('Room ditemukan %s',rooms) return True def filter_members(self): all_rooms = self.search([]) filtered_rooms = self.rooms_with_multiple_members(all_rooms) #_logger.info('Filtered Rooms: %s',filtered_rooms) print('Filtered Rooms:', filtered_rooms) def rooms_with_multiple_members(self, all_rooms): def predicate(room): if (len(room.student_ids))>1: return True return all_rooms.filtered(predicate) def mapped_rooms(self): all_rooms = self.search([]) room_authors = self.get_member_names(all_rooms) #_logger.info('Room Members: %s', room_authors) print('Room Members:', room_authors) def get_member_names(self,all_rooms): return all_rooms.mapped('student_ids.name') def sort_room(self): all_rooms = self.search([]) rooms_sorted = self.sort_by_rent_amount(all_rooms) #_logger.info('Rooms before sorting: %s', all_rooms) #_logger.info('Rooms after sorting: %s', rooms_sorted) print('Rooms before sorting:', all_rooms) print('Rooms after sorting:', rooms_sorted) def sort_by_rent_amount(self,all_rooms): return all_rooms.sorted(key='rent_amount') def average_rent(self): avg_result = self.read_group( [('rent_amount', '!=', False)], ['category_id','rent_amount:avg'], ['category_id'] ) print('Rata-rata harga sewa %s' % (avg_result)) return True def action_remove_room_members(self): for student in self.student_ids: student.with_context(is_hostel_room=True).action_remove_room() def action_category_with_amounts(self): sql = 'SELECT name, amount FROM asrama_category WHERE id = %s' % self.category_id.id print('SQL:', sql) self.env.cr.execute(sql) result = self.env.cr.fetchall() print('Result:', result) def _default_room_stage(self): Stage = self.env['asrama.room.stage'] return Stage.search([], limit=1) def _group_expand_stages(self,stages,domain,order): return stages.search([],order=order) stage_id = fields.Many2one( 'asrama.room.stage', default=_default_room_stage, group_expand ='_group_expand_stages' ) - View asrama_amenities
<?xml version="1.0" encoding="utf-8"?> <odoo> <!-- asrama.amenities action window --> <record id="asrama_amenities_action" model="ir.actions.act_window"> <field name="name">Asrama Amenities</field> <field name="res_model">asrama.amenities</field> <field name="view_mode">tree,form</field> </record> <menuitem id="asrama_amenities_menu" name="Asrama Amenities" action="asrama_amenities_action" groups="asramaku.group_hostel_manager" parent="hostel_sub1" sequence="20"/> </odoo>
- View asrama_room
<?xml version='1.0' encoding='utf-8'?> <odoo> <!-- asrama.room tree view --> <record id="asrama_room_view_tree" model="ir.ui.view"> <field name="name">asrama.room.view.tree</field> <field name="model">asrama.room</field> <field name="arch" type="xml"> <tree string='Room'> <field name="name"/> <field name="room_no" /> <field name="floor_no" /> <field name="stage_id" /> </tree> </field> </record> <!-- asrama.room form view --> <record id="asrama_room_view_form" model="ir.ui.view"> <field name="name">asrama.room.view.form</field> <field name="model">asrama.room</field> <field name="arch" type="xml"> <form string="Room"> <sheet> <header> <field name="stage_id" widget='statusbar' options="{'clickable': '1', 'fold_field':'fold'}" /> </header> <header> <button name="make_available" string="Make Available" type="object"/> <button name="make_closed" string="Make Closed" type="object"/> <button name="log_all_room_members" string="Log semua kamar members" type="object"/> <button name="average_rent" string="Rata-rata sewa" type="object"/> </header> <header> <button name="create_categories" string="Create categories" type="object"/> <button name="update_room_no" string="Update Room No" type="object"/> <button name="find_room" string="Find Room" type="object"/> <button name="action_remove_room_members" string="Remove Room Members" type="object"/> </header> <header> <button name="filter_members" string="Filter Members" type="object"/> <button name="mapped_rooms" string="Mapped Rooms" type="object"/> <button name="sort_room" string="Sort Room" type="object"/> <button name="action_category_with_amounts" string="LOG dengan SQL Query" type="object"/> </header> <group> <group description='Kamar'> <field name="name" /> <field name="room_no" /> <field name="hostel_id" required="1"/> <field name="floor_no" /> <field name="active" /> <field name="category_id" /> <field name="date_terminate" /> </group> <group description='Harga'> <field name="student_per_room"/> <field name="availability"/> <field name="rent_amount" /> <field name="currency_id" /> <field name="remarks" /> </group> </group> <notebook> <page name="room_studennt" string="Para Pelajar"> <field name="student_ids"/> </page> <page name="asrama_amenities" string="Room Amenities"> <group> <field name="asrama_amenities_ids" widget="many2many_tags"/> </group> </page> </notebook> </sheet> </form> </field> </record> <!-- asrama.room minimal form view --> <record id="asrama_room_view_form_minimal" model="ir.ui.view"> <field name="name">Asrama Room Minimal</field> <field name="model">asrama.room</field> <field name="arch" type="xml"> <form> <group> <field name="name" /> <field name="room_no" /> <field name="hostel_id" required="1" /> <field name="floor_no" /> <field name="student_per_room" /> <field name="asrama_amenities_ids" widget="many2many_tags" options="{'color_field': 'color','no_create_edit': True}" /> </group> </form> </field> </record> <!-- asrama.room kanban view --> <record id="asrama_room_view_kanban" model="ir.ui.view"> <field name="name">Asrama Room Kanban</field> <field name="model">asrama.room</field> <field name="arch" type="xml"> <kanban default_group_by="stage_id" on_create="quick_create" quick_create_view="asramaku.asrama_room_view_form_minimal"> <field name="stage_id" /> <field name="color" /> <templates> <t t-name="kanban-menu" groups="base.group_user"> <a t-if="widget.editable" role="menuitem" type="edit" class="dropdown-item">Edit</a> <a t-if="widget.deletable" role="menuitem" type="delete" class="dropdown-item">Delete</a> <ul class="oe_kanban_colorpicker" data-field="color" /> <div role="separator" class="dropdown-divider"></div> </t><!--kanban-menu--> <t t-name="kanban-box"> <div t-attf-class="{{'oe_kanban_color_' + kanban_getcolor(record.color.raw_value) }} oe_kanban_card oe_kanban_global_click oe_semantic_html_overide" > <div class="oe_kanban_card oe_kanban_global_click"> <strong class="o_kanban_record_title"> <field name="name" /> </strong> <div class="text-muted"> <i class="fa fa-building" /> <field name="hostel_id" /> </div> <span class="oe_kanban_list_many2many"> <field name="asrama_amenities_ids" widget="many2many_tags" options="{'color_field':'color'}" /> </span> <div> <field name="popularity" widget='priority' /> </div> </div><!--oe_kanban_card--> </div><!--oe_kanban_color--> </t><!--kanban-box--> </templates> </kanban> </field> </record> <record id="asrama_room_action_kanban" model="ir.actions.act_window.view"> <field name="act_window_id" ref="asrama_room_action"/> <field name="view_id" ref="asrama_room_view_kanban"/> <field name="view_mode">kanban</field> <field name="sequence" eval="1"/> </record> <record id="asrama_room_action_tree" model="ir.actions.act_window.view"> <field name="act_window_id" ref="asrama_room_action"/> <field name="view_id" ref="asrama_room_view_tree"/> <field name="view_mode">tree</field> <field name="sequence" eval="2"/> </record> <record id="asrama_room_action_form" model="ir.actions.act_window.view"> <field name="act_window_id" ref="asrama_room_action"/> <field name="view_id" ref="asrama_room_view_form"/> <field name="view_mode">form</field> <field name="sequence" eval="3"/> </record> <!-- asrama.room action window --> <record id="asrama_room_action" model="ir.actions.act_window"> <field name="name">Room</field> <field name="type">ir.actions.act_window</field> <field name="res_model">asrama.room</field> <field name="view_mode">kanban,tree,form</field> <field name="domain">[]</field> <field name="context">{}</field> <field name="help" type="html"> <p class="oe_view_nocontent_create"> Tambah kamar </p> </field> </record> <!-- This Menu Item must have a parent and an action --> <menuitem id="asrama_room_menu" name="Room Asrama" parent="hostel_main_menu" action="asrama_room_action" sequence="2"/> </odoo> - File manifest
{ 'name': 'Manajemen Asrama', "data":[ "security/hostel_security.xml", "security/ir.model.access.csv", "data/data.xml", "data/data_stage.xml", "views/hostel.xml", "views/asrama_room_stage.xml", "views/asrama_amenities.xml", "views/asrama_room.xml", "views/asrama_student.xml", "views/asrama_room_availability_view.xml", "views/res_config_settings.xml", "wizard/assign_room_student.xml", ], 'pre_init_hook' : 'pre_init_hook_hostel', 'post_init_hook' : 'add_room_hook', 'uninstall_hook' : 'uninstall_hook_user' } - Start Odoo dengan upgrade modul



- Ke sub menu Asrama Amenities


- Tampilan asrama amenities


- Tampilan kanban dengan warna dan prioritas


- Tampilan many2many pada amenities

Kunjungi https://www.proweb.co.id/implementasi-odoo/ untuk menambah wawasan anda mengenai Odoo ERP.
