[IMP] Models py guide lines

This commit is contained in:
Dario Lodeiros
2019-09-24 20:09:26 +02:00
parent 22b7ec6626
commit 857a8a6486
20 changed files with 707 additions and 459 deletions

View File

@@ -943,8 +943,9 @@ class HotelReservation(models.Model):
# Yes?, then, this is share folio ;)
for record in self:
if record.folio_id:
record.shared_folio = len(record.folio_id.reservation_ids) > 1 or \
any(record.folio_id.service_ids.filtered(
record.shared_folio = \
len(record.folio_id.reservation_ids) > 1 \
or any(record.folio_id.service_ids.filtered(
lambda x: x.ser_room_line.id != record.id))
@api.multi

View File

@@ -7,8 +7,9 @@ from odoo.exceptions import ValidationError
class HotelRoom(models.Model):
""" The rooms for lodging can be for sleeping, usually called rooms, and also
for speeches (conference rooms), parking, relax with cafe con leche, spa...
""" The rooms for lodging can be for sleeping, usually called rooms,
and also for speeches (conference rooms), parking,
relax with cafe con leche, spa...
"""
_name = 'hotel.room'
_description = 'Hotel Room'
@@ -16,15 +17,24 @@ class HotelRoom(models.Model):
# Fields declaration
name = fields.Char('Room Name', required=True)
hotel_id = fields.Many2one('hotel.property', store=True, readonly=True,
related='room_type_id.hotel_id')
room_type_id = fields.Many2one('hotel.room.type', 'Hotel Room Type',
required=True,
ondelete='restrict')
shared_room_id = fields.Many2one('hotel.shared.room', 'Shared Room',
default=False)
floor_id = fields.Many2one('hotel.floor', 'Ubication',
help='At which floor the room is located.')
hotel_id = fields.Many2one(
'hotel.property',
store=True,
readonly=True,
related='room_type_id.hotel_id')
room_type_id = fields.Many2one(
'hotel.room.type',
'Hotel Room Type',
required=True,
ondelete='restrict')
shared_room_id = fields.Many2one(
'hotel.shared.room',
'Shared Room',
default=False)
floor_id = fields.Many2one(
'hotel.floor',
'Ubication',
help='At which floor the room is located.')
capacity = fields.Integer('Capacity')
to_be_cleaned = fields.Boolean('To be Cleaned', default=False)
extra_beds_allowed = fields.Integer('Extra beds allowed',
@@ -43,29 +53,36 @@ class HotelRoom(models.Model):
def _check_capacity(self):
for record in self:
if record.capacity < 1:
raise ValidationError(_("The capacity of the room must be greater than 0."))
raise ValidationError(_("The capacity of the \
room must be greater than 0."))
# CRUD methods
@api.model
def create(self, vals):
if vals.get('hotel_id', self.env.user.hotel_id.id) != self.env['hotel.room.type'].browse(
vals['room_type_id']).hotel_id.id:
raise ValidationError(_("A room cannot be created in a room type of another hotel."))
if vals.get('hotel_id', self.env.user.hotel_id.id) != \
self.env['hotel.room.type'].browse(
vals['room_type_id']).hotel_id.id:
raise ValidationError(
_("A room cannot be created in a room type \
of another hotel."))
return super().create(vals)
@api.multi
def write(self, vals):
for record in self:
if vals.get('hotel_id', record.hotel_id.id) != record.hotel_id.id:
raise ValidationError(_("A room cannot be changed to another hotel.") + " " +
_("%s does not belong to %s.")
% (record, record.hotel_id))
raise ValidationError(
_("A room cannot be changed to another hotel.") + " " +
_("%s does not belong to %s.")
% (record, record.hotel_id))
room_type_ids = self.env['hotel.room.type'].search([
('hotel_id', '=', record.hotel_id.id)
]).ids
if vals.get('room_type_id', record.room_type_id.id) not in room_type_ids:
raise ValidationError(_("A room cannot be changed to a room type of another hotel or "
"unlinked from a room type."))
if vals.get('room_type_id', record.room_type_id.id) \
not in room_type_ids:
raise ValidationError(
_("A room cannot be changed to a room type of \
another hotel or unlinked from a room type."))
return super().write(vals)
# Business methods

View File

@@ -21,30 +21,47 @@ class HotelRoomType(models.Model):
return self.env.user.hotel_id
# Fields declaration
product_id = fields.Many2one('product.product', 'Product Room Type',
required=True, delegate=True,
ondelete='cascade')
hotel_id = fields.Many2one('hotel.property', 'Hotel', required=True, ondelete='restrict',
default=_get_default_hotel,)
room_ids = fields.One2many('hotel.room', 'room_type_id', 'Rooms')
class_id = fields.Many2one('hotel.room.type.class', 'Hotel Type Class')
product_id = fields.Many2one(
'product.product',
'Product Room Type',
required=True,
delegate=True,
ondelete='cascade')
hotel_id = fields.Many2one(
'hotel.property',
'Hotel',
required=True,
ondelete='restrict',
default=_get_default_hotel,)
room_ids = fields.One2many(
'hotel.room',
'room_type_id',
'Rooms')
class_id = fields.Many2one(
'hotel.room.type.class',
'Hotel Type Class')
board_service_room_type_ids = fields.One2many(
'hotel.board.service.room.type', 'hotel_room_type_id', string='Board Services')
room_amenity_ids = fields.Many2many('hotel.amenity',
'hotel_room_type_aminity_rel',
'room_type_ids', 'amenity_ids',
string='Room Type Amenities',
help='List of Amenities.')
'hotel.board.service.room.type',
'hotel_room_type_id',
string='Board Services')
room_amenity_ids = fields.Many2many(
'hotel.amenity',
'hotel_room_type_aminity_rel',
'room_type_ids',
'amenity_ids',
string='Room Type Amenities',
help='List of Amenities.')
code_type = fields.Char('Code', required=True, )
shared_room = fields.Boolean('Shared Room', default=False,
help="This room type is reservation by beds")
total_rooms_count = fields.Integer(compute='_compute_total_rooms', store=True)
total_rooms_count = fields.Integer(
compute='_compute_total_rooms', store=True)
active = fields.Boolean('Active', default=True)
sequence = fields.Integer('Sequence', default=0)
_sql_constraints = [
('code_type_hotel_unique', 'unique(code_type, hotel_id)', 'Room Type Code must be unique by Hotel!'),
('code_type_hotel_unique', 'unique(code_type, hotel_id)',
'Room Type Code must be unique by Hotel!'),
]
# Constraints and onchanges
@@ -80,7 +97,8 @@ class HotelRoomType(models.Model):
def check_availability_room_type(self, dfrom, dto,
room_type_id=False, notthis=[]):
"""
Check the max availability for an specific type of room in a range of dates
Check the max availability for an specific
type of room in a range of dates
"""
reservations = self.env['hotel.reservation'].get_reservations(dfrom,
dto)
@@ -118,10 +136,11 @@ class HotelRoomType(models.Model):
raise ValidationError(_('Date From and days are mandatory'))
partner_id = kwargs.get('partner_id', False)
partner = self.env['res.partner'].browse(partner_id)
pricelist_id = kwargs.get('pricelist_id',
partner.property_product_pricelist.id and
partner.property_product_pricelist.id or
self.env.user.hotel_id.default_pricelist_id.id)
pricelist_id = kwargs.get(
'pricelist_id',
partner.property_product_pricelist.id and
partner.property_product_pricelist.id or
self.env.user.hotel_id.default_pricelist_id.id)
vals.update({
'partner_id': partner_id if partner_id else False,
'discount': discount,
@@ -129,14 +148,16 @@ class HotelRoomType(models.Model):
rate_vals = {}
for room_type in room_types:
vals.update({'room_type_id': room_type.id})
room_vals = self.env['hotel.reservation'].prepare_reservation_lines(
date_from,
days,
pricelist_id=pricelist_id,
vals=vals,
update_old_prices=False)
room_vals = self.env['hotel.reservation'].\
prepare_reservation_lines(
date_from,
days,
pricelist_id=pricelist_id,
vals=vals,
update_old_prices=False)
rate_vals.update({
room_type.id: [item[2] for item in \
room_vals['reservation_line_ids'] if item[2]]
room_type.id: [item[2] for item in
room_vals['reservation_line_ids'] if
item[2]]
})
return rate_vals

View File

@@ -14,6 +14,7 @@ class HotelRoomTypeClass(models.Model):
_description = "Room Type Class"
_order = "sequence, name, code_class"
# Fields declaration
name = fields.Char('Class Name', required=True, translate=True)
# Relationship between models
hotel_ids = fields.Many2many(

View File

@@ -15,11 +15,19 @@ class HotelRoomTypeRestriction(models.Model):
# Fields declaration
name = fields.Char('Restriction Plan Name', required=True)
hotel_id = fields.Many2one('hotel.property', 'Hotel', required=True, ondelete='restrict',
default=_get_default_hotel)
item_ids = fields.One2many('hotel.room.type.restriction.item',
'restriction_id', string='Restriction Items',
copy=True)
active = fields.Boolean('Active', default=True,
help='If unchecked, it will allow you to hide the '
'restriction plan without removing it.')
hotel_id = fields.Many2one(
'hotel.property',
'Hotel',
required=True,
ondelete='restrict',
default=_get_default_hotel)
item_ids = fields.One2many(
'hotel.room.type.restriction.item',
'restriction_id',
string='Restriction Items',
copy=True)
active = fields.Boolean(
'Active',
default=True,
help='If unchecked, it will allow you to hide the '
'restriction plan without removing it.')

View File

@@ -25,7 +25,8 @@ class HotelRoomTypeRestrictionItem(models.Model):
_sql_constraints = [('room_type_registry_unique',
'unique(restriction_id, room_type_id, date)',
'Only can exists one restriction in the same day for the same room type!')]
'Only can exists one restriction in the same \
day for the same room type!')]
# Constraints and onchanges
@api.multi

View File

@@ -7,7 +7,6 @@ from odoo.tools import (
float_compare,
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__)
@@ -17,19 +16,7 @@ class HotelService(models.Model):
_name = 'hotel.service'
_description = 'Hotel Services and its charges'
@api.model
def name_search(self, name='', args=None, operator='ilike', limit=100):
if args is None:
args = []
if not(name == '' and operator == 'ilike'):
args += [
'|',
('ser_room_line.name', operator, name),
('name', operator, name)
]
return super(HotelService, self).name_search(
name='', args=args, operator='ilike', limit=limit)
# Default methods
@api.multi
def name_get(self):
result = []
@@ -57,239 +44,233 @@ class HotelService(models.Model):
return self._context['folio_id']
return False
# Fields declaration
name = fields.Char('Service description', required=True)
product_id = fields.Many2one(
'product.product',
'Service',
ondelete='restrict',
required=True)
folio_id = fields.Many2one(
'hotel.folio',
'Folio',
ondelete='cascade',
default=_default_folio_id)
ser_room_line = fields.Many2one(
'hotel.reservation',
'Room',
default=_default_ser_room_line)
service_line_ids = fields.One2many(
'hotel.service.line',
'service_id')
company_id = fields.Many2one(
related='folio_id.company_id',
string='Company',
store=True,
readonly=True)
hotel_id = fields.Many2one(
'hotel.property',
store=True,
readonly=True,
related='folio_id.hotel_id')
tax_ids = fields.Many2many(
'account.tax',
string='Taxes',
domain=['|', ('active', '=', False), ('active', '=', True)])
invoice_line_ids = fields.Many2many(
'account.invoice.line',
'service_line_invoice_rel',
'service_id',
'invoice_line_id',
string='Invoice Lines',
copy=False)
analytic_tag_ids = fields.Many2many(
'account.analytic.tag',
string='Analytic Tags')
currency_id = fields.Many2one(
related='folio_id.currency_id',
store=True,
string='Currency',
readonly=True)
sequence = fields.Integer(string='Sequence', default=10)
state = fields.Selection(related='folio_id.state')
per_day = fields.Boolean(related='product_id.per_day', related_sudo=True)
product_qty = fields.Integer('Quantity', default=1)
days_qty = fields.Integer(compute="_compute_days_qty", store=True)
is_board_service = fields.Boolean()
to_print = fields.Boolean('Print', help='Print in Folio Report')
# 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, related_sudo=True)
invoice_status = fields.Selection([
('invoiced', 'Fully Invoiced'),
('to invoice', 'To Invoice'),
('no', 'Nothing to Invoice')],
string='Invoice Status',
compute='_compute_invoice_status',
store=True,
readonly=True,
default='no')
channel_type = fields.Selection([
('door', 'Door'),
('mail', 'Mail'),
('phone', 'Phone'),
('call', 'Call Center'),
('web', 'Web')],
string='Sales Channel')
price_unit = fields.Float(
'Unit Price',
required=True,
digits=dp.get_precision('Product Price'), default=0.0)
discount = fields.Float(
string='Discount (%)',
digits=dp.get_precision('Discount'), default=0.0)
qty_to_invoice = fields.Float(
compute='_get_to_invoice_qty',
string='To Invoice',
store=True,
readonly=True,
digits=dp.get_precision('Product Unit of Measure'))
qty_invoiced = fields.Float(
compute='_get_invoice_qty',
string='Invoiced',
store=True,
readonly=True,
digits=dp.get_precision('Product Unit of Measure'))
price_subtotal = fields.Monetary(
string='Subtotal',
readonly=True,
store=True,
compute='_compute_amount_service')
price_total = fields.Monetary(
string='Total',
readonly=True,
store=True,
compute='_compute_amount_service')
price_tax = fields.Float(
string='Taxes',
readonly=True,
store=True,
compute='_compute_amount_service')
# Compute and Search methods
@api.depends('qty_invoiced', 'product_qty', 'folio_id.state')
def _get_to_invoice_qty(self):
"""
Compute the quantity to invoice. If the invoice policy is order, the quantity to invoice is
calculated from the ordered quantity. Otherwise, the quantity delivered is used.
Compute the quantity to invoice. If the invoice policy is order,
the quantity to invoice is calculated from the ordered quantity.
Otherwise, the quantity delivered is used.
"""
for line in self:
if line.folio_id.state not in ['draft']:
line.qty_to_invoice = line.product_qty - line.qty_invoiced
line.qty_to_invoice = line.product_qty - line.qty_invoiced
else:
line.qty_to_invoice = 0
@api.depends('invoice_line_ids.invoice_id.state', 'invoice_line_ids.quantity')
@api.depends('invoice_line_ids.invoice_id.state',
'invoice_line_ids.quantity')
def _get_invoice_qty(self):
"""
Compute the quantity invoiced. If case of a refund, the quantity invoiced is decreased. Note
that this is the case only if the refund is generated from the SO and that is intentional: if
a refund made would automatically decrease the invoiced quantity, then there is a risk of reinvoicing
it automatically, which may not be wanted at all. That's why the refund has to be created from the SO
Compute the quantity invoiced. If case of a refund,
the quantity invoiced is decreased. Note that this is the case only
if the refund is generated from the Folio and that is intentional: if
a refund made would automatically decrease the invoiced quantity,
then there is a risk of reinvoicing it automatically, which may
not be wanted at all. That's why the refund has to be
created from the Folio
"""
for line in self:
qty_invoiced = 0.0
for invoice_line in line.invoice_line_ids:
if invoice_line.invoice_id.state != 'cancel':
if invoice_line.invoice_id.type == 'out_invoice':
qty_invoiced += invoice_line.uom_id._compute_quantity(invoice_line.quantity, line.product_id.uom_id)
qty_invoiced += invoice_line.uom_id._compute_quantity(
invoice_line.quantity, line.product_id.uom_id)
elif invoice_line.invoice_id.type == 'out_refund':
qty_invoiced -= invoice_line.uom_id._compute_quantity(invoice_line.quantity, line.product_id.uom_id)
qty_invoiced -= invoice_line.uom_id._compute_quantity(
invoice_line.quantity, line.product_id.uom_id)
line.qty_invoiced = qty_invoiced
@api.depends('product_qty', 'qty_to_invoice', 'qty_invoiced')
def _compute_invoice_status(self):
"""
Compute the invoice status of a SO line. Possible statuses:
- no: if the SO is not in status 'sale' or 'done', we consider that there is nothing to
invoice. This is also hte default value if the conditions of no other status is met.
- to invoice: we refer to the quantity to invoice of the line. Refer to method
`_get_to_invoice_qty()` for more information on how this quantity is calculated.
- upselling: this is possible only for a product invoiced on ordered quantities for which
we delivered more than expected. The could arise if, for example, a project took more
time than expected but we decided not to invoice the extra cost to the client. This
occurs onyl in state 'sale', so that when a SO is set to done, the upselling opportunity
is removed from the list.
- invoiced: the quantity invoiced is larger or equal to the quantity ordered.
- no: if the SO is not in status 'sale' or 'done',
we consider that there is nothing to invoice.
This is also hte default value if the conditions of no other
status is met.
- to invoice: we refer to the quantity to invoice of the line.
Refer to method `_get_to_invoice_qty()` for more information on
how this quantity is calculated.
- upselling: this is possible only for a product invoiced on ordered
quantities for which we delivered more than expected.
The could arise if, for example, a project took more time than
expected but we decided not to invoice the extra cost to the
client. This occurs onyl in state 'sale', so that when a Folio
is set to done, the upselling opportunity is removed from the list.
- invoiced: the quantity invoiced is larger or equal to the
quantity ordered.
"""
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
precision = self.env['decimal.precision'].precision_get(
'Product Unit of Measure')
for line in self:
if line.folio_id.state in ('draft'):
line.invoice_status = 'no'
elif not float_is_zero(line.qty_to_invoice, precision_digits=precision):
elif not float_is_zero(line.qty_to_invoice,
precision_digits=precision):
line.invoice_status = 'to invoice'
elif float_compare(line.qty_invoiced, line.product_qty, precision_digits=precision) >= 0:
elif float_compare(line.qty_invoiced, line.product_qty,
precision_digits=precision) >= 0:
line.invoice_status = 'invoiced'
else:
line.invoice_status = 'no'
name = fields.Char('Service description', required=True)
sequence = fields.Integer(string='Sequence', default=10)
product_id = fields.Many2one('product.product', 'Service',
ondelete='restrict', required=True)
folio_id = fields.Many2one('hotel.folio', 'Folio',
ondelete='cascade',
default=_default_folio_id)
state = fields.Selection(related='folio_id.state')
ser_room_line = fields.Many2one('hotel.reservation', 'Room',
default=_default_ser_room_line)
per_day = fields.Boolean(related='product_id.per_day', related_sudo=True)
service_line_ids = fields.One2many('hotel.service.line', 'service_id')
product_qty = fields.Integer('Quantity', default=1)
days_qty = fields.Integer(compute="_compute_days_qty", store=True)
is_board_service = fields.Boolean()
to_print = fields.Boolean('Print', help='Print in Folio Report')
# 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, related_sudo=True)
company_id = fields.Many2one(related='folio_id.company_id', string='Company', store=True, readonly=True)
hotel_id = fields.Many2one('hotel.property', store=True, readonly=True,
related='folio_id.hotel_id')
invoice_status = fields.Selection([
('invoiced', 'Fully Invoiced'),
('to invoice', 'To Invoice'),
('no', 'Nothing to Invoice')
], string='Invoice Status', compute='_compute_invoice_status',
store=True, readonly=True, default='no')
channel_type = fields.Selection([
('door', 'Door'),
('mail', 'Mail'),
('phone', 'Phone'),
('call', 'Call Center'),
('web', 'Web')], 'Sales Channel')
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)
invoice_line_ids = fields.Many2many('account.invoice.line', 'service_line_invoice_rel', 'service_id', 'invoice_line_id', string='Invoice Lines', copy=False)
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags')
qty_to_invoice = fields.Float(
compute='_get_to_invoice_qty', string='To Invoice', store=True, readonly=True,
digits=dp.get_precision('Product Unit of Measure'))
qty_invoiced = fields.Float(
compute='_get_invoice_qty', string='Invoiced', store=True, readonly=True,
digits=dp.get_precision('Product Unit of Measure'))
price_subtotal = fields.Monetary(string='Subtotal',
readonly=True,
store=True,
compute='_compute_amount_service')
price_total = fields.Monetary(string='Total',
readonly=True,
store=True,
compute='_compute_amount_service')
price_tax = fields.Float(string='Taxes',
readonly=True,
store=True,
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'])
if reservation.splitted:
checkin = reservation.real_checkin
checkout = reservation.real_checkout
else:
checkin = reservation.checkin
checkout = reservation.checkout
checkin_dt = fields.Date.from_string(checkin)
checkout_dt = fields.Date.from_string(checkout)
nights = abs((checkout_dt - checkin_dt).days)
vals.update(self.prepare_service_ids(
dfrom=checkin,
days=nights,
per_person=product.per_person,
persons=reservation.adults,
old_day_lines=False,
consumed_on=product.consumed_on,
))
record = super(HotelService, self).create(vals)
return record
@api.multi
def write(self, vals):
#If you write product, We must check if its necesary create or delete
#service lines
if vals.get('product_id'):
product = self.env['product.product'].browse(vals.get('product_id'))
if not product.per_day:
vals.update({
'service_line_ids': [(5, 0, 0)]
})
else:
for record in self:
reservations = self.env['hotel.reservation']
reservation = reservations.browse(vals['ser_room_line']) \
if 'ser_room_line' in vals else record.ser_room_line
if reservation.splitted:
checkin = reservation.real_checkin
checkout = reservation.real_checkout
else:
checkin = reservation.checkin
checkout = reservation.checkout
checkin_dt = fields.Date.from_string(checkin)
checkout_dt = fields.Date.from_string(checkout)
nights = abs((checkout_dt - checkin_dt).days)
record.update(record.prepare_service_ids(
dfrom=checkin,
days=nights,
per_person=product.per_person,
persons=reservation.adults,
old_line_days=self.service_line_ids,
consumed_on=product.consumed_on,
))
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', 'name']
if values.get('product_id'):
line = self.new(values)
if any(f not in values for f in onchange_fields):
line.onchange_product_id()
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):
@api.depends('product_qty', 'discount', 'price_unit', 'tax_ids')
def _compute_amount_service(self):
"""
Compute if It is necesary service days in write/create
Compute the amounts of the service line.
"""
if not vals:
vals = {}
if 'product_id' in vals:
product = self.env['product.product'].browse(vals['product_id']) \
if 'product_id' in vals else self.product_id
if (product.per_day and 'service_line_ids' not in vals):
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 = record.folio_id or self.env['hotel.folio'].browse(self.env.context.get('default_folio_id'))
reservation = record.ser_room_line or self.env.context.get('ser_room_line')
origin = folio if folio else reservation
record.tax_ids = record.product_id.taxes_id.filtered(lambda r: not record.company_id or r.company_id == origin.company_id)
folio = record.folio_id or self.env['hotel.folio'].browse(
self.env.context.get('default_folio_id'))
reservation = record.ser_room_line or self.env.context.get(
'ser_room_line')
currency = folio.currency_id if folio else reservation.currency_id
product = record.product_id
price = record.price_unit * (1 - (record.discount or 0.0) * 0.01)
taxes = record.tax_ids.compute_all(
price, currency, record.product_qty, product=product)
@api.multi
def _get_display_price(self, product):
folio = self.folio_id or self.env.context.get('default_folio_id')
reservation = self.ser_room_line or self.env.context.get('ser_room_line')
origin = folio if folio else reservation
if origin.pricelist_id.discount_policy == 'with_discount':
return product.with_context(pricelist=origin.pricelist_id.id).price
product_context = dict(self.env.context, partner_id=origin.partner_id.id, date=folio.date_order if folio else fields.Date.today(), uom=self.product_id.uom_id.id)
final_price, rule_id = origin.pricelist_id.with_context(product_context).get_product_price_rule(self.product_id, self.product_qty or 1.0, origin.partner_id)
base_price, currency_id = self.with_context(product_context)._get_real_price_currency(product, rule_id, self.product_qty, self.product_id.uom_id, origin.pricelist_id.id)
if currency_id != origin.pricelist_id.currency_id.id:
base_price = self.env['res.currency'].browse(currency_id).with_context(product_context).compute(base_price, origin.pricelist_id.currency_id)
# negative discounts (= surcharge) are included in the display price
return max(base_price, final_price)
record.update({
'price_tax': sum(t.get('amount', 0.0) for t in
taxes.get('taxes', [])),
'price_total': taxes['total_included'],
'price_subtotal': taxes['total_excluded'],
})
@api.depends('service_line_ids.day_qty')
def _compute_days_qty(self):
for record in self:
if record.per_day:
qty = sum(record.service_line_ids.mapped('day_qty'))
vals = {
'days_qty': qty,
'product_qty': qty
}
else:
vals = {'days_qty': 0}
record.update(vals)
# Constraints and onchanges
@api.onchange('product_id')
def onchange_product_id(self):
"""
Compute the default quantity according to the
configuration of the selected product, in per_day product configuration,
the qty is autocalculated and readonly based on service_ids qty
configuration of the selected product, in per_day
product configuration, the qty is autocalculated and
readonly based on service_ids qty
"""
if not self.product_id:
return
@@ -320,11 +301,8 @@ class HotelService(models.Model):
persons=reservation.adults,
old_line_days=record.service_line_ids,
consumed_on=product.consumed_on,
))
))
if record.product_id.daily_limit > 0:
for i in range(0, nights):
idate = (fields.Date.from_string(checkin) + timedelta(days=i)).strftime(
DEFAULT_SERVER_DATE_FORMAT)
for day in record.service_line_ids:
day.no_free_resources()
"""
@@ -358,11 +336,175 @@ class HotelService(models.Model):
vals['price_unit'] = self._compute_price_unit()
record.update(vals)
# Action methods
@api.multi
def open_service_ids(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
# ORM Overrides
@api.model
def name_search(self, name='', args=None, operator='ilike', limit=100):
if args is None:
args = []
if not(name == '' and operator == 'ilike'):
args += [
'|',
('ser_room_line.name', operator, name),
('name', operator, name)
]
return super(HotelService, self).name_search(
name='', args=args, operator='ilike', limit=limit)
@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'])
if reservation.splitted:
checkin = reservation.real_checkin
checkout = reservation.real_checkout
else:
checkin = reservation.checkin
checkout = reservation.checkout
checkin_dt = fields.Date.from_string(checkin)
checkout_dt = fields.Date.from_string(checkout)
nights = abs((checkout_dt - checkin_dt).days)
vals.update(self.prepare_service_ids(
dfrom=checkin,
days=nights,
per_person=product.per_person,
persons=reservation.adults,
old_day_lines=False,
consumed_on=product.consumed_on,
))
record = super(HotelService, self).create(vals)
return record
@api.multi
def write(self, vals):
# If you write product, We must check if its necesary create or delete
# service lines
if vals.get('product_id'):
product = self.env['product.product'].browse(
vals.get('product_id'))
if not product.per_day:
vals.update({
'service_line_ids': [(5, 0, 0)]
})
else:
for record in self:
reservations = self.env['hotel.reservation']
reservation = reservations.browse(vals['ser_room_line']) \
if 'ser_room_line' in vals else record.ser_room_line
if reservation.splitted:
checkin = reservation.real_checkin
checkout = reservation.real_checkout
else:
checkin = reservation.checkin
checkout = reservation.checkout
checkin_dt = fields.Date.from_string(checkin)
checkout_dt = fields.Date.from_string(checkout)
nights = abs((checkout_dt - checkin_dt).days)
record.update(record.prepare_service_ids(
dfrom=checkin,
days=nights,
per_person=product.per_person,
persons=reservation.adults,
old_line_days=self.service_line_ids,
consumed_on=product.consumed_on,
))
res = super(HotelService, self).write(vals)
return res
# Business methods
@api.model
def _prepare_add_missing_fields(self, values):
""" Deduce missing required fields from the onchange """
res = {}
onchange_fields = ['price_unit', 'tax_ids', 'name']
if values.get('product_id'):
line = self.new(values)
if any(f not in values for f in onchange_fields):
line.onchange_product_id()
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):
"""
Compute if It is necesary service days in write/create
"""
if not vals:
vals = {}
if 'product_id' in vals:
product = self.env['product.product'].browse(vals['product_id']) \
if 'product_id' in vals else self.product_id
if (product.per_day and 'service_line_ids' not in vals):
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 = record.folio_id or self.env['hotel.folio'].browse(
self.env.context.get('default_folio_id'))
reservation = record.ser_room_line or self.env.context.get(
'ser_room_line')
origin = folio if folio else reservation
record.tax_ids = record.product_id.taxes_id.filtered(
lambda r: not record.company_id or
r.company_id == origin.company_id)
@api.multi
def _get_display_price(self, product):
folio = self.folio_id or self.env.context.get('default_folio_id')
reservation = self.ser_room_line or self.env.context.get(
'ser_room_line')
origin = folio if folio else reservation
if origin.pricelist_id.discount_policy == 'with_discount':
return product.with_context(pricelist=origin.pricelist_id.id).price
product_context = dict(
self.env.context,
partner_id=origin.partner_id.id,
date=folio.date_order if folio else fields.Date.today(),
uom=self.product_id.uom_id.id)
final_price, rule_id = origin.pricelist_id.with_context(
product_context).get_product_price_rule(
self.product_id,
self.product_qty or 1.0,
origin.partner_id)
base_price, currency_id = self.with_context(
product_context)._get_real_price_currency(
product,
rule_id,
self.product_qty,
self.product_id.uom_id,
origin.pricelist_id.id)
if currency_id != origin.pricelist_id.currency_id.id:
base_price = self.env['res.currency'].browse(
currency_id).with_context(product_context).compute(
base_price,
origin.pricelist_id.currency_id)
# negative discounts (= surcharge) are included in the display price
return max(base_price, final_price)
@api.multi
def _compute_price_unit(self):
self.ensure_one()
folio = self.folio_id or self.env.context.get('default_folio_id')
reservation = self.ser_room_line or self.env.context.get('ser_room_line')
reservation = self.ser_room_line or self.env.context.get(
'ser_room_line')
origin = reservation if reservation else folio
if origin:
partner = origin.partner_id
@@ -371,22 +513,27 @@ class HotelService(models.Model):
board_room_type = reservation.board_service_room_id
if board_room_type.price_type == 'fixed':
return self.env['hotel.board.service.room.type.line'].search([
('hotel_board_service_room_type_id', '=', board_room_type.id),
('hotel_board_service_room_type_id',
'=', board_room_type.id),
('product_id', '=', self.product_id.id)]).amount
else:
return (reservation.price_total * self.env['hotel.board.service.room.type.line'].search([
('hotel_board_service_room_type_id', '=', board_room_type.id),
('product_id', '=', self.product_id.id)]).amount) / 100
return (reservation.price_total *
self.env['hotel.board.service.room.type.line'].
search([
('hotel_board_service_room_type_id',
'=', board_room_type.id),
('product_id', '=', self.product_id.id)])
.amount) / 100
else:
product = self.product_id.with_context(
lang=partner.lang,
partner=partner.id,
quantity=self.product_qty,
date=folio.date_order if folio else fields.Date.today(),
pricelist=pricelist.id,
uom=self.product_id.uom_id.id,
fiscal_position=False
)
lang=partner.lang,
partner=partner.id,
quantity=self.product_qty,
date=folio.date_order if folio else fields.Date.today(),
pricelist=pricelist.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,
@@ -399,16 +546,19 @@ class HotelService(models.Model):
"""
cmds = [(5, 0, 0)]
old_line_days = kwargs.get('old_line_days')
consumed_on = kwargs.get('consumed_on') if kwargs.get('consumed_on') else 'before'
consumed_on = kwargs.get('consumed_on') if kwargs.get(
'consumed_on') else 'before'
total_qty = 0
day_qty = 1
if kwargs.get('per_person'): #WARNING: Change adults in reservation NOT update qty service!!
# WARNING: Change adults in reservation NOT update qty service!!
if kwargs.get('per_person'):
day_qty = kwargs.get('persons')
for i in range(0, kwargs.get('days')):
if consumed_on == 'after':
i += 1
idate = (fields.Date.from_string(kwargs.get('dfrom')) + timedelta(days=i)).strftime(
DEFAULT_SERVER_DATE_FORMAT)
idate = (fields.Date.from_string(kwargs.get('dfrom')) +
timedelta(days=i)).strftime(
DEFAULT_SERVER_DATE_FORMAT)
if not old_line_days or idate not in old_line_days.mapped('date'):
cmds.append((0, False, {
'date': idate,
@@ -420,52 +570,3 @@ class HotelService(models.Model):
cmds.append((4, old_line.id))
total_qty = total_qty + old_line.day_qty
return {'service_line_ids': cmds, 'product_qty': total_qty}
@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['hotel.folio'].browse(self.env.context.get('default_folio_id'))
reservation = record.ser_room_line or self.env.context.get('ser_room_line')
currency = folio.currency_id if folio else reservation.currency_id
product = record.product_id
price = record.price_unit * (1 - (record.discount or 0.0) * 0.01)
taxes = record.tax_ids.compute_all(price, currency, 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'],
'price_subtotal': taxes['total_excluded'],
})
@api.depends('service_line_ids.day_qty')
def _compute_days_qty(self):
for record in self:
if record.per_day:
qty = sum(record.service_line_ids.mapped('day_qty'))
vals = {
'days_qty': qty,
'product_qty': qty
}
else:
vals = {'days_qty': 0}
record.update(vals)
@api.multi
def open_service_ids(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('product_qty')
#~ def constrains_qty_per_day(self):
#~ for record in self:
#~ if record.per_day:
#~ service_ids = self.env['hotel.service_line']
#~ total_day_qty = sum(service_ids.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

@@ -9,43 +9,51 @@ class HotelServiceLine(models.Model):
_name = "hotel.service.line"
_order = "date"
service_id = fields.Many2one('hotel.service', string='Service Room',
ondelete='cascade', required=True,
copy=False)
# Fields declaration
service_id = fields.Many2one(
'hotel.service',
string='Service Room',
ondelete='cascade',
required=True,
copy=False)
product_id = fields.Many2one(
related='service_id.product_id',
store=True)
tax_ids = fields.Many2many(
'account.tax',
string='Taxes',
related="service_id.tax_ids",
readonly="True")
hotel_id = fields.Many2one(
'hotel.property',
store=True,
readonly=True,
related='service_id.hotel_id')
date = fields.Date('Date')
day_qty = fields.Integer('Units')
product_id = fields.Many2one(related='service_id.product_id', store=True)
price_total = fields.Float('Price Total',
compute='_compute_price_total',
store=True)
price_unit = fields.Float('Unit Price',
related="service_id.price_unit",
readonly=True,
store=True)
room_id = fields.Many2one(strin='Room',
related="service_id.ser_room_line",
readonly=True,
store=True)
discount = fields.Float('Discount',
related="service_id.discount",
readonly=True,
store=True)
cancel_discount = fields.Float('Discount', compute='_compute_cancel_discount')
tax_ids = fields.Many2many('account.tax',
string='Taxes',
related="service_id.tax_ids",
readonly="True")
hotel_id = fields.Many2one('hotel.property', store=True, readonly=True,
related='service_id.hotel_id')
def _cancel_discount(self):
for record in self:
if record.reservation_id:
day = record.reservation_id.reservation_line_ids.filtered(
lambda d: d.date == record.date
)
record.cancel_discount = day.cancel_discount
price_total = fields.Float(
'Price Total',
compute='_compute_price_total',
store=True)
price_unit = fields.Float(
'Unit Price',
related="service_id.price_unit",
readonly=True,
store=True)
room_id = fields.Many2one(
string='Room',
related="service_id.ser_room_line",
readonly=True,
store=True)
discount = fields.Float(
'Discount',
related="service_id.discount",
readonly=True,
store=True)
cancel_discount = fields.Float(
'Discount', compute='_compute_cancel_discount')
# Compute and Search methods
@api.depends('day_qty', 'service_id.price_total')
def _compute_price_total(self):
"""
@@ -53,10 +61,13 @@ class HotelServiceLine(models.Model):
"""
for record in self:
if record.service_id.product_qty != 0:
record.price_total = (record.service_id.price_total * record.day_qty) / record.service_id.product_qty
record.price_total = (
record.service_id.price_total * record.day_qty) \
/ record.service_id.product_qty
else:
record.price_total = 0
# Constraints and onchanges
@api.constrains('day_qty')
def no_free_resources(self):
for record in self:
@@ -69,5 +80,14 @@ class HotelServiceLine(models.Model):
]).mapped('day_qty'))
if limit < out_qty + record.day_qty:
raise ValidationError(
_("%s limit exceeded for %s")% (record.service_id.product_id.name,
record.date))
_("%s limit exceeded for %s") %
(record.service_id.product_id.name, record.date))
# Business methods
def _cancel_discount(self):
for record in self:
if record.reservation_id:
day = record.reservation_id.reservation_line_ids.filtered(
lambda d: d.date == record.date
)
record.cancel_discount = day.cancel_discount

View File

@@ -11,30 +11,41 @@ class HotelSharedRoom(models.Model):
_description = 'Hotel Shared Room'
_order = "room_type_id, name"
# Fields declaration
name = fields.Char('Room Name', required=True)
active = fields.Boolean('Active', default=True)
room_type_id = fields.Many2one(
'hotel.room.type', 'Hotel Room Type',
required=True, ondelete='restrict',
'hotel.room.type',
'Hotel Room Type',
required=True,
ondelete='restrict',
domain=[('shared_room', '=', True)]
)
hotel_id = fields.Many2one('hotel.property', store=True, readonly=True,
related='room_type_id.hotel_id')
floor_id = fields.Many2one('hotel.floor', 'Ubication',
help='At which floor the room is located.',
ondelete='restrict',)
hotel_id = fields.Many2one(
'hotel.property',
store=True,
readonly=True,
related='room_type_id.hotel_id')
floor_id = fields.Many2one(
'hotel.floor',
'Ubication',
ondelete='restrict',
help='At which floor the room is located.')
bed_ids = fields.One2many(
'hotel.room',
'shared_room_id',
readonly=True,
ondelete='restrict',)
active = fields.Boolean('Active', default=True)
sequence = fields.Integer('Sequence', required=True)
beds = fields.Integer('Beds')
bed_ids = fields.One2many('hotel.room',
'shared_room_id',
readonly=True,
ondelete='restrict',)
description_sale = fields.Text(
'Sale Description', translate=True,
'Sale Description',
translate=True,
help="A description of the Product that you want to communicate to "
" your customers. This description will be copied to every Sales "
" Order, Delivery Order and Customer Invoice/Credit Note")
# Constraints and onchanges
@api.constrains('beds')
def _constrain_beds(self):
self.ensure_one()

View File

@@ -10,12 +10,13 @@ class AccountInvoice(models.Model):
_inherit = 'account.invoice'
# Field Declarations
from_folio = fields.Boolean(
compute='_computed_folio_origin')
folio_ids = fields.Many2many(
comodel_name='hotel.folio',
compute='_computed_folio_origin')
hotel_id = fields.Many2one('hotel.property')
hotel_id = fields.Many2one(
'hotel.property')
from_folio = fields.Boolean(
compute='_computed_folio_origin')
outstanding_folios_debits_widget = fields.Text(
compute='_get_outstanding_folios_JSON')
has_folios_outstanding = fields.Boolean(
@@ -93,7 +94,8 @@ class AccountInvoice(models.Model):
else:
amount_to_show = line.company_id.currency_id.\
with_context(date=line.date).\
compute(abs(line.amount_residual), self.currency_id)
compute(abs(line.amount_residual),
self.currency_id)
if float_is_zero(
amount_to_show,
precision_rounding=self.currency_id.rounding

View File

@@ -1,11 +1,13 @@
# Copyright 2017 Alexandre Díaz
# Copyright 2017 Dario Lodeiros
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo import api, fields, models, _
from odoo import fields, models
class AccountInvoiceLine(models.Model):
_inherit = 'account.invoice.line'
# Fields declaration
reservation_ids = fields.Many2many(
'hotel.reservation',
'reservation_invoice_rel',

View File

@@ -3,10 +3,14 @@
from odoo.exceptions import except_orm
from odoo import models, fields, api, _
class AccountPayment(models.Model):
_inherit = 'account.payment'
folio_id = fields.Many2one('hotel.folio', string='Folio')
# Fields declaration
folio_id = fields.Many2one(
'hotel.folio',
string='Folio')
amount_total_folio = fields.Float(
compute="_compute_folio_amount", store=True,
string="Total amount in folio",
@@ -15,13 +19,38 @@ class AccountPayment(models.Model):
save_date = fields.Date()
save_journal_id = fields.Integer()
@api.onchange('amount','payment_date','journal_id')
# Compute and Search methods
@api.multi
@api.depends('state')
def _compute_folio_amount(self):
# FIXME: Finalize method
res = []
fol = ()
for payment in self:
if payment.folio_id:
fol = payment.env['hotel.folio'].search([
('id', '=', payment.folio_id.id)
])
else:
return
if not any(fol):
return
if len(fol) > 1:
raise except_orm(_('Warning'), _('This pay is related with \
more than one Reservation.'))
else:
fol.compute_amount()
return res
# Constraints and onchanges
@api.onchange('amount', 'payment_date', 'journal_id')
def onchange_amount(self):
if self._origin:
self.save_amount = self._origin.amount
self.save_journal_id = self._origin.journal_id.id
self.save_date = self._origin.payment_date
# Action methods
"""WIP"""
@api.multi
def return_payment_folio(self):
@@ -47,9 +76,11 @@ class AccountPayment(models.Model):
if self.save_date:
self.payment_date = self.save_date
if self.save_journal_id:
self.journal_id = self.env['account.journal'].browse(self.save_journal_id)
self.journal_id = self.env['account.journal'].browse(
self.save_journal_id)
return_pay.action_confirm()
# Business methods
@api.multi
def modify(self):
self.cancel()
@@ -66,53 +97,40 @@ class AccountPayment(models.Model):
if self.folio_id:
msg = _("Payment %s modified: \n") % (self.communication)
if self.save_amount and self.save_amount != self.amount:
msg += _("Amount from %s to %s %s \n") % (self.save_amount, self.amount, self.currency_id.symbol)
msg += _("Amount from %s to %s %s \n") % (
self.save_amount, self.amount, self.currency_id.symbol)
if self.save_date and self.save_date != self.payment_date:
msg += _("Date from %s to %s \n") % (self.save_date, self.payment_date)
if self.save_journal_id and self.save_journal_id != self.journal_id.id:
msg += _("Journal from %s to %s") % (self.env['account.journal'].browse(self.save_journal_id).name, self.journal_id.name)
msg += _("Date from %s to %s \n") % (
self.save_date, self.payment_date)
if self.save_journal_id and \
self.save_journal_id != self.journal_id.id:
msg += _("Journal from %s to %s") % (
self.env['account.journal'].browse(
self.save_journal_id).name, self.journal_id.name)
self.folio_id.message_post(subject=_('Payment'), body=msg)
@api.multi
def delete(self):
msg = False
if self.folio_id:
msg = _("Deleted payment: %s %s ") % (self.amount, self.currency_id.symbol)
msg = _("Deleted payment: %s %s ") % (
self.amount, self.currency_id.symbol)
self.cancel()
self.move_name = ''
self.unlink()
if msg:
self.folio_id.message_post(subject=_('Payment Deleted'), body=msg)
@api.multi
@api.depends('state')
def _compute_folio_amount(self):
# FIXME: Finalize method
res = []
fol = ()
for payment in self:
if payment.folio_id:
fol = payment.env['hotel.folio'].search([
('id', '=', payment.folio_id.id)
])
else:
return
if not any(fol):
return
if len(fol) > 1:
raise except_orm(_('Warning'), _('This pay is related with \
more than one Reservation.'))
else:
fol.compute_amount()
return res
@api.multi
def post(self):
rec = super(AccountPayment, self).post()
if rec and not self._context.get("ignore_notification_post", False):
for pay in self:
if pay.folio_id:
msg = _("Payment of %s %s registered from %s using %s payment method") % (pay.amount, pay.currency_id.symbol, pay.communication, pay.journal_id.name)
msg = _("Payment of %s %s registered from %s \
using %s payment method") % \
(pay.amount, pay.currency_id.symbol,
pay.communication, pay.journal_id.name)
pay.folio_id.message_post(subject=_('Payment'), body=msg)
@api.multi

View File

@@ -14,11 +14,16 @@ class IrHttp(models.AbstractModel):
res = super().session_info()
user = request.env.user
display_switch_hotel_menu = len(user.hotel_ids) > 1
# TODO: limit hotels to the current company? or switch company automatically
res['hotel_id'] = request.env.user.hotel_id.id if request.session.uid else None
res['user_hotels'] = {'current_hotel': (user.hotel_id.id, user.hotel_id.name),
'allowed_hotels': [(hotel.id, hotel.name) for hotel in
user.hotel_ids]} if display_switch_hotel_menu else False
# TODO: limit hotels to the current company?
# or switch company automatically
res['hotel_id'] = request.env.user.hotel_id.id if \
request.session.uid else None
res['user_hotels'] = {
'current_hotel': (user.hotel_id.id, user.hotel_id.name),
'allowed_hotels': [
(hotel.id, hotel.name) for hotel in user.hotel_ids
]
} if display_switch_hotel_menu else False
if user.hotel_id.company_id in user.company_ids:
user.company_id = user.hotel_id.company_id
res['company_id'] = user.hotel_id.company_id.id

View File

@@ -10,12 +10,15 @@ class MailComposeMessage(models.TransientModel):
@api.multi
def send_mail(self, auto_commit=False):
if self._context.get('default_model') == 'hotel.folio' and \
self._context.get('default_res_id') and self._context.get('mark_so_as_sent'):
self._context.get('default_res_id') and \
self._context.get('mark_so_as_sent'):
folio = self.env['hotel.folio'].browse([
self._context['default_res_id']
])
if folio:
cmds = [(1, lid, {'to_send': False}) for lid in folio.reservation_ids.ids]
cmds = [(1, lid, {'to_send': False}) for lid in
folio.reservation_ids.ids]
if any(cmds):
folio.reservation_ids = cmds
return super(MailComposeMessage, self).send_mail(auto_commit=auto_commit)
return super(MailComposeMessage, self).send_mail(
auto_commit=auto_commit)

View File

@@ -6,10 +6,17 @@ from openerp import models, fields, api, _
class PaymentReturn(models.Model):
_inherit = 'payment.return'
folio_id = fields.Many2one('hotel.folio', string='Folio')
hotel_id = fields.Many2one('hotel.property', store=True, readonly=True,
related='folio_id.hotel_id')
# Fields declaration
folio_id = fields.Many2one(
'hotel.folio',
string='Folio')
hotel_id = fields.Many2one(
'hotel.property',
store=True,
readonly=True,
related='folio_id.hotel_id')
# Business methods
@api.multi
def action_confirm(self):
pay = super(PaymentReturn, self).action_confirm()

View File

@@ -12,13 +12,18 @@ class ProductPricelist(models.Model):
_inherit = 'product.pricelist'
# Fields declaration
hotel_ids = fields.Many2many('hotel.property', string='Hotels', required=False,
ondelete='restrict')
cancelation_rule_id = fields.Many2one('hotel.cancelation.rule',string="Cancelation Policy")
hotel_ids = fields.Many2many(
'hotel.property',
string='Hotels',
required=False,
ondelete='restrict')
cancelation_rule_id = fields.Many2one(
'hotel.cancelation.rule',
string="Cancelation Policy")
pricelist_type = fields.Selection([
('daily', 'Daily Plan'),
], string='Pricelist Type', default='daily')
('daily', 'Daily Plan')],
string='Pricelist Type',
default='daily')
is_staff = fields.Boolean('Is Staff')
# Constraints and onchanges
@@ -26,13 +31,17 @@ class ProductPricelist(models.Model):
def _check_pricelist_type_hotel_ids(self):
for record in self:
if record.pricelist_type == 'daily' and len(record.hotel_ids) != 1:
raise ValidationError(_("A daily pricelist is used as a daily Rate Plan for room types "
"and therefore must be related with one and only one hotel."))
raise ValidationError(
_("A daily pricelist is used as a daily Rate Plan "
"for room types and therefore must be related with "
"one and only one hotel."))
if record.pricelist_type == 'daily' and len(record.hotel_ids) == 1:
hotel_id = self.env['hotel.property'].search([
('default_pricelist_id', '=', record.id)
]) or None
if hotel_id and hotel_id != record.hotel_ids:
raise ValidationError(_("Relationship mismatch.") + " " +
_("This pricelist is used as default in a different hotel."))
raise ValidationError(
_("Relationship mismatch.") + " " +
_("This pricelist is used as default in a "
"different hotel."))

View File

@@ -7,6 +7,11 @@ from odoo import models, fields
class ProductTemplate(models.Model):
_inherit = "product.template"
hotel_ids = fields.Many2many(
'hotel.property',
string='Hotels',
required=False,
ondelete='restrict')
per_day = fields.Boolean('Unit increment per day')
per_person = fields.Boolean('Unit increment per person')
consumed_on = fields.Selection([
@@ -14,7 +19,7 @@ class ProductTemplate(models.Model):
('after', 'After night')], 'Consumed', default='before')
daily_limit = fields.Integer('Daily limit')
is_extra_bed = fields.Boolean('Is extra bed', default=False)
show_in_calendar = fields.Boolean('Show in Calendar', default=False,
show_in_calendar = fields.Boolean(
'Show in Calendar',
default=False,
help='Specifies if the product is shown in the calendar information.')
hotel_ids = fields.Many2many('hotel.property', string='Hotels', required=False,
ondelete='restrict')

View File

@@ -7,8 +7,8 @@ from odoo import models, fields
class ResCompany(models.Model):
_inherit = 'res.company'
# Fields declaration
hotel_ids = fields.One2many('hotel.property', 'company_id', 'Hotels')
# TODO: need extra explanation or remove otherwise
# additional_hours = fields.Integer('Additional Hours',
# help="Provide the min hours value for \

View File

@@ -11,6 +11,18 @@ _logger = logging.getLogger(__name__)
class ResPartner(models.Model):
_inherit = 'res.partner'
# Fields declaration
main_partner_id = fields.Many2one(
'res.partner',
string='Destination Partner fusion')
reservations_count = fields.Integer(
'Reservations', compute='_compute_reservations_count')
folios_count = fields.Integer(
'Folios', compute='_compute_folios_count')
unconfirmed = fields.Boolean('Unconfirmed', default=True)
is_tour_operator = fields.Boolean('Is Tour Operator')
# Compute and Search methods
def _compute_reservations_count(self):
hotel_reservation_obj = self.env['hotel.reservation']
for record in self:
@@ -25,13 +37,7 @@ class ResPartner(models.Model):
('partner_id.id', '=', record.id)
])
reservations_count = fields.Integer('Reservations',
compute='_compute_reservations_count')
folios_count = fields.Integer('Folios', compute='_compute_folios_count')
unconfirmed = fields.Boolean('Unconfirmed', default=True)
main_partner_id = fields.Many2one('res.partner', string='Destination Partner fusion')
is_tour_operator = fields.Boolean('Is Tour Operator')
# ORM Overrides
@api.model
def name_search(self, name, args=None, operator='ilike', limit=100):
if not args:

View File

@@ -6,12 +6,22 @@ from odoo import models, api, fields
class ResUsers(models.Model):
_inherit = 'res.users'
# Default Methods ang Gets
@api.model
def _get_default_hotel(self):
return self.env.user.hotel_id
hotel_id = fields.Many2one('hotel.property', string='Hotel', default=_get_default_hotel,
help='The hotel this user is currently working for.',
context={'user_preference': True})
hotel_ids = fields.Many2many('hotel.property', 'hotel_property_users_rel', 'user_id', 'hotel_id',
string='Hotels', default=_get_default_hotel)
# Fields declaration
hotel_id = fields.Many2one(
'hotel.property',
string='Hotel',
default=_get_default_hotel,
help='The hotel this user is currently working for.',
context={'user_preference': True})
hotel_ids = fields.Many2many(
'hotel.property',
'hotel_property_users_rel',
'user_id',
'hotel_id',
string='Hotels',
default=_get_default_hotel)