First Commit

This commit is contained in:
Dario Lodeiros
2018-07-26 13:06:48 +02:00
commit a9a4e59882
142 changed files with 19956 additions and 0 deletions

30
hotel/wizard/__init__.py Normal file
View File

@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2017 Solucións Aloxa S.L. <info@aloxa.eu>
# Dario Lodeiros <>
# Alexandre Díaz <dev@redneboa.es>
#
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import hotel_wizard
from . import folio_make_invoice_advance
from . import checkinwizard
from . import massive_changes
from . import split_reservation
from . import duplicate_reservation
from . import massive_price_reservation_days

View File

@@ -0,0 +1,225 @@
# -*- coding: utf-8 -*-
import logging
from openerp import models, fields, api
from openerp.exceptions import UserError
from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
class Wizard(models.TransientModel):
_name = 'checkin.wizard'
def default_enter_date(self):
if ('reservation_ids' and 'folio') in self.env.context:
ids = [item[1] for item in self.env.context.get('reservation_ids')]
reservations = self.env['hotel.reservation'].browse(ids)
for res in reservations:
return res.checkin
if 'enter_date' in self.env.context:
return self.env.context['enter_date']
return False
def default_exit_date(self):
if ('reservation_ids' and 'folio') in self.env.context:
ids = [item[1] for item in self.env.context.get('reservation_ids')]
reservations = self.env['hotel.reservation'].browse(ids)
for res in reservations:
return res.checkout
if 'exit_date' in self.env.context:
return self.env.context['exit_date']
return False
def default_reservation_id(self):
if ('reservation_ids' and 'folio') in self.env.context:
ids = [item[1] for item in self.env.context.get('reservation_ids')]
reservations = self.env['hotel.reservation'].browse(ids)
if len(reservations) == 1:
# return current room line (onlyone in this case)
return reservations
for res in reservations:
# return the first room line with free space for a cardex
# TODO: add 'done' to res.state condition... Maybe too restrictive right now
if res.cardex_count < (res.adults + res.children) and res.state not in ["cancelled"]:
return res
elif 'reservation_id' in self.env.context:
return self.env['hotel.reservation'].browse(
self.env.context['reservation_id'])
_logger.info('default_reservation_id is FALSE')
return False
def default_partner_id(self):
# no partner by default. User must search and choose one
return False
def default_cardex_ids(self):
if ('reservation_ids' and 'folio') in self.env.context:
ids = [item[1] for item in self.env.context.get('reservation_ids')]
reservations = self.env['hotel.reservation'].browse(ids)
for res in reservations:
return res.cardex_ids
def default_cardex_ids(self):
if ('reservation_ids' and 'folio') in self.env.context:
ids = [item[1] for item in self.env.context.get('reservation_ids')]
reservations = self.env['hotel.reservation'].browse(ids)
for res in reservations:
return res.segmentation_id
''' TODO: clean-up
def default_count_cardex(self):
if 'reservation_ids' and 'folio' in self.env.context:
ids = [item[1] for item in self.env.context['reservation_ids']]
reservations = self.env['hotel.reservation'].browse(ids)
for res in reservations:
return res.cardex_count
'''
''' TODO: clean-up
def default_pending_cardex(self):
if 'reservation_ids' and 'folio' in self.env.context:
ids = [item[1] for item in self.env.context['reservation_ids']]
reservations = self.env['hotel.reservation'].browse(ids)
for res in reservations:
return res.adults + res.children - res.cardex_count
'''
''' TODO: clean-up - list of checkins on smart button clean is not used anymore
def comp_checkin_list_visible(self):
if 'partner_id' in self.env.context:
self.list_checkin_cardex = False
return
'''
def comp_checkin_edit(self):
if 'edit_cardex' in self.env.context:
return True
return False
cardex_ids = fields.Many2many('cardex', 'reservation_id',
default=default_cardex_ids)
# count_cardex = fields.Integer('Cardex counter',
# default=default_count_cardex)
# pending_cardex = fields.Integer('Cardex pending',
# default=default_pending_cardex)
partner_id = fields.Many2one('res.partner',
default=default_partner_id)
reservation_id = fields.Many2one('hotel.reservation',
default=default_reservation_id)
enter_date = fields.Date(default=default_enter_date,
required=True)
exit_date = fields.Date(default=default_exit_date,
required=True)
firstname_cardex = fields.Char('Firstname',
required=True)
lastname_cardex = fields.Char('Lastname',
required=True)
email_cardex = fields.Char('E-mail')
mobile_cardex = fields.Char('Mobile')
segmentation_id = fields.Many2many(
related='reservation_id.folio_id.segmentation_ids')
''' TODO: clean-up - list of checkins on smart button clean is not used anymore
list_checkin_cardex = fields.Boolean(compute=comp_checkin_list_visible,
default=True, store=True)
'''
# edit_checkin_cardex = fields.Boolean(default=comp_checkin_edit,
# store=True)
op_select_partner = fields.Selection([
('S', 'Select a partner for checkin'),
('C', 'Create a new partner for checkin')],
default='S',
string='Partner for checkin')
# checkin mode:
# 0 - no selection made by the user, so hide the client fields
# 1 - select a client for update his values and do the checkin
# 2 - create a new client with the values and do the checkin
checkin_mode = fields.Integer(default=0)
@api.multi
def action_save_check(self):
# prepare partner values
if self.op_select_partner == 'S':
partner_vals = {
'id': self.partner_id.id,
'firstname': self.firstname_cardex,
'lastname': self.lastname_cardex,
'email': self.email_cardex,
'mobile': self.mobile_cardex,
}
self.partner_id.sudo().write(partner_vals);
elif self.op_select_partner == 'C':
partner_vals = {
'firstname': self.firstname_cardex,
'lastname': self.lastname_cardex,
'email': self.email_cardex,
'mobile': self.mobile_cardex,
}
new_partner = self.env['res.partner'].create(partner_vals)
self.partner_id = self.env['res.partner'].browse(new_partner.id)
# prepare checkin values
cardex_val = {
'partner_id': self.partner_id.id,
'enter_date': self.enter_date,
'exit_date': self.exit_date
}
record_id = self.env['hotel.reservation'].browse(
self.reservation_id.id)
# save the cardex for this reservation
record_id.write({
'cardex_ids': [(0, False, cardex_val)],
'segmentation_id': self.segmentation_id,
})
# update the state of the current reservation
if record_id.cardex_count > 0:
record_id.state = 'booking'
record_id.is_checkin = False
folio = self.env['hotel.folio'].browse(self.reservation_id.folio_id.id)
folio.checkins_reservations -= 1
@api.onchange('reservation_id')
def change_enter_exit_date(self):
record_id = self.env['hotel.reservation'].browse(
self.reservation_id.id)
self.enter_date = record_id.checkin
self.exit_date = record_id.checkout
''' trying to filter the reservations only to pending checkins ...
if 'reservation_ids' and 'folio' in self.env.context:
ids = [item[1] for item in self.env.context['reservation_ids']]
reservations = self.env['hotel.reservation'].browse(ids)
for res in reservations:
_logger.info('reservation cardex_count %d', res.cardex_count)
# return {
# 'domain': {'reservation_id': [('folio_id','=', self.env.context['folio']), 'count_cardex','=','2']},
# 'warning': {'title': "Warning", 'message': self.env.context['cardex_count']},
# }
'''
@api.onchange('partner_id')
def onchange_partner_id(self):
# update partner fields
self.firstname_cardex = self.partner_id.firstname;
self.lastname_cardex = self.partner_id.lastname;
self.email_cardex = self.partner_id.email;
self.mobile_cardex = self.partner_id.mobile;
# show the checkin fields if a partner is selected
if self.op_select_partner == 'S' and self.partner_id.id != False:
self.checkin_mode = 1;
@api.onchange('op_select_partner')
def onchange_op_select_partner(self):
# field one2many return false is record does not exist
if self.op_select_partner == 'S' and self.partner_id.id != False:
self.checkin_mode = 1;
# field one2many return 0 on empty record (nothing typed)
elif self.op_select_partner == 'C' and self.partner_id.id == 0:
self.checkin_mode = 2;

