Skip to content

Commit d73b8f1

Browse files
committed
[ADD] booking_engine: new sub-module for the hotel industry suite
Contains all logic related to the rental-panning interactions,new fields, and the new app with its own customized views and menus to manage an hotel-like business task-5003819 Part-of: #846 Signed-off-by: Vallaeys Valentin (vava) <[email protected]>
1 parent 561d088 commit d73b8f1

22 files changed

+1542
-0
lines changed

.weblate.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@
6161
"filemask": "billboard_rental/i18n/*.po",
6262
"new_base": "billboard_rental/i18n/billboard_rental.pot"
6363
},
64+
{
65+
"name": "booking_engine",
66+
"filemask": "booking_engine/i18n/*.po",
67+
"new_base": "booking_engine/i18n/booking_engine.pot"
68+
},
6469
{
6570
"name": "bookstore",
6671
"filemask": "bookstore/i18n/*.po",

booking_engine/__manifest__.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
'name': 'Booking Engine',
3+
'version': '1.0',
4+
'category': 'Hidden/Tools',
5+
'author': 'Odoo S.A.',
6+
'depends': [
7+
'base_automation',
8+
'contacts',
9+
'knowledge',
10+
'sale_project',
11+
'sale_renting_planning',
12+
'web_studio',
13+
'website_appointment',
14+
'website_sale_renting',
15+
],
16+
'data': [
17+
'data/res_config_settings.xml',
18+
'data/ir_model_fields.xml',
19+
'data/account_tax.xml',
20+
'data/product_template.xml',
21+
'data/ir_actions_server.xml',
22+
'data/base_automation.xml',
23+
'data/ir_ui_view.xml',
24+
'data/ir_actions_act_window.xml',
25+
'data/ir_ui_menu.xml',
26+
'data/sale_temporal_recurrence.xml',
27+
'data/website_view.xml',
28+
'data/website_menu.xml',
29+
'data/resource_calendar_data.xml',
30+
'data/product_category.xml',
31+
'data/product_attribute.xml',
32+
'data/product_attribute_value.xml',
33+
],
34+
'demo': [
35+
'demo/website_menu.xml',
36+
'demo/payment_provider_demo.xml',
37+
],
38+
'license': 'OPL-1',
39+
'cloc_exclude': [
40+
'data/website_view.xml',
41+
],
42+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<odoo noupdate="1">
3+
<record id='city_tax_group' model='account.tax.group'>
4+
<field name="name">City Taxes</field>
5+
</record>
6+
<record id="account_tax_1" model="account.tax">
7+
<field name="amount">2.1</field>
8+
<field name="amount_type">fixed</field>
9+
<field name="name">City Tax</field>
10+
<field name="type_tax_use">sale</field>
11+
<field name="tax_group_id" ref="city_tax_group"/>
12+
<field name="price_include_override">tax_excluded</field>
13+
</record>
14+
</odoo>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<odoo>
3+
<record id="industry_on_so_line" model="base.automation">
4+
<field name="model_id" ref="sale.model_sale_order"/>
5+
<field name="action_server_ids" eval="[(6, 0, [ref('industry_add_update')])]"/>
6+
<field name="trigger">on_create_or_write</field>
7+
<field name="name">On SO line edit, add/edit city tax</field>
8+
<field name="trigger_field_ids" eval="[(6, 0, [ref('sale_model_sale_order_x_picked_up')])]"/>
9+
<field name="filter_pre_domain">[('x_picked_up', '=', False)]</field>
10+
<field name="filter_domain">[('x_picked_up', '=', True)]</field>
11+
</record>
12+
<record id="industry_on_slot_fix_times" model="base.automation">
13+
<field name="model_id" ref="planning.model_planning_slot"/>
14+
<field name="action_server_ids" eval="[(6, 0, [ref('industry_fix_slot_times'), ref('industry_trigger_so_update_from_slot')])]"/>
15+
<field name="trigger">on_create_or_write</field>
16+
<field name="name">Fix Slot Times</field>
17+
<field name="trigger_field_ids" eval="[(6, 0, [ref('planning.field_planning_slot__end_datetime'), ref('planning.field_planning_slot__start_datetime'), ref('sale_planning.field_planning_slot__sale_line_id')])]"/>
18+
</record>
19+
<record id="industry_on_rental_order_create" model="base.automation">
20+
<field name="model_id" ref="sale.model_sale_order"/>
21+
<field name="action_server_ids" eval="[(6, 0, [ref('industry_set_rental_hours')])]"/>
22+
<field name="trigger">on_create_or_write</field>
23+
<field name="name">Set Rental Start/Return Hours on Create</field>
24+
<field name="filter_domain">[('rental_start_date', '!=', False), ('rental_return_date', '!=', False)]</field>
25+
<field name="trigger_field_ids" eval="[(6, 0, [ref('sale_renting.field_sale_order__rental_start_date'), ref('sale_renting.field_sale_order__rental_return_date')])]"/>
26+
</record>
27+
</odoo>
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<odoo>
3+
<record id="booking_engine_rooms_schedule_action" model="ir.actions.act_window">
4+
<field name="domain">[("role_id", "ilike", ""), ("role_id.x_is_a_room_offer", "=", True)]</field>
5+
<field name="context">{'search_default_group_by_employee': 1, 'planning_expand_employee': 1, 'planning_expand_role': 1, 'planning_expand_sale_line_id': 1, 'planning_expand_resource': 1}</field>
6+
<field name="name">Schedule</field>
7+
<field name="res_model">planning.slot</field>
8+
<field name="view_mode">gantt,calendar,list,kanban,form</field>
9+
<field name="search_view_id" ref="planning_search_view"/>
10+
<field name="view_ids" eval="[
11+
(5, 0, 0),
12+
(0, 0, {'view_mode': 'gantt', 'view_id': ref('booking_engine_planning_gantt_view')}),
13+
(0, 0, {'view_mode': 'form', 'view_id': ref('booking_engine_schedule_form')}),
14+
(0, 0, {'view_mode': 'list', 'view_id': ref('booking_engine_planning_list_view')}),
15+
16+
]"/>
17+
</record>
18+
<record id="booking_engine_rooms_order_action" model="ir.actions.act_window">
19+
<field name="domain">[("x_order_involves_room", "=", True)]</field>
20+
<field name="context">{'search_default_filter_today': 1, 'in_rental_app': 1, 'search_default_from_rental': 1, 'group_by': ['rental_status']}</field>
21+
<field name="name">Orders</field>
22+
<field name="res_model">sale.order</field>
23+
<field name="view_mode">kanban,list,form</field>
24+
<field name="search_view_id" ref="booking_engine_order_search"/>
25+
<field name="view_ids" eval="[
26+
(5, 0, 0),
27+
(0, 0, {'view_mode': 'kanban', 'view_id': ref('booking_engine_orders_kanban')}),
28+
(0, 0, {'view_mode': 'list', 'view_id': ref('sale_renting.rental_order_view_tree')}),
29+
(0, 0, {'view_mode': 'form', 'view_id': ref('sale_renting.rental_order_primary_form_view')}),
30+
]"/>
31+
</record>
32+
<record id="booking_engine_rooms_offers_action" model="ir.actions.act_window">
33+
<field name="name">Room Offers</field>
34+
<field name="res_model">product.template</field>
35+
<field name="domain">[("x_is_a_room_offer", "=", True)]</field>
36+
<field name="context">{'default_x_is_a_room_offer': True, 'default_purchase_ok': False, 'default_sale_ok': True, 'default_rent_ok': True, 'default_type': 'service', 'default_planning_enabled': True}</field>
37+
<field name="view_mode">kanban,list,form</field>
38+
<field name="search_view_id" ref="booking_engine_offers_search"/>
39+
<field name="view_ids" eval="[
40+
(5, 0, 0),
41+
(0, 0, {'view_mode': 'kanban', 'view_id': ref('booking_engine_offers_kanban')}),
42+
(0, 0, {'view_mode': 'list', 'view_id': ref('booking_engine_offers_list')}),
43+
]"/>
44+
</record>
45+
<record id="booking_engine_rooms_roles_action" model="ir.actions.act_window">
46+
<field name="name">Room Roles</field>
47+
<field name="res_model">planning.role</field>
48+
<field name="domain">[("x_is_a_room_offer", "=", True)]</field>
49+
<field name="context">{'default_x_is_a_room_offer': True, 'default_sync_shift_rental': True}</field>
50+
<field name="view_mode">list,form</field>
51+
<field name="search_view_id" ref="booking_engine_planning_role_view_search"/>
52+
<field name="view_ids" eval="[
53+
(5, 0, 0),
54+
(0, 0, {'view_mode': 'list', 'view_id': ref('planning.planning_role_view_tree')}),
55+
]"/>
56+
</record>
57+
<record id="booking_engine_rooms_resources_action" model="ir.actions.act_window">
58+
<field name="name">Room Resources</field>
59+
<field name="res_model">resource.resource</field>
60+
<field name="context">{'default_resource_type': 'material'}</field>
61+
<field name="domain">[("resource_type", "=", "material"), ("role_ids.x_is_a_room_offer", "=", True)]</field>
62+
<field name="view_mode">list,kanban,form</field>
63+
<field name="search_view_id" ref="booking_engine_planning_resource_view_search"/>
64+
<field name="view_ids" eval="[
65+
(5, 0, 0),
66+
(0, 0, {'view_mode': 'list', 'view_id': ref('planning.resource_resource_tree_view_inherit')}),
67+
(0, 0, {'view_mode': 'form', 'view_id': ref('planning.resource_resource_form_view_inherit')}),
68+
]"/>
69+
</record>
70+
</odoo>
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<odoo>
3+
<record id="industry_add_update" model="ir.actions.server">
4+
<field name="name">Add/Update City Tax</field>
5+
<field name="model_id" ref="sale.model_sale_order"/>
6+
<field name="usage">base_automation</field>
7+
<field name="state">code</field>
8+
<field name="code"><![CDATA[for record in records:
9+
stay_taxes = 0
10+
stay_tax_line = False
11+
for so_line in record.order_line:
12+
if so_line.product_id.x_is_a_room_offer:
13+
if so_line.x_nights > 0: stay_taxes += so_line.x_nights
14+
if so_line.product_id.x_is_stay_tax: stay_tax_line = so_line
15+
if stay_taxes > 0:
16+
if stay_tax_line: stay_tax_line['product_uom_qty'] = stay_taxes
17+
else: # Add stay tax product
18+
product = env['product.product'].search([('x_is_stay_tax', '=', True)], limit=1)
19+
env['sale.order.line'].create({'order_id': record.id, 'product_id': product.id, 'product_uom_qty': stay_taxes})
20+
]]></field>
21+
</record>
22+
<record id="industry_trigger_so_update_from_slot" model="ir.actions.server">
23+
<field name="name">Trigger SO Update from Slot</field>
24+
<field name="model_id" ref="planning.model_planning_slot"/>
25+
<field name="usage">base_automation</field>
26+
<field name="state">code</field>
27+
<field name="code"><![CDATA[
28+
sale_order = record.sale_order_id
29+
if sale_order:
30+
stay_taxes = 0
31+
stay_tax_line = False
32+
for so_line in sale_order.order_line:
33+
if so_line.product_id.x_is_a_room_offer:
34+
if so_line.x_nights > 0:
35+
stay_taxes += so_line.x_nights
36+
if so_line.product_id.x_is_stay_tax:
37+
stay_tax_line = so_line
38+
if stay_taxes > 0:
39+
if stay_tax_line:
40+
stay_tax_line['product_uom_qty'] = stay_taxes
41+
]]></field>
42+
</record>
43+
<record id="industry_fix_slot_times" model="ir.actions.server">
44+
<field name="binding_model_id" ref="planning.model_planning_slot"/>
45+
<field name="model_id" ref="planning.model_planning_slot"/>
46+
<field name="usage">base_automation</field>
47+
<field name="state">code</field>
48+
<field name="name">Fix Slot Times</field>
49+
<field name="code"><![CDATA[def ceil(x):
50+
return int(x) if x == int(x) else int(x) + 1
51+
start_datetime = record.start_datetime.astimezone(tz=dateutil.tz.UTC)
52+
end_datetime = record.end_datetime.astimezone(tz=dateutil.tz.UTC)
53+
tz = env.ref('website.default_website').tz
54+
if record.role_id.x_is_a_room_offer:
55+
if record.start_datetime and str(record.start_datetime.time()) != "18:00:00":
56+
start = timezone(tz).localize(datetime.datetime(year=start_datetime.year, month=start_datetime.month, day=start_datetime.day, hour=18, minute=0, second=0))
57+
start_datetime = start.astimezone(tz=dateutil.tz.UTC)
58+
if start_datetime >= end_datetime:
59+
start_datetime = start_datetime - datetime.timedelta(days=1)
60+
if record.end_datetime and str(record.end_datetime.time()) != "09:00:00":
61+
end = timezone(tz).localize(datetime.datetime(year=end_datetime.year, month=end_datetime.month, day=end_datetime.day, hour=9, minute=0, second=0))
62+
end_datetime = end.astimezone(tz=dateutil.tz.UTC)
63+
if start_datetime >= end_datetime:
64+
end_datetime = end_datetime + datetime.timedelta(days=1)
65+
record['start_datetime'] = start_datetime.replace(tzinfo=None)
66+
record['end_datetime'] = end_datetime.replace(tzinfo=None)
67+
if record.sale_line_id:
68+
nights = 0
69+
if record.sale_line_id.planning_slot_ids:
70+
for slot in record.sale_line_id.planning_slot_ids:
71+
if slot.start_datetime and slot.end_datetime:
72+
nights += ceil((slot.end_datetime - slot.start_datetime).total_seconds() / (24 * 3600))
73+
record['sale_line_id']['x_nights'] = nights
74+
rent_unit_price = env['product.pricing'].search([('product_template_id', '=', record.sale_line_id.product_id.product_tmpl_id.id)], limit=1).price
75+
if rent_unit_price:
76+
record['sale_line_id']['price_unit'] = rent_unit_price * nights
77+
]]></field>
78+
</record>
79+
<record id="industry_set_rental_hours" model="ir.actions.server">
80+
<field name="name">Set Rental Start/Return Hours</field>
81+
<field name="model_id" ref="sale.model_sale_order"/>
82+
<field name="usage">base_automation</field>
83+
<field name="state">code</field>
84+
<field name="code"><![CDATA[
85+
for record in records:
86+
tz = env.ref('website.default_website').tz
87+
rental_start_date = record.rental_start_date.astimezone(tz=dateutil.tz.gettz(tz))
88+
rental_return_date = record.rental_return_date.astimezone(tz=dateutil.tz.gettz(tz))
89+
start = rental_start_date.replace(hour=18, minute=0, second=0, microsecond=0)
90+
start_datetime = start.astimezone(tz=dateutil.tz.UTC)
91+
record['rental_start_date'] = start_datetime.replace(tzinfo=None)
92+
end = rental_return_date.replace(hour=9, minute=0, second=0, microsecond=0)
93+
end_datetime = end.astimezone(tz=dateutil.tz.UTC)
94+
record['rental_return_date'] = end_datetime.replace(tzinfo=None)
95+
record.action_update_rental_prices()
96+
]]></field>
97+
</record>
98+
</odoo>

0 commit comments

Comments
 (0)