Merge branch '14.0' into 14.0-pms_improvements_layout_precheckin

This commit is contained in:
Darío Lodeiros
2021-11-09 08:00:48 +01:00
committed by GitHub
33 changed files with 889 additions and 192 deletions

View File

@@ -1,13 +1,13 @@
# Do NOT update manually; changes here will be overwritten by Copier
_commit: v1.1.4
_commit: v1.3.4
_src_path: https://github.com/OCA/oca-addons-repo-template.git
ci: Travis
dependency_installation_mode: OCA
generate_requirements_txt: true
include_wkhtmltopdf: false
odoo_version: 14.0
rebel_module_groups: []
repo_description:
All-in-One Property Management System (PMS) focused on medium-sizeations.
repo_description: All-in-One Property Management System (PMS) focused on medium-sizeations.
repo_name: Property Management System
repo_slug: pms
travis_apt_packages: []

View File

@@ -1,5 +1,6 @@
env:
browser: true
es6: true
# See https://github.com/OCA/odoo-community.org/issues/37#issuecomment-470686449
parserOptions:
@@ -14,7 +15,7 @@ globals:
moment: readonly
odoo: readonly
openerp: readonly
Promise: readonly
owl: readonly
# Styling is handled by Prettier, so we only need to enable AST rules;
# see https://github.com/OCA/maintainer-quality-tools/pull/618#issuecomment-558576890

View File

@@ -10,4 +10,8 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
# The pylint-odoo version we use here does not support python 3.10
# https://github.com/OCA/oca-addons-repo-template/issues/80
python-version: "3.9"
- uses: pre-commit/action@v2.0.0

69
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: Mark stale issues and pull requests
on:
schedule:
- cron: "0 12 * * 0"
jobs:
stale:
runs-on: ubuntu-latest
steps:
- name: Stale PRs and issues policy
uses: actions/stale@v4
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# General settings.
ascending: true
remove-stale-when-updated: true
# Pull Requests settings.
# 120+30 day stale policy for PRs
# * Except PRs marked as "no stale"
days-before-pr-stale: 120
days-before-pr-close: 30
exempt-pr-labels: "no stale"
stale-pr-label: "stale"
stale-pr-message: >
There hasn't been any activity on this pull request in the past 4 months, so
it has been marked as stale and it will be closed automatically if no
further activity occurs in the next 30 days.
If you want this PR to never become stale, please ask a PSC member to apply
the "no stale" label.
# Issues settings.
# 180+30 day stale policy for open issues
# * Except Issues marked as "no stale"
days-before-issue-stale: 180
days-before-issue-close: 30
exempt-issue-labels: "no stale,needs more information"
stale-issue-label: "stale"
stale-issue-message: >
There hasn't been any activity on this issue in the past 6 months, so it has
been marked as stale and it will be closed automatically if no further
activity occurs in the next 30 days.
If you want this issue to never become stale, please ask a PSC member to
apply the "no stale" label.
# 15+30 day stale policy for issues pending more information
# * Issues that are pending more information
# * Except Issues marked as "no stale"
- name: Needs more information stale issues policy
uses: actions/stale@v4
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
ascending: true
only-labels: "needs more information"
exempt-issue-labels: "no stale"
days-before-stale: 15
days-before-close: 30
days-before-pr-stale: -1
days-before-pr-close: -1
remove-stale-when-updated: true
stale-issue-label: "stale"
stale-issue-message: >
This issue needs more information and there hasn't been any activity
recently, so it has been marked as stale and it will be closed automatically
if no further activity occurs in the next 30 days.
If you think this is a mistake, please ask a PSC member to remove the "needs
more information" label.

View File

@@ -5,7 +5,7 @@ exclude: |
# Files and folders generated by bots, to avoid loops
^setup/|/static/description/index\.html$|
# We don't want to mess with tool-generated files
.svg$|/tests/([^/]+/)?cassettes/|
.svg$|/tests/([^/]+/)?cassettes/|^.copier-answers.yml$|^.github/|
# Maybe reactivate this when all README files include prettier ignore tags?
^README\.md$|
# Library files can have extraneous formatting (even minimized)

View File