View File

@@ -0,0 +1,104 @@
<?xml version="1.0"?>
<odoo>
<record id="checkin_wizard_form_2" model="ir.ui.view">
<field name="name">wizard.form2</field>
<field name="model">checkin.wizard</field>
<field name="arch" type="xml">
<form string="Add Checkin">
<sheet>
<group col="4">
<field options="{'no_quick_create': True, 'no_create_edit' : True, 'no_open': True}"
name="reservation_id" nolabel="1"
domain="[('folio_id','=',context.get('folio')), ('state', '!=', 'cancelled'), ('cardex_pending', '=', True)]"
style="max-width: 95%; width: 32.2em"/>
</group>
<group col="4">
<field name="enter_date" colspan="2"/>
<field name="exit_date" colspan="2"/>
</group>
<group attrs="{'invisible':[('checkin_mode', '>', 0)]}">
<field name="op_select_partner" widget="radio" options="{'horizontal': true}"/>
<field options="{'no_quick_create': True, 'no_create_edit' : True, 'no_open': True}"
name="partner_id" string="Client name"
style="max-width: 95%; width: 20em"/>
</group>
<group col="4" attrs="{'invisible':[('checkin_mode', '=', 0)]}" >
<field name="firstname_cardex" colspan="2"/>
<field name="lastname_cardex" colspan="2"/>
</group>
<group col="4" attrs="{'invisible':[('checkin_mode', '=', 0)]}" >
<field name="email_cardex" colspan="2"/>
<field name="mobile_cardex" colspan="2"/>
</group>
<footer>
<button name="action_save_check" string="Save Checkin and Print" type="object"
attrs="{'invisible':[('checkin_mode', '=', 0)]}" />
<button name="cancel" string="Cancel" special="cancel"/>
<field name="checkin_mode" invisible="True"/>
<!-- <field name="checkin_show" invisible="True"/> -->
<!-- <field name="edit_checkin_cardex" invisible="True"/> -->
</footer>
</sheet>
</form>
</field>
</record>
<record id="launch_checkin_wizard_add" model="ir.actions.act_window">
<field name="name">Add Check</field>
<field name="res_model">checkin.wizard</field>
<field name="view_id" ref="checkin_wizard_form_2"/>
<field name="target">new</field>
</record>
<!-- TODO: clean-up
<record id="checkin_wizard_form_view" model="ir.ui.view">
<field name="name">wizard.form</field>
<field name="model">checkin.wizard</field>
<field name="arch" type="xml">
<form string="List Checkin">
<sheet>
<group col="4">
<label for="reservation_id" string="Resevation"/>
<field name="reservation_id" nolabel="1" domain="[('folio_id','=',context.get('folio'))]"/>
<button type="action" class="oe_stat_button"
id="cardex_smart_button"
icon="fa-user-plus"
name="%(launch_checkin_wizard_add)d"
context="{'reservation_id': reservation_id, 'hidden_cardex': True}">
<div>
<field name="pending_cardex"
string="Pending" widget="statinfo"/>
</div>
</button>
</group>
<field name="cardex_ids" readonly="1"/>
</sheet>
</form>
</field>
</record>
-->
<!-- TODO: clean-up
<act_window id="launch_checkin_wizard_list"
name="List Checks"
res_model="checkin.wizard"
view_mode="form"
view_id="checkin_wizard_form_view"
target="new"
key2="client_action_multi"/>
-->
<!-- TODO: clean-up
<act_window id="launch_checkin_wizard"
name="List Checkin"
res_model="checkin.wizard"
view_mode="form"
target="new"
key2="client_action_multi"/>
-->
</odoo>

View File

