Introduction
In this article, we will implement a feature in Odoo that displays a popup containing a full calendar, allowing users to select a date. This feature will be achieved using a custom calendar view and extending Odoo’s default CalendarController
.
by default, Odoo uses a mini-calendar widget (typically a date picker) for day selection in forms and fields that require a date input.
However, sometimes we need to show a full calendar as it allows viewing events when selecting a date.
Expected Result
When the user clicks on the calendar icon next to the date field in the form view, a modal window will appear, displaying a full calendar. The user can then select a date, and the selected date will be updated in the corresponding field.
Project Structure
The project structure is as follows:
Flow Logic
The process flow starts with clicking the calendar button in the form view. This triggers an action that opens a popup with a calendar view. The user selects a date, and the system updates the date_start
field of the record.
Implementation
1. Define the Model
We use a simple model called simple.model
with the following fields:
from odoo import models, fields, api
class SimpleModel(models.Model):
_name = 'simple.model'
_description = 'Simple Model'
name = fields.Char(string="Name", required=True)
description = fields.Text(string="Description")
date_start = fields.Date("Select Date")
@api.model
def select_day_and_close_modal(self, vals):
date_selected = vals.get('date_selected')
active_id = vals.get('active_id')
record = self.browse(active_id)
record.date_start = date_selected
return {'type': 'ir.actions.act_window_close'}
2. Extend CalendarController
In CalendarController.js
, we extend the default CalendarController
and override the _onOpenCreate
method to handle the date selection logic.
odoo.define('nh_calenday_select_day.CalendarControllerCustom', function (require) {
"use strict";
var CalendarController = require('web.CalendarController');
var Dialog = require('web.Dialog');
var rpc = require('web.rpc');
var CalendarControllerCustom = CalendarController.extend({
_update: function(){
var self = this;
this._super.apply(this, arguments).then(function () {
self.renderer.state.scale = "month";
});
},
on_attach_callback: function() {
this._super.apply(this, arguments);
var self = this;
var button = $('<button>').text('Close').addClass('btn btn-secondary mt-2 mb-2');
button.on('click', function () {
self.do_action({ type: 'ir.actions.act_window_close' });
});
var footer = $('.modal-footer');
if (footer.length) {
footer.remove();
setTimeout(() => {
footer = $('<footer></footer>').append(button);
$('.modal-content').append(footer);
}, 0);
}
},
_onOpenCreate: function (event) {
if (event.target.model === 'simple.model') {
var self = this;
var active_id = event.target.state.context.active_id;
var formattedDate = event.data.start.format('YYYY-MM-DD');
var selectedDate = new Date(formattedDate);
var currentDate = new Date();
currentDate.setHours(0, 0, 0, 0);
if (selectedDate < currentDate) {
Dialog.alert(this, "The selected date is in the past. Please choose a future date.");
return;
}
Dialog.confirm(this, "Do you want to select this day: " + formattedDate + " ?", {
confirm_callback: function () {
rpc.query({
model: event.target.model,
method: 'select_day_and_close_modal',
args: [{ date_selected: formattedDate, active_id }]
}).then(function (action) {
if (action && action.type === 'ir.actions.act_window_close') {
self.do_action(action);
}
});
}
});
return;
}
return this._super(event);
},
});
return CalendarControllerCustom;
});
3. Register the Calendar View
In CalendarView.js
, we register the new calendar view:
odoo.define('nh_calenday_select_day.CalendarViewSelectDay', function (require) {
"use strict";
var CalendarView = require('web.CalendarView');
var CalendarModel = require('web.CalendarModel');
var CalendarRenderer = require('web.CalendarRenderer');
var CalendarControllerCustom = require('nh_calenday_select_day.CalendarControllerCustom');
var viewRegistry = require("web.view_registry");
var CalendarViewSelectDay = CalendarView.extend({
config: _.extend({}, CalendarView.prototype.config, {
Controller: CalendarControllerCustom,
Model: CalendarModel,
Renderer: CalendarRenderer,
}),
display_name: "Calendar Select Day",
groupable: false,
});
viewRegistry.add("calendar_select_day", CalendarViewSelectDay);
return CalendarViewSelectDay;
});
4. Define the Calendar View in XML
<record id="view_calendar_simple_model" model="ir.ui.view">
<field name="name">view_calendar_simple_model</field>
<field name="model">simple.model</field>
<field eval="1000" name="priority"/>
<field name="arch" type="xml">
<calendar js_class="calendar_select_day" date_start="date_start" event_limit="5" mode="day" quick_add="false">
<field name="name"/>
</calendar>
</field>
</record>
5. Add the Form View with Calendar Button
<record id="view_simple_model_form" model="ir.ui.view">
<field name="name">view_simple_model_form</field>
<field name="model">simple.model</field>
<field name="priority" eval="1"/>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name"/>
<div class="o_row">
<field name="date_start" class="oe_read_only" string="Select Day"/>
<button name="%(action_view_calendar_simple_model_selectday)d"
context="{'default_equipment_id': active_id}"
type="action"
class="ml-5 oe_highlight maintenance-button-calendar"
icon="fa-calendar" />
</div>
<field name="description"/>
</group>
</sheet>
</form>
</field>
</record>
Conclusion
Now, when users click the calendar button, a popup appears, allowing them to select a date. Upon confirmation, the selected date is updated in the form. This approach enhances usability and provides a seamless date selection experience in Odoo.
Like
Reply