[WIP] Clean Refactoring

This commit is contained in:
Darío Lodeiros
2020-10-12 19:34:04 +02:00
parent 0d1a90c7b3
commit 9ee2687dac
28 changed files with 32 additions and 5405 deletions

View File

@@ -1,4 +1,4 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import models
from . import wizard
#from . import wizard

View File

@@ -31,17 +31,9 @@
"data/cron_jobs.xml",
"data/pms_data.xml",
"data/pms_sequence.xml",
"data/email_template_cancel.xml",
"data/email_template_reserv.xml",
"data/email_template_exit.xml",
"report/pms_folio.xml",
"report/pms_folio_templates.xml",
"templates/pms_email_template.xml",
"wizard/massive_changes.xml",
"wizard/massive_price_reservation_days.xml",
"wizard/service_on_day.xml",
"wizard/split_reservation.xml",
"wizard/wizard_reservation.xml",
"views/general.xml",
"data/menus.xml",
"views/pms_amenity_views.xml",
@@ -70,7 +62,6 @@
"views/product_pricelist_views.xml",
"views/product_template_views.xml",
"views/webclient_templates.xml",
"wizard/folio_make_invoice_advance_views.xml",
],
"demo": ["demo/pms_master_data.xml", "demo/pms_reservation.xml"],
"qweb": ["static/src/xml/pms_base_templates.xml",],

View File