@@ -0,0 +1,103 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2017 Solucións Aloxa S.L. <info@aloxa.eu>
# Alexandre Díaz <dev@redneboa.es>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from datetime import datetime, timedelta
from openerp.exceptions import ValidationError
from openerp import models, fields, api, _
from openerp.tools import (
DEFAULT_SERVER_DATETIME_FORMAT,
DEFAULT_SERVER_DATE_FORMAT)
class DuplicateReservationWizard(models.TransientModel):
_name = 'hotel.wizard.duplicate.reservation'
num = fields.Integer('Num. New Reservations', default=1, min=1)
@api.multi
def duplicate_reservation(self):
self.ensure_one()
hotel_reservation_obj = self.env['hotel.reservation']
reservation_id = hotel_reservation_obj.browse(
self.env.context.get('active_id'))
if not reservation_id:
return False
if reservation_id.splitted:
raise ValidationError(_("Can't duplicate splitted reservations"))
hotel_room_obj = self.env['hotel.room']
hotel_vroom_obj = self.env['hotel.virtual.room']
room_id = hotel_room_obj.search([
('product_id', '=', reservation_id.product_id.id)
], limit=1)
vroom_ids = hotel_vroom_obj.search([
'|', ('room_ids', 'in', [room_id.id]),
('room_type_ids', 'in', [room_id.categ_id.id])
])
cmds_reservation_lines = []
for rline in reservation_id.reservation_lines:
cmds_reservation_lines.append((0, False, {
'date': rline.date,
'price': rline.price,
}))
# Check Input
total_free_rooms = 0
for vroom in vroom_ids:
avails = otel_vroom_obj.check_availability_virtual_room(
reservation_id.checkin,
reservation_id.checkout,
virtual_room_id=vroom.id)
total_free_rooms += len(avails)
if total_free_rooms < self.num:
raise ValidationError(_("Too much duplicated reservations! \
There are no '%d' free rooms") % self.num)
for i in range(0, self.num):
for vroom in vroom_ids:
free_rooms = hotel_vroom_obj.check_availability_virtual_room(
reservation_id.checkin,
reservation_id.checkout,
virtual_room_id=vroom.id)
if any(free_rooms):
new_reservation_id = hotel_reservation_obj.create({
'product_id': free_rooms[0].product_id.id,
'folio_id': reservation_id.folio_id.id,
'checkin': reservation_id.checkin,
'checkout': reservation_id.checkout,
'adults': reservation_id.adults,
'children': reservation_id.children,
'name': reservation_id.name,
'reservation_lines': cmds_reservation_lines,
'price_unit': reservation_id.price_unit,
})
if new_reservation_id:
rpartner_id = reservation_id.order_id.partner_id
new_reservation_id.order_id.partner_id = rpartner_id
break
else:
raise ValidationError(_("Unexpected Error: Can't found a \
free room"))
return True

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" ?>
<odoo>
<record model="ir.ui.view" id="view_hotel_duplicate_reservation_wizard">
<field name="name">hotel.wizard.duplicate.reservation</field>
<field name="model">hotel.wizard.duplicate.reservation</field>
<field name="arch" type="xml">
<form string="Duplicate Rerservation" >
<!-- Common Fields -->
<group>
<field name="num" required="1" />
</group>
<footer>
<button name="duplicate_reservation" string="Duplicate" type="object"
class="oe_highlight" />
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="action_hotel_duplicate_reservation" model="ir.actions.act_window">
<field name="name">Duplicate Reservation</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">hotel.wizard.duplicate.reservation</field>
<field name="view_id" ref="view_hotel_duplicate_reservation_wizard"/>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="binding_model_id" ref="model_hotel_reservation" />
<field name="binding_type">report</field>
</record>
</odoo>

View File

@@ -0,0 +1,183 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import time
from odoo import api, fields, models, _
import odoo.addons.decimal_precision as dp
from odoo.exceptions import UserError
class FolioAdvancePaymentInv(models.TransientModel):
_name = "folio.advance.payment.inv"
_description = "Folios Advance Payment Invoice"
@api.model
def _count(self):
return len(self._context.get('active_ids', []))
@api.model
def _get_advance_payment_method(self):
if self._count() == 1:
sale_obj = self.env['sale.order']
folio_obj = self.env['hotel.folio']
folio = folio_obj.browse(self._context.get('active_ids'))[0]
order = sale_obj.browse(folio_obj.mapped('order_id.id'))
if all([line.product_id.invoice_policy == 'order' for line in order.order_line]) or order.invoice_count:
return 'all'
return 'delivered'
@api.model
def _default_product_id(self):
product_id = self.env['ir.default'].sudo().get('sale.config.settings', 'deposit_product_id_setting')
return self.env['product.product'].browse(product_id)
@api.model
def _default_deposit_account_id(self):
return self._default_product_id().property_account_income_id
@api.model
def _default_deposit_taxes_id(self):
return self._default_product_id().taxes_id
advance_payment_method = fields.Selection([
('delivered', 'Invoiceable lines'),
('all', 'Invoiceable lines (deduct down payments)'),
('percentage', 'Down payment (percentage)'),
('fixed', 'Down payment (fixed amount)')
], string='What do you want to invoice?', default=_get_advance_payment_method, required=True)
product_id = fields.Many2one('product.product', string='Down Payment Product', domain=[('type', '=', 'service')],
default=_default_product_id)
count = fields.Integer(default=_count, string='# of Orders')
amount = fields.Float('Down Payment Amount', digits=dp.get_precision('Account'), help="The amount to be invoiced in advance, taxes excluded.")
deposit_account_id = fields.Many2one("account.account", string="Income Account", domain=[('deprecated', '=', False)],
help="Account used for deposits", default=_default_deposit_account_id)
deposit_taxes_id = fields.Many2many("account.tax", string="Customer Taxes", help="Taxes used for deposits", default=_default_deposit_taxes_id)
@api.onchange('advance_payment_method')
def onchange_advance_payment_method(self):
if self.advance_payment_method == 'percentage':
return {'value': {'amount': 0}}
return {}
@api.multi
def _create_invoice(self, order, so_line, amount):
inv_obj = self.env['account.invoice']
ir_property_obj = self.env['ir.property']
account_id = False
if self.product_id.id:
account_id = self.product_id.property_account_income_id.id or self.product_id.categ_id.property_account_income_categ_id.id
if not account_id:
inc_acc = ir_property_obj.get('property_account_income_categ_id', 'product.category')
account_id = order.fiscal_position_id.map_account(inc_acc).id if inc_acc else False
if not account_id:
raise UserError(
_('There is no income account defined for this product: "%s". You may have to install a chart of account from Accounting app, settings menu.') %
(self.product_id.name,))
if self.amount <= 0.00:
raise UserError(_('The value of the down payment amount must be positive.'))
context = {'lang': order.partner_id.lang}
if self.advance_payment_method == 'percentage':
amount = order.amount_untaxed * self.amount / 100
name = _("Down payment of %s%%") % (self.amount,)
else:
amount = self.amount
name = _('Down Payment')
del context
taxes = self.product_id.taxes_id.filtered(lambda r: not order.company_id or r.company_id == order.company_id)
if order.fiscal_position_id and taxes:
tax_ids = order.fiscal_position_id.map_tax(taxes).ids
else:
tax_ids = taxes.ids
invoice = inv_obj.create({
'name': order.client_order_ref or order.name,
'origin': order.name,
'type': 'out_invoice',
'reference': False,
'account_id': order.partner_id.property_account_receivable_id.id,
'partner_id': order.partner_invoice_id.id,
'partner_shipping_id': order.partner_shipping_id.id,
'invoice_line_ids': [(0, 0, {
'name': name,
'origin': order.name,
'account_id': account_id,
'price_unit': amount,
'quantity': 1.0,
'discount': 0.0,
'uom_id': self.product_id.uom_id.id,
'product_id': self.product_id.id,
'sale_line_ids': [(6, 0, [so_line.id])],
'invoice_line_tax_ids': [(6, 0, tax_ids)],
'account_analytic_id': order.project_id.id or False,
})],
'currency_id': order.pricelist_id.currency_id.id,
'payment_term_id': order.payment_term_id.id,
'fiscal_position_id': order.fiscal_position_id.id or order.partner_id.property_account_position_id.id,
'team_id': order.team_id.id,
'user_id': order.user_id.id,
'comment': order.note,
})
invoice.compute_taxes()
invoice.message_post_with_view('mail.message_origin_link',
values={'self': invoice, 'origin': order},
subtype_id=self.env.ref('mail.mt_note').id)
return invoice
@api.multi
def create_invoices(self):
folios = self.env['hotel.folio'].browse(self._context.get('active_ids', []))
sale_orders = self.env['sale.order'].browse(folios.mapped('order_id.id'))
if self.advance_payment_method == 'delivered':
sale_orders.action_invoice_create()
elif self.advance_payment_method == 'all':
sale_orders.action_invoice_create(final=True)
else:
# Create deposit product if necessary
if not self.product_id:
vals = self._prepare_deposit_product()
self.product_id = self.env['product.product'].create(vals)
self.env['ir.default'].sudo().set('sale.config.settings', 'deposit_product_id_setting', self.product_id.id)
sale_line_obj = self.env['sale.order.line']
for order in sale_orders:
if self.advance_payment_method == 'percentage':
amount = order.amount_untaxed * self.amount / 100
else:
amount = self.amount
if self.product_id.invoice_policy != 'order':
raise UserError(_('The product used to invoice a down payment should have an invoice policy set to "Ordered quantities". Please update your deposit product to be able to create a deposit invoice.'))
if self.product_id.type != 'service':
raise UserError(_("The product used to invoice a down payment should be of type 'Service'. Please use another product or update this product."))
taxes = self.product_id.taxes_id.filtered(lambda r: not order.company_id or r.company_id == order.company_id)
if order.fiscal_position_id and taxes:
tax_ids = order.fiscal_position_id.map_tax(taxes).ids
else:
tax_ids = taxes.ids
context = {'lang': order.partner_id.lang}
so_line = sale_line_obj.create({
'name': _('Advance: %s') % (time.strftime('%m %Y'),),
'price_unit': amount,
'product_uom_qty': 0.0,
'order_id': order.id,
'discount': 0.0,
'product_uom': self.product_id.uom_id.id,
'product_id': self.product_id.id,
'tax_id': [(6, 0, tax_ids)],
})
del context
self._create_invoice(order, so_line, amount)
if self._context.get('open_invoices', False):
return sale_orders.action_view_invoice()
return {'type': 'ir.actions.act_window_close'}
def _prepare_deposit_product(self):
return {
'name': 'Down payment',
'type': 'service',
'invoice_policy': 'order',
'property_account_income_id': self.deposit_account_id.id,
'taxes_id': [(6, 0, self.deposit_taxes_id.ids)],
}

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_folio_advance_payment_inv" model="ir.ui.view">
<field name="name">Invoice Orders</field>
<field name="model">folio.advance.payment.inv</field>
<field name="arch" type="xml">
<form string="Invoice Sales Order">
<p class="oe_grey">
Invoices will be created in draft so that you can review
them before validation.
</p>
<group>
<field name="count" invisible="[('count','=',1)]" readonly="True"/>
<field name="advance_payment_method" class="oe_inline" widget="radio"
attrs="{'invisible': [('count','&gt;',1)]}"/>
<field name="product_id"
context="{'search_default_services': 1, 'default_type': 'service', 'default_invoice_policy': 'order'}" class="oe_inline"
attrs="{'invisible': 1}"/>
<label for="amount" attrs="{'invisible': [('advance_payment_method', 'not in', ('fixed','percentage'))]}"/>
<div attrs="{'invisible': [('advance_payment_method', 'not in', ('fixed','percentage'))]}">
<field name="amount"
attrs="{'required': [('advance_payment_method', 'in', ('fixed','percentage'))]}" class="oe_inline" widget="monetary"/>
<label string="%%"
attrs="{'invisible': [('advance_payment_method', '!=', 'percentage')]}" class="oe_inline"/>
</div>
<field name="deposit_account_id" class="oe_inline"
attrs="{'invisible': ['|', ('advance_payment_method', 'not in', ('fixed', 'percentage')), ('product_id', '!=', False)]}" groups="account.group_account_manager"/>
<field name="deposit_taxes_id" class="oe_inline" widget="many2many_tags"
domain="[('type_tax_use','=','sale')]"
attrs="{'invisible': ['|', ('advance_payment_method', 'not in', ('fixed', 'percentage')), ('product_id', '!=', False)]}"/>
</group>
<footer>
<button name="create_invoices" string="Create and View Invoices" type="object"
context="{'open_invoices': True}" class="btn-primary"/>
<button name="create_invoices" string="Create Invoices" type="object"
class="btn-primary"/>
<button string="Cancel" class="btn-default" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="action_view_folio_advance_payment_inv" model="ir.actions.act_window">
<field name="name">Invoice Order</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">folio.advance.payment.inv</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="groups_id" eval="[(4,ref('sales_team.group_sale_salesman'))]"/>
</record>
</odoo>

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Alexandre Díaz
# Copyright 2017 Dario Lodeiros
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import models, fields, api
class FolioReportWizard(models.TransientModel):
_name = 'folio.report.wizard'
_rec_name = 'date_start'
date_start = fields.Datetime('Start Date')
date_end = fields.Datetime('End Date')
@api.multi
def print_report(self):
data = {
'ids': self.ids,
'model': 'hotel.folio',
'form': self.read(['date_start', 'date_end'])[0]
}
return self.env.ref('hotel.report_hotel_folio').report_action(self, data=data)

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" ?>
<odoo>
<!--Form view for folio report wizard -->
<record model="ir.ui.view" id="view_hotel_folio_wizard">
<field name="name">folio.report.wizard</field>
<field name="model">folio.report.wizard</field>
<field name="arch" type="xml">
<form string="Folio Report" >
<group col="4">
<field name="date_start" required="1" />
<field name="date_end" required="1" />
</group>
<footer>
<button name="print_report" string="Print Folio" type="object"
class="oe_highlight" />
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<!--Action for folio report wizard -->
<record model="ir.actions.act_window" id="hotel_folio_wizard">
<field name="name">Hotel Folio Report</field>
<field name="res_model">folio.report.wizard</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<!-- <menuitem name="Hotel Folio Report" action="hotel_folio_wizard"
id="wizard_hotel_menu" parent="hotel_report_menu" sequence="31" />-->
</odoo>

