EVOLUTION-MANAGER
Edit File: hotel.py
# -*- coding: utf-8 -*- # See LICENSE file for full copyright and licensing details. import time import datetime import urllib2 from odoo.exceptions import except_orm, ValidationError from odoo.osv import expression from odoo.tools import misc, DEFAULT_SERVER_DATETIME_FORMAT from odoo import models, fields, api, _ from decimal import Decimal def _offset_format_timestamp1(src_tstamp_str, src_format, dst_format, ignore_unparsable_time=True, context=None): """ Convert a source timeStamp string into a destination timeStamp string, attempting to apply the correct offset if both the server and local timeZone are recognized,or no offset at all if they aren't or if tz_offset is false (i.e. assuming they are both in the same TZ). @param src_tstamp_str: the STR value containing the timeStamp. @param src_format: the format to use when parsing the local timeStamp. @param dst_format: the format to use when formatting the resulting timeStamp. @param server_to_client: specify timeZone offset direction (server=src and client=dest if True, or client=src and server=dest if False) @param ignore_unparsable_time: if True, return False if src_tstamp_str cannot be parsed using src_format or formatted using dst_format. @return: destination formatted timestamp, expressed in the destination timezone if possible and if tz_offset is true, or src_tstamp_str if timezone offset could not be determined. """ if not src_tstamp_str: return False res = src_tstamp_str if src_format and dst_format: try: # dt_value needs to be a datetime.datetime object\ # (so notime.struct_time or mx.DateTime.DateTime here!) dt_value = datetime.datetime.strptime(src_tstamp_str, src_format) if context.get('tz', False): try: import pytz src_tz = pytz.timezone(context['tz']) dst_tz = pytz.timezone('UTC') src_dt = src_tz.localize(dt_value, is_dst=True) dt_value = src_dt.astimezone(dst_tz) except Exception: pass res = dt_value.strftime(dst_format) except Exception: # Normal ways to end up here are if strptime or strftime failed if not ignore_unparsable_time: return False pass return res class HotelFloor(models.Model): _name = "hotel.floor" _description = "Floor" name = fields.Char('Floor Name', size=64, required=True, index=True) sequence = fields.Integer('Sequence', size=64, index=True) class HotelRoomType(models.Model): _name = "hotel.room.type" _description = "Room Type" name = fields.Char('Name', size=64, required=True) categ_id = fields.Many2one('hotel.room.type', 'Category') child_id = fields.One2many('hotel.room.type', 'categ_id', 'Child Categories') @api.multi def name_get(self): def get_names(cat): """ Return the list [cat.name, cat.categ_id.name, ...] """ res = [] while cat: res.append(cat.name) cat = cat.categ_id return res return [(cat.id, " / ".join(reversed(get_names(cat)))) for cat in self] @api.model def name_search(self, name, args=None, operator='ilike', limit=100): if not args: args = [] if name: # Be sure name_search is symetric to name_get category_names = name.split(' / ') parents = list(category_names) child = parents.pop() domain = [('name', operator, child)] if parents: names_ids = self.name_search(' / '.join(parents), args=args, operator='ilike', limit=limit) category_ids = [name_id[0] for name_id in names_ids] if operator in expression.NEGATIVE_TERM_OPERATORS: categories = self.search([('id', 'not in', category_ids)]) domain = expression.OR([[('categ_id', 'in', categories.ids)], domain]) else: domain = expression.AND([[('categ_id', 'in', category_ids)], domain]) for i in range(1, len(category_names)): domain = [[('name', operator, ' / '.join(category_names[-1 - i:]))], domain] if operator in expression.NEGATIVE_TERM_OPERATORS: domain = expression.AND(domain) else: domain = expression.OR(domain) categories = self.search(expression.AND([domain, args]), limit=limit) else: categories = self.search(args, limit=limit) return categories.name_get() class ProductProduct(models.Model): _inherit = "product.product" isroom = fields.Boolean('Is Room') iscategid = fields.Boolean('Is categ id') isservice = fields.Boolean('Is Service id') class HotelRoomAmenitiesType(models.Model): _name = 'hotel.room.amenities.type' _description = 'amenities Type' name = fields.Char('Name', size=64, required=True) amenity_id = fields.Many2one('hotel.room.amenities.type', 'Category') child_id = fields.One2many('hotel.room.amenities.type', 'amenity_id', 'Child Categories') @api.multi def name_get(self): def get_names(cat): """ Return the list [cat.name, cat.amenity_id.name, ...] """ res = [] while cat: res.append(cat.name) cat = cat.amenity_id return res return [(cat.id, " / ".join(reversed(get_names(cat)))) for cat in self] @api.model def name_search(self, name, args=None, operator='ilike', limit=100): if not args: args = [] if name: # Be sure name_search is symetric to name_get category_names = name.split(' / ') parents = list(category_names) child = parents.pop() domain = [('name', operator, child)] if parents: names_ids = self.name_search(' / '.join(parents), args=args, operator='ilike', limit=limit) category_ids = [name_id[0] for name_id in names_ids] if operator in expression.NEGATIVE_TERM_OPERATORS: categories = self.search([('id', 'not in', category_ids)]) domain = expression.OR([[('amenity_id', 'in', categories.ids)], domain]) else: domain = expression.AND([[('amenity_id', 'in', category_ids)], domain]) for i in range(1, len(category_names)): domain = [[('name', operator, ' / '.join(category_names[-1 - i:]))], domain] if operator in expression.NEGATIVE_TERM_OPERATORS: domain = expression.AND(domain) else: domain = expression.OR(domain) categories = self.search(expression.AND([domain, args]), limit=limit) else: categories = self.search(args, limit=limit) return categories.name_get() class HotelRoomAmenities(models.Model): _name = 'hotel.room.amenities' _description = 'Room amenities' product_id = fields.Many2one('product.product', 'Product Category', required=True, delegate=True, ondelete='cascade') categ_id = fields.Many2one('hotel.room.amenities.type', string='Amenities Category', required=True) product_manager = fields.Many2one('res.users', string='Product Manager') class FolioRoomLine(models.Model): _name = 'folio.room.line' _description = 'Hotel Room Reservation' _rec_name = 'room_id' room_id = fields.Many2one(comodel_name='hotel.room', string='Room id') check_in = fields.Datetime('Check In Date', required=True) check_out = fields.Datetime('Check Out Date', required=True) folio_id = fields.Many2one('hotel.folio', string='Folio Number') status = fields.Selection(string='state', related='folio_id.state') class HotelRoom(models.Model): _name = 'hotel.room' _description = 'Hotel Room' product_id = fields.Many2one('product.product', 'Product_id', required=True, delegate=True, ondelete='cascade') floor_id = fields.Many2one('hotel.floor', 'Floor No', help='At which floor the room is located.') max_adult = fields.Integer('Max Adult') max_child = fields.Integer('Max Child') categ_id = fields.Many2one('hotel.room.type', string='Room Category', required=True) room_amenities = fields.Many2many('hotel.room.amenities', 'temp_tab', 'room_amenities', 'rcateg_id', string='Room Amenities', help='List of room amenities. ') status = fields.Selection([('available', 'Available'), ('occupied', 'Occupied')], 'Status', default='available') capacity = fields.Integer('Capacity', required=True) room_line_ids = fields.One2many('folio.room.line', 'room_id', string='Room Reservation Line') product_manager = fields.Many2one('res.users', string='Product Manager') @api.constrains('capacity') def check_capacity(self): for room in self: if room.capacity <= 0: raise ValidationError(_('Room capacity must be more than 0')) @api.onchange('isroom') def isroom_change(self): ''' Based on isroom, status will be updated. ---------------------------------------- @param self: object pointer ''' if self.isroom is False: self.status = 'occupied' if self.isroom is True: self.status = 'available' @api.multi def write(self, vals): """ Overrides orm write method. @param self: The object pointer @param vals: dictionary of fields value. """ if 'isroom' in vals and vals['isroom'] is False: vals.update({'color': 2, 'status': 'occupied'}) if 'isroom'in vals and vals['isroom'] is True: vals.update({'color': 5, 'status': 'available'}) ret_val = super(HotelRoom, self).write(vals) return ret_val @api.multi def set_room_status_occupied(self): """ This method is used to change the state to occupied of the hotel room. --------------------------------------- @param self: object pointer """ return self.write({'isroom': False, 'color': 2}) @api.multi def set_room_status_available(self): """ This method is used to change the state to available of the hotel room. --------------------------------------- @param self: object pointer """ return self.write({'isroom': True, 'color': 5}) class HotelFolio(models.Model): @api.multi def name_get(self): res = [] disp = '' for rec in self: if rec.order_id: disp = str(rec.name) res.append((rec.id, disp)) return res @api.model def name_search(self, name='', args=None, operator='ilike', limit=100): if args is None: args = [] args += ([('name', operator, name)]) mids = self.search(args, limit=100) return mids.name_get() @api.model def _needaction_count(self, domain=None): """ Show a count of draft state folio on the menu badge. @param self: object pointer """ return self.search_count([('state', '=', 'draft')]) @api.model def _get_checkin_date(self): if self._context.get('tz'): to_zone = self._context.get('tz') else: to_zone = 'UTC' return _offset_format_timestamp1(time.strftime("%Y-%m-%d 12:00:00"), DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, ignore_unparsable_time=True, context={'tz': to_zone}) @api.model def _get_checkout_date(self): if self._context.get('tz'): to_zone = self._context.get('tz') else: to_zone = 'UTC' tm_delta = datetime.timedelta(days=1) return datetime.datetime.strptime(_offset_format_timestamp1 (time.strftime("%Y-%m-%d 12:00:00"), DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, ignore_unparsable_time=True, context={'tz': to_zone}), '%Y-%m-%d %H:%M:%S') + tm_delta @api.multi def copy(self, default=None): ''' @param self: object pointer @param default: dict of default values to be set ''' return super(HotelFolio, self).copy(default=default) _name = 'hotel.folio' _description = 'hotel folio new' _rec_name = 'order_id' _order = 'id' _inherit = ['ir.needaction_mixin'] name = fields.Char('Folio Number', readonly=True, index=True, default='New') order_id = fields.Many2one('sale.order', 'Order', delegate=True, required=True, ondelete='cascade') checkin_date = fields.Datetime('Check In', required=True, readonly=True, states={'draft': [('readonly', False)]}, default=_get_checkin_date) checkout_date = fields.Datetime('Check Out', required=True, readonly=True, states={'draft': [('readonly', False)]}, default=_get_checkout_date) room_lines = fields.One2many('hotel.folio.line', 'folio_id', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Hotel room reservation detail.") service_lines = fields.One2many('hotel.service.line', 'folio_id', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Hotel services detail provide to" "customer and it will include in " "main Invoice.") hotel_policy = fields.Selection([('prepaid', 'On Booking'), ('manual', 'On Check In'), ('picking', 'On Checkout')], 'Hotel Policy', default='manual', help="Hotel policy for payment that " "either the guest has to payment at " "booking time or check-in " "check-out time.") duration = fields.Float('Duration in Days', help="Number of days which will automatically " "count from the check-in and check-out date. ") currrency_ids = fields.One2many('currency.exchange', 'folio_no', readonly=True) hotel_invoice_id = fields.Many2one('account.invoice', 'Invoice', copy=False) duration_dummy = fields.Float('Duration Dummy') @api.multi def go_to_currency_exchange(self): ''' when Money Exchange button is clicked then this method is called. ------------------------------------------------------------------- @param self: object pointer ''' ctx = dict(self._context) for rec in self: if rec.partner_id.id and len(rec.room_lines) != 0: ctx.update({'folioid': rec.id, 'guest': rec.partner_id.id, 'room_no': rec.room_lines[0].product_id.name, 'hotel': rec.warehouse_id.id}) self.env.args = misc.frozendict(ctx) else: raise except_orm(_('Warning'), _('Please Reserve Any Room.')) return {'name': _('Currency Exchange'), 'res_model': 'currency.exchange', 'type': 'ir.actions.act_window', 'view_id': False, 'view_mode': 'form,tree', 'view_type': 'form', 'context': {'default_folio_no': ctx.get('folioid'), 'default_hotel_id': ctx.get('hotel'), 'default_guest_name': ctx.get('guest'), 'default_room_number': ctx.get('room_no') }, } @api.constrains('room_lines') def folio_room_lines(self): ''' This method is used to validate the room_lines. ------------------------------------------------ @param self: object pointer @return: raise warning depending on the validation ''' folio_rooms = [] for room in self[0].room_lines: if room.product_id.id in folio_rooms: raise ValidationError(_('You Cannot Take Same Room Twice')) folio_rooms.append(room.product_id.id) @api.onchange('checkout_date', 'checkin_date') def onchange_dates(self): ''' This method gives the duration between check in and checkout if customer will leave only for some hour it would be considers as a whole day.If customer will check in checkout for more or equal hours, which configured in company as additional hours than it would be consider as full days -------------------------------------------------------------------- @param self: object pointer @return: Duration and checkout_date ''' configured_addition_hours = 0 wid = self.warehouse_id whouse_com_id = wid or wid.company_id if whouse_com_id: configured_addition_hours = wid.company_id.additional_hours myduration = 0 chckin = self.checkin_date chckout = self.checkout_date if chckin and chckout: server_dt = DEFAULT_SERVER_DATETIME_FORMAT chkin_dt = datetime.datetime.strptime(chckin, server_dt) chkout_dt = datetime.datetime.strptime(chckout, server_dt) dur = chkout_dt - chkin_dt sec_dur = dur.seconds if (not dur.days and not sec_dur) or (dur.days and not sec_dur): myduration = dur.days else: myduration = dur.days + 1 # To calculate additional hours in hotel room as per minutes if configured_addition_hours > 0: additional_hours = abs((dur.seconds / 60) / 60) if additional_hours >= configured_addition_hours: myduration += 1 self.duration = myduration self.duration_dummy = self.duration @api.model def create(self, vals, check=True): """ Overrides orm create method. @param self: The object pointer @param vals: dictionary of fields value. @return: new record set for hotel folio. """ if not 'service_lines' and 'folio_id' in vals: tmp_room_lines = vals.get('room_lines', []) vals['order_policy'] = vals.get('hotel_policy', 'manual') vals.update({'room_lines': []}) folio_id = super(HotelFolio, self).create(vals) for line in (tmp_room_lines): line[2].update({'folio_id': folio_id}) vals.update({'room_lines': tmp_room_lines}) folio_id.write(vals) else: if not vals: vals = {} vals['name'] = self.env['ir.sequence'].next_by_code('hotel.folio') vals['duration'] = vals.get('duration', 0.0) or vals.get('duration_dummy', 0.0) folio_id = super(HotelFolio, self).create(vals) folio_room_line_obj = self.env['folio.room.line'] h_room_obj = self.env['hotel.room'] try: for rec in folio_id: if not rec.reservation_id: for room_rec in rec.room_lines: prod = room_rec.product_id.name room_obj = h_room_obj.search([('name', '=', prod)]) room_obj.write({'isroom': False}) vals = {'room_id': room_obj.id, 'check_in': rec.checkin_date, 'check_out': rec.checkout_date, 'folio_id': rec.id, } folio_room_line_obj.create(vals) except: for rec in folio_id: for room_rec in rec.room_lines: prod = room_rec.product_id.name room_obj = h_room_obj.search([('name', '=', prod)]) room_obj.write({'isroom': False}) vals = {'room_id': room_obj.id, 'check_in': rec.checkin_date, 'check_out': rec.checkout_date, 'folio_id': rec.id, } folio_room_line_obj.create(vals) return folio_id @api.multi def write(self, vals): """ Overrides orm write method. @param self: The object pointer @param vals: dictionary of fields value. """ product_obj = self.env['product.product'] h_room_obj = self.env['hotel.room'] folio_room_line_obj = self.env['folio.room.line'] room_lst1 = [] for rec in self: for res in rec.room_lines: room_lst1.append(res.product_id.id) room_lst = [] for folio_obj in self: if vals and vals.get('duration_dummy', False): vals['duration'] = vals.get('duration_dummy', 0.0) else: vals['duration'] = folio_obj.duration for folio_rec in folio_obj.room_lines: room_lst.append(folio_rec.product_id.id) new_rooms = set(room_lst).difference(set(room_lst1)) if len(list(new_rooms)) != 0: room_list = product_obj.browse(list(new_rooms)) for rm in room_list: room_obj = h_room_obj.search([('name', '=', rm.name)]) room_obj.write({'isroom': False}) vals = {'room_id': room_obj.id, 'check_in': folio_obj.checkin_date, 'check_out': folio_obj.checkout_date, 'folio_id': folio_obj.id, } folio_room_line_obj.create(vals) if len(list(new_rooms)) == 0: room_list_obj = product_obj.browse(room_lst1) for rom in room_list_obj: room_obj = h_room_obj.search([('name', '=', rom.name)]) room_obj.write({'isroom': False}) room_vals = {'room_id': room_obj.id, 'check_in': folio_obj.checkin_date, 'check_out': folio_obj.checkout_date, 'folio_id': folio_obj.id, } folio_romline_rec = (folio_room_line_obj.search ([('folio_id', '=', folio_obj.id)])) folio_romline_rec.write(room_vals) return super(HotelFolio, self).write(vals) @api.onchange('warehouse_id') def onchange_warehouse_id(self): ''' When you change warehouse it will update the warehouse of the hotel folio as well ---------------------------------------------------------- @param self: object pointer ''' return self.order_id._onchange_warehouse_id() @api.onchange('partner_id') def onchange_partner_id(self): ''' When you change partner_id it will update the partner_invoice_id, partner_shipping_id and pricelist_id of the hotel folio as well --------------------------------------------------------------- @param self: object pointer ''' if self.partner_id: partner_rec = self.env['res.partner'].browse(self.partner_id.id) order_ids = [folio.order_id.id for folio in self] if not order_ids: self.partner_invoice_id = partner_rec.id self.partner_shipping_id = partner_rec.id self.pricelist_id = partner_rec.property_product_pricelist.id raise _('Not Any Order For %s ' % (partner_rec.name)) else: self.partner_invoice_id = partner_rec.id self.partner_shipping_id = partner_rec.id self.pricelist_id = partner_rec.property_product_pricelist.id @api.multi def button_dummy(self): ''' @param self: object pointer ''' for folio in self: folio.order_id.button_dummy() return True @api.multi def action_done(self): self.state = 'done' @api.multi def action_invoice_create(self, grouped=False, final=False): ''' @param self: object pointer ''' room_lst = [] invoice_id = (self.order_id.action_invoice_create(grouped=False, final=False)) for line in self: values = {'invoiced': True, 'hotel_invoice_id': invoice_id } line.write(values) for rec in line.room_lines: room_lst.append(rec.product_id) for room in room_lst: room_obj = self.env['hotel.room' ].search([('name', '=', room.name)]) room_obj.write({'isroom': True}) return invoice_id @api.multi def action_invoice_cancel(self): ''' @param self: object pointer ''' if not self.order_id: raise except_orm(_('Warning'), _('Order id is not available')) for sale in self: for line in sale.order_line: line.write({'invoiced': 'invoiced'}) self.state = 'invoice_except' return self.order_id.action_invoice_cancel @api.multi def action_cancel(self): ''' @param self: object pointer ''' if not self.order_id: raise except_orm(_('Warning'), _('Order id is not available')) for sale in self: for invoice in sale.invoice_ids: invoice.state = 'cancel' return self.order_id.action_cancel() @api.multi def action_confirm(self): for order in self.order_id: order.state = 'sale' order.order_line._action_procurement_create() if not order.project_id: for line in order.order_line: if line.product_id.invoice_policy == 'cost': order._create_analytic_account() break if self.env['ir.values'].get_default('sale.config.settings', 'auto_done_setting'): self.order_id.action_done() @api.multi def test_state(self, mode): ''' @param self: object pointer @param mode: state of workflow ''' write_done_ids = [] write_cancel_ids = [] if write_done_ids: test_obj = self.env['sale.order.line'].browse(write_done_ids) test_obj.write({'state': 'done'}) if write_cancel_ids: test_obj = self.env['sale.order.line'].browse(write_cancel_ids) test_obj.write({'state': 'cancel'}) @api.multi def action_cancel_draft(self): ''' @param self: object pointer ''' if not len(self._ids): return False query = "select id from sale_order_line \ where order_id IN %s and state=%s" self._cr.execute(query, (tuple(self._ids), 'cancel')) cr1 = self._cr line_ids = map(lambda x: x[0], cr1.fetchall()) self.write({'state': 'draft', 'invoice_ids': [], 'shipped': 0}) sale_line_obj = self.env['sale.order.line'].browse(line_ids) sale_line_obj.write({'invoiced': False, 'state': 'draft', 'invoice_lines': [(6, 0, [])]}) return True class HotelFolioLine(models.Model): @api.multi def copy(self, default=None): ''' @param self: object pointer @param default: dict of default values to be set ''' return super(HotelFolioLine, self).copy(default=default) @api.model def _get_checkin_date(self): if 'checkin' in self._context: return self._context['checkin'] return time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) @api.model def _get_checkout_date(self): if 'checkout' in self._context: return self._context['checkout'] return time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) _name = 'hotel.folio.line' _description = 'hotel folio1 room line' order_line_id = fields.Many2one('sale.order.line', string='Order Line', required=True, delegate=True, ondelete='cascade') folio_id = fields.Many2one('hotel.folio', string='Folio', ondelete='cascade') checkin_date = fields.Datetime('Check In', required=True, default=_get_checkin_date) checkout_date = fields.Datetime('Check Out', required=True, default=_get_checkout_date) is_reserved = fields.Boolean('Is Reserved', help='True when folio line created from \ Reservation') @api.model def create(self, vals, check=True): """ Overrides orm create method. @param self: The object pointer @param vals: dictionary of fields value. @return: new record set for hotel folio line. """ if 'folio_id' in vals: folio = self.env["hotel.folio"].browse(vals['folio_id']) vals.update({'order_id': folio.order_id.id}) return super(HotelFolioLine, self).create(vals) @api.constrains('checkin_date', 'checkout_date') def check_dates(self): ''' This method is used to validate the checkin_date and checkout_date. ------------------------------------------------------------------- @param self: object pointer @return: raise warning depending on the validation ''' if self.checkin_date >= self.checkout_date: raise ValidationError(_('Room line Check In Date Should be \ less than the Check Out Date!')) if self.folio_id.date_order and self.checkin_date: if self.checkin_date <= self.folio_id.date_order: raise ValidationError(_('Room line check in date should be \ greater than the current date.')) @api.multi def unlink(self): """ Overrides orm unlink method. @param self: The object pointer @return: True/False. """ sale_line_obj = self.env['sale.order.line'] fr_obj = self.env['folio.room.line'] for line in self: if line.order_line_id: sale_unlink_obj = (sale_line_obj.browse ([line.order_line_id.id])) for rec in sale_unlink_obj: room_obj = self.env['hotel.room' ].search([('name', '=', rec.name)]) if room_obj.id: folio_arg = [('folio_id', '=', line.folio_id.id), ('room_id', '=', room_obj.id)] folio_room_line_myobj = fr_obj.search(folio_arg) if folio_room_line_myobj.id: folio_room_line_myobj.unlink() room_obj.write({'isroom': True, 'status': 'available'}) sale_unlink_obj.unlink() return super(HotelFolioLine, self).unlink() @api.onchange('product_id') def product_id_change(self): ''' - @param self: object pointer - ''' context = dict(self._context) if not context: context = {} if context.get('folio', False): if self.product_id and self.folio_id.partner_id: self.name = self.product_id.name self.price_unit = self.product_id.list_price self.product_uom = self.product_id.uom_id tax_obj = self.env['account.tax'] pr = self.product_id self.price_unit = tax_obj._fix_tax_included_price(pr.price, pr.taxes_id, self.tax_id) else: if not self.product_id: return {'domain': {'product_uom': []}} val = {} pr = self.product_id.with_context( lang=self.folio_id.partner_id.lang, partner=self.folio_id.partner_id.id, quantity=val.get('product_uom_qty') or self.product_uom_qty, date=self.folio_id.date_order, pricelist=self.folio_id.pricelist_id.id, uom=self.product_uom.id ) p = pr.with_context(pricelist=self.order_id.pricelist_id.id).price if self.folio_id.pricelist_id and self.folio_id.partner_id: obj = self.env['account.tax'] val['price_unit'] = obj._fix_tax_included_price(p, pr.taxes_id, self.tax_id) @api.onchange('product_uom') def product_uom_change(self): if not self.product_uom: self.price_unit = 0.0 return self.price_unit = self.product_id.list_price if self.folio_id.partner_id: prod = self.product_id.with_context( lang=self.folio_id.partner_id.lang, partner=self.folio_id.partner_id.id, quantity=1, date_order=self.folio_id.checkin_date, pricelist=self.folio_id.pricelist_id.id, uom=self.product_uom.id ) tax_obj = self.env['account.tax'] self.price_unit = tax_obj._fix_tax_included_price(prod.price, prod.taxes_id, self.tax_id) @api.onchange('checkin_date', 'checkout_date') def on_change_checkout(self): ''' When you change checkin_date or checkout_date it will checked it and update the qty of hotel folio line ----------------------------------------------------------------- @param self: object pointer ''' configured_addition_hours = 0 fwhouse_id = self.folio_id.warehouse_id fwc_id = fwhouse_id or fwhouse_id.company_id if fwc_id: configured_addition_hours = fwhouse_id.company_id.additional_hours myduration = 0 if not self.checkin_date: self.checkin_date = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) if not self.checkout_date: self.checkout_date = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) chckin = self.checkin_date chckout = self.checkout_date if chckin and chckout: server_dt = DEFAULT_SERVER_DATETIME_FORMAT chkin_dt = datetime.datetime.strptime(chckin, server_dt) chkout_dt = datetime.datetime.strptime(chckout, server_dt) dur = chkout_dt - chkin_dt sec_dur = dur.seconds if (not dur.days and not sec_dur) or (dur.days and not sec_dur): myduration = dur.days else: myduration = dur.days + 1 # To calculate additional hours in hotel room as per minutes if configured_addition_hours > 0: additional_hours = abs((dur.seconds / 60) / 60) if additional_hours >= configured_addition_hours: myduration += 1 self.product_uom_qty = myduration hotel_room_obj = self.env['hotel.room'] hotel_room_ids = hotel_room_obj.search([]) avail_prod_ids = [] for room in hotel_room_ids: assigned = False for rm_line in room.room_line_ids: if rm_line.status != 'cancel': if(self.checkin_date <= rm_line.check_in <= self.checkout_date) or (self.checkin_date <= rm_line.check_out <= self.checkout_date): assigned = True elif (rm_line.check_in <= self.checkin_date <= rm_line.check_out) or (rm_line.check_in <= self.checkout_date <= rm_line.check_out): assigned = True if not assigned: avail_prod_ids.append(room.product_id.id) domain = {'product_id': [('id', 'in', avail_prod_ids)]} return {'domain': domain} @api.multi def button_confirm(self): ''' @param self: object pointer ''' for folio in self: line = folio.order_line_id line.button_confirm() return True @api.multi def button_done(self): ''' @param self: object pointer ''' lines = [folio_line.order_line_id for folio_line in self] lines.button_done() self.state = 'done' return True @api.multi def copy_data(self, default=None): ''' @param self: object pointer @param default: dict of default values to be set ''' line_id = self.order_line_id.id sale_line_obj = self.env['sale.order.line'].browse(line_id) return sale_line_obj.copy_data(default=default) class HotelServiceLine(models.Model): @api.multi def copy(self, default=None): ''' @param self: object pointer @param default: dict of default values to be set ''' return super(HotelServiceLine, self).copy(default=default) @api.model def _service_checkin_date(self): if 'checkin' in self._context: return self._context['checkin'] return time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) @api.model def _service_checkout_date(self): if 'checkout' in self._context: return self._context['checkout'] return time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) _name = 'hotel.service.line' _description = 'hotel Service line' service_line_id = fields.Many2one('sale.order.line', 'Service Line', required=True, delegate=True, ondelete='cascade') folio_id = fields.Many2one('hotel.folio', 'Folio', ondelete='cascade') ser_checkin_date = fields.Datetime('From Date', required=True, default=_service_checkin_date) ser_checkout_date = fields.Datetime('To Date', required=True, default=_service_checkout_date) @api.model def create(self, vals, check=True): """ Overrides orm create method. @param self: The object pointer @param vals: dictionary of fields value. @return: new record set for hotel service line. """ if 'folio_id' in vals: folio = self.env['hotel.folio'].browse(vals['folio_id']) vals.update({'order_id': folio.order_id.id}) return super(HotelServiceLine, self).create(vals) @api.multi def unlink(self): """ Overrides orm unlink method. @param self: The object pointer @return: True/False. """ s_line_obj = self.env['sale.order.line'] for line in self: if line.service_line_id: sale_unlink_obj = s_line_obj.browse([line.service_line_id.id]) sale_unlink_obj.unlink() return super(HotelServiceLine, self).unlink() @api.onchange('product_id') def product_id_change(self): ''' @param self: object pointer ''' if self.product_id and self.folio_id.partner_id: self.name = self.product_id.name self.price_unit = self.product_id.list_price self.product_uom = self.product_id.uom_id tax_obj = self.env['account.tax'] prod = self.product_id self.price_unit = tax_obj._fix_tax_included_price(prod.price, prod.taxes_id, self.tax_id) @api.onchange('product_uom') def product_uom_change(self): ''' @param self: object pointer ''' if not self.product_uom: self.price_unit = 0.0 return self.price_unit = self.product_id.list_price if self.folio_id.partner_id: prod = self.product_id.with_context( lang=self.folio_id.partner_id.lang, partner=self.folio_id.partner_id.id, quantity=1, date_order=self.folio_id.checkin_date, pricelist=self.folio_id.pricelist_id.id, uom=self.product_uom.id ) tax_obj = self.env['account.tax'] self.price_unit = tax_obj._fix_tax_included_price(prod.price, prod.taxes_id, self.tax_id) @api.onchange('ser_checkin_date', 'ser_checkout_date') def on_change_checkout(self): ''' When you change checkin_date or checkout_date it will checked it and update the qty of hotel service line ----------------------------------------------------------------- @param self: object pointer ''' if not self.ser_checkin_date: time_a = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) self.ser_checkin_date = time_a if not self.ser_checkout_date: self.ser_checkout_date = time_a if self.ser_checkout_date < self.ser_checkin_date: raise _('Checkout must be greater or equal checkin date') if self.ser_checkin_date and self.ser_checkout_date: date_a = time.strptime(self.ser_checkout_date, DEFAULT_SERVER_DATETIME_FORMAT)[:5] date_b = time.strptime(self.ser_checkin_date, DEFAULT_SERVER_DATETIME_FORMAT)[:5] diffDate = datetime.datetime(*date_a) - datetime.datetime(*date_b) qty = diffDate.days + 1 self.product_uom_qty = qty @api.multi def button_confirm(self): ''' @param self: object pointer ''' for folio in self: line = folio.service_line_id x = line.button_confirm() return x @api.multi def button_done(self): ''' @param self: object pointer ''' for folio in self: line = folio.service_line_id x = line.button_done() return x @api.multi def copy_data(self, default=None): ''' @param self: object pointer @param default: dict of default values to be set ''' sale_line_obj = self.env['sale.order.line' ].browse(self.service_line_id.id) return sale_line_obj.copy_data(default=default) class HotelServiceType(models.Model): _name = "hotel.service.type" _description = "Service Type" name = fields.Char('Service Name', size=64, required=True) service_id = fields.Many2one('hotel.service.type', 'Service Category') child_id = fields.One2many('hotel.service.type', 'service_id', 'Child Categories') @api.multi def name_get(self): def get_names(cat): """ Return the list [cat.name, cat.service_id.name, ...] """ res = [] while cat: res.append(cat.name) cat = cat.service_id return res return [(cat.id, " / ".join(reversed(get_names(cat)))) for cat in self] @api.model def name_search(self, name, args=None, operator='ilike', limit=100): if not args: args = [] if name: # Be sure name_search is symetric to name_get category_names = name.split(' / ') parents = list(category_names) child = parents.pop() domain = [('name', operator, child)] if parents: names_ids = self.name_search(' / '.join(parents), args=args, operator='ilike', limit=limit) category_ids = [name_id[0] for name_id in names_ids] if operator in expression.NEGATIVE_TERM_OPERATORS: categories = self.search([('id', 'not in', category_ids)]) domain = expression.OR([[('service_id', 'in', categories.ids)], domain]) else: domain = expression.AND([[('service_id', 'in', category_ids)], domain]) for i in range(1, len(category_names)): domain = [[('name', operator, ' / '.join(category_names[-1 - i:]))], domain] if operator in expression.NEGATIVE_TERM_OPERATORS: domain = expression.AND(domain) else: domain = expression.OR(domain) categories = self.search(expression.AND([domain, args]), limit=limit) else: categories = self.search(args, limit=limit) return categories.name_get() class HotelServices(models.Model): _name = 'hotel.services' _description = 'Hotel Services and its charges' product_id = fields.Many2one('product.product', 'Service_id', required=True, ondelete='cascade', delegate=True) categ_id = fields.Many2one('hotel.service.type', string='Service Category', required=True) product_manager = fields.Many2one('res.users', string='Product Manager') class ResCompany(models.Model): _inherit = 'res.company' additional_hours = fields.Integer('Additional Hours', help="Provide the min hours value for \ check in, checkout days, whatever the hours will be provided here based \ on that extra days will be calculated.") class CurrencyExchangeRate(models.Model): _name = "currency.exchange" _description = "currency" @api.depends('input_curr', 'out_curr', 'in_amount') def _compute_get_currency(self): ''' When you change input_curr, out_curr or in_amount it will update the out_amount of the currency exchange ------------------------------------------------------ @param self: object pointer ''' for rec in self: rec.out_amount = 0.0 if rec.input_curr: result = rec.get_rate(rec.input_curr.name, rec.out_curr.name) if rec.out_curr: rec.rate = result if rec.rate == Decimal('-1.00'): raise except_orm(_('Warning'), _('Please Check Your \ Network Connectivity.')) rec.out_amount = (float(result) * float(rec.in_amount)) @api.depends('out_amount', 'tax') def _compute_tax_change(self): ''' When you change out_amount or tax it will update the total of the currency exchange ------------------------------------------------- @param self: object pointer ''' for rec in self: if rec.out_amount: ser_tax = ((rec.out_amount) * (float(rec.tax))) / 100 rec.total = rec.out_amount + ser_tax @api.model def get_rate(self, a, b): ''' Calculate rate between two currency ----------------------------------- @param self: object pointer ''' try: url = 'http://finance.yahoo.com/d/quotes.csv?s=%s%s=X&f=l1' % (a, b) rate = urllib2.urlopen(url).read().rstrip() return Decimal(rate) except: return Decimal('-1.00') name = fields.Char('Reg Number', readonly=True, default='New') today_date = fields.Datetime('Date Ordered', required=True, default=(lambda *a: time.strftime (DEFAULT_SERVER_DATETIME_FORMAT))) input_curr = fields.Many2one('res.currency', string='Input Currency', track_visibility='always') in_amount = fields.Float('Amount Taken', size=64, default=1.0, index=True) out_curr = fields.Many2one('res.currency', string='Output Currency', track_visibility='always') out_amount = fields.Float(compute="_compute_get_currency", string='Subtotal', size=64) folio_no = fields.Many2one('hotel.folio', 'Folio Number') guest_name = fields.Many2one('res.partner', string='Guest Name') room_number = fields.Char(string='Room Number') state = fields.Selection([('draft', 'Draft'), ('done', 'Done'), ('cancel', 'Cancel')], 'State', default='draft') rate = fields.Float(compute="_compute_get_currency", string='Rate (Per Unit)', size=64, readonly=True) hotel_id = fields.Many2one('stock.warehouse', 'Hotel Name') type = fields.Selection([('cash', 'Cash')], 'Type', default='cash') tax = fields.Selection([('2', '2%'), ('5', '5%'), ('10', '10%')], 'Service Tax', default='2') total = fields.Float(compute="_compute_tax_change", string='Total Amount') @api.constrains('out_curr') def check_out_curr(self): for cur in self: if cur.out_curr == cur.input_curr: raise ValidationError(_('Input currency and output currency ' 'must not be same')) @api.model def create(self, vals): """ Overrides orm create method. @param self: The object pointer @param vals: dictionary of fields value. """ if not vals: vals = {} if self._context is None: self._context = {} seq_obj = self.env['ir.sequence'] vals['name'] = seq_obj.next_by_code('currency.exchange') or 'New' return super(CurrencyExchangeRate, self).create(vals) @api.onchange('folio_no') def get_folio_no(self): ''' When you change folio_no, based on that it will update the guest_name,hotel_id and room_number as well --------------------------------------------------------- @param self: object pointer ''' for rec in self: self.guest_name = False self.hotel_id = False self.room_number = False if rec.folio_no and len(rec.folio_no.room_lines) != 0: self.guest_name = rec.folio_no.partner_id.id self.hotel_id = rec.folio_no.warehouse_id.id self.room_number = rec.folio_no.room_lines[0].product_id.name @api.multi def act_cur_done(self): """ This method is used to change the state to done of the currency exchange --------------------------------------- @param self: object pointer """ self.state = 'done' return True @api.multi def act_cur_cancel(self): """ This method is used to change the state to cancel of the currency exchange --------------------------------------- @param self: object pointer """ self.state = 'cancel' return True @api.multi def act_cur_cancel_draft(self): """ This method is used to change the state to draft of the currency exchange --------------------------------------- @param self: object pointer """ self.state = 'draft' return True class AccountInvoice(models.Model): _inherit = 'account.invoice' @api.model def create(self, vals): res = super(AccountInvoice, self).create(vals) if self._context.get('folio_id'): folio = self.env['hotel.folio'].browse(self._context['folio_id']) folio.write({'hotel_invoice_id': res.id, 'invoice_status': 'invoiced'}) return res