@@ -21,9 +21,9 @@ addon | version | maintainers | summary
--- | --- | --- | ---
[multi_pms_properties](multi_pms_properties/) | 14.0.1.0.0 | | Multi Properties Manager
[payment_acquirer_multi_pms_properties](payment_acquirer_multi_pms_properties/) | 14.0.1.0.1 | | Payment Acquirer Multiproperty
[pms](pms/) | 14.0.2.20.0 | | A property management system
[pms](pms/) | 14.0.2.20.3 | | A property management system
[pms_housekeeping](pms_housekeeping/) | 14.0.1.0.1 | | Housekeeping
[pms_l10n_es](pms_l10n_es/) | 14.0.2.1.1 | | PMS Spanish Adaptation
[pms_l10n_es](pms_l10n_es/) | 14.0.2.2.0 | | PMS Spanish Adaptation
[pms_rooming_xls](pms_rooming_xls/) | 14.0.1.0.0 | | Rooming xlsx Management
[//]: # (end addons)

View File

@@ -4,7 +4,7 @@
{
"name": "PMS (Property Management System)",
"summary": "A property management system",
"version": "14.0.2.20.0",
"version": "14.0.2.20.3",
"development_status": "Alpha",
"category": "Generic Modules/Property Management System",
"website": "https://github.com/OCA/pms",
@@ -88,6 +88,7 @@
"views/precheckin_portal_templates.xml",
"wizards/wizard_massive_changes.xml",
"wizards/wizard_advanced_filters.xml",
"views/payment_transaction_views.xml",
],
"demo": [
"demo/pms_master_data.xml",

View File

@@ -4,6 +4,7 @@ from odoo import _, fields, http, tools
from odoo.exceptions import AccessError, MissingError
from odoo.http import request
from odoo.addons.payment.controllers.portal import PaymentProcessing
from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager
@@ -26,10 +27,127 @@ class PortalFolio(CustomerPortal):
def _folio_get_page_view_values(self, folio, access_token, **kwargs):
values = {"folio": folio, "token": access_token}
payment_inputs = request.env["payment.acquirer"]._get_available_payment_input(
partner=folio.partner_id, company=folio.company_id
)
is_public_user = request.env.user._is_public()
if is_public_user:
payment_inputs.pop("pms", None)
token_count = (
request.env["payment.token"]
.sudo()
.search_count(
[
("acquirer_id.company_id", "=", folio.company_id.id),
("partner_id", "=", folio.partner_id.id),
]
)
)
values["existing_token"] = token_count > 0
values.update(payment_inputs)
values["partner_id"] = (
folio.partner_id if is_public_user else request.env.user.partner_id,
)
return self._get_page_view_values(
folio, access_token, values, "my_folios_history", False, **kwargs
)
@http.route(
"/folio/pay/<int:folio_id>/form_tx", type="json", auth="public", website=True
)
def folio_pay_form(
self, acquirer_id, folio_id, save_token=False, access_token=None, **kwargs
):
folio_sudo = request.env["pms.folio"].sudo().browse(folio_id)
if not folio_sudo:
return False
try:
acquirer_id = int(acquirer_id)
except Exception:
return False
if request.env.user._is_public():
save_token = False # we avoid to create a token for the public user
success_url = kwargs.get(
"success_url",
"%s?%s" % (folio_sudo.access_url, access_token if access_token else ""),
)
vals = {
"acquirer_id": acquirer_id,
"return_url": success_url,
}
if save_token:
vals["type"] = "form_save"
transaction = folio_sudo._create_payment_transaction(vals)
PaymentProcessing.add_payment_transaction(transaction)
if not transaction:
return False
tx_ids_list = set(request.session.get("__payment_tx_ids__", [])) | set(
transaction.ids
)
request.session["__payment_tx_ids__"] = list(tx_ids_list)
return transaction.render_folio_button(
folio_sudo,
submit_txt=_("Pay & Confirm"),
render_values={
"type": "form_save" if save_token else "form",
"alias_usage": _(
"If we store your payment information on our server, "
"subscription payments will be made automatically."
),
},
)
# @http.route(
# '/invoice/pay/<int:invoice_id>/s2s_token_tx',
# type='http',
# auth='public',
# website=True
# )
# def invoice_pay_token(self, invoice_id, pm_id=None, **kwargs):
# """ Use a token to perform a s2s transaction """
# error_url = kwargs.get('error_url', '/my')
# access_token = kwargs.get('access_token')
# params = {}
# if access_token:
# params['access_token'] = access_token
#
# invoice_sudo = request.env['account.move'].sudo().browse(invoice_id).exists()
# if not invoice_sudo:
# params['error'] = 'pay_invoice_invalid_doc'
# return request.redirect(_build_url_w_params(error_url, params))
#
# success_url = kwargs.get(
# 'success_url',
# "%s?%s" % (
# invoice_sudo.access_url,
# url_encode({'access_token': access_token}) if access_token else '')
# )
# try:
# token = request.env['payment.token'].sudo().browse(int(pm_id))
# except (ValueError, TypeError):
# token = False
# token_owner = invoice_sudo.partner_id if \
# request.env.user._is_public() else request.env.user.partner_id
# if not token or token.partner_id != token_owner:
# params['error'] = 'pay_invoice_invalid_token'
# return request.redirect(_build_url_w_params(error_url, params))
#
# vals = {
# 'payment_token_id': token.id,
# 'type': 'server2server',
# 'return_url': _build_url_w_params(success_url, params),
# }
#
# tx = invoice_sudo._create_payment_transaction(vals)
# PaymentProcessing.add_payment_transaction(tx)
#
# params['success'] = 'pay_invoice'
# return request.redirect('/payment/process')
@http.route(
["/my/folios", "/my/folios/page/<int:page>"],
type="http",

View File

@@ -169,6 +169,21 @@ msgstr ""
msgid "<i class=\"fa fa-download\"/> Download"
msgstr ""
#. module: pms
#: model_terms:ir.ui.view,arch_db:pms.portal_folio_page_payment
msgid "<i class=\"fa fa-fw fa-arrow-circle-right\"/> Pay Now"
msgstr ""
#. module: pms
#: model_terms:ir.ui.view,arch_db:pms.portal_folio_page_payment
msgid "<i class=\"fa fa-fw fa-check-circle\"/> Paid"
msgstr ""
#. module: pms
#: model_terms:ir.ui.view,arch_db:pms.portal_folio_page_payment
msgid "<i class=\"fa fa-fw fa-check-circle\"/> Pending"
msgstr ""
#. module: pms
#: model_terms:ir.ui.view,arch_db:pms.pms_reservation_view_form
msgid ""
@@ -784,6 +799,18 @@ msgid ""
"Customer Invoice/Credit Note"
msgstr ""
#. module: pms
#: code:addons/pms/models/pms_folio.py:0
#, python-format
msgid "A journal must be specified for the acquirer %s."
msgstr ""
#. module: pms
#: code:addons/pms/models/pms_folio.py:0
#, python-format
msgid "A payment acquirer is required to create a transaction."
msgstr ""
#. module: pms
#: model:ir.model.fields,help:pms.field_folio_advance_payment_inv__advance_payment_method
msgid ""
@@ -800,6 +827,18 @@ msgid ""
"A service is a non-material product you provide."
msgstr ""
#. module: pms
#: code:addons/pms/models/pms_folio.py:0
#, python-format
msgid "A transaction can't be linked to folios having different currencies."
msgstr ""
#. module: pms
#: code:addons/pms/models/pms_folio.py:0
#, python-format
msgid "A transaction can't be linked to folios having different partners."
msgstr ""
#. module: pms
#: model:ir.model.fields,field_description:pms.field_pms_property__user_ids
msgid "Accepted Users"
@@ -1267,13 +1306,18 @@ msgid "Apply Pricelist"
msgstr ""
#. module: pms
#: model:ir.model.fields,field_description:pms.field_pms_board_service_room_type__by_default
msgid "Apply by Default"
#: model_terms:ir.ui.view,arch_db:pms.massive_changes_wizard
msgid "Apply and close"
msgstr ""
#. module: pms
#: model_terms:ir.ui.view,arch_db:pms.massive_changes_wizard
msgid "Apply changes"
msgid "Apply and continue"
msgstr ""
#. module: pms
#: model:ir.model.fields,field_description:pms.field_pms_board_service_room_type__by_default
msgid "Apply by Default"
msgstr ""
#. module: pms
@@ -2200,6 +2244,11 @@ msgstr ""
msgid "Class to which the room type belongs"
msgstr ""
#. module: pms
#: model_terms:ir.ui.view,arch_db:pms.portal_folio_payment
msgid "Close"
msgstr ""
#. module: pms
#: model:ir.model.fields,field_description:pms.field_pms_availability_plan_rule__closed
#: model:ir.model.fields,field_description:pms.field_pms_massive_changes_wizard__closed
@@ -2469,13 +2518,6 @@ msgstr ""
msgid "Contacts"
msgstr ""
#. module: pms
#: model:ir.model.fields,help:pms.field_folio_sale_line__product_uom_category_id
msgid ""
"Conversion between Units of Measure can only occur if they belong to the "
"same category. The conversion will be made based on the ratios."
msgstr ""
#. module: pms
#: model:ir.model.fields,field_description:pms.field_pms_room_type__standard_price
msgid "Cost"
@@ -2988,7 +3030,6 @@ msgstr ""
#. module: pms
#: model:ir.model.fields,help:pms.field_pms_folio__credit_card_details
#: model:ir.model.fields,help:pms.field_pms_reservation__credit_card_details
msgid "Details of partner credit card"
msgstr ""
@@ -3104,6 +3145,7 @@ msgstr ""
#: model:ir.model.fields,field_description:pms.field_ir_config_parameter__display_name
#: model:ir.model.fields,field_description:pms.field_ir_http__display_name
#: model:ir.model.fields,field_description:pms.field_mail_compose_message__display_name
#: model:ir.model.fields,field_description:pms.field_payment_transaction__display_name
#: model:ir.model.fields,field_description:pms.field_pms_advanced_filters_wizard__display_name
#: model:ir.model.fields,field_description:pms.field_pms_amenity__display_name
#: model:ir.model.fields,field_description:pms.field_pms_amenity_type__display_name
@@ -3734,6 +3776,7 @@ msgstr ""
#: model:ir.model.fields,field_description:pms.field_account_move__folio_ids
#: model:ir.model.fields,field_description:pms.field_account_move_line__folio_ids
#: model:ir.model.fields,field_description:pms.field_account_payment__folio_ids
#: model:ir.model.fields,field_description:pms.field_payment_transaction__folio_ids
#: model:ir.model.fields,field_description:pms.field_pms_property__pms_folio_ids
#: model:ir.model.fields,field_description:pms.field_res_partner__pms_folio_ids
#: model:ir.model.fields,field_description:pms.field_res_users__pms_folio_ids
@@ -4071,6 +4114,7 @@ msgstr ""
#: model:ir.model.fields,field_description:pms.field_ir_config_parameter__id
#: model:ir.model.fields,field_description:pms.field_ir_http__id
#: model:ir.model.fields,field_description:pms.field_mail_compose_message__id
#: model:ir.model.fields,field_description:pms.field_payment_transaction__id
#: model:ir.model.fields,field_description:pms.field_pms_advanced_filters_wizard__id
#: model:ir.model.fields,field_description:pms.field_pms_amenity__id
#: model:ir.model.fields,field_description:pms.field_pms_amenity_type__id
@@ -4227,6 +4271,14 @@ msgstr ""
msgid "If unchecked, it will allow you to hide the room type"
msgstr ""
#. module: pms
#: code:addons/pms/controllers/pms_portal.py:0
#, python-format
msgid ""
"If we store your payment information on our server, subscription payments "
"will be made automatically."
msgstr ""
#. module: pms
#: model:ir.model.fields,field_description:pms.field_pms_checkin_partner__image_128
#: model:ir.model.fields,field_description:pms.field_pms_property__image_1920
@@ -4591,6 +4643,18 @@ msgstr ""
msgid "Invalid reservation"
msgstr ""
#. module: pms
#: code:addons/pms/models/pms_folio.py:0
#, python-format
msgid "Invalid token found! Token acquirer %s != %s"
msgstr ""
#. module: pms
#: code:addons/pms/models/pms_folio.py:0
#, python-format
msgid "Invalid token found! Token partner %s != %s"
msgstr ""
#. module: pms
#: model_terms:ir.ui.view,arch_db:pms.portal_my_folio_precheckin
msgid "Invitation email sent"
@@ -4942,6 +5006,7 @@ msgstr ""
#: model:ir.model.fields,field_description:pms.field_ir_config_parameter____last_update
#: model:ir.model.fields,field_description:pms.field_ir_http____last_update
#: model:ir.model.fields,field_description:pms.field_mail_compose_message____last_update
#: model:ir.model.fields,field_description:pms.field_payment_transaction____last_update
#: model:ir.model.fields,field_description:pms.field_pms_advanced_filters_wizard____last_update
#: model:ir.model.fields,field_description:pms.field_pms_amenity____last_update
#: model:ir.model.fields,field_description:pms.field_pms_amenity_type____last_update
@@ -5238,6 +5303,12 @@ msgstr ""
msgid "Massive changes on Pricelist & Availability Plans"
msgstr ""
#. module: pms
#: code:addons/pms/wizards/wizard_massive_changes.py:0
#, python-format
msgid "Massive changes on Pricelist and Availability Plans"
msgstr ""
#. module: pms
#: model:ir.model.fields,field_description:pms.field_pms_folio__max_reservation_priority
#: model:ir.model.fields,help:pms.field_pms_folio__max_reservation_priority
@@ -6183,6 +6254,24 @@ msgstr ""
msgid "Pay"
msgstr ""
#. module: pms
#: code:addons/pms/controllers/pms_portal.py:0
#, python-format
msgid "Pay & Confirm"
msgstr ""
#. module: pms
#: code:addons/pms/models/payment_transaction.py:0
#: model_terms:ir.ui.view,arch_db:pms.portal_folio_payment
#, python-format
msgid "Pay Now"
msgstr ""
#. module: pms
#: model_terms:ir.ui.view,arch_db:pms.portal_folio_payment
msgid "Pay with"
msgstr ""
#. module: pms
#: model:ir.model.fields,field_description:pms.field_pms_property__debit_limit
msgid "Payable Limit"
@@ -6241,6 +6330,11 @@ msgstr ""
msgid "Payment Tokens"
msgstr ""
#. module: pms
#: model:ir.model,name:pms.model_payment_transaction
msgid "Payment Transaction"
msgstr ""
#. module: pms
#: model:ir.model.fields,help:pms.field_pms_folio__payment_term_id
msgid "Payment terms for current folio."
@@ -8198,6 +8292,14 @@ msgstr ""
msgid "Services on Board Service included in Room"
msgstr ""
#. module: pms
#: model:ir.model.fields,help:pms.field_pms_reservation__overnight_room
#: model:ir.model.fields,help:pms.field_pms_reservation_line__overnight_room
#: model:ir.model.fields,help:pms.field_pms_room_type__overnight_room
#: model:ir.model.fields,help:pms.field_pms_room_type_class__overnight
msgid "Set False if if these types of spaces are not used for overnight stays"
msgstr ""
#. module: pms
#: model_terms:ir.ui.view,arch_db:pms.pms_folio_view_form
msgid "Set to Done"
@@ -9685,6 +9787,14 @@ msgstr ""
msgid "Use a barcode to identify this contact."
msgstr ""
#. module: pms
#: model:ir.model.fields,field_description:pms.field_pms_reservation__overnight_room
#: model:ir.model.fields,field_description:pms.field_pms_reservation_line__overnight_room
#: model:ir.model.fields,field_description:pms.field_pms_room_type__overnight_room
#: model:ir.model.fields,field_description:pms.field_pms_room_type_class__overnight
msgid "Use for overnight stays"
msgstr ""
#. module: pms
#: model:ir.model.fields,help:pms.field_account_journal__allowed_pms_payments
msgid "Use to pay for reservations"

View File

@@ -47,3 +47,4 @@ from . import account_journal
from . import pms_availability
from . import res_partner_id_number
from . import pms_automated_mails
from . import payment_transaction

View File

@@ -1,15 +1,15 @@
from odoo import fields, models
from odoo import _, fields, models
class PaymentTransaction(models.Model):
_name = "payment.transaction"
_inherit = "payment.transaction"
folio_ids = fields.Many2many(
string="Folios",
comodel_name="pms.folio",
ondelete="cascade",
relation="account_bank_statement_folio_rel",
column1="account_journal_id",
relation="payment_transaction_folio_rel",
column1="payment_transaction_id",
column2="folio_id",
)
@@ -20,3 +20,23 @@ class PaymentTransaction(models.Model):
if self.folio_ids:
add_payment_vals["folio_ids"] = [(6, 0, self.folio_ids.ids)]
return super(PaymentTransaction, self)._create_payment(add_payment_vals)
def render_folio_button(self, folio, submit_txt=None, render_values=None):
values = {
"partner_id": folio.partner_id.id,
"type": self.type,
}
if render_values:
values.update(render_values)
return (
self.acquirer_id.with_context(
submit_class="btn btn-primary", submit_txt=submit_txt or _("Pay Now")
)
.sudo()
.render(
self.reference,
folio.pending_amount,
folio.currency_id.id,
values=values,
)
)

View File

@@ -325,10 +325,12 @@ class PmsCheckinPartner(models.Model):
record.state = "draft"
if record.reservation_id.state == "cancel":
record.state = "cancel"
elif record.state in ("draft", "cancel"):
elif record.state in ("draft", "precheckin", "cancel"):
if any(
not getattr(record, field)
for field in record._checkin_mandatory_fields()
for field in record._checkin_mandatory_fields(
country=record.nationality_id
)
):
record.state = "draft"
else:
@@ -610,14 +612,9 @@ class PmsCheckinPartner(models.Model):
return res
@api.model
def _checkin_mandatory_fields(self, depends=False):
def _checkin_mandatory_fields(self, country=False, depends=False):
mandatory_fields = [
"name",
"birthdate_date",
"gender",
"document_number",
"document_type",
"document_expedition_date",
]
# api.depends need "reservation_id.state" in the lambda function
if depends:

View File

@@ -185,9 +185,9 @@ class PmsFolio(models.Model):
readonly=True,
copy=False,
comodel_name="payment.transaction",
relation="folio_transaction_rel",
relation="payment_transaction_folio_rel",
column1="folio_id",
column2="transaction_id",
column2="payment_transaction_id",
)
payment_ids = fields.Many2many(
string="Bank Payments",
@@ -964,7 +964,9 @@ class PmsFolio(models.Model):
}
record.update(vals)
else:
journals = record.pms_property_id._get_payment_methods()
journals = record.pms_property_id._get_payment_methods(
automatic_included=True
)
paid_out = 0
for journal in journals:
paid_out += sum(
@@ -1955,3 +1957,78 @@ class PmsFolio(models.Model):
}
self.env["res.partner.id_number"].create(number_values)
record.partner_id = partner
def _create_payment_transaction(self, vals):
# Ensure the currencies are the same.
currency = self[0].currency_id
if any(folio.currency_id != currency for folio in self):
raise ValidationError(
_(
"A transaction can't be linked to folios having different currencies."
)
)
# Ensure the partner are the same.
partner = self[0].partner_id
if any(folio.partner_id != partner for folio in self):
raise ValidationError(
_("A transaction can't be linked to folios having different partners.")
)
# Try to retrieve the acquirer. However, fallback to the token's acquirer.
acquirer_id = vals.get("acquirer_id")
acquirer = None
payment_token_id = vals.get("payment_token_id")
if payment_token_id:
payment_token = self.env["payment.token"].sudo().browse(payment_token_id)
# Check payment_token/acquirer matching or take the acquirer from token
if acquirer_id:
acquirer = self.env["payment.acquirer"].browse(acquirer_id)
if payment_token and payment_token.acquirer_id != acquirer:
raise ValidationError(
_("Invalid token found! Token acquirer %s != %s")
% (payment_token.acquirer_id.name, acquirer.name)
)
if payment_token and payment_token.partner_id != partner:
raise ValidationError(
_("Invalid token found! Token partner %s != %s")
% (payment_token.partner.name, partner.name)
)
else:
acquirer = payment_token.acquirer_id
# Check an acquirer is there.
if not acquirer_id and not acquirer:
raise ValidationError(
_("A payment acquirer is required to create a transaction.")
)
if not acquirer:
acquirer = self.env["payment.acquirer"].browse(acquirer_id)
# Check a journal is set on acquirer.
if not acquirer.journal_id:
raise ValidationError(
_("A journal must be specified for the acquirer %s.", acquirer.name)
)
if not acquirer_id and acquirer:
vals["acquirer_id"] = acquirer.id
vals.update(
{
"amount": sum(self.mapped("amount_total")),
"currency_id": currency.id,
"partner_id": partner.id,
"folio_ids": [(6, 0, self.ids)],
}
)
transaction = self.env["payment.transaction"].create(vals)
# Process directly if payment_token
if transaction.payment_token_id:
transaction.s2s_do_transaction()
return transaction

View File

@@ -151,16 +151,20 @@ class PmsProperty(models.Model):
@api.depends_context(
"checkin",
"checkout",
"real_avail",
"room_type_id",
"ubication_id",
"capacity",
"amenity_ids",
"pricelist_id",
"class_id",
"overnight_rooms",
"current_lines",
)
def _compute_free_room_ids(self):
checkin = self._context["checkin"]
checkout = self._context["checkout"]
if isinstance(checkin, str):
checkin = datetime.datetime.strptime(
checkin, DEFAULT_SERVER_DATE_FORMAT
@@ -175,11 +179,14 @@ class PmsProperty(models.Model):
pricelist_id = self.env.context.get("pricelist_id", False)
room_type_id = self.env.context.get("room_type_id", False)
class_id = self._context.get("class_id", False)
real_avail = self._context.get("real_avail", False)
overnight_rooms = self._context.get("overnight_rooms", False)
for pms_property in self:
free_rooms = pms_property.get_real_free_rooms(
checkin, checkout, current_lines
)
if pricelist_id:
if pricelist_id and not real_avail:
# TODO: only closed_departure take account checkout date!
domain_rules = [
("date", ">=", checkin),
@@ -208,6 +215,14 @@ class PmsProperty(models.Model):
free_rooms = free_rooms.filtered(
lambda x: x.room_type_id.id not in room_types_to_remove
)
if class_id:
free_rooms = free_rooms.filtered(
lambda x: x.room_type_id.class_id.id == class_id
)
if overnight_rooms:
free_rooms = free_rooms.filtered(
lambda x: x.room_type_id.overnight_room
)
if len(free_rooms) > 0:
pms_property.free_room_ids = free_rooms.ids
else:
@@ -261,11 +276,14 @@ class PmsProperty(models.Model):
@api.depends_context(
"checkin",
"checkout",
"real_avail",
"room_type_id",
"ubication_id",
"capacity",
"amenity_ids",
"pricelist_id",
"class_id",
"overnight_rooms",
"current_lines",
)
def _compute_availability(self):
@@ -283,12 +301,18 @@ class PmsProperty(models.Model):
room_type_id = self.env.context.get("room_type_id", False)
pricelist_id = self.env.context.get("pricelist_id", False)
current_lines = self.env.context.get("current_lines", [])
class_id = self._context.get("class_id", False)
real_avail = self._context.get("real_avail", False)
overnight_rooms = self._context.get("overnight_rooms", False)
pms_property = record.with_context(
checkin=checkin,
checkout=checkout,
room_type_id=room_type_id,
current_lines=current_lines,
pricelist_id=pricelist_id,
class_id=class_id,
real_avail=real_avail,
overnight_rooms=overnight_rooms,
)
count_free_rooms = len(pms_property.free_room_ids)
if current_lines and not isinstance(current_lines, list):
@@ -305,7 +329,7 @@ class PmsProperty(models.Model):
pricelist = False
if pricelist_id:
pricelist = self.env["product.pricelist"].browse(pricelist_id)
if pricelist and pricelist.availability_plan_id:
if pricelist and pricelist.availability_plan_id and not real_avail:
domain_rules.append(
("availability_plan_id", "=", pricelist.availability_plan_id.id)
)
@@ -404,12 +428,14 @@ class PmsProperty(models.Model):
dt = dt.replace(tzinfo=None)
return dt
def _get_payment_methods(self):
def _get_payment_methods(self, automatic_included=False):
# We use automatic_included to True to see absolutely
# all the journals with associated payments, if it is
# false, we will only see those journals that can be used
# to pay manually
self.ensure_one()
payment_methods = self.env["account.journal"].search(
[
("allowed_pms_payments", "=", True),
"&",
("type", "in", ["cash", "bank"]),
"|",
("pms_property_ids", "in", self.id),
@@ -422,6 +448,8 @@ class PmsProperty(models.Model):
("company_id", "=", False),
]
)
if not automatic_included:
payment_methods = payment_methods.filtered(lambda p: p.allowed_pms_payments)
return payment_methods
@api.model

View File

@@ -643,7 +643,6 @@ class PmsReservation(models.Model):
comodel_name="res.partner",
inverse_name="reservation_possible_customer_id",
)
is_mail_send = fields.Boolean(string="Mail Sent", default=False)
is_modified_reservation = fields.Boolean(
@@ -652,7 +651,10 @@ class PmsReservation(models.Model):
readonly=False,
store=True,
)
overnight_room = fields.Boolean(
related="room_type_id.overnight_room",
store=True,
)
lang = fields.Many2one(
string="Language", comodel_name="res.lang", compute="_compute_lang"
)
@@ -804,6 +806,7 @@ class PmsReservation(models.Model):
"reservation_line_ids.room_id",
"reservation_line_ids.occupies_availability",
"preferred_room_id",
"room_type_id",
"pricelist_id",
"pms_property_id",
)
@@ -812,7 +815,9 @@ class PmsReservation(models.Model):
if reservation.checkin and reservation.checkout:
if reservation.overbooking or reservation.state in ("cancel"):
reservation.allowed_room_ids = self.env["pms.room"].search(
[("active", "=", True)]
[
("active", "=", True),
]
)
return
pms_property = reservation.pms_property_id
@@ -822,9 +827,12 @@ class PmsReservation(models.Model):
room_type_id=False, # Allows to choose any available room
current_lines=reservation.reservation_line_ids.ids,
pricelist_id=reservation.pricelist_id.id,
class_id=reservation.room_type_id.class_id.id
if reservation.room_type_id
else False,
real_avail=True,
)
reservation.allowed_room_ids = pms_property.free_room_ids
else:
reservation.allowed_room_ids = False
@@ -1045,6 +1053,7 @@ class PmsReservation(models.Model):
True
if (
record.reservation_type != "out"
and record.overnight_room
and record.state in ["draft", "confirm", "arrival_delayed"]
and record.checkin <= fields.Date.today()
)
@@ -1197,9 +1206,11 @@ class PmsReservation(models.Model):
reservation.commission_amount = 0
# REVIEW: Dont run with set room_type_id -> room_id(compute)-> No set adults¿?
@api.depends("preferred_room_id", "reservation_type")
@api.depends("preferred_room_id", "reservation_type", "overnight_room")
def _compute_adults(self):
for reservation in self:
if not reservation.overnight_room:
reservation.adults = 0
if reservation.preferred_room_id and reservation.reservation_type != "out":
if reservation.adults == 0:
reservation.adults = reservation.preferred_room_id.capacity
@@ -1384,7 +1395,7 @@ class PmsReservation(models.Model):
def _compute_checkin_partner_count(self):
for record in self:
if record.reservation_type != "out":
if record.reservation_type != "out" and record.overnight_room:
record.checkin_partner_count = len(record.checkin_partner_ids)
record.checkin_partner_pending_count = record.adults - len(
record.checkin_partner_ids
@@ -1488,6 +1499,7 @@ class PmsReservation(models.Model):
return [
("state", "in", ("draft", "confirm", "arrival_delayed")),
("checkin", "<=", today),
("adults", ">", 0),
]
def _search_allowed_checkout(self, operator, value):
@@ -1505,6 +1517,7 @@ class PmsReservation(models.Model):
return [
("state", "in", ("onboard", "departure_delayed")),
("checkout", ">=", today),
("adults", ">", 0),
]
def _search_allowed_cancel(self, operator, value):
@@ -2014,7 +2027,9 @@ class PmsReservation(models.Model):
("checkin", "<", fields.Date.today()),
]
)
arrival_delayed_reservations.state = "arrival_delayed"
for record in arrival_delayed_reservations:
if record.overnight_room:
record.state = "arrival_delayed"
@api.model
def auto_departure_delayed(self):
@@ -2026,8 +2041,11 @@ class PmsReservation(models.Model):
]
)
for reservation in reservations:
if reservation.checkout_datetime <= fields.Datetime.now():
reservations.state = "departure_delayed"
if reservation.overnight_room:
if reservation.checkout_datetime <= fields.Datetime.now():
reservations.state = "departure_delayed"
else:
reservation.state = "done"
def preview_reservation(self):
self.ensure_one()

View File

@@ -112,7 +112,10 @@ class PmsReservationLine(models.Model):
store=True,
compute="_compute_impacts_quota",
)
overnight_room = fields.Boolean(
related="reservation_id.overnight_room",
store=True,
)
_sql_constraints = [
(
"rule_availability",
@@ -175,6 +178,8 @@ class PmsReservationLine(models.Model):
free_room_select = True if reservation.preferred_room_id else False
# we get the rooms available for the entire stay
# (real_avail if True if the reservation was created with
# specific room selected)
pms_property = line.pms_property_id
pms_property = pms_property.with_context(
checkin=reservation.checkin,
@@ -184,6 +189,7 @@ class PmsReservationLine(models.Model):
else False,
current_lines=reservation.reservation_line_ids.ids,
pricelist_id=reservation.pricelist_id.id,
real_avail=free_room_select,
)
rooms_available = pms_property.free_room_ids

View File

@@ -86,6 +86,10 @@ class PmsRoomType(models.Model):
"Use `-1` for managing no quota.",
default=-1,
)
overnight_room = fields.Boolean(
related="class_id.overnight",
store=True,
)
def name_get(self):
result = []

View File

@@ -57,6 +57,11 @@ class PmsRoomTypeClass(models.Model):
help="Room type class identification code",
required=True,
)
overnight = fields.Boolean(
string="Use for overnight stays",
help="Set False if if these types of spaces are not used for overnight stays",
default=True,
)
@api.model
def get_unique_by_property_code(self, pms_property_id, default_code=None):