View File

@@ -0,0 +1,302 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2017 Solucións Aloxa S.L. <info@aloxa.eu>
# Alexandre Díaz <dev@redneboa.es>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from datetime import datetime, timedelta
from openerp.exceptions import ValidationError
from openerp import models, fields, api
from openerp.tools import (
DEFAULT_SERVER_DATE_FORMAT,
DEFAULT_SERVER_DATETIME_FORMAT)
from odoo.addons.hotel import date_utils
class MassiveChangesWizard(models.TransientModel):
_name = 'hotel.wizard.massive.changes'
# Common fields
section = fields.Selection([
('0', 'Availability'),
('1', 'Restrictions'),
('2', 'Pricelist'),
], string='Section', default='0')
date_start = fields.Datetime('Start Date', required=True)
date_end = fields.Datetime('End Date', required=True)
dmo = fields.Boolean('Monday', default=True)
dtu = fields.Boolean('Tuesday', default=True)
dwe = fields.Boolean('Wednesday', default=True)
dth = fields.Boolean('Thursday', default=True)
dfr = fields.Boolean('Friday', default=True)
dsa = fields.Boolean('Saturday', default=True)
dsu = fields.Boolean('Sunday', default=True)
applied_on = fields.Selection([
('0', 'Global'),
('1', 'Virtual Room'),
], string='Applied On', default='0')
# virtual_room_ids = fields.Many2many('hotel.virtual.room',
# string="Virtual Rooms")
room_type_ids = fields.Many2many('hotel.room.type',
string="Room Types")
# Availability fields
change_avail = fields.Boolean(default=False)
avail = fields.Integer('Avail', default=0)
change_no_ota = fields.Boolean(default=False)
no_ota = fields.Boolean('No OTA', default=False)
# Restriction fields
restriction_id = fields.Many2one('hotel.virtual.room.restriction',
'Restriction Plan')
change_min_stay = fields.Boolean(default=False)
min_stay = fields.Integer("Min. Stay")
change_min_stay_arrival = fields.Boolean(default=False)
min_stay_arrival = fields.Integer("Min. Stay Arrival")
change_max_stay = fields.Boolean(default=False)
max_stay = fields.Integer("Max. Stay")
change_max_stay_arrival = fields.Boolean(default=False)
max_stay_arrival = fields.Integer("Max. Stay Arrival")
change_closed = fields.Boolean(default=False)
closed = fields.Boolean('Closed')
change_closed_departure = fields.Boolean(default=False)
closed_departure = fields.Boolean('Closed Departure')
change_closed_arrival = fields.Boolean(default=False)
closed_arrival = fields.Boolean('Closed Arrival')
# Pricelist fields
pricelist_id = fields.Many2one('product.pricelist', 'Pricelist')
price = fields.Char('Price', help="Can use '+','-' \
or '%'...\nExamples:\n a) +12.3 \
\t> Increase the price in 12.3\n \
b) -1.45% \t> Substract 1.45%\n c) 45 \
\t\t> Sets the price to 45")
@api.onchange('date_start')
def onchange_date_start(self):
self.ensure_one()
self.date_end = self.date_start
@api.multi
def is_valid_date(self, chkdate):
self.ensure_one()
date_start_dt = fields.Datetime.from_string(self.date_start)
date_end_dt = fields.Datetime.from_string(self.date_end)
wday = chkdate.timetuple()[6]
wedays = (self.dmo, self.dtu, self.dwe, self.dth, self.dfr, self.dsa,
self.dsu)
return (chkdate >= self.date_start and chkdate <= self.date_end
and wedays[wday])
@api.model
def _save_prices(self, ndate, vrooms, record):
product_pricelist_item_obj = self.env['product.pricelist.item']
price = 0.0
operation = 'a'
if record.price[0] == '+' or record.price[0] == '-':
if record.price[-1] == '%':
price = float(record.price[1:-1])
operation = (record.price[0] == '+') and 'ap' or 'sp'
else:
price = float(record.price[1:])
operation = (record.price[0] == '+') and 'a' or 's'
else:
if record.price[-1] == '%':
price = float(record.price[:-1])
operation = 'np'
else:
price = float(record.price)
operation = 'n'
domain = [
('pricelist_id', '=', record.pricelist_id.id),
('date_start', '>=', ndate.strftime(
DEFAULT_SERVER_DATE_FORMAT)),
('date_end', '<=', ndate.strftime(
DEFAULT_SERVER_DATE_FORMAT)),
('compute_price', '=', 'fixed'),
('applied_on', '=', '1_product'),
]
product_tmpl_ids = vrooms.mapped(
'product_id.product_tmpl_id')
for vroom in vrooms:
prod_tmpl_id = vroom.product_id.product_tmpl_id
pricelist_item_ids = product_pricelist_item_obj.search(
domain+[('product_tmpl_id', '=', prod_tmpl_id.id)])
if any(pricelist_item_ids):
if operation != 'n':
for pli in pricelist_item_ids:
pli_price = pli.fixed_price
if operation == 'a':
pli.write({
'fixed_price': pli_price + price})
elif operation == 'ap':
pli.write({'fixed_price': pli_price + price * pli_price * 0.01})
elif operation == 's':
pli.write({
'fixed_price': pli_price - price})
elif operation == 'sp':
pli.write({'fixed_price': pli_price - price * pli_price * 0.01})
elif operation == 'np':
pli.write({'fixed_price': price * pli_price * 0.01})
else:
pricelist_item_ids.write({'fixed_price': price})
else:
product_pricelist_item_obj.create({
'pricelist_id': record.pricelist_id.id,
'date_start': ndate.strftime(
DEFAULT_SERVER_DATE_FORMAT),
'date_end': ndate.strftime(
DEFAULT_SERVER_DATE_FORMAT),
'compute_price': 'fixed',
'applied_on': '1_product',
'product_tmpl_id': prod_tmpl_id.id,
'fixed_price': price,
})
@api.model
def _get_restrictions_values(self, ndate, vroom, record):
vals = {}
if record.change_min_stay:
vals.update({'min_stay': record.min_stay})
if record.change_min_stay_arrival:
vals.update({'min_stay_arrival': record.min_stay_arrival})
if record.change_max_stay:
vals.update({'max_stay': record.max_stay})
if record.change_max_stay_arrival:
vals.update({'max_stay_arrival': record.max_stay_arrival})
if record.change_closed:
vals.update({'closed': record.closed})
if record.change_closed_departure:
vals.update({'closed_departure': record.closed_departure})
if record.change_closed_arrival:
vals.update({'closed_arrival': record.closed_arrival})
return vals
@api.model
def _save_restrictions(self, ndate, vrooms, record):
hotel_vroom_re_it_obj = self.env['hotel.virtual.room.restriction.item']
domain = [
('date_start', '>=', ndate.strftime(DEFAULT_SERVER_DATE_FORMAT)),
('date_end', '<=', ndate.strftime(DEFAULT_SERVER_DATE_FORMAT)),
('restriction_id', '=', record.restriction_id.id),
('applied_on', '=', '0_virtual_room'),
]
for vroom in vrooms:
vals = self._get_restrictions_values(ndate, vroom, record)
if not any(vals):
continue
rrest_item_ids = hotel_vroom_re_it_obj.search(
domain+[('virtual_room_id', '=', vroom.id)])
if any(rrest_item_ids):
rrest_item_ids.write(vals)
else:
vals.update({
'date_start': ndate.strftime(DEFAULT_SERVER_DATE_FORMAT),
'date_end': ndate.strftime(DEFAULT_SERVER_DATE_FORMAT),
'restriction_id': record.restriction_id.id,
'virtual_room_id': vroom.id,
'applied_on': '0_virtual_room',
})
hotel_vroom_re_it_obj.create(vals)
@api.model
def _get_availability_values(self, ndate, vroom, record):
hotel_vroom_obj = self.env['hotel.virtual.room']
vals = {}
if record.change_no_ota:
vals.update({'no_ota': record.no_ota})
if record.change_avail:
cavail = len(hotel_vroom_obj.check_availability_virtual_room(
ndate.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
ndate.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
virtual_room_id=vroom.id))
vals.update({
'avail': min(cavail, vroom.total_rooms_count, record.avail),
})
return vals
@api.model
def _save_availability(self, ndate, vrooms, record):
hotel_vroom_obj = self.env['hotel.virtual.room']
hotel_vroom_avail_obj = self.env['hotel.virtual.room.availability']
domain = [('date', '=', ndate.strftime(DEFAULT_SERVER_DATE_FORMAT))]
for vroom in vrooms:
vals = self._get_availability_values(ndate, vroom, record)
if not any(vals):
continue
vrooms_avail = hotel_vroom_avail_obj.search(
domain+[('virtual_room_id', '=', vroom.id)]
)
if any(vrooms_avail):
# Mail module want a singleton
for vr_avail in vrooms_avail:
vr_avail.write(vals)
else:
vals.update({
'date': ndate.strftime(DEFAULT_SERVER_DATE_FORMAT),
'virtual_room_id': vroom.id
})
hotel_vroom_avail_obj.with_context({
'mail_create_nosubscribe': True,
}).create(vals)
@api.multi
def massive_change_close(self):
self._do_massive_change()
return True
@api.multi
def massive_change(self):
self._do_massive_change()
return {
"type": "ir.actions.do_nothing",
}
@api.multi
def _do_massive_change(self):
hotel_vroom_obj = self.env['hotel.virtual.room']
for record in self:
date_start_dt = date_utils.get_datetime(record.date_start,
hours=False)
# Use min '1' for same date
diff_days = date_utils.date_diff(record.date_start,
record.date_end,
hours=False) + 1
wedays = (record.dmo, record.dtu, record.dwe, record.dth,
record.dfr, record.dsa, record.dsu)
vrooms = record.applied_on == '1' and record.room_type_id \
or hotel_vroom_obj.search([])
for i in range(0, diff_days):
ndate = date_start_dt + timedelta(days=i)
if not wedays[ndate.timetuple()[6]]:
continue
if record.section == '0':
self._save_availability(ndate, vrooms, record)
elif record.section == '1':
self._save_restrictions(ndate, vrooms, record)
elif record.section == '2':
self._save_prices(ndate, vrooms, record)
return True

