[WIP] Invoice WorkFlow

This commit is contained in:
Dario Lodeiros
2019-01-10 15:21:10 +01:00
parent 8ba2051496
commit b2c8305b26
11 changed files with 666 additions and 727 deletions

View File

@@ -33,9 +33,57 @@ class HotelFolio(models.Model):
# @api.depends('product_id.invoice_policy', 'order_id.state')
def _compute_qty_delivered_updateable(self):
pass
# @api.depends('state', 'order_line.invoice_status')
@api.depends('state', 'room_lines.invoice_status', 'service_ids.invoice_status')
def _get_invoiced(self):
pass
"""
Compute the invoice status of a Folio. Possible statuses:
- no: if the Folio is not in status 'sale' or 'done', we consider that there is nothing to
invoice. This is also the default value if the conditions of no other status is met.
- to invoice: if any Folio line is 'to invoice', the whole Folio is 'to invoice'
- invoiced: if all Folio lines are invoiced, the Folio is invoiced.
- upselling: if all Folio lines are invoiced or upselling, the status is upselling.
The invoice_ids are obtained thanks to the invoice lines of the Folio lines, and we also search
for possible refunds created directly from existing invoices. This is necessary since such a
refund is not directly linked to the Folio.
"""
for folio in self:
invoice_ids = folio.room_lines.mapped('invoice_line_ids').mapped('invoice_id').filtered(lambda r: r.type in ['out_invoice', 'out_refund'])
invoice_ids |= folio.service_ids.mapped('invoice_line_ids').mapped('invoice_id').filtered(lambda r: r.type in ['out_invoice', 'out_refund'])
# Search for invoices which have been 'cancelled' (filter_refund = 'modify' in
# 'account.invoice.refund')
# use like as origin may contains multiple references (e.g. 'SO01, SO02')
refunds = invoice_ids.search([('origin', 'like', folio.name), ('company_id', '=', folio.company_id.id)]).filtered(lambda r: r.type in ['out_invoice', 'out_refund'])
invoice_ids |= refunds.filtered(lambda r: folio.id in r.folio_ids.ids)
# Search for refunds as well
refund_ids = self.env['account.invoice'].browse()
if invoice_ids:
for inv in invoice_ids:
refund_ids += refund_ids.search([('type', '=', 'out_refund'), ('origin', '=', inv.number), ('origin', '!=', False), ('journal_id', '=', inv.journal_id.id)])
# Ignore the status of the deposit product
deposit_product_id = self.env['sale.advance.payment.inv']._default_product_id()
#~ line_invoice_status = [line.invoice_status for line in order.order_line if line.product_id != deposit_product_id]
#~ TODO: REVIEW INVOICE_STATUS
#~ if folio.state not in ('confirm', 'done'):
#~ invoice_status = 'no'
#~ elif any(invoice_status == 'to invoice' for invoice_status in line_invoice_status):
#~ invoice_status = 'to invoice'
#~ elif all(invoice_status == 'invoiced' for invoice_status in line_invoice_status):
#~ invoice_status = 'invoiced'
#~ elif all(invoice_status in ['invoiced', 'upselling'] for invoice_status in line_invoice_status):
#~ invoice_status = 'upselling'
#~ else:
#~ invoice_status = 'no'
folio.update({
'invoice_count': len(set(invoice_ids.ids + refund_ids.ids)),
'invoice_ids': invoice_ids.ids + refund_ids.ids,
#~ 'invoice_status': invoice_status
})
# @api.depends('state', 'product_uom_qty', 'qty_delivered', 'qty_to_invoice', 'qty_invoiced')
def _compute_invoice_status(self):
pass
@@ -68,8 +116,8 @@ class HotelFolio(models.Model):
help="Hotel services detail provide to "
"customer and it will include in "
"main Invoice.")
company_id = fields.Many2one('res.company', 'Company')
company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env['res.company']._company_default_get('hotel.folio'))
analytic_account_id = fields.Many2one('account.analytic.account', 'Analytic Account', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="The analytic account related to a folio.", copy=False)
currency_id = fields.Many2one('res.currency', related='pricelist_id.currency_id',
string='Currency', readonly=True, required=True)
@@ -148,8 +196,7 @@ class HotelFolio(models.Model):
compute='_compute_checkin_partner_count')
#Invoice Fields-----------------------------------------------------
hotel_invoice_id = fields.Many2one('account.invoice', 'Invoice')
num_invoices = fields.Integer(compute='_compute_num_invoices')
invoice_count = fields.Integer(compute='_get_invoiced')
invoice_ids = fields.Many2many('account.invoice', string='Invoices',
compute='_get_invoiced', readonly=True, copy=False)
invoice_status = fields.Selection([('upselling', 'Upselling Opportunity'),
@@ -160,10 +207,8 @@ class HotelFolio(models.Model):
compute='_compute_invoice_status',
store=True, readonly=True, default='no')
partner_invoice_id = fields.Many2one('res.partner',
string='Invoice Address',
readonly=True, required=True,
states={'draft': [('readonly', False)],
'sent': [('readonly', False)]},
string='Invoice Address', required=True,
states={'done': [('readonly', True)]},
help="Invoice address for current sales order.")
fiscal_position_id = fields.Many2one('account.fiscal.position', oldname='fiscal_position', string='Fiscal Position')
@@ -212,12 +257,6 @@ class HotelFolio(models.Model):
for record in self:
record.rooms_char = ', '.join(record.mapped('room_lines.room_id.name'))
@api.multi
def _compute_num_invoices(self):
pass
# for fol in self:
# fol.num_invoices = len(self.mapped('invoice_ids.id'))
# @api.depends('order_line.price_total', 'payment_ids', 'return_ids')
@api.multi
def compute_amount(self):
@@ -380,8 +419,9 @@ class HotelFolio(models.Model):
"""
Update the following fields when the partner is changed:
- Pricelist
- Payment terms
- Invoice address
- user_id
- Delivery address
"""
if not self.partner_id:
self.update({
@@ -390,13 +430,18 @@ class HotelFolio(models.Model):
'fiscal_position_id': False,
})
return
addr = self.partner_id.address_get(['invoice'])
pricelist = self.partner_id.property_product_pricelist and \
self.partner_id.property_product_pricelist.id or \
self.env['ir.default'].sudo().get('res.config.settings', 'default_pricelist_id')
values = {'user_id': self.partner_id.user_id.id or self.env.uid,
'pricelist_id': pricelist
}
values = {
'pricelist_id': pricelist,
'payment_term_id': self.partner_id.property_payment_term_id and self.partner_id.property_payment_term_id.id or False,
'partner_invoice_id': addr['invoice'],
'user_id': self.partner_id.user_id.id or self.env.uid
}
if self.env['ir.config_parameter'].sudo().get_param('sale.use_sale_note') and \
self.env.user.company_id.sale_note:
values['note'] = self.with_context(
@@ -424,127 +469,6 @@ class HotelFolio(models.Model):
else:
return 'normal'
@api.multi
def action_invoice_create(self, grouped=False, states=None):
'''
@param self: object pointer
'''
pass
# if states is None:
# states = ['confirmed', 'done']
# order_ids = [folio.order_id.id for folio in self]
# sale_obj = self.env['sale.order'].browse(order_ids)
# invoice_id = (sale_obj.action_invoice_create(grouped=False,
# states=['confirmed',
# 'done']))
# for line in self:
# values = {'invoiced': True,
# 'state': 'progress' if grouped else 'progress',
# 'hotel_invoice_id': invoice_id
# }
# line.write(values)
# return invoice_id
@api.multi
def advance_invoice(self):
pass
@api.multi
def _prepare_invoice(self):
"""
Prepare the dict of values to create the new invoice for a sales order. This method may be
overridden to implement custom invoice generation (making sure to call super() to establish
a clean extension chain).
"""
self.ensure_one()
journal_id = self.env['account.invoice'].default_get(['journal_id'])['journal_id']
if not journal_id:
raise UserError(_('Please define an accounting sales journal for this company.'))
import wdb; wdb.set_trace()
invoice_vals = {
'name': self.client_order_ref or '',
'origin': self.name,
'type': 'out_invoice',
'account_id': self.partner_invoice_id.property_account_receivable_id.id,
'partner_id': self.partner_invoice_id.id,
'partner_shipping_id': self.partner_id.id,
'journal_id': journal_id,
'currency_id': self.pricelist_id.currency_id.id,
'comment': self.note,
'payment_term_id': self.payment_term_id.id,
'fiscal_position_id': self.fiscal_position_id.id or self.partner_invoice_id.property_account_position_id.id,
'company_id': self.company_id.id,
'user_id': self.user_id and self.user_id.id,
'team_id': self.team_id.id
}
return invoice_vals
@api.multi
def action_invoice_create(self, grouped=False):
"""
Create the invoice associated to the Folio.
:param grouped: if True, invoices are grouped by Folio id. If False, invoices are grouped by
(partner_invoice_id, currency)
:returns: list of created invoices
"""
inv_obj = self.env['account.invoice']
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
invoices = {}
references = {}
invoices_origin = {}
invoices_name = {}
for folio in self:
group_key = folio.id if grouped else (folio.partner_invoice_id.id, folio.currency_id.id)
for line in folio.room_lines.sorted(key=lambda l: l.qty_to_invoice < 0):
if float_is_zero(line.qty_to_invoice, precision_digits=precision):
continue
if group_key not in invoices:
inv_data = folio._prepare_invoice()
invoice = inv_obj.create(inv_data)
references[invoice] = folio
invoices[group_key] = invoice
invoices_origin[group_key] = [invoice.origin]
invoices_name[group_key] = [invoice.name]
elif group_key in invoices:
if folio.name not in invoices_origin[group_key]:
invoices_origin[group_key].append(folio.name)
if folio.client_order_ref and folio.client_order_ref not in invoices_name[group_key]:
invoices_name[group_key].append(folio.client_order_ref)
if line.qty_to_invoice > 0:
line.invoice_line_create(invoices[group_key].id, line.nights)
if references.get(invoices.get(group_key)):
if folio not in references[invoices[group_key]]:
references[invoices[group_key]] |= folio
for group_key in invoices:
invoices[group_key].write({'name': ', '.join(invoices_name[group_key]),
'origin': ', '.join(invoices_origin[group_key])})
if not invoices:
raise UserError(_('There is no invoiceable line.'))
for invoice in invoices.values():
if not invoice.invoice_line_ids:
raise UserError(_('There is no invoiceable line.'))
# If invoice is negative, do a refund invoice instead
if invoice.amount_untaxed < 0:
invoice.type = 'out_refund'
for line in invoice.invoice_line_ids:
line.quantity = -line.quantity
# Use additional field helper function (for account extensions)
for line in invoice.invoice_line_ids:
line._set_additional_fields(invoice)
# Necessary to force computation of taxes. In account_invoice, they are triggered
# by onchanges, which are not triggered when doing a create.
invoice.compute_taxes()
invoice.message_post_with_view('mail.message_origin_link',
values={'self': invoice, 'origin': references[invoice]},
subtype_id=self.env.ref('mail.mt_note').id)
return [inv.id for inv in invoices.values()]
'''
WORKFLOW STATE
'''

View File

@@ -77,6 +77,36 @@ class HotelReservation(models.Model):
return folio.room_lines[0].departure_hour
else:
return default_departure_hour
@api.depends('state', 'qty_to_invoice', 'qty_invoiced')
def _compute_invoice_status(self):
"""
Compute the invoice status of a Reservation. Possible statuses:
- no: if the SO is not in status 'sale' or 'done', we consider that there is nothing to
invoice. This is also hte default value if the conditions of no other status is met.
- to invoice: we refer to the quantity to invoice of the line. Refer to method
`_get_to_invoice_qty()` for more information on how this quantity is calculated.
- upselling: this is possible only for a product invoiced on ordered quantities for which
we delivered more than expected. The could arise if, for example, a project took more
time than expected but we decided not to invoice the extra cost to the client. This
occurs onyl in state 'sale', so that when a SO is set to done, the upselling opportunity
is removed from the list.
- invoiced: the quantity invoiced is larger or equal to the quantity ordered.
"""
pass
#~ precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
#~ for line in self:
#~ if line.state not in ('confirm', 'done'):
#~ line.invoice_status = 'no'
#~ elif not float_is_zero(line.qty_to_invoice, precision_digits=precision):
#~ line.invoice_status = 'to invoice'
#~ elif line.state == 'sale' and line.product_id.invoice_policy == 'order' and\
#~ float_compare(line.qty_delivered, line.product_uom_qty, precision_digits=precision) == 1:
#~ line.invoice_status = 'upselling'
#~ elif float_compare(line.qty_invoiced, line.product_uom_qty, precision_digits=precision) >= 0:
#~ line.invoice_status = 'invoiced'
#~ else:
#~ line.invoice_status = 'no'
@api.model
@@ -170,7 +200,7 @@ class HotelReservation(models.Model):
partner_id = fields.Many2one(related='folio_id.partner_id')
closure_reason_id = fields.Many2one(related='folio_id.closure_reason_id')
company_id = fields.Many2one('res.company', 'Company')
company_id = fields.Many2one(related='folio_id.company_id', string='Company', store=True, readonly=True)
reservation_line_ids = fields.One2many('hotel.reservation.line',
'reservation_id',
readonly=True, required=True,
@@ -237,13 +267,13 @@ class HotelReservation(models.Model):
currency_id = fields.Many2one('res.currency',
related='pricelist_id.currency_id',
string='Currency', readonly=True, required=True)
# invoice_status = fields.Selection([
# ('upselling', 'Upselling Opportunity'),
# ('invoiced', 'Fully Invoiced'),
# ('to invoice', 'To Invoice'),
# ('no', 'Nothing to Invoice')
# ], string='Invoice Status', compute='_compute_invoice_status', store=True, readonly=True, default='no')
tax_id = fields.Many2many('account.tax',
invoice_status = fields.Selection([
('upselling', 'Upselling Opportunity'),
('invoiced', 'Fully Invoiced'),
('to invoice', 'To Invoice'),
('no', 'Nothing to Invoice')
], string='Invoice Status', compute='_compute_invoice_status', store=True, readonly=True, default='no')
tax_ids = fields.Many2many('account.tax',
string='Taxes',
domain=['|', ('active', '=', False), ('active', '=', True)])
qty_to_invoice = fields.Float(
@@ -252,7 +282,7 @@ class HotelReservation(models.Model):
qty_invoiced = fields.Float(
compute='_get_invoice_qty', string='Invoiced', store=True, readonly=True,
digits=dp.get_precision('Product Unit of Measure'))
invoice_lines = fields.Many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_id', 'invoice_line_id', string='Invoice Lines', copy=False)
invoice_line_ids = fields.Many2many('account.invoice.line', 'reservation_line_invoice_rel', 'reservation_id', 'invoice_line_id', string='Invoice Lines', copy=False)
# qty_delivered = fields.Float(string='Delivered', copy=False, digits=dp.get_precision('Product Unit of Measure'), default=0.0)
# qty_delivered_updateable = fields.Boolean(compute='_compute_qty_delivered_updateable', string='Can Edit Delivered', readonly=True, default=True)
price_subtotal = fields.Monetary(string='Subtotal',
@@ -765,7 +795,7 @@ class HotelReservation(models.Model):
return True
return False
@api.depends('reservation_line_ids', 'reservation_line_ids.discount', 'tax_id')
@api.depends('reservation_line_ids', 'reservation_line_ids.discount', 'tax_ids')
def _compute_amount_reservation(self):
"""
Compute the amounts of the reservation.
@@ -775,7 +805,7 @@ class HotelReservation(models.Model):
if amount_room > 0:
product = record.room_type_id.product_id
price = amount_room * (1 - (record.discount or 0.0) * 0.01)
taxes = record.tax_id.compute_all(price, record.currency_id, 1, product=product)
taxes = record.tax_ids.compute_all(price, record.currency_id, 1, product=product)
record.update({
'price_tax': sum(t.get('amount', 0.0) for t in taxes.get('taxes', [])),
'price_total': taxes['total_included'],
@@ -809,7 +839,7 @@ class HotelReservation(models.Model):
pricelist=pricelist_id,
uom=product.uom_id.id)
line_price = self.env['account.tax']._fix_tax_included_price_company(
product.price, product.taxes_id, self.tax_id, self.company_id)
product.price, product.taxes_id, self.tax_ids, self.company_id)
if old_line:
cmds.append((1, old_line.id, {
'price': line_price
@@ -1131,22 +1161,24 @@ class HotelReservation(models.Model):
"""
INVOICING PROCESS
"""
@api.depends('qty_invoiced', 'nights', 'folio_id.state')
def _get_to_invoice_qty(self):
"""
Compute the quantity to invoice. If the invoice policy is order, the quantity to invoice is
calculated from the ordered quantity. Otherwise, the quantity delivered is used.
"""
for line in self:
if line.folio_id.state in ['confirm', 'done']:
if line.room_type_id.product_id.invoice_policy == 'order':
line.qty_to_invoice = line.nights - line.qty_invoiced
else:
line.qty_to_invoice = line.qty_delivered - line.qty_invoiced
else:
line.qty_to_invoice = 0
pass
#~ for line in self:
#~ if line.folio_id.state in ['confirm', 'done']:
#~ if line.room_type_id.product_id.invoice_policy == 'order':
#~ line.qty_to_invoice = line.nights - line.qty_invoiced
#~ else:
#~ line.qty_to_invoice = line.qty_delivered - line.qty_invoiced
#~ else:
#~ line.qty_to_invoice = 0
@api.depends('invoice_lines.invoice_id.state', 'invoice_lines.quantity')
@api.depends('invoice_line_ids.invoice_id.state', 'invoice_line_ids.quantity')
def _get_invoice_qty(self):
"""
Compute the quantity invoiced. If case of a refund, the quantity invoiced is decreased. Note
@@ -1154,64 +1186,13 @@ class HotelReservation(models.Model):
a refund made would automatically decrease the invoiced quantity, then there is a risk of reinvoicing
it automatically, which may not be wanted at all. That's why the refund has to be created from the SO
"""
for line in self:
qty_invoiced = 0.0
for invoice_line in line.invoice_lines:
if invoice_line.invoice_id.state != 'cancel':
if invoice_line.invoice_id.type == 'out_invoice':
qty_invoiced += invoice_line.uom_id._compute_quantity(invoice_line.quantity, line.product_uom)
elif invoice_line.invoice_id.type == 'out_refund':
qty_invoiced -= invoice_line.uom_id._compute_quantity(invoice_line.quantity, line.product_uom)
line.qty_invoiced = qty_invoiced
@api.multi
def _prepare_invoice_line(self, qty):
"""
Prepare the dict of values to create the new invoice line for a reservation.
:param qty: float quantity to invoice
"""
self.ensure_one()
res = {}
product = self.env['product.product'].browse(self.room_type_id.product_id.id)
account = product.property_account_income_id or product.categ_id.property_account_income_categ_id
if not account:
raise UserError(_('Please define income account for this product: "%s" (id:%d) - or for its category: "%s".') %
(product.name, product.id, product.categ_id.name))
fpos = self.folio_id.fiscal_position_id or self.folio_id.partner_id.property_account_position_id
if fpos:
account = fpos.map_account(account)
res = {
'name': self.name,
'sequence': self.sequence,
'origin': self.folio_id.name,
'account_id': account.id,
'price_unit': self.price_unit,
'quantity': qty,
'discount': self.discount,
'uom_id': self.product_uom.id,
'product_id': product.id or False,
'invoice_line_tax_ids': [(6, 0, self.tax_id.ids)],
'account_analytic_id': self.folio_id.analytic_account_id.id,
'analytic_tag_ids': [(6, 0, self.analytic_tag_ids.ids)],
}
return res
@api.multi
def invoice_line_create(self, invoice_id, qty):
""" Create an invoice line. The quantity to invoice can be positive (invoice) or negative (refund).
:param invoice_id: integer
:param qty: float quantity to invoice
:returns recordset of account.invoice.line created
"""
invoice_lines = self.env['account.invoice.line']
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
for line in self:
if not float_is_zero(qty, precision_digits=precision):
vals = line._prepare_invoice_line(qty=qty)
vals.update({'invoice_id': invoice_id, 'reservation_ids': [(6, 0, [line.id])]})
invoice_lines |= self.env['account.invoice.line'].create(vals)
return invoice_lines
pass
#~ for line in self:
#~ qty_invoiced = 0.0
#~ for invoice_line in line.invoice_line_ids:
#~ if invoice_line.invoice_id.state != 'cancel':
#~ if invoice_line.invoice_id.type == 'out_invoice':
#~ qty_invoiced += invoice_line.uom_id._compute_quantity(invoice_line.quantity, line.product_uom)
#~ elif invoice_line.invoice_id.type == 'out_refund':
#~ qty_invoiced -= invoice_line.uom_id._compute_quantity(invoice_line.quantity, line.product_uom)
#~ line.qty_invoiced = qty_invoiced

View File

@@ -44,7 +44,73 @@ class HotelService(models.Model):
(ids)], limit=1)
return False
name = fields.Char('Service description')
@api.depends('qty_invoiced', 'product_qty', 'folio_id.state')
def _get_to_invoice_qty(self):
"""
Compute the quantity to invoice. If the invoice policy is order, the quantity to invoice is
calculated from the ordered quantity. Otherwise, the quantity delivered is used.
"""
pass
#~ for line in self:
#~ if line.folio_id.state in ['confirm', 'done']:
#~ if line.room_type_id.product_id.invoice_policy == 'order':
#~ line.qty_to_invoice = line.nights - line.qty_invoiced
#~ else:
#~ line.qty_to_invoice = line.qty_delivered - line.qty_invoiced
#~ else:
#~ line.qty_to_invoice = 0
@api.depends('invoice_line_ids.invoice_id.state', 'invoice_line_ids.quantity')
def _get_invoice_qty(self):
"""
Compute the quantity invoiced. If case of a refund, the quantity invoiced is decreased. Note
that this is the case only if the refund is generated from the SO and that is intentional: if
a refund made would automatically decrease the invoiced quantity, then there is a risk of reinvoicing
it automatically, which may not be wanted at all. That's why the refund has to be created from the SO
"""
pass
#~ for line in self:
#~ qty_invoiced = 0.0
#~ for invoice_line in line.invoice_line_ids:
#~ if invoice_line.invoice_id.state != 'cancel':
#~ if invoice_line.invoice_id.type == 'out_invoice':
#~ qty_invoiced += invoice_line.uom_id._compute_quantity(invoice_line.quantity, line.product_uom)
#~ elif invoice_line.invoice_id.type == 'out_refund':
#~ qty_invoiced -= invoice_line.uom_id._compute_quantity(invoice_line.quantity, line.product_uom)
#~ line.qty_invoiced = qty_invoiced
@api.depends('product_qty', 'qty_to_invoice', 'qty_invoiced')
def _compute_invoice_status(self):
"""
Compute the invoice status of a SO line. Possible statuses:
- no: if the SO is not in status 'sale' or 'done', we consider that there is nothing to
invoice. This is also hte default value if the conditions of no other status is met.
- to invoice: we refer to the quantity to invoice of the line. Refer to method
`_get_to_invoice_qty()` for more information on how this quantity is calculated.
- upselling: this is possible only for a product invoiced on ordered quantities for which
we delivered more than expected. The could arise if, for example, a project took more
time than expected but we decided not to invoice the extra cost to the client. This
occurs onyl in state 'sale', so that when a SO is set to done, the upselling opportunity
is removed from the list.
- invoiced: the quantity invoiced is larger or equal to the quantity ordered.
"""
pass
#~ precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
#~ for line in self:
#~ if line.state not in ('sale', 'done'):
#~ line.invoice_status = 'no'
#~ elif not float_is_zero(line.qty_to_invoice, precision_digits=precision):
#~ line.invoice_status = 'to invoice'
#~ elif line.state == 'sale' and line.product_id.invoice_policy == 'order' and\
#~ float_compare(line.qty_delivered, line.product_uom_qty, precision_digits=precision) == 1:
#~ line.invoice_status = 'upselling'
#~ elif float_compare(line.qty_invoiced, line.product_uom_qty, precision_digits=precision) >= 0:
#~ line.invoice_status = 'invoiced'
#~ else:
#~ line.invoice_status = 'no'
name = fields.Char('Service description', required=True)
sequence = fields.Integer(string='Sequence', default=10)
product_id = fields.Many2one('product.product', 'Service', required=True)
folio_id = fields.Many2one('hotel.folio', 'Folio', ondelete='cascade')
ser_room_line = fields.Many2one('hotel.reservation', 'Room',
@@ -57,6 +123,12 @@ class HotelService(models.Model):
# Non-stored related field to allow portal user to see the image of the product he has ordered
product_image = fields.Binary('Product Image', related="product_id.image", store=False)
company_id = fields.Many2one(related='folio_id.company_id', string='Company', store=True, readonly=True)
invoice_status = fields.Selection([
('upselling', 'Upselling Opportunity'),
('invoiced', 'Fully Invoiced'),
('to invoice', 'To Invoice'),
('no', 'Nothing to Invoice')
], string='Invoice Status', compute='_compute_invoice_status', store=True, readonly=True, default='no')
channel_type = fields.Selection([
('door', 'Door'),
('mail', 'Mail'),
@@ -67,6 +139,14 @@ class HotelService(models.Model):
tax_ids = fields.Many2many('account.tax', string='Taxes', domain=['|', ('active', '=', False), ('active', '=', True)])
discount = fields.Float(string='Discount (%)', digits=dp.get_precision('Discount'), default=0.0)
currency_id = fields.Many2one(related='folio_id.currency_id', store=True, string='Currency', readonly=True)
invoice_line_ids = fields.Many2many('account.invoice.line', 'service_line_invoice_rel', 'service_id', 'invoice_line_id', string='Invoice Lines', copy=False)
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags')
qty_to_invoice = fields.Float(
compute='_get_to_invoice_qty', string='To Invoice', store=True, readonly=True,
digits=dp.get_precision('Product Unit of Measure'))
qty_invoiced = fields.Float(
compute='_get_invoice_qty', string='Invoiced', store=True, readonly=True,
digits=dp.get_precision('Product Unit of Measure'))
price_subtotal = fields.Monetary(string='Subtotal',
readonly=True,
store=True,
@@ -130,7 +210,7 @@ class HotelService(models.Model):
if values.get('product_id'):
line = self.new(values)
if any(f not in values for f in onchange_fields):
line.onchange_product_calc_qty()
line.onchange_product_id()
for field in onchange_fields:
if field not in values:
res[field] = line._fields[field].convert_to_write(line[field], line)
@@ -171,7 +251,7 @@ class HotelService(models.Model):
return max(base_price, final_price)
@api.onchange('product_id')
def onchange_product_calc_qty(self):
def onchange_product_id(self):
"""
Compute the default quantity according to the
configuration of the selected product, in per_day product configuration,
@@ -195,6 +275,30 @@ class HotelService(models.Model):
for day in record.service_line_ids:
day.no_free_resources()
"""
Description and warnings
"""
product = self.product_id.with_context(
lang=self.folio_id.partner_id.lang,
partner=self.folio_id.partner_id.id
)
title = False
message = False
warning = {}
if product.sale_line_warn != 'no-message':
title = _("Warning for %s") % product.name
message = product.sale_line_warn_msg
warning['title'] = title
warning['message'] = message
result = {'warning': warning}
if product.sale_line_warn == 'block':
self.product_id = False
return result
name = product.name_get()[0][1]
if product.description_sale:
name += '\n' + product.description_sale
vals['name'] = name
"""
Compute tax and price unit
"""
self._compute_tax_ids()

View File

@@ -33,7 +33,6 @@ class AccountInvoice(models.Model):
'domain': [('id', 'in', payment_ids)],
}
dif_customer_payment = fields.Boolean(compute='_compute_dif_customer_payment')
from_folio = fields.Boolean(compute='_compute_dif_customer_payment')
sale_ids = fields.Many2many(
'sale.order', 'sale_order_invoice_rel', 'invoice_id',
@@ -45,17 +44,11 @@ class AccountInvoice(models.Model):
@api.multi
def _compute_dif_customer_payment(self):
for inv in self:
return False
#~ sales = inv.mapped('invoice_line_ids.sale_line_ids.order_id')
#~ folios = self.env['hotel.folio'].search([('order_id.id','in',sales.ids)])
#~ if folios:
#~ inv.from_folio = True
#~ inv.folio_ids = [(6, 0, folios.ids)]
#~ payments_obj = self.env['account.payment']
#~ payments = payments_obj.search([('folio_id','in',folios.ids)])
#~ for pay in payments:
#~ if pay.partner_id != inv.partner_id:
#~ inv.dif_customer_payment = True
folios = inv.mapped('invoice_line_ids.reservation_ids.folio_id')
folios |= inv.mapped('invoice_line_ids.service_ids.folio_id')
if folios:
inv.from_folio = True
inv.folio_ids = [(6, 0, folios.ids)]
@api.multi
def action_invoice_open(self):

View File

@@ -11,3 +11,8 @@ class AccountInvoiceLine(models.Model):
'reservation_line_invoice_rel',
'invoice_line_id', 'reservation_id',
string='Reservations', readonly=True, copy=False)
service_ids = fields.Many2many(
'hotel.service',
'service_line_invoice_rel',
'invoice_line_id', 'service_id',
string='Services', readonly=True, copy=False)

View File

@@ -108,17 +108,17 @@
</div>
</button> -->
<!-- <button type="object" class="oe_stat_button" id="invoice_button"
<button type="object" class="oe_stat_button" id="invoice_button"
icon="fa-pencil-square-o" name="open_invoices_folio"
attrs="{'invisible': [('num_invoices', '=', 0)]}"
attrs="{'invisible': [('invoice_count', '=', 0)]}"
context="{'default_folio_id': active_id}">
<div class="o_form_field o_stat_info">
<span class="o_stat_value">
<field name="num_invoices"/>
<field name="invoice_count"/>
</span>
<span class="o_stat_text">Invoices</span>
</div>
</button> -->
</button>
</div>
<!-- <field name="image" widget="image" class="oe_avatar" options="{&quot;preview_image&quot;: &quot;image_medium&quot;, &quot;size&quot;: [90, 90]}"/> -->
@@ -152,315 +152,26 @@
</group>
</group>
<group invisible="1">
<!-- <field name="partner_shipping_id" invisible="1" domain="[('parent_id','=',partner_id)]" /> -->
<!-- <field name="warehouse_id" string="Branch" invisible="1"/> -->
<field name="invoice_ids" invisible="1"/>
<field name="invoice_status" invisible="1" />
<!-- <field name="hotel_invoice_id" states='progress,done,cancel'
readonly="1" invisible="1" /> -->
</group>
<notebook colspan="4" col="1">
<page string="Lines">
<field name="room_lines" colspan="4" string="Room Line"
nolabel="1" context="{'room_lines':room_lines,'folio_id': id}">
<tree string="Rooms"
colors="red:state == 'cancelled'"
default_order="id"
delete="false"
options="{'no_open': True}">
<button type="object" class="oe_stat_button"
id="splitted" icon="fa fa-1x fa-chain-broken"
name="open_master"
attrs="{'invisible':[('splitted','=', False)]}"
/>
<field name="folio_id" invisible="1"/>
<field name="state" />
<button type="action" class="oe_stat_button"
id="checkin_partner_smart_button" icon="fa fa-1x fa-user-plus"
name="%(launch_checkin_wizard_add)d"
context="{'partner_id': partner_id,'enter_date': checkin,
'exit_date': checkout,'reservation_id': id, 'hidden_checkin_partner': True, 'edit_checkin_partner': True }"
attrs="{'invisible':['|','|', ('state','not in',('confirm','booking')),
('checkin_partner_pending_count','=', 0),('parent_reservation','!=',False)]}"
/>
<field name="partner_id"/>
<field name="splitted" invisible="1" />
<field name="parent_reservation" invisible="1" />
<!-- <field name="room_type_id" string="Reserved Room Type"/> -->
<field name="room_type_id" string="Reserved Room Type"/>
<field name="nights" />
<field name="adults" string="Persons"/>
<field name="checkin" widget="date"/>
<field name="checkout" widget="date"/>
<field name="checkin_partner_ids" invisible ="1"/>
<field name="to_assign" invisible="1"/>
<field name="checkin_partner_pending_count" invisible="1"/>
<!-- <field name="qty_delivered" invisible="1"/> -->
<!-- attrs="{'readonly': [('qty_delivered_updateable', '=', False)]}"/> -->
<!-- <field name="qty_invoiced" invisible="1"/> -->
<!-- <field name="qty_to_invoice" invisible="1"/> -->
<!-- <field name="product_uom"
attrs="{'readonly': [('state', 'in', ('sale','done', 'cancel'))]}"
context="{'company_id': parent.company_id}"
groups="product.group_uom" options='{"no_open": True}'
invisible="1"/> -->
<!-- <field name="analytic_tag_ids" groups="analytic.group_analytic_accounting" widget="many2many_tags" invisible="1"/> -->
<!-- <field name="tax_id" widget="many2many_tags" domain="[('type_tax_use','=','sale'),('company_id','=',parent.company_id)]"
invisible="1"/> -->
<!-- <field name="qty_delivered_updateable" invisible="1"/> -->
<field name="state" invisible="1"/>
<!-- <field name="invoice_status" invisible="1"/> -->
<!-- <field name="customer_lead" invisible="1"/> -->
<!-- <field name="currency_id" invisible="1"/> -->
<!-- <field name="price_unit" invisible="1"/> -->
<!-- <field name="amount_room" string="Reservation Price" readonly="1"/>-->
<!-- <field name="amount_discount" string="Final Price"/> -->
<button type="object" class="oe_stat_button"
id="go_reservation" icon="fa fa-2x fa-bars"
name="open_reservation_form"/>
</tree>
<form string="Reservation" >
<sheet>
<header>
<field name="splitted" invisible="1" />
<field name="shared_folio" invisible="1"/>
<field name="folio_id" invisible="1" />
<field name="partner_id" invisible="1"/>
<field name="parent_reservation" invisible="True" />
<!-- <field name="has_confirmed_reservations_to_send" invisible="1" /> -->
<!-- <field name="has_cancelled_reservations_to_send" invisible="1" /> -->
<!-- <field name="has_checkout_to_send" invisible="1" /> -->
<!--<button name="send_reservation_mail" type="object" string="Send Reservation Email" states="confirm" class="oe_highlight"/>-->
<button name="confirm" string="Confirm" class="oe_highlight"
type="object"
attrs="{'invisible':[('state','not in',('draft','cancelled'))]}"
/>
<button name="action_cancel" string="Cancel Reservation"
class="oe_highlight" type="object"
attrs="{'invisible':['|',('folio_id', '=', False),('state','not in',('confirm','booking'))]}"
/>
<button name="action_reservation_checkout" string="Done"
states="booking" class="oe_highlight"
type="object"
/>
<button name="draft" string="Set to Draft"
states="cancelled" class="oe_highlight"
type="object"
/>
<button name="%(action_hotel_split_reservation)d" string="Split"
type="action" class="oe_highlight"
icon="fa-cut"
attrs="{'invisible':['|',('folio_id', '=', False),('state','not in',('draft','confirm','booking'))]}"
/>
<button name="unify" string="Unify"
type="object" class="oe_highlight"
icon="fa-compress"
attrs="{'invisible':[('splitted', '=', False)]}"
/>
<label for="preconfirm"
string="Autoconfirm"
attrs="{'invisible':[('folio_id', '!=', False)]}"
/>
<span name="preconfirm" attrs="{'invisible':[('folio_id', '!=', False)]}">
<field name="preconfirm" />
</span>
<!--button name="open_master" string="Open Master" type="object" class="oe_highlight" icon="fa-file" attrs="{'invisible':['|',['parent_reservation', '=', False]]}" /-->
<field name="state" widget="statusbar"/>
</header>
<span class="label label-danger" attrs="{'invisible': [('state', 'not in', ('cancelled'))]}">Cancelled Reservation!</span>
<span class="label label-warning" attrs="{'invisible': [('overbooking', '=', False)]}">OverBooking!</span>
<h1>
<field name="room_id" select="1"
nolabel="1" options="{'no_create': True,'no_open': True}" placeholder="Room"
style="margin-right: 30px;" required='1'/>
<field name="partner_id" default_focus="1"
placeholder="Lastname, Firstname"
attrs="{'readonly':[('folio_id','!=',False)]}"
required="1"/>
<span class="fa fa-user" style="margin-left:20px;"/>
</h1>
<h3>
<!-- <field name="room_id" select="1" domain="[('isroom','=',True)]"
nolabel="1" options="{'no_create': True,'no_open': True}" placeholder="Room"
style="margin-right: 30px;"/> -->
From <span class="fa fa-sign-in" style="margin: 5px;"/><field name="checkin" style="margin-right: 10px;"/> to
<span class="fa fa-sign-out" style="margin-right: 5px;"/><field name="checkout" />
</h3>
<group col="6">
<group string="General Info" name="contact_details" >
<field name="email" placeholder="email" widget="email" />
<field name="mobile" placeholder="mobile" widget="phone" />
<field name="phone" placeholder="phone" widget="phone" />
<field name="pricelist_id"/>
<field name="partner_internal_comment" string="Partner Note"/>
<field name="cancelled_reason" attrs="{'invisible':[('state','not in',('cancelled'))]}"/>
</group>
<group colspan="4" string="Reservation Details" name="reservation_details">
<field name="arrival_hour"/>
<field name="departure_hour"/>
<field name="nights" invisible="1"/>
<field name="board_service_room_id" domain="[
('hotel_room_type_id', '=', room_type_id),
('pricelist_id', 'in', [pricelist_id, False])]" />
<field name="name"/>
<field name="adults"/>
<field name="children"/>
<field name="room_type_id" on_change="1" options="{'no_create': True,'no_open': True}"
attrs="{'readonly':[('state','not in',('draft'))]}"/>
<field name="channel_type" attrs="{'required':[('reservation_type','not in',('staff','out'))]}"/>
</group>
<group class="oe_subtotal_footer" style="margin-right: 20px; !important" colspan="2" name="reservation_total" string="Amounts">
<!-- <field name="amount_room" widget="monetary" options="{'currency_field': 'currency_id'}"/> -->
<!-- <field name="discount" string="Room Discount" attrs="{'invisible': [('discount_type','=','fixed')]}" /> -->
<!-- <field name="discount_fixed" string="Room Discount" attrs="{'invisible': [('discount_type','=','percent')]}" />
<field name="discount_type" widget="radio" options="{'horizontal': true}" nolabel="1" colspan="2"/>
<div class="oe_subtotal_footer_separator oe_inline o_td_label">
<label for="amount_discount" />
<button name="%(action_hotel_massive_price_change_reservation_days)d" string="Massive Day Prices"
type="action" class="oe_edit_only oe_link" icon="fa-bolt"/>
</div>-->
<!-- <field name="amount_discount" nolabel="1" widget='monetary' class="oe_subtotal_footer_separator" options="{'currency_field': 'currency_id'}"/> -->
<!--<div class="oe_subtotal_footer_separator oe_inline o_td_label">
<label for="amount_reservation_services" />
</div>-->
<!-- <field name="amount_reservation_services" nolabel="1" widget='monetary' class="oe_subtotal_footer_separator" options="{'currency_field': 'currency_id'}"/> -->
<field name="price_total" invisible="1"/>
<!-- <field name="qty_delivered_updateable" invisible="1"/> -->
<field name="state" invisible="1"/>
<!-- <field name="invoice_status" invisible="1"/> -->
<!-- <field name="customer_lead" invisible="1"/> -->
<!-- <field name="currency_id" invisible="1"/> -->
<field name="price_subtotal" widget="monetary" invisible="1"/>
<!-- <field name="price_unit" invisible="1"/> -->
</group>
</group>
<group invisible="1">
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
<!-- <field name="check_rooms" invisible="1"/> -->
<field name="checkin_partner_pending_count" invisible="1"/>
</group>
<notebook>
<page name="days" string="Service and Days">
<group col="9">
<group colspan="6" string="Reservation Services" name="reservation_services" attrs="{'invisible': [('folio_id','=',False)]}">
<field name="service_ids"
context="{'default_ser_room_line': active_id, 'default_folio_id': folio_id}"
nolabel="1" style="padding-right:10px !important;">
<tree string="Services" editable="bottom">
<field name="folio_id" invisible="1"/>
<!-- <field name="layout_category_id" groups="sale.group_sale_layout"/> -->
<field name="name"/>
<field name="ser_room_line" invisible="1" />
<!-- <field name="product_uom_qty"
string="Ordered Qty"
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'uom':product_uom, 'company_id': parent.company_id}"
/> -->
<!-- <field name="qty_delivered" invisible="1"/> -->
<!-- attrs="{'readonly': [('qty_delivered_updateable', '=', False)]}" -->
<!-- <field name="qty_invoiced" invisible="1"/> -->
<!-- <field name="qty_to_invoice" invisible="1"/> -->
<!-- <field name="product_uom"
attrs="{'readonly': [('state', 'in', ('sale','done', 'cancel'))]}"
context="{'company_id': parent.company_id}"
groups="product.group_uom" options='{"no_open": True}'/> -->
<!-- <field name="analytic_tag_ids" groups="analytic.group_analytic_accounting" widget="many2many_tags"/> -->
<!-- <field name="price_unit"
attrs="{'readonly': [('qty_invoiced', '&gt;', 0)]}"/> -->
<!-- <field name="tax_id" widget="many2many_tags" invisible="1"/> -->
<!-- <field name="discount" groups="sale.group_discount_per_so_line"/> -->
<!-- <field name="price_subtotal" widget="monetary" invisible="1"/> -->
<!-- <field name="price_total" widget="monetary"/> -->
<!-- <field name="qty_delivered_updateable" invisible="1"/> -->
<!-- <field name="state" invisible="1"/> -->
<!-- <field name="invoice_status" invisible="1"/> -->
<!-- <field name="customer_lead" invisible="1"/> -->
<!-- <field name="currency_id" invisible="1"/> -->
</tree>
</field>
</group>
<group colspan="3" string="Days" name="days">
<field name="reservation_line_ids" nolabel="1">
<tree create="false" delete="false" editable="bottom">
<field name="date" readonly="True" />
<field name="price" />
<field name="discount" />
</tree>
</field>
</group>
</group>
</page>
<page name="others" string="Others">
<group>
<group>
<field name="segmentation_ids" widget="many2many_tags" placeholder="Segmentation..."
options="{'no_create': True,'no_open': True}" />
<field name="overbooking" />
</group>
<group>
<field name="reservation_type" />
<field name="out_service_description"
attrs="{'invisible':[('reservation_type','not in',('out'))]}"/>
</group>
</group>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
nolabel="1" context="{'from_folio':True,'room_lines':room_lines,'folio_id': id,'tree_view_ref':'hotel.hotel_reservation_view_bottom_tree', 'form_view_ref':'hotel.hotel_reservation_view_form'}"/>
<separator string="Service Lines" colspan="4"/>
<field name="service_ids"
context="{'default_folio_id': active_id}"
nolabel="1">
<tree string="Services" editable="bottom">
<field name="folio_id" invisible="1"/>
<!-- <field name="layout_category_id" groups="sale.group_sale_layout"/> -->
<field name="product_id"
domain="[('sale_ok', '=', True)]"
options="{'create': False, 'create_edit': False}" />
<field name="name"/>
<field name="ser_room_line" options="{'create': False, 'create_edit': False}"/>
<!-- <field name="product_uom_qty"
string="Ordered Qty"
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'uom':product_uom, 'company_id': parent.company_id}"
/> -->
<!-- <field name="qty_delivered" invisible="1"/> -->
<!-- attrs="{'readonly': [('qty_delivered_updateable', '=', False)]}"/> -->
<!-- <field name="qty_invoiced" invisible="1"/> -->
<!-- <field name="qty_to_invoice" invisible="1"/> -->
<!-- <field name="product_uom"
attrs="{'readonly': [('state', 'in', ('sale','done', 'cancel'))]}"
context="{'company_id': parent.company_id}"
groups="product.group_uom" options='{"no_open": True}'/> -->
<!-- <field name="analytic_tag_ids" groups="analytic.group_analytic_accounting" widget="many2many_tags"/> -->
<!-- <field name="price_unit"
attrs="{'readonly': [('qty_invoiced', '&gt;', 0)]}"/> -->
<!-- <field name="tax_id" widget="many2many_tags" domain="[('type_tax_use','=','sale'),('company_id','=',parent.company_id)]"
attrs="{'readonly': [('qty_invoiced', '&gt;', 0)]}"/> -->
<!-- <field name="discount" groups="sale.group_discount_per_so_line"/> -->
<!-- <field name="price_subtotal" widget="monetary" invisible="1"/> -->
<!-- <field name="price_total" widget="monetary"/> -->
<!-- <field name="qty_delivered_updateable" invisible="1"/> -->
<!-- <field name="state" invisible="1"/> -->
<!-- <field name="invoice_status" invisible="1"/> -->
<!-- <field name="customer_lead" invisible="1"/> -->
<!-- <field name="currency_id" invisible="1"/> -->
<field name="channel_type" sttrs="{'invisible':[('channel_type', '!=', 'call')]"/>
</tree>
</field>
<group colspan="2" class="oe_subtotal_footer oe_right">
<field name="amount_untaxed" sum="Untaxed amount" widget='monetary' />
<field name="amount_tax" widget='monetary' />
<div class="oe_subtotal_footer_separator oe_inline">
<label for="amount_total" />
</div>
<field name="amount_total" nolabel="1" sum="Total amount"
widget='monetary' />
</group>
context="{'from_room':False,'folio_id': id,'tree_view_ref':'hotel.hotel_service_view_tree', 'form_view_ref':'hotel.hotel_service_view_form'}"
nolabel="1" />
<group colspan="2" class="oe_subtotal_footer oe_right">
<field name="amount_untaxed" sum="Untaxed amount" widget='monetary' />
<field name="amount_tax" widget='monetary' />
<div class="oe_subtotal_footer_separator oe_inline">
<label for="amount_total" />
</div>
<field name="amount_total" nolabel="1" sum="Total amount"
widget='monetary' />
</group>
<div class="oe_clear" />
<group>
<field name="note" />

