mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[WIP] Invoice WorkFlow
This commit is contained in:
@@ -31,3 +31,4 @@ from . import hotel_service_line
|
||||
from . import hotel_board_service
|
||||
from . import hotel_board_service_room_type_line
|
||||
from . import hotel_board_service_line
|
||||
from . import inherited_account_invoice_line
|
||||
|
||||
@@ -11,6 +11,8 @@ from dateutil.relativedelta import relativedelta
|
||||
from odoo.exceptions import except_orm, UserError, ValidationError
|
||||
from odoo.tools import (
|
||||
misc,
|
||||
float_is_zero,
|
||||
float_compare,
|
||||
DEFAULT_SERVER_DATETIME_FORMAT,
|
||||
DEFAULT_SERVER_DATE_FORMAT)
|
||||
from odoo import models, fields, api, _
|
||||
@@ -44,9 +46,14 @@ class HotelFolio(models.Model):
|
||||
def _amount_all(self):
|
||||
pass
|
||||
|
||||
@api.model
|
||||
def _get_default_team(self):
|
||||
return self.env['crm.team']._get_default_team_id()
|
||||
|
||||
#Main Fields--------------------------------------------------------
|
||||
name = fields.Char('Folio Number', readonly=True, index=True,
|
||||
default=lambda self: _('New'))
|
||||
client_order_ref = fields.Char(string='Customer Reference', copy=False)
|
||||
partner_id = fields.Many2one('res.partner',
|
||||
track_visibility='onchange')
|
||||
|
||||
@@ -87,6 +94,7 @@ class HotelFolio(models.Model):
|
||||
required=True, readonly=True, index=True,
|
||||
states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
|
||||
copy=False, default=fields.Datetime.now)
|
||||
confirmation_date = fields.Datetime(string='Confirmation Date', readonly=True, index=True, help="Date on which the folio is confirmed.", copy=False)
|
||||
state = fields.Selection([
|
||||
('draft', 'Quotation'),
|
||||
('sent', 'Quotation Sent'),
|
||||
@@ -111,6 +119,7 @@ class HotelFolio(models.Model):
|
||||
readonly=True)
|
||||
return_ids = fields.One2many('payment.return', 'folio_id',
|
||||
readonly=True)
|
||||
payment_term_id = fields.Many2one('account.payment.term', string='Payment Terms', oldname='payment_term')
|
||||
|
||||
#Amount Fields------------------------------------------------------
|
||||
pending_amount = fields.Monetary(compute='compute_amount',
|
||||
@@ -150,12 +159,13 @@ class HotelFolio(models.Model):
|
||||
string='Invoice Status',
|
||||
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)]},
|
||||
#~ help="Invoice address for current sales order.")
|
||||
partner_invoice_id = fields.Many2one('res.partner',
|
||||
string='Invoice Address',
|
||||
readonly=True, required=True,
|
||||
states={'draft': [('readonly', False)],
|
||||
'sent': [('readonly', False)]},
|
||||
help="Invoice address for current sales order.")
|
||||
fiscal_position_id = fields.Many2one('account.fiscal.position', oldname='fiscal_position', string='Fiscal Position')
|
||||
|
||||
#WorkFlow Mail Fields-----------------------------------------------
|
||||
has_confirmed_reservations_to_send = fields.Boolean(
|
||||
@@ -179,6 +189,7 @@ class HotelFolio(models.Model):
|
||||
client_order_ref = fields.Char(string='Customer Reference', copy=False)
|
||||
note = fields.Text('Terms and conditions')
|
||||
sequence = fields.Integer(string='Sequence', default=10)
|
||||
team_id = fields.Many2one('crm.team', 'Sales Channel', change_default=True, default=_get_default_team, oldname='section_id')
|
||||
|
||||
@api.depends('room_lines.price_total','service_ids.price_total')
|
||||
def _amount_all(self):
|
||||
@@ -356,7 +367,7 @@ class HotelFolio(models.Model):
|
||||
if any(f not in vals for f in lfields):
|
||||
partner = self.env['res.partner'].browse(vals.get('partner_id'))
|
||||
addr = partner.address_get(['delivery', 'invoice'])
|
||||
#~ vals['partner_invoice_id'] = vals.setdefault('partner_invoice_id', addr['invoice'])
|
||||
vals['partner_invoice_id'] = vals.setdefault('partner_invoice_id', addr['invoice'])
|
||||
vals['pricelist_id'] = vals.setdefault(
|
||||
'pricelist_id',
|
||||
partner.property_product_pricelist and partner.property_product_pricelist.id)
|
||||
@@ -373,11 +384,11 @@ class HotelFolio(models.Model):
|
||||
- user_id
|
||||
"""
|
||||
if not self.partner_id:
|
||||
#~ self.update({
|
||||
#~ 'partner_invoice_id': False,
|
||||
#~ 'payment_term_id': False,
|
||||
#~ 'fiscal_position_id': False,
|
||||
#~ })
|
||||
self.update({
|
||||
'partner_invoice_id': False,
|
||||
'payment_term_id': False,
|
||||
'fiscal_position_id': False,
|
||||
})
|
||||
return
|
||||
addr = self.partner_id.address_get(['invoice'])
|
||||
pricelist = self.partner_id.property_product_pricelist and \
|
||||
@@ -438,6 +449,102 @@ class HotelFolio(models.Model):
|
||||
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
|
||||
'''
|
||||
@@ -483,7 +590,21 @@ class HotelFolio(models.Model):
|
||||
|
||||
@api.multi
|
||||
def action_confirm(self):
|
||||
_logger.info('action_confirm')
|
||||
for folio in self.filtered(lambda folio: folio.partner_id not in folio.message_partner_ids):
|
||||
folio.message_subscribe([folio.partner_id.id])
|
||||
self.write({
|
||||
'state': 'confirm',
|
||||
'confirmation_date': fields.Datetime.now()
|
||||
})
|
||||
#~ if self.env.context.get('send_email'):
|
||||
#~ self.force_quotation_send()
|
||||
|
||||
# create an analytic account if at least an expense product
|
||||
#~ if any([expense_policy != 'no' for expense_policy in self.order_line.mapped('product_id.expense_policy')]):
|
||||
#~ if not self.analytic_account_id:
|
||||
#~ self._create_analytic_account()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
"""
|
||||
|
||||
@@ -8,6 +8,8 @@ from lxml import etree
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools import (
|
||||
misc,
|
||||
float_is_zero,
|
||||
float_compare,
|
||||
DEFAULT_SERVER_DATE_FORMAT,
|
||||
DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
from odoo import models, fields, api, _
|
||||
@@ -75,6 +77,7 @@ class HotelReservation(models.Model):
|
||||
return folio.room_lines[0].departure_hour
|
||||
else:
|
||||
return default_departure_hour
|
||||
|
||||
|
||||
@api.model
|
||||
def name_search(self, name='', args=None, operator='ilike', limit=100):
|
||||
@@ -115,6 +118,7 @@ class HotelReservation(models.Model):
|
||||
).days
|
||||
|
||||
name = fields.Text('Reservation Description', required=True)
|
||||
sequence = fields.Integer(string='Sequence', default=10)
|
||||
|
||||
room_id = fields.Many2one('hotel.room', string='Room')
|
||||
|
||||
@@ -230,8 +234,6 @@ class HotelReservation(models.Model):
|
||||
# order_line = fields.One2many('sale.order.line', 'order_id', string='Order Lines', states={'cancel': [('readonly', True)], 'done': [('readonly', True)]}, copy=True, auto_join=True)
|
||||
# product_id = fields.Many2one('product.product', related='order_line.product_id', string='Product')
|
||||
# product_uom = fields.Many2one('product.uom', string='Unit of Measure', required=True)
|
||||
# product_uom_qty = fields.Float(string='Quantity', digits=dp.get_precision('Product Unit of Measure'), required=True, default=1.0)
|
||||
|
||||
currency_id = fields.Many2one('res.currency',
|
||||
related='pricelist_id.currency_id',
|
||||
string='Currency', readonly=True, required=True)
|
||||
@@ -244,12 +246,13 @@ class HotelReservation(models.Model):
|
||||
tax_id = fields.Many2many('account.tax',
|
||||
string='Taxes',
|
||||
domain=['|', ('active', '=', False), ('active', '=', True)])
|
||||
# qty_to_invoice = fields.Float(
|
||||
# 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'))
|
||||
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'))
|
||||
invoice_lines = fields.Many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_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',
|
||||
@@ -275,7 +278,7 @@ class HotelReservation(models.Model):
|
||||
# FIXME discount per night
|
||||
discount = fields.Float(string='Discount (%)', digits=dp.get_precision('Discount'), default=0.0)
|
||||
|
||||
# analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags')
|
||||
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags')
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
@@ -1124,3 +1127,91 @@ class HotelReservation(models.Model):
|
||||
@api.multi
|
||||
def send_cancel_mail(self):
|
||||
return self.folio_id.send_cancel_mail()
|
||||
|
||||
"""
|
||||
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
|
||||
|
||||
@api.depends('invoice_lines.invoice_id.state', 'invoice_lines.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
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ class HotelReservationLine(models.Model):
|
||||
discount = fields.Float(
|
||||
string='Discount (%)',
|
||||
digits=dp.get_precision('Discount'), default=0.0)
|
||||
invoiced = fields.Boolean('Invoiced')
|
||||
|
||||
|
||||
@api.constrains('date')
|
||||
def constrains_duplicated_date(self):
|
||||
|
||||
@@ -45,16 +45,17 @@ class AccountInvoice(models.Model):
|
||||
@api.multi
|
||||
def _compute_dif_customer_payment(self):
|
||||
for inv in self:
|
||||
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
|
||||
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
|
||||
|
||||
@api.multi
|
||||
def action_invoice_open(self):
|
||||
|
||||
13
hotel/models/inherited_account_invoice_line.py
Normal file
13
hotel/models/inherited_account_invoice_line.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# Copyright 2017 Alexandre Díaz
|
||||
# Copyright 2017 Dario Lodeiros
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
class AccountInvoiceLine(models.Model):
|
||||
_inherit = 'account.invoice.line'
|
||||
|
||||
reservation_ids = fields.Many2many(
|
||||
'hotel.reservation',
|
||||
'reservation_line_invoice_rel',
|
||||
'invoice_line_id', 'reservation_id',
|
||||
string='Reservations', readonly=True, copy=False)
|
||||
@@ -20,9 +20,9 @@
|
||||
attrs="{'invisible': [('has_cancelled_reservations_to_send', '=', False)]}" class="oe_highlight"/> -->
|
||||
<!-- <button name="send_exit_mail" type="object" string="Send Exit Email"
|
||||
attrs="{'invisible': [('has_checkout_to_send', '=', False)]}" class="oe_highlight"/> -->
|
||||
<!-- <button name="%(hotel.action_view_folio_advance_payment_inv)d"
|
||||
<button name="%(hotel.action_view_folio_advance_payment_inv)d"
|
||||
string="Create Invoice" type="action" class="btn-primary" states="sale"
|
||||
attrs="{'invisible': [('invoice_status', '!=', 'to invoice')]}"/> -->
|
||||
attrs="{'invisible': [('state', '!=', 'confirm')]}"/>
|
||||
<!-- <button name="action_cancel_draft" states="cancel,sale" string="Set to Draft"
|
||||
type="object" icon="fa-undo" class="oe_highlight" /> -->
|
||||
<button name="action_cancel" string="Cancel Folio" states="sale"
|
||||
@@ -132,10 +132,12 @@
|
||||
<field name="email" placeholder="email"/>
|
||||
<field name="mobile" placeholder="mobile"/>
|
||||
<field name="phone" />
|
||||
<field name="partner_invoice_id" />
|
||||
<field name="cancelled_reason" attrs="{'invisible':[('state','not in',('cancel'))]}"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="pricelist_id" domain="[('type','=','sale')]" />
|
||||
<field name="company_id" />
|
||||
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
|
||||
<field name="reservation_type" attrs="{'readonly':[('state','not in',('draft'))]}"/>
|
||||
<field name="channel_type" attrs="{'required':[('reservation_type','=','normal')]}"/>
|
||||
|
||||
@@ -16,14 +16,7 @@ class FolioAdvancePaymentInv(models.TransientModel):
|
||||
|
||||
@api.model
|
||||
def _get_advance_payment_method(self):
|
||||
if self._count() == 1:
|
||||
sale_obj = self.env['sale.order']
|
||||
folio_obj = self.env['hotel.folio']
|
||||
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'
|
||||
return 'all'
|
||||
|
||||
@api.model
|
||||
def _default_product_id(self):
|
||||
@@ -40,15 +33,22 @@ class FolioAdvancePaymentInv(models.TransientModel):
|
||||
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)
|
||||
count = fields.Integer(default=_count, string='# of Orders')
|
||||
folio_ids = fields.Many2many("hotel.folio", string="Folios",
|
||||
help="Folios grouped")
|
||||
group_folios = fields.Boolean('Group Folios')
|
||||
line_ids = fields.One2many('line.advance.inv',
|
||||
'advance_inv_id',
|
||||
string="Invoice Lines")
|
||||
view_detail = fields.Boolean('View Detail')
|
||||
#Advance Payment
|
||||
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.")
|
||||
@@ -139,12 +139,11 @@ class FolioAdvancePaymentInv(models.TransientModel):
|
||||
@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()
|
||||
folios.action_invoice_create()
|
||||
elif self.advance_payment_method == 'all':
|
||||
sale_orders.action_invoice_create(final=True)
|
||||
folios.action_invoice_create()
|
||||
else:
|
||||
# Create deposit product if necessary
|
||||
if not self.product_id:
|
||||
@@ -196,3 +195,68 @@ class FolioAdvancePaymentInv(models.TransientModel):
|
||||
'property_account_income_id': self.deposit_account_id.id,
|
||||
'taxes_id': [(6, 0, self.deposit_taxes_id.ids)],
|
||||
}
|
||||
|
||||
@api.onchange('view_detail')
|
||||
def prepare_reservation_invoice_lines(self):
|
||||
vals = []
|
||||
folios = self.env['hotel.folio'].browse(self._context.get('active_ids', []))
|
||||
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 day in reservation.reservation_line_ids.sorted('date'):
|
||||
if day.price == unit_price and day.discount == discount:
|
||||
date_to = day.date
|
||||
qty += 1
|
||||
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
|
||||
}))
|
||||
self.line_ids = 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')
|
||||
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')
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -28,8 +28,23 @@
|
||||
<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)]}"/>
|
||||
<field name="view_detail" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="line_ids" attrs="{'invisible': [('view_detail', '=', False)]}">
|
||||
<tree string="Lines" editable="bottom">
|
||||
<field name="room_type_id" />
|
||||
<field name="date_from" />
|
||||
<field name="date_to" />
|
||||
<field name="qty" />
|
||||
<field name="discount" />
|
||||
<field name="unit_price" />
|
||||
</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"
|
||||
|
||||
Reference in New Issue
Block a user