View File

@@ -0,0 +1,113 @@
<?xml version="1.0" ?>
<odoo>
<record model="ir.ui.view" id="view_hotel_massive_changes_wizard">
<field name="name">hotel.wizard.massive.changes</field>
<field name="model">hotel.wizard.massive.changes</field>
<field name="arch" type="xml">
<form string="Massive Changes" >
<!-- Common Fields -->
<group>
<field name="section" required="1" />
<field name="applied_on" required="1" />
<!-- <field name="virtual_room_ids" widget="many2many_tags" attrs="{'invisible':[('applied_on', '!=', '1')], 'required':[('applied_on', '=', '1')]}" /> -->
<field name="room_type_ids" widget="many2many_tags" attrs="{'invisible':[('applied_on', '!=', '1')], 'required':[('applied_on', '=', '1')]}" />
</group>
<group colspan="8" col="8">
<field name="date_start" required="1" colspan="4"/>
<field name="date_end" required="1" colspan="4" />
<field name="dmo" colspan="1" />
<field name="dtu" colspan="1" />
<field name="dwe" colspan="1" />
<field name="dth" colspan="1" />
<field name="dfr" colspan="1" />
<field name="dsa" colspan="1" />
<field name="dsu" colspan="1" />
</group>
<!-- Availability Fields -->
<group col="3" colspan="3" attrs="{'invisible':[('section', '!=', '0')]}">
<table class="oe_form_group">
<thead>
<th width="12%"></th>
<th></th>
</thead>
<tbody>
<tr class="oe_form_group_row">
<td class="oe_form_group_cell oe_form_group_cell_label"><field name="change_avail" /> <strong> Avail</strong></td>
<td class="oe_form_group_cell" colspan="3"><field name="avail" attrs="{'readonly':[('change_avail', '=', False)]}" /></td>
</tr>
<tr class="oe_form_group_row">
<td class="oe_form_group_cell oe_form_group_cell_label"><field name="change_no_ota" /> <strong> No OTA</strong></td>
<td class="oe_form_group_cell"><field name="no_ota" attrs="{'readonly':[('change_no_ota', '=', False)]}" /></td>
</tr>
</tbody>
</table>
</group>
<!-- Restricion Fields -->
<group col="4" colspan="4" attrs="{'invisible':[('section', '!=', '1')]}">
<field name="restriction_id" colspan="4" attrs="{'required':[('section', '=', '1')]}" />
<table class="oe_form_group" colspan="4">
<thead>
<th width="20%"></th>
<th></th>
</thead>
<tbody>
<tr class="oe_form_group_row">
<td class="oe_form_group_cell oe_form_group_cell_label"><field name="change_min_stay" /> <strong> Stay Min.</strong></td>
<td class="oe_form_group_cell" colspan="3"><field name="min_stay" attrs="{'readonly':[('change_min_stay', '=', False)]}" /></td>
</tr>
<tr class="oe_form_group_row">
<td class="oe_form_group_cell oe_form_group_cell_label"><field name="change_max_stay" /> <strong> Stay Max.</strong></td>
<td class="oe_form_group_cell"><field name="max_stay" attrs="{'readonly':[('change_max_stay', '=', False)]}" /></td>
</tr>
<tr class="oe_form_group_row">
<td class="oe_form_group_cell oe_form_group_cell_label"><field name="change_min_stay_arrival" /> <strong> Stay Min. Arrival</strong></td>
<td class="oe_form_group_cell"><field name="min_stay_arrival" attrs="{'readonly':[('change_min_stay_arrival', '=', False)]}" /></td>
</tr>
<tr class="oe_form_group_row">
<td class="oe_form_group_cell oe_form_group_cell_label"><field name="change_max_stay_arrival" /> <strong> Stay Max. Arrival</strong></td>
<td class="oe_form_group_cell"><field name="max_stay_arrival" attrs="{'readonly':[('change_max_stay_arrival', '=', False)]}" /></td>
</tr>
<tr class="oe_form_group_row">
<td class="oe_form_group_cell oe_form_group_cell_label"><field name="change_closed" /> <strong> Closed</strong></td>
<td class="oe_form_group_cell"><field name="closed" attrs="{'readonly':[('change_closed', '=', False)]}" /></td>
</tr>
<tr class="oe_form_group_row">
<td class="oe_form_group_cell oe_form_group_cell_label"><field name="change_closed_departure" /> <strong> Closed Departure</strong></td>
<td class="oe_form_group_cell"><field name="closed_departure" attrs="{'readonly':[('change_closed_departure', '=', False)]}" /></td>
</tr>
<tr class="oe_form_group_row">
<td class="oe_form_group_cell oe_form_group_cell_label"><field name="change_closed_arrival" /> <strong> Closed Arrival</strong></td>
<td class="oe_form_group_cell"><field name="closed_arrival" attrs="{'readonly':[('change_closed_arrival', '=', False)]}" /></td>
</tr>
</tbody>
</table>
</group>
<!-- Priclist Fields -->
<group attrs="{'invisible':[('section', '!=', '2')]}">
<field name="pricelist_id" attrs="{'required':[('section', '=', '2')]}"/>
<field name="price" attrs="{'required':[('section', '=', '2')]}"/>
</group>
<footer>
<button name="massive_change" string="Massive Change" type="object"
class="oe_highlight" />
<button name="massive_change_close" string="Massive Change &amp; Close" type="object"
class="oe_highlight" />
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="action_hotel_massive_change" model="ir.actions.act_window">
<field name="name">Hotel Massive Change</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">hotel.wizard.massive.changes</field>
<field name="view_id" ref="view_hotel_massive_changes_wizard"/>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>

