From ab4c5e3dee2c756839ed7598edb7396ccf68b722 Mon Sep 17 00:00:00 2001 From: "Atul Makwana(SerpentCS)" Date: Tue, 22 May 2012 19:36:57 +0530 Subject: [PATCH 001/143] [IMP] Improve the module --- hotel/__init__.py | 25 + hotel/__openerp__.py | 56 +++ hotel/data/hotel-color.png | Bin 0 -> 6481 bytes hotel/data/hotel-grey.png | Bin 0 -> 2539 bytes hotel/hotel.py | 473 ++++++++++++++++++ hotel/hotel_data.xml | 155 ++++++ hotel/hotel_folio_workflow.xml | 242 +++++++++ hotel/hotel_report.xml | 13 + hotel/hotel_view.xml | 747 ++++++++++++++++++++++++++++ hotel/i18n/bg.po | 760 ++++++++++++++++++++++++++++ hotel/i18n/da.po | 761 ++++++++++++++++++++++++++++ hotel/i18n/es.po | 774 +++++++++++++++++++++++++++++ hotel/i18n/fr_BE.po | 757 ++++++++++++++++++++++++++++ hotel/i18n/hotel.pot | 757 ++++++++++++++++++++++++++++ hotel/i18n/hr.po | 760 ++++++++++++++++++++++++++++ hotel/i18n/sv.po | 759 ++++++++++++++++++++++++++++ hotel/report/__init__.py | 24 + hotel/report/hotel_report.py | 54 ++ hotel/report/total_folio.rml | 114 +++++ hotel/report/total_folio.sxw | Bin 0 -> 11488 bytes hotel/security/hotel_security.xml | 11 + hotel/security/ir.model.access.csv | 13 + hotel/wizard/__init__.py | 24 + hotel/wizard/hotel_wizard.py | 54 ++ hotel/wizard/hotel_wizard.xml | 36 ++ 25 files changed, 7369 insertions(+) create mode 100755 hotel/__init__.py create mode 100755 hotel/__openerp__.py create mode 100644 hotel/data/hotel-color.png create mode 100644 hotel/data/hotel-grey.png create mode 100755 hotel/hotel.py create mode 100755 hotel/hotel_data.xml create mode 100755 hotel/hotel_folio_workflow.xml create mode 100755 hotel/hotel_report.xml create mode 100755 hotel/hotel_view.xml create mode 100644 hotel/i18n/bg.po create mode 100644 hotel/i18n/da.po create mode 100644 hotel/i18n/es.po create mode 100755 hotel/i18n/fr_BE.po create mode 100755 hotel/i18n/hotel.pot create mode 100644 hotel/i18n/hr.po create mode 100644 hotel/i18n/sv.po create mode 100755 hotel/report/__init__.py create mode 100755 hotel/report/hotel_report.py create mode 100755 hotel/report/total_folio.rml create mode 100755 hotel/report/total_folio.sxw create mode 100755 hotel/security/hotel_security.xml create mode 100755 hotel/security/ir.model.access.csv create mode 100755 hotel/wizard/__init__.py create mode 100755 hotel/wizard/hotel_wizard.py create mode 100755 hotel/wizard/hotel_wizard.xml diff --git a/hotel/__init__.py b/hotel/__init__.py new file mode 100755 index 000000000..a4c01af67 --- /dev/null +++ b/hotel/__init__.py @@ -0,0 +1,25 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import hotel +import wizard +import report + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/hotel/__openerp__.py b/hotel/__openerp__.py new file mode 100755 index 000000000..43b87749e --- /dev/null +++ b/hotel/__openerp__.py @@ -0,0 +1,56 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +{ + "name" : "Hotel Management", + "version" : "1.0", + "author" : "Tiny", + "category" : "Generic Modules/Hotel Management", + "description": """ + Module for Hotel/Resort/Property management. You can manage: + * Configure Property + * Hotel Configuration + * Check In, Check out + * Manage Folio + * Payment + + Different reports are also provided, mainly for hotel statistics. + """, + "depends" : ["base","product","sale"], + "init_xml" : [], + "demo_xml" : [ + ], + "update_xml" : [ + + "hotel_view.xml", + "hotel_data.xml", + "hotel_folio_workflow.xml", + "hotel_report.xml", + "wizard/hotel_wizard.xml", + "security/hotel_security.xml", + "security/ir.model.access.csv", + + ], + "active": False, + "installable": True +} + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/hotel/data/hotel-color.png b/hotel/data/hotel-color.png new file mode 100644 index 0000000000000000000000000000000000000000..0e3644a507a09351cfed1d31fe7194d448a596c5 GIT binary patch literal 6481 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 literal 0 HcmV?d00001 diff --git a/hotel/data/hotel-grey.png b/hotel/data/hotel-grey.png new file mode 100644 index 0000000000000000000000000000000000000000..110e14614eb14272bcc29643d8c1e92ec9843239 GIT binary patch literal 2539 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. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from osv import fields, osv +import time +import netsvc +#import ir +from mx import DateTime +import datetime +from tools import config + +class hotel_floor(osv.osv): + _name = "hotel.floor" + _description = "Floor" + _columns = { + 'name': fields.char('Floor Name', size=64, required=True,select=True), + 'sequence' : fields.integer('Sequence', size=64), + } +hotel_floor() + +class product_category(osv.osv): + _inherit="product.category" + _columns = { + 'isroomtype':fields.boolean('Is Room Type'), + 'isamenitype':fields.boolean('Is amenities Type'), + 'isservicetype':fields.boolean('Is Service Type'), + } +product_category() + +class hotel_room_type(osv.osv): + _name = "hotel.room_type" + _inherits = {'product.category':'cat_id'} + _description = "Room Type" + _columns = { + 'cat_id':fields.many2one('product.category','category',required=True,select=True,ondelete='cascade'), + + } + _defaults = { + 'isroomtype': lambda *a: 1, + } +hotel_room_type() + + +class product_product(osv.osv): + _inherit="product.product" + _columns = { + 'isroom':fields.boolean('Is Room'), + 'iscategid':fields.boolean('Is categ id'), + 'isservice':fields.boolean('Is Service id'), + + } +product_product() + +class hotel_room_amenities_type(osv.osv): + _name='hotel.room_amenities_type' + _description='amenities Type' + _inherits = {'product.category':'cat_id'} + _columns = { + 'cat_id':fields.many2one('product.category','category',required=True,ondelete='cascade'), + } + _defaults = { + 'isamenitype': lambda *a: 1, + + } + +hotel_room_amenities_type() + +class hotel_room_amenities(osv.osv): + _name='hotel.room_amenities' + _description='Room amenities' + _inherits={'product.product':'room_categ_id'} + _columns = { + 'room_categ_id':fields.many2one('product.product','Product Category',required=True,ondelete='cascade'), + 'rcateg_id':fields.many2one('hotel.room_amenities_type','Amenity Catagory'), + 'amenity_rate':fields.integer('Amenity Rate'), + } + _defaults = { + 'iscategid': lambda *a: 1, + } + +hotel_room_amenities() + +class hotel_room(osv.osv): + + _name='hotel.room' + _inherits={'product.product':'product_id'} + _description='Hotel Room' + _columns = { + + 'product_id': fields.many2one('product.product','Product_id',required=True,ondelete='cascade'), + 'floor_id':fields.many2one('hotel.floor','Floor No'), + 'max_adult':fields.integer('Max Adult'), + 'max_child':fields.integer('Max Child'), + 'avail_status':fields.selection([('assigned','Assigned'),(' unassigned','Unassigned')],'Room Status'), + 'room_amenities':fields.many2many('hotel.room_amenities','temp_tab','room_amenities','rcateg_id','Room Amenities'), + } + _defaults = { + 'isroom': lambda *a: 1, + 'rental': lambda *a: 1, + } + +hotel_room() + +class hotel_folio(osv.osv): + + def _incoterm_get(self, cr, uid, context={}): + return self.pool.get('sale.order')._incoterm_get(cr, uid, context={}) + + def copy(self, cr, uid, id, default=None,context={}): + return self.pool.get('sale.order').copy(cr, uid, id, default=None,context={}) + + def _invoiced(self, cursor, user, ids, name, arg, context=None): + return self.pool.get('sale.order')._invoiced(cursor, user, ids, name, arg, context=None) + + def _invoiced_search(self, cursor, user, obj, name, args): + return self.pool.get('sale.order')._invoiced_search(cursor, user, obj, name, args) + + def _amount_untaxed(self, cr, uid, ids, field_name, arg, context): + return self.pool.get('sale.order')._amount_untaxed(cr, uid, ids, field_name, arg, context) + + def _amount_tax(self, cr, uid, ids, field_name, arg, context): + return self.pool.get('sale.order')._amount_tax(cr, uid, ids, field_name, arg, context) + + def _amount_total(self, cr, uid, ids, field_name, arg, context): + return self.pool.get('sale.order')._amount_total(cr, uid, ids, field_name, arg, context) + + _name = 'hotel.folio' + _description = 'hotel folio new' + _inherits = {'sale.order':'order_id'} + _columns = { + 'order_id':fields.many2one('sale.order','order_id',required=True,ondelete='cascade'), + 'checkin_date': fields.datetime('Check In',required=True,readonly=True, states={'draft':[('readonly',False)]}), + 'checkout_date': fields.datetime('Check Out',required=True,readonly=True, states={'draft':[('readonly',False)]}), + 'room_lines': fields.one2many('hotel_folio.line','folio_id'), + 'service_lines': fields.one2many('hotel_service.line','folio_id'), + 'hotel_policy':fields.selection([('prepaid','On Booking'),('manual','On Check In'),('picking','On Checkout')],'Hotel Policy', required=True), + 'duration':fields.float('Duration'), + } + _defaults = { + 'hotel_policy':'manual' + } + _sql_constraints = [ + ('check_in_out', 'CHECK (checkin_date<=checkout_date)', 'Check in Date Should be lesser than the Check Out Date!'), + ] + + def onchange_dates(self,cr,uid,ids,checkin_date=False,checkout_date=False,duration=False): + value = {} + if not duration: + duration = 0 + if checkin_date and checkout_date: + chkin_dt = datetime.datetime.strptime(checkin_date,'%Y-%m-%d %H:%M:%S') + chkout_dt = datetime.datetime.strptime(checkout_date,'%Y-%m-%d %H:%M:%S') + dur = chkout_dt - chkin_dt + duration = dur.days + value.update({'value':{'duration':duration}}) + else: + if checkin_date: + chkin_dt = datetime.datetime.strptime(checkin_date,'%Y-%m-%d %H:%M:%S') + chkout_dt = chkin_dt + datetime.timedelta(days=duration) + checkout_date = datetime.datetime.strftime(chkout_dt,'%Y-%m-%d %H:%M:%S') + value.update({'value':{'checkout_date':checkout_date}}) + return value + + def create(self, cr, uid, vals, context=None, check=True): + tmp_room_lines = vals.get('room_lines',[]) + tmp_service_lines = vals.get('service_lines',[]) + vals['order_policy'] = vals.get('hotel_policy','manual') + if not vals.has_key("folio_id"): + vals.update({'room_lines':[],'service_lines':[]}) + folio_id = super(hotel_folio, self).create(cr, uid, vals, context) + for line in tmp_room_lines: + line[2].update({'folio_id':folio_id}) + for line in tmp_service_lines: + line[2].update({'folio_id':folio_id}) + vals.update({'room_lines':tmp_room_lines,'service_lines':tmp_service_lines}) + super(hotel_folio, self).write(cr, uid,[folio_id], vals, context) + else: + folio_id = super(hotel_folio, self).create(cr, uid, vals, context) + return folio_id + + + def onchange_shop_id(self, cr, uid, ids, shop_id): + return self.pool.get('sale.order').onchange_shop_id(cr, uid, ids, shop_id) + + def onchange_partner_id(self, cr, uid, ids, part): + return self.pool.get('sale.order').onchange_partner_id(cr, uid, ids, part) + + def button_dummy(self, cr, uid, ids, context={}): + return self.pool.get('sale.order').button_dummy(cr, uid, ids, context={}) + + def action_invoice_create(self, cr, uid, ids, grouped=False, states=['confirmed','done']): + i = self.pool.get('sale.order').action_invoice_create(cr, uid, ids, grouped=False, states=['confirmed','done']) + for line in self.browse(cr, uid, ids, context={}): + self.write(cr, uid, [line.id], {'invoiced':True}) + if grouped: + self.write(cr, uid, [line.id], {'state' : 'progress'}) + else: + self.write(cr, uid, [line.id], {'state' : 'progress'}) + return i + + + def action_invoice_cancel(self, cr, uid, ids, context={}): + res = self.pool.get('sale.order').action_invoice_cancel(cr, uid, ids, context={}) + for sale in self.browse(cr, uid, ids): + for line in sale.order_line: + self.pool.get('sale.order.line').write(cr, uid, [line.id], {'invoiced': invoiced}) + self.write(cr, uid, ids, {'state':'invoice_except', 'invoice_id':False}) + return res + def action_cancel(self, cr, uid, ids, context={}): + c = self.pool.get('sale.order').action_cancel(cr, uid, ids, context={}) + ok = True + for sale in self.browse(cr, uid, ids): + for r in self.read(cr,uid,ids,['picking_ids']): + for pick in r['picking_ids']: + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'stock.picking', pick, 'button_cancel', cr) + for r in self.read(cr,uid,ids,['invoice_ids']): + for inv in r['invoice_ids']: + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'account.invoice', inv, 'invoice_cancel', cr) + + self.write(cr,uid,ids,{'state':'cancel'}) + return c + + def action_wait(self, cr, uid, ids, *args): + res = self.pool.get('sale.order').action_wait(cr, uid, ids, *args) + for o in self.browse(cr, uid, ids): + if (o.order_policy == 'manual') and (not o.invoice_ids): + self.write(cr, uid, [o.id], {'state': 'manual'}) + else: + self.write(cr, uid, [o.id], {'state': 'progress'}) + return res + def test_state(self, cr, uid, ids, mode, *args): + write_done_ids = [] + write_cancel_ids = [] + res = self.pool.get('sale.order').test_state(cr, uid, ids, mode, *args) + if write_done_ids: + self.pool.get('sale.order.line').write(cr, uid, write_done_ids, {'state': 'done'}) + if write_cancel_ids: + self.pool.get('sale.order.line').write(cr, uid, write_cancel_ids, {'state': 'cancel'}) + return res + def procurement_lines_get(self, cr, uid, ids, *args): + res = self.pool.get('sale.order').procurement_lines_get(cr, uid, ids, *args) + return res + def action_ship_create(self, cr, uid, ids, *args): + res = self.pool.get('sale.order').action_ship_create(cr, uid, ids, *args) + return res + def action_ship_end(self, cr, uid, ids, context={}): + res = self.pool.get('sale.order').action_ship_end(cr, uid, ids, context={}) + for order in self.browse(cr, uid, ids): + val = {'shipped':True} + self.write(cr, uid, [order.id], val) + return res + def _log_event(self, cr, uid, ids, factor=0.7, name='Open Order'): + return self.pool.get('sale.order')._log_event(cr, uid, ids, factor=0.7, name='Open Order') + def has_stockable_products(self,cr, uid, ids, *args): + return self.pool.get('sale.order').has_stockable_products(cr, uid, ids, *args) + def action_cancel_draft(self, cr, uid, ids, *args): + d = self.pool.get('sale.order').action_cancel_draft(cr, uid, ids, *args) + self.write(cr, uid, ids, {'state':'draft', 'invoice_ids':[], 'shipped':0}) + self.pool.get('sale.order.line').write(cr, uid,ids, {'invoiced':False, 'state':'draft', 'invoice_lines':[(6,0,[])]}) + return d + +hotel_folio() + +class hotel_folio_line(osv.osv): + + def copy(self, cr, uid, id, default=None, context={}): + return self.pool.get('sale.order.line').copy(cr, uid, id, default=None, context={}) + def _amount_line_net(self, cr, uid, ids, field_name, arg, context): + return self.pool.get('sale.order.line')._amount_line_net(cr, uid, ids, field_name, arg, context) + def _amount_line(self, cr, uid, ids, field_name, arg, context): + return self.pool.get('sale.order.line')._amount_line(cr, uid, ids, field_name, arg, context) + def _number_packages(self, cr, uid, ids, field_name, arg, context): + return self.pool.get('sale.order.line')._number_packages(cr, uid, ids, field_name, arg, context) + def _get_1st_packaging(self, cr, uid, context={}): + return self.pool.get('sale.order.line')._get_1st_packaging(cr, uid, context={}) + def _get_checkin_date(self,cr, uid, context={}): + if 'checkin_date' in context: + return context['checkin_date'] + return time.strftime('%Y-%m-%d %H:%M:%S') + def _get_checkout_date(self,cr, uid, context={}): + if 'checkin_date' in context: + return context['checkout_date'] + return time.strftime('%Y-%m-%d %H:%M:%S') + + _name='hotel_folio.line' + _description='hotel folio1 room line' + _inherits={'sale.order.line':'order_line_id'} + _columns={ + 'order_line_id':fields.many2one('sale.order.line','order_line_id',required=True,ondelete='cascade'), + 'folio_id':fields.many2one('hotel.folio','folio_id',ondelete='cascade'), + 'checkin_date': fields.datetime('Check In',required=True), + 'checkout_date': fields.datetime('Check Out',required=True), + } + _defaults={ + 'checkin_date':_get_checkin_date, + 'checkout_date':_get_checkout_date, + + } + + def create(self, cr, uid, vals, context=None, check=True): + if not context: + context={} + if vals.has_key("folio_id"): + folio = self.pool.get("hotel.folio").browse(cr,uid,[vals['folio_id']])[0] + vals.update({'order_id':folio.order_id.id}) + roomline = super(osv.osv, self).create(cr, uid, vals, context) + return roomline + + def uos_change(self, cr, uid, ids, product_uos, product_uos_qty=0, product_id=None): + return self.pool.get('sale.order.line').uos_change(cr, uid, ids, product_uos, product_uos_qty=0, product_id=None) + + def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, + uom=False, qty_uos=0, uos=False, name='', partner_id=False, + lang=False, update_tax=True, date_order=False): + return self.pool.get('sale.order.line').product_id_change(cr, uid, ids, pricelist, product, qty=0, + uom=False, qty_uos=0, uos=False, name='',partner_id=partner_id, + lang=False, update_tax=True, date_order=False) + + def product_uom_change(self, cursor, user, ids, pricelist, product, qty=0, + uom=False, qty_uos=0, uos=False, name='', partner_id=False, + lang=False, update_tax=True, date_order=False): + return self.pool.get('sale.order.line').product_uom_change(cursor, user, ids, pricelist, product, qty=0, + uom=False, qty_uos=0, uos=False, name='', partner_id=partner_id, + lang=False, update_tax=True, date_order=False) + + def on_change_checkout(self,cr, uid, ids, checkin_date=time.strftime('%Y-%m-%d %H:%M:%S'),checkout_date=time.strftime('%Y-%m-%d %H:%M:%S'),context=None): + qty = 1 + if checkout_date < checkin_date: + raise osv.except_osv ('Error !','Checkout must be greater or equal checkin date') + if checkin_date: + diffDate = datetime.datetime(*time.strptime(checkout_date,'%Y-%m-%d %H:%M:%S')[:5]) - datetime.datetime(*time.strptime(checkin_date,'%Y-%m-%d %H:%M:%S')[:5]) + qty = diffDate.days + if qty == 0: + qty=1 + return {'value':{'product_uom_qty':qty}} + + def button_confirm(self, cr, uid, ids, context={}): + + return self.pool.get('sale.order.line').button_confirm(cr, uid, ids, context={}) + def button_done(self, cr, uid, ids, context={}): + res = self.pool.get('sale.order.line').button_done(cr, uid, ids, context={}) + wf_service = netsvc.LocalService("workflow") + res = self.write(cr, uid, ids, {'state':'done'}) + for line in self.browse(cr,uid,ids,context): + wf_service.trg_write(uid, 'sale.order', line.order_id.id, cr) + return res + + + def uos_change(self, cr, uid, ids, product_uos, product_uos_qty=0, product_id=None): + return self.pool.get('sale.order.line').uos_change(cr, uid, ids, product_uos, product_uos_qty=0, product_id=None) + def copy(self, cr, uid, id, default=None,context={}): + return self.pool.get('sale.order.line').copy(cr, uid, id, default=None,context={}) + + + +hotel_folio_line() + +class hotel_service_line(osv.osv): + + def copy(self, cr, uid, id, default=None, context={}): + return self.pool.get('sale.order.line').copy(cr, uid, id, default=None, context={}) + def _amount_line_net(self, cr, uid, ids, field_name, arg, context): + return self.pool.get('sale.order.line')._amount_line_net(cr, uid, ids, field_name, arg, context) + def _amount_line(self, cr, uid, ids, field_name, arg, context): + return self.pool.get('sale.order.line')._amount_line(cr, uid, ids, field_name, arg, context) + def _number_packages(self, cr, uid, ids, field_name, arg, context): + return self.pool.get('sale.order.line')._number_packages(cr, uid, ids, field_name, arg, context) + def _get_1st_packaging(self, cr, uid, context={}): + return self.pool.get('sale.order.line')._get_1st_packaging(cr, uid, context={}) + + + _name='hotel_service.line' + _description='hotel Service line' + _inherits={'sale.order.line':'service_line_id'} + _columns={ + 'service_line_id':fields.many2one('sale.order.line','service_line_id',required=True,ondelete='cascade'), + 'folio_id':fields.many2one('hotel.folio','folio_id',ondelete='cascade'), + + } + + def create(self, cr, uid, vals, context=None, check=True): + if not context: + context={} + if vals.has_key("folio_id"): + folio = self.pool.get("hotel.folio").browse(cr,uid,[vals['folio_id']])[0] + vals.update({'order_id':folio.order_id.id}) + roomline = super(osv.osv, self).create(cr, uid, vals, context) + return roomline + def uos_change(self, cr, uid, ids, product_uos, product_uos_qty=0, product_id=None): + return self.pool.get('sale.order.line').uos_change(cr, uid, ids, product_uos, product_uos_qty=0, product_id=None) + def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, + uom=False, qty_uos=0, uos=False, name='', partner_id=False, + lang=False, update_tax=True, date_order=False): + return self.pool.get('sale.order.line').product_id_change(cr, uid, ids, pricelist, product, qty=0, + uom=False, qty_uos=0, uos=False, name='', partner_id=partner_id, + lang=False, update_tax=True, date_order=False) + def product_uom_change(self, cursor, user, ids, pricelist, product, qty=0, + uom=False, qty_uos=0, uos=False, name='', partner_id=False, + lang=False, update_tax=True, date_order=False): + return self.pool.get('sale.order.line').product_uom_change(cursor, user, ids, pricelist, product, qty=0, + uom=False, qty_uos=0, uos=False, name='', partner_id=partner_id, + lang=False, update_tax=True, date_order=False) + def on_change_checkout(self,cr, uid, ids, checkin_date=time.strftime('%Y-%m-%d %H:%M:%S'),checkout_date=time.strftime('%Y-%m-%d %H:%M:%S'),context=None): + qty = 1 + if checkout_date < checkin_date: + raise osv.except_osv ('Error !','Checkout must be greater or equal checkin date') + if checkin_date: + diffDate = datetime.datetime(*time.strptime(checkout_date,'%Y-%m-%d %H:%M:%S')[:5]) - datetime.datetime(*time.strptime(checkin_date,'%Y-%m-%d %H:%M:%S')[:5]) + qty = diffDate.days + return {'value':{'product_uom_qty':qty}} + + def button_confirm(self, cr, uid, ids, context={}): + + return self.pool.get('sale.order.line').button_confirm(cr, uid, ids, context={}) + def button_done(self, cr, uid, ids, context={}): + return self.pool.get('sale.order.line').button_done(cr, uid, ids, context={}) + def uos_change(self, cr, uid, ids, product_uos, product_uos_qty=0, product_id=None): + return self.pool.get('sale.order.line').uos_change(cr, uid, ids, product_uos, product_uos_qty=0, product_id=None) + def copy(self, cr, uid, id, default=None,context={}): + return self.pool.get('sale.order.line').copy(cr, uid, id, default=None,context={}) + + + +hotel_service_line() + +class hotel_service_type(osv.osv): + _name = "hotel.service_type" + _inherits = {'product.category':'ser_id'} + _description = "Service Type" + _columns = { + 'ser_id':fields.many2one('product.category','category',required=True,select=True,ondelete='cascade'), + + } + _defaults = { + 'isservicetype': lambda *a: 1, + } +hotel_service_type() + +class hotel_services(osv.osv): + + _name = 'hotel.services' + _description = 'Hotel Services and its charges' + _inherits={'product.product':'service_id'} + _columns = { + 'service_id': fields.many2one('product.product','Service_id',required=True,ondelete='cascade'), + + } + _defaults = { + 'isservice': lambda *a: 1, + } +hotel_services() + + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/hotel/hotel_data.xml b/hotel/hotel_data.xml new file mode 100755 index 000000000..d382cd8d9 --- /dev/null +++ b/hotel/hotel_data.xml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + All Aminities + + + + + Beds + + + + Tables + + + + Single Bed + + + + Double Bed + + + + + + + All Rooms + + + + Single + + + + Double + + + + + + No Room + + + + + No Room + + + + + + All Services + + + + Fixed + + + + Variable + + + + + + + + No Room Available + + 0.00 + + + Single-101 + + 100.00 + + + + Single-102 + + 100.00 + + + Single-103 + + 100.00 + + + Double-201 + + 200.00 + + + Double-202 + + 200.00 + + + Double-203 + + 200.00 + + + No Room Record + + 0.00 + + + + + + + Internet + + 200.00 + + + Taxi + + 500.00 + + + Laundry + + 150.00 + + + + + \ No newline at end of file diff --git a/hotel/hotel_folio_workflow.xml b/hotel/hotel_folio_workflow.xml new file mode 100755 index 000000000..32881584b --- /dev/null +++ b/hotel/hotel_folio_workflow.xml @@ -0,0 +1,242 @@ + + + + + hotel.folio.basic + hotel.folio + True + + + #---------------------------------------------- + # Activity + #---------------------------------------------- + + + + True + draft + + + + + router + function + action_wait() + OR + + + + wait_invoice + + + + wait_ship + + + + + done + True + function + write({'state':'done'}) + AND + + + + cancel + True + stopall + action_cancel() + + + + cancel2 + True + stopall + action_cancel() + + + + cancel3 + True + stopall + action_cancel() + + + + + invoice + subflow + + action_invoice_create() + + + + invoice_except + function + action_invoice_cancel() + + + + invoice_end + dummy + + + + invoice_cancel + True + stopall + action_cancel() + + + + + ship + function + action_ship_create() + + + + ship_except + function + write({'state':'shipping_except'}) + + + + ship_end + function + action_ship_end() + + + + ship_cancel + True + stopall + action_cancel() + + + #---------------------------------------------- + # Transistion + #---------------------------------------------- + + + + + + + + + order_confirm + + + + + cancel + + + + + (order_policy=='picking') + + + + + + + + + + + + + + + + + cancel + + + + + + cancel + + + + + + (order_policy!='prepaid') or invoiced + + + + + + (order_policy=='prepaid') or ((order_policy=='postpaid') and shipped) + + + + + + manual_invoice + + + + + + subflow.paid + + + + + subflow.cancel + + + + + invoice_recreate + + + + + invoice_corrected + + + + + invoice_cancel + + + + + + + + + + mrp.procurement + procurement_lines_get() + test_state('finished') + + + + + test_state('canceled') + + + + + ship_recreate + + + + + ship_corrected + + + + + ship_cancel + + + + \ No newline at end of file diff --git a/hotel/hotel_report.xml b/hotel/hotel_report.xml new file mode 100755 index 000000000..60d0414c5 --- /dev/null +++ b/hotel/hotel_report.xml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/hotel/hotel_view.xml b/hotel/hotel_view.xml new file mode 100755 index 000000000..59d3a7b31 --- /dev/null +++ b/hotel/hotel_view.xml @@ -0,0 +1,747 @@ + + + + + + + + + + + + hotel.floor.form + hotel.floor + form + +
+ + + + +
+ + hotel.floor.tree + hotel.floor + tree + + + + + + + + + Floor Structure + hotel.floor + form + tree,form + + + + + + + hotel.room_amenities_type_form + hotel.room_amenities_type + form + +
+ + + + +
+ + hotel.room_amenities_type_list + hotel.room_amenities_type + tree + + + + + + + + + Hotel Room Amenities Type + hotel.room_amenities_type + form + tree,form + + + + + + + + + + hotel.room_amenities_form + hotel.room_amenities + form + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + hotel.room_amenities_search + hotel.room_amenities + search + + + + + + + + + + + + + + + hotel.room_amenities_list + hotel.room_amenities + tree + + + + + + + + + + + Hotel Room Amenities + hotel.room_amenities + form + tree,form + + + + + + + hotel.room_type.form + hotel.room_type + form + +
+ + + + +
+ + hotel.room_type.tree + hotel.room_type + tree + + + + + + + + Room Type + hotel.room_type + form + tree,form + + + + + + + + hotel.room.form + hotel.room + form + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + hotel.room.search + hotel.room + search + + + + + + + + + + + + + + + hotel.room.tree + hotel.room + tree + + + + + + + + + + Hotel Room + hotel.room + form + tree,form + + + + + + + + hotel.service_type.form + hotel.service_type + form + +
+ + + + +
+ + hotel.service_type.tree + hotel.service_type + tree + + + + + + + + Service Type + hotel.service_type + form + tree,form + + + + + + + .hotel.services.form + hotel.services + form + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + hotel.services.search + hotel.services + search + + + + + + + + + + + + + + + hotel.services.tree + hotel.services + tree + + + + + + + + + + Hotel Services + hotel.services + form + tree,form + + + + + + + + + hotel.folio.form + hotel.folio + form + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + +