mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[ADD] WorkFlow Partner checkin and duplicated
This commit is contained in:
@@ -16,6 +16,3 @@ class HotelBoardServiceRoomTypeLine(models.Model):
|
|||||||
product_id = fields.Many2one(
|
product_id = fields.Many2one(
|
||||||
'product.product', 'Product', required=True, readonly=True)
|
'product.product', 'Product', required=True, readonly=True)
|
||||||
amount = fields.Float('Amount', digits=dp.get_precision('Product Price'), default=0.0)
|
amount = fields.Float('Amount', digits=dp.get_precision('Product Price'), default=0.0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,25 @@ class HotelCheckinPartner(models.Model):
|
|||||||
return reservation
|
return reservation
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _default_partner_id(self):
|
||||||
|
if 'reservation_id' in self.env.context:
|
||||||
|
reservation = self.env['hotel.reservation'].browse([
|
||||||
|
self.env.context['reservation_id']
|
||||||
|
])
|
||||||
|
partner_ids = []
|
||||||
|
if reservation.folio_id:
|
||||||
|
for room in reservation.folio_id.room_lines:
|
||||||
|
partner_ids.append(room.mapped(
|
||||||
|
'checkin_partner_ids.partner_id.id'))
|
||||||
|
if 'checkin_partner_ids' in self.env.context:
|
||||||
|
for checkin in self.env.context['checkin_partner_ids']:
|
||||||
|
if checkin[0] == 0:
|
||||||
|
partner_ids.append(checkin[2].get('partner_id'))
|
||||||
|
if self._context.get('include_customer') and reservation.partner_id.id \
|
||||||
|
not in partner_ids and not reservation.partner_id.is_company:
|
||||||
|
return reservation.partner_id
|
||||||
|
return False
|
||||||
|
|
||||||
def _default_folio_id(self):
|
def _default_folio_id(self):
|
||||||
if 'folio_id' in self.env.context:
|
if 'folio_id' in self.env.context:
|
||||||
folio = self.env['hotel.folio'].browse([
|
folio = self.env['hotel.folio'].browse([
|
||||||
@@ -48,16 +67,6 @@ class HotelCheckinPartner(models.Model):
|
|||||||
return reservation.checkout
|
return reservation.checkout
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _default_partner_id(self):
|
|
||||||
if 'reservation_id' in self.env.context:
|
|
||||||
reservation = self.env['hotel.reservation'].browse([
|
|
||||||
self.env.context['reservation_id']
|
|
||||||
])
|
|
||||||
if reservation.partner_id.id not in reservation.mapped(
|
|
||||||
'checkin_partner_ids.partner_id.id'):
|
|
||||||
return reservation.partner_id
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _default_to_enter(self):
|
def _default_to_enter(self):
|
||||||
tz_hotel = self.env['ir.default'].sudo().get(
|
tz_hotel = self.env['ir.default'].sudo().get(
|
||||||
'res.config.settings', 'tz_hotel')
|
'res.config.settings', 'tz_hotel')
|
||||||
@@ -122,25 +131,13 @@ class HotelCheckinPartner(models.Model):
|
|||||||
@api.onchange('partner_id')
|
@api.onchange('partner_id')
|
||||||
def _check_partner_id(self):
|
def _check_partner_id(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
checkins = self.env['hotel.checkin.partner'].search([
|
if record.partner_id:
|
||||||
('id', '!=', record.id),
|
indoor_partner_ids = record.reservation_id.checkin_partner_ids.\
|
||||||
('reservation_id', '=', record.reservation_id.id)
|
filtered(lambda r: r.id != record.id).mapped('partner_id.id')
|
||||||
])
|
if indoor_partner_ids.count(record.partner_id.id) > 1:
|
||||||
if record.partner_id.id in checkins.mapped('partner_id.id'):
|
raise models.ValidationError(
|
||||||
raise models.ValidationError(
|
_('This guest is already registered in the room'))
|
||||||
_('This guest is already registered in the room'))
|
record.partner_id = None
|
||||||
|
|
||||||
@api.multi
|
|
||||||
@api.constrains('partner_id')
|
|
||||||
def _check_partner_id(self):
|
|
||||||
for record in self:
|
|
||||||
checkins = self.env['hotel.checkin.partner'].search([
|
|
||||||
('id', '!=', record.id),
|
|
||||||
('reservation_id', '=', record.reservation_id.id)
|
|
||||||
])
|
|
||||||
if record.partner_id.id in checkins.mapped('partner_id.id'):
|
|
||||||
raise models.ValidationError(
|
|
||||||
_('This guest is already registered in the room'))
|
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def action_on_board(self):
|
def action_on_board(self):
|
||||||
@@ -152,4 +149,6 @@ class HotelCheckinPartner(models.Model):
|
|||||||
@api.multi
|
@api.multi
|
||||||
def action_done(self):
|
def action_done(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
record.state = 'done'
|
if record.state == 'booking':
|
||||||
|
record.state = 'done'
|
||||||
|
return True
|
||||||
|
|||||||
@@ -3,10 +3,8 @@
|
|||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
import time
|
import time
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from lxml import etree
|
|
||||||
from odoo.exceptions import UserError, ValidationError
|
from odoo.exceptions import UserError, ValidationError
|
||||||
from odoo.tools import (
|
from odoo.tools import (
|
||||||
misc,
|
|
||||||
float_is_zero,
|
float_is_zero,
|
||||||
float_compare,
|
float_compare,
|
||||||
DEFAULT_SERVER_DATE_FORMAT,
|
DEFAULT_SERVER_DATE_FORMAT,
|
||||||
@@ -245,11 +243,17 @@ class HotelReservation(models.Model):
|
|||||||
# Non-stored field hotel.reservation.checkin_partner_count cannot be searched
|
# Non-stored field hotel.reservation.checkin_partner_count cannot be searched
|
||||||
# searching on a computed field can also be enabled by setting the search parameter.
|
# searching on a computed field can also be enabled by setting the search parameter.
|
||||||
# The value is a method name returning a Domains
|
# The value is a method name returning a Domains
|
||||||
checkin_partner_count = fields.Integer('Checkin counter',
|
checkin_partner_count = fields.Integer(
|
||||||
compute='_compute_checkin_partner_count')
|
'Checkin counter',
|
||||||
checkin_partner_pending_count = fields.Integer('Checkin Pending Num',
|
compute='_compute_checkin_partner_count')
|
||||||
compute='_compute_checkin_partner_count',
|
checkin_partner_pending_count = fields.Integer(
|
||||||
search='_search_checkin_partner_pending')
|
'Checkin Pending Num',
|
||||||
|
compute='_compute_checkin_partner_count',
|
||||||
|
search='_search_checkin_partner_pending')
|
||||||
|
customer_sleep_here = fields.Boolean(default=True,
|
||||||
|
string="Include customer",
|
||||||
|
help="Indicates if the customer \
|
||||||
|
sleeps in this room")
|
||||||
# check_rooms = fields.Boolean('Check Rooms')
|
# check_rooms = fields.Boolean('Check Rooms')
|
||||||
splitted = fields.Boolean('Splitted', default=False)
|
splitted = fields.Boolean('Splitted', default=False)
|
||||||
parent_reservation = fields.Many2one('hotel.reservation',
|
parent_reservation = fields.Many2one('hotel.reservation',
|
||||||
@@ -621,7 +625,6 @@ class HotelReservation(models.Model):
|
|||||||
"""
|
"""
|
||||||
ONCHANGES ----------------------------------------------------------
|
ONCHANGES ----------------------------------------------------------
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@api.onchange('adults', 'room_id')
|
@api.onchange('adults', 'room_id')
|
||||||
def onchange_room_id(self):
|
def onchange_room_id(self):
|
||||||
if self.room_id:
|
if self.room_id:
|
||||||
@@ -633,7 +636,6 @@ class HotelReservation(models.Model):
|
|||||||
_('%s people do not fit in this room! ;)') % (self.adults))
|
_('%s people do not fit in this room! ;)') % (self.adults))
|
||||||
if self.adults == 0:
|
if self.adults == 0:
|
||||||
write_vals.update({'adults': self.room_id.capacity})
|
write_vals.update({'adults': self.room_id.capacity})
|
||||||
#Si el registro no existe, modificar room_type aunque ya esté establecido
|
|
||||||
if not self.room_type_id:
|
if not self.room_type_id:
|
||||||
write_vals.update({'room_type_id': self.room_id.room_type_id.id})
|
write_vals.update({'room_type_id': self.room_id.room_type_id.id})
|
||||||
self.update(write_vals)
|
self.update(write_vals)
|
||||||
@@ -1195,7 +1197,8 @@ class HotelReservation(models.Model):
|
|||||||
for record in self:
|
for record in self:
|
||||||
record.state = 'done'
|
record.state = 'done'
|
||||||
if record.checkin_partner_ids:
|
if record.checkin_partner_ids:
|
||||||
record.checkin_partner_ids.action_done()
|
record.checkin_partner_ids.filtered(
|
||||||
|
lambda check: check.state == 'booking').action_done()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
|
|||||||
@@ -1,15 +1,9 @@
|
|||||||
# Copyright 2017 Alexandre Díaz
|
# Copyright 2017 Alexandre Díaz
|
||||||
# Copyright 2017 Dario Lodeiros
|
# Copyright 2017 Dario Lodeiros
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
import functools
|
from odoo import api, fields, models
|
||||||
import itertools
|
|
||||||
import logging
|
|
||||||
import psycopg2
|
|
||||||
|
|
||||||
from odoo import api, fields, models, _
|
|
||||||
from odoo.osv.expression import get_unaccent_wrapper
|
from odoo.osv.expression import get_unaccent_wrapper
|
||||||
from odoo.exceptions import ValidationError, UserError
|
import logging
|
||||||
from odoo.tools import mute_logger
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -34,37 +28,7 @@ class ResPartner(models.Model):
|
|||||||
compute='_compute_reservations_count')
|
compute='_compute_reservations_count')
|
||||||
folios_count = fields.Integer('Folios', compute='_compute_folios_count')
|
folios_count = fields.Integer('Folios', compute='_compute_folios_count')
|
||||||
unconfirmed = fields.Boolean('Unconfirmed', default=True)
|
unconfirmed = fields.Boolean('Unconfirmed', default=True)
|
||||||
|
main_partner_id = fields.Many2one('res.partner')
|
||||||
def _get_fk_on(self, table):
|
|
||||||
""" return a list of many2one relation with the given table.
|
|
||||||
:param table : the name of the sql table to return relations
|
|
||||||
:returns a list of tuple 'table name', 'column name'.
|
|
||||||
"""
|
|
||||||
query = """
|
|
||||||
SELECT cl1.relname as table, att1.attname as column
|
|
||||||
FROM pg_constraint as con, pg_class as cl1, pg_class as cl2, pg_attribute as att1, pg_attribute as att2
|
|
||||||
WHERE con.conrelid = cl1.oid
|
|
||||||
AND con.confrelid = cl2.oid
|
|
||||||
AND array_lower(con.conkey, 1) = 1
|
|
||||||
AND con.conkey[1] = att1.attnum
|
|
||||||
AND att1.attrelid = cl1.oid
|
|
||||||
AND cl2.relname = %s
|
|
||||||
AND att2.attname = 'id'
|
|
||||||
AND array_lower(con.confkey, 1) = 1
|
|
||||||
AND con.confkey[1] = att2.attnum
|
|
||||||
AND att2.attrelid = cl2.oid
|
|
||||||
AND con.contype = 'f'
|
|
||||||
"""
|
|
||||||
self._cr.execute(query, (table,))
|
|
||||||
return self._cr.fetchall()
|
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def write(self, vals):
|
|
||||||
res = super(ResPartner, self).write(vals)
|
|
||||||
for i, record in enumerate(self):
|
|
||||||
if record.unconfirmed is True:
|
|
||||||
res = self.env['res.partner']._check_duplicated_partner(record)
|
|
||||||
return res
|
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def name_search(self, name, args=None, operator='ilike', limit=100):
|
def name_search(self, name, args=None, operator='ilike', limit=100):
|
||||||
@@ -111,247 +75,3 @@ class ResPartner(models.Model):
|
|||||||
if partner_ids:
|
if partner_ids:
|
||||||
result += self.browse(partner_ids).name_get()
|
result += self.browse(partner_ids).name_get()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _check_duplicated_partner(self, partner):
|
|
||||||
duplicated_ids = self.env['res.partner']._get_duplicated_ids(partner)
|
|
||||||
if len(duplicated_ids) > 1:
|
|
||||||
partners = self.env['res.partner'].browse(duplicated_ids)
|
|
||||||
action = self.env.ref('crm.action_partner_deduplicate').read()[0]
|
|
||||||
if partners:
|
|
||||||
action['context'] = {
|
|
||||||
'default_partner_ids': partners.ids,
|
|
||||||
'default_dst_partner_id': partner.id,
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
action = {'type': 'ir.actions.act_window_close'}
|
|
||||||
return action
|
|
||||||
# return partner._merge(partners._ids)
|
|
||||||
return partner
|
|
||||||
|
|
||||||
def _merge_fields(self):
|
|
||||||
duplicated_fields = ['vat']
|
|
||||||
return duplicated_fields
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _get_duplicated_ids(self, partner):
|
|
||||||
partner_ids = []
|
|
||||||
for field in self.env['res.partner']._merge_fields():
|
|
||||||
if partner[field]:
|
|
||||||
partner_ids += self.env['res.partner'].search([(field, '=', partner[field])]).ids
|
|
||||||
return partner_ids
|
|
||||||
|
|
||||||
def _merge(self, partner_ids, dst_partner=None):
|
|
||||||
""" private implementation of merge partner
|
|
||||||
:param partner_ids : ids of partner to merge
|
|
||||||
:param dst_partner : record of destination res.partner
|
|
||||||
"""
|
|
||||||
partner = self.env['res.partner']
|
|
||||||
partner_ids = partner.browse(partner_ids).exists()
|
|
||||||
if len(partner_ids) < 2:
|
|
||||||
return
|
|
||||||
|
|
||||||
if len(partner_ids) > 3:
|
|
||||||
raise UserError(_("For safety reasons, you cannot merge more than 3 contacts together. You can re-open the wizard several times if needed."))
|
|
||||||
|
|
||||||
# check if the list of partners to merge contains child/parent relation
|
|
||||||
child_ids = self.env['res.partner']
|
|
||||||
for partner_id in partner_ids:
|
|
||||||
child_ids |= partner.search([('id', 'child_of', [partner_id.id])]) - partner_id
|
|
||||||
if partner_ids & child_ids:
|
|
||||||
raise UserError(_("You cannot merge a contact with one of his parent."))
|
|
||||||
|
|
||||||
# remove dst_partner from partners to merge
|
|
||||||
if dst_partner and dst_partner in partner_ids:
|
|
||||||
src_partners = partner_ids - dst_partner
|
|
||||||
else:
|
|
||||||
ordered_partners = self._get_ordered_partner(partner_ids.ids)
|
|
||||||
dst_partner = ordered_partners[-1]
|
|
||||||
src_partners = ordered_partners[:-1]
|
|
||||||
_logger.info("dst_partner: %s", dst_partner.id)
|
|
||||||
|
|
||||||
# call sub methods to do the merge
|
|
||||||
self._update_foreign_keys(src_partners, dst_partner)
|
|
||||||
self._update_reference_fields(src_partners, dst_partner)
|
|
||||||
self._update_values(src_partners, dst_partner)
|
|
||||||
|
|
||||||
_logger.info('(uid = %s) merged the partners %r with %s', self._uid, src_partners.ids, dst_partner.id)
|
|
||||||
dst_partner.message_post(body='%s %s' % (_("Merged with the following partners:"), ", ".join('%s <%s> (ID %s)' % (p.name, p.email or 'n/a', p.id) for p in src_partners)))
|
|
||||||
|
|
||||||
# delete source partner, since they are merged
|
|
||||||
#~ src_partners.update({'active':False})
|
|
||||||
src_partners.unlink()
|
|
||||||
return dst_partner
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _update_foreign_keys(self, src_partners, dst_partner):
|
|
||||||
""" Update all foreign key from the src_partner to dst_partner. All many2one fields will be updated.
|
|
||||||
:param src_partners : merge source res.partner recordset (does not include destination one)
|
|
||||||
:param dst_partner : record of destination res.partner
|
|
||||||
"""
|
|
||||||
_logger.debug('_update_foreign_keys for dst_partner: %s for src_partners: %s', dst_partner.id, str(src_partners.ids))
|
|
||||||
|
|
||||||
# find the many2one relation to a partner
|
|
||||||
partner = self.env['res.partner']
|
|
||||||
relations = self._get_fk_on('res_partner')
|
|
||||||
|
|
||||||
for table, column in relations:
|
|
||||||
# get list of columns of current table (exept the current fk column)
|
|
||||||
query = "SELECT column_name FROM information_schema.columns WHERE table_name LIKE '%s'" % (table)
|
|
||||||
self._cr.execute(query, ())
|
|
||||||
columns = []
|
|
||||||
for data in self._cr.fetchall():
|
|
||||||
if data[0] != column:
|
|
||||||
columns.append(data[0])
|
|
||||||
|
|
||||||
# do the update for the current table/column in SQL
|
|
||||||
query_dic = {
|
|
||||||
'table': table,
|
|
||||||
'column': column,
|
|
||||||
'value': columns[0],
|
|
||||||
}
|
|
||||||
if len(columns) <= 1:
|
|
||||||
# unique key treated
|
|
||||||
query = """
|
|
||||||
UPDATE "%(table)s" as ___tu
|
|
||||||
SET %(column)s = %%s
|
|
||||||
WHERE
|
|
||||||
%(column)s = %%s AND
|
|
||||||
NOT EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM "%(table)s" as ___tw
|
|
||||||
WHERE
|
|
||||||
%(column)s = %%s AND
|
|
||||||
___tu.%(value)s = ___tw.%(value)s
|
|
||||||
)""" % query_dic
|
|
||||||
for partner in src_partners:
|
|
||||||
self._cr.execute(query, (dst_partner.id, partner.id, dst_partner.id))
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
with mute_logger('odoo.sql_db'), self._cr.savepoint():
|
|
||||||
query = 'UPDATE "%(table)s" SET %(column)s = %%s WHERE %(column)s IN %%s' % query_dic
|
|
||||||
self._cr.execute(query, (dst_partner.id, tuple(src_partners.ids),))
|
|
||||||
|
|
||||||
# handle the recursivity with parent relation
|
|
||||||
if column == partner._parent_name and table == 'res_partner':
|
|
||||||
query = """
|
|
||||||
WITH RECURSIVE cycle(id, parent_id) AS (
|
|
||||||
SELECT id, parent_id FROM res_partner
|
|
||||||
UNION
|
|
||||||
SELECT cycle.id, res_partner.parent_id
|
|
||||||
FROM res_partner, cycle
|
|
||||||
WHERE res_partner.id = cycle.parent_id AND
|
|
||||||
cycle.id != cycle.parent_id
|
|
||||||
)
|
|
||||||
SELECT id FROM cycle WHERE id = parent_id AND id = %s
|
|
||||||
"""
|
|
||||||
self._cr.execute(query, (dst_partner.id,))
|
|
||||||
# NOTE JEM : shouldn't we fetch the data ?
|
|
||||||
except psycopg2.Error:
|
|
||||||
# updating fails, most likely due to a violated unique constraint
|
|
||||||
# keeping record with nonexistent partner_id is useless, better delete it
|
|
||||||
query = 'DELETE FROM "%(table)s" WHERE "%(column)s" IN %%s' % query_dic
|
|
||||||
self._cr.execute(query, (tuple(src_partners.ids),))
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _update_reference_fields(self, src_partners, dst_partner):
|
|
||||||
""" Update all reference fields from the src_partner to dst_partner.
|
|
||||||
:param src_partners : merge source res.partner recordset (does not include destination one)
|
|
||||||
:param dst_partner : record of destination res.partner
|
|
||||||
"""
|
|
||||||
_logger.debug('_update_reference_fields for dst_partner: %s for src_partners: %r', dst_partner.id, src_partners.ids)
|
|
||||||
|
|
||||||
def update_records(model, src, field_model='model', field_id='res_id'):
|
|
||||||
Model = self.env[model] if model in self.env else None
|
|
||||||
if Model is None:
|
|
||||||
return
|
|
||||||
records = Model.sudo().search([(field_model, '=', 'res.partner'), (field_id, '=', src.id)])
|
|
||||||
try:
|
|
||||||
with mute_logger('odoo.sql_db'), self._cr.savepoint():
|
|
||||||
return records.sudo().write({field_id: dst_partner.id})
|
|
||||||
except psycopg2.Error:
|
|
||||||
# updating fails, most likely due to a violated unique constraint
|
|
||||||
# keeping record with nonexistent partner_id is useless, better delete it
|
|
||||||
return records.sudo().unlink()
|
|
||||||
|
|
||||||
update_records = functools.partial(update_records)
|
|
||||||
|
|
||||||
for partner in src_partners:
|
|
||||||
update_records('calendar', src=partner, field_model='model_id.model')
|
|
||||||
update_records('ir.attachment', src=partner, field_model='res_model')
|
|
||||||
update_records('mail.followers', src=partner, field_model='res_model')
|
|
||||||
update_records('mail.message', src=partner)
|
|
||||||
update_records('ir.model.data', src=partner)
|
|
||||||
|
|
||||||
records = self.env['ir.model.fields'].search([('ttype', '=', 'reference')])
|
|
||||||
for record in records.sudo():
|
|
||||||
try:
|
|
||||||
Model = self.env[record.model]
|
|
||||||
field = Model._fields[record.name]
|
|
||||||
except KeyError:
|
|
||||||
# unknown model or field => skip
|
|
||||||
continue
|
|
||||||
|
|
||||||
if field.compute is not None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
for partner in src_partners:
|
|
||||||
records_ref = Model.sudo().search([(record.name, '=', 'res.partner,%d' % partner.id)])
|
|
||||||
values = {
|
|
||||||
record.name: 'res.partner,%d' % dst_partner.id,
|
|
||||||
}
|
|
||||||
records_ref.sudo().write(values)
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _update_values(self, src_partners, dst_partner):
|
|
||||||
""" Update values of dst_partner with the ones from the src_partners.
|
|
||||||
:param src_partners : recordset of source res.partner
|
|
||||||
:param dst_partner : record of destination res.partner
|
|
||||||
"""
|
|
||||||
_logger.debug('_update_values for dst_partner: %s for src_partners: %r', dst_partner.id, src_partners.ids)
|
|
||||||
|
|
||||||
model_fields = dst_partner.fields_get().keys()
|
|
||||||
|
|
||||||
def write_serializer(item):
|
|
||||||
if isinstance(item, models.BaseModel):
|
|
||||||
return item.id
|
|
||||||
else:
|
|
||||||
return item
|
|
||||||
# get all fields that are not computed or x2many
|
|
||||||
values = dict()
|
|
||||||
for column in model_fields:
|
|
||||||
field = dst_partner._fields[column]
|
|
||||||
if field.type not in ('many2many', 'one2many') and field.compute is None:
|
|
||||||
for item in itertools.chain(src_partners, [dst_partner]):
|
|
||||||
if item[column]:
|
|
||||||
values[column] = write_serializer(item[column])
|
|
||||||
# remove fields that can not be updated (id and parent_id)
|
|
||||||
values.pop('id', None)
|
|
||||||
parent_id = values.pop('parent_id', None)
|
|
||||||
dst_partner.write(values)
|
|
||||||
# try to update the parent_id
|
|
||||||
if parent_id and parent_id != dst_partner.id:
|
|
||||||
try:
|
|
||||||
dst_partner.write({'parent_id': parent_id})
|
|
||||||
except ValidationError:
|
|
||||||
_logger.info('Skip recursive partner hierarchies for parent_id %s of partner: %s', parent_id, dst_partner.id)
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _get_ordered_partner(self, partner_ids):
|
|
||||||
""" Helper : returns a `res.partner` recordset ordered by create_date/active fields
|
|
||||||
:param partner_ids : list of partner ids to sort
|
|
||||||
"""
|
|
||||||
return self.env['res.partner'].browse(partner_ids).sorted(
|
|
||||||
key=lambda p: (p.active, (p.create_date or '')),
|
|
||||||
reverse=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _compute_models(self):
|
|
||||||
""" Compute the different models needed by the system if you want to exclude some partners. """
|
|
||||||
model_mapping = {}
|
|
||||||
if self.exclude_contact:
|
|
||||||
model_mapping['res.users'] = 'partner_id'
|
|
||||||
if 'account.move.line' in self.env and self.exclude_journal_item:
|
|
||||||
model_mapping['account.move.line'] = 'partner_id'
|
|
||||||
return model_mapping
|
|
||||||
|
|||||||
@@ -22,7 +22,8 @@
|
|||||||
<sheet>
|
<sheet>
|
||||||
<group name="group_top">
|
<group name="group_top">
|
||||||
<group name="group_left">
|
<group name="group_left">
|
||||||
<field name="partner_id" required="True"/>
|
<field name="partner_id" required="True"
|
||||||
|
domain="[('is_company','=', False)]"/>
|
||||||
<field name="enter_date"/>
|
<field name="enter_date"/>
|
||||||
<field name="exit_date"/>
|
<field name="exit_date"/>
|
||||||
</group>
|
</group>
|
||||||
@@ -42,25 +43,25 @@
|
|||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree editable="bottom" create="1"
|
<tree editable="bottom" create="1"
|
||||||
decoration-danger="state == 'draft'"
|
decoration-danger="state == 'draft'"
|
||||||
decoration-muted="state == 'cancelled' or state =='done'"
|
decoration-info="state == 'done'"
|
||||||
|
decoration-muted="state == 'cancelled'"
|
||||||
decoration-success="state == 'booking'">
|
decoration-success="state == 'booking'">
|
||||||
<button type="object" class="oe_stat_button"
|
<button type="object" class="oe_read_only oe_stat_button"
|
||||||
icon="fa fa-2x fa-check-circle"
|
icon="fa fa-2x fa-check-circle"
|
||||||
name="action_on_board"
|
name="action_on_board"
|
||||||
attrs="{'invisible':['|',
|
|
||||||
('state','not in', ['draft']),
|
|
||||||
('id','=',False)]}"
|
|
||||||
help="Get in"
|
help="Get in"
|
||||||
|
attrs="{'invisible': [('state','!=','draft')]}"
|
||||||
/>
|
/>
|
||||||
<field name="auto_booking" attrs="{'invisible':[('id','!=',False)]}" />
|
<field name="auto_booking" invisible="1" />
|
||||||
<field name="partner_id" required="True"/>
|
<field name="partner_id" required="True"
|
||||||
<field name="mobile"/>
|
domain="[('is_company','=', False)]"/>
|
||||||
|
<field name="mobile" />
|
||||||
<field name="email"/>
|
<field name="email"/>
|
||||||
<field name="enter_date"/>
|
<field name="enter_date"/>
|
||||||
<field name="exit_date"/>
|
<field name="exit_date"/>
|
||||||
<field name="reservation_id" invisible="1"/>
|
<field name="reservation_id" invisible="1"/>
|
||||||
<field name="folio_id" force_save="1" invisible="1"/>
|
<field name="folio_id" force_save="1" invisible="1"/>
|
||||||
<field name="state"/>
|
<field name="state" invisible="1"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
@@ -80,7 +81,8 @@
|
|||||||
attrs="{'invisible':[('state','not in', ['draft'])]}"
|
attrs="{'invisible':[('state','not in', ['draft'])]}"
|
||||||
help="Get in"
|
help="Get in"
|
||||||
/>
|
/>
|
||||||
<field name="partner_id" required="True"/>
|
<field name="partner_id" required="True"
|
||||||
|
domain="[('is_company','=', False)]"/>
|
||||||
<field name="mobile"/>
|
<field name="mobile"/>
|
||||||
<field name="email"/>
|
<field name="email"/>
|
||||||
<field name="enter_date"/>
|
<field name="enter_date"/>
|
||||||
|
|||||||
@@ -296,12 +296,15 @@
|
|||||||
<page name="persons" string="Persons"
|
<page name="persons" string="Persons"
|
||||||
attrs="{'invisible': ['|',('reservation_type','in',('out')),
|
attrs="{'invisible': ['|',('reservation_type','in',('out')),
|
||||||
('parent_reservation','!=',False)]}">
|
('parent_reservation','!=',False)]}">
|
||||||
<group>
|
<group colspan="2" cols="6">
|
||||||
<field name="segmentation_ids" widget="many2many_tags" placeholder="Segmentation..."
|
<field name="segmentation_ids" widget="many2many_tags" placeholder="Segmentation..."
|
||||||
options="{'no_create': True,'no_open': True}" />
|
options="{'no_create': True,'no_open': True}" />
|
||||||
|
<field name="customer_sleep_here" />
|
||||||
</group>
|
</group>
|
||||||
<field name="checkin_partner_ids"
|
<field name="checkin_partner_ids"
|
||||||
context="{
|
context="{
|
||||||
|
'checkin_partner_ids': checkin_partner_ids,
|
||||||
|
'include_customer': customer_sleep_here,
|
||||||
'default_reservation_id': id,
|
'default_reservation_id': id,
|
||||||
'reservation_id': id,
|
'reservation_id': id,
|
||||||
'tree_view_ref':'hotel.hotel_checkin_partner_reservation_view_tree',
|
'tree_view_ref':'hotel.hotel_checkin_partner_reservation_view_tree',
|
||||||
|
|||||||
@@ -18,24 +18,20 @@
|
|||||||
<field name="model">res.partner</field>
|
<field name="model">res.partner</field>
|
||||||
<field name="inherit_id" ref="base.view_partner_form" />
|
<field name="inherit_id" ref="base.view_partner_form" />
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
|
||||||
<xpath expr='//div[@name="button_box"]' position='inside'>
|
<xpath expr='//div[@name="button_box"]' position='inside'>
|
||||||
<button class="oe_stat_button" type="action" icon="fa-bed"
|
<button class="oe_stat_button" type="action" icon="fa-bed"
|
||||||
name="%(hotel.hotel_partner_reservations)d"
|
name="%(hotel.hotel_partner_reservations)d"
|
||||||
help="Reservations related with this contact">
|
help="Reservations related with this contact">
|
||||||
<field string="Reservations" name="reservations_count" widget="statinfo"/>
|
<field string="Reservations" name="reservations_count" widget="statinfo"/>
|
||||||
</button>
|
</button>
|
||||||
</xpath>
|
|
||||||
<xpath expr='//div[@name="button_box"]' position='inside'>
|
|
||||||
<button class="oe_stat_button" type="action" icon="fa-file"
|
<button class="oe_stat_button" type="action" icon="fa-file"
|
||||||
name="%(hotel.hotel_partner_folios)d"
|
name="%(hotel.hotel_partner_folios)d"
|
||||||
help="Folios related with this contact">
|
help="Folios related with this contact">
|
||||||
<field string="Folios" name="folios_count" widget="statinfo"/>
|
<field string="Folios" name="folios_count" widget="statinfo"/>
|
||||||
</button>
|
</button>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr='//field[@name="vat"]' position='after'>
|
|
||||||
<field name="unconfirmed" />
|
|
||||||
</xpath>
|
|
||||||
|
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
'partner_contact_gender',
|
'partner_contact_gender',
|
||||||
'partner_contact_birthdate',
|
'partner_contact_birthdate',
|
||||||
'partner_firstname',
|
'partner_firstname',
|
||||||
'web_responsive'
|
'partner_vat_unique',
|
||||||
],
|
],
|
||||||
'data': [
|
'data': [
|
||||||
'data/code.ine.csv',
|
'data/code.ine.csv',
|
||||||
@@ -50,6 +50,7 @@
|
|||||||
'views/inherit_hotel_checkin_partner_views.xml',
|
'views/inherit_hotel_checkin_partner_views.xml',
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'views/inherit_res_partner.xml',
|
'views/inherit_res_partner.xml',
|
||||||
|
'views/inherited_hotel_reservation_views.xml',
|
||||||
'views/report_viajero_document.xml',
|
'views/report_viajero_document.xml',
|
||||||
'views/report_viajero_head.xml',
|
'views/report_viajero_head.xml',
|
||||||
'views/report_viajero_data.xml',
|
'views/report_viajero_data.xml',
|
||||||
|
|||||||
@@ -25,3 +25,4 @@ from . import code_ine
|
|||||||
from . import inherit_res_company
|
from . import inherit_res_company
|
||||||
from . import inherit_res_partner
|
from . import inherit_res_partner
|
||||||
from . import inherit_hotel_checkin_partner
|
from . import inherit_hotel_checkin_partner
|
||||||
|
from . import inherit_hotel_reservation
|
||||||
|
|||||||
@@ -1,39 +1,127 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
# OpenERP, Open Source Management Solution
|
# OpenERP, Open Source Management Solution
|
||||||
# Copyright (C) 2017 Alda Hotels <informatica@aldahotels.com>
|
# Copyright (C) 2017 Alda Hotels <informatica@aldahotels.com>
|
||||||
# Jose Luis Algara <osotranquilo@gmail.com>
|
# Jose Luis Algara <osotranquilo@gmail.com>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
from openerp import models, fields, api, _
|
from openerp import models, fields, api, _
|
||||||
from odoo.osv.expression import get_unaccent_wrapper
|
from odoo.exceptions import UserError
|
||||||
|
import logging
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
class HotelCheckinPartner(models.Model):
|
|
||||||
_inherit = 'hotel.checkin.partner'
|
|
||||||
|
class HotelCheckinPartner(models.Model):
|
||||||
document_type = fields.Selection(related='partner_id.document_type')
|
_inherit = 'hotel.checkin.partner'
|
||||||
document_number = fields.Char(related='partner_id.document_number')
|
|
||||||
document_expedition_date = fields.Date(related='partner_id.document_expedition_date')
|
document_type = fields.Selection(related='partner_id.document_type')
|
||||||
gender = fields.Selection('Gender', related='partner_id.gender')
|
document_number = fields.Char(related='partner_id.document_number')
|
||||||
birthdate_date = fields.Date('Birhdate', related='partner_id.birthdate_date')
|
document_expedition_date = fields.Date(
|
||||||
code_ine_id = fields.Many2one(related="partner_id.code_ine_id")
|
related='partner_id.document_expedition_date')
|
||||||
|
gender = fields.Selection('Gender', related='partner_id.gender')
|
||||||
#TMP_FIX VAT Validation
|
birthdate_date = fields.Date('Birhdate',
|
||||||
@api.constrains("vat")
|
related='partner_id.birthdate_date')
|
||||||
def check_vat(self):
|
code_ine_id = fields.Many2one(related="partner_id.code_ine_id")
|
||||||
return
|
name = fields.Char(related='partner_id.name')
|
||||||
|
lastname = fields.Char(related='partner_id.lastname')
|
||||||
|
firstname = fields.Char(related='partner_id.firstname')
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def create(self, vals):
|
||||||
|
if not vals.get('partner_id'):
|
||||||
|
name = self.env['res.partner']._get_computed_name(
|
||||||
|
vals.get('lastname'),
|
||||||
|
vals.get('firstname')
|
||||||
|
)
|
||||||
|
partner = self.env['res.partner'].create({
|
||||||
|
'name': name,
|
||||||
|
})
|
||||||
|
vals.update({'partner_id': partner.id})
|
||||||
|
vals.pop('firstname')
|
||||||
|
vals.pop('lastname')
|
||||||
|
return super(HotelCheckinPartner, self).create(vals)
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def write(self, vals):
|
||||||
|
for record in self:
|
||||||
|
if not vals.get('partner_id') and not record.partner_id:
|
||||||
|
name = self.env['res.partner']._get_computed_name(
|
||||||
|
vals.get('lastname'),
|
||||||
|
vals.get('firstname')
|
||||||
|
)
|
||||||
|
partner = self.env['res.partner'].create({
|
||||||
|
'name': name,
|
||||||
|
})
|
||||||
|
record.update({'partner_id': partner.id})
|
||||||
|
vals.pop('firstname')
|
||||||
|
vals.pop('lastname')
|
||||||
|
return super(HotelCheckinPartner, self).write(vals)
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def action_on_board(self):
|
||||||
|
self.check_required_fields()
|
||||||
|
return super(HotelCheckinPartner, self).action_on_board()
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def check_dni(self, dni):
|
||||||
|
digits = "TRWAGMYFPDXBNJZSQVHLCKE"
|
||||||
|
dig_ext = "XYZ"
|
||||||
|
reemp_dig_ext = {'X': '0', 'Y': '1', 'Z': '2'}
|
||||||
|
numbers = "1234567890"
|
||||||
|
dni = dni.upper()
|
||||||
|
if len(dni) == 9:
|
||||||
|
dig_control = dni[8]
|
||||||
|
dni = dni[:8]
|
||||||
|
if dni[0] in dig_ext:
|
||||||
|
dni = dni.replace(dni[0], reemp_dig_ext[dni[0]])
|
||||||
|
return len(dni) == len([n for n in dni if n in numbers]) \
|
||||||
|
and digits[int(dni) % 23] == dig_control
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@api.onchange('document_number', 'document_type')
|
||||||
|
def onchange_document_number(self):
|
||||||
|
for record in self:
|
||||||
|
if record.document_type == 'D' and record.document_number:
|
||||||
|
if not record.check_dni(record.document_number):
|
||||||
|
record.document_number = False
|
||||||
|
raise UserError(_('Incorrect DNI'))
|
||||||
|
if not record.partner_id and record.document_number and record.document_type:
|
||||||
|
partner = self.env['res.partner'].search([
|
||||||
|
('document_number', '=', record.document_number),
|
||||||
|
('document_type', '=', record.document_type)
|
||||||
|
], limit=1)
|
||||||
|
if partner:
|
||||||
|
record.update({'partner_id': partner})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def check_required_fields(self):
|
||||||
|
for record in self:
|
||||||
|
missing_fields = []
|
||||||
|
required_fields = ['document_type', 'document_number',
|
||||||
|
'document_expedition_date', 'gender',
|
||||||
|
'birthdate_date', 'code_ine_id',
|
||||||
|
'lastname', 'firstname']
|
||||||
|
for field in required_fields:
|
||||||
|
if not record[field]:
|
||||||
|
missing_fields.append(record._fields[field].string)
|
||||||
|
if missing_fields:
|
||||||
|
raise UserError(
|
||||||
|
_('To perform the checkin the following data are missing:\
|
||||||
|
%s') % (', '.join(missing_fields)))
|
||||||
|
|||||||
35
hotel_l10n_es/models/inherit_hotel_reservation.py
Normal file
35
hotel_l10n_es/models/inherit_hotel_reservation.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# Copyright (C) 2017 Darío Lodeiros <dariodafoz@gmail.com>
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
from openerp import models, api
|
||||||
|
|
||||||
|
|
||||||
|
class HotelReservation(models.Model):
|
||||||
|
_inherit = 'hotel.reservation'
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def print_all_checkins(self):
|
||||||
|
checkins = self.env['hotel.checkin.partner']
|
||||||
|
for record in self:
|
||||||
|
checkins += record.checkin_partner_ids.filtered(
|
||||||
|
lambda s: s.state in ('booking', 'done'))
|
||||||
|
if checkins:
|
||||||
|
return self.env.ref('hotel_l10n_es.action_report_viajero').\
|
||||||
|
report_action(checkins)
|
||||||
@@ -1,100 +1,415 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
# OpenERP, Open Source Management Solution
|
# OpenERP, Open Source Management Solution
|
||||||
# Copyright (C) 2017 Alda Hotels <informatica@aldahotels.com>
|
# Copyright (C) 2017 Alda Hotels <informatica@aldahotels.com>
|
||||||
# Jose Luis Algara <osotranquilo@gmail.com>
|
# Jose Luis Algara <osotranquilo@gmail.com>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
from openerp import models, fields, api, _
|
import functools
|
||||||
from odoo.osv.expression import get_unaccent_wrapper
|
import itertools
|
||||||
|
import logging
|
||||||
|
import psycopg2
|
||||||
class ResPartner(models.Model):
|
|
||||||
_inherit = 'res.partner'
|
from odoo import api, fields, models, _
|
||||||
|
from odoo.osv.expression import get_unaccent_wrapper
|
||||||
document_type = fields.Selection([
|
from odoo.exceptions import ValidationError, UserError
|
||||||
('D', 'DNI'),
|
from odoo.tools import mute_logger
|
||||||
('P', 'Pasaporte'),
|
_logger = logging.getLogger(__name__)
|
||||||
('C', 'Permiso de Conducir'),
|
|
||||||
('I', 'Carta o Doc. de Identidad'),
|
|
||||||
('N', 'Permiso Residencia Español'),
|
class ResPartner(models.Model):
|
||||||
('X', 'Permiso Residencia Europeo')],
|
_inherit = 'res.partner'
|
||||||
help=_('Select a valid document type'),
|
|
||||||
default='D',
|
document_type = fields.Selection([
|
||||||
string='Doc. type',
|
('D', 'DNI'),
|
||||||
)
|
('P', 'Pasaporte'),
|
||||||
document_number = fields.Char('Document number')
|
('C', 'Permiso de Conducir'),
|
||||||
document_expedition_date = fields.Date('Document expedition date')
|
('I', 'Carta o Doc. de Identidad'),
|
||||||
|
('N', 'Permiso Residencia Español'),
|
||||||
code_ine_id = fields.Many2one('code.ine',
|
('X', 'Permiso Residencia Europeo')],
|
||||||
help=_('Country or province of origin. Used for INE statistics.'))
|
help=_('Select a valid document type'),
|
||||||
|
default='D',
|
||||||
@api.model
|
string='Doc. type',
|
||||||
def name_search(self, name, args=None, operator='ilike', limit=100):
|
)
|
||||||
result = super(ResPartner, self).name_search(name, args=None,
|
document_number = fields.Char('Document number', index=True)
|
||||||
operator='ilike',
|
document_expedition_date = fields.Date('Document expedition date')
|
||||||
limit=100)
|
code_ine_id = fields.Many2one('code.ine',
|
||||||
if args is None:
|
help=_('Country or province of origin. Used for INE statistics.'))
|
||||||
args = []
|
unconfirmed = fields.Boolean('Unconfirmed', default=True)
|
||||||
if name and operator in ('=', 'ilike', '=ilike', 'like', '=like'):
|
main_partner_id = fields.Many2one('res.partner')
|
||||||
self.check_access_rights('read')
|
|
||||||
where_query = self._where_calc(args)
|
@api.model
|
||||||
self._apply_ir_rules(where_query, 'read')
|
def name_search(self, name, args=None, operator='ilike', limit=100):
|
||||||
from_clause, where_clause, where_clause_params = where_query.get_sql()
|
result = super(ResPartner, self).name_search(name, args=None,
|
||||||
where_str = where_clause and (" WHERE %s AND " % where_clause) or ' WHERE '
|
operator='ilike',
|
||||||
|
limit=100)
|
||||||
# search on the name of the contacts and of its company
|
if args is None:
|
||||||
search_name = name
|
args = []
|
||||||
if operator in ('ilike', 'like'):
|
if name and operator in ('=', 'ilike', '=ilike', 'like', '=like'):
|
||||||
search_name = '%%%s%%' % name
|
self.check_access_rights('read')
|
||||||
if operator in ('=ilike', '=like'):
|
where_query = self._where_calc(args)
|
||||||
operator = operator[1:]
|
self._apply_ir_rules(where_query, 'read')
|
||||||
|
from_clause, where_clause, where_clause_params = where_query.get_sql()
|
||||||
unaccent = get_unaccent_wrapper(self.env.cr)
|
where_str = where_clause and (" WHERE %s AND " % where_clause) or ' WHERE '
|
||||||
|
|
||||||
query = """SELECT id
|
# search on the name of the contacts and of its company
|
||||||
FROM res_partner
|
search_name = name
|
||||||
{where} ({document_number} {operator} {percent})
|
if operator in ('ilike', 'like'):
|
||||||
ORDER BY {display_name} {operator} {percent} desc,
|
search_name = '%%%s%%' % name
|
||||||
{display_name}
|
if operator in ('=ilike', '=like'):
|
||||||
""".format(where=where_str,
|
operator = operator[1:]
|
||||||
operator=operator,
|
|
||||||
document_number=unaccent('document_number'),
|
unaccent = get_unaccent_wrapper(self.env.cr)
|
||||||
display_name=unaccent('display_name'),
|
|
||||||
percent=unaccent('%s'),)
|
query = """SELECT id
|
||||||
|
FROM res_partner
|
||||||
where_clause_params += [search_name]*2
|
{where} ({document_number} {operator} {percent})
|
||||||
if limit:
|
ORDER BY {display_name} {operator} {percent} desc,
|
||||||
query += ' limit %s'
|
{display_name}
|
||||||
where_clause_params.append(limit)
|
""".format(where=where_str,
|
||||||
self.env.cr.execute(query, where_clause_params)
|
operator=operator,
|
||||||
partner_ids = [row[0] for row in self.env.cr.fetchall()]
|
document_number=unaccent('document_number'),
|
||||||
if partner_ids:
|
display_name=unaccent('display_name'),
|
||||||
result += self.browse(partner_ids).name_get()
|
percent=unaccent('%s'),)
|
||||||
return result
|
|
||||||
|
where_clause_params += [search_name]*2
|
||||||
#TMP_FIX VAT Validation
|
if limit:
|
||||||
@api.constrains("vat")
|
query += ' limit %s'
|
||||||
def check_vat(self):
|
where_clause_params.append(limit)
|
||||||
return
|
self.env.cr.execute(query, where_clause_params)
|
||||||
|
partner_ids = [row[0] for row in self.env.cr.fetchall()]
|
||||||
#TODO: Review better VAT & DocumentNumber integration
|
if partner_ids:
|
||||||
@api.onchange('document_number')
|
result += self.browse(partner_ids).name_get()
|
||||||
def onchange_document_number(self):
|
return result
|
||||||
for partner in self:
|
|
||||||
if partner.document_number and partner.document_type == 'D':
|
@api.model
|
||||||
partner.vat = 'ES' + partner.document_number
|
def _get_duplicated_ids(self, partner):
|
||||||
|
partner_ids = []
|
||||||
|
if partner.vat:
|
||||||
|
partner_ids += self.env['res.partner'].search([
|
||||||
|
('vat', '=', partner.vat),
|
||||||
|
('parent_id', '=', False)
|
||||||
|
]).ids
|
||||||
|
if partner.document_number:
|
||||||
|
partner_ids += self.env['res.partner'].search([
|
||||||
|
('document_number', '=', partner.document_number),
|
||||||
|
('child_ids', '=', False)
|
||||||
|
]).ids
|
||||||
|
if partner_ids:
|
||||||
|
return partner_ids
|
||||||
|
|
||||||
|
def _merge_fields(self):
|
||||||
|
duplicated_fields = ['vat', 'document_number']
|
||||||
|
return duplicated_fields
|
||||||
|
|
||||||
|
@api.constrains('vat')
|
||||||
|
def _check_vat_unique(self):
|
||||||
|
for record in self:
|
||||||
|
if record.unconfirmed:
|
||||||
|
if record.vat:
|
||||||
|
record.update({'unconfirmed': False})
|
||||||
|
partner_ids = self.env['res.partner'].search([
|
||||||
|
('vat', '=', record.vat),
|
||||||
|
('parent_id', '=', False)
|
||||||
|
]).ids
|
||||||
|
if len(partner_ids) > 1:
|
||||||
|
partners = self.env['res.partner'].browse(partner_ids)
|
||||||
|
record._merge(partners._ids)
|
||||||
|
else:
|
||||||
|
return super(ResPartner, self)._check_vat_unique()
|
||||||
|
return True
|
||||||
|
|
||||||
|
@api.constrains('document_number')
|
||||||
|
def _check_document_number_unique(self):
|
||||||
|
for record in self:
|
||||||
|
if not record.document_number:
|
||||||
|
continue
|
||||||
|
if record.unconfirmed:
|
||||||
|
if record.document_number:
|
||||||
|
record.update({'unconfirmed': False})
|
||||||
|
partner_ids = self.env['res.partner'].search([
|
||||||
|
('document_number', '=', record.document_number),
|
||||||
|
]).ids
|
||||||
|
if len(partner_ids) > 1:
|
||||||
|
partners = self.env['res.partner'].browse(partner_ids)
|
||||||
|
record._merge(partners._ids)
|
||||||
|
if not record.parent_id and record.document_type == 'D' \
|
||||||
|
and not record.vat:
|
||||||
|
record.update({
|
||||||
|
'vat': record.document_number,
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
results = self.env['res.partner'].search_count([
|
||||||
|
('document_type', '=', record.document_type),
|
||||||
|
('document_number', '=', record.document_number),
|
||||||
|
('id', '!=', record.id)
|
||||||
|
])
|
||||||
|
if results:
|
||||||
|
raise ValidationError(_(
|
||||||
|
"The Document Number %s already exists in another "
|
||||||
|
"partner.") % record.document_number)
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def open_main_partner(self):
|
||||||
|
self.ensure_one()
|
||||||
|
action = self.env.ref('base.action_partner_form').read()[0]
|
||||||
|
if self.main_partner_id:
|
||||||
|
action['views'] = [(self.env.ref('base.view_partner_form').id, 'form')]
|
||||||
|
action['res_id'] = self.main_partner_id.id
|
||||||
|
else:
|
||||||
|
action = {'type': 'ir.actions.act_window_close'}
|
||||||
|
return action
|
||||||
|
|
||||||
|
def _get_fk_on(self, table):
|
||||||
|
""" return a list of many2one relation with the given table.
|
||||||
|
:param table : the name of the sql table to return relations
|
||||||
|
:returns a list of tuple 'table name', 'column name'.
|
||||||
|
"""
|
||||||
|
query = """
|
||||||
|
SELECT cl1.relname as table, att1.attname as column
|
||||||
|
FROM pg_constraint as con, pg_class as cl1, pg_class as cl2, pg_attribute as att1, pg_attribute as att2
|
||||||
|
WHERE con.conrelid = cl1.oid
|
||||||
|
AND con.confrelid = cl2.oid
|
||||||
|
AND array_lower(con.conkey, 1) = 1
|
||||||
|
AND con.conkey[1] = att1.attnum
|
||||||
|
AND att1.attrelid = cl1.oid
|
||||||
|
AND cl2.relname = %s
|
||||||
|
AND att2.attname = 'id'
|
||||||
|
AND array_lower(con.confkey, 1) = 1
|
||||||
|
AND con.confkey[1] = att2.attnum
|
||||||
|
AND att2.attrelid = cl2.oid
|
||||||
|
AND con.contype = 'f'
|
||||||
|
"""
|
||||||
|
self._cr.execute(query, (table,))
|
||||||
|
return self._cr.fetchall()
|
||||||
|
|
||||||
|
def _merge(self, partner_ids, dst_partner=None):
|
||||||
|
""" private implementation of merge partner
|
||||||
|
:param partner_ids : ids of partner to merge
|
||||||
|
:param dst_partner : record of destination res.partner
|
||||||
|
"""
|
||||||
|
partner = self.env['res.partner']
|
||||||
|
partner_ids = partner.browse(partner_ids).exists()
|
||||||
|
if len(partner_ids) < 2:
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(partner_ids) > 3:
|
||||||
|
raise UserError(_("For safety reasons, you cannot merge more than 3 contacts together. You can re-open the wizard several times if needed."))
|
||||||
|
|
||||||
|
# check if the list of partners to merge contains child/parent relation
|
||||||
|
child_ids = self.env['res.partner']
|
||||||
|
for partner_id in partner_ids:
|
||||||
|
child_ids |= partner.search([('id', 'child_of', [partner_id.id])]) - partner_id
|
||||||
|
if partner_ids & child_ids:
|
||||||
|
raise UserError(_("You cannot merge a contact with one of his parent."))
|
||||||
|
|
||||||
|
# remove dst_partner from partners to merge
|
||||||
|
if dst_partner and dst_partner in partner_ids:
|
||||||
|
src_partners = partner_ids - dst_partner
|
||||||
|
else:
|
||||||
|
ordered_partners = self._get_ordered_partner(partner_ids.ids)
|
||||||
|
dst_partner = ordered_partners[-1]
|
||||||
|
src_partners = ordered_partners[:-1]
|
||||||
|
_logger.info("dst_partner: %s", dst_partner.id)
|
||||||
|
|
||||||
|
# call sub methods to do the merge
|
||||||
|
self._update_foreign_keys(src_partners, dst_partner)
|
||||||
|
self._update_reference_fields(src_partners, dst_partner)
|
||||||
|
self._update_values(src_partners, dst_partner)
|
||||||
|
|
||||||
|
_logger.info('(uid = %s) merged the partners %r with %s', self._uid, src_partners.ids, dst_partner.id)
|
||||||
|
dst_partner.message_post(body='%s %s' % (_("Merged with the following partners:"), ", ".join('%s <%s> (ID %s)' % (p.name, p.email or 'n/a', p.id) for p in src_partners)))
|
||||||
|
|
||||||
|
return dst_partner
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _update_foreign_keys(self, src_partners, dst_partner):
|
||||||
|
""" Update all foreign key from the src_partner to dst_partner. All many2one fields will be updated.
|
||||||
|
:param src_partners : merge source res.partner recordset (does not include destination one)
|
||||||
|
:param dst_partner : record of destination res.partner
|
||||||
|
"""
|
||||||
|
_logger.debug('_update_foreign_keys for dst_partner: %s for src_partners: %s', dst_partner.id, str(src_partners.ids))
|
||||||
|
|
||||||
|
# find the many2one relation to a partner
|
||||||
|
partner = self.env['res.partner']
|
||||||
|
relations = self._get_fk_on('res_partner')
|
||||||
|
|
||||||
|
for table, column in relations:
|
||||||
|
# get list of columns of current table (exept the current fk column)
|
||||||
|
query = "SELECT column_name FROM information_schema.columns WHERE table_name LIKE '%s'" % (table)
|
||||||
|
self._cr.execute(query, ())
|
||||||
|
columns = []
|
||||||
|
for data in self._cr.fetchall():
|
||||||
|
if data[0] != column:
|
||||||
|
columns.append(data[0])
|
||||||
|
|
||||||
|
# do the update for the current table/column in SQL
|
||||||
|
query_dic = {
|
||||||
|
'table': table,
|
||||||
|
'column': column,
|
||||||
|
'value': columns[0],
|
||||||
|
}
|
||||||
|
if len(columns) <= 1:
|
||||||
|
# unique key treated
|
||||||
|
query = """
|
||||||
|
UPDATE "%(table)s" as ___tu
|
||||||
|
SET %(column)s = %%s
|
||||||
|
WHERE
|
||||||
|
%(column)s = %%s AND
|
||||||
|
NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM "%(table)s" as ___tw
|
||||||
|
WHERE
|
||||||
|
%(column)s = %%s AND
|
||||||
|
___tu.%(value)s = ___tw.%(value)s
|
||||||
|
)""" % query_dic
|
||||||
|
for partner in src_partners:
|
||||||
|
self._cr.execute(query, (dst_partner.id, partner.id, dst_partner.id))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
with mute_logger('odoo.sql_db'), self._cr.savepoint():
|
||||||
|
query = 'UPDATE "%(table)s" SET %(column)s = %%s WHERE %(column)s IN %%s' % query_dic
|
||||||
|
self._cr.execute(query, (dst_partner.id, tuple(src_partners.ids),))
|
||||||
|
|
||||||
|
# handle the recursivity with parent relation
|
||||||
|
if column == partner._parent_name and table == 'res_partner':
|
||||||
|
query = """
|
||||||
|
WITH RECURSIVE cycle(id, parent_id) AS (
|
||||||
|
SELECT id, parent_id FROM res_partner
|
||||||
|
UNION
|
||||||
|
SELECT cycle.id, res_partner.parent_id
|
||||||
|
FROM res_partner, cycle
|
||||||
|
WHERE res_partner.id = cycle.parent_id AND
|
||||||
|
cycle.id != cycle.parent_id
|
||||||
|
)
|
||||||
|
SELECT id FROM cycle WHERE id = parent_id AND id = %s
|
||||||
|
"""
|
||||||
|
self._cr.execute(query, (dst_partner.id,))
|
||||||
|
# NOTE JEM : shouldn't we fetch the data ?
|
||||||
|
except psycopg2.Error:
|
||||||
|
# updating fails, most likely due to a violated unique constraint
|
||||||
|
# keeping record with nonexistent partner_id is useless, better delete it
|
||||||
|
query = 'DELETE FROM "%(table)s" WHERE "%(column)s" IN %%s' % query_dic
|
||||||
|
self._cr.execute(query, (tuple(src_partners.ids),))
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _update_reference_fields(self, src_partners, dst_partner):
|
||||||
|
""" Update all reference fields from the src_partner to dst_partner.
|
||||||
|
:param src_partners : merge source res.partner recordset (does not include destination one)
|
||||||
|
:param dst_partner : record of destination res.partner
|
||||||
|
"""
|
||||||
|
_logger.debug('_update_reference_fields for dst_partner: %s for src_partners: %r', dst_partner.id, src_partners.ids)
|
||||||
|
|
||||||
|
def update_records(model, src, field_model='model', field_id='res_id'):
|
||||||
|
Model = self.env[model] if model in self.env else None
|
||||||
|
if Model is None:
|
||||||
|
return
|
||||||
|
records = Model.sudo().search([(field_model, '=', 'res.partner'), (field_id, '=', src.id)])
|
||||||
|
try:
|
||||||
|
with mute_logger('odoo.sql_db'), self._cr.savepoint():
|
||||||
|
return records.sudo().write({field_id: dst_partner.id})
|
||||||
|
except psycopg2.Error:
|
||||||
|
# updating fails, most likely due to a violated unique constraint
|
||||||
|
# keeping record with nonexistent partner_id is useless, better delete it
|
||||||
|
return records.sudo().unlink()
|
||||||
|
|
||||||
|
update_records = functools.partial(update_records)
|
||||||
|
|
||||||
|
for partner in src_partners:
|
||||||
|
update_records('calendar', src=partner, field_model='model_id.model')
|
||||||
|
update_records('ir.attachment', src=partner, field_model='res_model')
|
||||||
|
update_records('mail.followers', src=partner, field_model='res_model')
|
||||||
|
update_records('mail.message', src=partner)
|
||||||
|
update_records('ir.model.data', src=partner)
|
||||||
|
|
||||||
|
records = self.env['ir.model.fields'].search([('ttype', '=', 'reference')])
|
||||||
|
for record in records.sudo():
|
||||||
|
try:
|
||||||
|
Model = self.env[record.model]
|
||||||
|
field = Model._fields[record.name]
|
||||||
|
except KeyError:
|
||||||
|
# unknown model or field => skip
|
||||||
|
continue
|
||||||
|
|
||||||
|
if field.compute is not None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for partner in src_partners:
|
||||||
|
records_ref = Model.sudo().search([(record.name, '=', 'res.partner,%d' % partner.id)])
|
||||||
|
values = {
|
||||||
|
record.name: 'res.partner,%d' % dst_partner.id,
|
||||||
|
}
|
||||||
|
records_ref.sudo().write(values)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _update_values(self, src_partners, dst_partner):
|
||||||
|
""" Update values of dst_partner with the ones from the src_partners.
|
||||||
|
:param src_partners : recordset of source res.partner
|
||||||
|
:param dst_partner : record of destination res.partner
|
||||||
|
"""
|
||||||
|
_logger.debug('_update_values for dst_partner: %s for src_partners: %r', dst_partner.id, src_partners.ids)
|
||||||
|
|
||||||
|
model_fields = dst_partner.fields_get().keys()
|
||||||
|
|
||||||
|
def write_serializer(item):
|
||||||
|
if isinstance(item, models.BaseModel):
|
||||||
|
return item.id
|
||||||
|
else:
|
||||||
|
return item
|
||||||
|
# get all fields that are not computed or x2many
|
||||||
|
values = dict()
|
||||||
|
for column in model_fields:
|
||||||
|
field = dst_partner._fields[column]
|
||||||
|
if field.type not in ('many2many', 'one2many') and field.compute is None:
|
||||||
|
for item in itertools.chain(src_partners, [dst_partner]):
|
||||||
|
if item[column]:
|
||||||
|
values[column] = write_serializer(item[column])
|
||||||
|
# remove fields that can not be updated (id and parent_id)
|
||||||
|
values.pop('id', None)
|
||||||
|
parent_id = values.pop('parent_id', None)
|
||||||
|
src_partners.update({
|
||||||
|
'active': False,
|
||||||
|
'document_number': False,
|
||||||
|
'vat': False,
|
||||||
|
'main_partner_id': dst_partner.id})
|
||||||
|
dst_partner.write(values)
|
||||||
|
# try to update the parent_id
|
||||||
|
if parent_id and parent_id != dst_partner.id:
|
||||||
|
try:
|
||||||
|
dst_partner.write({'parent_id': parent_id})
|
||||||
|
except ValidationError:
|
||||||
|
_logger.info('Skip recursive partner hierarchies for parent_id %s of partner: %s', parent_id, dst_partner.id)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _get_ordered_partner(self, partner_ids):
|
||||||
|
""" Helper : returns a `res.partner` recordset ordered by create_date/active fields
|
||||||
|
:param partner_ids : list of partner ids to sort
|
||||||
|
"""
|
||||||
|
return self.env['res.partner'].browse(partner_ids).sorted(
|
||||||
|
key=lambda p: (p.active, (p.create_date or '')),
|
||||||
|
reverse=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def _compute_models(self):
|
||||||
|
""" Compute the different models needed by the system if you want to exclude some partners. """
|
||||||
|
model_mapping = {}
|
||||||
|
if self.exclude_contact:
|
||||||
|
model_mapping['res.users'] = 'partner_id'
|
||||||
|
if 'account.move.line' in self.env and self.exclude_journal_item:
|
||||||
|
model_mapping['account.move.line'] = 'partner_id'
|
||||||
|
return model_mapping
|
||||||
|
|||||||
@@ -1,78 +1,132 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<record id="hotel_checkin_partner_view_form" model="ir.ui.view">
|
<record id="hotel_checkin_partner_view_form" model="ir.ui.view">
|
||||||
<field name="name">hotel.checkin.partner.view.form</field>
|
<field name="name">hotel.checkin.partner.view.form</field>
|
||||||
<field name="model">hotel.checkin.partner</field>
|
<field name="model">hotel.checkin.partner</field>
|
||||||
<field name="inherit_id" ref="hotel.hotel_checkin_partner_view_form"/>
|
<field name="inherit_id" ref="hotel.hotel_checkin_partner_view_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//field[@name='exit_date']" position="after">
|
<xpath expr="//field[@name='partner_id']" position="before">
|
||||||
<field name="document_type"/>
|
<field name="document_type"/>
|
||||||
<field name="document_number"/>
|
<field name="document_number"/>
|
||||||
<field name="document_expedition_date"/>
|
<field name="lastname"/>
|
||||||
</xpath>
|
<field name="firstname"/>
|
||||||
<xpath expr="//field[@name='reservation_id']" position="after">
|
</xpath>
|
||||||
<field name="gender"/>
|
<xpath expr="//field[@name='exit_date']" position="after">
|
||||||
<field name="birthdate_date"/>
|
<field name="document_expedition_date"/>
|
||||||
<field name="code_ine_id" />
|
</xpath>
|
||||||
<button type="action" class="oe_stat_button"
|
<xpath expr="//field[@name='reservation_id']" position="after">
|
||||||
icon="fa-file-pdf-o"
|
<field name="gender"/>
|
||||||
name="%(action_report_viajero)d"
|
<field name="birthdate_date"/>
|
||||||
context="{'partner_id': partner_id,'enter_date': enter_date,
|
<field name="code_ine_id" />
|
||||||
'exit_date': exit_date,'reservation_ids': reservation_id,
|
<button type="action" class="oe_stat_button"
|
||||||
'hidden_cardex': True, 'edit_cardex': True }"
|
icon="fa-file-pdf-o"
|
||||||
string="Print in PDF" />
|
name="%(action_report_viajero)d"
|
||||||
</xpath>
|
context="{'partner_id': partner_id,'enter_date': enter_date,
|
||||||
</field>
|
'exit_date': exit_date,'reservation_ids': reservation_id,
|
||||||
</record>
|
'hidden_cardex': True, 'edit_cardex': True }"
|
||||||
|
string="Print in PDF" />
|
||||||
<record id="hotel_checkin_partner_view_tree" model="ir.ui.view">
|
</xpath>
|
||||||
<field name="name">hotel.checkin.partner.view.tree</field>
|
</field>
|
||||||
<field name="model">hotel.checkin.partner</field>
|
</record>
|
||||||
<field name="inherit_id" ref="hotel.hotel_checkin_partner_view_tree"/>
|
|
||||||
<field name="arch" type="xml">
|
<record id="hotel_checkin_partner_view_tree" model="ir.ui.view">
|
||||||
<xpath expr="//button[@name='action_on_board']" position="before">
|
<field name="name">hotel.checkin.partner.view.tree</field>
|
||||||
<button type="action" class="oe_stat_button"
|
<field name="model">hotel.checkin.partner</field>
|
||||||
icon="fa fa-2x fa-file-pdf-o"
|
<field name="inherit_id" ref="hotel.hotel_checkin_partner_view_tree"/>
|
||||||
name="%(action_report_viajero)d"
|
<field name="arch" type="xml">
|
||||||
context="{'partner_id': partner_id,'enter_date': enter_date,
|
<xpath expr="//button[@name='action_on_board']" position="before">
|
||||||
'exit_date': exit_date,'reservation_ids': reservation_id,
|
<button type="action" class="oe_stat_button"
|
||||||
'hidden_cardex': True, 'edit_cardex': True }"
|
icon="fa fa-2x fa-file-pdf-o"
|
||||||
string="Print in PDF" />
|
name="%(action_report_viajero)d"
|
||||||
</xpath>
|
context="{'partner_id': partner_id,'enter_date': enter_date,
|
||||||
<xpath expr="//field[@name='partner_id']" position="after">
|
'exit_date': exit_date,'reservation_ids': reservation_id,
|
||||||
<field name="gender"/>
|
'hidden_cardex': True, 'edit_cardex': True }"
|
||||||
<field name="document_type"/>
|
string="Print in PDF"
|
||||||
<field name="document_number" string="Doc. Number"/>
|
attrs="{'invisible': [('state','not in', ('booking','done'))]}"
|
||||||
<field name="document_expedition_date" string="Exp. Date"/>
|
/>
|
||||||
<field name="birthdate_date" string="Birthdate" />
|
</xpath>
|
||||||
<field name="code_ine_id" />
|
<xpath expr="//field[@name='partner_id']" position="before">
|
||||||
</xpath>
|
<field name="document_type"/>
|
||||||
</field>
|
<field name="document_number" string="Doc. Number"/>
|
||||||
</record>
|
<field name="firstname"/>
|
||||||
|
<field name="lastname"/>
|
||||||
<record id="hotel_checkin_partner_reservation_view_tree" model="ir.ui.view">
|
</xpath>
|
||||||
<field name="name">hotel.checkin.partner.view.tree</field>
|
<xpath expr="//field[@name='partner_id']" position="after">
|
||||||
<field name="model">hotel.checkin.partner</field>
|
<field name="gender"/>
|
||||||
<field name="inherit_id" ref="hotel.hotel_checkin_partner_reservation_view_tree"/>
|
<field name="document_expedition_date" string="Exp. Date"/>
|
||||||
<field name="arch" type="xml">
|
<field name="birthdate_date" string="Birthdate" />
|
||||||
<xpath expr="//button[@name='action_on_board']" position="before">
|
<field name="code_ine_id" />
|
||||||
<button type="action" class="oe_stat_button"
|
</xpath>
|
||||||
icon="fa fa-2x fa-file-pdf-o"
|
</field>
|
||||||
name="%(action_report_viajero)d"
|
</record>
|
||||||
context="{'partner_id': partner_id,'enter_date': enter_date,
|
|
||||||
'exit_date': exit_date,'reservation_ids': reservation_id,
|
<record id="hotel_checkin_partner_reservation_view_tree" model="ir.ui.view">
|
||||||
'hidden_cardex': True, 'edit_cardex': True }"/>
|
<field name="name">hotel.checkin.partner.view.tree</field>
|
||||||
</xpath>
|
<field name="model">hotel.checkin.partner</field>
|
||||||
<xpath expr="//field[@name='partner_id']" position="after">
|
<field name="inherit_id" ref="hotel.hotel_checkin_partner_reservation_view_tree"/>
|
||||||
<field name="gender"/>
|
<field name="arch" type="xml">
|
||||||
<field name="document_type"/>
|
<xpath expr="//field[@name='partner_id']" position="after">
|
||||||
<field name="document_number" string="Doc. Number"/>
|
<field name="firstname" attrs="{'required': [
|
||||||
<field name="document_expedition_date" string="Exp. Date"/>
|
('lastname','==', False)
|
||||||
<field name="birthdate_date" string="Birthdate" />
|
],'readonly': [('partner_id', '!=', False),
|
||||||
<field name="code_ine_id" />
|
('firstname','!=', False)]
|
||||||
</xpath>
|
}"
|
||||||
</field>
|
force_save="1" />
|
||||||
</record>
|
<field name="lastname" attrs="{'required': [
|
||||||
|
('firstname','==', False)
|
||||||
</odoo>
|
], 'readonly': [('partner_id', '!=', False),
|
||||||
|
('lastname','!=', False)]}"
|
||||||
|
force_save="1" />
|
||||||
|
<field name="document_type"
|
||||||
|
attrs="{'required': [
|
||||||
|
('document_number','!=', False)
|
||||||
|
],'readonly': [('document_number', '!=', False),
|
||||||
|
('document_type','!=', False),
|
||||||
|
('partner_id','!=', False)]}"
|
||||||
|
force_save="1"/>
|
||||||
|
<field name="document_number" string="Doc. Number"
|
||||||
|
attrs="{'readonly': [('partner_id', '!=', False),
|
||||||
|
('document_number','!=', False)]}"
|
||||||
|
force_save="1"/>
|
||||||
|
<field name="document_expedition_date" string="Exp. Date"
|
||||||
|
attrs="{'readonly': [('partner_id', '!=', False),
|
||||||
|
('document_expedition_date','!=', False)]}"
|
||||||
|
force_save="1"/>
|
||||||
|
<field name="birthdate_date" string="Birthdate"
|
||||||
|
attrs="{'readonly': [('partner_id', '!=', False),
|
||||||
|
('birthdate_date','!=', False)]}"
|
||||||
|
force_save="1" />
|
||||||
|
<field name="code_ine_id"
|
||||||
|
attrs="{'readonly': [('partner_id', '!=', False),
|
||||||
|
('code_ine_id','!=', False)]}"
|
||||||
|
force_save="1" />
|
||||||
|
<field name="gender"
|
||||||
|
attrs="{'readonly': [('partner_id', '!=', False),
|
||||||
|
('gender','!=', False)]}"
|
||||||
|
force_save="1" />
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='partner_id']" position="attributes">
|
||||||
|
<attribute name="options">{"no_create": True}</attribute>
|
||||||
|
<attribute name="required">False</attribute>
|
||||||
|
<attribute name="string">'Search'</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//button[@name='action_on_board']" position="before">
|
||||||
|
<button type="action" class="oe_stat_button"
|
||||||
|
icon="fa fa-2x fa-file-pdf-o"
|
||||||
|
name="%(action_report_viajero)d"
|
||||||
|
context="{'partner_id': partner_id,'enter_date': enter_date,
|
||||||
|
'exit_date': exit_date,'reservation_ids': reservation_id,
|
||||||
|
'hidden_cardex': True, 'edit_cardex': True }"
|
||||||
|
attrs="{'invisible': [('state','not in', ('booking','done'))]}"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='enter_date']" position="attributes">
|
||||||
|
<attribute name="invisible">True</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='exit_date']" position="attributes">
|
||||||
|
<attribute name="invisible">True</attribute>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
|
|||||||
@@ -7,6 +7,15 @@
|
|||||||
<field name="model">res.partner</field>
|
<field name="model">res.partner</field>
|
||||||
<field name="inherit_id" ref="partner_contact_personal_information_page.personal_information" />
|
<field name="inherit_id" ref="partner_contact_personal_information_page.personal_information" />
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr='//div[@name="button_box"]' position='before'>
|
||||||
|
<field name="main_partner_id" invisible="1"/>
|
||||||
|
<div class="alert alert-warning" role="alert" style="margin-bottom:0px;"
|
||||||
|
attrs="{'invisible': [('main_partner_id','=',False)]}">
|
||||||
|
This partner was deactivated and merged with another client to avoid duplicity, you can see the active client in
|
||||||
|
<bold><button class="alert-link" type="object" name="open_main_partner" string="Customer Link"/></bold>
|
||||||
|
</div>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
<xpath expr="//page[@name='personal_information_page']" position='inside'>
|
<xpath expr="//page[@name='personal_information_page']" position='inside'>
|
||||||
<group colspan="4">
|
<group colspan="4">
|
||||||
<group>
|
<group>
|
||||||
@@ -19,6 +28,11 @@
|
|||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
|
<xpath expr='//field[@name="vat"]' position='after'>
|
||||||
|
<field name="unconfirmed" />
|
||||||
|
</xpath>
|
||||||
|
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|||||||
17
hotel_l10n_es/views/inherited_hotel_reservation_views.xml
Normal file
17
hotel_l10n_es/views/inherited_hotel_reservation_views.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="hotel_reservation_view_form" model="ir.ui.view">
|
||||||
|
<field name="model">hotel.reservation</field>
|
||||||
|
<field name="inherit_id" ref="hotel.hotel_reservation_view_form" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//button[@name='send_reservation_mail']" position="before">
|
||||||
|
<button name="print_all_checkins" string="Print All Checkins"
|
||||||
|
type="object" icon="fa-print" attrs="{'invisible':[
|
||||||
|
('checkin_partner_ids','=', [])
|
||||||
|
]}" />
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
Reference in New Issue
Block a user