View File

@@ -6,6 +6,7 @@
<field name="res_model">hotel.reservation</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,graph,pivot</field>
<field name="context">{'from_room': True}</field>
</record>
<!--=== Hotel Reservation ==== -->
@@ -20,6 +21,7 @@
<field name="parent_reservation" invisible="True" />
<field name="has_confirmed_reservations_to_send" invisible="1" />
<field name="has_cancelled_reservations_to_send" invisible="1" />
<field name="tax_ids" invisible="1"/>
<field name="has_checkout_to_send" invisible="1" />
<button name="send_reservation_mail" type="object" string="Send Confirmation Email"
attrs="{'invisible': [('has_confirmed_reservations_to_send', '=', False)]}" class="oe_highlight"/>
@@ -97,16 +99,6 @@
<span class="o_stat_text">Books</span>
</div>
</button>
<!-- <button type="object" class="oe_stat_button"
id="payment_smart_button"
icon="fa-money"
name="action_recalcule_payment"
attrs="{'invisible': [('fix_folio_pending','=',False)]}"
help="Calcule the total Price">
<div class="o_form_field o_stat_info">
<span class="o_stat_text">Calcule Price</span>
</div>
</button> -->
<button type="object" class="oe_stat_button"
id="open_master"
icon="fa-chain-broken"
@@ -240,55 +232,56 @@
</group>
<notebook>
<page name="days" string="Service and Days">
<group string="Reservation Services" name="reservation_services">
<field name="service_ids"
context="{'default_ser_room_line': active_id, 'default_folio_id': folio_id}"
nolabel="1">
<tree string="Services" editable="bottom"
decoration-success="is_board_service == True">
<button type="object" class="oe_stat_button"
id="included_in_room" icon="fa fa-1x fa-bed"
name="open_service_lines"
attrs="{'invisible':[('is_board_service','=', False)]}" />
<!-- <field name="sequence" widget="handle"/> -->
<field name="per_day" invisible="1"/>
<field name="is_board_service" invisible="1" />
<field name="folio_id" invisible="1"/>
<field name="product_id"
domain="[('sale_ok', '=', True)]"
options="{'create': False, 'create_edit': False}" />
<!-- <field name="layout_category_id" groups="sale.group_sale_layout"/> -->
<field name="name"/>
<field name="product_qty" attrs="{'readonly': [('per_day','=',True)]}" force_save="1"/>
<button type="object" class="oe_stat_button"
id="go_service_lines" icon="fa fa-2x fa-bars"
name="open_service_lines"
attrs="{'invisible': [('per_day','=',False)]}"/>
<field name="days_qty" invisible="1"/>
<field name="price_unit" />
<field name="discount" />
<field name="tax_ids" widget="many2many_tags"/>
<field name="price_subtotal" />
<field name="price_tax" />
<field name="price_total" />
<field name="service_line_ids" invisible="1">
<tree string="Days" >
<field name="date" />
<field name="day_qty" />
</tree>
</field>
</tree>
</field>
</group>
<group string="Days" name="days">
<field name="reservation_line_ids" nolabel="1">
<tree create="false" delete="false" editable="bottom">
<field name="date" />
<field name="price" />
<field name="discount" />
</tree>
</field>
</group>
<group string="Reservation Services" name="reservation_services">
<field name="service_ids"
context="{'default_ser_room_line': active_id, 'default_folio_id': folio_id, 'form_view_ref':'hotel.hotel_service_view_form'}"
nolabel="1">
<!-- If charge this view with tree_view_ref, its !crash! (field not found) when open reservation form from folio form... ¿?-->
<tree string="Services" editable="bottom"
decoration-success="is_board_service == True">
<field name="is_board_service" invisible="1" />
<button type="object" class="oe_stat_button"
id="included_in_room" icon="fa fa-1x fa-bed"
name="open_service_lines"
attrs="{'invisible':[('is_board_service','=', False)]}" />
<field name="per_day" invisible="1"/>
<field name="folio_id" invisible="1"/>
<field name="ser_room_line" invisible="context.get('from_room',True)"
attrs = "{'required': [('per_day','=',True)]}" />
<field name="product_id"
domain="[('sale_ok', '=', True)]"
options="{'create': False, 'create_edit': False}" />
<field name="name"/>
<field name="product_qty" attrs="{'readonly': [('per_day','=',True)]}" force_save="1"/>
<button type="object" class="oe_stat_button"
id="go_service_lines" icon="fa fa-2x fa-bars"
name="open_service_lines"
attrs="{'invisible': [('per_day','=',False)]}"/>
<field name="days_qty" invisible="1"/>
<field name="price_unit" />
<field name="discount" />
<field name="tax_ids" widget="many2many_tags"/>
<field name="price_subtotal" />
<field name="price_tax" />
<field name="price_total" />
<field name="service_line_ids" invisible="1">
<tree string="Days" >
<field name="date" />
<field name="day_qty" />
</tree>
</field>
</tree>
</field>
</group>
<group string="Days" name="days">
<field name="reservation_line_ids" nolabel="1">
<tree create="false" delete="false" editable="bottom">
<field name="date" />
<field name="price" />
<field name="discount" />
</tree>
</field>
</group>
</page>
<page name="others" string="Others">
<group>
@@ -331,6 +324,7 @@
<button icon="fa fa-2x fa-angellist"
attrs="{'invisible':['|',('folio_pending_amount','&gt;',0),('state' ,'!=', 'done')]}"
type="object"
id="open_folio"
name="open_folio" />
<field name="state" />
<button type="object" class="oe_stat_button"
@@ -345,6 +339,7 @@
'exit_date': checkout,'reservation_id': id, 'hidden_checkin_partner': True, 'edit_checkin_partner': True }"
attrs="{'invisible':['|','|', ('state','not in',('confirm','booking')),('checkin_partner_pending_count','=', 0),('parent_reservation','!=',False)]}"
/>
<field name="room_id" />
<button type="action" class="oe_stat_button"
icon="fa fa-2x fa-list-ul"
name="%(open_hotel_reservation_form_tree_all)d"
@@ -352,7 +347,6 @@
/>
<field name="partner_id"/>
<field name="parent_reservation" invisible="1" />
<!-- <field name="room_type_id" string="Reserved Room Type"/> -->
<field name="room_type_id" string="Reserved Unit Type" />
<field name="nights" />
<field name="adults" string="Persons"/>
@@ -363,8 +357,11 @@
<field name="last_updated_res" string="Updated on"/>
<field name="checkin_partner_ids" invisible ="1"/>
<field name="to_assign" invisible="1"/>
<!-- checkin_partner_smart_button attrs depends on checkin_partner_pending to be showed -->
<field name="checkin_partner_pending_count" invisible="1"/>
<field name="discount" />
<field name="tax_ids" invisible="1"/>
<field name="price_subtotal" invisible="1"/>
<field name="price_total" />
<field name="folio_pending_amount" string="Folio Pending Amount"/>
<button type="object" class="oe_stat_button"
icon="fa fa-3x fa-money"
@@ -376,6 +373,46 @@
</field>
</record>
<!-- Tree editable BOTTOM view of hotel reservation -->
<record model="ir.ui.view" id="hotel_reservation_view_bottom_tree">
<field name="name">hotel.reservation.tree</field>
<field name="model">hotel.reservation</field>
<field name="inherit_id" ref="hotel.hotel_reservation_view_tree" />
<field name="mode">primary</field>
<field name="arch" type="xml">
<tree position="attributes">
<attribute name="editable">bottom</attribute>
<attribute name="delete">false</attribute>
<attribute name="options">{'no_open': True}</attribute>
<attribute name="string">Rooms</attribute>
</tree>
<xpath expr="//field[@name='folio_id']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<xpath expr="//button[@name='open_folio'][1]" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//button[@name='open_folio'][2]" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//button[@name='open_folio'][2]" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//button[@name='%(open_hotel_reservation_form_tree_all)d']" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//field[@name='partner_id']" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//field[@name='folio_pending_amount']" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//field[@name='create_uid']" position="attributes">
<attribute name="invisible">True</attribute>
</xpath>
</field>
</record>
<!-- Search view of hotel reservation -->
<record model="ir.ui.view" id="hotel_reservation_view_search">
<field name="name">hotel.reservation.search</field>

