[WIP] Invoice WorkFlow

This commit is contained in:
Dario Lodeiros
2019-01-18 17:37:43 +01:00
parent c5d3f7facc
commit 9e1bf442b3
6 changed files with 72 additions and 56 deletions

View File

@@ -228,7 +228,6 @@ class HotelFolio(models.Model):
'Prepaid Warning Days', 'Prepaid Warning Days',
help='Margin in days to create a notice if a payment \ help='Margin in days to create a notice if a payment \
advance has not been recorded') advance has not been recorded')
rooms_char = fields.Char('Rooms', compute='_computed_rooms_char')
segmentation_ids = fields.Many2many('res.partner.category', segmentation_ids = fields.Many2many('res.partner.category',
string='Segmentation') string='Segmentation')
client_order_ref = fields.Char(string='Customer Reference', copy=False) client_order_ref = fields.Char(string='Customer Reference', copy=False)
@@ -253,10 +252,6 @@ class HotelFolio(models.Model):
'amount_total': amount_untaxed + amount_tax, 'amount_total': amount_untaxed + amount_tax,
}) })
def _computed_rooms_char(self):
for record in self:
record.rooms_char = ', '.join(record.mapped('room_lines.room_id.name'))
@api.depends('amount_total', 'payment_ids', 'return_ids') @api.depends('amount_total', 'payment_ids', 'return_ids')
@api.multi @api.multi
def compute_amount(self): def compute_amount(self):

View File

@@ -4,11 +4,21 @@
from odoo import models, fields, api, _ from odoo import models, fields, api, _
from odoo.addons import decimal_precision as dp from odoo.addons import decimal_precision as dp
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
from datetime import date
class HotelReservationLine(models.Model): class HotelReservationLine(models.Model):
_name = "hotel.reservation.line" _name = "hotel.reservation.line"
_order = "date" _order = "date"
@api.multi
def name_get(self):
result = []
for res in self:
date = fields.Date.from_string(res.date)
name = u'%s/%s' % (date.day, date.month)
result.append((res.id, name))
return result
reservation_id = fields.Many2one('hotel.reservation', string='Reservation', reservation_id = fields.Many2one('hotel.reservation', string='Reservation',
ondelete='cascade', required=True, ondelete='cascade', required=True,
copy=False) copy=False)

View File

@@ -201,6 +201,7 @@
</sheet> </sheet>
<div class="oe_chatter"> <div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/> <field name="message_follower_ids" widget="mail_followers"/>
<field name="activity_ids" widget="mail_activity"/>
<field name="message_ids" widget="mail_thread"/> <field name="message_ids" widget="mail_thread"/>
</div> </div>
</form> </form>
@@ -219,7 +220,7 @@
<field name="name"/> <field name="name"/>
<field name="partner_id" select="1"/> <field name="partner_id" select="1"/>
<field name="date_order" select="1"/> <field name="date_order" select="1"/>
<field name="rooms_char" string="Rooms"/> <field name="room_lines" widget="many2many_tags" />
<field name="amount_total" sum="Total amount"/> <field name="amount_total" sum="Total amount"/>
</tree> </tree>
</field> </field>

View File

@@ -195,10 +195,13 @@
<!-- TODO: How to filter to avoid show False (generic) pricelist board when exist a specific pricelist board¿? --> <!-- TODO: How to filter to avoid show False (generic) pricelist board when exist a specific pricelist board¿? -->
<field name="board_service_room_id" domain="[ <field name="board_service_room_id" domain="[
('hotel_room_type_id', '=', room_type_id), ('hotel_room_type_id', '=', room_type_id),
('pricelist_id', 'in', [pricelist_id, False])]" /> ('pricelist_id', 'in', [pricelist_id, False])]"
options="{'no_create': True,'no_open': True}" />
<field name="name"/> <field name="name"/>
<field name="adults"/> <field name="adults"/>
<field name="children"/> <field name="children"/>
<field name="qty_invoiced" />
<field name="qty_to_invoice" />
<field name="room_type_id" on_change="1" options="{'no_create': True,'no_open': True}" <field name="room_type_id" on_change="1" options="{'no_create': True,'no_open': True}"
attrs="{'readonly':[('state','not in',('draft'))]}"/> attrs="{'readonly':[('state','not in',('draft'))]}"/>
<field name="channel_type" attrs="{'required':[('reservation_type','not in',('staff','out'))]}"/> <field name="channel_type" attrs="{'required':[('reservation_type','not in',('staff','out'))]}"/>
@@ -259,7 +262,7 @@
attrs = "{'required': [('per_day','=',True)]}" /> attrs = "{'required': [('per_day','=',True)]}" />
<field name="product_id" <field name="product_id"
domain="[('sale_ok', '=', True)]" domain="[('sale_ok', '=', True)]"
options="{'create': False, 'create_edit': False}" /> options="{'no_create': True,'no_open': True}" />
<field name="name"/> <field name="name"/>
<field name="product_qty" attrs="{'readonly': [('per_day','=',True)]}" force_save="1"/> <field name="product_qty" attrs="{'readonly': [('per_day','=',True)]}" force_save="1"/>
<button type="object" class="oe_stat_button" <button type="object" class="oe_stat_button"
@@ -308,6 +311,7 @@
</sheet> </sheet>
<div class="oe_chatter"> <div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/> <field name="message_follower_ids" widget="mail_followers"/>
<field name="activity_ids" widget="mail_activity"/>
<field name="message_ids" widget="mail_thread"/> <field name="message_ids" widget="mail_thread"/>
</div> </div>
</form> </form>

View File

@@ -61,6 +61,7 @@ class FolioAdvancePaymentInv(models.TransientModel):
advance_payment_method = fields.Selection([ advance_payment_method = fields.Selection([
('all', 'Invoiceable lines (deduct down payments)'), ('all', 'Invoiceable lines (deduct down payments)'),
('one', 'One line (Bill all in one line)'),
('percentage', 'Down payment (percentage)'), ('percentage', 'Down payment (percentage)'),
('fixed', 'Down payment (fixed amount)') ('fixed', 'Down payment (fixed amount)')
], string='What do you want to invoice?', default=_get_advance_payment_method, ], string='What do you want to invoice?', default=_get_advance_payment_method,
@@ -80,7 +81,6 @@ class FolioAdvancePaymentInv(models.TransientModel):
line_ids = fields.One2many('line.advance.inv', line_ids = fields.One2many('line.advance.inv',
'advance_inv_id', 'advance_inv_id',
string="Invoice Lines") string="Invoice Lines")
view_detail = fields.Boolean('View Detail')
#Advance Payment #Advance Payment
product_id = fields.Many2one('product.product', string="Product", product_id = fields.Many2one('product.product', string="Product",
domain=[('type', '=', 'service')], default=_default_product_id) domain=[('type', '=', 'service')], default=_default_product_id)
@@ -185,6 +185,9 @@ class FolioAdvancePaymentInv(models.TransientModel):
invoice = inv_obj.create(inv_data) invoice = inv_obj.create(inv_data)
for line in self.line_ids: for line in self.line_ids:
line.invoice_line_create(invoice.id, line.qty) line.invoice_line_create(invoice.id, line.qty)
elif self.advance_payment_method == 'all':
pass
#Group lines by tax_ids
else: else:
# Create deposit product if necessary # Create deposit product if necessary
if not self.product_id: if not self.product_id:
@@ -282,50 +285,29 @@ class FolioAdvancePaymentInv(models.TransientModel):
extra_price += service.price_unit * \ extra_price += service.price_unit * \
service.service_line_ids.filtered( service.service_line_ids.filtered(
lambda x: x.date == day.date).day_qty lambda x: x.date == day.date).day_qty
#group_key: if group by reservation, We no need group by room_type
group_key = (reservation.id, reservation.room_type_id.id, day.price + extra_price, day.discount) group_key = (reservation.id, reservation.room_type_id.id, day.price + extra_price, day.discount)
date = fields.Date.from_string(day.date) date = fields.Date.from_string(day.date)
if group_key in invoice_lines: description = folio.name + ' ' + reservation.room_type_id.name + ' (' + \
invoice_lines[group_key][('qty')] += 1 reservation.board_service_room_id.hotel_board_service_id.name + ')' \
if date == fields.Date.from_string( if board_service else folio.name + ' ' + reservation.room_type_id.name
invoice_lines[group_key][('date_to')]) + timedelta(days=1): invoice_lines[group_key] = {
desc = invoice_lines[group_key][('description')] 'description' : description,
invoice_lines[group_key][('description')] = \ 'reservation_id': reservation.id,
desc.replace(desc[desc.rfind(" - "):], ' - ' + \ 'room_type_id': reservation.room_type_id,
(date + timedelta(days=1)).strftime(DEFAULT_SERVER_DATE_FORMAT) + ')') 'product_id': self.env['product.product'].browse(
else: reservation.room_type_id.product_id.id),
invoice_lines[group_key][('description')] += \ 'discount': day.discount,
' (' + date.strftime(DEFAULT_SERVER_DATE_FORMAT) + \ 'price_unit': day.price + extra_price,
' - ' + (date + timedelta(days=1)).strftime( 'reservation_line_ids': []
DEFAULT_SERVER_DATE_FORMAT) + \
')'
invoice_lines[group_key][('date_to')] = day.date
else:
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,
'reservation_line_ids': []
} }
invoice_lines[group_key][('reservation_line_ids')].append((4,day.id)) invoice_lines[group_key][('reservation_line_ids')].append((4,day.id))
for group_key in invoice_lines: for group_key in invoice_lines:
vals.append((0, False, invoice_lines[group_key])) vals.append((0, False, invoice_lines[group_key]))
self.line_ids = vals self.line_ids = vals
self.line_ids.onchange_reservation_line_ids()
@api.onchange('view_detail', 'folio_ids') @api.onchange('folio_ids')
def onchange_folio_ids(self): def onchange_folio_ids(self):
vals = [] vals = []
folios = self.folio_ids folios = self.folio_ids
@@ -388,11 +370,13 @@ class LineAdvancePaymentInv(models.TransientModel):
qty = fields.Integer('Quantity') qty = fields.Integer('Quantity')
price_unit = fields.Float('Price') price_unit = fields.Float('Price')
advance_inv_id = fields.Many2one('folio.advance.payment.inv') advance_inv_id = fields.Many2one('folio.advance.payment.inv')
price_room = fields.Float(compute='_compute_price_room')
discount = fields.Float( discount = fields.Float(
string='Discount (%)', string='Discount (%)',
digits=dp.get_precision('Discount'), default=0.0) digits=dp.get_precision('Discount'), default=0.0)
to_invoice = fields.Boolean('To Invoice') to_invoice = fields.Boolean('To Invoice')
description = fields.Text('Description') description = fields.Text('Description')
description_dates = fields.Text('Range')
reservation_id = fields.Many2one('hotel.reservation') reservation_id = fields.Many2one('hotel.reservation')
service_id = fields.Many2one('hotel.service') service_id = fields.Many2one('hotel.service')
folio_id = fields.Many2one('hotel.folio', compute='_compute_folio_id') folio_id = fields.Many2one('hotel.folio', compute='_compute_folio_id')
@@ -400,11 +384,26 @@ class LineAdvancePaymentInv(models.TransientModel):
'hotel.reservation.line', 'hotel.reservation.line',
string='Reservation Lines') string='Reservation Lines')
def _compute_price_room(self):
for record in self:
record.price_room = self.reservation_line_ids[0].price
def _compute_folio_id(self): def _compute_folio_id(self):
for record in self: for record in self:
origin = record.reservation_id if record.reservation_id.id else record.service_id origin = record.reservation_id if record.reservation_id.id else record.service_id
record.folio_id = origin.folio_id record.folio_id = origin.folio_id
@api.onchange('reservation_line_ids')
def onchange_reservation_line_ids(self):
for record in self:
if record.reservation_id:
if not record.reservation_line_ids:
raise UserError(_('If you want drop the line, use the trash icon'))
record.qty = len(record.reservation_line_ids)
record.description_dates = record.reservation_line_ids[0].date + ' - ' + \
((fields.Date.from_string(record.reservation_line_ids[-1].date)) + \
timedelta(days=1)).strftime(DEFAULT_SERVER_DATE_FORMAT)
@api.multi @api.multi
def invoice_line_create(self, invoice_id, qty): def invoice_line_create(self, invoice_id, qty):
""" Create an invoice line. """ Create an invoice line.
@@ -446,6 +445,7 @@ class LineAdvancePaymentInv(models.TransientModel):
'reservation_ids': [(6, 0, [origin.id])], 'reservation_ids': [(6, 0, [origin.id])],
'reservation_line_ids': [(6, 0, line.reservation_line_ids.ids)] 'reservation_line_ids': [(6, 0, line.reservation_line_ids.ids)]
}) })
import wdb; wdb.set_trace()
elif line.service_id: elif line.service_id:
vals.update({ vals.update({
'invoice_id': invoice_id, 'invoice_id': invoice_id,

View File

@@ -27,6 +27,9 @@
domain="[('type_tax_use','=','sale')]" domain="[('type_tax_use','=','sale')]"
attrs="{'invisible': ['|', ('advance_payment_method', 'not in', ('fixed', 'percentage')), ('product_id', '!=', False)]}"/> attrs="{'invisible': ['|', ('advance_payment_method', 'not in', ('fixed', 'percentage')), ('product_id', '!=', False)]}"/>
</group> </group>
<group>
<field name="group_folios" string="Add Folios"/>
</group>
<field name="folio_ids" attrs="{'invisible': [('group_folios', '=', False)]}"> <field name="folio_ids" attrs="{'invisible': [('group_folios', '=', False)]}">
<tree string="Folios" editable="bottom" <tree string="Folios" editable="bottom"
decoration-danger="partner_invoice_id != parent.partner_invoice_id"> decoration-danger="partner_invoice_id != parent.partner_invoice_id">
@@ -41,18 +44,21 @@
domain="[('folio_id', 'in', folio_ids)]"/> domain="[('folio_id', 'in', folio_ids)]"/>
</group> </group>
<group> <group>
<field name="group_folios" /> <field name="line_ids">
<field name="view_detail" />
</group>
<group>
<field name="line_ids" attrs="{'invisible': [('view_detail', '=', False)]}"
nolabel="1">
<tree string="Lines" editable="bottom"> <tree string="Lines" editable="bottom">
<field name="product_id" invisible="1"/> <field name="product_id" invisible="1" />
<field name="reservation_line_ids" /> <field name="price_room" invisible="1" />
<field name="reservation_id" /> <field name="reservation_id"
<field name="service_id" /> options="{'no_create': True,'no_open': True}"/>
<field name="service_id"
options="{'no_create': True,'no_open': True}"/>
<field name="description" /> <field name="description" />
<field name="description_dates" readonly="1" />
<field name="reservation_line_ids"
widget="many2many_tags" string="Nights"
domain="[('reservation_id','=',reservation_id),
('price','=', price_room)]"
options="{'no_create': True,'no_open': True}"/>
<field name="qty" /> <field name="qty" />
<field name="discount" /> <field name="discount" />
<field name="price_unit" /> <field name="price_unit" />