Skip to content

Commit 4137286

Browse files
[IMP] booking_engine: early checkin and late checkout
- Add products for early checkin and late checkout to optionally add them in SO. - Add a priority flag system to check what kind of accommodation specific products are in SO. - Modify house keeping task creation to indicate if the room has early checkin/late checkout on the same day task-6150387
1 parent 7436cd7 commit 4137286

10 files changed

Lines changed: 165 additions & 72 deletions

File tree

booking_engine/__manifest__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,15 @@
5858
],
5959
'web.assets_backend_lazy': [
6060
'booking_engine/static/src/js/patch.js', # web_gantt/gantt_renderer is lazy so this needs to be lazy too
61+
'booking_engine/static/src/xml/gantt_pill_flags.xml',
6162
],
6263
},
6364
'license': 'OEEL-1',
6465
'cloc_exclude': [
6566
'data/res_config_settings.xml',
6667
'data/website_view.xml',
6768
'static/src/scss/gantt.scss',
69+
'static/src/xml/gantt_pill_flags.xml',
6870
],
6971
'images': ['images/main.png'],
7072
}

booking_engine/data/ir_actions_server.xml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
<field name="code"><![CDATA[
88
sequence, stay_tax_line = 0, False
99
for so_line in record.x_sale_order_id.order_line:
10-
if so_line.product_id.x_is_stay_tax: stay_tax_line = so_line
10+
if so_line.product_id.x_accommodation_product == 'stay_tax': stay_tax_line = so_line
1111
sequence = max(sequence, so_line.sequence)
1212
if stay_tax_line: stay_tax_line.write({'product_uom_qty': record.x_total, 'qty_delivered': record.x_total, 'sequence': 1 + sequence})
1313
elif record.x_total > 0:
14-
product = env['product.product'].search([('x_is_stay_tax', '=', True)], limit=1)
14+
product = env['product.product'].search([('x_accommodation_product', '=', 'stay_tax')], limit=1)
1515
env['sale.order.line'].create({'order_id': record.x_sale_order_id.id, 'product_id': product.id, 'product_uom_qty': record.x_total, 'qty_delivered': record.x_total})
1616
]]></field>
1717
</record>
@@ -130,8 +130,8 @@ if env['ir.config_parameter'].get_bool('booking_engine.x_approvers_setting', Fal
130130
<field name="code"><![CDATA[
131131
if (project := env['project.project'].search([('x_is_house_keeping_project', '=', True)], limit=1)):
132132
newtasks = []
133-
last_midnight = datetime.datetime.today().replace(hour=0, minute=0)
134-
for slot in env['planning.slot'].search([('start_datetime', '<', last_midnight),('end_datetime', '>=', last_midnight), ('resource_ids.x_category', '=', 'stay_offer')]):
133+
last_midnight = datetime.datetime.today().replace(hour=0, minute=0, second=0, microsecond=0)
134+
for slot in env['planning.slot'].search([('start_datetime', '<', last_midnight), ('end_datetime', '>=', last_midnight), ('resource_ids.x_category', '=', 'stay_offer')]):
135135
end_today = slot.end_datetime < last_midnight + datetime.timedelta(hours=24)
136136
cleaning_points = slot.sale_line_id.product_id.x_checkout_cleaning if end_today else slot.sale_line_id.product_id.x_stayover_cleaning
137137
if not cleaning_points: continue
@@ -145,6 +145,8 @@ if (project := env['project.project'].search([('x_is_house_keeping_project', '='
145145
'x_resource_id': slot.resource_ids[:1].id,
146146
'x_cleaning': 'checkout' if end_today else 'stayover',
147147
'x_cleaning_points': cleaning_points,
148+
'x_priority_flag_ids': end_today and ([env.ref('booking_engine.product_product_early_checkin').id] if slot.x_has_eci_at_checkout else []) +
149+
([env.ref('booking_engine.product_product_late_checkout').id] if slot.x_has_late_checkout else []),
148150
'user_ids': [(5,0)],
149151
})
150152
env['project.task'].create(newtasks)

booking_engine/data/ir_cron.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<field name="interval_number">1</field>
1616
<field name="interval_type">days</field>
1717
<field name="nextcall" eval="datetime.today().date()+relativedelta(days=1)"/>
18-
<field name="state">code</field>
18+
<field name="state">multi</field>
1919
<field name="ir_actions_server_id" ref="server_action_house_keeping_daily_update_actions"/>
2020
</record>
2121
<!-- Availability/Occupancy-->

booking_engine/data/ir_model_fields.xml

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,32 @@
134134
<field name="field_id" ref="product_model_product_template_x_offer_type" />
135135
<field name="value">SUI</field>
136136
</record>
137-
<record id="product_model_product_template_x_is_stay_tax_field" model="ir.model.fields">
138-
<field name="ttype">boolean</field>
137+
<record id="product_model_product_template_x_accommodation_product_field" model="ir.model.fields">
138+
<field name="ttype">selection</field>
139139
<field name="copied" eval="True"/>
140-
<field name="field_description">Is City Tax</field>
140+
<field name="field_description">Accommodation Product</field>
141141
<field name="model_id" ref="product.model_product_template"/>
142-
<field name="name">x_is_stay_tax</field>
142+
<field name="name">x_accommodation_product</field>
143+
<field name="help">Provides specific behaviour related to accommodation needs:
144+
- City Tax: Allows to automatically add this product during check out to sale orders.
145+
- Early Check In: Flags sale orders, and house keeping tasks when part of a sale order (informative only).
146+
- Late Check Out: Flags sale orders, and house keeping tasks when part of a sale order (informative only).
147+
</field>
148+
</record>
149+
<record id="product_model_product_template_x_accommodation_product_field_selection_stay_tax" model="ir.model.fields.selection">
150+
<field name="name">City Tax</field>
151+
<field name="field_id" ref="product_model_product_template_x_accommodation_product_field" />
152+
<field name="value">stay_tax</field>
153+
</record>
154+
<record id="product_model_product_template_x_accommodation_product_field_selection_early_checkin" model="ir.model.fields.selection">
155+
<field name="name">Early Check In</field>
156+
<field name="field_id" ref="product_model_product_template_x_accommodation_product_field" />
157+
<field name="value">early_checkin</field>
158+
</record>
159+
<record id="product_model_product_template_x_accommodation_product_field_selection_late_checkout" model="ir.model.fields.selection">
160+
<field name="name">Late Check Out</field>
161+
<field name="field_id" ref="product_model_product_template_x_accommodation_product_field" />
162+
<field name="value">late_checkout</field>
143163
</record>
144164
<record id="product_model_product_template_x_resource_ids_field" model="ir.model.fields">
145165
<field name="ttype">many2many</field>
@@ -149,6 +169,19 @@
149169
<field name="related">planning_role_id.resource_ids</field>
150170
<field name="relation">resource.resource</field>
151171
</record>
172+
<record id="sale_model_sale_order_x_priority_flag_ids_field" model="ir.model.fields">
173+
<field name="ttype">many2many</field>
174+
<field name="field_description">Priority Flags</field>
175+
<field name="readonly" eval="True"/>
176+
<field name="model_id" ref="sale.model_sale_order"/>
177+
<field name="name">x_priority_flag_ids</field>
178+
<field name="relation">product.product</field>
179+
<field name="depends">order_line, order_line.product_id, order_line.product_id.x_accommodation_product</field>
180+
<field name="compute"><![CDATA[
181+
for record in self:
182+
record['x_priority_flag_ids'] = record.order_line.product_id.filtered('x_accommodation_product').ids
183+
]]></field>
184+
</record>
152185
<record id="sale_order_x_order_involves_room" model="ir.model.fields">
153186
<field name="ttype">boolean</field>
154187
<field name="field_description">Order Involves Room</field>
@@ -233,6 +266,43 @@ for record in self: record['x_order_involves_room'] = any(line.product_id.x_is_a
233266
<field name="related">default_role_id.x_resource_category</field>
234267
<field name="store" eval="False"/>
235268
</record>
269+
<record id="model_planning_slot_x_has_early_checkin" model="ir.model.fields">
270+
<field name="ttype">boolean</field>
271+
<field name="field_description">Has Early Checkin</field>
272+
<field name="model_id" ref="planning.model_planning_slot"/>
273+
<field name="name">x_has_early_checkin</field>
274+
<field name="depends">sale_order_id, sale_order_id.x_priority_flag_ids</field>
275+
<field name="store" eval="False"/>
276+
<field name="compute"><![CDATA[
277+
for record in self:
278+
record['x_has_early_checkin'] = any(p.x_accommodation_product == 'early_checkin' for p in record.sale_order_id.x_priority_flag_ids)
279+
]]></field>
280+
</record>
281+
<record id="model_planning_slot_x_has_late_checkout" model="ir.model.fields">
282+
<field name="ttype">boolean</field>
283+
<field name="field_description">Has Late Checkout</field>
284+
<field name="model_id" ref="planning.model_planning_slot"/>
285+
<field name="name">x_has_late_checkout</field>
286+
<field name="depends">sale_order_id, sale_order_id.x_priority_flag_ids</field>
287+
<field name="store" eval="False"/>
288+
<field name="compute"><![CDATA[
289+
for record in self:
290+
record['x_has_late_checkout'] = any(p.x_accommodation_product == 'late_checkout' for p in record.sale_order_id.x_priority_flag_ids)
291+
]]></field>
292+
</record>
293+
<record id="model_planning_slot_x_has_eci_at_checkout" model="ir.model.fields">
294+
<field name="ttype">boolean</field>
295+
<field name="field_description">Has ECI at checkout</field>
296+
<field name="model_id" ref="planning.model_planning_slot"/>
297+
<field name="name">x_has_eci_at_checkout</field>
298+
<field name="store" eval="False"/>
299+
<field name="compute"><![CDATA[
300+
for record in self:
301+
# if the next slot starts on the same day the current ends on, and the next slot has early checkin then there is an early checkin at the end of the current slot
302+
next_slot = self.env['planning.slot'].search([('start_datetime', '>=', record.end_datetime), ('resource_ids', '=', record.resource_ids)], order='start_datetime', limit=1)
303+
record['x_has_eci_at_checkout'] = next_slot and next_slot.x_has_early_checkin and next_slot.start_datetime.date() == record.end_datetime.date()
304+
]]></field>
305+
</record>
236306
<record id="model_planning_slot_x_nights" model="ir.model.fields">
237307
<field name="ttype">integer</field>
238308
<field name="field_description">Nights</field>
@@ -471,6 +541,15 @@ for record in self: record['x_guests_count'] = self.env['x_guests_line'].search_
471541
</record>
472542

473543
<!-- project.task fields -->
544+
<record id="x_priority_flag_ids_field_project_task" model="ir.model.fields">
545+
<field name="name">x_priority_flag_ids</field>
546+
<field name="ttype">many2many</field>
547+
<field name="field_description">Priority Flag</field>
548+
<field name="model_id" ref="project.model_project_task"/>
549+
<field name="relation">product.product</field>
550+
<field name="domain" eval="[('x_accommodation_product', 'in', ['early_checkin', 'late_checkout'])]"/>
551+
<field name="readonly" eval="True"/>
552+
</record>
474553
<record id="x_resource_id_field_resource_resource" model="ir.model.fields">
475554
<field name="name">x_resource_id</field>
476555
<field name="ttype">many2one</field>

booking_engine/data/ir_ui_view.xml

Lines changed: 13 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@
5656
<xpath expr="//field[@name='is_rental_order']" position="after">
5757
<button string="City Tax" type="action" name="%(open_x_city_tax_action)d" context="{'search_default_x_sale_order_id': id, 'default_x_sale_order_id': id}" invisible="not x_order_involves_room"/>
5858
</xpath>
59-
<xpath expr="//form[1]/sheet[1]/notebook[1]" position="inside">
59+
<xpath expr="//group[@name='order_details']" postion="inside">
60+
<field name="x_priority_flag_ids" widget="many2many_tags" invisible="not x_priority_flag_ids"/>
61+
</xpath>
62+
<xpath expr="//notebook" position="inside">
6063
<page string="Guests" invisible="not x_order_involves_room">
6164
<field name="x_guest_line_ids">
6265
<list default_order="x_sequence asc,id desc" editable="bottom">
@@ -163,23 +166,6 @@
163166
<field name="type">form</field>
164167
<field name="active" eval="True"/>
165168
</record>
166-
<record id="product_product_form_view" model="ir.ui.view">
167-
<field name="arch" type="xml">
168-
<xpath expr="//label[@for='lst_price']" position="attributes">
169-
<attribute name="invisible">planning_role_id.x_resource_category</attribute>
170-
</xpath>
171-
<xpath expr="//div[@name='list_price_per_uom_id']" position="attributes">
172-
<attribute name="invisible">x_is_a_room_offer</attribute>
173-
</xpath>
174-
</field>
175-
<field name="inherit_id" ref="product.product_normal_form_view"/>
176-
<field name="mode">extension</field>
177-
<field name="model">product.product</field>
178-
<field name="name">product.product.form.inherit.booking_engine</field>
179-
<field name="priority">160</field>
180-
<field name="type">form</field>
181-
<field name="active" eval="True"/>
182-
</record>
183169
<record id="planning_kanban_view" model="ir.ui.view">
184170
<field name="active" eval="True"/>
185171
<field name="arch" type="xml">
@@ -407,41 +393,6 @@
407393
<field name="type">search</field>
408394
<field name="active" eval="True"/>
409395
</record>
410-
<record id="booking_engine_order_search" model="ir.ui.view">
411-
<field name="arch" type="xml">
412-
<xpath expr="//filter[@name='my_sale_orders_filter']" position="before">
413-
<filter domain="['|',
414-
'&amp;', '&amp;', '&amp;',
415-
('rental_status', '=', 'return'),
416-
('rental_return_date', '&gt;=', datetime.datetime.combine(context_today(), datetime.time(0, 0, 0)).to_utc().strftime('%Y-%m-%d %H:%M:%S')),
417-
('rental_return_date', '&lt;', datetime.datetime.combine(context_today() + relativedelta(days = 1), datetime.time(0, 0, 0)).to_utc().strftime('%Y-%m-%d %H:%M:%S')),
418-
('state', '=', 'sale'),
419-
'&amp;', '&amp;', '&amp;',
420-
('rental_status', '=', 'pickup'),
421-
('rental_start_date', '&gt;=', datetime.datetime.combine(context_today(), datetime.time(0, 0, 0)).to_utc().strftime('%Y-%m-%d %H:%M:%S')),
422-
('rental_start_date', '&lt;', datetime.datetime.combine(context_today() + relativedelta(days = 1), datetime.time(0, 0, 0)).to_utc().strftime('%Y-%m-%d %H:%M:%S')),
423-
('state', '=', 'sale')]"
424-
name="filter_today" string="Today"/>
425-
<separator/>
426-
</xpath>
427-
<xpath expr="//filter[@name='my_sale_orders_filter']" position="after">
428-
<separator/>
429-
<filter domain="[('rental_status', 'in', ['sent', 'draft'])]" name="filter_draft" string="Draft"/>
430-
<filter domain="[('rental_status', '=', 'pickup'), ('state', '=', 'sale')]" name="filter_check_in" string="To Check In"/>
431-
<filter domain="[('rental_status', '=', 'return'), ('state', '=', 'sale')]" name="filter_check_out" string="To Check Out"/>
432-
</xpath>
433-
<xpath expr="//filter[@name='salesperson']" position="before">
434-
<filter string="Status" name="rental_status_group" context="{'group_by': 'rental_status'}"/>
435-
</xpath>
436-
</field>
437-
<field name="inherit_id" ref="sale.view_sales_order_filter"/>
438-
<field name="mode">primary</field>
439-
<field name="model">sale.order</field>
440-
<field name="name">sale.order.list.select.inherit.booking_engine</field>
441-
<field name="priority">320</field>
442-
<field name="type">search</field>
443-
<field name="active" eval="True"/>
444-
</record>
445396
<record id="booking_engine_schedule_form" model="ir.ui.view">
446397
<field name="arch" type="xml">
447398
<xpath expr="//field[@name='resource_ids']" position="attributes">
@@ -472,6 +423,10 @@
472423
<attribute name="multi_create_view"/>
473424
<attribute name="color">x_booking_color</attribute>
474425
</xpath>
426+
<xpath expr="/gantt" position="inside">
427+
<field name="x_has_early_checkin"/>
428+
<field name="x_has_late_checkout"/>
429+
</xpath>
475430
</field>
476431
<field name="inherit_id" ref="planning.planning_view_gantt"/>
477432
<field name="mode">primary</field>
@@ -591,7 +546,7 @@
591546
]"/>
592547
</record>
593548
<record id="booking_engine_rooms_order_action" model="ir.actions.act_window">
594-
<field name="search_view_id" ref="booking_engine_order_search"/>
549+
<field name="search_view_id" ref="sale_renting.rental_order_view_search"/>
595550
<field name="view_ids" eval="[
596551
(5, 0, 0),
597552
(0, 0, {'view_mode': 'kanban', 'view_id': ref('booking_engine_orders_kanban')}),
@@ -702,6 +657,9 @@
702657
<!-- project.task views -->
703658
<record id="project_task_form_view_primary" model="ir.ui.view">
704659
<field name="arch" type="xml">
660+
<xpath expr="//field[@name='project_id']" position="replace">
661+
<field name="x_priority_flag_ids" widget="many2many_tags"/>
662+
</xpath>
705663
<xpath expr="//field[@name='stage_id']" position="attributes">
706664
<attribute name="options">{'clickable': '1', 'fold_field': 'auto_validation_state'}</attribute>
707665
</xpath>
@@ -734,6 +692,7 @@
734692
<xpath expr="/list//field[@name='priority']" position="move"/>
735693
</xpath>
736694
<xpath expr="/list//field[@name='name']" position="after">
695+
<field name="x_priority_flag_ids" widget="many2many_tags"/>
737696
<field name="x_task_occupancy_color" column_invisible="True"/>
738697
<field name="x_cleaning_color" column_invisible="True"/>
739698
<field name="x_task_occupancy" optional="show" widget="badge" options="{'color_field': 'x_task_occupancy_color'}"/>

booking_engine/data/product_template.xml

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,31 @@
99
<field name="is_favorite" eval="True"/>
1010
<field name="service_type">manual</field>
1111
<field name="invoice_policy">delivery</field>
12-
<field name="x_is_stay_tax" eval="True"/>
12+
<field name="x_accommodation_product">stay_tax</field>
1313
<field name="taxes_id" eval="[(6, 0, [ref('account_tax_1')])]"/>
1414
</record>
15+
<record id="product_product_early_checkin" model="product.product">
16+
<field name="name">Early Check In</field>
17+
<field name="type">service</field>
18+
<field name="categ_id" ref="product.product_category_services"/>
19+
<field name="list_price" eval="False"/>
20+
<field name="purchase_ok" eval="False"/>
21+
<field name="is_favorite" eval="True"/>
22+
<field name="service_type">manual</field>
23+
<field name="invoice_policy">delivery</field>
24+
<field name="x_accommodation_product">early_checkin</field>
25+
<field name="list_price">15.0</field>
26+
</record>
27+
<record id="product_product_late_checkout" model="product.product">
28+
<field name="name">Late Check Out</field>
29+
<field name="type">service</field>
30+
<field name="categ_id" ref="product.product_category_services"/>
31+
<field name="list_price" eval="False"/>
32+
<field name="purchase_ok" eval="False"/>
33+
<field name="is_favorite" eval="True"/>
34+
<field name="service_type">manual</field>
35+
<field name="invoice_policy">delivery</field>
36+
<field name="x_accommodation_product">late_checkout</field>
37+
<field name="list_price">15.0</field>
38+
</record>
1539
</odoo>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<templates xml:space="preserve">
3+
<t t-inherit="planning.PlanningGanttRenderer.Pill" t-inherit-mode="extension">
4+
<xpath expr="//div[hasclass('o_gantt_pill')]" position="attributes">
5+
<attribute name="t-attf-style">{{ pill.record.x_has_early_checkin ? 'padding-left: 16px !important;' : '' }} {{ pill.record.x_has_late_checkout ? 'padding-right: 16px !important;' : '' }}</attribute>
6+
</xpath>
7+
<xpath expr="//div[hasclass('o_gantt_pill')]" position="inside">
8+
<span t-if="pill.record.x_has_early_checkin"
9+
class="fa fa-backward"
10+
style="position: absolute; left: 3px;"
11+
title="Early Check-in"/>
12+
<span t-if="pill.record.x_has_late_checkout"
13+
class="fa fa-forward"
14+
style="position: absolute; right: 3px;"
15+
title="Late Check-out"/>
16+
</xpath>
17+
</t>
18+
19+
</templates>

0 commit comments

Comments
 (0)