mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[WIP] Clean Refactoring
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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",],
|
||||
|
||||
@@ -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>
|
||||
@@ -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;">
|
||||
</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;">
|
||||
</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;">
|
||||
</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;">
|
||||
</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
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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','<=',0),
|
||||
('parent_reservation','!=',False)]}"
|
||||
attrs="{'invisible': [('checkin_partner_pending_count','<=',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','>',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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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','>',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>
|
||||
@@ -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]
|
||||
@@ -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 & 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>
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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"],}
|
||||
)
|
||||
@@ -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 < rooms_num"
|
||||
decoration-muted="can_confirm == False"
|
||||
decoration-success="max_rooms >= rooms_num and rooms_num > 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>
|
||||
Reference in New Issue
Block a user