[ADD] Service Lines Prices and Compute Flow

This commit is contained in:
Dario Lodeiros
2018-12-10 09:14:06 +01:00
parent c872b5847d
commit d8f1da7e1b
7 changed files with 158 additions and 102 deletions

View File

@@ -46,6 +46,7 @@
'views/hotel_room_type_restriction_item_views.xml',
'views/hotel_reservation_views.xml',
'views/hotel_room_closure_reason_views.xml',
'views/hotel_service_views.xml',
'views/hotel_board_service_views.xml',
'views/hotel_checkin_partner_views.xml',
'views/hotel_room_type_availability_views.xml',

View File

@@ -90,7 +90,6 @@ class HotelFolio(models.Model):
pricelist_id = fields.Many2one('product.pricelist',
string='Pricelist',
required=True,
readonly=True,
states={'draft': [('readonly', False)],
'sent': [('readonly', False)]},
help="Pricelist for current folio.")

View File

@@ -173,8 +173,7 @@ class HotelReservation(models.Model):
service_ids = fields.One2many('hotel.service', 'ser_room_line')
pricelist_id = fields.Many2one('product.pricelist',
related='folio_id.pricelist_id',
readonly="1")
related='folio_id.pricelist_id') #TODO: Warning Mens to update pricelist
checkin_partner_ids = fields.One2many('hotel.checkin.partner', 'reservation_id')
# TODO: As checkin_partner_count is a computed field, it can't not be used in a domain filer
# Non-stored field hotel.reservation.checkin_partner_count cannot be searched
@@ -290,6 +289,7 @@ class HotelReservation(models.Model):
board_services.append((0, False, {
'product_id': product.id,
'is_board_service': True,
'folio_id': vals.get('folio_id'),
}))
vals.update({'service_ids': board_services})
if self.compute_price_out_vals(vals):
@@ -327,6 +327,7 @@ class HotelReservation(models.Model):
board_services.append((0, False, {
'product_id': product.id,
'is_board_service': True,
'folio_id': record.folio_id.id or vals.get('folio_id')
}))
# NEED REVIEW: Why I need add manually the old IDs if board service is (0,0,(-)) ¿?¿?¿
record.update({'service_ids': [(6, 0, record.service_ids.ids)] + board_services})
@@ -610,6 +611,7 @@ class HotelReservation(models.Model):
vals = {
'product_id': product.id,
'is_board_service': True,
'folio_id': self.folio_id.id,
}
vals.update(self.env['hotel.service'].prepare_service_lines(
dfrom=self.checkin,
@@ -619,8 +621,10 @@ class HotelReservation(models.Model):
old_line_days=False))
board_services.append((0, False, vals))
other_services = self.service_ids.filtered(lambda r: r.is_board_service == False)
self.update({'service_ids': [(6, 0, other_services.ids)] + board_services})
for service in self.service_ids.filtered(lambda r: r.is_board_service == True):
service._compute_tax_ids()
service.price_unit = service._compute_price_unit()
"""
STATE WORKFLOW -----------------------------------------------------

View File

@@ -5,6 +5,10 @@ from odoo import models, fields, api, _
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
from datetime import timedelta
from odoo.exceptions import ValidationError
from odoo.addons import decimal_precision as dp
import logging
_logger = logging.getLogger(__name__)
class HotelService(models.Model):
_name = 'hotel.service'
@@ -50,31 +54,34 @@ class HotelService(models.Model):
product_qty = fields.Integer('Quantity')
days_qty = fields.Integer(compute="_compute_days_qty", store=True)
is_board_service = fields.Boolean()
pricelist_id = fields.Many2one(related='folio_id.pricelist_id')
# Non-stored related field to allow portal user to see the image of the product he has ordered
product_image = fields.Binary('Product Image', related="product_id.image", store=False)
channel_type = fields.Selection([
('door', 'Door'),
('mail', 'Mail'),
('phone', 'Phone'),
('call', 'Call Center'),
('web', 'Web')], 'Sales Channel')
currency_id = fields.Many2one('res.currency',
related='pricelist_id.currency_id',
string='Currency', readonly=True, required=True)
price_unit = fields.Float('Unit Price', required=True, digits=dp.get_precision('Product Price'), default=0.0)
tax_ids = fields.Many2many('account.tax', string='Taxes', domain=['|', ('active', '=', False), ('active', '=', True)])
discount = fields.Float(string='Discount (%)', digits=dp.get_precision('Discount'), default=0.0)
currency_id = fields.Many2one(related='folio_id.currency_id', store=True, string='Currency', readonly=True)
price_subtotal = fields.Monetary(string='Subtotal',
readonly=True,
store=True,
compute='_compute_amount_reservation')
compute='_compute_amount_service')
price_total = fields.Monetary(string='Total',
readonly=True,
store=True,
compute='_compute_amount_reservation')
compute='_compute_amount_service')
price_tax = fields.Float(string='Taxes',
readonly=True,
store=True,
compute='_compute_amount_reservation')
compute='_compute_amount_service')
@api.model
def create(self, vals):
vals.update(self._prepare_add_missing_fields(vals))
if self.compute_lines_out_vals(vals):
reservation = self.env['hotel.reservation'].browse(vals['ser_room_line'])
product = self.env['product.product'].browse(vals['product_id'])
@@ -114,6 +121,20 @@ class HotelService(models.Model):
res = super(HotelService, self).write(vals)
return res
@api.model
def _prepare_add_missing_fields(self, values):
""" Deduce missing required fields from the onchange """
res = {}
onchange_fields = ['price_unit','tax_ids']
if values.get('product_id'):
line = self.new(values)
if any(f not in values for f in onchange_fields):
line.onchange_product_calc_qty()
for field in onchange_fields:
if field not in values:
res[field] = line._fields[field].convert_to_write(line[field], line)
return res
@api.multi
def compute_lines_out_vals(self, vals):
"""
@@ -128,6 +149,26 @@ class HotelService(models.Model):
return True
return False
@api.multi
def _compute_tax_ids(self):
for record in self:
# If company_id is set, always filter taxes by the company
folio = self.folio_id or self.env.context.get('default_folio_id')
record.tax_id = record.product_id.taxes_id.filtered(lambda r: not record.company_id or r.company_id == folio.company_id)
@api.multi
def _get_display_price(self, product):
folio = self.folio_id or self.env.context.get('default_folio_id')
if folio.pricelist_id.discount_policy == 'with_discount':
return product.with_context(pricelist=folio.pricelist_id.id).price
product_context = dict(self.env.context, partner_id=folio.partner_id.id, date=folio.date_order, uom=self.product_id.uom_id.id)
final_price, rule_id = folio.pricelist_id.with_context(product_context).get_product_price_rule(self.product_id, self.product_qty or 1.0, folio.partner_id)
base_price, currency_id = self.with_context(product_context)._get_real_price_currency(product, rule_id, self.product_qty, product_id.uom_id, folio.pricelist_id.id)
if currency_id != folio.pricelist_id.currency_id.id:
base_price = self.env['res.currency'].browse(currency_id).with_context(product_context).compute(base_price, folio.pricelist_id.currency_id)
# negative discounts (= surcharge) are included in the display price
return max(base_price, final_price)
@api.onchange('product_id')
def onchange_product_calc_qty(self):
"""
@@ -135,11 +176,15 @@ class HotelService(models.Model):
configuration of the selected product, in per_day product configuration,
the qty is autocalculated and readonly based on service_lines qty
"""
if not self.product_id:
return
vals = {}
vals['product_qty'] = 1.0
for record in self:
if record.per_day and record.ser_room_line:
product = record.product_id
reservation = record.ser_room_line
record.update(self.prepare_service_lines(
vals.update(self.prepare_service_lines(
dfrom=reservation.checkin,
days=reservation.nights,
per_person=product.per_person,
@@ -148,6 +193,30 @@ class HotelService(models.Model):
if record.product_id.daily_limit > 0:
for day in record.service_line_ids:
day.no_free_resources()
"""
Compute tax and price unit
"""
self._compute_tax_ids()
vals['price_unit'] = self._compute_price_unit()
record.update(vals)
@api.multi
def _compute_price_unit(self):
"""
Compute tax and price unit
"""
folio = self.folio_id or self.env.context.get('default_folio_id')
product = self.product_id.with_context(
lang=folio.partner_id.lang,
partner=folio.partner_id.id,
quantity=self.product_qty,
date=folio.date_order,
pricelist=folio.pricelist_id.id,
uom=self.product_id.uom_id.id,
fiscal_position=False
)
return self.env['account.tax']._fix_tax_included_price_company(self._get_display_price(product), product.taxes_id, self.tax_ids, folio.company_id)
@api.model
def prepare_service_lines(self, **kwargs):
@@ -155,16 +224,15 @@ class HotelService(models.Model):
Prepare line and respect the old manual changes on lines
"""
cmds = [(5, 0, 0)]
old_lines_days = kwargs.get('old_lines_days')
old_line_days = kwargs.get('old_line_days')
total_qty = 0
day_qty = 1
if kwargs.get('per_person'): #WARNING: Change adults in reservation NOT update qty service!!
day_qty = kwargs.get('persons')
old_line_days = self.env['hotel.service.line'].browse(kwargs.get('old_line_days'))
for i in range(0, kwargs.get('days')):
idate = (fields.Date.from_string(kwargs.get('dfrom')) + timedelta(days=i)).strftime(
DEFAULT_SERVER_DATE_FORMAT)
if not old_lines_days or idate not in old_lines_days.mapped('date'):
if not old_line_days or idate not in old_line_days.mapped('date'):
cmds.append((0, False, {
'date': idate,
'day_qty': day_qty
@@ -176,15 +244,16 @@ class HotelService(models.Model):
total_qty = total_qty + old_line.day_qty
return {'service_line_ids': cmds, 'product_qty': total_qty}
@api.depends('qty_product', 'tax_id')
@api.depends('product_qty', 'discount', 'price_unit', 'tax_ids')
def _compute_amount_service(self):
"""
Compute the amounts of the service line.
"""
for record in self:
folio = record.folio_id or self.env.context.get('default_folio_id')
product = record.product_id
price = amount_room * (1 - (record.discount or 0.0) * 0.01)
taxes = record.tax_id.compute_all(price, record.currency_id, 1, product=product)
price = record.price_unit * (1 - (record.discount or 0.0) * 0.01)
taxes = record.tax_ids.compute_all(price, folio.currency_id, record.product_qty, product=product)
record.update({
'price_tax': sum(t.get('amount', 0.0) for t in taxes.get('taxes', [])),
'price_total': taxes['total_included'],
@@ -203,12 +272,20 @@ class HotelService(models.Model):
else:
vals = {'days_qty': 0}
record.update(vals)
@api.multi
def open_service_lines(self):
action = self.env.ref('hotel.action_hotel_services_form').read()[0]
action['views'] = [(self.env.ref('hotel.hotel_service_view_form').id, 'form')]
action['res_id'] = self.id
action['target'] = 'new'
return action
@api.constrains('qty_product')
def constrains_qty_per_day(self):
for record in self:
if record.per_day:
service_lines = self.env['hotel.service_line']
total_day_qty = sum(service_lines.with_context({'service_id': record.id}).mapped('day_qty'))
if record.qty_product != total_day_qty:
raise ValidationError (_('The quantity per line and per day does not correspond'))
#~ @api.constrains('product_qty')
#~ def constrains_qty_per_day(self):
#~ for record in self:
#~ if record.per_day:
#~ service_lines = self.env['hotel.service_line']
#~ total_day_qty = sum(service_lines.with_context({'service_id': record.id}).mapped('day_qty'))
#~ if record.product_qty != total_day_qty:
#~ raise ValidationError (_('The quantity per line and per day does not correspond'))

View File

@@ -287,19 +287,24 @@
</h3>
<group col="6">
<group string="General Info" name="contact_details" invisible="1">
<group string="General Info" name="contact_details" >
<field name="email" placeholder="email" widget="email" />
<field name="mobile" placeholder="mobile" widget="phone" />
<field name="phone" placeholder="phone" widget="phone" />
<field name="pricelist_id"/>
<field name="partner_internal_comment" string="Partner Note"/>
<field name="cancelled_reason" attrs="{'invisible':[('state','not in',('cancelled'))]}"/>
</group>
<group colspan="4" string="Reservation Details" name="reservation_details">
<field name="cancelled_reason" attrs="{'invisible':[('state','not in',('cancelled'))]}"/>
<field name="arrival_hour"/>
<field name="departure_hour"/>
<field name="nights" invisible="1"/>
<field name="board_service_id" />
<field name="name"/>
<field name="adults"/>
<field name="children"/>
<field name="room_type_id" on_change="1" options="{'no_create': True,'no_open': True}"
attrs="{'readonly':[('state','not in',('draft'))]}"/>
attrs="{'readonly':[('state','not in',('draft'))]}"/>
<field name="channel_type" attrs="{'required':[('reservation_type','not in',('staff','out'))]}"/>
</group>
<group class="oe_subtotal_footer" style="margin-right: 20px; !important" colspan="2" name="reservation_total" string="Amounts">
@@ -331,8 +336,6 @@
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
<!-- <field name="check_rooms" invisible="1"/> -->
<field name="checkin_partner_pending_count" invisible="1"/>
<!-- <field name="pricelist_id" invisible="1"/> -->
<field name="nights" invisible="1"/>
</group>
<notebook>
<page name="days" string="Service and Days">
@@ -417,7 +420,6 @@
domain="[('sale_ok', '=', True)]"
options="{'create': False, 'create_edit': False}" />
<field name="name"/>
<field name="pricelist_id"/>
<field name="ser_room_line" options="{'create': False, 'create_edit': False}"/>
<!-- <field name="product_uom_qty"
string="Ordered Qty"

View File

@@ -183,18 +183,18 @@
<field name="email" placeholder="email" widget="email" />
<field name="mobile" placeholder="mobile" widget="phone" />
<field name="phone" placeholder="phone" widget="phone" />
<field name="pricelist_id"/>
<field name="partner_internal_comment" string="Partner Note"/>
<field name="cancelled_reason" attrs="{'invisible':[('state','not in',('cancelled'))]}"/>
</group>
<group colspan="4" string="Reservation Details" name="reservation_details">
<field name="arrival_hour"/>
<field name="departure_hour"/>
<field name="nights" invisible="1"/>
<field name="board_service_id" />
<field name="name"/>
<field name="adults"/>
<field name="children"/>
<!-- <field name="room_type_id" on_change="1" options="{'no_create': True,'no_open': True}"
attrs="{'readonly':[('state','not in',('draft'))]}"/> -->
<field name="room_type_id" on_change="1" options="{'no_create': True,'no_open': True}"
attrs="{'readonly':[('state','not in',('draft'))]}"/>
<field name="channel_type" attrs="{'required':[('reservation_type','not in',('staff','out'))]}"/>
@@ -231,29 +231,36 @@
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
<!-- <field name="check_rooms" invisible="1"/> -->
<field name="checkin_partner_pending_count" invisible="1"/>
<!-- <field name="pricelist_id" invisible="1"/> -->
<field name="nights" invisible="1"/>
<!-- <field name="product_uom" string="Rent(UOM)" invisible="1" /> -->
</group>
<notebook>
<page name="days" string="Service and Days">
<group col="9">
<group colspan="6" string="Reservation Services" name="reservation_services" attrs="{'invisible': [('folio_id','=',False)]}">
<group string="Reservation Services" name="reservation_services" attrs="{'invisible': [('folio_id','=',False)]}">
<field name="service_ids"
nolabel="1" style="padding-right:10px !important;">
<!-- <field name="service_ids"> -->
<tree string="Services">
context="{'default_ser_room_line': active_id, 'default_folio_id': folio_id}"
nolabel="1">
<tree string="Services" editable="bottom">
<!-- <field name="sequence" widget="handle"/> -->
<field name="per_day" />
<field name="is_board_service" />
<field name="per_day" invisible="1"/>
<field name="is_board_service" invisible="1" />
<field name="folio_id" invisible="1"/>
<field name="product_id"
domain="[('sale_ok', '=', True)]"
options="{'create': False, 'create_edit': False}" />
<!-- <field name="layout_category_id" groups="sale.group_sale_layout"/> -->
<field name="name"/>
<field name="product_qty" attrs="{'readonly': [('per_day','=',True)]}" force_save="1"/>
<field name="days_qty" />
<field name="product_qty" attrs="{'readonly': [('per_day','=',True)]}" force_save="1"/>
<button type="object" class="oe_stat_button"
id="go_service_lines" icon="fa fa-2x fa-bars"
name="open_service_lines"
attrs="{'invisible': [('per_day','=',False)]}"/>
<field name="days_qty" invisible="1"/>
<field name="price_unit" />
<field name="discount" />
<field name="tax_ids" widget="many2many_tags"/>
<field name="price_subtotal" />
<field name="price_tax" />
<field name="price_total" />
<field name="service_line_ids" invisible="1">
<tree string="Days" >
<field name="date" />
@@ -261,26 +268,9 @@
</tree>
</field>
</tree>
<form string="Services">
<!-- <field name="sequence" widget="handle"/> -->
<field name="per_day" />
<field name="folio_id" invisible="1"/>
<field name="product_id"
domain="[('sale_ok', '=', True)]"
options="{'create': False, 'create_edit': False}" />
<!-- <field name="layout_category_id" groups="sale.group_sale_layout"/> -->
<field name="name"/>
<field name="product_qty" attrs="{'readonly': [('per_day','=',True)]}" force_save="1"/>
<field name="service_line_ids">
<tree string="Days" >
<field name="date" />
<field name="day_qty" />
</tree>
</field>
</form>
</field>
</group>
<group colspan="4" string="Days" name="days">
<group string="Days" name="days">
<field name="reservation_line_ids" nolabel="1">
<tree create="false" delete="false" editable="bottom">
<field name="date" />
@@ -289,7 +279,6 @@
</tree>
</field>
</group>
</group>
</page>
<page name="others" string="Others">
<group>

View File

@@ -9,19 +9,29 @@
<form string="Reservation Service">
<sheet>
<h1>
<label string="Service" />
<field name="name" select="1" />
<field name="name" readonly="1"/>
</h1>
<group>
<field name="product_id" />
<field name="product_qty" />
</group>
<group>
<field name="price_subtotal" />
<field name="price_total" />
</group>
<field name="service_line_ids">
<tree string="Days" >
<field name="per_day" invisible="1"/>
<field name="is_board_service" invisible="1" />
<field name="folio_id" invisible="1"/>
<field name="product_id"
domain="[('sale_ok', '=', True)]"
options="{'create': False, 'create_edit': False}"
invisible="1" />
<!-- <field name="layout_category_id" groups="sale.group_sale_layout"/> -->
<field name="product_qty" invisible="0"
attrs="{'readonly': [('per_day','=',True)]}"
force_save="1"/>
<field name="days_qty" invisible="0"/>
<field name="price_unit" invisible="1" />
<field name="discount" invisible="1"/>
<field name="tax_ids" widget="many2many_tags"
invisible="1"/>
<field name="price_subtotal" invisible="1"/>
<field name="price_tax" invisible="1"/>
<field name="price_total" invisible="1"/>
<field name="service_line_ids" nolabel="1">
<tree string="Days" editable="bottom" >
<field name="date" />
<field name="day_qty" />
</tree>
@@ -68,36 +78,10 @@
<field name="name">Hotel Services</field>
<field name="res_model">hotel.service</field>
<field name="view_type">form</field>
<!-- <field name="context">{'default_isservice':1}
</field> -->
<field name="view_mode">tree,form</field>
</record>
<menuitem name="Services" id="menu_open_hotel_services_form"
action="action_hotel_services_form" sequence="8"
parent="hotel.menu_hotel_service" />
<!-- Categories for Services -->
<record model="ir.actions.act_window" id="hotel_service_category_action">
<field name="name">Services by Category</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.category</field>
<!-- <field name="domain">[('parent_id','=',False),('isservicetype','=',True)]
</field> -->
<field name="view_type">tree</field>
<!-- <field name="view_id" ref="product_category_tree_view" /> -->
</record>
<!--record id="ir_service_category_open" model='ir.default'>
<field eval="'tree_but_open'" name="key2"/>
<field eval="'product.category'" name="model"/>
<field name="name">Services</field>
<field eval="'ir.actions.act_window,%d'%action_room_cate" name="value"/>
</record-->
<menuitem name="Services by Type" id="menu_hotel_service_category_action"
action="hotel_service_category_action" sequence="10"
parent="hotel.menu_hotel_service" />
</odoo>