View File

@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2017 Solucións Aloxa S.L. <info@aloxa.eu>
# Alexandre Díaz <dev@redneboa.es>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields, api
class MassivePriceChangeWizard(models.TransientModel):
_name = 'hotel.wizard.massive.price.reservation.days'
new_price = fields.Float('New Price', default=1, min=1)
@api.multi
def massive_price_change_days(self):
self.ensure_one()
hotel_reservation_obj = self.env['hotel.reservation']
reservation_id = hotel_reservation_obj.browse(
self.env.context.get('active_id'))
if not reservation_id:
return False
cmds = []
for rline in reservation_id.reservation_lines:
cmds.append((
1,
rline.id,
{
'price': self.new_price
}
))
reservation_id.write({
'reservation_lines': cmds
})
# FIXME: For some reason need force reservation price calcs
reservation_id._computed_amount_reservation()
# FIXME: Workaround for dispatch updated price
reservation_id.folio_id.write({
'room_lines': [
(
1,
reservation_id.id, {
'reservation_lines': cmds
}
)
]
})
return True

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" ?>
<odoo>
<record model="ir.ui.view" id="view_hotel_massive_price_change_wizard">
<field name="name">hotel.wizard.massive.price.reservation.days</field>
<field name="model">hotel.wizard.massive.price.reservation.days</field>
<field name="arch" type="xml">
<form string="Massive Price Change" >
<group>
<field name="new_price" required="1" />
</group>
<footer>
<button name="massive_price_change_days" string="Massive Change" type="object"
class="oe_highlight" />
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="action_hotel_massive_price_change_reservation_days" model="ir.actions.act_window">
<field name="name">Massive Price Change</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">hotel.wizard.massive.price.reservation.days</field>
<field name="view_id" ref="view_hotel_massive_price_change_wizard"/>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>