View File

@@ -41,6 +41,48 @@
</field>
</record>
<!-- Tree view of hotel service -->
<record model="ir.ui.view" id="hotel_service_view_tree">
<field name="name">.hotel.service.view.tree</field>
<field name="model">hotel.service</field>
<field name="arch" type="xml">
<tree string="Services" editable="bottom"
decoration-success="is_board_service == True">
<field name="is_board_service" invisible="1" />
<button type="object" class="oe_stat_button"
id="included_in_room" icon="fa fa-1x fa-bed"
name="open_service_lines"
attrs="{'invisible':[('is_board_service','=', False)]}" />
<field name="per_day" invisible="1"/>
<field name="folio_id" invisible="1"/>
<field name="ser_room_line" invisible="context.get('from_room',True)"
attrs = "{'required': [('per_day','=',True)]}" />
<field name="product_id"
domain="[('sale_ok', '=', True)]"
options="{'create': False, 'create_edit': False}"/>
<field name="name"/>
<field name="product_qty" attrs="{'readonly': [('per_day','=',True)]}" force_save="1"/>
<button type="object" class="oe_stat_button"
id="go_service_lines" icon="fa fa-2x fa-bars"
name="open_service_lines"
attrs="{'invisible': [('per_day','=',False)]}"/>
<field name="days_qty" invisible="1"/>
<field name="price_unit" />
<field name="discount" />
<field name="tax_ids" widget="many2many_tags"/>
<field name="price_subtotal" />
<field name="price_tax" />
<field name="price_total" />
<field name="service_line_ids" invisible="1">
<tree string="Days" >
<field name="date" />
<field name="day_qty" />
</tree>
</field>
</tree>
</field>
</record>
<!-- search view of hotel service -->
<record model="ir.ui.view" id="hotel_service_view_search">
<field name="name">hotel.service.search</field>
@@ -60,19 +102,6 @@
</field>
</record>
<!-- Tree view of hotel service -->
<record model="ir.ui.view" id="hotel_service_view_tree">
<field name="name">hotel.service.tree</field>
<field name="model">hotel.service</field>
<field name="arch" type="xml">
<tree string="Hotel Services">
<field name="name" />
<field name="product_id" />
<field name="product_qty" />
</tree>
</field>
</record>
<!-- Action for hotel service -->
<record model="ir.actions.act_window" id="action_hotel_services_form">
<field name="name">Hotel Services</field>