View File

@@ -299,6 +299,12 @@ class PmsService(models.Model):
day_qty = 1
if service.reservation_id and service.product_id:
reservation = service.reservation_id
# REVIEW: review method dependencies, reservation_line_ids
# instead of checkin/checkout
if not reservation.checkin or not reservation.checkout:
if not service.service_line_ids:
service.service_line_ids = False
continue
product = service.product_id
consumed_on = product.consumed_on
if product.per_day:

View File

@@ -29,6 +29,15 @@ class TestPmsReservations(TestPms):
}
)
self.room_type_triple = self.env["pms.room.type"].create(
{
"pms_property_ids": [self.pms_property1.id],
"name": "Triple Test",
"default_code": "TRP_Test",
"class_id": self.room_type_class1.id,
}
)
# create rooms
self.room1 = self.env["pms.room"].create(
{
@@ -59,6 +68,16 @@ class TestPmsReservations(TestPms):
"extra_beds_allowed": 1,
}
)
self.room4 = self.env["pms.room"].create(
{
"pms_property_id": self.pms_property1.id,
"name": "Triple 104",
"room_type_id": self.room_type_triple.id,
"capacity": 3,
"extra_beds_allowed": 1,
}
)
self.partner1 = self.env["res.partner"].create(
{
"firstname": "Jaime",
@@ -1645,6 +1664,7 @@ class TestPmsReservations(TestPms):
"checkin": fields.date.today(),
"checkout": fields.date.today() + datetime.timedelta(days=3),
"partner_id": self.partner1.id,
"room_type_id": self.room_type_double.id,
}
)
@@ -1678,6 +1698,7 @@ class TestPmsReservations(TestPms):
"checkin": fields.date.today() + datetime.timedelta(days=300),
"checkout": fields.date.today() + datetime.timedelta(days=305),
"partner_id": self.partner1.id,
"room_type_id": self.room_type_double.id,
}
)
r = reservation.checkin
@@ -1695,8 +1716,8 @@ class TestPmsReservations(TestPms):
Check available rooms after creating a reservation.
-----------
Create an availability rule, create a reservation,
and then check that the allopwed_room_ids field of the
reservation and the room_type_id.room_ids field of the
and then check that the allowed_room_ids field filtered by room
type of the reservation and the room_type_id.room_ids field of the
availability rule match.
"""
availability_rule = self.env["pms.availability.plan.rule"].create(
@@ -1718,8 +1739,10 @@ class TestPmsReservations(TestPms):
}
)
self.assertEqual(
reservation.allowed_room_ids,
availability_rule.room_type_id.room_ids,
reservation.allowed_room_ids.filtered(
lambda r: r.room_type_id.id == availability_rule.room_type_id.id
).ids,
availability_rule.room_type_id.room_ids.ids,
"Rooms allowed don't match",
)
@@ -1750,6 +1773,7 @@ class TestPmsReservations(TestPms):
"checkin": fields.date.today() + datetime.timedelta(days=150),
"checkout": fields.date.today() + datetime.timedelta(days=152),
"agency_id": agency.id,
"room_type_id": self.room_type_double.id,
}
)
@@ -1794,6 +1818,7 @@ class TestPmsReservations(TestPms):
"checkin": fields.date.today() + datetime.timedelta(days=150),
"checkout": fields.date.today() + datetime.timedelta(days=152),
"agency_id": agency.id,
"room_type_id": self.room_type_double.id,
}
)
self.assertEqual(
@@ -1815,6 +1840,7 @@ class TestPmsReservations(TestPms):
"checkin": fields.date.today() + datetime.timedelta(days=150),
"checkout": fields.date.today() + datetime.timedelta(days=152),
"partner_id": self.partner1.id,
"room_type_id": self.room_type_double.id,
}
)
@@ -1873,6 +1899,7 @@ class TestPmsReservations(TestPms):
"allowed_checkin": True,
"pms_property_id": self.pms_property1.id,
"adults": 3,
"room_type_id": self.room_type_triple.id,
}
)
self.checkin1 = self.env["pms.checkin.partner"].create(
@@ -1916,6 +1943,7 @@ class TestPmsReservations(TestPms):
"checkout": fields.date.today(),
"pms_property_id": self.pms_property1.id,
"partner_id": self.host1.id,
"room_type_id": self.room_type_double.id,
}
)
@@ -2294,6 +2322,7 @@ class TestPmsReservations(TestPms):
"partner_id": self.host1.id,
"pms_property_id": self.pms_property1.id,
"adults": 3,
"room_type_id": self.room_type_triple.id,
}
)
self.checkin1 = self.env["pms.checkin.partner"].create(
@@ -2351,6 +2380,7 @@ class TestPmsReservations(TestPms):
"checkout": "2014-01-17",
"partner_id": self.host1.id,
"pms_property_id": self.pms_property1.id,
"room_type_id": self.room_type_triple.id,
"adults": 3,
}
)
@@ -2404,6 +2434,7 @@ class TestPmsReservations(TestPms):
"partner_id": host.id,
"allowed_checkout": True,
"pms_property_id": self.pms_property1.id,
"room_type_id": self.room_type_double.id,
}
)
@@ -2435,6 +2466,7 @@ class TestPmsReservations(TestPms):
"checkout": "2014-01-17",
"pms_property_id": self.pms_property1.id,
"folio_id": self.folio1.id,
"room_type_id": self.room_type_double.id,
}
)
# ACT AND ASSERT
@@ -2472,6 +2504,7 @@ class TestPmsReservations(TestPms):
"checkin": fields.date.today() + datetime.timedelta(days=150),
"checkout": fields.date.today() + datetime.timedelta(days=152),
"agency_id": agency.id,
"room_type_id": self.room_type_double.id,
}
)

View File

@@ -73,6 +73,116 @@
</tbody>
</t>
</t>
</template>
<template id="portal_folio_payment" name="Folio Payment">
<div
class="row"
t-if="not tx_ids and folio.state in ('confirm','done') and folio.payment_state in ('not_paid', 'partial') and folio.amount_total"
id="portal_pay"
>
<div class="modal fade" id="pay_with" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Pay with</h3>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>×</button>
</div>
<div class="modal-body">
<div
t-if="pms or acquirers"
id="payment_method"
class="text-left col-md-13"
>
<t t-call="payment.payment_tokens_list">
<t t-set="mode" t-value="'payment'" />
<t
t-set="partner_id"
t-value="folio.partner_id.id if request.env.user._is_public() else request.env.user.partner_id.id"
/>
<t
t-set="success_url"
t-value="folio.get_portal_url()"
/>
<t
t-set="error_url"
t-value="folio.get_portal_url()"
/>
<t
t-set="access_token"
t-value="access_token or ''"
/>
<t t-set="callback_method" t-value="''" />
<t
t-set="form_action"
t-value="'/folio/pay/' + str(folio.id) + '/s2s_token_tx/'"
/>
<t
t-set="prepare_tx_url"
t-value="'/folio/pay/' + str(folio.id) + '/form_tx/'"
/>
<t t-set="submit_txt">Pay Now</t>
<t t-set="icon_class" t-value="'fa-lock'" />
</t>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<template id="portal_folio_page_payment" name="Payment on My Folios">
<t
t-set="tx_ids"
t-value="folio.transaction_ids.filtered(lambda tx: tx.state in ('pending', 'authorized', 'done'))"
/>
<t
t-set="pending_manual_txs"
t-value="tx_ids.filtered(lambda tx: tx.state == 'pending' and tx.acquirer_id.provider in ('transfer', 'manual'))"
/>
<div>
<a
href="#"
t-if="folio.state in ('confirm','done') and folio.payment_state in ('not_paid', 'partial') and folio.amount_total and (pending_manual_txs or not tx_ids)"
class="btn btn-primary btn-block mb-2"
data-toggle="modal"
data-target="#pay_with"
>
<i class="fa fa-fw fa-arrow-circle-right" /> Pay Now
</a>
<div
t-if="tx_ids and not pending_manual_txs and folio.payment_state not in ('paid', 'in_payment')"
class="alert alert-info py-1 mb-2"
>
<i class="fa fa-fw fa-check-circle" /> Pending
</div>
<div
t-if="folio.payment_state in ('paid', 'in_payment')"
class="alert alert-success py-1 mb-2"
>
<i class="fa fa-fw fa-check-circle" /> Paid
</div>
</div>
<t
t-set="tx_ids"
t-value="folio.transaction_ids.filtered(lambda tx: tx.state in ('authorized', 'done'))"
/>
<div
t-if="not tx_ids and folio.state in('confirm','done') and folio.payment_state in ('not_paid', 'partial') and folio.amount_total"
id="portal_pay"
>
<div t-if="pms or acquirers" id="payment_method">
<t t-call="pms.portal_folio_payment" />
</div>
</div>
</template>
<template
id="folio_portal_template"
@@ -94,7 +204,10 @@
<ul
class="list-group list-group-flush flex-wrap flex-row flex-lg-column"
>
<li class="list-group-item">
<li class="list-group-item flex-grow-1">
<div>
<div t-call="pms.portal_folio_page_payment" />
</div>
<div class="o_download_pdf btn-toolbar flex-sm-nowrap">
<div class="btn-group mr-1 mb-1">
<a

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="payment_transaction_inherit_view_form" model="ir.ui.view">
<field name="model">payment.transaction</field>
<field name="inherit_id" ref="payment.transaction_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='date']" position="before">
<field name="folio_ids" />
</xpath>
</field>
</record>
</odoo>

View File

@@ -91,7 +91,11 @@
<field name="property_modified_template" />
<field name="property_canceled_template" />
</group>
<group string="Mail Information" colspan="4">
<group
string="Mail Information"
name="mail_information"
colspan="4"
>
<field
name="mail_information"
string="Aditional Mail Information"

View File

@@ -29,6 +29,7 @@
/>
<field name="name" />
<field name="default_code" />
<field name="overnight" />
</group>
</group>
<group colspan="2">

View File

@@ -39,6 +39,8 @@
<group name="room_ids_group">
<field name="room_ids" widget="many2many_tags" />
<field name="total_rooms_count" />
<field name="default_max_avail" />
<field name="default_quota" />
</group>
<group name="accounting_group">
<field

View File

@@ -1,6 +1,6 @@
import datetime
from odoo import api, fields, models
from odoo import _, api, fields, models
from odoo.osv import expression
@@ -872,145 +872,161 @@ class AvailabilityWizard(models.TransientModel):
return new_items
def continue_massive_changes(self):
self.apply_massive_changes()
return {
"name": _("Massive changes on Pricelist and Availability Plans"),
"res_model": "pms.massive.changes.wizard",
"type": "ir.actions.act_window",
"view_id": self.env.ref("pms.massive_changes_wizard").id,
"target": "new",
"view_mode": "form",
"context": {
"default_massive_changes_on": self.massive_changes_on,
"default_start_date": self.start_date,
"default_end_date": self.end_date,
"default_pms_property_ids": [(6, 0, self.pms_property_ids.ids)],
"default_pricelist_ids": [(6, 0, self.pricelist_ids.ids)],
"default_availability_plan_ids": [
(6, 0, self.availability_plan_ids.ids)
],
},
}
def save_and_close(self):
items = self.apply_massive_changes()
if self.massive_changes_on == "pricelist" and not self.pricelist_readonly:
action = {
"view": self.env.ref("pms.product_pricelist_item_action2").read()[0]
}
action["view"]["domain"] = [("id", "in", items)]
return action["view"]
if self.massive_changes_on == "availability_plan" and not self.avail_readonly:
action = {
"view": self.env.ref(
"pms.availability_plan_rule_view_tree_action"
).read()[0]
}
action["view"]["domain"] = [("id", "in", items)]
return action["view"]
def apply_massive_changes(self):
self.ensure_one()
self.pricelist_items_to_overwrite.unlink()
week_days_to_apply = (
self.apply_on_monday,
self.apply_on_tuesday,
self.apply_on_wednesday,
self.apply_on_thursday,
self.apply_on_friday,
self.apply_on_saturday,
self.apply_on_sunday,
)
for record in self:
record.pricelist_items_to_overwrite.unlink()
week_days_to_apply = (
record.apply_on_monday,
record.apply_on_tuesday,
record.apply_on_wednesday,
record.apply_on_thursday,
record.apply_on_friday,
record.apply_on_saturday,
record.apply_on_sunday,
)
# dates between start and end (both included)
items = []
for date in [
self.start_date + datetime.timedelta(days=x)
for x in range(0, (self.end_date - self.start_date).days + 1)
]:
# dates between start and end (both included)
items = []
for date in [
record.start_date + datetime.timedelta(days=x)
for x in range(0, (record.end_date - record.start_date).days + 1)
]:
if (
not self.apply_on_all_week
and not week_days_to_apply[date.timetuple()[6]]
):
continue
if not self.room_type_ids:
room_types = self.env["pms.room.type"].search(
[
"|",
("pms_property_ids", "=", False),
("pms_property_ids", "in", self.pms_property_ids.ids),
]
)
else:
room_types = self.room_type_ids
for pms_property in self.pms_property_ids:
if (
not record.apply_on_all_week
and not week_days_to_apply[date.timetuple()[6]]
self.massive_changes_on == "pricelist"
and self.apply_pricelists_on == "room_types"
):
continue
if not record.room_type_ids:
room_types = self.env["pms.room.type"].search(
[
"|",
("pms_property_ids", "=", False),
("pms_property_ids", "in", record.pms_property_ids.ids),
]
new_items = self.create_pricelists_items_room_types(
room_types,
self.pricelist_ids,
self.price,
self.min_quantity,
pms_property,
date,
self.date_types,
)
else:
room_types = record.room_type_ids
items = items + new_items if new_items else items
for pms_property in record.pms_property_ids:
if (
record.massive_changes_on == "pricelist"
and record.apply_pricelists_on == "room_types"
):
new_items = self.create_pricelists_items_room_types(
room_types,
record.pricelist_ids,
record.price,
record.min_quantity,
pms_property,
date,
record.date_types,
)
items = items + new_items if new_items else items
elif (
self.massive_changes_on == "pricelist"
and self.apply_pricelists_on == "board_services"
):
new_items = self.create_pricelists_items_board_services(
self.board_service_room_type_ids,
self.pricelist_ids,
self.board_service,
self.price,
self.min_quantity,
pms_property,
self.date_types,
date,
)
items = items + new_items if new_items else items
elif (
record.massive_changes_on == "pricelist"
and record.apply_pricelists_on == "board_services"
):
new_items = self.create_pricelists_items_board_services(
record.board_service_room_type_ids,
record.pricelist_ids,
record.board_service,
record.price,
record.min_quantity,
pms_property,
record.date_types,
date,
)
items = items + new_items if new_items else items
elif (
self.massive_changes_on == "pricelist"
and self.apply_pricelists_on == "service"
):
for pricelist in self.pricelist_ids:
if self.service:
vals = {
"pricelist_id": pricelist.id,
"compute_price": "fixed",
"applied_on": "0_product_variant",
"product_id": self.service.id,
"fixed_price": self.price,
"min_quantity": self.min_quantity,
"pms_property_ids": [pms_property.id],
}
vals = self.generate_dates_vals(self.date_types, vals, date)
elif (
record.massive_changes_on == "pricelist"
and record.apply_pricelists_on == "service"
):
for pricelist in record.pricelist_ids:
if record.service:
vals = {
"pricelist_id": pricelist.id,
"compute_price": "fixed",
"applied_on": "0_product_variant",
"product_id": record.service.id,
"fixed_price": record.price,
"min_quantity": record.min_quantity,
"pms_property_ids": [pms_property.id],
}
vals = self.generate_dates_vals(
record.date_types, vals, date
)
pricelist_item = self.env["product.pricelist.item"].create(
vals
)
items.append(pricelist_item.id)
elif self.massive_changes_on == "availability_plan":
pricelist_item = self.env[
"product.pricelist.item"
].create(vals)
items.append(pricelist_item.id)
elif record.massive_changes_on == "availability_plan":
new_items = self.create_availability_plans_rules(
room_types,
record.availability_plan_ids,
record.min_stay,
record.apply_min_stay,
record.min_stay_arrival,
record.apply_min_stay_arrival,
record.max_stay,
record.apply_max_stay,
record.max_stay_arrival,
record.apply_max_stay_arrival,
record.quota,
record.apply_quota,
record.max_avail,
record.apply_max_avail,
record.closed,
record.apply_closed,
record.closed_arrival,
record.apply_closed_arrival,
record.closed_departure,
record.apply_closed_departure,
date,
record.rules_to_overwrite,
pms_property,
)
items = items + new_items if new_items else items
if (
record.massive_changes_on == "pricelist"
and not record.pricelist_readonly
):
action = {
"view": self.env.ref("pms.product_pricelist_item_action2").read()[0]
}
action["view"]["domain"] = [("id", "in", items)]
return action["view"]
if (
record.massive_changes_on == "availability_plan"
and not record.avail_readonly
):
action = {
"view": self.env.ref(
"pms.availability_plan_rule_view_tree_action"
).read()[0]
}
action["view"]["domain"] = [("id", "in", items)]
return action["view"]
new_items = self.create_availability_plans_rules(
room_types,
self.availability_plan_ids,
self.min_stay,
self.apply_min_stay,
self.min_stay_arrival,
self.apply_min_stay_arrival,
self.max_stay,
self.apply_max_stay,
self.max_stay_arrival,
self.apply_max_stay_arrival,
self.quota,
self.apply_quota,
self.max_avail,
self.apply_max_avail,
self.closed,
self.apply_closed,
self.closed_arrival,
self.apply_closed_arrival,
self.closed_departure,
self.apply_closed_departure,
date,
self.rules_to_overwrite,
pms_property,
)
items = items + new_items if new_items else items
return items

View File

@@ -474,8 +474,14 @@
</field>
<footer>
<button
name="apply_massive_changes"
string="Apply changes"
name="save_and_close"
string="Apply and close"
type="object"
class="btn-primary"
/>
<button
name="continue_massive_changes"
string="Apply and continue"
type="object"
class="btn-primary"
/>

View File

@@ -179,6 +179,7 @@ class ReservationSplitJoinSwapWizard(models.TransientModel):
room_type_id=False, # Allows to choose any available room
current_lines=record.reservation_id.reservation_line_ids.ids,
pricelist_id=record.reservation_id.pricelist_id.id,
real_avail=True,
)
rooms_available = pms_property.free_room_ids
@@ -224,6 +225,8 @@ class ReservationSplitJoinSwapWizard(models.TransientModel):
).date(),
current_lines=reservation.reservation_line_ids.ids,
pricelist_id=reservation.pricelist_id.id,
real_avail=True,
class_id=reservation.room_type_id.class_id.id,
)
rooms_available = pms_property.free_room_ids
@@ -242,6 +245,7 @@ class ReservationSplitJoinSwapWizard(models.TransientModel):
checkout=reservation.checkout,
current_lines=reservation.reservation_line_ids.ids,
pricelist_id=reservation.pricelist_id.id,
real_avail=True,
)
rooms_available = pms_property.free_room_ids
@@ -360,6 +364,8 @@ class ReservationLinesToSplit(models.TransientModel):
checkout=line.date + datetime.timedelta(days=1),
room_type_id=False, # Allows to choose any available room
pricelist_id=reservation.pricelist_id.id,
real_avail=True,
class_id=reservation.room_type_id.class_id.id,
)
rooms_available = pms_property.free_room_ids
rooms_available += line.room_id

View File

@@ -3,7 +3,7 @@
{
"name": "PMS Spanish Adaptation",
"version": "14.0.2.1.1",
"version": "14.0.2.2.0",
"author": "Commit [Sun], Odoo Community Association (OCA)",
"license": "AGPL-3",
"application": True,

View File

@@ -136,6 +136,7 @@ msgid "Detailed message"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_checkin_partner__display_name
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_ine_tourism_type_category__display_name
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_ine_wizard__display_name
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__display_name
@@ -218,6 +219,7 @@ msgid "Hotel category in the Ministry of Tourism. Used for INE statistics."
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_checkin_partner__id
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_ine_tourism_type_category__id
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_ine_wizard__id
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__id
@@ -288,6 +290,7 @@ msgid "Institution user"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_checkin_partner____last_update
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_ine_tourism_type_category____last_update
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_ine_wizard____last_update
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report____last_update
@@ -345,6 +348,11 @@ msgstr ""
msgid "Mossos_d'esquadra (soon)"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model,name:pms_l10n_es.model_pms_checkin_partner
msgid "Partner Checkins"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,help:pms_l10n_es.field_pms_property__institution_password
msgid "Password provided by institution to send the data."

View File

@@ -6,3 +6,4 @@ from . import res_country_state
from . import pms_ine_tourism_type_category
from . import pms_room
from . import res_partner
from . import pms_checkin_partner

View File

@@ -0,0 +1,30 @@
import logging
from odoo import api, models
CODE_SPAIN = "ES"
_logger = logging.getLogger(__name__)
class PmsCheckinParnert(models.Model):
_inherit = "pms.checkin.partner"
@api.model
def _checkin_mandatory_fields(self, country=False, depends=False):
mandatory_fields = super(PmsCheckinParnert, self)._checkin_mandatory_fields(
depends
)
mandatory_fields.extend(
[
"birthdate_date",
"gender",
"document_number",
"document_type",
"document_expedition_date",
"nationality_id",
]
)
if depends or (country and country.code == CODE_SPAIN):
mandatory_fields.append("state_id")
return mandatory_fields

View File

@@ -101,11 +101,11 @@ class TravellerReport(models.TransientModel):
for line in lines:
content += "2"
# [P|N|..]
if line.document_type.name != "D":
if line.document_type.code != "D":
content += "||" + line.document_number.upper() + "|"
else:
content += "|" + line.document_number.upper() + "||"
content += line.document_type.name + "|"
content += line.document_type.code + "|"
content += line.document_expedition_date.strftime("%Y%m%d") + "|"
content += line.lastname.upper() + "|"
if line.lastname2: