diff --git a/__init__.py b/__init__.py index 6c6ca22..383fb5c 100644 --- a/__init__.py +++ b/__init__.py @@ -6,7 +6,6 @@ from . import configuration from . import room from . import booking from . import folio -from . import housekeeping from . import company from . import city from . import party @@ -44,8 +43,8 @@ def register(): booking.BookingStatementLine, booking.StatementPaymentForm, statement.StatementLine, - housekeeping.Housekeeping, - housekeeping.HousekeepingCleaningType, + # housekeeping.Housekeeping, + # housekeeping.HousekeepingCleaningType, party.Party, channel.SaleChannel, channel.ChannelTax, @@ -69,14 +68,14 @@ def register(): service.ServiceLine, service.ServiceKind, service.CreateDailyServicesStart, - housekeeping.HotelHousekeepingTask, - housekeeping.HotelTask, - housekeeping.HousekeepingServiceStart, room.CleanningDays, + room.CleaningType, dash.DashApp, dash.AppHotelPlanner, invoice.InvoiceLine, invoice.Invoice, + room.HotelTask, + room.HousekeepingStart, # folio.TransferfolioStart, # folio.TransferChargeStart, # folio.CheckOutfolioFailed, @@ -96,7 +95,7 @@ def register(): sale.InvoiceIncomeDailyReport, sale.InvoiceSimplifiedReport, service.ServiceReport, - housekeeping.HousekeepingServiceReport, + room.HousekeepingReport, module='hotel', type_='report') Pool.register( booking.SelectRooms, @@ -111,7 +110,7 @@ def register(): folio.StatisticsByMonth, folio.ReverseCheckout, service.CreateDailyServices, - housekeeping.HousekeepingService, + room.Housekeeping, sale.InvoiceIncomeDaily, party.CreateGuest, # folio.ChangeRoom, diff --git a/booking.fodt b/booking.fodt index e99b5ec..2dd61b3 100644 Binary files a/booking.fodt and b/booking.fodt differ diff --git a/booking.py b/booking.py index cd6008e..5436da6 100644 --- a/booking.py +++ b/booking.py @@ -49,7 +49,7 @@ class Booking(Workflow, ModelSQL, ModelView): party = fields.Many2One('party.party', 'Customer', required=False, select=True, help="Person or company owner of the booking.", states={ - 'required': Eval('state') == 'check_in', + # 'required': Eval('state') == 'check_in', 'readonly': Not(In(Eval('state'), ['offer', 'confirmed'])), }) contact = fields.Char('Contact', states=STATES_CHECKIN, @@ -420,6 +420,9 @@ class Booking(Workflow, ModelSQL, ModelView): return payment_mode = bk.channel.payment_mode + if not payment_mode: + raise UserError(gettext('hotel.msg_missing_payment_mode')) + party = bk.channel.agent.party account_id = Voucher.get_account('receipt', payment_mode) diff --git a/configuration.py b/configuration.py index c6dc541..d9e1dad 100644 --- a/configuration.py +++ b/configuration.py @@ -38,8 +38,6 @@ class Configuration(ModelSQL, ModelView): 'configuration', 'product', 'Default Charges') check_in_time = fields.Time('Check In Time', required=True) check_out_time = fields.Time('Check Out Time', required=True) - default_accommodation = fields.Many2One('product.product', - 'Default Accommodation') taxes_exception_rule = fields.Many2One('account.tax.rule', 'Taxes Exception Rule') default_channel_seller = fields.Many2One('hotel.channel', 'Default Channel') company = fields.Many2One('company.company', 'Company', required=True, @@ -54,11 +52,11 @@ class Configuration(ModelSQL, ModelView): ], 'Full Clean Time') full_clean_lapse = fields.Integer('Full Clean Lapse', help='In Days') age_children_policy = fields.Integer('Age Children Policy', help='In Days') - cleaning_check_out = fields.Many2One('hotel.housekeeping.cleaning_type', + cleaning_check_out = fields.Many2One('hotel.room.cleaning_type', 'Cleaning Check Out') - cleaning_check_in = fields.Many2One('hotel.housekeeping.cleaning_type', + cleaning_check_in = fields.Many2One('hotel.room.cleaning_type', 'Cleaning Check In') - cleaning_occupied = fields.Many2One('hotel.housekeeping.cleaning_type', + cleaning_occupied = fields.Many2One('hotel.room.cleaning_type', 'Cleaning Occupied') quarantine_rooms = fields.Numeric('Quarantine Rooms', digits=(2, 0), help='In days') booking_email_template = fields.Many2One('email.template', diff --git a/folio.py b/folio.py index 9ff53dd..2a2c5cb 100644 --- a/folio.py +++ b/folio.py @@ -219,23 +219,21 @@ class Folio(ModelSQL, ModelView): raise UserError(gettext('hotel.msg_missing_select_room')) rec.set_registration_number() rec.check_room() - cls.update_room(rec.room, 'dirty') + cls.update_room(rec, 'check_in') cls.write(records, {'registration_state': 'check_in'}) def check_room(self): - Housekeeping = Pool().get('hotel.housekeeping') - rooms = Housekeeping.search([ - ('room', '=', self.room.id) - ]) - if not rooms or rooms[0].state != 'clean': - raise UserError(gettext('hotel.msg_room_no_clean', s=self.room.name)) + if self.room.state != 'clean': + raise UserError( + gettext('hotel.msg_room_no_clean', s=self.room.name) + ) @classmethod @ModelView.button def check_out(cls, records): for record in records: cls.write([record], {'registration_state': 'check_out'}) - cls.update_room(record.room, 'dirty') + cls.update_room(record, 'check_out') @classmethod @ModelView.button @@ -257,14 +255,21 @@ class Folio(ModelSQL, ModelView): return 'pending' @classmethod - def update_room(cls, room, state): - Housekeeping = Pool().get('hotel.housekeeping') - hk_rooms = Housekeeping.search([ - ('room', '=', room.id), - ]) - for hk_room in hk_rooms: - hk_room.state = state - hk_room.save() + def update_room(cls, folio, status): + pool = Pool() + Configuration = pool.get('hotel.configuration') + config = Configuration.get_configuration() + cleaning_type_id = None + if status in 'check_in' and config.cleaning_occupied: + cleaning_type_id = config.cleaning_occupied.id + elif status in 'check_out' and config.cleaning_check_out: + cleaning_type_id = config.cleaning_check_out.id + + room = folio.room + room.state = 'dirty' + if cleaning_type_id: + room.cleaning_type = cleaning_type_id + room.save() def get_invoice_state(self, name=None): if self.invoice_line: diff --git a/housekeeping.fods b/housekeeping.fods index 686dfb8..c9ea765 100644 Binary files a/housekeeping.fods and b/housekeeping.fods differ diff --git a/housekeeping.py b/housekeeping.py index 6f71fca..7890637 100644 --- a/housekeeping.py +++ b/housekeeping.py @@ -11,11 +11,16 @@ from trytond.pool import Pool STATES = {'invisible': (Eval('type') != 'service')} -class HousekeepingCleaningType(ModelSQL, ModelView): - "Housekeeping Cleaning Type" +class RoomCleaningType(ModelSQL, ModelView): + "Room Cleaning Type" __name__ = "hotel.housekeeping.cleaning_type" name = fields.Char('Name', required=True, select=True) description = fields.Text('Description') + kind = fields.Selection([ + ('tweak', 'Tweak'), + ('deep', 'Deep'), + ('standard', 'Standard'), + ], 'Kind', required=True) class Housekeeping(Workflow, ModelSQL, ModelView): @@ -24,17 +29,17 @@ class Housekeeping(Workflow, ModelSQL, ModelView): employee = fields.Many2One('company.employee', 'Employee') room = fields.Many2One('hotel.room', 'Room', required=True) state = fields.Selection([ - ('inspected', 'Inspected'), - ('dirty', 'Dirty'), - ('clean', 'Clean'), - ('maintenance', 'Maintenance'), - ], 'Status', required=True, readonly=True) + ('inspected', 'Inspected'), + ('dirty', 'Dirty'), + ('clean', 'Clean'), + ('maintenance', 'Maintenance'), + ], 'Status', required=True, readonly=True) state_string = state.translated('state') availability = fields.Selection([ - ('blocked', 'Blocked'), - ('available', 'Available'), - ('occupied', 'Occupied'), - ], 'Availability', required=True) + ('blocked', 'Blocked'), + ('available', 'Available'), + ('occupied', 'Occupied'), + ], 'Availability', required=True) cleaning_type = fields.Many2One('hotel.housekeeping.cleaning_type', 'Cleaning Type', required=False) availability_string = state.translated('availability') @@ -44,8 +49,10 @@ class Housekeeping(Workflow, ModelSQL, ModelView): start_date_assigned = fields.Date('Start Date Assigned') end_date_assigned = fields.Date('End Date Assigned') notes = fields.Text('Notes') - check_in_time = fields.Function(fields.Time('Check In Time'), 'get_check_in_time') - check_out_time = fields.Function(fields.Time('Check In Time'), 'get_check_out_time') + check_in_time = fields.Function(fields.Time('Check In Time'), + 'get_check_in_time') + check_out_time = fields.Function(fields.Time('Check In Time'), + 'get_check_out_time') amenities = fields.Function(fields.Many2Many('hotel.room-hotel.amenities', 'room', 'amenities', 'Amenities'), 'get_amenities') @@ -165,87 +172,3 @@ class Housekeeping(Workflow, ModelSQL, ModelView): # for line in lines: # occupancies.add(line.id) # return list(occupancies) - - -class HotelTask(ModelSQL, ModelView): - "Hotel Task" - __name__ = "hotel.task" - name = fields.Char('Name Task', required=True, select=True) - frecuency = fields.Integer('Frecuency', select=True, help='In days') - quantity = fields.Integer('Quantity', select=True) - - -class HotelHousekeepingTask(ModelView, ModelSQL): - 'Hotel Housekeeping Task' - __name__ = 'hotel.housekeeping.task' - housekeeping = fields.Many2One('hotel.housekeeping', 'Hotel Housekeeping', - ondelete='CASCADE', select=True, required=True) - task = fields.Many2One('hotel.task', 'Task', select=True, required=True) - frecuency = fields.Integer('Frecuency', select=True, help='In days') - quantity = fields.Integer('Quantity', select=True) - - @fields.depends('task', 'frecuency') - def on_change_task(self, name=None): - if self.task: - self.frecuency = self.task.frecuency - - -class HousekeepingServiceStart(ModelView): - 'Print Housekeeping Service Start' - __name__ = 'hotel.print_housekeeping_service.start' - date = fields.Date('Date', required=True) - employee = fields.Many2One('company.employee', 'Employee') - company = fields.Many2One('company.company', 'Company', required=True) - - @staticmethod - def default_date(): - Date_ = Pool().get('ir.date') - return Date_.today() - - @staticmethod - def default_company(): - return Transaction().context.get('company') - - -class HousekeepingService(Wizard): - 'Housekeeping Service' - __name__ = 'hotel.print_housekeeping_service' - start = StateView('hotel.print_housekeeping_service.start', - 'hotel.print_housekeeping_service_start_view_form', [ - Button('Cancel', 'end', 'tryton-cancel'), - Button('Open', 'print_', 'tryton-print', default=True), - ]) - print_ = StateReport('hotel.print_housekeeping_service.report') - - def do_print_(self, action): - company = self.start.company - data = { - 'date': self.start.date, - 'employee': self.start.employee.id if self.start.employee else None, - 'company': company.id, - } - return action, data - - def transition_print_(self): - return 'end' - - -class HousekeepingServiceReport(Report): - __name__ = 'hotel.print_housekeeping_service.report' - - @classmethod - def get_context(cls, records, header, data): - report_context = super().get_context(records, header, data) - pool = Pool() - Company = pool.get('company.company') - Housekeeping = pool.get('hotel.housekeeping') - - dom = [] - if data['employee']: - dom.append(('employee.id', '=', data['employee'])) - - housekeepings = Housekeeping.search(dom) - report_context['records'] = housekeepings - report_context['company'] = Company(data['company']).party.name - report_context['date'] = datetime.now() - return report_context diff --git a/message.xml b/message.xml index ac92270..36d5cd6 100644 --- a/message.xml +++ b/message.xml @@ -81,5 +81,8 @@ this repository contains the full copyright notices and license terms. --> Missing party or customer to pay! + + Missing payment mode in channel! + diff --git a/room.py b/room.py index 604d03f..7cf8061 100644 --- a/room.py +++ b/room.py @@ -1,13 +1,27 @@ #This file is part of Presik. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from trytond.model import ModelView, ModelSQL, fields +from trytond.model import ModelView, ModelSQL, Workflow, fields from trytond.pyson import Eval +from trytond.wizard import ( + Wizard, StateView, Button, StateTransition, StateReport +) +from trytond.pool import Pool +from trytond.report import Report -STATES = {'readonly': (Eval('state') != 'draft')} +class CleaningType(ModelSQL, ModelView): + "Room Cleaning Type" + __name__ = "hotel.room.cleaning_type" + name = fields.Char('Name', required=True, select=True) + description = fields.Text('Description') + kind = fields.Selection([ + ('tweak', 'Tweak'), + ('deep', 'Deep'), + ('standard', 'Standard'), + ], 'Kind', required=True) -class Room(ModelSQL, ModelView): +class Room(Workflow, ModelSQL, ModelView): 'Room' __name__ = 'hotel.room' name = fields.Char('Name', required=True) @@ -33,19 +47,87 @@ class Room(ModelSQL, ModelView): # TODO: Maybe add require a Current State field min_unit_price = fields.Numeric('Min. Unit Price', digits=(16, 4)) max_unit_price = fields.Numeric('Max. Unit Price', digits=(16, 4)) + state = fields.Selection([ + ('inspected', 'Inspected'), + ('dirty', 'Dirty'), + ('clean', 'Clean'), + ('maintenance', 'Maintenance'), + ], 'Status', required=True, readonly=True) + state_string = state.translated('state') + cleaning_type = fields.Many2One('hotel.room.cleaning_type', + 'Cleaning Type', required=False) + last_check_in = fields.DateTime('Last Check In') + last_check_out = fields.DateTime('Last Check Out') + housekeeping = fields.Many2One('company.employee', 'Employee') + notes = fields.Text('Notes') @classmethod def __setup__(cls): super(Room, cls).__setup__() + cls._transitions |= set(( + ('clean', 'dirty'), + ('clean', 'inspected'), + ('clean', 'maintenance'), + ('dirty', 'clean'), + ('dirty', 'maintenance'), + ('dirty', 'inspected'), + ('maintenance', 'inspected'), + ('maintenance', 'dirty'), + ('inspected', 'dirty'), + ('inspected', 'maintenance'), + ('inspected', 'clean'), + )) + cls._buttons.update({ + 'clean': { + 'invisible': Eval('state').in_(['maintenance', 'clean']), + }, + 'dirty': { + 'invisible': Eval('state').in_(['dirty']), + }, + 'inspected': { + 'invisible': Eval('state').in_(['inspected']), + }, + 'maintenance': { + 'invisible': Eval('state').in_(['maintenance']), + }, + }) @staticmethod def default_active(): return True - @fields.depends('start_mnt', 'end_mnt') - def on_change_start_mnt(self): - if not self.start_mnt: - self.end_mnt = None + @staticmethod + def default_state(): + return 'dirty' + + @classmethod + @ModelView.button + @Workflow.transition('clean') + def clean(cls, records): + for rec in records: + rec.cleaning_type = None + rec.save() + + @classmethod + @ModelView.button + @Workflow.transition('dirty') + def dirty(cls, records): + Config = Pool().get('hotel.configuration') + config = Config.get_configuration() + for rec in records: + rec.cleaning_type = config.cleaning_occupied.id + + @classmethod + @ModelView.button + @Workflow.transition('maintenance') + def maintenance(cls, records): + pass + + @classmethod + @ModelView.button + @Workflow.transition('inspected') + def inspected(cls, records): + pass class RoomAmenities(ModelSQL): @@ -74,10 +156,6 @@ class Amenities(ModelSQL, ModelView): 'invisible': Eval('type') != 'lingerie', }, depends=['type']) - @classmethod - def __setup__(cls): - super(Amenities, cls).__setup__() - class RoomClassification(ModelSQL, ModelView): 'Room Classification' @@ -109,3 +187,87 @@ class RoomTemplate(ModelSQL): ondelete='CASCADE') template = fields.Many2One('product.template', 'Product Template', required=True, ondelete='CASCADE') + + +class HousekeepingStart(ModelView): + 'Print Housekeeping Service Start' + __name__ = 'hotel.print_housekeeping.start' + date = fields.Date('Date', required=True) + employee = fields.Many2One('company.employee', 'Employee') + company = fields.Many2One('company.company', 'Company', required=True) + + @staticmethod + def default_date(): + Date_ = Pool().get('ir.date') + return Date_.today() + + @staticmethod + def default_company(): + return Transaction().context.get('company') + + +class Housekeeping(Wizard): + 'Housekeeping Service' + __name__ = 'hotel.print_housekeeping' + start = StateView('hotel.print_housekeeping.start', + 'hotel.print_housekeeping_start_view_form', [ + Button('Cancel', 'end', 'tryton-cancel'), + Button('Open', 'print_', 'tryton-print', default=True), + ]) + print_ = StateReport('hotel.print_housekeeping.report') + + def do_print_(self, action): + company = self.start.company + data = { + 'date': self.start.date, + 'employee': self.start.employee.id if self.start.employee else None, + 'company': company.id, + } + return action, data + + def transition_print_(self): + return 'end' + + +class HousekeepingReport(Report): + __name__ = 'hotel.print_housekeeping.report' + + @classmethod + def get_context(cls, records, header, data): + report_context = super().get_context(records, header, data) + pool = Pool() + Company = pool.get('company.company') + Housekeeping = pool.get('hotel.housekeeping') + + dom = [] + if data['employee']: + dom.append(('employee.id', '=', data['employee'])) + + housekeepings = Housekeeping.search(dom) + report_context['records'] = housekeepings + report_context['company'] = Company(data['company']) + report_context['date'] = datetime.now() + return report_context + + +class HotelTask(ModelSQL, ModelView): + "Hotel Task" + __name__ = "hotel.task" + name = fields.Char('Name Task', required=True, select=True) + frecuency = fields.Integer('Frecuency', select=True, help='In days') + quantity = fields.Integer('Quantity', select=True) + + +# class HotelHousekeepingTask(ModelView, ModelSQL): +# 'Hotel Housekeeping Task' +# __name__ = 'hotel.housekeeping.task' +# room = fields.Many2One('hotel.room', 'Hotel Housekeeping', +# ondelete='CASCADE', select=True, required=True) +# task = fields.Many2One('hotel.task', 'Task', select=True, required=True) +# frecuency = fields.Integer('Frecuency', select=True, help='In days') +# quantity = fields.Integer('Quantity', select=True) +# +# @fields.depends('task', 'frecuency') +# def on_change_task(self, name=None): +# if self.task: +# self.frecuency = self.task.frecuency diff --git a/room.xml b/room.xml index 8560176..0980d2c 100644 --- a/room.xml +++ b/room.xml @@ -193,5 +193,150 @@ this repository contains the full copyright notices and license terms. --> + + + hotel.room + form + room_housekeeping_form + + + hotel.room + tree + room_housekeeping_tree + + + Housekeeping + hotel.room + + + + + + + + + + + + + + + + + + hotel.room.cleaning_type + form + room_cleaning_type_form + + + hotel.room.cleaning_type + tree + room_cleaning_type_tree + + + Cleaning Type + hotel.room.cleaning_type + + + + + + + + + + + + + + + + + hotel.task + form + task_form + + + hotel.task + tree + task_tree + + + Tasks Housekeeping + hotel.task + + + + + + + + + + + + + + + Housekeeping Report + + hotel.print_housekeeping.report + hotel/housekeeping.fods + + + hotel.print_housekeeping.start + form + housekeeping_start_form + + + Housekeeping Report + hotel.print_housekeeping + + + diff --git a/tryton.cfg b/tryton.cfg index 71a5189..f7cb1c7 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -1,5 +1,5 @@ [tryton] -version=6.0.24 +version=6.0.25 depends: party company @@ -23,7 +23,6 @@ xml: location.xml booking.xml folio.xml - housekeeping.xml company.xml city.xml party.xml diff --git a/view/configuration_form.xml b/view/configuration_form.xml index e4990ff..400dbf2 100644 --- a/view/configuration_form.xml +++ b/view/configuration_form.xml @@ -10,8 +10,6 @@ this repository contains the full copyright notices and license terms. -->