View File

@@ -5,20 +5,12 @@
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_form" />
<field name="arch" type="xml">
<xpath expr="//header" position="after">
<div class="alert alert-danger" role="alert" style="margin-bottom:0px;"
attrs="{'invisible': [('dif_customer_payment','=',False)]}">
You have payments on the related folio associated with other customers than the current one on the invoice.
Make sure to <bold><button class="alert-link" type="object" name="action_folio_payments" string="modify the payment"/></bold> from the folio if necessary before paying this invoice
</div>
<field name="dif_customer_payment" invisible="1" />
<field name="from_folio" invisible="1" />
</xpath>
<xpath expr="//field[@name='date_invoice']" position="after">
<field name="folio_ids" widget="many2many_tags"/>
<field name="from_folio" invisible="1" />
</xpath>
<xpath expr="//button[@name='%(account.action_account_invoice_payment)d']" position="attributes">
<attribute name="attrs">{'invisible': ['|',('from_folio','=',True)]}</attribute>
<attribute name="attrs">{'invisible': ['|',('from_folio','=',True)]}</attribute>
</xpath>
</field>
</record>

View File

@@ -1,9 +1,11 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import time
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
from odoo import api, fields, models, _
import odoo.addons.decimal_precision as dp
from odoo.exceptions import UserError
from datetime import timedelta
class FolioAdvancePaymentInv(models.TransientModel):
@@ -20,9 +22,25 @@ class FolioAdvancePaymentInv(models.TransientModel):
@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)
product_id = self.env['ir.config_parameter'].sudo().get_param('sale.default_deposit_product_id')
return self.env['product.product'].browse(int(product_id))
@api.model
def _get_default_folio(self):
folios = self.env['hotel.folio'].browse(self._context.get('active_ids', []))
return folios
@api.model
def _get_default_reservation(self):
folios = self._get_default_folio()
reservations = self.env['hotel.reservation']
for folio in folios:
reservations |= folio.room_lines
return reservations
@api.model
def _get_default_partner_invoice(self):
return self.env['hotel.folio'].browse(self._context.get('active_id', [])).partner_invoice_id
@api.model
def _default_deposit_account_id(self):
@@ -40,14 +58,25 @@ class FolioAdvancePaymentInv(models.TransientModel):
required=True)
count = fields.Integer(default=_count, string='# of Orders')
folio_ids = fields.Many2many("hotel.folio", string="Folios",
help="Folios grouped")
help="Folios grouped",
default=_get_default_folio)
reservation_ids = fields.Many2many("hotel.reservation", string="Rooms",
help="Folios grouped",
default=_get_default_reservation)
group_folios = fields.Boolean('Group Folios')
partner_invoice_id = fields.Many2one('res.partner',
string='Invoice Address', required=True,
default=_get_default_partner_invoice,
help="Invoice address for current Invoice.")
line_ids = fields.One2many('line.advance.inv',
'advance_inv_id',
string="Invoice Lines")
view_detail = fields.Boolean('View Detail')
line_ids = fields.One2many('line.advance.inv',
'advance_inv_id',
string="Lines")
#Advance Payment
product_id = fields.Many2one('product.product', string='Down Payment Product',
product_id = fields.Many2one('product.product', string="Product",
domain=[('type', '=', 'service')], default=_default_product_id)
amount = fields.Float('Down Payment Amount',
digits=dp.get_precision('Account'),
@@ -107,7 +136,6 @@ class FolioAdvancePaymentInv(models.TransientModel):
'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,
@@ -119,7 +147,7 @@ class FolioAdvancePaymentInv(models.TransientModel):
'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,
'account_analytic_id': order.analytic_account_id.id or False,
})],
'currency_id': order.pricelist_id.currency_id.id,
'payment_term_id': order.payment_term_id.id,
@@ -138,26 +166,31 @@ class FolioAdvancePaymentInv(models.TransientModel):
@api.multi
def create_invoices(self):
folios = self.env['hotel.folio'].browse(self._context.get('active_ids', []))
inv_obj = self.env['account.invoice']
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
folios = self.folio_ids
if self.advance_payment_method == 'delivered':
folios.action_invoice_create()
elif self.advance_payment_method == 'all':
folios.action_invoice_create()
for folio in folios:
if folio.partner_invoice_id != self.partner_invoice_id:
raise UserError(_('The billing directions must match'))
if self.advance_payment_method == 'all':
inv_data = self._prepare_invoice()
invoice = inv_obj.create(inv_data)
for line in self.line_ids:
line.invoice_line_create(invoice.id, line.qty)
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)
self.env['ir.config_parameter'].sudo().set_param(
'sale.default_deposit_product_id', self.product_id.id)
sale_line_obj = self.env['sale.order.line']
for order in sale_orders:
service_obj = self.env['hotel.service']
for folio in folios:
if self.advance_payment_method == 'percentage':
amount = order.amount_untaxed * self.amount / 100
amount = folio.amount_untaxed * folio.amount_total / 100
else:
amount = self.amount
if self.product_id.invoice_policy != 'order':
@@ -165,26 +198,26 @@ class FolioAdvancePaymentInv(models.TransientModel):
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
lambda r: not folio.company_id or r.company_id == folio.company_id)
if folio.fiscal_position_id and taxes:
tax_ids = folio.fiscal_position_id.map_tax(taxes).ids
else:
tax_ids = taxes.ids
context = {'lang': order.partner_id.lang}
so_line = sale_line_obj.create({
context = {'lang': folio.partner_id.lang}
service_line = service_obj.create({
'name': _('Advance: %s') % (time.strftime('%m %Y'),),
'price_unit': amount,
'product_uom_qty': 0.0,
'order_id': order.id,
'folio_id': folio.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)
self._create_invoice(folio, service_line, amount)
if self._context.get('open_invoices', False):
return sale_orders.action_view_invoice()
return folios.open_invoices_folio()
return {'type': 'ir.actions.act_window_close'}
def _prepare_deposit_product(self):
@@ -196,67 +229,184 @@ class FolioAdvancePaymentInv(models.TransientModel):
'taxes_id': [(6, 0, self.deposit_taxes_id.ids)],
}
@api.onchange('view_detail')
def prepare_reservation_invoice_lines(self):
@api.onchange('reservation_ids')
def prepare_invoice_lines(self):
vals = []
folios = self.env['hotel.folio'].browse(self._context.get('active_ids', []))
folios = self.folio_ids
invoice_lines = {}
for folio in folios:
folio_name = folio.name
for reservation in folio.room_lines:
reservation_name = reservation.name
unit_price = False
discount = False
qty = 0
for service in folio.service_ids.filtered(
lambda x: x.is_board_service == False and \
(x.ser_room_line.id in self.reservation_ids.ids or \
x.ser_room_line.id == False)):
invoice_lines[service.id] = {
'description' : service.name,
'product_id': service.product_id.id,
'qty': service.product_qty,
'discount': service.discount,
'price_unit': service.price_unit,
'service_id': service.id,
}
for reservation in folio.room_lines.filtered(
lambda x: x.id in self.reservation_ids.ids):
board_service = reservation.board_service_room_id
for day in reservation.reservation_line_ids.sorted('date'):
if day.price == unit_price and day.discount == discount:
date_to = day.date
qty += 1
extra_price = 0
if board_service:
services = reservation.service_ids.filtered(
lambda x: x.is_board_service == True)
for service in services:
extra_price += service.price_unit * \
service.service_line_ids.filtered(
lambda x: x.date == day.date).day_qty
group_key = (reservation.id, reservation.room_type_id.id, day.price + extra_price, day.discount)
date = fields.Date.from_string(day.date)
if group_key in invoice_lines:
invoice_lines[group_key][('qty')] += 1
if date == fields.Date.from_string(
invoice_lines[group_key][('date_to')]) + timedelta(days=1):
desc = invoice_lines[group_key][('description')]
invoice_lines[group_key][('description')] = \
desc.replace(desc[desc.rfind(" - "):], ' - ' + \
(date + timedelta(days=1)).strftime(DEFAULT_SERVER_DATE_FORMAT) + ')')
else:
invoice_lines[group_key][('description')] += \
' (' + date.strftime(DEFAULT_SERVER_DATE_FORMAT) + \
' - ' + (date + timedelta(days=1)).strftime(
DEFAULT_SERVER_DATE_FORMAT) + \
')'
invoice_lines[group_key][('date_to')] = day.date
else:
if unit_price:
vals.append((0, False, {
'date_from': date_from,
'date_to': date_to,
'room_type_id': reservation.room_type_id,
'product_id': self.env['product.product'].browse(
reservation.room_type_id.product_id.id
),
'qty': qty,
'discount': discount,
'unit_price': unit_price
}))
qty = 1
unit_price = day.price
date_from = day.date
date_to = day.date
vals.append((0, False, {
'date_from': date_from,
'date_to': date_to,
'room_type_id': reservation.room_type_id,
'product_id': self.env['product.product'].browse(
reservation.room_type_id.product_id.id
),
'qty': qty,
'discount': discount,
'unit_price': unit_price
}))
room_type_description = folio.name + ' ' + reservation.room_type_id.name + ' (' + \
reservation.board_service_room_id.hotel_board_service_id.name + ')' \
if board_service else folio.name + ' ' + reservation.room_type_id.name
description = room_type_description + \
': (' + date.strftime(DEFAULT_SERVER_DATE_FORMAT) + \
' - ' + (date + timedelta(days=1)).strftime(DEFAULT_SERVER_DATE_FORMAT) + \
')'
invoice_lines[group_key] = {
'description' : description,
'reservation_id': reservation.id,
'room_type_id': reservation.room_type_id,
'product_id': self.env['product.product'].browse(
reservation.room_type_id.product_id.id
),
'qty': 1,
'discount': day.discount,
'price_unit': day.price + extra_price,
'date_to': day.date
}
for group_key in invoice_lines:
vals.append((0, False, invoice_lines[group_key]))
self.line_ids = vals
@api.onchange('view_detail', 'folio_ids')
def onchange_folio_ids(self):
vals = []
folios = self.folio_ids
invoice_lines = {}
reservations = self.env['hotel.reservation']
services = self.env['hotel.service']
for folio in folios:
folio_reservations = folio.room_lines
if folio_reservations:
reservations |= folio_reservations
self.reservation_ids = reservations
self.prepare_invoice_lines()
@api.model
def _prepare_invoice(self):
"""
Prepare the dict of values to create the new invoice for a folio. This method may be
overridden to implement custom invoice generation (making sure to call super() to establish
a clean extension chain).
"""
journal_id = self.env['account.invoice'].default_get(['journal_id'])['journal_id']
if not journal_id:
raise UserError(_('Please define an accounting sales journal for this company.'))
origin = ' '.join(self.folio_ids.mapped('name'))
pricelist = self.folio_ids[0].pricelist_id
currency = self.folio_ids[0].currency_id
payment_term = self.folio_ids[0].payment_term_id
fiscal_position = self.folio_ids[0].fiscal_position_id
company = self.folio_ids[0].company_id
user = self.folio_ids[0].user_id
team = self.folio_ids[0].team_id
for folio in self.folio_ids:
if folio.pricelist_id != pricelist:
raise UserError(_('All Folios must hace the same pricelist'))
invoice_vals = {
'name': self.folio_ids[0].client_order_ref or '',
'origin': origin,
'type': 'out_invoice',
'account_id': self.partner_invoice_id.property_account_receivable_id.id,
'partner_id': self.partner_invoice_id.id,
'journal_id': journal_id,
'currency_id': pricelist.id,
'payment_term_id': payment_term.id,
'fiscal_position_id': fiscal_position.id or self.partner_invoice_id.property_account_position_id.id,
'company_id': company.id,
'user_id': user and user.id,
'team_id': team.id
}
return invoice_vals
class LineAdvancePaymentInv(models.TransientModel):
_name = "line.advance.inv"
_description = "Lines Advance Invoice"
date_from = fields.Date('From')
date_to = fields.Date('To')
room_type_id = fields.Many2one('hotel.room.type')
product_id = fields.Many2one('product.product', string='Down Payment Product',
domain=[('type', '=', 'service')])
qty = fields.Integer('Quantity')
unit_price = fields.Float('Price')
price_unit = fields.Float('Price')
advance_inv_id = fields.Many2one('folio.advance.payment.inv')
discount = fields.Float(
string='Discount (%)',
digits=dp.get_precision('Discount'), default=0.0)
to_invoice = fields.Boolean('To Invoice')
description = fields.Text('Description')
reservation_id = fields.Many2one('hotel.reservation')
service_id = fields.Many2one('hotel.service')
@api.multi
def invoice_line_create(self, invoice_id, qty):
""" Create an invoice line.
:param invoice_id: integer
:param qty: float quantity to invoice
:returns recordset of account.invoice.line created
"""
invoice_lines = self.env['account.invoice.line']
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
for line in self:
origin = self.reservation_id if self.reservation_id.id else self.service_id
res = {}
product = line.product_id
account = product.property_account_income_id or product.categ_id.property_account_income_categ_id
if not account:
raise UserError(_('Please define income account for this product: "%s" (id:%d) - or for its category: "%s".') %
(product.name, product.id, product.categ_id.name))
fpos = origin.folio_id.fiscal_position_id or origin.folio_id.partner_id.property_account_position_id
if fpos:
account = fpos.map_account(account)
vals = {
'name': line.description,
'sequence': origin.sequence,
'origin': origin.name,
'account_id': account.id,
'price_unit': line.price_unit,
'quantity': line.qty,
'discount': line.discount,
'uom_id': product.uom_id.id,
'product_id': product.id or False,
'invoice_line_tax_ids': [(6, 0, origin.tax_ids.ids)],
'account_analytic_id': origin.folio_id.analytic_account_id.id,
'analytic_tag_ids': [(6, 0, origin.analytic_tag_ids.ids)],
}
vals.update({'invoice_id': invoice_id, 'reservation_ids': [(6, 0, [origin.id])]})
invoice_lines |= self.env['account.invoice.line'].create(vals)
return invoice_lines

View File

@@ -4,47 +4,60 @@
<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>
<form string="Invoice Folio">
<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>
<field name="partner_invoice_id" />
<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" string="Down Payment Product"
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>
<field name="folio_ids" attrs="{'invisible': [('group_folios', '=', False)]}">
<tree string="Folios" editable="bottom"
decoration-danger="partner_invoice_id != parent.partner_invoice_id">
<field name="partner_invoice_id" />
<field name="name" />
<field name="state" />
<field name="pending_amount" />
<field name="amount_total" />
</tree>
</field>
<field name="reservation_ids" widget="many2many_tags"
domain="[('folio_id', 'in', folio_ids)]"/>
</group>
<group>
<field name="group_folios" />
<field name="view_detail" />
</group>
<group>
<field name="line_ids" attrs="{'invisible': [('view_detail', '=', False)]}">
<field name="line_ids" attrs="{'invisible': [('view_detail', '=', False)]}"
nolabel="1">
<tree string="Lines" editable="bottom">
<field name="room_type_id" />
<field name="date_from" />
<field name="date_to" />
<field name="product_id" invisible="1"/>
<field name="reservation_id" />
<field name="description" />
<field name="qty" />
<field name="discount" />
<field name="unit_price" />
<field name="price_unit" />
</tree>
</field>
</group>
<footer>
<button name="prepare_reservation_invoice_lines" string="Prepare Lines" type="object"
class="btn-primary"/>
<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"