@@ -1,713 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE xml>
<odoo>
<data noupdate="1">
<!-- Email Template For PMS Reservation -->
<record id="mail_template_pms_cancel" model="mail.template">
<field name="name">Cancel Reservation-Send by Email</field>
<field
name="subject"
>Cancelación de su reserva en ${object.company_id.property_name}</field>
<field name="partner_to">${(object.partner_id.id or '')}</field>
<field name="model_id" ref="pms.model_pms_folio" />
<field name="auto_delete" eval="True" />
<field name="body_html">
<![CDATA[
<style type="text/css">
/*Global Styles*/
.global {
margin: 0;
padding: 0;
min-width: 100% !important;
}
a {
color: #5e96ea;
text-decoration: none;
font-weight: bold;
}
img {
height: auto;
}
.content {
border: 1px solid #eeeeee;
}
.logo {
font-family: sans-serif;
font-size: 36px;
font-weight: bold;
color: #ffffff;
}
.link a {
font-family: sans-serif;
font-size: 12px;
color: #ffffff;
}
.subheading {
font-size: 14px;
color: #cccccc;
font-family: sans-serif;
font-weight: bold;
padding: 0 0 0 0;
text-transform: uppercase;
letter-spacing: 1px;
}
.h1 {
font-family: sans-serif;
font-size: 48px;
font-weight: bold;
line-height: 56px;
color: #ffffff;
padding: 0 0 0 0;
}
.h2 {
font-family: sans-serif;
font-size: 18px;
font-weight: bold;
color: #444444;
padding: 0 0 0 0;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.h3 {
font-family: sans-serif;
font-size: 24px;
font-weight: regular;
color: #555555;
padding: 0 0 0 0;
}
.h4 {
font-family: sans-serif;
font-size: 18px;
font-weight: bold;
color: #666666;
padding: 0 0 0 0;
}
.paragraph {
font-family: sans-serif;
font-size: 14px;
line-height: 22px;
color: #666666;
font-weight: 200;
padding: 20px 0 0 0;
}
.listitem {
font-family: sans-serif;
font-size: 15px;
color: #666666;
font-weight: 200;
padding: 0 0 20px 0;
}
.smalltext {
font-family: sans-serif;
font-size: 14px;
color: #cccccc;
padding: 3px 0 0 0;
}
.borderbottom {
border-bottom: 1px solid #f2eeed;
}
/*Media Queries*/
@media only screen and (max-width: 651px) {
.columns {
width: 100% !important;
}
.columncontainer {
display: block !important;
width: 100% !important;
}
.paragraph,
.listitem {
font-size: 18px;
}
.link {
float: left;
}
}
@media only screen and (min-width: 651px) {
.content {
width: 650px !important;
}
}
</style>
<div style="bgcolor:#ffffff; margin: 0; padding: 0; min-width: 100%!important;">
<table width="100%" cellspacing="0" cellpadding="0" border="0" bgcolor="#ffffff">
<tbody>
<tr>
<td>
<!--[if (gte mso 9)|(IE)]>
<table width="540" align="center" cellpadding="0" cellspacing="0" border="0">
<tr>
<td>
<![endif]-->
<!--Content Wrapper-->
<table class="content" background-color="#f6f6f6" style="width: 650px !important; border: 1px solid #eeeeee; background-color:#f6f6f6;" cellspacing="0" cellpadding="0" border="0" align="center">
<!--Header-->
<tbody>
<tr>
<td style="padding: 30px 30px 20px 30px;background-color:#1B1B1B">
<table class="columns" width="100%" cellspacing="0" cellpadding="0" border="0x">
<tbody>
<tr valign="top">
</tr>
</tbody>
</table>
<img src="https://www.aldahotels.es/firma/email/llegada/logobl.png" alt="Alda Hotels" width="251" height="57">
<table class="columns" width="100%" cellspacing="0" cellpadding="0" border="0x">
<tbody>
<tr valign="top">
<td class="columncontainer" width="50%" valign="middle">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td style="padding: 0px 0px 0px 0px;" valign="middle" align="right">
<table class="link" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td style="padding: 0px 15px 0px 0px;">
<a href="#" style="color:#FFFFFF">www.aldahotels.com</a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
</tr>
<!--1 Column-->
<tr>
<td class="borderbottom" style="padding: 60px 30px 60px 30px;">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td class="h2" align="center"> Tu reserva se ha cancelado en ${object.company_id.property_name}</td>
</tr>
<tr>
<td style="padding: 20px 0 0 0;" align="center">
<table style="width: 50px;" width="50px" height="5px" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td style="font-size: 1px; line-height: 5px;" height="5px" bgcolor="#45C2B1">
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td class="paragraph" style="padding: 10px 0 0 0;" align="justify">Hola ${object.partner_id.firstname}<br>
Tu reserva en <b>${object.company_id.property_name}</b> se ha anulado correctamente. No es necesario que hagas nada más.
Si la cancelación conlleva la devolución de alguna cantidad, nos pondremos en contacto contigo.
En caso de que tengas alguna duda, estaremos encantados de atenderte.
</td>
</tr>
<tr>
<td style="padding: 20px 0 0px 0; " align="center">
<table cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td align="center">
<a href="tel:${object.company_id.phone}" target="_blank" style="text-decoration: none; color: #FFFFFF; font-size: 20px; padding: 10px 20px 10px 20px;">
<div style="padding: 0.5em; background-color: #45C2B1; border-color: #45C2B1; border-width: 2px;border-style:solid; border-bottom-style: solid;border-left-style: solid;border-right-style: solid;border-top-style: solid;-webkit-border-radius: 10; -moz-border-radius: 10; border-radius: 10px;">Contactar
</div>
</a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<!--Section with sidebar-->
<tr>
</tr>
<!--Article with thumbnail-->
<tr>
<td class="borderbottom" style="padding: 30px 0px 0px 0px;">
<table class="columns" width="650" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr valign="top">
<td class="columncontainer" width="10%">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td style="padding: 20px 0px 0px 30px;" height="115">
<img src="http://www.aldahotels.es/firma/email/llegada/cancelada.png" alt="Cancelación" border="0">
</td>
</tr>
</tbody>
</table>
</td>
<td class="columncontainer" width="90%">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td style="padding: 20px 30px 20px 30px;" valign="top">
<table cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td class="h2" style="color: #45C2B1">Datos de tu reserva cancelada</td>
</tr>
<tr>
<td class="subheading" style="color: #555555" align="justify">
<strong>${object.partner_id.name}</strong>
</td>
</tr>
<tr>
<td class="paragraph" style="padding: 10px 0 0 0;" align="justify">
% if object.partner_id.contact_address:
${object.partner_id.contact_address}<br>
% endif
<br>
% for rline in object.get_grouped_reservations_json('cancelled'):
<strong>
${rline['num']} x ${rline['room_type']['name']}
% if rline['childrens'] == 0:
(${rline['adults']} Adultos)
% else:
(${rline['adults']} Adultos + ${rline['childrens']} Niños)
% endif
<br />
</strong>
<br>
<strong style="margin-left:2em">Entrada</strong>: ${format_tz(rline['checkin']+ ' 00:00:00', format="%d de %B de %Y")}<br>
<strong style="margin-left:2em">Salida</strong>: ${format_tz(rline['checkout']+ ' 00:00:00', format="%d de %B de %Y")}<br>
<strong style="margin-left:2em" 2="">Nº de noches</strong>: ${rline['nights']}<br><br>
% endfor
</td>
</tr>
<tr>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<!--2 Column-->
<tr>
</tr>
<!--Article with thumbnail importes-->
<tr>
<td class="borderbottom" style="padding: 30px 0px 0px 0px;">
<table class="columns" width="650" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr valign="top">
<td class="columncontainer" width="10%">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td style="padding: 20px 0px 0px 30px;" height="115">
<img src="http://www.aldahotels.es/firma/email/llegada/importes.png" alt="Pago" border="0">
</td>
</tr>
</tbody>
</table>
</td>
<td class="columncontainer" width="90%">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td style="padding: 20px 30px 20px 30px;" valign="top">
<table cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td class="h2" style="color: #45C2B1">IMPORTES</td>
</tr>
<tr>
<td class="subheading" style="color: #555555" align="justify">
<strong>Noches</strong>: ${len(object.reservation_ids[0].reservation_lines)}</td>
</tr>
<tr>
<td class="paragraph" style="padding: 10px 0 0 0;" align="justify">
<strong>Base imponible</strong>: ${object.amount_untaxed} €<br>
<strong>I.V.A (10%)</strong>: ${object.amount_tax} €<br>
<strong>Precio total</strong>: ${object.amount_total} €<br>
<strong>Coste de cancelación</strong>: [[importe]]<br>
<br>
<br>
</td>
</tr>
<tr>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<!--Article with thumbnail-->
<tr>
</tr>
<!--Article with thumbnail-->
<tr>
</tr>
<!--Section with sidebar-->
<tr>
</tr>
<!--Article with thumbnail-->
<tr>
</tr>
<!--Article with thumbnail-->
<tr>
</tr>
<!--Block Layout-->
<tr>
<td>
<table class="columns" width="650" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr valign="top">
<td class="columncontainer" width="50%">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td width="50%" valign="top" bgcolor="#f9f9f9">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
<td class="columncontainer" width="50%">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
</tr>
</tbody>
</table>
</td>
</tr>
<tr valign="top">
</tr>
</tbody>
</table>
</td>
</tr>
<!--2 Column-->
<tr>
<td class="borderbottom" style="padding: 10px 0px 0px 30px;">
<table class="columns" width="620" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr valign="top">
<td class="columncontainer" width="50%">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td class="h2" style="color: #45C2B1">NUESTRAS REDES SOCIALES</td>
<td style="padding: 0 0 30px 0;"></td>
</tr>
<tr>
</tr>
<tr>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<!--3 Column-->
<tr>
<td class="borderbottom" style="padding: 10px 0px 0px 30px;">
<table class="columns" width="620" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr valign="top">
<td class="columncontainer" width="33%">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td style="padding: 20px 15px 15px 0px;" valign="top">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td style="padding: 0 0 20px 0;" align="center">
<a href="https://www.facebook.com/aldahotels" target="_blank">
<img src="http://www.aldahotels.es/firma/email/llegada/fb.png" alt="Facebook" width="51" height="50" border="0">
</a>
</td>
</tr>
<tr>
<td class="h2" style="font-family: sans-serif; font-size: 18px; font-weight: bold; color: #3B5998; padding: 0 0 0 0; text-transform: uppercase; letter-spacing: 0.5px;"
align="center"> Facebook</td>
</tr>
<tr>
<td style="padding: 20px 0 10px 0;" align="center">
<table style="width: 50px;" width="50px" height="5px" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td background-color="#3B5998" style="font-size: 1px; line-height: 5px;" height="5px">
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td class="paragraph" style="padding: 10px 0 0 0;" align="center"> Toda la actualidad de nuestros alojamientos, así como ofertas y promociones.</td>
</tr>
<tr>
<td style="padding: 20px 0 20px 0; " align="center">
<table cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td align="center">
<!--[if gte mso 12>
<div style="border: 2px solid #5e96ea;">
<![endif]-->
<a href="https://www.facebook.com/aldahotels" target="_blank" style="text-decoration: none; color: #FFFFFF; font-size: 20px; padding: 10px 20px 10px 20px;">
<div style="padding: 0.5em; background-color: #45C2B1; border-color: #45C2B1; border-width: 2px;border-style:solid; border-bottom-style: solid;border-left-style: solid;border-right-style: solid;border-top-style: solid;-webkit-border-radius: 10; -moz-border-radius: 10; border-radius: 10px;">Dale
a Me gusta</div>
</a>
<!--[if gte mso 12>
</div>
<![endif]-->
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
<td class="columncontainer" width="33%">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td style="padding: 20px 15px 15px 0px;" valign="top">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td style="padding: 0 0 20px 0;" align="center">
<a href="https://www.instagram.com/aldahotels" target="_blank">
<img src="http://www.aldahotels.es/firma/email/llegada/ig.png" alt="Instagram" width="50" height="50" border="0">
</a>
</td>
</tr>
<tr>
<td class="h2" style="font-family: sans-serif; font-size: 18px; font-weight: bold; color: #E56459; padding: 0 0 0 0; text-transform: uppercase; letter-spacing: 0.5px;"
align="center"> Instagram</td>
</tr>
<tr>
<td style="padding: 20px 0 10px 0;" align="center">
<table style="width: 50px;" width="50px" height="5px" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td background-color="#E56459" style="font-size: 1px; line-height: 5px;" height="5px">
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td class="paragraph" style="padding: 10px 0 0 0;" align="center"> Cada detalle cuenta, y es por eso que tratamos de reflejarlo en nuestras fotos.</td>
</tr>
<tr>
<td style="padding: 20px 0 20px 0; " align="center">
<table cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td align="center">
<!--[if gte mso 12>
<div style="border: 2px solid #5e96ea;">
<![endif]-->
<a href="https://www.instagram.com/aldahotels/" target="_blank" style="text-decoration: none; color: #FFFFFF; font-size: 20px; padding: 10px 20px 10px 20px;">
<div style="padding: 0.5em; background-color: #45C2B1; border-color: #45C2B1; border-width: 2px;border-style:solid; border-bottom-style: solid;border-left-style: solid;border-right-style: solid;border-top-style: solid;-webkit-border-radius: 10; -moz-border-radius: 10; border-radius: 10px;">#Entra
</div>
</a>
<!--[if gte mso 12>
</div>
<![endif]-->
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
<td class="columncontainer" width="33%">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td style="padding: 20px 15px 15px 0px;" valign="top">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td style="padding: 0 0 20px 0;" align="center">
<a href="https://www.twitter.com/aldahotels" target="_blank">
<img src="http://www.aldahotels.es/firma/email/llegada/tw.png" alt="Twitter" width="50" height="50" border="0">
</a>
</td>
</tr>
<tr>
<td class="h2" style="font-family: sans-serif; font-size: 18px; font-weight: bold; color: #1DA1F2; padding: 0 0 0 0; text-transform: uppercase; letter-spacing: 0.5px;"
align="center"> Twitter</td>
</tr>
<tr>
<td style="padding: 20px 0 10px 0;" align="center">
<table style="width: 50px;" width="50px" height="5px" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td background-color="#1DA1F2" style="font-size: 1px; line-height: 5px;" height="5px">
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td class="paragraph" style="padding: 10px 0 0 0;" align="center"> Propuestas al minuto para hacer de tu viaje una experiencia inmejorable.</td>
</tr>
<tr>
<td style="padding: 20px 0 20px 0; " align="center">
<table cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td align="center">
<!--[if gte mso 12>
<div style="border: 2px solid #5e96ea;">
<![endif]-->
<a href="https://www.twitter.com/aldahotels" target="_blank" style="text-decoration: none; color: #FFFFFF; font-size: 20px; padding: 10px 20px 10px 20px;">
<div style="padding: 0.5em; background-color: #45C2B1; border-color: #45C2B1; border-width: 2px;border-style:solid; border-bottom-style: solid;border-left-style: solid;border-right-style: solid;border-top-style: solid;-webkit-border-radius: 10; -moz-border-radius: 10; border-radius: 10px;">Síguenos
</div>
</a>
<!--[if gte mso 12>
</div>
<![endif]-->
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<!--2 Column-->
<tr>
</tr>
<!--1 Column-->
<tr>
<td style="padding: 30px 30px 20px 30px;background-color:#45C2B1">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td class="h2" style="font-family: sans-serif; font-size: 18px; font-weight: bold; color: #FFFFFF; padding: 0 0 0 0; text-transform: uppercase; letter-spacing: 0.5px;" align="center"> ¡Esperamos verte
pronto!</td>
</tr>
<tr>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td class="borderbottom" style="padding: 45px 0px 30px 30px;" valign="middle" align="center">
<table class="columns" width="620" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr valign="top">
</tr>
</tbody>
</table>
<img src="http://www.aldahotels.es/firma/email/llegada/logo.png" alt="Alda Hotels" width="300" height="75">
<table class="columns" width="620" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr valign="top">
</tr>
</tbody>
</table>
</td>
</tr>
<!--Footer-->
<tr>
<td class="footer">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td class="smalltext" style="color: #828282; padding: 20px 20px 20px 20px;" align="justify">“En cumplimiento de la Ley 34/2002 de Servicios de la Sociedad de la Información y del Comercio Electrónico (LSSI-CE), así como lo dispuesto en el Reglamento General de Protección de Datos y demás legislación concordante, le informamos de que sus datos personales figuran en un sistema de tratamiento automatizado cuya responsabilidad es de Hoteles Rías Altas SL, con dirección postal a efecto de notificación en PLAZA DE ALGALIA DE ARRIBA, Nº 3 15704 SANTIAGO DE COMPOSTELA. Los datos personales que existen en nuestro poder están protegidos por nuestra Política de Privacidad y serán tratados con la finalidad de atender su solicitud de información o contacto, así como mantenerle informado de nuestros productos y servicios, actuales o futuros. Los datos no se cederán a terceros salvo en los casos en que exista una obligación legal. Para ejercer sus derechos de acceso, rectificación, cancelación, oposición, limitación, supresión y/o portabilidad debe enviar un correo electrónico a protecciondatos@aldahotels.com” “Este correo podría ser confidencial. Si recibe este e-mail por error, por favor elimínelo, así como cualquier documento adjunto, y notifíquelo a su emisor. Si usted no es el destinatario del mensaje, sepa que no está permitida ninguna difusión, copia o utilización no autorizada.”</td>
</tr>
<tr>
</tr>
</tbody>
</table>
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td class="listitem" style="color: #0A5F19; padding: 0px 0px 0px 0px" valign="middle" align="center">
<img src="http://www.aldahotels.es/firma/email/llegada/eco.png" width="30" height="30">
<strong>Antes de imprimir este mensaje, compruebe que es verdaderamente necesario. El medioambiente es cosa de todos. </strong>
</td>
</tr>
<tr>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<!--[if (gte mso 9)|(IE)]>
</td>
</tr>
</table>
<![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
]]>
</field>
</record>
</data>
</odoo>

View File

@@ -1,583 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE xml>
<odoo>
<data noupdate="1">
<!-- Email Template For PMS Reservation -->
<record id="mail_template_pms_exit" model="mail.template">
<field name="name">Exit Reservation-Send by Email</field>
<field
name="subject"
>Gracias por alojarse con nosotros en ${object.company_id.property_name}</field>
<field name="partner_to">${(object.partner_id.id or '')}</field>
<field name="model_id" ref="pms.model_pms_folio" />
<field name="auto_delete" eval="True" />
<field
name="body_html"
><![CDATA[<style type="text/css">/*Global Styles*/
.marco {bgcolor:#f6f6f6; margin: 0; padding: 0; min-width: 100%!important;}
a { color: #5e96ea; text-decoration: none; font-weight: bold;}
img {height: auto;}
.content { border: 1px solid #eeeeee; }
.logo {font-family: sans-serif; font-size: 36px; font-weight: bold; color: #ffffff;}
.link a {font-family: sans-serif; font-size: 12px; color: #ffffff;}
.subheading {font-size: 14px; color: #cccccc; font-family: sans-serif; font-weight: bold; padding: 0 0 0 0; text-transform: uppercase; letter-spacing: 1px;}
.h1 {font-family: sans-serif; font-size: 48px; font-weight: bold; line-height: 56px; color: #ffffff; padding: 0 0 0 0;}
.h2 {font-family: sans-serif; font-size: 18px; font-weight: bold; color: #444444; padding: 0 0 0 0; text-transform: uppercase; letter-spacing: 0.5px;}
.h3 {font-family: sans-serif; font-size: 24px; font-weight: regular; color: #555555; padding: 0 0 0 0;}
.h4 {font-family: sans-serif; font-size: 18px; font-weight: bold; color: #666666; padding: 0 0 0 0;}
.paragraph {font-family: sans-serif; font-size: 14px; line-height: 22px; color: #666666; font-weight: 200; padding: 20px 0 0 0;}
.listitem {font-family: sans-serif; font-size: 15px; color: #666666; font-weight: 200; padding: 0 0 20px 0;}
.smalltext { font-family: sans-serif; font-size: 14px; color: #cccccc; padding: 3px 0 0 0; }
.borderbottom {border-bottom: 1px solid #f2eeed;}
/*Media Queries*/
@media only screen and (max-width: 651px){
.columns{width:100% !important;}
.columncontainer{display:block !important; width:100% !important;}
.paragraph, .listitem {font-size: 18px;}
.link { float: left;}
}
@media only screen and (min-width: 651px) {
.content {width: 650px !important;}
}</style>
<div style="background-color:#ffffff; margin: 0; padding: 0; min-width: 100%!important;font-family: sans-serif; font-size: 16px; line-height: 22px; color: #666666; font-weight: 200; padding: 20px 0 0 0;">
<table width="100%" bgcolor="#ffffff" border="0" cellpadding="0" cellspacing="0">
<tr>
<td>
<!--[if (gte mso 9)|(IE)]>
<table width="540" align="center" cellpadding="0" cellspacing="0" border="0">
<tr>
<td>
<![endif]-->
<!--Content Wrapper-->
<table class="content" background-color="#f6f6f6" align="center" cellpadding="0" cellspacing="0" border="0" style="width: 650px !important; border: 1px solid #eeeeee; background-color:#f6f6f6;">
<!--Header-->
<tr>
<td style="padding: 30px 30px 20px 30px;background-color:#1B1B1B">
<table border="0x" cellpadding="0" cellspacing="0" width="100%" class="columns">
<tr valign="top">
</tr>
</table>
<img src="https://www.aldahotels.es/firma/email/llegada/logobl.png" width="251" height="57" alt="Alda Hotels"/>
<table border="0x" cellpadding="0" cellspacing="0" width="100%" class="columns">
<tr valign="top">
<td width="50%" valign="middle" class="columncontainer" >
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td align="right" valign="middle" style="padding: 0px 0px 0px 0px;">
<table border="0" cellspacing="0" cellpadding="0" class="link">
<tr>
<td style="padding: 0px 15px 0px 0px;"><a href="https://www.aldahotels.es/" style="color:#FFFFFF" >www.aldahotels.com</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<tr>
</tr>
<!--1 Column-->
<tr>
<td class="borderbottom" style="padding: 60px 30px 40px 30px;">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" class="h2" style="font-family: sans-serif; font-size: 18px; font-weight: bold; color: #444444; padding: 0 0 0 0; text-transform: uppercase; letter-spacing: 0.5px;"> ¡Muchas gracias por tu visita!
</td>
</tr>
<tr>
<td align="center" style="padding: 20px 0 0 0;">
<p style="font-size: 2em; line_height=0px; color: #C50967;">__</p>
</td>
</tr>
<tr>
<td align="justify" class="paragraph" style="padding: 10px 0 0 0;">Hola ${object.partner_id.firstname},<br />
Esperamos que hayas disfrutado de la ciudad, y que muy especialmente te hayas sentido a gusto en nuestro alojamiento. Todo el equipo de <strong>${object.company_id.property_name}</strong> te agradece tu estancia en nuestro centro y te desea un feliz regreso.<br />
Recibe un cordial saludo y esperamos volver a verte.<br />
</td>
</tr>
<tr>
</tr>
</table>
</td>
</tr>
<!--Section with sidebar-->
<tr>
<td class="borderbottom" style="padding: 10px 0px 10px 30px;">
<table border="0" cellpadding="0" cellspacing="0" width="620" class="columns">
<tr valign="top">
<td width="50%" class="columncontainer">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td valign="top" style="padding: 20px 30px 20px 0px;">
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" class="h2" style="font-family: sans-serif; font-size: 18px; font-weight: bold; color: #444444; padding: 0 0 0 0; text-transform: uppercase; letter-spacing: 0.5px;"> Disfruta de tu descuento de cliente
</td>
</tr>
<tr>
<td align="center" style="padding: 20px 0 0 0;">
<p style="font-size: 2em; line_height=0px; color: #C50967;">__</p>
</td>
</tr>
<tr>
</tr>
</table>
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="justify" class="paragraph" style="padding: 10px 0 0 0;">Solo por ser cliente de Alda Hotels disfruta, automáticamente, de <strong>hasta un 10% de descuento en cualquiera de nuestros establecimientos</strong>, totalmente transferible y sin fecha de caducidad.<br />
</td>
</tr>
<tr>
</tr>
</table>
</td>
</tr>
</table>
<img src="https://www.aldahotels.es/firma/email/salida/descuento.png" width="600" height="170" alt="Descuento 10%"/>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<!--Section with sidebar-->
<tr>
<td class="borderbottom" style="padding: 10px 0px 10px 30px;">
<table border="0" cellpadding="0" cellspacing="0" width="620" class="columns">
<tr valign="top">
<td width="50%" class="columncontainer">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td valign="top" style="padding: 20px 30px 20px 0px;">
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" class="h2" style="font-family: sans-serif; font-size: 18px; font-weight: bold; color: #444444; padding: 0 0 0 0; text-transform: uppercase; letter-spacing: 0.5px;"> ¿Tienes algo que comentarnos?
</td>
</tr>
<tr>
<td align="center" style="padding: 20px 0 0 0;">
<p style="font-size: 2em; line_height=0px; color: #C50967;">__</p>
</td>
</tr>
<tr>
</tr>
</table>
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="justify" class="paragraph" style="padding: 10px 0 30px 0;">Cada día intentamos dar un mejor servicio, es por ello que si quieres aportar alguna propuesta o crítica constructiva, te invitamos a utilizar el siguiente formulario. <strong>Estamos encantados de recibir tus propuestas</strong>. Todas serán leídas y tenidas en cuenta, y atenderemos aquellas que nos permitan nuestros medios y posibilidades.<br />
</td>
</tr>
<tr>
<td align="center">
<!--[if gte mso 12>
<div style="border: 2px solid #5e96ea;">
<![endif]-->
<a href="https://goo.gl/forms/GbhPNgcGGSMQT2rG2" target="_blank" style="text-decoration: none; color: #FFFFFF; font-size: 20px; padding: 10px 20px 10px 20px;">
<div style="padding: 0.5em; background-color: #C50967; border-color: #C50967; border-width: 2px;border-style:solid; border-bottom-style: solid;border-left-style: solid;border-right-style: solid;border-top-style: solid;-webkit-border-radius: 10; -moz-border-radius: 10; border-radius: 10px;">Entrar al formulario ▶
</div>
</a>
<!--[if gte mso 12>
</div>
<![endif]-->
</td>
</tr>
</table>
<table border="0" cellspacing="0" cellpadding="0">
<tr>
</tr>
<tr>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<!--Article with thumbnail-->
<tr>
</tr>
<!--2 Column-->
<tr>
</tr>
<!--Article with thumbnail-->
<tr>
</tr>
<!--Article with thumbnail-->
<tr>
</tr>
<!--Article with thumbnail-->
<tr>
<td class="borderbottom" style="padding: 30px 0px 0px 0px;">
<table border="0" cellpadding="0" cellspacing="0" width="650" class="columns">
<tr valign="top">
<td width="10%" class="columncontainer">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td height="115" style="padding: 20px 0px 0px 30px;">
<img src="http://www.aldahotels.es/firma/email/llegada/localizacion.png" border="0" alt="Localización" />
</td>
</tr>
</table>
</td>
<td width="90%" class="columncontainer">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td valign="top" style="padding: 20px 30px 20px 30px;">
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td class="h2" style="color: #C50967"> nuestros alojamientos</td>
</tr>
<tr>
<td align="justify" class="subheading" style="color: #555555"><strong>Descubre dónde estamos</strong>
</td>
</tr>
<tr>
<td align="justify" class="paragraph" style="padding: 10px 0 10px 0;">Estamos presentes a lo largo del territorio peninsular. Si quieres saber dónde puedes alojarte con nosotros, pincha en el mapa.<br />
</td>
</tr>
</table>
<center>
<a href="https://www.aldahotels.es/wpresscorporate/nuestros-alojamientos/"><img src="http://www.aldahotels.es/firma/email/salida/mapaalojamientos.png" border="0" alt="Mapa" />
</a>
<table border="0" cellspacing="0" cellpadding="0">
</center>
<tr>
</tr>
<tr>
</tr>
</table>
</td>
</tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
</tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<!--Section with sidebar-->
<tr>
</tr>
<!--Article with thumbnail-->
<tr>
</tr>
<!--Article with thumbnail-->
<tr>
</tr>
<!--Block Layout-->
<tr>
<td>
<table border="0" cellpadding="0" cellspacing="0" width="650" class="columns">
<tr valign="top">
<td width="50%" class="columncontainer">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td valign="top" width="50%" bgcolor="#f9f9f9">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
</tr>
</table>
</td>
</tr>
</table>
</td>
<td width="50%" class="columncontainer">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
</tr>
</table>
</td>
</tr>
<tr valign="top">
<td width="50%" class="columncontainer">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
</tr>
</table>
</td>
<td width="50%" class="columncontainer">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td valign="top" width="50%" bgcolor="#C50967">
<table border="0" cellpadding="0" cellspacing="0" width="100%" >
<tr>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<!--2 Column-->
<tr>
<td class="borderbottom" style="padding: 10px 0px 0px 30px;">
<table border="0" cellpadding="0" cellspacing="0" width="620" class="columns">
<tr valign="top">
<td width="50%" class="columncontainer">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td class="h2" style="font-family: sans-serif; font-size: 18px; font-weight: bold; color: #C50967; padding: 0 0 0 0; text-transform: uppercase; letter-spacing: 0.5px;">NUESTRAS REDES SOCIALES
</td>
<td style="padding: 0 0 50px 0;">&nbsp;
</td>
</tr>
<tr>
</tr>
<tr>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<!--3 Column-->
<tr>
<td class="borderbottom" style="padding: 10px 0px 0px 30px;">
<table border="0" cellpadding="0" cellspacing="0" width="620" class="columns">
<tr valign="top">
<td width="33%" class="columncontainer">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td valign="top" style="padding: 20px 15px 15px 0px;">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" style="padding: 0 0 20px 0;"><a href="https://www.facebook.com/aldahotels" target="_blank"><img src="http://www.aldahotels.es/firma/email/llegada/fb.png" width="51" height="50" border="0" alt="Facebook" /></a>
</td>
</tr>
<tr>
<td align="center" class="h2" style="font-family: sans-serif; font-size: 18px; font-weight: bold; color: #3B5998; padding: 0 0 0 0; text-transform: uppercase; letter-spacing: 0.5px;"> Facebook</td>
</tr>
<tr>
<td align="center" style="padding: 20px 0 10px 0;">
<table width="50px" style="width: 50px;" height="5px" border="0" cellspacing="0" cellpadding="0" >
<tr>
<td background-color="#3B5998" height="5px" style="font-size: 1px; line-height: 5px;">&nbsp;
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center" class="paragraph" style="padding: 10px 0 0 0;"> Toda la actualidad de nuestros alojamientos, así como ofertas y promociones.</td>
</tr>
<tr>
<td align="center" style="padding: 20px 0 20px 0; ">
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center">
<!--[if gte mso 12>
<div style="border: 2px solid #5e96ea;">
<![endif]-->
<a href="https://www.facebook.com/aldahotels" target="_blank" style="text-decoration: none; color: #FFFFFF; font-size: 20px; padding: 10px 20px 10px 20px;">
<div style="padding: 0.5em; background-color: #C50967; border-color: #C50967; border-width: 2px;border-style:solid; border-bottom-style: solid;border-left-style: solid;border-right-style: solid;border-top-style: solid;-webkit-border-radius: 10; -moz-border-radius: 10; border-radius: 10px;">Dale a Me gusta</div>
</a>
<!--[if gte mso 12>
</div>
<![endif]-->
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<td width="33%" class="columncontainer">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td valign="top" style="padding: 20px 15px 15px 0px;">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" style="padding: 0 0 20px 0;"><a href="https://www.instagram.com/aldahotels" target="_blank"><img src="http://www.aldahotels.es/firma/email/llegada/ig.png" width="50" height="50" border="0" alt="Instagram" /></a>
</td>
</tr>
<tr>
<td align="center" class="h2" style="font-family: sans-serif; font-size: 18px; font-weight: bold; color: #E56459; padding: 0 0 0 0; text-transform: uppercase; letter-spacing: 0.5px;"> Instagram</td>
</tr>
<tr>
<td align="center" style="padding: 20px 0 10px 0;">
<table width="50px" style="width: 50px;" height="5px" border="0" cellspacing="0" cellpadding="0" >
<tr>
<td background-color="#E56459" height="5px" style="font-size: 1px; line-height: 5px;">&nbsp;
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center" class="paragraph" style="padding: 10px 0 0 0;"> Cada detalle cuenta, y es por eso que tratamos de reflejarlo en nuestras fotos.
</td>
</tr>
<tr>
<td align="center" style="padding: 20px 0 20px 0; ">
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center">
<!--[if gte mso 12>
<div style="border: 2px solid #5e96ea;">
<![endif]-->
<a href="https://www.instagram.com/aldahotels/" target="_blank" style="text-decoration: none; color: #FFFFFF; font-size: 20px; padding: 10px 20px 10px 20px;">
<div style="padding: 0.5em; background-color: #C50967; border-color: #C50967; border-width: 2px;border-style:solid; border-bottom-style: solid;border-left-style: solid;border-right-style: solid;border-top-style: solid;-webkit-border-radius: 10; -moz-border-radius: 10; border-radius: 10px;">#Entra
</div>
</a>
<!--[if gte mso 12>
</div>
<![endif]-->
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<td width="33%" class="columncontainer">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td valign="top" style="padding: 20px 15px 15px 0px;">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" style="padding: 0 0 20px 0;"><a href="https://www.twitter.com/aldahotels" target="_blank"><img src="http://www.aldahotels.es/firma/email/llegada/tw.png" width="50" height="50" border="0" alt="Twitter" /></a>
</td>
</tr>
<tr>
<td align="center" class="h2" style="font-family: sans-serif; font-size: 18px; font-weight: bold; color: #1DA1F2; padding: 0 0 0 0; text-transform: uppercase; letter-spacing: 0.5px;"> Twitter
</td>
</tr>
<tr>
<td align="center" style="padding: 20px 0 10px 0;">
<table width="50px" style="width: 50px;" height="5px" border="0" cellspacing="0" cellpadding="0" >
<tr>
<td background-color="#1DA1F2" height="5px" style="font-size: 1px; line-height: 5px;">&nbsp;
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center" class="paragraph" style="padding: 10px 0 0 0;"> Propuestas al minuto para hacer de tu viaje una experiencia inmejorable.
</td>
</tr>
<tr>
<td align="center" style="padding: 20px 0 20px 0; ">
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center">
<!--[if gte mso 12>
<div style="border: 2px solid #5e96ea;">
<![endif]-->
<a href="https://www.twitter.com/aldahotels" target="_blank" style="text-decoration: none; color: #FFFFFF; font-size: 20px; padding: 10px 20px 10px 20px;">
<div style="padding: 0.5em; background-color: #C50967; border-color: #C50967; border-width: 2px;border-style:solid; border-bottom-style: solid;border-left-style: solid;border-right-style: solid;border-top-style: solid;-webkit-border-radius: 10; -moz-border-radius: 10; border-radius: 10px;">Síguenos
</div>
</a>
<!--[if gte mso 12>
</div>
<![endif]-->
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<!--2 Column-->
<tr>
</tr>
<!--1 Column-->
<tr>
<td style="padding: 30px 30px 20px 30px;background-color:#C50967">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" class="h2" style="font-family: sans-serif; font-size: 18px; font-weight: bold; color: #FFFFFF; padding: 0 0 0 0; text-transform: uppercase; letter-spacing: 0.5px;"> ¡Muchas gracias por alojarte con nosotros!
</td>
</tr>
<tr>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center" valign="middle" class="borderbottom" style="padding: 45px 0px 30px 30px;">
<table border="0" cellpadding="0" cellspacing="0" width="620" class="columns">
<tr valign="top">
</tr>
</table>
<img src="https://www.aldahotels.es/firma/email/llegada/logoft.png" width="300" height="75" alt="Alda Hotels"/>
<table border="0" cellpadding="0" cellspacing="0" width="620" class="columns">
<tr valign="top">
</tr>
</table>
</td>
</tr>
<!--Footer-->
<tr>
<td class="footer">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="justify" class="smalltext" style="color: #828282; padding: 20px 20px 20px 20px;">En cumplimiento de la Ley 34/2002 de Servicios de la Sociedad de la Información y del Comercio Electrónico, así como de la Ley Orgánica 15/1999 del 13 de Diciembre de Protección de Datos de Carácter Personal y demás legislación concordante, se le informa que sus datos personales figuran en un fichero automatizado cuya responsabilidad es de ALDA COMPOSTELA S.L. Praza da Algalia de Arriba, 3 C.P. 15704 Santiago de Compostela. Los datos personales que existen en nuestro poder están protegidos por nuestra Política de Privacidad y solo serán utilizados para los fines propios de nuestra actividad. Para ejercer sus derechos de acceso, rectificación, cancelación u oposición debe enviar un correo electrónico a info@aldahotels.com indicándonos la opción a realizar. Este correo podría ser confidencial. Si recibe este e-mail por error, por favor elimínelo, así como cualquier documento adjunto, y notifíquelo a su emisor. Si usted no es el destinatario del mensaje, sepa que no está permitida ninguna difusión, copia o utilización no autorizada.</td>
</tr>
<tr>
</tr>
</table>
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" valign="middle" class="listitem" style="color: #0A5F19; padding: 0px 0px 0px 0px"><img src="https://www.aldahotels.es/firma/email/llegada/eco.png" width="30" height="30" /><strong>Antes de imprimir este mensaje, compruebe que es verdaderamente necesario. El medioambiente es cosa de todos. </strong>
</td>
</tr>
<tr> </tr>
</table>
</td>
</tr>
</table>
<!--[if (gte mso 9)|(IE)]>
</td>
</tr>
</table>
<![endif]-->
</td>
</tr>
</table>
</div>]]></field>
</record>
</data>
</odoo>

File diff suppressed because it is too large Load Diff

View File

@@ -34,11 +34,4 @@
sequence="10"
groups="pms.group_pms_manager"
/>
<menuitem
id="pms_massive_change"
name="Massive Changes"
parent="pms.configuration_others"
sequence="10"
action="action_pms_massive_change"
/>
</odoo>

View File

@@ -7896,7 +7896,6 @@ msgstr "Precio de venta"
#. module: hotel
#: model:ir.model.fields,field_description:hotel.field_hotel_folio_channel_type
#: model:ir.model.fields,field_description:hotel.field_hotel_folio_team_id
#: model:ir.model.fields,field_description:hotel.field_hotel_folio_wizard_channel_type
#: model:ir.model.fields,field_description:hotel.field_hotel_reservation_channel_type
#: model:ir.model.fields,field_description:hotel.field_hotel_service_channel_type

View File

@@ -166,24 +166,6 @@ class PmsCheckinPartner(models.Model):
record.update(vals)
if record.reservation_id.state == "confirm":
record.reservation_id.state = "booking"
if record.reservation_id.splitted:
master_reservation = (
record.reservation_id.parent_reservation
or record.reservation_id
)
splitted_reservs = self.env["pms.reservation"].search(
[
("splitted", "=", True),
"|",
("parent_reservation", "=", master_reservation.id),
("id", "=", master_reservation.id),
("folio_id", "=", record.folio_id.id),
("id", "!=", record.id),
("state", "=", "confirm"),
]
)
if splitted_reservs:
splitted_reservs.update({"state": "booking"})
return {
"type": "ir.actions.do_nothing",
}

View File

@@ -30,7 +30,7 @@ class PmsFolio(models.Model):
@api.model
def _get_default_pms_property(self):
return self.env.user.pms_property_id
return self.env.user.pms_property_id #TODO: Change by property env variable (like company)
# Fields declaration
name = fields.Char(
@@ -128,7 +128,6 @@ class PmsFolio(models.Model):
readonly=False,
help="Invoice address for current group.",
)
partner_parent_id = fields.Many2one(related="partner_id.parent_id")
partner_invoice_state_id = fields.Many2one(related="partner_invoice_id.state_id")
partner_invoice_country_id = fields.Many2one(
related="partner_invoice_id.country_id"
@@ -140,14 +139,6 @@ class PmsFolio(models.Model):
segmentation_ids = fields.Many2many(
"res.partner.category", string="Segmentation", ondelete="restrict"
)
team_id = fields.Many2one(
"crm.team",
string="Sales Team",
ondelete="restrict",
compute="_compute_team_id",
store=True,
readonly=False,
)
client_order_ref = fields.Char(string="Customer Reference", copy=False)
reservation_type = fields.Selection(
[("normal", "Normal"), ("staff", "Staff"), ("out", "Out of Service")],
@@ -254,28 +245,6 @@ class PmsFolio(models.Model):
readonly=True,
default="no",
)
partner_invoice_vat = fields.Char(related="partner_invoice_id.vat")
partner_invoice_name = fields.Char(
related="partner_invoice_id.name", string="Partner Name"
)
partner_invoice_street = fields.Char(
related="partner_invoice_id.street", string="Street"
)
partner_invoice_street2 = fields.Char(
related="partner_invoice_id.street", string="Street2"
)
partner_invoice_zip = fields.Char(related="partner_invoice_id.zip")
partner_invoice_city = fields.Char(related="partner_invoice_id.city")
partner_invoice_email = fields.Char(related="partner_invoice_id.email")
partner_invoice_lang = fields.Selection(related="partner_invoice_id.lang")
# WorkFlow Mail Fields-----------------------------------------------
has_confirmed_reservations_to_send = fields.Boolean(
compute="_compute_has_confirmed_reservations_to_send"
)
has_cancelled_reservations_to_send = fields.Boolean(
compute="_compute_has_cancelled_reservations_to_send"
)
has_checkout_to_send = fields.Boolean(compute="_compute_has_checkout_to_send")
# Generic Fields-----------------------------------------------------
internal_comment = fields.Text(string="Internal Folio Notes")
cancelled_reason = fields.Text("Cause of cancelled")
@@ -328,14 +297,6 @@ class PmsFolio(models.Model):
or False
)
@api.depends("partner_id")
def _compute_team_id(self):
for folio in self:
folio.team_id = (
self.partner_id.team_id.id
or self.env["crm.team"]._get_default_team_id()
)
@api.depends(
"state", "reservation_ids.invoice_status", "service_ids.invoice_status"
)
@@ -490,89 +451,6 @@ class PmsFolio(models.Model):
}
record.update(vals)
@api.depends("reservation_ids")
def _compute_has_confirmed_reservations_to_send(self):
has_to_send = False
if self.reservation_type != "out":
for rline in self.reservation_ids:
if rline.splitted:
master_reservation = rline.parent_reservation or rline
has_to_send = (
self.env["pms.reservation"].search_count(
[
("splitted", "=", True),
("folio_id", "=", self.id),
("to_send", "=", True),
("state", "in", ("confirm", "booking")),
"|",
("parent_reservation", "=", master_reservation.id),
("id", "=", master_reservation.id),
]
)
> 0
)
elif rline.to_send and rline.state in ("confirm", "booking"):
has_to_send = True
break
self.has_confirmed_reservations_to_send = has_to_send
else:
self.has_confirmed_reservations_to_send = False
@api.depends("reservation_ids")
def _compute_has_cancelled_reservations_to_send(self):
has_to_send = False
if self.reservation_type != "out":
for rline in self.reservation_ids:
if rline.splitted:
master_reservation = rline.parent_reservation or rline
has_to_send = (
self.env["pms.reservation"].search_count(
[
("splitted", "=", True),
("folio_id", "=", self.id),
("to_send", "=", True),
("state", "=", "cancelled"),
"|",
("parent_reservation", "=", master_reservation.id),
("id", "=", master_reservation.id),
]
)
> 0
)
elif rline.to_send and rline.state == "cancelled":
has_to_send = True
break
self.has_cancelled_reservations_to_send = has_to_send
else:
self.has_cancelled_reservations_to_send = False
@api.depends("reservation_ids")
def _compute_has_checkout_to_send(self):
has_to_send = True
if self.reservation_type != "out":
for rline in self.reservation_ids:
if rline.splitted:
master_reservation = rline.parent_reservation or rline
nreservs = self.env["pms.reservation"].search_count(
[
("splitted", "=", True),
("folio_id", "=", self.id),
("to_send", "=", True),
("state", "=", "done"),
"|",
("parent_reservation", "=", master_reservation.id),
("id", "=", master_reservation.id),
]
)
if nreservs != len(self.reservation_ids):
has_to_send = False
elif not rline.to_send or rline.state != "done":
has_to_send = False
break
self.has_checkout_to_send = has_to_send
else:
self.has_checkout_to_send = False
# Action methods
def action_pay(self):
@@ -647,144 +525,6 @@ class PmsFolio(models.Model):
"target": "new",
}
def send_reservation_mail(self):
"""
This function opens a window to compose an email,
template message loaded by default.
@param self: object pointer
"""
# Debug Stop -------------------
# import wdb; wdb.set_trace()
# Debug Stop -------------------
self.ensure_one()
ir_model_data = self.env["ir.model.data"]
try:
template_id = ir_model_data.get_object_reference(
"pms", "email_template_reservation"
)[1]
except ValueError:
template_id = False
try:
compose_form_id = ir_model_data.get_object_reference(
"mail", "email_compose_message_wizard_form"
)[1]
except ValueError:
compose_form_id = False
ctx = dict()
ctx.update(
{
"default_model": "pms.folio",
"default_res_id": self._ids[0],
"default_use_template": bool(template_id),
"default_template_id": template_id,
"default_composition_mode": "comment",
"force_send": True,
"mark_so_as_sent": True,
}
)
return {
"type": "ir.actions.act_window",
"view_type": "form",
"view_mode": "form",
"res_model": "mail.compose.message",
"views": [(compose_form_id, "form")],
"view_id": compose_form_id,
"target": "new",
"context": ctx,
"force_send": True,
}
def send_exit_mail(self):
"""
This function opens a window to compose an email,
template message loaded by default.
@param self: object pointer
"""
# Debug Stop -------------------
# import wdb; wdb.set_trace()
# Debug Stop -------------------
self.ensure_one()
ir_model_data = self.env["ir.model.data"]
try:
template_id = ir_model_data.get_object_reference(
"pms", "mail_template_pms_exit"
)[1]
except ValueError:
template_id = False
try:
compose_form_id = ir_model_data.get_object_reference(
"mail", "email_compose_message_wizard_form"
)[1]
except ValueError:
compose_form_id = False
ctx = dict()
ctx.update(
{
"default_model": "pms.reservation",
"default_res_id": self._ids[0],
"default_use_template": bool(template_id),
"default_template_id": template_id,
"default_composition_mode": "comment",
"force_send": True,
"mark_so_as_sent": True,
}
)
return {
"type": "ir.actions.act_window",
"view_type": "form",
"view_mode": "form",
"res_model": "mail.compose.message",
"views": [(compose_form_id, "form")],
"view_id": compose_form_id,
"target": "new",
"context": ctx,
"force_send": True,
}
def send_cancel_mail(self):
"""
This function opens a window to compose an email,
template message loaded by default.
@param self: object pointer
"""
self.ensure_one()
ir_model_data = self.env["ir.model.data"]
try:
template_id = ir_model_data.get_object_reference(
"pms", "mail_template_pms_cancel"
)[1]
except ValueError:
template_id = False
try:
compose_form_id = ir_model_data.get_object_reference(
"mail", "email_compose_message_wizard_form"
)[1]
except ValueError:
compose_form_id = False
ctx = dict()
ctx.update(
{
"default_model": "pms.reservation",
"default_res_id": self._ids[0],
"default_use_template": bool(template_id),
"default_template_id": template_id,
"default_composition_mode": "comment",
"force_send": True,
"mark_so_as_sent": True,
}
)
return {
"type": "ir.actions.act_window",
"view_type": "form",
"view_mode": "form",
"res_model": "mail.compose.message",
"views": [(compose_form_id, "form")],
"view_id": compose_form_id,
"target": "new",
"context": ctx,
"force_send": True,
}
# ORM Overrides
@api.model
def create(self, vals):
@@ -842,7 +582,7 @@ class PmsFolio(models.Model):
for record in self:
if record.reservation_type == "normal" and record.reservation_ids:
filtered_reservs = record.reservation_ids.filtered(
lambda x: x.state != "cancelled" and not x.parent_reservation
lambda x: x.state != "cancelled"
)
mapped_checkin_partner = filtered_reservs.mapped(
"checkin_partner_ids.id"
@@ -853,54 +593,6 @@ class PmsFolio(models.Model):
)
record.checkin_partner_pending_count = sum(mapped_checkin_partner_count)
def get_grouped_reservations_json(self, state, import_all=False):
self.ensure_one()
info_grouped = []
for rline in self.reservation_ids:
if (
(import_all or rline.to_send)
and not rline.parent_reservation
and rline.state == state
):
dates = (rline.checkin, rline.checkout)
vals = {
"num": len(
self.reservation_ids.filtered(
lambda r: r.checkin == dates[0]
and r.checkout == dates[1]
and r.room_type_id.id == rline.room_type_id.id
and (r.to_send or import_all)
and not r.parent_reservation
and r.state == rline.state
)
),
"room_type": {
"id": rline.room_type_id.id,
"name": rline.room_type_id.name,
},
"checkin": dates[0],
"checkout": dates[1],
"nights": len(rline.reservation_line_ids),
"adults": rline.adults,
"childrens": rline.children,
}
founded = False
for srline in info_grouped:
if (
srline["num"] == vals["num"]
and srline["room_type"]["id"] == vals["room_type"]["id"]
and srline["checkin"] == vals["checkin"]
and srline["checkout"] == vals["checkout"]
):
founded = True
break
if not founded:
info_grouped.append(vals)
return sorted(
sorted(info_grouped, key=lambda k: k["num"], reverse=True),
key=lambda k: k["room_type"]["id"],
)
def _get_tax_amount_by_group(self):
self.ensure_one()
res = {}

View File

@@ -28,9 +28,7 @@ class PmsReservation(models.Model):
if folio and folio.reservation_ids:
return folio.reservation_ids[0].checkin
else:
tz_property = self.env.user.pms_property_id.tz
today = fields.Date.context_today(self.with_context(tz=tz_property))
return fields.Date.from_string(today).strftime(DEFAULT_SERVER_DATE_FORMAT)
return fields.Date.today()
def _get_default_checkout(self):
folio = False
@@ -41,11 +39,7 @@ class PmsReservation(models.Model):
if folio and folio.reservation_ids:
return folio.reservation_ids[0].checkout
else:
tz_property = self.env.user.pms_property_id.tz
today = fields.Date.context_today(self.with_context(tz=tz_property))
return (fields.Date.from_string(today) + timedelta(days=1)).strftime(
DEFAULT_SERVER_DATE_FORMAT
)
return fields.Date.today() + timedelta(1)
def _get_default_arrival_hour(self):
folio = False
@@ -71,6 +65,17 @@ class PmsReservation(models.Model):
else:
return default_departure_hour
def _get_default_segmentation(self):
folio = False
segmentation_ids = False
if "folio_id" in self._context:
folio = self.env["pms.folio"].search(
[("id", "=", self._context["folio_id"])]
)
if folio and folio.segmentation_ids:
segmentation_ids = folio.segmentation_ids
return segmentation_ids
@api.model
def _default_diff_invoicing(self):
"""
@@ -134,11 +139,6 @@ class PmsReservation(models.Model):
store=True,
readonly=False,
)
partner_invoice_state_id = fields.Many2one(related="partner_invoice_id.state_id")
partner_invoice_country_id = fields.Many2one(
related="partner_invoice_id.country_id"
)
partner_parent_id = fields.Many2one(related="partner_id.parent_id")
closure_reason_id = fields.Many2one(related="folio_id.closure_reason_id")
company_id = fields.Many2one(
related="folio_id.company_id", string="Company", store=True, readonly=True
@@ -170,8 +170,12 @@ class PmsReservation(models.Model):
)
# TODO: Warning Mens to update pricelist
checkin_partner_ids = fields.One2many("pms.checkin.partner", "reservation_id")
parent_reservation = fields.Many2one("pms.reservation", string="Parent Reservation")
segmentation_ids = fields.Many2many(related="folio_id.segmentation_ids")
segmentation_ids = fields.Many2many(
"res.partner.category",
string="Segmentation",
ondelete="restrict",
default=_get_default_segmentation,
)
currency_id = fields.Many2one(
"res.currency",
related="pricelist_id.currency_id",
@@ -251,18 +255,6 @@ class PmsReservation(models.Model):
default=_get_default_departure_hour,
help="Default Departure Hour (HH:MM)",
)
partner_invoice_vat = fields.Char(related="partner_invoice_id.vat")
partner_invoice_name = fields.Char(related="partner_invoice_id.name")
partner_invoice_street = fields.Char(
related="partner_invoice_id.street", string="Street"
)
partner_invoice_street2 = fields.Char(
related="partner_invoice_id.street", string="Street2"
)
partner_invoice_zip = fields.Char(related="partner_invoice_id.zip")
partner_invoice_city = fields.Char(related="partner_invoice_id.city")
partner_invoice_email = fields.Char(related="partner_invoice_id.email")
partner_invoice_lang = fields.Selection(related="partner_invoice_id.lang")
# TODO: As checkin_partner_count is a computed field, it can't not
# be used in a domain filer Non-stored field
# pms.reservation.checkin_partner_count cannot be searched
@@ -276,11 +268,6 @@ class PmsReservation(models.Model):
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",
)
overbooking = fields.Boolean("Is Overbooking", default=False)
reselling = fields.Boolean("Is Reselling", default=False)
nights = fields.Integer("Nights", compute="_computed_nights", store=True)
@@ -314,20 +301,6 @@ class PmsReservation(models.Model):
string="Internal Folio Notes", related="folio_id.internal_comment"
)
preconfirm = fields.Boolean("Auto confirm to Save", default=True)
# TODO: to_send in this module?¿
to_send = fields.Boolean(
"To Send", default=True, compute="_compute_to_send", store=True, readonly=False,
)
has_confirmed_reservations_to_send = fields.Boolean(
related="folio_id.has_confirmed_reservations_to_send", readonly=True
)
has_cancelled_reservations_to_send = fields.Boolean(
related="folio_id.has_cancelled_reservations_to_send", readonly=True
)
has_checkout_to_send = fields.Boolean(
related="folio_id.has_checkout_to_send", readonly=True
)
to_print = fields.Boolean("Print", help="Print in Folio Report", default=True)
invoice_status = fields.Selection(
[
("invoiced", "Fully Invoiced"),
@@ -423,7 +396,7 @@ class PmsReservation(models.Model):
@api.depends("reservation_line_ids", "reservation_line_ids.room_id")
def _compute_room_id(self):
for reservation in self:
for reservation in self.filtered("reservation_line_ids"):
reservation.room_id = reservation.reservation_line_ids[0].room_id
@api.depends("room_id")
@@ -530,6 +503,7 @@ class PmsReservation(models.Model):
# TODO: Warning change de pricelist?
reservation.pricelist_id = pricelist_id
#REVIEW: Dont run with set room_type_id -> room_id(compute)-> No set adults¿?
@api.depends("room_id")
def _compute_adults(self):
for reservation in self:
@@ -539,8 +513,6 @@ class PmsReservation(models.Model):
elif reservation.adults == False:
reservation.adults = 0
# REVIEW: Adult not computed with room_type set
@api.depends("checkin", "checkout", "state")
def _compute_to_send(self):
for reservation in self:
@@ -726,30 +698,6 @@ class PmsReservation(models.Model):
# Action methods
def open_invoices_reservation(self):
invoices = self.folio_id.mapped("move_ids")
action = self.env.ref("account.action_move_out_invoice_type").read()[0]
if len(invoices) > 1:
action["domain"] = [("id", "in", invoices.ids)]
elif len(invoices) == 1:
action["views"] = [(self.env.ref("account.view_move_form").id, "form")]
action["res_id"] = invoices.ids[0]
else:
action = self.env.ref("pms.action_view_folio_advance_payment_inv").read()[0]
action["context"] = {
"default_reservation_id": self.id,
"default_folio_id": self.folio_id.id,
}
return action
def create_invoice(self):
action = self.env.ref("pms.action_view_folio_advance_payment_inv").read()[0]
action["context"] = {
"default_reservation_id": self.id,
"default_folio_id": self.folio_id.id,
}
return action
def open_folio(self):
action = self.env.ref("pms.open_pms_folio1_form_tree_all").read()[0]
if self.folio_id:
@@ -904,7 +852,6 @@ class PmsReservation(models.Model):
"checkin": checkin or self.checkin,
"checkout": checkout or self.checkout,
"folio_id": self.folio_id.id,
"parent_reservation": self.parent_reservation.id,
"state": self.state,
"overbooking": self.overbooking,
"reselling": self.reselling,
@@ -933,23 +880,6 @@ class PmsReservation(models.Model):
record.reservation_line_ids.update({"cancel_discount": 0})
if record.folio_id.state != "confirm":
record.folio_id.action_confirm()
if record.splitted:
master_reservation = record.parent_reservation or record
splitted_reservs = pms_reserv_obj.search(
[
("splitted", "=", True),
"|",
("parent_reservation", "=", master_reservation.id),
("id", "=", master_reservation.id),
("folio_id", "=", record.folio_id.id),
("id", "!=", record.id),
("state", "not in", ("confirm", "booking")),
]
)
if master_reservation.checkin_partner_ids:
record.update({"state": "booking"})
splitted_reservs.confirm()
return True
def button_done(self):
@@ -971,20 +901,6 @@ class PmsReservation(models.Model):
_logger.info("Modified Reservation - No Penalty")
record.write({"state": "cancelled", "cancelled_reason": cancel_reason})
# record._compute_cancelled_discount()
if record.splitted:
master_reservation = record.parent_reservation or record
splitted_reservs = self.env["pms.reservation"].search(
[
("splitted", "=", True),
"|",
("parent_reservation", "=", master_reservation.id),
("id", "=", master_reservation.id),
("folio_id", "=", record.folio_id.id),
("id", "!=", record.id),
("state", "!=", "cancelled"),
]
)
splitted_reservs.action_cancel()
record.folio_id.compute_amount()
def compute_cancelation_reason(self):
@@ -1008,20 +924,6 @@ class PmsReservation(models.Model):
for record in self:
record.state = "draft"
record.reservation_line_ids.update({"cancel_discount": 0})
if record.splitted:
master_reservation = record.parent_reservation or record
splitted_reservs = self.env["pms.reservation"].search(
[
("splitted", "=", True),
"|",
("parent_reservation", "=", master_reservation.id),
("id", "=", master_reservation.id),
("folio_id", "=", record.folio_id.id),
("id", "!=", record.id),
("state", "!=", "draft"),
]
)
splitted_reservs.draft()
# INFO: This function is not in use and should include `dto` in the search
@api.model
@@ -1090,21 +992,6 @@ class PmsReservation(models.Model):
record.checkin_partner_ids.filtered(
lambda check: check.state == "booking"
).action_done()
if record.splitted:
master_reservation = record.parent_reservation or record
splitted_reservs = self.env["pms.reservation"].search(
[
("splitted", "=", True),
"|",
("parent_reservation", "=", master_reservation.id),
("id", "=", master_reservation.id),
("folio_id", "=", record.folio_id.id),
("id", "!=", record.id),
("state", "not in", ("cancelled", "done")),
]
)
if splitted_reservs:
splitted_reservs.update({"state": "done"})
return True
def action_checks(self):

View File

@@ -94,7 +94,6 @@ class PmsService(models.Model):
"Quantity", compute="_compute_product_qty", store=True, readonly=False,
)
is_board_service = fields.Boolean()
to_print = fields.Boolean("Print", help="Print in Folio Report")
# Non-stored related field to allow portal user to
# see the image of the product he has ordered
product_image = fields.Binary(

View File

@@ -90,7 +90,7 @@
<tbody class="sale_tbody">
<!-- Lines associated -->
<t t-foreach="doc.reservation_ids" t-as="l">
<t t-if="l.to_print == True and l.price_total > 0">
<t t-if="l.price_total > 0">
<tr>
<td>
<span t-field="l.name" />
@@ -132,7 +132,7 @@
</t>
</t>
<t t-foreach="doc.service_ids" t-as="l">
<t t-if="l.to_print == True and l.price_total > 0">
<t t-if="l.price_total > 0">
<tr>
<td>
<span t-field="l.name" />

View File

@@ -1,22 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<act_window
id="action_view_folio_advance_payment_inv"
name="Invoice Folio"
binding_model="pms.folio"
res_model="folio.advance.payment.inv"
target="new"
view_mode="form"
/>
<record model="ir.ui.view" id="pms_folio_view_form">
<field name="name">pms.folio.form</field>
<field name="model">pms.folio</field>
<field name="arch" type="xml">
<form string="Folio">
<header>
<!-- <field name="has_confirmed_reservations_to_send" invisible="1" /> -->
<!-- <field name="has_cancelled_reservations_to_send" invisible="1" /> -->
<!-- <field name="has_checkout_to_send" invisible="1" /> -->
<header>out_to_send" invisible="1" /> -->
<button
name="action_confirm"
states="draft"
@@ -24,21 +13,6 @@
class="btn-primary"
type="object"
/>
<!-- <button name="send_reservation_mail" type="object" string="Send Confirmation Email"
attrs="{'invisible': [('has_confirmed_reservations_to_send', '=', False)]}" class="oe_highlight"/> -->
<!-- <button name="send_cancel_mail" type="object" string="Send Cancel Email"
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="%(pms.action_view_folio_advance_payment_inv)d"
string="Create Invoice"
type="action"
class="btn-primary"
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"
@@ -51,7 +25,6 @@
string="Set to Done"
help="If a Folio is done, you cannot modify it manually anymore. However, you will still be able to invoice. This is used to freeze the Folio."
/>
<!-- <button name="print_quotation" string="Print" type="object" states="sent,sale"/> -->
<field
name="state"
select="2"
@@ -226,80 +199,11 @@
these are the billing information associated with the booking client or the company (if a company is assigned). If you want to bill an independent contact, you can select it in the billing assistant
</div>
<group>
<field name="partner_parent_id" />
<field
name="partner_invoice_id"
string="Contact Invoiced"
/>
</group>
<group>
<group>
<field
name="partner_invoice_vat"
attrs="{'readonly': [('partner_invoice_id', '!=', False),
('partner_invoice_vat','!=', False)]}"
/>
<field
name="partner_invoice_email"
attrs="{'readonly': [('partner_invoice_id', '!=', False),
('partner_invoice_email','!=', False)]}"
/>
</group>
<group>
<label
for="partner_invoice_street"
string="Address"
/>
<div class="o_address_format">
<field
name="partner_invoice_street"
placeholder="Street..."
class="o_address_street"
attrs="{'readonly': [('partner_invoice_id', '!=', False),
('partner_invoice_street','!=', False)]}"
/>
<field
name="partner_invoice_street2"
placeholder="Street 2..."
class="o_address_street"
attrs="{'readonly': [('partner_invoice_id', '!=', False),
('partner_invoice_street2','!=', False)]}"
/>
<field
name="partner_invoice_city"
placeholder="City"
class="o_address_city"
attrs="{'readonly': [('partner_invoice_id', '!=', False),
('partner_invoice_city','!=', False)]}"
/>
<field
name="partner_invoice_state_id"
class="o_address_state"
placeholder="State"
options='{"no_open": True}'
attrs="{'readonly': [('partner_invoice_id', '!=', False),
('partner_invoice_state_id','!=', False)]}"
/>
<field
name="partner_invoice_zip"
placeholder="ZIP"
class="o_address_zip"
attrs="{'readonly': [('partner_invoice_id', '!=', False),
('partner_invoice_zip','!=', False)]}"
/>
<field
name="partner_invoice_country_id"
placeholder="Country"
class="o_address_country"
options='{"no_open": True, "no_create": True}'
attrs="{'readonly': [('partner_invoice_id', '!=', False),
('partner_invoice_country_id','!=', False)]}"
/>
</div>
</group>
<group>
</group>
</group>
</page>
<page
name="payments"

View File

@@ -15,39 +15,7 @@
<header>
<field name="splitted" invisible="True" />
<field name="tax_ids" invisible="1" />
<field name="parent_reservation" invisible="True" />
<field name="has_confirmed_reservations_to_send" invisible="1" />
<field name="has_cancelled_reservations_to_send" invisible="1" />
<field name="has_checkout_to_send" invisible="1" />
<field name="checkin_partner_count" invisible="1" />
<button
name="create_invoice"
string="Create Invoice"
type="object"
class="btn-primary"
states="sale"
attrs="{'invisible': ['|','|',
('state', '=', 'draft'), ('reservation_type','not in',('normal')),
('invoice_status','!=', 'to invoice')]}"
/>
<button
name="send_reservation_mail"
type="object"
string="Send Confirmation Email"
attrs="{'invisible': [('has_confirmed_reservations_to_send', '=', False)]}"
/>
<button
name="send_cancel_mail"
type="object"
string="Send Cancel Email"
attrs="{'invisible': [('has_cancelled_reservations_to_send', '=', False)]}"
/>
<button
name="send_exit_mail"
type="object"
string="Send Exit Email"
attrs="{'invisible': [('has_checkout_to_send', '=', False)]}"
/>
<button
name="confirm"
string="Confirm"
@@ -74,28 +42,6 @@
class="oe_highlight"
type="object"
/>
<button
name="%(action_pms_split_reservation)d"
string="Split"
type="action"
icon="fa-cut"
attrs="{'invisible':['|',('folio_id', '=', False),('state','not in',('draft','confirm','booking'))]}"
/>
<button
name="unify"
string="Unify"
type="object"
icon="fa-compress"
attrs="{'invisible':[('splitted', '=', False)]}"
/>
<button
name="open_master"
string="Open Master"
type="object"
class="oe_highlight"
icon="fa-file"
attrs="{'invisible':['|',['parent_reservation', '=', False]]}"
/>
<field
name="state"
widget="statusbar"
@@ -200,31 +146,12 @@
<span class="o_stat_text">Invoices</span>
</div>
</button>
<button
type="object"
class="oe_stat_button"
icon="fa-chain-broken"
name="open_master"
attrs="{'invisible':[('splitted','=',False)]}"
>
<div class="o_field_widget o_stat_info">
<span>
<field
name="parent_reservation"
nolabel="1"
readonly="1"
/>
</span>
<span class="o_stat_text">Splitted!</span>
</div>
</button>
<button
type="object"
class="oe_stat_button"
icon="fa-users"
name="action_checks"
attrs="{'invisible': ['|', ('checkin_partner_pending_count','&lt;=',0),
('parent_reservation','!=',False)]}"
attrs="{'invisible': [('checkin_partner_pending_count','&lt;=',0)]}"
>
<div class="o_form_field o_stat_info">
<span class="o_stat_value">
@@ -420,7 +347,7 @@
('pms_room_type_id', '=', room_type_id),
('pricelist_id', 'in', [pricelist_id, False])]"
options="{'no_create': True,'no_open': True}"
attrs="{'invisible': ['|',('reservation_type','in',('out')),('parent_reservation','!=',False)]}"
attrs="{'invisible': [('reservation_type','in',('out'))]}"
/>
<field
name="folio_internal_comment"
@@ -496,12 +423,6 @@
</group>
<notebook>
<page name="detail" string="Detail">
<button
name="%(action_pms_massive_price_change_reservation_days)d"
string="Massive Day Prices"
type="action"
icon="fa-bolt"
/>
<field name="reservation_line_ids" nolabel="1">
<tree create="false" delete="false" editable="bottom">
<field name="room_id" />
@@ -514,12 +435,6 @@
/>
</tree>
</field>
<button
name="%(action_service_on_day)d"
string="Service on Day"
type="action"
icon="fa-coffee"
/>
<group string="Services" name="reservation_services">
<field
name="service_ids"
@@ -590,8 +505,7 @@
<page
name="persons"
string="Persons"
attrs="{'invisible': ['|',('reservation_type','in',('out')),
('parent_reservation','!=',False)]}"
attrs="{'invisible': [('reservation_type','in',('out'))]}"
>
<group colspan="2" cols="6">
<field
@@ -600,13 +514,11 @@
placeholder="Segmentation..."
options="{'no_create': True,'no_open': True}"
/>
<field name="customer_sleep_here" />
</group>
<field
name="checkin_partner_ids"
context="{
'checkin_partner_ids': checkin_partner_ids,
'include_customer': customer_sleep_here,
'default_reservation_id': id,
'reservation_id': id,
'tree_view_ref':'pms.pms_checkin_partner_reservation_view_tree',
@@ -616,64 +528,14 @@
<page
name="invoicing"
string="Invoicing"
attrs="{'invisible': ['|',('reservation_type','in',('out')),
('parent_reservation','!=',False)]}"
attrs="{'invisible': [('reservation_type','in',('out'))]}"
>
<group>
<field name="partner_parent_id" />
<field
name="partner_invoice_id"
string="Contact Invoiced"
/>
</group>
<group>
<group>
<field name="partner_invoice_vat" />
<field name="partner_invoice_email" />
</group>
<group>
<label
for="partner_invoice_street"
string="Address"
/>
<div class="o_address_format">
<field
name="partner_invoice_street"
placeholder="Street..."
class="o_address_street"
/>
<field
name="partner_invoice_street2"
placeholder="Street 2..."
class="o_address_street"
/>
<field
name="partner_invoice_city"
placeholder="City"
class="o_address_city"
/>
<field
name="partner_invoice_state_id"
class="o_address_state"
placeholder="State"
options='{"no_open": True}'
/>
<field
name="partner_invoice_zip"
placeholder="ZIP"
class="o_address_zip"
/>
<field
name="partner_invoice_country_id"
placeholder="Country"
class="o_address_country"
options='{"no_open": True, "no_create": True}'
/>
</div>
</group>
<group>
</group>
</group>
</page>
<page name="others" string="Others">
<group>
@@ -807,14 +669,6 @@
>
<field name="splitted" invisible="1" />
<field name="pricelist_id" invisible="1" />
<field name="to_print" invisible="1" />
<button
icon="fa-chain-broken"
type="object"
class="oe_stat_button"
name="open_master"
attrs="{'invisible':[('splitted','=', False)]}"
/>
<button
icon="fa-angellist"
attrs="{'invisible':['|',('folio_pending_amount','&gt;',0),('state' ,'!=', 'done')]}"
@@ -836,7 +690,7 @@
name="action_checks"
context="{'partner_id': partner_id,'enter_date': checkin,
'exit_date': checkout,'reservation_id': id, 'hidden_checkin_partner': True, 'edit_checkin_partner': True }"
attrs="{'invisible':['|','|', ('state','not in',('confirm','booking')),('checkin_partner_pending_count','=', 0),('parent_reservation','!=',False)]}"
attrs="{'invisible':['|',('state','not in',('confirm','booking')),('checkin_partner_pending_count','=', 0)]}"
/>
<field name="allowed_room_ids" invisible="1" />
<field name="room_id" options="{'no_create': True,'no_open': True}" />
@@ -848,7 +702,6 @@
context="{'search_default_partner_id': partner_id}"
/>
<field name="partner_id" />
<field name="parent_reservation" invisible="1" />
<field name="room_type_id" string="Reserved Unit Type" />
<field name="nights" />
<field name="adults" string="Persons" />
@@ -890,9 +743,6 @@
<attribute name="delete">false</attribute>
<attribute name="string">Rooms</attribute>
</tree>
<xpath expr="//field[@name='to_print']" position="attributes">
<attribute name="invisible">0</attribute>
</xpath>
<xpath expr="//field[@name='state']" position="before">
<button
type="object"

View File

@@ -54,7 +54,6 @@
decoration-success="is_board_service == True"
>
<field name="is_board_service" invisible="1" />
<field name="to_print" />
<button
type="object"
class="oe_stat_button"

View File

@@ -1,8 +0,0 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import folio_make_invoice_advance
from . import massive_changes
from . import split_reservation
from . import massive_price_reservation_days
from . import wizard_reservation
from . import service_on_day

View File

@@ -1,651 +0,0 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import time
from datetime import timedelta
from odoo import _, api, fields, models
from odoo.exceptions import UserError, ValidationError
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
class FolioAdvancePaymentInv(models.TransientModel):
_name = "folio.advance.payment.inv"
_description = "Folios Advance Payment Invoice"
@api.model
def _get_advance_payment_method(self):
return "all"
@api.model
def _default_product_id(self):
product_id = (
self.env["ir.config_parameter"]
.sudo()
.get_param("sale.default_deposit_product_id")
)
return self.env["product.product"].browse(int(product_id))
@api.model
def _get_default_folio(self):
if self._context.get("default_reservation_id"):
folio_ids = self._context.get("default_folio_id", [])
else:
folio_ids = self._context.get("active_ids", [])
folios = self.env["pms.folio"].browse(folio_ids)
return folios
@api.model
def _get_default_reservation(self):
if self._context.get("default_reservation_id"):
reservations = self.env["pms.reservation"].browse(
self._context.get("active_ids", [])
)
else:
folios = self._get_default_folio()
reservations = self.env["pms.reservation"]
for folio in folios:
reservations |= folio.reservation_ids
return reservations
@api.model
def _get_default_partner_invoice(self):
folios = self._get_default_folio()
if folios[0].agency:
return folios[0].agency
return folios[0].partner_invoice_id
@api.model
def _default_deposit_account_id(self):
return self._default_product_id().property_account_income_id
@api.model
def _default_deposit_taxes_id(self):
return self._default_product_id().taxes_id
advance_payment_method = fields.Selection(
[
("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,
)
auto_invoice = fields.Boolean(
"Auto Payment Invoice",
default=True,
help="Automatic validation and link payment to invoice",
)
count = fields.Integer(compute="_count", store=True, string="# of Orders")
folio_ids = fields.Many2many(
"pms.folio", string="Folios", help="Folios grouped", default=_get_default_folio
)
reservation_ids = fields.Many2many(
"pms.reservation",
string="Rooms",
help="Folios grouped",
default=_get_default_reservation,
)
group_folios = fields.Boolean("Group Folios")
partner_invoice_id = fields.Many2one(
"res.partner",
string="Invoice Address",
required=True,
default=_get_default_partner_invoice,
help="Invoice address for current Invoice.",
)
line_ids = fields.One2many(
"line.advance.inv", "advance_inv_id", string="Invoice Lines"
)
# Advance Payment
product_id = fields.Many2one(
"product.product",
string="Product",
domain=[("type", "=", "service")],
default=_default_product_id,
)
amount = fields.Float(
"Down Payment Amount",
digits=("Account"),
help="The amount to be invoiced in advance, taxes excluded.",
)
deposit_account_id = fields.Many2one(
"account.account",
string="Income Account",
domain=[("deprecated", "=", False)],
help="Account used for deposits",
default=_default_deposit_account_id,
)
deposit_taxes_id = fields.Many2many(
"account.tax",
string="Customer Taxes",
help="Taxes used for deposits",
default=_default_deposit_taxes_id,
)
@api.depends("folio_ids")
def _count(self):
for record in self:
record.update({"count": len(self.folio_ids)})
@api.onchange("advance_payment_method")
def onchange_advance_payment_method(self):
if self.advance_payment_method == "percentage":
return {"value": {"amount": 0}}
return {}
def _create_invoice(self, folio, service, amount):
inv_obj = self.env["account.invoice"]
ir_property_obj = self.env["ir.property"]
account_id = False
if self.product_id.id:
account_id = (
self.product_id.property_account_income_id.id
or self.product_id.categ_id.property_account_income_categ_id.id
)
if not account_id:
inc_acc = ir_property_obj.get(
"property_account_income_categ_id", "product.category"
)
account_id = (
folio.fiscal_position_id.map_account(inc_acc).id if inc_acc else False
)
if not account_id:
raise UserError(
_(
'There is no income account defined for this product: "%s". You may have to install a chart of account from Accounting app, settings menu.'
)
% (self.product_id.name,)
)
if self.amount <= 0.00:
raise UserError(_("The value of the down payment amount must be positive."))
context = {"lang": folio.partner_id.lang}
if self.advance_payment_method == "percentage":
amount = folio.amount_untaxed * self.amount / 100
name = _("Down payment of %s%%") % (self.amount,)
else:
amount = self.amount
name = _("Down Payment")
del context
taxes = self.product_id.taxes_id.filtered(
lambda r: not folio.company_id or r.company_id == folio.company_id
)
if folio.fiscal_position_id and taxes:
tax_ids = folio.fiscal_position_id.map_tax(taxes).ids
else:
tax_ids = taxes.ids
invoice = inv_obj.create(
{
"name": folio.client_order_ref or folio.name,
"invoice_origin": folio.name,
"type": "out_invoice",
"reference": False,
"folio_ids": [
(6, 0, [folio.id])
], # REVIEW: Folio_ids is a computed field, Why need this value?
"account_id": folio.partner_id.property_account_receivable_id.id,
"partner_id": folio.partner_invoice_id.id,
"invoice_line_ids": [
(
0,
0,
{
"name": name,
"invoice_origin": folio.name,
"account_id": account_id,
"price_unit": amount,
"quantity": 1.0,
"discount": 0.0,
"uom_id": self.product_id.uom_id.id,
"product_id": self.product_id.id,
"service_ids": [(6, 0, [service.id])],
"invoice_line_tax_ids": [(6, 0, tax_ids)],
"account_analytic_id": folio.analytic_account_id.id
or False,
},
)
],
"currency_id": folio.pricelist_id.currency_id.id,
"invoice_payment_term_id": folio.payment_term_id.id,
"fiscal_position_id": folio.fiscal_position_id.id
or folio.partner_id.property_account_position_id.id,
"team_id": folio.team_id.id,
"user_id": folio.user_id.id,
"comment": folio.note,
}
)
# invoice.compute_taxes() #TODO: Review method (view sales module)
invoice.message_post_with_view(
"mail.message_origin_link",
values={"self": invoice, "invoice_origin": folio},
subtype_id=self.env.ref("mail.mt_note").id,
)
return invoice
@api.model
def _validate_invoices(self, invoice):
if self.auto_invoice:
invoice.action_invoice_open()
payment_ids = self.folio_ids.mapped("payment_ids.id")
domain = [
("account_id", "=", invoice.account_id.id),
("payment_id", "in", payment_ids),
("reconciled", "=", False),
"|",
("amount_residual", "!=", 0.0),
("amount_residual_currency", "!=", 0.0),
]
if invoice.type in ("out_invoice", "in_refund"):
domain.extend([("credit", ">", 0), ("debit", "=", 0)])
type_payment = _("Outstanding credits")
else:
domain.extend([("credit", "=", 0), ("debit", ">", 0)])
type_payment = _("Outstanding debits")
info = {
"title": "",
"outstanding": True,
"content": [],
"invoice_id": invoice.id,
}
lines = self.env["account.move.line"].search(domain)
currency_id = invoice.currency_id
for line in lines:
invoice.assign_outstanding_credit(line.id)
return True
def create_invoices(self):
inv_obj = self.env["account.move"]
precision = self.env["decimal.precision"].precision_get(
"Product Unit of Measure"
)
folios = self.folio_ids
if not self.partner_invoice_id or not self.partner_invoice_id.vat:
vat_error = _("We need the VAT of the customer")
raise ValidationError(vat_error)
if self.advance_payment_method == "all":
inv_data = self._prepare_invoice()
invoice = inv_obj.create(inv_data)
for line in self.line_ids:
line.invoice_line_create(invoice.id, line.qty)
else:
# Create deposit product if necessary
if not self.product_id:
vals = self._prepare_deposit_product()
self.product_id = self.env["product.product"].sudo().create(vals)
self.env["ir.config_parameter"].sudo().set_param(
"sale.default_deposit_product_id", self.product_id.id
)
service_obj = self.env["pms.service"]
for folio in folios:
if self.advance_payment_method == "percentage":
amount = folio.amount_untaxed * folio.amount_total / 100
else:
amount = self.amount
if self.product_id.invoice_policy != "order":
raise UserError(
_(
'The product used to invoice a down payment should have an invoice policy set to "Ordered quantities". Please update your deposit product to be able to create a deposit invoice.'
)
)
if self.product_id.type != "service":
raise UserError(
_(
"The product used to invoice a down payment should be of type 'Service'. Please use another product or update this product."
)
)
taxes = self.product_id.taxes_id.filtered(
lambda r: not folio.company_id or r.company_id == folio.company_id
)
if folio.fiscal_position_id and taxes:
tax_ids = folio.fiscal_position_id.map_tax(taxes).ids
else:
tax_ids = taxes.ids
context = {"lang": folio.partner_id.lang}
service_line = service_obj.create(
{
"name": _("Advance: %s") % (time.strftime("%m %Y"),),
"price_unit": amount,
"product_qty": 0.0,
"folio_id": folio.id,
"discount": 0.0,
"product_uom": self.product_id.uom_id.id,
"product_id": self.product_id.id,
"tax_id": [(6, 0, tax_ids)],
}
)
del context
invoice = self._create_invoice(folio, service_line, amount)
# invoice.compute_taxes() #TODO: Review Method
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_total < 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() TODO: REVIEW method
self._validate_invoices(invoice)
invoice.message_post_with_view(
"mail.message_origin_link",
values={"self": invoice, "invoice_origin": folios},
subtype_id=self.env.ref("mail.mt_note").id,
)
if self._context.get("open_invoices", False):
return folios.open_invoices_folio()
return {"type": "ir.actions.act_window_close"}
def _prepare_deposit_product(self):
return {
"name": "Down payment",
"type": "service",
"invoice_policy": "order",
"property_account_income_id": self.deposit_account_id.id,
"taxes_id": [(6, 0, self.deposit_taxes_id.ids)],
}
@api.onchange("reservation_ids")
def prepare_invoice_lines(self):
vals = [(5, 0, 0)]
folios = self.folio_ids
invoice_lines = {}
for folio in folios:
for service in folio.service_ids.filtered(
lambda x: x.is_board_service == False
and x.qty_to_invoice != 0
and (
x.reservation_id.id in self.reservation_ids.ids
or not x.reservation_id.id
)
):
invoice_lines[service.id] = {
"description": service.name,
"product_id": service.product_id.id,
"qty": service.qty_to_invoice,
"discount": service.discount,
"price_unit": service.price_unit,
"service_id": service.id,
}
for reservation in folio.reservation_ids.filtered(
lambda x: x._origin.id in self.reservation_ids.ids
and x.invoice_status == "to invoice"
):
board_service = reservation.board_service_room_id
for day in reservation.reservation_line_ids.filtered(
lambda x: not x.move_line_ids
).sorted("date"):
extra_price = 0
if board_service:
services = reservation.service_ids.filtered(
lambda x: x.is_board_service == True
)
for service in services:
service_date = day.date
if service.product_id.consumed_on == "after":
service_date = (
fields.Date.from_string(day.date)
+ timedelta(days=1)
).strftime(DEFAULT_SERVER_DATE_FORMAT)
extra_price += (
service.price_unit
* service.service_line_ids.filtered(
lambda x: x.date == service_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,
day.cancel_discount,
)
if day.cancel_discount == 100:
continue
discount_factor = 1.0
for discount in [day.discount, day.cancel_discount]:
discount_factor = discount_factor * ((100.0 - discount) / 100.0)
final_discount = 100.0 - (discount_factor * 100.0)
description = (
folio.name
+ " "
+ reservation.room_type_id.name
+ " ("
+ reservation.board_service_room_id.pms_board_service_id.name
+ ")"
if board_service
else folio.name + " " + reservation.room_type_id.name
)
if group_key not in invoice_lines:
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
),
"discount": final_discount,
"price_unit": day.price + extra_price,
"reservation_line_ids": [(4, day.id)],
}
else:
invoice_lines[group_key][("reservation_line_ids")].append(
(4, day.id)
)
for group_key in invoice_lines:
vals.append((0, False, invoice_lines[group_key]))
self.line_ids = vals
self.line_ids.onchange_reservation_line_ids()
@api.onchange("folio_ids")
def onchange_folio_ids(self):
vals = []
folios = self.folio_ids
invoice_lines = {}
reservations = self.env["pms.reservation"]
services = self.env["pms.service"]
old_folio_ids = self.reservation_ids.mapped("folio_id.id")
for folio in folios.filtered(lambda r: r.id not in old_folio_ids):
folio_reservations = folio.reservation_ids
if folio_reservations:
reservations |= folio_reservations
self.reservation_ids |= reservations
self.prepare_invoice_lines()
@api.model
def _prepare_invoice(self):
"""
Prepare the dict of values to create the new invoice for a folio. This method may be
overridden to implement custom invoice generation (making sure to call super() to establish
a clean extension chain).
"""
journal_id = self.env["account.move"].default_get(["journal_id"])["journal_id"]
if not journal_id:
raise UserError(
_("Please define an accounting sales journal for this company.")
)
origin = " ".join(self.folio_ids.mapped("name"))
pricelist = self.folio_ids[0].pricelist_id
currency = self.folio_ids[0].currency_id
payment_term = self.folio_ids[0].payment_term_id
fiscal_position = self.folio_ids[0].fiscal_position_id
company = self.folio_ids[0].company_id
user = self.folio_ids[0].user_id
team = self.folio_ids[0].team_id
# REVIEW: Multi pricelist in folios??
# for folio in self.folio_ids:
# if folio.pricelist_id != pricelist:
# raise UserError(_('All Folios must hace the same pricelist'))
invoice_vals = {
"name": self.folio_ids[0].client_order_ref or "",
"invoice_origin": origin,
"type": "out_invoice",
"partner_id": self.partner_invoice_id.id,
"journal_id": journal_id,
"currency_id": currency.id,
"invoice_payment_term_id": payment_term.id,
"fiscal_position_id": fiscal_position.id
or self.partner_invoice_id.property_account_position_id.id,
"company_id": company.id,
"user_id": user and user.id,
"team_id": team.id,
"narration": self.folio_ids[0].note,
}
return invoice_vals
class LineAdvancePaymentInv(models.TransientModel):
_name = "line.advance.inv"
_description = "Lines Advance Invoice"
room_type_id = fields.Many2one("pms.room.type")
product_id = fields.Many2one(
"product.product",
string="Down Payment Product",
domain=[("type", "=", "service")],
)
qty = fields.Integer("Quantity")
price_unit = fields.Float("Price Unit")
price_total = fields.Float("Price Total", compute="_compute_price_total")
price_tax = fields.Float("Price Tax", compute="_compute_price_total")
price_subtotal = fields.Float(
"Price Subtotal", compute="_compute_price_total", store=True
)
advance_inv_id = fields.Many2one("folio.advance.payment.inv")
price_room = fields.Float(compute="_compute_price_room")
discount = fields.Float(string="Discount (%)", digits=("Discount"), default=0.0)
to_invoice = fields.Boolean("To Invoice")
description = fields.Text("Description")
description_dates = fields.Text("Range")
reservation_id = fields.Many2one("pms.reservation")
service_id = fields.Many2one("pms.service")
folio_id = fields.Many2one("pms.folio", compute="_compute_folio_id")
reservation_line_ids = fields.Many2many(
"pms.reservation.line", string="Reservation Lines"
)
@api.depends("qty", "price_unit", "discount")
def _compute_price_total(self):
for record in self:
origin = (
record.reservation_id if record.reservation_id.id else record.service_id
)
amount_line = record.price_unit * record.qty
if amount_line != 0:
product = record.product_id
price = amount_line * (1 - (record.discount or 0.0) * 0.01)
taxes = origin.tax_ids.compute_all(
price, origin.currency_id, 1, product=product
)
record.update(
{
"price_tax": sum(
t.get("amount", 0.0) for t in taxes.get("taxes", [])
),
"price_total": taxes["total_included"],
"price_subtotal": taxes["total_excluded"],
}
)
def _compute_price_room(self):
self.price_room = False
for record in self:
if record.reservation_id:
record.price_room = record.reservation_line_ids[0].price
def _compute_folio_id(self):
for record in self:
origin = (
record.reservation_id if record.reservation_id.id else record.service_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).strftime(
DEFAULT_SERVER_DATE_FORMAT
)
+ " - "
+ (
(record.reservation_line_ids[-1].date) + timedelta(days=1)
).strftime(DEFAULT_SERVER_DATE_FORMAT)
)
def invoice_line_create(self, invoice_id, qty):
"""Create an invoice line.
:param invoice_id: integer
:param qty: float quantity to invoice
:returns recordset of account.move.line created
"""
self.ensure_one()
invoice_lines = self.env["account.move.line"]
origin = self.reservation_id if self.reservation_id.id else self.service_id
product = self.product_id
account = (
product.property_account_income_id
or product.categ_id.property_account_income_categ_id
)
if not account:
raise UserError(
_(
'Please define income account for this product: "%s" (id:%d) - or for its category: "%s".'
)
% (product.name, product.id, product.categ_id.name)
)
fpos = (
self.folio_id.fiscal_position_id
or self.folio_id.partner_id.property_account_position_id
)
if fpos:
account = fpos.map_account(account)
vals = {
"sequence": origin.sequence,
"invoice_origin": origin.name,
"price_unit": self.price_unit,
"quantity": self.qty,
"discount": self.discount,
"uom_id": product.uom_id.id,
"product_id": product.id or False,
"invoice_line_tax_ids": [(6, 0, origin.tax_ids.ids)],
"account_analytic_id": self.folio_id.analytic_account_id.id or False,
"analytic_tag_ids": [(6, 0, origin.analytic_tag_ids.ids)],
}
if self.reservation_id:
vals.update(
{
"name": self.description + " (" + self.description_dates + ")",
"move_id": invoice_id,
"reservation_ids": [(6, 0, [origin.id])],
"reservation_line_ids": [(6, 0, self.reservation_line_ids.ids)],
}
)
elif self.service_id:
vals.update(
{
"name": self.description,
"move_id": invoice_id,
"service_ids": [(6, 0, [origin.id])],
}
)
invoice_lines |= self.env["account.move.line"].create(vals)
return invoice_lines

View File

@@ -1,163 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_folio_advance_payment_inv" model="ir.ui.view">
<field name="name">Invoice Orders</field>
<field name="model">folio.advance.payment.inv</field>
<field name="arch" type="xml">
<form string="Invoice Folio">
<group>
<group>
<field name="partner_invoice_id" />
<field
name="count"
invisible="[('count','=',1)]"
readonly="True"
/>
<field
name="advance_payment_method"
class="oe_inline"
widget="radio"
attrs="{'invisible': [('count','&gt;',1)]}"
/>
<field
name="product_id"
string="Down Payment Product"
context="{'search_default_services': 1, 'default_type': 'service', 'default_invoice_policy': 'order'}"
class="oe_inline"
attrs="{'invisible': 1}"
/>
<label
for="amount"
attrs="{'invisible': [('advance_payment_method', 'not in', ('fixed','percentage'))]}"
/>
<div
attrs="{'invisible': [('advance_payment_method', 'not in', ('fixed','percentage'))]}"
>
<field
name="amount"
attrs="{'required': [('advance_payment_method', 'in', ('fixed','percentage'))]}"
class="oe_inline"
widget="monetary"
/>
<label
for=""
string="%%"
attrs="{'invisible': [('advance_payment_method', '!=', 'percentage')]}"
class="oe_inline"
/>
</div>
<field
name="deposit_account_id"
class="oe_inline"
attrs="{'invisible': ['|', ('advance_payment_method', 'not in', ('fixed', 'percentage')), ('product_id', '!=', False)]}"
groups="account.group_account_manager"
/>
<field
name="deposit_taxes_id"
class="oe_inline"
widget="many2many_tags"
domain="[('type_tax_use','=','sale')]"
attrs="{'invisible': ['|', ('advance_payment_method', 'not in', ('fixed', 'percentage')), ('product_id', '!=', False)]}"
/>
</group>
<group>
<field
name="group_folios"
string="Add Folios"
attrs="{'invisible': [('advance_payment_method', 'in', ('fixed','percentage'))]}"
/>
</group>
<field
name="folio_ids"
attrs="{'invisible': [('group_folios', '=', False)]}"
domain="[('invoice_status','=','to invoice')]"
context="{'search_default_partner_invoice_id': partner_invoice_id}"
>
<tree
string="Folios"
editable="bottom"
decoration-danger="partner_invoice_id != parent.partner_invoice_id"
>
<field name="partner_invoice_id" />
<field name="name" />
<field name="state" />
<field name="pending_amount" />
<field name="amount_total" />
</tree>
</field>
<field
name="reservation_ids"
widget="many2many_tags"
domain="[('folio_id', 'in', folio_ids)]"
attrs="{'invisible': [('advance_payment_method', 'in', ('fixed','percentage'))]}"
/>
</group>
<group>
<field
name="line_ids"
attrs="{'invisible': [('advance_payment_method', 'in', ('fixed','percentage'))]}"
nolabel="1"
>
<tree string="Lines" editable="bottom" create="false">
<field name="product_id" invisible="1" />
<field name="price_room" invisible="1" />
<field
name="reservation_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_dates"
readonly="1"
force_save="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="price_unit" />
<field name="discount" />
<field name="price_total" />
</tree>
</field>
</group>
<group>
<field name="auto_invoice" />
</group>
<footer>
<button
name="create_invoices"
string="Create and View Invoices"
type="object"
context="{'open_invoices': True}"
class="btn-primary"
/>
<button
name="create_invoices"
string="Create Invoices"
type="object"
class="btn-primary"
/>
<button string="Cancel" class="btn-default" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="action_view_folio_advance_payment_inv" model="ir.actions.act_window">
<field name="name">Invoice Order</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">folio.advance.payment.inv</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="groups_id" eval="[(4,ref('pms.group_pms_user'))]" />
</record>
</odoo>

View File

@@ -1,308 +0,0 @@
# Copyright 2017 Alexandre Díaz
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from datetime import timedelta
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
class MassiveChangesWizard(models.TransientModel):
_name = "pms.wizard.massive.changes"
_description = "Massive Changes"
# Default methods
@api.model
def _get_default_pms_property(self):
return self.env.user.pms_property_id
# Fields declaration
pms_property_id = fields.Many2one(
"pms.property", "Property", required=True, default=_get_default_pms_property
)
pricelist_id = fields.Many2one(
"product.pricelist",
"Pricelist Plan",
domain="[('pricelist_type', '=', 'daily'), ('pms_property_ids.id', '=', context.get('pms_property_id'))]",
)
restriction_id = fields.Many2one(
"pms.room.type.restriction",
"Restriction Plan",
domain="[('pms_property_id.id', '=', context.get('pms_property_id'))]",
)
room_type_ids = fields.Many2many(
"pms.room.type",
string="Room Types",
domain="[('pms_property_id.id', '=', context.get('pms_property_id'))]",
)
section = fields.Selection(
[("restrictions", "Restrictions"), ("prices", "Pricelist"),],
string="Section",
default="prices",
)
date_start = fields.Date("Start Date", required=True)
date_end = fields.Date("End Date", required=True)
dmo = fields.Boolean("Monday", default=True)
dtu = fields.Boolean("Tuesday", default=True)
dwe = fields.Boolean("Wednesday", default=True)
dth = fields.Boolean("Thursday", default=True)
dfr = fields.Boolean("Friday", default=True)
dsa = fields.Boolean("Saturday", default=True)
dsu = fields.Boolean("Sunday", default=True)
applied_on = fields.Selection(
[("0", "Global"), ("1", "Room Type"),], string="Applied On", default="0",
)
# Restriction fields
change_min_stay = fields.Boolean(
default=False, help="Mark for a massive change in this field."
)
min_stay = fields.Integer("Min. Stay")
change_min_stay_arrival = fields.Boolean(
default=False, help="Mark for a massive change in this field."
)
min_stay_arrival = fields.Integer("Min. Stay Arrival")
change_max_stay = fields.Boolean(
default=False, help="Mark for a massive change in this field."
)
max_stay = fields.Integer("Max. Stay")
change_max_stay_arrival = fields.Boolean(
default=False, help="Mark for a massive change in this field."
)
max_stay_arrival = fields.Integer("Max. Stay Arrival")
change_closed = fields.Boolean(
default=False, help="Mark for a massive change in this field."
)
closed = fields.Boolean("Closed")
change_closed_departure = fields.Boolean(
default=False, help="Mark for a massive change in this field."
)
closed_departure = fields.Boolean("Closed Departure")
change_closed_arrival = fields.Boolean(
default=False, help="Mark for a massive change in this field."
)
closed_arrival = fields.Boolean("Closed Arrival")
# Pricelist fields
price = fields.Char(
"Price",
help="Can use '+','-' \
or '%'...\nExamples:\n a) +12.3 \
\t> Increase the price in 12.3\n \
b) -1.45% \t> Substract 1.45%\n c) 45 \
\t\t> Sets the price to 45",
)
# Constraints and onchanges
@api.constrains("pricelist_id")
def _check_pricelist_id(self):
for record in self:
if record.pricelist_id.pricelist_type != "daily":
raise ValidationError(
_("A daily pricelist plan is required for massive changes.")
+ " "
+ _("Please review the property configuration before proceed.")
)
@api.constrains("hotepms_property_idl_id")
def _check_pms_property_id(self):
for record in self:
if (
record.section == "prices"
and record.pricelist_id.pms_property_ids != record.pms_property_id
):
raise ValidationError(
_("Mismatch between property and pricelist plan.")
+ " "
+ _("The pricelist plan does not belongs to the current property.")
)
if (
record.section == "restriction"
and record.restriction_id.pms_property_id != record.pms_property_id
):
raise ValidationError(
_("Mismatch between property and restriction plan.")
+ " "
+ _(
"The restriction plan does not belongs to the current property."
)
)
@api.onchange("date_start")
def onchange_date_start(self):
self.ensure_one()
self.date_end = self.date_start
# Action methods
def _get_restrictions_values(self, record):
self.ensure_one()
vals = {}
if record.change_min_stay:
vals.update({"min_stay": record.min_stay})
if record.change_min_stay_arrival:
vals.update({"min_stay_arrival": record.min_stay_arrival})
if record.change_max_stay:
vals.update({"max_stay": record.max_stay})
if record.change_max_stay_arrival:
vals.update({"max_stay_arrival": record.max_stay_arrival})
if record.change_closed:
vals.update({"closed": record.closed})
if record.change_closed_departure:
vals.update({"closed_departure": record.closed_departure})
if record.change_closed_arrival:
vals.update({"closed_arrival": record.closed_arrival})
return vals
@api.model
def _save_restrictions(self, ndate, room_types, record):
pms_room_type_re_it_obj = self.env["pms.room.type.restriction.item"]
domain = [
("date", ">=", ndate.strftime(DEFAULT_SERVER_DATE_FORMAT)),
("date", "<=", ndate.strftime(DEFAULT_SERVER_DATE_FORMAT)),
("restriction_id", "=", record.restriction_id.id),
]
for room_type in room_types:
vals = self._get_restrictions_values(record)
if not any(vals):
continue
rrest_item_ids = pms_room_type_re_it_obj.search(
domain + [("room_type_id", "=", room_type.id)]
)
if any(rrest_item_ids):
rrest_item_ids.write(vals)
else:
vals.update(
{
"date": ndate.strftime(DEFAULT_SERVER_DATE_FORMAT),
"restriction_id": record.restriction_id.id,
"room_type_id": room_type.id,
}
)
pms_room_type_re_it_obj.create(vals)
@api.model
def _save_prices(self, ndate, room_types, record):
product_pricelist_item_obj = self.env["product.pricelist.item"]
price = 0.0
operation = "a"
if record.price[0] == "+" or record.price[0] == "-":
if record.price[-1] == "%":
price = float(record.price[1:-1])
operation = "ap" if (record.price[0] == "+") else "sp"
else:
price = float(record.price[1:])
operation = "a" if (record.price[0] == "+") else "s"
else:
if record.price[-1] == "%":
price = float(record.price[:-1])
operation = "np"
else:
price = float(record.price)
operation = "n"
domain = [
("pricelist_id", "=", record.pricelist_id.id),
("date_start", ">=", ndate.strftime(DEFAULT_SERVER_DATE_FORMAT)),
("date_end", "<=", ndate.strftime(DEFAULT_SERVER_DATE_FORMAT)),
("compute_price", "=", "fixed"),
("applied_on", "=", "1_product"),
]
for room_type in room_types:
prod_tmpl_id = room_type.product_id.product_tmpl_id
pricelist_item_ids = product_pricelist_item_obj.search(
domain + [("product_tmpl_id", "=", prod_tmpl_id.id)]
)
if any(pricelist_item_ids):
if operation != "n":
for pli in pricelist_item_ids:
pli_price = pli.fixed_price
if operation == "a":
pli.write({"fixed_price": pli_price + price})
elif operation == "ap":
pli.write(
{"fixed_price": pli_price + price * pli_price * 0.01}
)
elif operation == "s":
pli.write({"fixed_price": pli_price - price})
elif operation == "sp":
pli.write(
{"fixed_price": pli_price - price * pli_price * 0.01}
)
elif operation == "np":
pli.write({"fixed_price": price * pli_price * 0.01})
else:
pricelist_item_ids.write({"fixed_price": price})
else:
product_pricelist_item_obj.create(
{
"pricelist_id": record.pricelist_id.id,
"date_start": ndate.strftime(DEFAULT_SERVER_DATE_FORMAT),
"date_end": ndate.strftime(DEFAULT_SERVER_DATE_FORMAT),
"compute_price": "fixed",
"applied_on": "1_product",
"product_tmpl_id": prod_tmpl_id.id,
"fixed_price": price,
}
)
@api.model
def _save(self, ndate, room_types, record):
if record.section == "restrictions":
self._save_restrictions(ndate, room_types, record)
elif record.section == "prices":
self._save_prices(ndate, room_types, record)
def _do_massive_change(self):
pms_room_type_obj = self.env["pms.room.type"]
for record in self:
date_start_dt = fields.Date.from_string(record.date_start)
date_end_dt = fields.Date.from_string(record.date_end)
# Use min '1' for same date
diff_days = abs((date_end_dt - date_start_dt).days) + 1
wedays = (
record.dmo,
record.dtu,
record.dwe,
record.dth,
record.dfr,
record.dsa,
record.dsu,
)
# filter room types to the pricelist or restriction property
room_types = (
record.room_type_ids
if record.applied_on == "1"
else pms_room_type_obj.search(
[("pms_property_id", "=", record.pms_property_id.id)]
)
)
for i in range(0, diff_days):
ndate = date_start_dt + timedelta(days=i)
if not wedays[ndate.timetuple()[6]]:
continue
self._save(ndate, room_types, record)
return True
def massive_change(self):
self._do_massive_change()
return {
"type": "ir.actions.do_nothing",
}
def massive_change_close(self):
self._do_massive_change()
return True
# TODO: method deprecated and not used anywhere
def is_valid_date(self, chkdate):
self.ensure_one()
wday = chkdate.timetuple()[6]
wedays = (self.dmo, self.dtu, self.dwe, self.dth, self.dfr, self.dsa, self.dsu)
return chkdate >= self.date_start and chkdate <= self.date_end and wedays[wday]

View File

@@ -1,123 +0,0 @@
<?xml version="1.0" ?>
<odoo>
<record model="ir.ui.view" id="view_pms_massive_changes_wizard">
<field name="name">pms.wizard.massive.changes</field>
<field name="model">pms.wizard.massive.changes</field>
<field name="arch" type="xml">
<form string="Massive Changes">
<group>
<field name="pms_property_id" readonly="1" />
<field name="section" required="1" />
<field name="applied_on" required="1" />
<field
name="room_type_ids"
widget="many2many_tags"
attrs="{'invisible':[('applied_on', '!=', '1')], 'required':[('applied_on', '=', '1')]}"
options="{'no_create': True,'no_open': True}"
/>
</group>
<group colspan="8" col="8">
<field name="date_start" required="1" colspan="4" />
<field name="date_end" required="1" colspan="4" />
<field name="dmo" />
<field name="dtu" />
<field name="dwe" />
<field name="dth" />
<field name="dfr" />
<field name="dsa" />
<field name="dsu" />
</group>
<!-- Restriction Fields -->
<group
col="8"
attrs="{'invisible':[('section', '!=', 'restrictions')]}"
>
<field
name="restriction_id"
attrs="{'required':[('section', '=', 'restrictions')]}"
options="{'no_create': True,'no_open': True}"
colspan="8"
/>
<field colspan="2" name="change_min_stay" />
<field
colspan="2"
name="min_stay"
attrs="{'readonly':[('change_min_stay', '=', False)]}"
/>
<field colspan="2" name="change_max_stay" />
<field
colspan="2"
name="max_stay"
attrs="{'readonly':[('change_max_stay', '=', False)]}"
/>
<field colspan="2" name="change_min_stay_arrival" />
<field
colspan="2"
name="min_stay_arrival"
attrs="{'readonly':[('change_min_stay_arrival', '=', False)]}"
/>
<field colspan="2" name="change_max_stay_arrival" />
<field
colspan="2"
name="max_stay_arrival"
attrs="{'readonly':[('change_max_stay_arrival', '=', False)]}"
/>
<field colspan="2" name="change_closed" />
<field
colspan="6"
name="closed"
attrs="{'readonly':[('change_closed', '=', False)]}"
/>
<field colspan="2" name="change_closed_departure" />
<field
colspan="6"
name="closed_departure"
attrs="{'readonly':[('change_closed_departure', '=', False)]}"
/>
<field colspan="2" name="change_closed_arrival" />
<field
colspan="6"
name="closed_arrival"
attrs="{'readonly':[('change_closed_arrival', '=', False)]}"
/>
</group>
<!-- Pricelist Fields -->
<group attrs="{'invisible':[('section', '!=', 'prices')]}">
<field
name="pricelist_id"
attrs="{'required':[('section', '=', 'prices')]}"
options="{'no_create': True,'no_open': True}"
/>
<field
name="price"
attrs="{'required':[('section', '=', 'prices')]}"
/>
</group>
<footer>
<button
name="massive_change"
string="Massive Change"
type="object"
class="oe_highlight"
/>
<button
name="massive_change_close"
string="Massive Change &amp; Close"
type="object"
class="oe_highlight"
/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="action_pms_massive_change" model="ir.actions.act_window">
<field name="name">Massive Change</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">pms.wizard.massive.changes</field>
<field name="view_id" ref="view_pms_massive_changes_wizard" />
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>

View File

@@ -1,39 +0,0 @@
# Copyright 2017 Alexandre Díaz
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import api, fields, models
class MassivePriceChangeWizard(models.TransientModel):
_name = "pms.wizard.massive.price.reservation.days"
_description = "Massive Price Changes"
new_price = fields.Float("New Price", default=1, min=1)
change_price = fields.Boolean("Change Prices", default=False)
new_discount = fields.Float("New Discount", default=0, min=1)
change_discount = fields.Boolean("Change Discounts", default=False)
def massive_price_change_days(self):
self.ensure_one()
pms_reservation_obj = self.env["pms.reservation"]
reservation_id = pms_reservation_obj.browse(self.env.context.get("active_id"))
if not reservation_id:
return False
cmds = []
for rline in reservation_id.reservation_line_ids:
cmds.append(
(
1,
rline.id,
{
"price": self.new_price
if self.change_price == True
else rline.price,
"discount": self.new_discount
if self.change_discount == True
else rline.discount,
},
)
)
reservation_id.write({"reservation_line_ids": cmds})
return True

View File

@@ -1,48 +0,0 @@
<?xml version="1.0" ?>
<odoo>
<record model="ir.ui.view" id="view_pms_massive_price_change_wizard">
<field name="name">pms.wizard.massive.price.reservation.days</field>
<field name="model">pms.wizard.massive.price.reservation.days</field>
<field name="arch" type="xml">
<form string="Massive Price Change">
<group>
<field name="change_price" />
<field
name="new_price"
required="1"
attrs="{'readonly':[('change_price','=', False)]}"
/>
</group>
<group>
<field name="change_discount" />
<field
name="new_discount"
required="1"
attrs="{'readonly':[('change_discount','=', False)]}"
/>
</group>
<footer>
<button
name="massive_price_change_days"
string="Massive Change"
type="object"
class="oe_highlight"
/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<record
id="action_pms_massive_price_change_reservation_days"
model="ir.actions.act_window"
>
<field name="name">Massive Price Change</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">pms.wizard.massive.price.reservation.days</field>
<field name="view_id" ref="view_pms_massive_price_change_wizard" />
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>

View File

@@ -1,44 +0,0 @@
# Copyright 2017 Darío Lodeiros
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
class ServiceOnDay(models.TransientModel):
_name = "service.on.day"
_description = "Set service for today"
def _get_default_date(self):
tz_property = self.env.user.pms_property_id.tz
today = fields.Date.context_today(self.with_context(tz=tz_property))
return fields.Date.from_string(today).strftime(DEFAULT_SERVER_DATE_FORMAT)
product_id = fields.Many2one(
"product.product", "Service", required=True, domain=[("per_day", "=", True)]
)
product_qty = fields.Integer("Quantity", default=1)
date = fields.Date("Date", default=_get_default_date)
def set_service(self):
self.ensure_one()
pms_reservation_obj = self.env["pms.reservation"]
reservation = pms_reservation_obj.browse(self.env.context.get("active_id"))
if not reservation:
return False
service_data = [
(
0,
0,
{
"product_id": self.product_id.id,
"reservation_id": reservation.id,
"folio_id": reservation.folio_id.id,
"product_qty": self.product_qty,
"service_line_ids": [
(0, 0, {"date": self.date, "day_qty": self.product_qty})
],
},
)
]
reservation.write({"service_ids": service_data})
return True

View File

@@ -1,29 +0,0 @@
<?xml version="1.0" ?>
<odoo>
<record model="ir.ui.view" id="service_on_day_view_form">
<field name="name">service.on.day.view.form</field>
<field name="model">service.on.day</field>
<field name="arch" type="xml">
<form string="Service on Day">
<group>
<field name="product_id" />
<field name="date" />
<field name="product_qty" />
</group>
<footer>
<button name="set_service" string="Set Service" type="object" class="oe_highlight" />
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="action_service_on_day" model="ir.actions.act_window">
<field name="name">Service on Day</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">service.on.day</field>
<field name="view_id" ref="service_on_day_view_form" />
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>

View File

@@ -1,22 +0,0 @@
# Copyright 2017 Alexandre Díaz
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from datetime import timedelta
from openerp import _, api, fields, models
from openerp.exceptions import ValidationError
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
class SplitReservationWizard(models.TransientModel):
_name = "pms.wizard.split.reservation"
_description = "Split Reservation"
nights = fields.Integer("Nights", default=1, min=1)
def split_reservation(self):
reservation_id = self.env["pms.reservation"].browse(
self.env.context.get("active_id")
)
if reservation_id:
reservation_id.split(self.nights)
return True

View File

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

View File

@@ -1,687 +0,0 @@
# Copyright 2018 Dario Lodeiros
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
import time
from datetime import datetime, timedelta
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
_logger = logging.getLogger(__name__)
class FolioWizard(models.TransientModel):
_name = "pms.folio.wizard"
_description = "Wizard for reservation groups"
@api.model
def _get_default_checkin(self):
folio = False
if "folio_id" in self._context:
folio = self.env["pms.folio"].search(
[("id", "=", self._context["folio_id"])]
)
if folio and folio.reservation_ids:
return folio.reservation_ids[0].checkin
return fields.Date.today()
@api.model
def _get_default_checkout(self):
folio = False
if "folio_id" in self._context:
folio = self.env["pms.folio"].search(
[("id", "=", self._context["folio_id"])]
)
if folio and folio.reservation_ids:
return folio.reservation_ids[0].checkout
return fields.Date.today()
@api.model
def _get_default_pricelist(self):
return self.env.user.pms_property_id.default_pricelist_id.id
partner_id = fields.Many2one("res.partner", required=True, string="Customer")
email = fields.Char("E-mail")
mobile = fields.Char("Mobile")
pricelist_id = fields.Many2one(
"product.pricelist",
string="Pricelist",
required=True,
default=_get_default_pricelist,
help="Pricelist for current folio.",
)
checkin = fields.Date("Check In", required=True, default=_get_default_checkin)
checkout = fields.Date("Check Out", required=True, default=_get_default_checkout)
credit_card_details = fields.Text("Credit Card Details")
internal_comment = fields.Text(string="Internal Folio Notes")
reservation_wizard_ids = fields.One2many(
"pms.reservation.wizard", "folio_wizard_id", string="Resevations"
)
service_wizard_ids = fields.One2many(
"pms.service.wizard", "folio_wizard_id", string="Services"
)
total = fields.Float("Total", compute="_computed_total")
date_order = fields.Date("Date Order", default=fields.Datetime.now)
confirm = fields.Boolean("Confirm Reservations", default="1")
autoassign = fields.Boolean("Autoassign", default="1")
company_id = fields.Many2one(
"res.company", "Company", default=lambda self: self.env.company
)
channel_type = fields.Selection(
[("direct", "Direct"), ("ota", "Ota"), ("agency", "Agency")],
string="Sales Channel",
)
room_type_wizard_ids = fields.One2many(
"pms.room.type.wizard", "folio_wizard_id", string="Room Types"
)
def assign_rooms(self):
self.assign = True
@api.onchange("pricelist_id")
def onchange_pricelist_id(self):
if self.pricelist_id:
self.onchange_checks()
@api.onchange("partner_id")
def onchange_partner_id(self):
if self.partner_id:
vals = {}
pricelist = (
self.partner_id.property_product_pricelist
and self.partner_id.property_product_pricelist.id
or self.env.user.pms_property_id.default_pricelist_id.id
)
vals.update(
{
"pricelist_id": pricelist,
"email": self.partner_id.email,
"mobile": self.partner_id.mobile,
}
)
self.update(vals)
@api.onchange("autoassign")
def create_reservations(self):
self.ensure_one()
cmds = [(5, 0, 0)]
for line in self.room_type_wizard_ids:
if line.rooms_num == 0:
continue
if line.rooms_num > line.max_rooms:
raise ValidationError(_("Too many rooms!"))
elif line.room_type_id:
occupied = self.env["pms.reservation"].get_reservations(
line.checkin,
(
fields.Date.from_string(line.checkout) - timedelta(days=1)
).strftime(DEFAULT_SERVER_DATE_FORMAT),
)
rooms_occupied = occupied.mapped("room_id.id")
free_rooms = self.env["pms.room"].search(
[
("id", "not in", rooms_occupied),
("room_type_id.id", "=", line.room_type_id.id),
],
order="sequence",
limit=line.rooms_num,
)
room_ids = free_rooms.mapped("id")
room_list = self.env["pms.room"].search([("id", "in", room_ids)])
checkin_dt = fields.Date.from_string(line.checkin)
checkout_dt = fields.Date.from_string(line.checkout)
nights = abs((checkout_dt - checkin_dt).days)
for room in room_list:
adults = (
self.env["pms.room"].search([("id", "=", room.id)]).capacity
)
cmds.append(
(
0,
False,
{
"checkin": line.checkin,
"checkout": line.checkout,
"discount": line.discount,
"room_id": room.id,
"nights": nights,
"adults": adults,
"folio_wizard_id": self.id,
"board_service_room_id": line.board_service_room_id,
"children": 0,
"room_type_id": line.room_type_id,
"price": line.price,
},
)
)
self.reservation_wizard_ids = cmds
@api.onchange("checkin", "checkout")
def onchange_checks(self):
"""
When you change checkin or checkout it will checked it
and update the qty of folio line
-----------------------------------------------------------------
@param self: object pointer
"""
self.ensure_one()
tz_property = self.env.user.pms_property_id.tz
today = fields.Date.context_today(self.with_context(tz=tz_property))
checkin_dt = (
fields.Date.from_string(today)
if not self.checkin
else fields.Date.from_string(self.checkin)
)
checkout_dt = (
fields.Date.from_string(today)
if not self.checkout
else fields.Date.from_string(self.checkout)
)
if checkin_dt >= checkout_dt:
checkout_dt = checkin_dt + timedelta(days=1)
checkin_str = checkin_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)
checkout_str = checkout_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)
cmds = [(5, 0, 0)]
room_type_ids = self.env["pms.room.type"].search([])
for room_type in room_type_ids:
cmds.append(
(
0,
False,
{
"room_type_id": room_type.id,
"folio_wizard_id": self.id,
"checkin": checkin_str,
"checkout": checkout_str,
},
)
)
self.update(
{
"checkin": checkin_str,
"checkout": checkout_str,
"room_type_wizard_ids": cmds,
}
)
for room_type in self.room_type_wizard_ids:
room_type.update_price()
@api.depends(
"room_type_wizard_ids.total_price",
"reservation_wizard_ids.price",
"reservation_wizard_ids",
"service_wizard_ids.price_total",
)
def _computed_total(self):
total = 0
for line in self.service_wizard_ids:
total += line.price_total
if not self.reservation_wizard_ids:
for line in self.room_type_wizard_ids:
total += line.total_price
else:
for line in self.reservation_wizard_ids:
total += line.price
self.total = total
def create_folio(self):
self.ensure_one()
if not self.partner_id:
raise ValidationError(_("We need know the customer!"))
reservations = [(5, False, False)]
services = []
if self.autoassign:
self.create_reservations()
for line in self.reservation_wizard_ids:
services_room = []
for product in line.product_ids:
services_room.append((0, False, {"product_id": product.id}))
reservations.append(
(
0,
False,
{
"room_id": line.room_id.id,
"adults": line.adults,
"children": line.children,
"checkin": line.checkin,
"checkout": line.checkout,
"discount": line.discount,
"room_type_id": line.room_type_id.id,
"board_service_room_id": line.board_service_room_id.id,
"to_assign": line.to_assign,
"service_ids": services_room,
# REVIEW: Create folio with reservations dont respect the pricelist_id on folio dict
"pricelist_id": self.pricelist_id.id,
},
)
)
for line in self.service_wizard_ids:
services.append(
(
0,
False,
{
"product_id": line.product_id.id,
"discount": line.discount,
"price_unit": line.price_unit,
"product_qty": line.product_uom_qty,
},
)
)
if not self.reservation_wizard_ids:
raise ValidationError(_("We cant create avoid folio"))
vals = {
"partner_id": self.partner_id.id,
"channel_type": self.channel_type,
"reservation_ids": reservations,
"service_ids": services,
"pricelist_id": self.pricelist_id.id,
"internal_comment": self.internal_comment,
"credit_card_details": self.credit_card_details,
"email": self.email,
"mobile": self.mobile,
}
newfol = self.env["pms.folio"].create(vals)
if self.confirm:
newfol.reservation_ids.confirm()
action = self.env.ref("pms.open_pms_folio1_form_tree_all").read()[0]
if newfol:
action["views"] = [(self.env.ref("pms.pms_folio_view_form").id, "form")]
action["res_id"] = newfol.id
else:
action = {"type": "ir.actions.act_window_close"}
return action
class PmsRoomTypeWizards(models.TransientModel):
_name = "pms.room.type.wizard"
_description = "Virtual Room Type to Reserve Groups"
def _get_default_checkin(self):
return self.folio_wizard_id.checkin
@api.model
def _get_default_checkout(self):
return self.folio_wizard_id.checkout
room_type_id = fields.Many2one("pms.room.type", string="Rooms Type")
rooms_num = fields.Integer("Number of Rooms")
max_rooms = fields.Integer("Max", compute="_compute_max")
price = fields.Float(string="Price by Room")
total_price = fields.Float(
string="Total Price", compute="_compute_total", store="True"
)
folio_wizard_id = fields.Many2one("pms.folio.wizard")
discount = fields.Float("discount")
min_stay = fields.Integer("Min. Days", compute="_compute_max")
checkin = fields.Date("Check In", required=True, default=_get_default_checkin)
checkout = fields.Date("Check Out", required=True, default=_get_default_checkout)
can_confirm = fields.Boolean(compute="_can_confirm")
board_service_room_id = fields.Many2one(
"pms.board.service.room.type", string="Board Service"
)
@api.onchange("rooms_num")
def domain_board_service(self):
for line in self:
board_service_room_ids = []
if line.room_type_id:
pricelist_id = self.folio_wizard_id.pricelist_id.id
board_services_room_type = self.env[
"pms.board.service.room.type"
].search(
[
("pms_room_type_id", "=", self.room_type_id.id),
("pricelist_id", "in", (pricelist_id, False)),
]
)
board_service_room_ids = board_services_room_type.ids
domain_boardservice = [("id", "in", board_service_room_ids)]
return {"domain": {"board_service_room_id": domain_boardservice}}
def _can_confirm(self):
for record in self:
date_start = fields.Date.from_string(record.checkin)
date_end = fields.Date.from_string(record.checkout)
date_diff = abs((date_end - date_start).days)
record.can_confirm = record.max_rooms > 0 and record.min_stay <= date_diff
def _compute_max(self):
# REVIEW: This methid has a incorrect dependencies with pms_channel_conector
# because use avail model defined on this module
for res in self:
user = self.env["res.users"].browse(self.env.uid)
date_start = fields.Date.from_string(res.checkin)
date_end = fields.Date.from_string(res.checkout)
date_diff = abs((date_end - date_start).days)
minstay_restrictions = self.env["pms.room.type.restriction.item"].search(
[("room_type_id", "=", res.room_type_id.id),]
)
avail_restrictions = self.env["pms.room.type.availability"].search(
[("room_type_id", "=", res.room_type_id.id)]
)
real_max = len(
self.env["pms.room.type.availability"].rooms_available(
res.checkin,
(
fields.Date.from_string(res.checkout) - timedelta(days=1)
).strftime(DEFAULT_SERVER_DATE_FORMAT),
res.room_type_id.id,
)
)
res.real_avail = real_max
avail = 100000
min_stay = 0
dates = []
for i in range(0, date_diff):
ndate_dt = date_start + timedelta(days=i)
ndate_str = ndate_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)
dates.append(ndate_str)
if minstay_restrictions:
date_min_days = minstay_restrictions.filtered(
lambda r: r.date == ndate_str
).min_stay
if date_min_days > min_stay:
min_stay = date_min_days
if not user.has_group("pms.group_pms_manager"):
max_avail = real_max
restriction = False
if avail_restrictions:
restriction = avail_restrictions.filtered(
lambda r: r.date == ndate_str
)
if restriction:
if restriction.channel_bind_ids[0]:
max_avail = restriction.channel_bind_ids[
0
].channel_avail
if not restriction and res.room_type_id.channel_bind_ids:
if res.room_type_id.channel_bind_ids[0]:
max_avail = res.room_type_id.channel_bind_ids[
0
].default_availability
if max_avail < avail:
avail = min(max_avail, real_max)
else:
avail = real_max
if avail < 100000 and avail > 0:
res.max_rooms = avail
else:
res.max_rooms = 0
if min_stay > 0:
res.min_stay = min_stay
@api.depends("rooms_num", "price")
def _compute_total(self):
for res in self:
res.total_price = res.price * res.rooms_num
@api.onchange(
"rooms_num",
"discount",
"price",
"board_service_room_id",
"room_type_id",
"checkin",
"checkout",
)
def update_price(self):
for record in self:
if record.rooms_num > record.max_rooms:
raise ValidationError(_("There are not enough rooms!"))
checkin = record.checkin or record.folio_wizard_id.checkin
checkout = record.checkout or record.folio_wizard_id.checkout
chkin_utc_dt = fields.Date.from_string(checkin)
chkout_utc_dt = fields.Date.from_string(checkout)
if chkin_utc_dt >= chkout_utc_dt:
chkout_utc_dt = chkin_utc_dt + timedelta(days=1)
nights = abs((chkout_utc_dt - chkin_utc_dt).days)
pricelist_id = self.folio_wizard_id.pricelist_id.id
res_price = 0
for i in range(0, nights):
ndate = chkin_utc_dt + timedelta(days=i)
ndate_str = ndate.strftime(DEFAULT_SERVER_DATE_FORMAT)
product = record.room_type_id.product_id.with_context(
lang=record.folio_wizard_id.partner_id.lang,
partner=record.folio_wizard_id.partner_id.id,
quantity=1,
date=ndate_str,
pricelist=pricelist_id,
uom=record.room_type_id.product_id.uom_id.id,
)
res_price += product.price
board_service = record.board_service_room_id
if board_service:
if board_service.price_type == "fixed":
board_price = (
board_service.amount * record.room_type_id.capacity * nights
)
res_price += board_price
else:
res_price += (res_price * board_service.amount) * 0.01
price = res_price - ((res_price * record.discount) * 0.01)
vals = {
"checkin": checkin,
"checkout": checkout,
"price": price,
}
record.update(vals)
class ReservationWizard(models.TransientModel):
_name = "pms.reservation.wizard"
_description = "Virtual Reservation to Groups"
_rec_name = "room_id"
room_id = fields.Many2one("pms.room", string="Room")
folio_wizard_id = fields.Many2one("pms.folio.wizard")
adults = fields.Integer("Adults", help="List of adults there in guest list. ")
children = fields.Integer(
"Children", help="Number of children there in guest list."
)
checkin = fields.Date("Check In", required=True)
checkout = fields.Date("Check Out", required=True)
room_type_id = fields.Many2one("pms.room.type", string="Room Type", required=True)
nights = fields.Integer("Nights", readonly=True)
price = fields.Float(string="Total")
partner_id = fields.Many2one(related="folio_wizard_id.partner_id")
discount = fields.Float("discount")
product_ids = fields.Many2many("product.product", string="Products")
board_service_room_id = fields.Many2one(
"pms.board.service.room.type", string="Board Service"
)
@api.onchange("room_id")
def onchange_room_id(self):
for line in self:
if line.checkin and line.checkout:
if line.adults == 0:
line.adults = line.room_id.capacity
line.room_type_id = line.room_id.room_type_id.id
checkout_dt = fields.Date.from_string(line.checkout)
checkout_dt -= timedelta(days=1)
occupied = self.env["pms.reservation"].get_reservations(
line.checkin, checkout_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)
)
rooms_occupied = occupied.mapped("room_id.id")
if line.room_id.id in rooms_occupied:
raise ValidationError(
_(
"This room is occupied!, please, \
choice other room or change the reservation date"
)
)
@api.onchange(
"checkin",
"checkout",
"room_type_id",
"discount",
"board_service_room_id",
"product_ids",
)
def onchange_dates(self):
for line in self:
if not line.checkin:
line.checkin = line.folio_wizard_id.checkin
if not line.checkout:
line.checkout = line.folio_wizard_id.checkout
start_date_utc_dt = fields.Date.from_string(line.checkin)
end_date_utc_dt = fields.Date.from_string(line.checkout)
if line.room_type_id:
# First, compute room price
pricelist_id = line.folio_wizard_id.pricelist_id.id
nights = abs((end_date_utc_dt - start_date_utc_dt).days)
res_price = 0
for i in range(0, nights):
ndate = start_date_utc_dt + timedelta(days=i)
ndate_str = ndate.strftime(DEFAULT_SERVER_DATE_FORMAT)
product = line.room_type_id.product_id.with_context(
lang=line.partner_id.lang,
partner=line.partner_id.id,
quantity=1,
date=ndate_str,
pricelist=pricelist_id,
uom=line.room_type_id.product_id.uom_id.id,
)
res_price += product.price
board_service = line.board_service_room_id
# Second, compute BoardService price
if board_service:
if board_service.price_type == "fixed":
board_price = board_service.amount * line.adults * nights
res_price += board_price
else:
res_price += (res_price * board_service.amount) * 0.01
res_price = res_price - (res_price * line.discount) * 0.01
# And compute products Room price
for product in line.product_ids:
pricelist_id = line.folio_wizard_id.pricelist_id.id
product = product.with_context(
lang=line.folio_wizard_id.partner_id.lang,
partner=line.folio_wizard_id.partner_id.id,
quantity=1,
date=fields.Datetime.now(),
pricelist=pricelist_id,
uom=product.uom_id.id,
)
values = {
"pricelist_id": pricelist_id,
"product_id": product.id,
"product_qty": 1,
}
service_line = self.env["pms.service"].new(values)
vals = service_line.with_context(
{"default_folio_id": line.folio_wizard_id}
)._prepare_add_missing_fields(values)
if product.per_day or product.per_person:
checkin_dt = fields.Date.from_string(line.checkin)
checkout_dt = fields.Date.from_string(line.checkout)
nights = abs((checkout_dt - checkin_dt).days)
vals.update(
service_line.prepare_service_ids(
dfrom=line.checkin,
days=nights,
per_person=product.per_person,
persons=line.adults,
old_line_days=False,
consumed_on=product.consumed_on,
)
)
service_line.update(vals)
price_product = service_line.price_unit * (
1 - (line.discount or 0.0) * 0.01
)
pricelist = line.folio_wizard_id.pricelist_id
currency = pricelist.currency_id
taxes = service_line.tax_ids.compute_all(
price_product,
currency,
service_line.product_qty,
product=product,
)
# TODO Daily Limits
res_price += taxes["total_included"]
line.price = res_price
end_date_utc_dt -= timedelta(days=1)
occupied = self.env["pms.reservation"].get_reservations(
line.checkin, end_date_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)
)
rooms_occupied = occupied.mapped("room_id.id")
domain_rooms = [("id", "not in", rooms_occupied)]
return {"domain": {"room_id": domain_rooms}}
class ServiceWizard(models.TransientModel):
_name = "pms.service.wizard"
_description = "Virtual Service for Groups"
product_id = fields.Many2one("product.product", string="Service")
name = fields.Char("Description")
folio_wizard_id = fields.Many2one("pms.folio.wizard")
discount = fields.Float("discount")
price_unit = fields.Float(
"Unit Price", required=True, digits=("Product Price"), default=0.0
)
price_total = fields.Float(
compute="_compute_amount", string="Total", readonly=True, store=True
)
tax_ids = fields.Many2many(
"account.tax",
string="Taxes",
domain=["|", ("active", "=", False), ("active", "=", True)],
)
product_uom_qty = fields.Float(
string="Quantity",
digits=("Product Unit of Measure"),
required=True,
default=1.0,
)
@api.onchange("product_id")
def onchange_product_id(self):
if self.product_id:
pricelist_id = self.folio_wizard_id.pricelist_id.id
prod = self.product_id.with_context(
lang=self.folio_wizard_id.partner_id.lang,
partner=self.folio_wizard_id.partner_id.id,
quantity=1,
date=fields.Datetime.now(),
pricelist=pricelist_id,
uom=self.product_id.uom_id.id,
)
# TODO change pricelist for partner
values = {"pricelist_id": pricelist_id, "product_id": prod.id}
line = self.env["pms.service"].new(values)
vals = line.with_context(
{"default_folio_id": self.folio_wizard_id}
)._prepare_add_missing_fields(values)
self.update(vals)
@api.depends("product_uom_qty", "discount", "price_unit", "tax_ids")
def _compute_amount(self):
"""
Compute the amounts of the service line.
"""
for record in self:
pricelist = record.folio_wizard_id.pricelist_id
currency = pricelist.currency_id
product = record.product_id
price = record.price_unit * (1 - (record.discount or 0.0) * 0.01)
taxes = record.tax_ids.compute_all(
price, currency, record.product_uom_qty, product=product
)
record.update(
{"price_total": taxes["total_included"],}
)

View File

@@ -1,177 +0,0 @@
<?xml version="1.0" ?>
<odoo>
<record model="ir.ui.view" id="pms_folio_wizard">
<field name="name">pms.folio.wizard</field>
<field name="model">pms.folio.wizard</field>
<field name="arch" type="xml">
<form string="Reservation Wizard">
<sheet>
<group>
<group>
<field name="checkin" required="1" />
<field name="checkout" required="1" />
<field name="partner_id" />
<field name="mobile" required="1" />
<field name="email" required="1" />
<field name="company_id" invisible="1" />
</group>
<group>
<field name="channel_type" required="1" force_save="1" />
<field name="pricelist_id" />
<field name="internal_comment" />
<field name="credit_card_details" />
<field name="date_order" invisible="1" />
</group>
</group>
<group>
<field
name="room_type_wizard_ids"
nolabel="1"
attrs="{'invisible': [('autoassign','=',False)]}"
>
<tree
editable="bottom"
create="false"
delete="false"
decoration-danger="max_rooms &lt; rooms_num"
decoration-muted="can_confirm == False"
decoration-success="max_rooms &gt;= rooms_num and rooms_num &gt; 0"
>
<field name="min_stay" />
<field name="max_rooms" />
<field name="folio_wizard_id" invisible="1" />
<field
name="room_type_id"
string="Room Type"
force_save="1"
readonly="1"
/>
<field
name="rooms_num"
force_save="1"
attrs="{'readonly': [('can_confirm', '=', False)]}"
/>
<field
name="board_service_room_id"
attrs="{'readonly': [('rooms_num', '=', 0)]}"
options="{'no_create': True,'no_open': True}"
/>
<field name="checkin" widget="date" />
<field name="checkout" widget="date" />
<field
name="discount"
force_save="1"
attrs="{'readonly': [('can_confirm','=',False)]}"
/>
<field
name="price"
force_save="1"
attrs="{'readonly': [('can_confirm','=',False)]}"
/>
<field name="total_price" />
<field name="can_confirm" invisible="1" />
</tree>
</field>
</group>
<label
for="autoassign"
attrs="{'invisible': [('autoassign','=', False)]}"
/>
<field
name="autoassign"
attrs="{'invisible': [('autoassign','=', False)]}"
/>
<field name="confirm" invisible="1" />
<group colspan="2" class="oe_subtotal_footer">
<field name="total" />
</group>
<group>
<field
name="reservation_wizard_ids"
colspan="4"
string="Room Lines"
nolabel="1"
attrs="{'invisible': [('autoassign','=',True)]}"
>
<tree string="Room Line" delete="false" editable="buttom">
<field
name="room_id"
string="Room No"
options="{'no_create': True}"
/>
<field
name="room_type_id"
options="{'no_create': True}"
/>
<field name="folio_wizard_id" invisible="1" />
<field name="checkin" />
<field name="checkout" />
<field name="nights" />
<field name="adults" />
<field name="children" />
<field
name="board_service_room_id"
domain="[
('pms_room_type_id', '=', room_type_id)
]"
/>
<field
name="product_ids"
widget="many2many_tags"
domain="[
('sale_ok', '=', True)
]"
options="{'no_create': True}"
/>
<field name="discount" />
<field name="price" />
<field name="partner_id" invisible="1" />
</tree>
</field>
</group>
<group string="Articles" name="articles">
<field
name="service_wizard_ids"
colspan="4"
string="Services"
nolabel="1"
>
<tree string="Services" editable="buttom">
<field
name="product_id"
string="Service"
options="{'no_create': True}"
domain="[('sale_ok', '=', True),
('per_day', '=', False),
('per_person', '=', False)]"
/>
<field name="folio_wizard_id" invisible="0" />
<field name="product_uom_qty" />
<field name="price_unit" />
<field name="discount" />
<field name="tax_ids" widget="many2many_tags" />
<field name="price_total" />
</tree>
</field>
</group>
<footer>
<button
name="create_folio"
string="Create Reservations"
type="object"
class="oe_highlight"
/>
</footer>
</sheet>
</form>
</field>
</record>
<record id="open_wizard_reservations" model="ir.actions.act_window">
<field name="name">Reservation Wizard</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">pms.folio.wizard</field>
<field name="view_id" ref="pms.pms_folio_wizard" />
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>