View File

@@ -0,0 +1,102 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2017 Solucións Aloxa S.L. <info@aloxa.eu>
# Alexandre Díaz <dev@redneboa.es>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import logging
from datetime import datetime, timedelta
from openerp.exceptions import ValidationError
from openerp import models, fields, api, _
from openerp.tools import (
DEFAULT_SERVER_DATETIME_FORMAT,
DEFAULT_SERVER_DATE_FORMAT)
from odoo.addons.hotel import date_utils
_logger = logging.getLogger(__name__)
class SplitReservationWizard(models.TransientModel):
_name = 'hotel.wizard.split.reservation'
nights = fields.Integer('Nights', default=1, min=1)
@api.multi
def split_reservation(self):
reservation_id = self.env['hotel.reservation'].browse(
self.env.context.get('active_id'))
if reservation_id:
date_start_dt = date_utils.get_datetime(reservation_id.checkin)
date_end_dt = date_utils.get_datetime(reservation_id.checkout)
date_diff = date_utils.date_diff(date_start_dt, date_end_dt,
hours=False)
for record in self:
new_start_date_dt = date_start_dt + \
timedelta(days=date_diff-record.nights)
if record.nights >= date_diff or record.nights < 1:
raise ValidationError(_("Invalid Nights! Max is \
'%d'") % (date_diff-1))
vals = reservation_id.generate_copy_values(
new_start_date_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
date_end_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
)
# Days Price
reservation_lines = [[], []]
tprice = [0.0, 0.0]
div_dt = date_utils.dt_no_hours(new_start_date_dt)
for rline in reservation_id.reservation_lines:
rline_dt = date_utils.get_datetime(rline.date, hours=False)
if rline_dt >= div_dt:
reservation_lines[1].append((0, False, {
'date': rline.date,
'price': rline.price
}))
tprice[1] += rline.price
reservation_lines[0].append((2, rline.id, False))
else:
tprice[0] += rline.price
reservation_id.write({
'checkout': new_start_date_dt.strftime(
DEFAULT_SERVER_DATETIME_FORMAT),
'price_unit': tprice[0],
'splitted': True,
})
reservation_id.reservation_lines = reservation_lines[0]
parent_res = reservation_id.parent_reservation or \
reservation_id
vals.update({
'splitted': True,
'price_unit': tprice[1],
'parent_reservation': parent_res.id,
'virtual_room_id': parent_res.virtual_room_id.id,
'discount': parent_res.discount,
})
reservation_copy = self.env['hotel.reservation'].create(vals)
if not reservation_copy:
raise ValidationError(_("Unexpected error copying record. \
Can't split reservation!"))
reservation_copy.reservation_lines = reservation_lines[1]
# return {
# 'type': 'ir.actions.act_window',
# 'res_model': 'hotel.folio',
# 'views': [[False, "form"]],
# 'target': 'new',
# 'res_id': reservation_id.folio_id.id,
# }
return True

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" ?>
<odoo>
<record model="ir.ui.view" id="view_hotel_split_reservation_wizard">
<field name="name">hotel.wizard.split.reservation</field>
<field name="model">hotel.wizard.split.reservation</field>
<field name="arch" type="xml">
<form string="Split Reservation" >
<!-- Common Fields -->
<group>
<field name="nights" required="1" />
</group>
<footer>
<button name="split_reservation" string="Split" type="object"
class="oe_highlight" />
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="action_hotel_split_reservation" model="ir.actions.act_window">
<field name="name">Split Reservation</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">hotel.wizard.split.reservation</field>
<field name="view_id" ref="view_hotel_split_reservation_wizard"/>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>