From b5c170eddce1aeb5f9b666b3e5db3484ceb74a35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo=20Lodeiros?= Date: Sat, 1 Aug 2020 22:31:16 +0200 Subject: [PATCH 1/3] [ADD] Precommit repository configuration --- .editorconfig | 20 + .eslintrc.yml | 180 ++++++ .flake8 | 10 + .isort.cfg | 13 + .pre-commit-config.yaml | 102 ++++ .prettierrc.yml | 8 + .pylintrc | 87 +++ .pylintrc-mandatory | 65 ++ LICENSE | 661 +++++++++++++++++++++ setup/.setuptools-odoo-make-default-ignore | 2 + setup/README | 2 + 11 files changed, 1150 insertions(+) create mode 100644 .editorconfig create mode 100644 .eslintrc.yml create mode 100644 .flake8 create mode 100644 .isort.cfg create mode 100644 .pre-commit-config.yaml create mode 100644 .prettierrc.yml create mode 100644 .pylintrc create mode 100644 .pylintrc-mandatory create mode 100644 LICENSE create mode 100644 setup/.setuptools-odoo-make-default-ignore create mode 100644 setup/README diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..bfd7ac53d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +# Configuration for known file extensions +[*.{css,js,json,less,md,py,rst,sass,scss,xml,yaml,yml}] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{json,yml,yaml,rst,md}] +indent_size = 2 + +# Do not configure editor for libs and autogenerated content +[{*/static/{lib,src/lib}/**,*/static/description/index.html,*/readme/../README.rst}] +charset = unset +end_of_line = unset +indent_size = unset +indent_style = unset +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 000000000..88f2881b4 --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,180 @@ +env: + browser: true + +# See https://github.com/OCA/odoo-community.org/issues/37#issuecomment-470686449 +parserOptions: + ecmaVersion: 2017 + +# Globals available in Odoo that shouldn't produce errorings +globals: + _: readonly + $: readonly + fuzzy: readonly + jQuery: readonly + moment: readonly + odoo: readonly + openerp: readonly + Promise: 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 +rules: + accessor-pairs: warn + array-callback-return: warn + callback-return: warn + capitalized-comments: + - warn + - always + - ignoreConsecutiveComments: true + ignoreInlineComments: true + complexity: + - warn + - 15 + constructor-super: warn + dot-notation: warn + eqeqeq: warn + global-require: warn + handle-callback-err: warn + id-blacklist: warn + id-match: warn + init-declarations: error + max-depth: warn + max-nested-callbacks: warn + max-statements-per-line: warn + no-alert: warn + no-array-constructor: warn + no-caller: warn + no-case-declarations: warn + no-class-assign: warn + no-cond-assign: error + no-const-assign: error + no-constant-condition: warn + no-control-regex: warn + no-debugger: error + no-delete-var: warn + no-div-regex: warn + no-dupe-args: error + no-dupe-class-members: error + no-dupe-keys: error + no-duplicate-case: error + no-duplicate-imports: error + no-else-return: warn + no-empty-character-class: warn + no-empty-function: error + no-empty-pattern: error + no-empty: warn + no-eq-null: error + no-eval: error + no-ex-assign: error + no-extend-native: warn + no-extra-bind: warn + no-extra-boolean-cast: warn + no-extra-label: warn + no-fallthrough: warn + no-func-assign: error + no-global-assign: error + no-implicit-coercion: + - warn + - allow: ["~"] + no-implicit-globals: warn + no-implied-eval: warn + no-inline-comments: warn + no-inner-declarations: warn + no-invalid-regexp: warn + no-irregular-whitespace: warn + no-iterator: warn + no-label-var: warn + no-labels: warn + no-lone-blocks: warn + no-lonely-if: error + no-mixed-requires: error + no-multi-str: warn + no-native-reassign: error + no-negated-condition: warn + no-negated-in-lhs: error + no-new-func: warn + no-new-object: warn + no-new-require: warn + no-new-symbol: warn + no-new-wrappers: warn + no-new: warn + no-obj-calls: warn + no-octal-escape: warn + no-octal: warn + no-param-reassign: warn + no-path-concat: warn + no-process-env: warn + no-process-exit: warn + no-proto: warn + no-prototype-builtins: warn + no-redeclare: warn + no-regex-spaces: warn + no-restricted-globals: warn + no-restricted-imports: warn + no-restricted-modules: warn + no-restricted-syntax: warn + no-return-assign: error + no-script-url: warn + no-self-assign: warn + no-self-compare: warn + no-sequences: warn + no-shadow-restricted-names: warn + no-shadow: warn + no-sparse-arrays: warn + no-sync: warn + no-this-before-super: warn + no-throw-literal: warn + no-undef-init: warn + no-undef: error + no-unmodified-loop-condition: warn + no-unneeded-ternary: error + no-unreachable: error + no-unsafe-finally: error + no-unused-expressions: error + no-unused-labels: error + no-unused-vars: error + no-use-before-define: error + no-useless-call: warn + no-useless-computed-key: warn + no-useless-concat: warn + no-useless-constructor: warn + no-useless-escape: warn + no-useless-rename: warn + no-void: warn + no-with: warn + operator-assignment: [error, always] + prefer-const: warn + radix: warn + require-yield: warn + sort-imports: warn + spaced-comment: [error, always] + strict: [error, function] + use-isnan: error + valid-jsdoc: + - warn + - prefer: + arg: param + argument: param + augments: extends + constructor: class + exception: throws + func: function + method: function + prop: property + return: returns + virtual: abstract + yield: yields + preferType: + array: Array + bool: Boolean + boolean: Boolean + number: Number + object: Object + str: String + string: String + requireParamDescription: false + requireReturn: false + requireReturnDescription: false + requireReturnType: false + valid-typeof: warn + yoda: warn diff --git a/.flake8 b/.flake8 new file mode 100644 index 000000000..44ed868f7 --- /dev/null +++ b/.flake8 @@ -0,0 +1,10 @@ +[flake8] +max-line-length = 80 +max-complexity = 16 +# B = bugbear +# B9 = bugbear opinionated (incl line length) +select = C,E,F,W,B,B9 +# E203: whitespace before ':' (black behaviour) +# E501: flake8 line length (covered by bugbear B950) +# W503: line break before binary operator (black behaviour) +ignore = E203,E501,W503 diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 000000000..d2e2e305f --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,13 @@ +[settings] +; see https://github.com/psf/black +multi_line_output=3 +include_trailing_comma=True +force_grid_wrap=0 +combine_as_imports=True +use_parentheses=True +line_length=88 +known_odoo=odoo +known_odoo_addons=odoo.addons +sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER +default_section=THIRDPARTY +known_third_party=openerp,werkzeug diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..749967bb6 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,102 @@ +exclude: | + (?x) + # Files and folders generated by bots, to avoid loops + ^setup/|/static/description/index\.html$| + # Maybe reactivate this when all README files include prettier ignore tags? + ^README\.md$| + # Library files can have extraneous formatting (even minimized) + /static/(src/)?lib/| + # Repos using Sphinx to generate docs don't need prettying + ^docs/_templates/.*\.html$| + # You don't usually want a bot to modify your legal texts + (LICENSE.*|COPYING.*) +default_language_version: + python: python3 +repos: + - repo: https://github.com/psf/black + rev: 19.10b0 + hooks: + - id: black + - repo: https://github.com/prettier/prettier + rev: "1.19.1" + hooks: + - id: prettier + # TODO Avoid awebdeveloper/pre-commit-prettier if possible + # HACK https://github.com/prettier/prettier/issues/7407 + - repo: https://github.com/awebdeveloper/pre-commit-prettier + rev: v0.0.1 + hooks: + - id: prettier + name: prettier xml plugin + additional_dependencies: + - "prettier@1.19.1" + - "@prettier/plugin-xml@0.7.2" + files: \.xml$ + - repo: https://github.com/pre-commit/mirrors-eslint + rev: v6.8.0 + hooks: + - id: eslint + verbose: true + args: + - --color + - --fix + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.4.0 + hooks: + - id: trailing-whitespace + # exclude autogenerated files + exclude: /README\.rst$|\.pot?$ + - id: end-of-file-fixer + # exclude autogenerated files + exclude: /README\.rst$|\.pot?$ + - id: debug-statements + - id: fix-encoding-pragma + args: ["--remove"] + - id: check-case-conflict + - id: check-docstring-first + - id: check-executables-have-shebangs + - id: check-merge-conflict + # exclude files where underlines are not distinguishable from merge conflicts + exclude: /README\.rst$|^docs/.*\.rst$ + - id: check-symlinks + - id: check-xml + - id: mixed-line-ending + args: ["--fix=lf"] + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.7.9 + hooks: + - id: flake8 + name: flake8 except __init__.py + exclude: /__init__\.py$ + additional_dependencies: ["flake8-bugbear==19.8.0"] + - id: flake8 + name: flake8 only __init__.py + args: ["--extend-ignore=F401"] # ignore unused imports in __init__.py + files: /__init__\.py$ + additional_dependencies: ["flake8-bugbear==19.8.0"] + - repo: https://github.com/pre-commit/mirrors-pylint + rev: v2.3.1 + hooks: + - id: pylint + name: pylint with optional checks + args: ["--rcfile=.pylintrc", "--exit-zero"] + verbose: true + additional_dependencies: ["pylint-odoo==3.1.0"] + - id: pylint + name: pylint with mandatory checks + args: ["--rcfile=.pylintrc-mandatory"] + additional_dependencies: ["pylint-odoo==3.1.0"] + - repo: https://github.com/asottile/pyupgrade + rev: v1.26.2 + hooks: + - id: pyupgrade + - repo: https://github.com/pre-commit/mirrors-isort + rev: v4.3.21 + hooks: + - id: isort + name: isort except __init__.py + exclude: /__init__\.py$ + - repo: https://github.com/acsone/setuptools-odoo + rev: 2.5.2 + hooks: + - id: setuptools-odoo-make-default diff --git a/.prettierrc.yml b/.prettierrc.yml new file mode 100644 index 000000000..fc5f3a7c4 --- /dev/null +++ b/.prettierrc.yml @@ -0,0 +1,8 @@ +# Defaults for all prettier-supported languages. +# Prettier will complete this with settings from .editorconfig file. +bracketSpacing: false +printWidth: 88 +proseWrap: always +semi: true +trailingComma: "es5" +xmlWhitespaceSensitivity: "ignore" diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 000000000..f9ddbaa3f --- /dev/null +++ b/.pylintrc @@ -0,0 +1,87 @@ +[MASTER] +load-plugins=pylint_odoo +score=n + +[ODOOLINT] +readme_template_url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst" +manifest_required_authors=Odoo Community Association (OCA) +manifest_required_keys=license +manifest_deprecated_keys=description,active +license_allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3 +valid_odoo_versions=13.0 + +[MESSAGES CONTROL] +disable=all + +# This .pylintrc contains optional AND mandatory checks and is meant to be +# loaded in an IDE to have it check everything, in the hope this will make +# optional checks more visible to contributors who otherwise never look at a +# green travis to see optional checks that failed. +# .pylintrc-mandatory containing only mandatory checks is used the pre-commit +# config as a blocking check. + +enable=anomalous-backslash-in-string, + api-one-deprecated, + api-one-multi-together, + assignment-from-none, + attribute-deprecated, + class-camelcase, + dangerous-default-value, + dangerous-view-replace-wo-priority, + duplicate-id-csv, + duplicate-key, + duplicate-xml-fields, + duplicate-xml-record-id, + eval-referenced, + eval-used, + incoherent-interpreter-exec-perm, + license-allowed, + manifest-author-string, + manifest-deprecated-key, + manifest-required-author, + manifest-required-key, + manifest-version-format, + method-compute, + method-inverse, + method-required-super, + method-search, + missing-import-error, + missing-manifest-dependency, + openerp-exception-warning, + pointless-statement, + pointless-string-statement, + print-used, + redundant-keyword-arg, + redundant-modulename-xml, + reimported, + relative-import, + return-in-init, + rst-syntax-error, + sql-injection, + too-few-format-args, + translation-field, + translation-required, + unreachable, + use-vim-comment, + wrong-tabs-instead-of-spaces, + xml-syntax-error, + # messages that do not cause the lint step to fail + consider-merging-classes-inherited, + create-user-wo-reset-password, + dangerous-filter-wo-user, + deprecated-module, + file-not-used, + invalid-commit, + missing-newline-extrafiles, + missing-readme, + no-utf8-coding-comment, + odoo-addons-relative-import, + old-api7-method-defined, + redefined-builtin, + too-complex, + unnecessary-utf8-coding-comment + +[REPORTS] +msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} +output-format=colorized +reports=no diff --git a/.pylintrc-mandatory b/.pylintrc-mandatory new file mode 100644 index 000000000..7635cbb17 --- /dev/null +++ b/.pylintrc-mandatory @@ -0,0 +1,65 @@ +[MASTER] +load-plugins=pylint_odoo +score=n + +[ODOOLINT] +readme_template_url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst" +manifest_required_authors=Odoo Community Association (OCA) +manifest_required_keys=license +manifest_deprecated_keys=description,active +license_allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3 +valid_odoo_versions=13.0 + +[MESSAGES CONTROL] +disable=all + +enable=anomalous-backslash-in-string, + api-one-deprecated, + api-one-multi-together, + assignment-from-none, + attribute-deprecated, + class-camelcase, + dangerous-default-value, + dangerous-view-replace-wo-priority, + duplicate-id-csv, + duplicate-key, + duplicate-xml-fields, + duplicate-xml-record-id, + eval-referenced, + eval-used, + incoherent-interpreter-exec-perm, + license-allowed, + manifest-author-string, + manifest-deprecated-key, + manifest-required-author, + manifest-required-key, + manifest-version-format, + method-compute, + method-inverse, + method-required-super, + method-search, + missing-import-error, + missing-manifest-dependency, + openerp-exception-warning, + pointless-statement, + pointless-string-statement, + print-used, + redundant-keyword-arg, + redundant-modulename-xml, + reimported, + relative-import, + return-in-init, + rst-syntax-error, + sql-injection, + too-few-format-args, + translation-field, + translation-required, + unreachable, + use-vim-comment, + wrong-tabs-instead-of-spaces, + xml-syntax-error + +[REPORTS] +msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} +output-format=colorized +reports=no diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..58777e31a --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ +GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/setup/.setuptools-odoo-make-default-ignore b/setup/.setuptools-odoo-make-default-ignore new file mode 100644 index 000000000..207e61533 --- /dev/null +++ b/setup/.setuptools-odoo-make-default-ignore @@ -0,0 +1,2 @@ +# addons listed in this file are ignored by +# setuptools-odoo-make-default (one addon per line) diff --git a/setup/README b/setup/README new file mode 100644 index 000000000..a63d633e8 --- /dev/null +++ b/setup/README @@ -0,0 +1,2 @@ +To learn more about this directory, please visit +https://pypi.python.org/pypi/setuptools-odoo From aac22ef43fa4ec9eb40998735d6a6a0c9d06f0a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo=20Lodeiros?= Date: Sat, 1 Aug 2020 22:34:47 +0200 Subject: [PATCH 2/3] [RFC] Clean reposiroty --- call_center_report/README.rst | 6 - call_center_report/__init__.py | 21 - call_center_report/__manifest__.py | 48 - call_center_report/data/menus.xml | 11 - call_center_report/i18n/es.po | 171 - call_center_report/wizard/__init__.py | 21 - .../wizard/call_center_report.py | 348 -- .../wizard/call_center_report.xml | 41 - cash_daily_report/README.rst | 13 - cash_daily_report/__init__.py | 21 - cash_daily_report/__manifest__.py | 51 - cash_daily_report/data/cron_jobs.xml | 23 - cash_daily_report/data/menus.xml | 43 - cash_daily_report/i18n/es.po | 203 -- cash_daily_report/wizard/__init__.py | 21 - cash_daily_report/wizard/cash_daily_report.py | 307 -- .../wizard/cash_daily_report.xml | 41 - glasof_exporter/README.rst | 15 - glasof_exporter/__init__.py | 21 - glasof_exporter/__manifest__.py | 48 - glasof_exporter/data/menus.xml | 11 - glasof_exporter/wizard/__init__.py | 21 - glasof_exporter/wizard/glasof_wizard.py | 256 -- glasof_exporter/wizard/glasof_wizard.xml | 52 - hotel_calendar/README.rst | 61 - hotel_calendar/__init__.py | 4 - hotel_calendar/__manifest__.py | 42 - hotel_calendar/controllers/__init__.py | 3 - hotel_calendar/controllers/bus.py | 21 - hotel_calendar/data/menus.xml | 29 - hotel_calendar/i18n/es.po | 1383 -------- hotel_calendar/models/__init__.py | 14 - hotel_calendar/models/bus_hotel_calendar.py | 137 - hotel_calendar/models/hotel_calendar.py | 23 - .../models/hotel_calendar_management.py | 277 -- .../models/inherited_hotel_folio.py | 34 - .../models/inherited_hotel_property.py | 51 - .../models/inherited_hotel_reservation.py | 521 --- ...erited_hotel_room_type_restriction_item.py | 76 - .../inherited_product_pricelist_item.py | 122 - .../models/inherited_res_company.py | 29 - hotel_calendar/models/inherited_res_users.py | 34 - .../models/ir_actions_act_window_view.py | 10 - hotel_calendar/models/ir_ui_view.py | 10 - hotel_calendar/readme/CONFIGURE.rst | 8 - hotel_calendar/readme/CONTRIBUTORS.rst | 2 - hotel_calendar/readme/CREDITS.rst | 2 - hotel_calendar/readme/DESCRIPTION.rst | 10 - hotel_calendar/readme/INSTALL.rst | 5 - hotel_calendar/readme/ROADMAP.rst | 5 - hotel_calendar/readme/USAGE.rst | 14 - hotel_calendar/security/ir.model.access.csv | 4 - hotel_calendar/static/description/icon.png | Bin 4155 -> 0 bytes .../icon_calendar_configurator.png | Bin 5221 -> 0 bytes hotel_calendar/static/src/css/view.css | 396 --- .../calendar/hotel_calendar_controller.js | 1113 ------ .../js/views/calendar/hotel_calendar_model.js | 192 - .../views/calendar/hotel_calendar_renderer.js | 284 -- .../js/views/calendar/hotel_calendar_view.js | 179 - .../hotel_calendar_management_controller.js | 176 - .../hotel_calendar_management_model.js | 72 - .../hotel_calendar_management_renderer.js | 512 --- .../hotel_calendar_management_v_deprecated.js | 661 ---- .../hotel_calendar_management_view.js | 93 - .../static/src/js/views/constants.js | 20 - .../static/src/js/widgets/MultiCalendar.js | 398 --- hotel_calendar/static/src/lib/bootbox.js | 985 ------ .../src/lib/hcalendar/css/hcalendar.css | 537 --- .../hcalendar/css/hcalendar_management.css | 198 -- .../static/src/lib/hcalendar/js/hcalendar.js | 3126 ----------------- .../lib/hcalendar/js/hcalendar_management.js | 1147 ------ hotel_calendar/static/src/lib/moment.js | 551 --- .../xml/hotel_calendar_management_view.xml | 81 - .../src/xml/hotel_calendar_templates.xml | 378 -- .../static/src/xml/hotel_calendar_view.xml | 146 - hotel_calendar/tests/__init__.py | 2 - hotel_calendar/tests/common.py | 41 - .../tests/test_management_calendar.py | 424 --- .../tests/test_product_pricelist.py | 66 - .../tests/test_reservations_calendar.py | 289 -- hotel_calendar/views/actions.xml | 28 - hotel_calendar/views/general.xml | 32 - .../views/hotel_calendar_management_views.xml | 12 - hotel_calendar/views/hotel_calendar_views.xml | 38 - .../views/hotel_reservation_views.xml | 12 - .../views/inherited_hotel_property_views.xml | 31 - .../views/inherited_hotel_room_type_views.xml | 14 - .../views/inherited_hotel_room_views.xml | 14 - .../views/inherited_res_company_views.xml | 43 - .../views/inherited_res_users_views.xml | 20 - hotel_calendar_channel_connector/README.md | 12 - hotel_calendar_channel_connector/__init__.py | 3 - .../__manifest__.py | 33 - hotel_calendar_channel_connector/i18n/es.po | 88 - .../models/__init__.py | 6 - .../models/inherited_bus_hotel_calendar.py | 80 - .../inherited_hotel_calendar_management.py | 107 - .../models/inherited_hotel_reservation.py | 58 - .../inherited_hotel_room_type_availability.py | 50 - .../readme/CONTRIBUTORS.rst | 1 - .../readme/DESCRIPTION.rst | 7 - .../readme/ROADMAP.rst | 0 .../readme/USAGE.rst | 1 - .../static/description/eiqui_logo.png | Bin 6240 -> 0 bytes .../static/description/index.html | 22 - .../static/sfx/book_cancelled.mp3 | Bin 10152 -> 0 bytes .../static/sfx/book_new.mp3 | Bin 10176 -> 0 bytes .../static/src/css/view.css | 52 - .../src/js/views/hotel_calendar_controller.js | 102 - .../hotel_calendar_management_controller.js | 47 - .../hotel_calendar_management_renderer.js | 28 - .../src/js/views/hotel_calendar_renderer.js | 66 - .../src/xml/hotel_calendar_templates.xml | 25 - .../static/src/xml/hotel_calendar_view.xml | 35 - .../tests/__init__.py | 3 - .../tests/test_folio.py | 11 - .../views/actions.xml | 23 - .../views/general.xml | 15 - .../views/hotel_reservation.xml | 38 - hotel_channel_connector/README.md | 14 - hotel_channel_connector/__init__.py | 6 - hotel_channel_connector/__manifest__.py | 51 - .../components/__init__.py | 10 - .../components/backend_adapter.py | 126 - hotel_channel_connector/components/binder.py | 19 - hotel_channel_connector/components/core.py | 28 - hotel_channel_connector/components/deleter.py | 10 - .../components/exporter.py | 10 - .../components/importer.py | 10 - hotel_channel_connector/components/mapper.py | 16 - hotel_channel_connector/data/cron_jobs.xml | 17 - .../data/email_availability_watchdog.xml | 13 - hotel_channel_connector/data/menus.xml | 30 - hotel_channel_connector/i18n/es.po | 2805 --------------- hotel_channel_connector/models/__init__.py | 18 - .../models/channel_backend/__init__.py | 4 - .../models/channel_backend/common.py | 299 -- .../models/channel_binding/__init__.py | 4 - .../models/channel_binding/common.py | 23 - .../models/channel_ota_info/__init__.py | 5 - .../models/channel_ota_info/common.py | 28 - .../models/channel_ota_info/importer.py | 20 - .../models/hotel_channel_connector_issue.py | 63 - .../models/hotel_reservation/__init__.py | 6 - .../models/hotel_reservation/common.py | 328 -- .../models/hotel_reservation/exporter.py | 24 - .../models/hotel_reservation/importer.py | 22 - .../models/hotel_room_type/__init__.py | 7 - .../models/hotel_room_type/common.py | 227 -- .../models/hotel_room_type/deleter.py | 16 - .../models/hotel_room_type/exporter.py | 20 - .../models/hotel_room_type/importer.py | 16 - .../hotel_room_type_availability/__init__.py | 6 - .../hotel_room_type_availability/common.py | 327 -- .../hotel_room_type_availability/exporter.py | 15 - .../hotel_room_type_availability/importer.py | 16 - .../hotel_room_type_restriction/__init__.py | 7 - .../hotel_room_type_restriction/common.py | 141 - .../hotel_room_type_restriction/deleter.py | 16 - .../hotel_room_type_restriction/exporter.py | 20 - .../hotel_room_type_restriction/importer.py | 16 - .../__init__.py | 6 - .../common.py | 101 - .../exporter.py | 20 - .../importer.py | 21 - ...inherited_hotel_board_service_room_type.py | 16 - .../models/inherited_hotel_folio.py | 91 - .../models/inherited_hotel_room.py | 129 - .../models/inherited_hotel_room_type_class.py | 25 - .../models/product_pricelist/__init__.py | 7 - .../models/product_pricelist/common.py | 189 - .../models/product_pricelist/deleter.py | 16 - .../models/product_pricelist/exporter.py | 28 - .../models/product_pricelist/importer.py | 16 - .../models/product_pricelist_item/__init__.py | 6 - .../models/product_pricelist_item/common.py | 102 - .../models/product_pricelist_item/exporter.py | 16 - .../models/product_pricelist_item/importer.py | 20 - .../readme/CONTRIBUTORS.rst | 1 - .../readme/DESCRIPTION.rst | 6 - hotel_channel_connector/readme/ROADMAP.rst | 0 hotel_channel_connector/readme/USAGE.rst | 1 - .../security/ir.model.access.csv | 34 - .../security/wubook_security.xml | 21 - .../static/description/eiqui_logo.png | Bin 6240 -> 0 bytes .../static/description/index.html | 22 - hotel_channel_connector/tests/__init__.py | 2 - hotel_channel_connector/tests/common.py | 310 -- .../test_hotel_calendar_management_model.py | 152 - .../tests/test_hotel_folio_model.py | 125 - .../tests/test_hotel_reservation_model.py | 181 - .../tests/test_hotel_virtual_room_model.py | 73 - .../test_product_pricelist_item_model.py | 75 - .../tests/test_product_pricelist_model.py | 70 - .../tests/test_res_partner_model.py | 84 - ...test_reservation_restriction_item_model.py | 45 - .../test_reservation_restriction_model.py | 99 - .../test_virtual_room_availability_model.py | 43 - hotel_channel_connector/tests/test_wubook.py | 608 ---- .../tests/test_wubook_channel_info_model.py | 38 - .../tests/test_wubook_issue_model.py | 57 - .../views/channel_connector_backend_views.xml | 212 -- .../views/channel_hotel_reservation_views.xml | 30 - ...nel_hotel_room_type_availability_views.xml | 37 - ...hotel_room_type_restriction_item_views.xml | 30 - ...nnel_hotel_room_type_restriction_views.xml | 37 - .../views/channel_hotel_room_type_views.xml | 69 - .../views/channel_ota_info_views.xml | 36 - .../channel_product_pricelist_item_views.xml | 27 - .../views/channel_product_pricelist_views.xml | 39 - .../hotel_channel_connector_issue_views.xml | 85 - .../hotel_room_type_availability_views.xml | 66 - .../views/inherited_hotel_folio_views.xml | 33 - .../inherited_hotel_reservation_views.xml | 118 - ...hotel_room_type_restriction_item_views.xml | 24 - ...ited_hotel_room_type_restriction_views.xml | 46 - .../views/inherited_hotel_room_type_views.xml | 59 - ...inherited_product_pricelist_item_views.xml | 25 - .../inherited_product_pricelist_views.xml | 46 - hotel_channel_connector/wizard/__init__.py | 4 - .../wizard/inherited_massive_changes.py | 82 - .../wizard/inherited_massive_changes.xml | 30 - ...nherited_massive_price_reservation_days.py | 23 - hotel_channel_connector_wubook/README.md | 13 - hotel_channel_connector_wubook/__init__.py | 6 - .../__manifest__.py | 32 - .../components/__init__.py | 4 - .../components/backend_adapter.py | 569 --- .../controllers/__init__.py | 4 - .../controllers/main.py | 77 - .../data/cron_jobs.xml | 27 - .../data/records.xml | 44 - .../data/sequences.xml | 11 - hotel_channel_connector_wubook/i18n/es.po | 341 -- .../models/__init__.py | 15 - .../models/channel_backend/__init__.py | 4 - .../models/channel_backend/common.py | 50 - .../models/channel_ota_info/__init__.py | 5 - .../models/channel_ota_info/common.py | 23 - .../models/channel_ota_info/importer.py | 84 - .../models/hotel_reservation/__init__.py | 6 - .../models/hotel_reservation/common.py | 119 - .../models/hotel_reservation/exporter.py | 48 - .../models/hotel_reservation/importer.py | 689 ---- .../models/hotel_room_type/__init__.py | 7 - .../models/hotel_room_type/common.py | 40 - .../models/hotel_room_type/deleter.py | 22 - .../models/hotel_room_type/exporter.py | 90 - .../models/hotel_room_type/importer.py | 70 - .../hotel_room_type_availability/__init__.py | 6 - .../hotel_room_type_availability/common.py | 20 - .../hotel_room_type_availability/exporter.py | 72 - .../hotel_room_type_availability/importer.py | 118 - .../hotel_room_type_restriction/__init__.py | 7 - .../hotel_room_type_restriction/common.py | 22 - .../hotel_room_type_restriction/deleter.py | 22 - .../hotel_room_type_restriction/exporter.py | 40 - .../hotel_room_type_restriction/importer.py | 58 - .../__init__.py | 6 - .../common.py | 16 - .../exporter.py | 129 - .../importer.py | 131 - ...inherited_hotel_board_service_room_type.py | 17 - .../models/inherited_hotel_folio.py | 10 - .../models/inherited_hotel_room_type_class.py | 25 - .../models/product_pricelist/__init__.py | 7 - .../models/product_pricelist/common.py | 28 - .../models/product_pricelist/deleter.py | 22 - .../models/product_pricelist/exporter.py | 109 - .../models/product_pricelist/importer.py | 71 - .../models/product_pricelist_item/__init__.py | 6 - .../models/product_pricelist_item/common.py | 17 - .../models/product_pricelist_item/exporter.py | 81 - .../models/product_pricelist_item/importer.py | 143 - .../readme/CONTRIBUTORS.rst | 1 - .../readme/DESCRIPTION.rst | 11 - .../readme/ROADMAP.rst | 0 .../readme/USAGE.rst | 16 - .../security/wubook_security.xml | 21 - .../static/description/eiqui_logo.png | Bin 6240 -> 0 bytes .../static/description/index.html | 22 - .../tests/README.rst | 285 -- .../tests/__init__.py | 3 - .../tests/common.py | 322 -- ...t_channel_hotel_room_type_adapter_model.py | 84 - .../test_hotel_calendar_management_model.py | 152 - .../tests/test_hotel_folio_model.py | 125 - .../tests/test_hotel_reservation_model.py | 181 - .../test_product_pricelist_item_model.py | 75 - .../tests/test_product_pricelist_model.py | 70 - .../tests/test_res_partner_model.py | 84 - ...test_reservation_restriction_item_model.py | 45 - .../test_reservation_restriction_model.py | 99 - .../test_virtual_room_availability_model.py | 43 - .../tests/test_wubook.py | 608 ---- .../tests/test_wubook_channel_info_model.py | 38 - .../tests/test_wubook_issue_model.py | 57 - ...erited_channel_connector_backend_views.xml | 24 - ...nnel_hotel_room_type_restriction_views.xml | 15 - .../inherited_channel_ota_info_views.xml | 15 - hotel_data_bi/README.rst | 74 - hotel_data_bi/__init__.py | 1 - hotel_data_bi/__manifest__.py | 32 - hotel_data_bi/models/__init__.py | 3 - hotel_data_bi/models/budget.py | 48 - hotel_data_bi/models/data_bi.py | 705 ---- hotel_data_bi/models/inherit_res_company.py | 24 - hotel_data_bi/security/data_bi.xml | 10 - hotel_data_bi/security/ir.model.access.csv | 17 - hotel_data_bi/static/description/icon.png | Bin 66994 -> 0 bytes hotel_data_bi/views/budget.xml | 71 - hotel_data_bi/views/inherit_res_company.xml | 23 - hotel_door_codes/README.rst | 13 - hotel_door_codes/__init__.py | 22 - hotel_door_codes/__manifest__.py | 48 - hotel_door_codes/data/menus.xml | 13 - hotel_door_codes/i18n/es.po | 162 - hotel_door_codes/models/__init__.py | 23 - .../models/inherit_hotel_property.py | 29 - .../models/inherit_hotel_reservation.py | 79 - hotel_door_codes/static/description/icon.png | Bin 24484 -> 0 bytes .../views/inherit_hotel_property.xml | 20 - .../views/inherit_hotel_reservation.xml | 23 - hotel_door_codes/wizard/__init__.py | 21 - hotel_door_codes/wizard/door_code.py | 77 - hotel_door_codes/wizard/door_code.xml | 33 - hotel_ine/__init__.py | 23 - hotel_ine/__manifest__.py | 41 - hotel_ine/models/__init__.py | 1 - hotel_ine/models/inherited_hotel_room.py | 9 - hotel_ine/static/description/icon.png | Bin 24576 -> 0 bytes hotel_ine/views/inherited_hotel_room_view.xml | 18 - hotel_ine/wizard/__init__.py | 23 - hotel_ine/wizard/inewizard.py | 321 -- hotel_ine/wizard/inewizard.xml | 54 - hotel_l10n_es/README.rst | 22 - hotel_l10n_es/__init__.py | 24 - hotel_l10n_es/__manifest__.py | 66 - hotel_l10n_es/data/code.ine.csv | 309 -- .../data/report_viajero_paperformat.xml | 22 - hotel_l10n_es/data/tourism.category.csv | 54 - hotel_l10n_es/i18n/es.po | 812 ----- hotel_l10n_es/models/__init__.py | 28 - hotel_l10n_es/models/category_type.py | 37 - hotel_l10n_es/models/code_ine.py | 41 - .../models/inherit_hotel_checkin_partner.py | 127 - .../models/inherit_hotel_reservation.py | 35 - hotel_l10n_es/models/inherit_res_company.py | 57 - hotel_l10n_es/models/inherit_res_partner.py | 450 --- hotel_l10n_es/report/report_parte_viajero.xml | 13 - hotel_l10n_es/security/ir.model.access.csv | 7 - hotel_l10n_es/static/description/icon.png | Bin 19995 -> 0 bytes .../static/src/css/hotel_l10n_es.css | 5 - hotel_l10n_es/static/src/img/logo_bn.png | Bin 33801 -> 0 bytes hotel_l10n_es/static/src/img/watermark.jpg | Bin 10643 -> 0 bytes .../src/xml/hotel_l10n_es_templates.xml | 13 - hotel_l10n_es/views/category_tourism.xml | 51 - hotel_l10n_es/views/code_ine.xml | 53 - .../views/hotel_l10n_es_hotel_name.xml | 13 - .../inherit_hotel_checkin_partner_views.xml | 109 - hotel_l10n_es/views/inherit_res_company.xml | 32 - hotel_l10n_es/views/inherit_res_partner.xml | 40 - .../inherited_hotel_reservation_views.xml | 17 - hotel_l10n_es/views/report_viajero.xml | 10 - hotel_l10n_es/views/report_viajero_data.xml | 103 - .../views/report_viajero_document.xml | 64 - .../views/report_viajero_document_new.xml | 101 - hotel_l10n_es/views/report_viajero_head.xml | 93 - hotel_l10n_es/wizard/__init__.py | 23 - hotel_l10n_es/wizard/police_wizard.py | 123 - hotel_l10n_es/wizard/police_wizard.xml | 51 - hotel_roommatik/__init__.py | 1 - hotel_roommatik/__manifest__.py | 29 - hotel_roommatik/data/res_users_data.xml | 25 - hotel_roommatik/models/__init__.py | 6 - .../models/inherited_account_payment.py | 46 - .../models/inherited_hotel_checkin_partner.py | 140 - .../models/inherited_hotel_reservation.py | 79 - .../models/inherited_hotel_room_type.py | 71 - .../models/inherited_res_partner.py | 146 - hotel_roommatik/models/roommatik.py | 110 - hotel_roommatik/static/description/icon.png | Bin 44610 -> 0 bytes hotel_roommatik/static/img/avatar.png | Bin 44610 -> 0 bytes kellys_daily_report/README.rst | 13 - kellys_daily_report/__init__.py | 22 - kellys_daily_report/__manifest__.py | 51 - kellys_daily_report/data/menus.xml | 13 - .../data/report_kellys_paperformat.xml | 22 - kellys_daily_report/models/__init__.py | 23 - kellys_daily_report/models/kellysnames.py | 28 - kellys_daily_report/report/report_kellys.xml | 205 -- .../security/ir.model.access.csv | 4 - .../static/description/icon.png | Bin 36423 -> 0 bytes .../static/src/css/kellys_daily_report.css | 1 - kellys_daily_report/views/kellysnames.xml | 16 - kellys_daily_report/wizard/__init__.py | 22 - .../wizard/kellys_daily_pdf.py | 123 - .../wizard/kellys_daily_pdf.xml | 31 - .../wizard/kellys_daily_rooms.py | 36 - .../wizard/kellys_daily_rooms.xml | 20 - mail_reply_to_sender/__init__.py | 3 - mail_reply_to_sender/__manifest__.py | 22 - mail_reply_to_sender/models/__init__.py | 2 - mail_reply_to_sender/models/ir_mail_server.py | 17 - .../static/description/banner.png | Bin 232656 -> 0 bytes .../static/description/icon.png | Bin 46313 -> 0 bytes .../static/description/index.html | 44 - .../static/description/mail_reply_to_1.png | Bin 80902 -> 0 bytes .../static/description/mail_reply_to_2.png | Bin 84619 -> 0 bytes .../static/description/reply_to.png | Bin 27416 -> 0 bytes setup/pms/odoo/addons/pms | 1 + setup/pms/setup.py | 6 + 412 files changed, 7 insertions(+), 39258 deletions(-) delete mode 100644 call_center_report/README.rst delete mode 100644 call_center_report/__init__.py delete mode 100644 call_center_report/__manifest__.py delete mode 100644 call_center_report/data/menus.xml delete mode 100644 call_center_report/i18n/es.po delete mode 100644 call_center_report/wizard/__init__.py delete mode 100644 call_center_report/wizard/call_center_report.py delete mode 100644 call_center_report/wizard/call_center_report.xml delete mode 100644 cash_daily_report/README.rst delete mode 100644 cash_daily_report/__init__.py delete mode 100644 cash_daily_report/__manifest__.py delete mode 100644 cash_daily_report/data/cron_jobs.xml delete mode 100644 cash_daily_report/data/menus.xml delete mode 100644 cash_daily_report/i18n/es.po delete mode 100644 cash_daily_report/wizard/__init__.py delete mode 100644 cash_daily_report/wizard/cash_daily_report.py delete mode 100644 cash_daily_report/wizard/cash_daily_report.xml delete mode 100644 glasof_exporter/README.rst delete mode 100644 glasof_exporter/__init__.py delete mode 100644 glasof_exporter/__manifest__.py delete mode 100644 glasof_exporter/data/menus.xml delete mode 100644 glasof_exporter/wizard/__init__.py delete mode 100644 glasof_exporter/wizard/glasof_wizard.py delete mode 100644 glasof_exporter/wizard/glasof_wizard.xml delete mode 100644 hotel_calendar/README.rst delete mode 100644 hotel_calendar/__init__.py delete mode 100644 hotel_calendar/__manifest__.py delete mode 100644 hotel_calendar/controllers/__init__.py delete mode 100644 hotel_calendar/controllers/bus.py delete mode 100644 hotel_calendar/data/menus.xml delete mode 100644 hotel_calendar/i18n/es.po delete mode 100644 hotel_calendar/models/__init__.py delete mode 100644 hotel_calendar/models/bus_hotel_calendar.py delete mode 100644 hotel_calendar/models/hotel_calendar.py delete mode 100644 hotel_calendar/models/hotel_calendar_management.py delete mode 100644 hotel_calendar/models/inherited_hotel_folio.py delete mode 100644 hotel_calendar/models/inherited_hotel_property.py delete mode 100644 hotel_calendar/models/inherited_hotel_reservation.py delete mode 100644 hotel_calendar/models/inherited_hotel_room_type_restriction_item.py delete mode 100644 hotel_calendar/models/inherited_product_pricelist_item.py delete mode 100644 hotel_calendar/models/inherited_res_company.py delete mode 100644 hotel_calendar/models/inherited_res_users.py delete mode 100644 hotel_calendar/models/ir_actions_act_window_view.py delete mode 100644 hotel_calendar/models/ir_ui_view.py delete mode 100644 hotel_calendar/readme/CONFIGURE.rst delete mode 100644 hotel_calendar/readme/CONTRIBUTORS.rst delete mode 100644 hotel_calendar/readme/CREDITS.rst delete mode 100644 hotel_calendar/readme/DESCRIPTION.rst delete mode 100644 hotel_calendar/readme/INSTALL.rst delete mode 100644 hotel_calendar/readme/ROADMAP.rst delete mode 100644 hotel_calendar/readme/USAGE.rst delete mode 100644 hotel_calendar/security/ir.model.access.csv delete mode 100644 hotel_calendar/static/description/icon.png delete mode 100644 hotel_calendar/static/description/icon_calendar_configurator.png delete mode 100644 hotel_calendar/static/src/css/view.css delete mode 100644 hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js delete mode 100644 hotel_calendar/static/src/js/views/calendar/hotel_calendar_model.js delete mode 100644 hotel_calendar/static/src/js/views/calendar/hotel_calendar_renderer.js delete mode 100644 hotel_calendar/static/src/js/views/calendar/hotel_calendar_view.js delete mode 100644 hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_controller.js delete mode 100644 hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_model.js delete mode 100644 hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js delete mode 100644 hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v_deprecated.js delete mode 100644 hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_view.js delete mode 100644 hotel_calendar/static/src/js/views/constants.js delete mode 100644 hotel_calendar/static/src/js/widgets/MultiCalendar.js delete mode 100644 hotel_calendar/static/src/lib/bootbox.js delete mode 100644 hotel_calendar/static/src/lib/hcalendar/css/hcalendar.css delete mode 100644 hotel_calendar/static/src/lib/hcalendar/css/hcalendar_management.css delete mode 100644 hotel_calendar/static/src/lib/hcalendar/js/hcalendar.js delete mode 100644 hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js delete mode 100644 hotel_calendar/static/src/lib/moment.js delete mode 100644 hotel_calendar/static/src/xml/hotel_calendar_management_view.xml delete mode 100644 hotel_calendar/static/src/xml/hotel_calendar_templates.xml delete mode 100644 hotel_calendar/static/src/xml/hotel_calendar_view.xml delete mode 100644 hotel_calendar/tests/__init__.py delete mode 100644 hotel_calendar/tests/common.py delete mode 100644 hotel_calendar/tests/test_management_calendar.py delete mode 100644 hotel_calendar/tests/test_product_pricelist.py delete mode 100644 hotel_calendar/tests/test_reservations_calendar.py delete mode 100644 hotel_calendar/views/actions.xml delete mode 100644 hotel_calendar/views/general.xml delete mode 100644 hotel_calendar/views/hotel_calendar_management_views.xml delete mode 100644 hotel_calendar/views/hotel_calendar_views.xml delete mode 100644 hotel_calendar/views/hotel_reservation_views.xml delete mode 100644 hotel_calendar/views/inherited_hotel_property_views.xml delete mode 100644 hotel_calendar/views/inherited_hotel_room_type_views.xml delete mode 100644 hotel_calendar/views/inherited_hotel_room_views.xml delete mode 100644 hotel_calendar/views/inherited_res_company_views.xml delete mode 100644 hotel_calendar/views/inherited_res_users_views.xml delete mode 100644 hotel_calendar_channel_connector/README.md delete mode 100644 hotel_calendar_channel_connector/__init__.py delete mode 100644 hotel_calendar_channel_connector/__manifest__.py delete mode 100644 hotel_calendar_channel_connector/i18n/es.po delete mode 100644 hotel_calendar_channel_connector/models/__init__.py delete mode 100644 hotel_calendar_channel_connector/models/inherited_bus_hotel_calendar.py delete mode 100644 hotel_calendar_channel_connector/models/inherited_hotel_calendar_management.py delete mode 100644 hotel_calendar_channel_connector/models/inherited_hotel_reservation.py delete mode 100644 hotel_calendar_channel_connector/models/inherited_hotel_room_type_availability.py delete mode 100644 hotel_calendar_channel_connector/readme/CONTRIBUTORS.rst delete mode 100644 hotel_calendar_channel_connector/readme/DESCRIPTION.rst delete mode 100644 hotel_calendar_channel_connector/readme/ROADMAP.rst delete mode 100644 hotel_calendar_channel_connector/readme/USAGE.rst delete mode 100644 hotel_calendar_channel_connector/static/description/eiqui_logo.png delete mode 100644 hotel_calendar_channel_connector/static/description/index.html delete mode 100644 hotel_calendar_channel_connector/static/sfx/book_cancelled.mp3 delete mode 100644 hotel_calendar_channel_connector/static/sfx/book_new.mp3 delete mode 100644 hotel_calendar_channel_connector/static/src/css/view.css delete mode 100644 hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_controller.js delete mode 100644 hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_management_controller.js delete mode 100644 hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_management_renderer.js delete mode 100644 hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_renderer.js delete mode 100644 hotel_calendar_channel_connector/static/src/xml/hotel_calendar_templates.xml delete mode 100644 hotel_calendar_channel_connector/static/src/xml/hotel_calendar_view.xml delete mode 100644 hotel_calendar_channel_connector/tests/__init__.py delete mode 100644 hotel_calendar_channel_connector/tests/test_folio.py delete mode 100644 hotel_calendar_channel_connector/views/actions.xml delete mode 100644 hotel_calendar_channel_connector/views/general.xml delete mode 100644 hotel_calendar_channel_connector/views/hotel_reservation.xml delete mode 100644 hotel_channel_connector/README.md delete mode 100644 hotel_channel_connector/__init__.py delete mode 100644 hotel_channel_connector/__manifest__.py delete mode 100644 hotel_channel_connector/components/__init__.py delete mode 100644 hotel_channel_connector/components/backend_adapter.py delete mode 100644 hotel_channel_connector/components/binder.py delete mode 100644 hotel_channel_connector/components/core.py delete mode 100644 hotel_channel_connector/components/deleter.py delete mode 100644 hotel_channel_connector/components/exporter.py delete mode 100644 hotel_channel_connector/components/importer.py delete mode 100644 hotel_channel_connector/components/mapper.py delete mode 100644 hotel_channel_connector/data/cron_jobs.xml delete mode 100644 hotel_channel_connector/data/email_availability_watchdog.xml delete mode 100644 hotel_channel_connector/data/menus.xml delete mode 100644 hotel_channel_connector/i18n/es.po delete mode 100644 hotel_channel_connector/models/__init__.py delete mode 100644 hotel_channel_connector/models/channel_backend/__init__.py delete mode 100644 hotel_channel_connector/models/channel_backend/common.py delete mode 100644 hotel_channel_connector/models/channel_binding/__init__.py delete mode 100644 hotel_channel_connector/models/channel_binding/common.py delete mode 100644 hotel_channel_connector/models/channel_ota_info/__init__.py delete mode 100644 hotel_channel_connector/models/channel_ota_info/common.py delete mode 100644 hotel_channel_connector/models/channel_ota_info/importer.py delete mode 100644 hotel_channel_connector/models/hotel_channel_connector_issue.py delete mode 100644 hotel_channel_connector/models/hotel_reservation/__init__.py delete mode 100644 hotel_channel_connector/models/hotel_reservation/common.py delete mode 100644 hotel_channel_connector/models/hotel_reservation/exporter.py delete mode 100644 hotel_channel_connector/models/hotel_reservation/importer.py delete mode 100644 hotel_channel_connector/models/hotel_room_type/__init__.py delete mode 100644 hotel_channel_connector/models/hotel_room_type/common.py delete mode 100644 hotel_channel_connector/models/hotel_room_type/deleter.py delete mode 100644 hotel_channel_connector/models/hotel_room_type/exporter.py delete mode 100644 hotel_channel_connector/models/hotel_room_type/importer.py delete mode 100644 hotel_channel_connector/models/hotel_room_type_availability/__init__.py delete mode 100644 hotel_channel_connector/models/hotel_room_type_availability/common.py delete mode 100644 hotel_channel_connector/models/hotel_room_type_availability/exporter.py delete mode 100644 hotel_channel_connector/models/hotel_room_type_availability/importer.py delete mode 100644 hotel_channel_connector/models/hotel_room_type_restriction/__init__.py delete mode 100644 hotel_channel_connector/models/hotel_room_type_restriction/common.py delete mode 100644 hotel_channel_connector/models/hotel_room_type_restriction/deleter.py delete mode 100644 hotel_channel_connector/models/hotel_room_type_restriction/exporter.py delete mode 100644 hotel_channel_connector/models/hotel_room_type_restriction/importer.py delete mode 100644 hotel_channel_connector/models/hotel_room_type_restriction_item/__init__.py delete mode 100644 hotel_channel_connector/models/hotel_room_type_restriction_item/common.py delete mode 100644 hotel_channel_connector/models/hotel_room_type_restriction_item/exporter.py delete mode 100644 hotel_channel_connector/models/hotel_room_type_restriction_item/importer.py delete mode 100644 hotel_channel_connector/models/inherited_hotel_board_service_room_type.py delete mode 100644 hotel_channel_connector/models/inherited_hotel_folio.py delete mode 100644 hotel_channel_connector/models/inherited_hotel_room.py delete mode 100644 hotel_channel_connector/models/inherited_hotel_room_type_class.py delete mode 100644 hotel_channel_connector/models/product_pricelist/__init__.py delete mode 100644 hotel_channel_connector/models/product_pricelist/common.py delete mode 100644 hotel_channel_connector/models/product_pricelist/deleter.py delete mode 100644 hotel_channel_connector/models/product_pricelist/exporter.py delete mode 100644 hotel_channel_connector/models/product_pricelist/importer.py delete mode 100644 hotel_channel_connector/models/product_pricelist_item/__init__.py delete mode 100644 hotel_channel_connector/models/product_pricelist_item/common.py delete mode 100644 hotel_channel_connector/models/product_pricelist_item/exporter.py delete mode 100644 hotel_channel_connector/models/product_pricelist_item/importer.py delete mode 100644 hotel_channel_connector/readme/CONTRIBUTORS.rst delete mode 100644 hotel_channel_connector/readme/DESCRIPTION.rst delete mode 100644 hotel_channel_connector/readme/ROADMAP.rst delete mode 100644 hotel_channel_connector/readme/USAGE.rst delete mode 100644 hotel_channel_connector/security/ir.model.access.csv delete mode 100644 hotel_channel_connector/security/wubook_security.xml delete mode 100644 hotel_channel_connector/static/description/eiqui_logo.png delete mode 100644 hotel_channel_connector/static/description/index.html delete mode 100644 hotel_channel_connector/tests/__init__.py delete mode 100644 hotel_channel_connector/tests/common.py delete mode 100644 hotel_channel_connector/tests/test_hotel_calendar_management_model.py delete mode 100644 hotel_channel_connector/tests/test_hotel_folio_model.py delete mode 100644 hotel_channel_connector/tests/test_hotel_reservation_model.py delete mode 100644 hotel_channel_connector/tests/test_hotel_virtual_room_model.py delete mode 100644 hotel_channel_connector/tests/test_product_pricelist_item_model.py delete mode 100644 hotel_channel_connector/tests/test_product_pricelist_model.py delete mode 100644 hotel_channel_connector/tests/test_res_partner_model.py delete mode 100644 hotel_channel_connector/tests/test_reservation_restriction_item_model.py delete mode 100644 hotel_channel_connector/tests/test_reservation_restriction_model.py delete mode 100644 hotel_channel_connector/tests/test_virtual_room_availability_model.py delete mode 100644 hotel_channel_connector/tests/test_wubook.py delete mode 100644 hotel_channel_connector/tests/test_wubook_channel_info_model.py delete mode 100644 hotel_channel_connector/tests/test_wubook_issue_model.py delete mode 100644 hotel_channel_connector/views/channel_connector_backend_views.xml delete mode 100644 hotel_channel_connector/views/channel_hotel_reservation_views.xml delete mode 100644 hotel_channel_connector/views/channel_hotel_room_type_availability_views.xml delete mode 100644 hotel_channel_connector/views/channel_hotel_room_type_restriction_item_views.xml delete mode 100644 hotel_channel_connector/views/channel_hotel_room_type_restriction_views.xml delete mode 100644 hotel_channel_connector/views/channel_hotel_room_type_views.xml delete mode 100644 hotel_channel_connector/views/channel_ota_info_views.xml delete mode 100644 hotel_channel_connector/views/channel_product_pricelist_item_views.xml delete mode 100644 hotel_channel_connector/views/channel_product_pricelist_views.xml delete mode 100644 hotel_channel_connector/views/hotel_channel_connector_issue_views.xml delete mode 100644 hotel_channel_connector/views/hotel_room_type_availability_views.xml delete mode 100644 hotel_channel_connector/views/inherited_hotel_folio_views.xml delete mode 100644 hotel_channel_connector/views/inherited_hotel_reservation_views.xml delete mode 100644 hotel_channel_connector/views/inherited_hotel_room_type_restriction_item_views.xml delete mode 100644 hotel_channel_connector/views/inherited_hotel_room_type_restriction_views.xml delete mode 100644 hotel_channel_connector/views/inherited_hotel_room_type_views.xml delete mode 100644 hotel_channel_connector/views/inherited_product_pricelist_item_views.xml delete mode 100644 hotel_channel_connector/views/inherited_product_pricelist_views.xml delete mode 100644 hotel_channel_connector/wizard/__init__.py delete mode 100644 hotel_channel_connector/wizard/inherited_massive_changes.py delete mode 100644 hotel_channel_connector/wizard/inherited_massive_changes.xml delete mode 100644 hotel_channel_connector/wizard/inherited_massive_price_reservation_days.py delete mode 100644 hotel_channel_connector_wubook/README.md delete mode 100644 hotel_channel_connector_wubook/__init__.py delete mode 100644 hotel_channel_connector_wubook/__manifest__.py delete mode 100644 hotel_channel_connector_wubook/components/__init__.py delete mode 100644 hotel_channel_connector_wubook/components/backend_adapter.py delete mode 100644 hotel_channel_connector_wubook/controllers/__init__.py delete mode 100644 hotel_channel_connector_wubook/controllers/main.py delete mode 100644 hotel_channel_connector_wubook/data/cron_jobs.xml delete mode 100644 hotel_channel_connector_wubook/data/records.xml delete mode 100644 hotel_channel_connector_wubook/data/sequences.xml delete mode 100644 hotel_channel_connector_wubook/i18n/es.po delete mode 100644 hotel_channel_connector_wubook/models/__init__.py delete mode 100644 hotel_channel_connector_wubook/models/channel_backend/__init__.py delete mode 100644 hotel_channel_connector_wubook/models/channel_backend/common.py delete mode 100644 hotel_channel_connector_wubook/models/channel_ota_info/__init__.py delete mode 100644 hotel_channel_connector_wubook/models/channel_ota_info/common.py delete mode 100644 hotel_channel_connector_wubook/models/channel_ota_info/importer.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_reservation/__init__.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_reservation/common.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_reservation/exporter.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_reservation/importer.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type/__init__.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type/common.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type/deleter.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type/exporter.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type/importer.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type_availability/__init__.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type_availability/common.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type_availability/exporter.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type_availability/importer.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type_restriction/__init__.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type_restriction/common.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type_restriction/deleter.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type_restriction/exporter.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type_restriction/importer.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type_restriction_item/__init__.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type_restriction_item/common.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type_restriction_item/exporter.py delete mode 100644 hotel_channel_connector_wubook/models/hotel_room_type_restriction_item/importer.py delete mode 100644 hotel_channel_connector_wubook/models/inherited_hotel_board_service_room_type.py delete mode 100644 hotel_channel_connector_wubook/models/inherited_hotel_folio.py delete mode 100644 hotel_channel_connector_wubook/models/inherited_hotel_room_type_class.py delete mode 100644 hotel_channel_connector_wubook/models/product_pricelist/__init__.py delete mode 100644 hotel_channel_connector_wubook/models/product_pricelist/common.py delete mode 100644 hotel_channel_connector_wubook/models/product_pricelist/deleter.py delete mode 100644 hotel_channel_connector_wubook/models/product_pricelist/exporter.py delete mode 100644 hotel_channel_connector_wubook/models/product_pricelist/importer.py delete mode 100644 hotel_channel_connector_wubook/models/product_pricelist_item/__init__.py delete mode 100644 hotel_channel_connector_wubook/models/product_pricelist_item/common.py delete mode 100644 hotel_channel_connector_wubook/models/product_pricelist_item/exporter.py delete mode 100644 hotel_channel_connector_wubook/models/product_pricelist_item/importer.py delete mode 100644 hotel_channel_connector_wubook/readme/CONTRIBUTORS.rst delete mode 100644 hotel_channel_connector_wubook/readme/DESCRIPTION.rst delete mode 100644 hotel_channel_connector_wubook/readme/ROADMAP.rst delete mode 100644 hotel_channel_connector_wubook/readme/USAGE.rst delete mode 100644 hotel_channel_connector_wubook/security/wubook_security.xml delete mode 100644 hotel_channel_connector_wubook/static/description/eiqui_logo.png delete mode 100644 hotel_channel_connector_wubook/static/description/index.html delete mode 100644 hotel_channel_connector_wubook/tests/README.rst delete mode 100644 hotel_channel_connector_wubook/tests/__init__.py delete mode 100644 hotel_channel_connector_wubook/tests/common.py delete mode 100644 hotel_channel_connector_wubook/tests/test_channel_hotel_room_type_adapter_model.py delete mode 100644 hotel_channel_connector_wubook/tests/test_hotel_calendar_management_model.py delete mode 100644 hotel_channel_connector_wubook/tests/test_hotel_folio_model.py delete mode 100644 hotel_channel_connector_wubook/tests/test_hotel_reservation_model.py delete mode 100644 hotel_channel_connector_wubook/tests/test_product_pricelist_item_model.py delete mode 100644 hotel_channel_connector_wubook/tests/test_product_pricelist_model.py delete mode 100644 hotel_channel_connector_wubook/tests/test_res_partner_model.py delete mode 100644 hotel_channel_connector_wubook/tests/test_reservation_restriction_item_model.py delete mode 100644 hotel_channel_connector_wubook/tests/test_reservation_restriction_model.py delete mode 100644 hotel_channel_connector_wubook/tests/test_virtual_room_availability_model.py delete mode 100644 hotel_channel_connector_wubook/tests/test_wubook.py delete mode 100644 hotel_channel_connector_wubook/tests/test_wubook_channel_info_model.py delete mode 100644 hotel_channel_connector_wubook/tests/test_wubook_issue_model.py delete mode 100644 hotel_channel_connector_wubook/views/inherited_channel_connector_backend_views.xml delete mode 100644 hotel_channel_connector_wubook/views/inherited_channel_hotel_room_type_restriction_views.xml delete mode 100644 hotel_channel_connector_wubook/views/inherited_channel_ota_info_views.xml delete mode 100644 hotel_data_bi/README.rst delete mode 100644 hotel_data_bi/__init__.py delete mode 100644 hotel_data_bi/__manifest__.py delete mode 100644 hotel_data_bi/models/__init__.py delete mode 100644 hotel_data_bi/models/budget.py delete mode 100644 hotel_data_bi/models/data_bi.py delete mode 100644 hotel_data_bi/models/inherit_res_company.py delete mode 100644 hotel_data_bi/security/data_bi.xml delete mode 100644 hotel_data_bi/security/ir.model.access.csv delete mode 100644 hotel_data_bi/static/description/icon.png delete mode 100644 hotel_data_bi/views/budget.xml delete mode 100644 hotel_data_bi/views/inherit_res_company.xml delete mode 100644 hotel_door_codes/README.rst delete mode 100644 hotel_door_codes/__init__.py delete mode 100644 hotel_door_codes/__manifest__.py delete mode 100644 hotel_door_codes/data/menus.xml delete mode 100644 hotel_door_codes/i18n/es.po delete mode 100644 hotel_door_codes/models/__init__.py delete mode 100644 hotel_door_codes/models/inherit_hotel_property.py delete mode 100644 hotel_door_codes/models/inherit_hotel_reservation.py delete mode 100644 hotel_door_codes/static/description/icon.png delete mode 100644 hotel_door_codes/views/inherit_hotel_property.xml delete mode 100644 hotel_door_codes/views/inherit_hotel_reservation.xml delete mode 100644 hotel_door_codes/wizard/__init__.py delete mode 100644 hotel_door_codes/wizard/door_code.py delete mode 100644 hotel_door_codes/wizard/door_code.xml delete mode 100644 hotel_ine/__init__.py delete mode 100644 hotel_ine/__manifest__.py delete mode 100644 hotel_ine/models/__init__.py delete mode 100644 hotel_ine/models/inherited_hotel_room.py delete mode 100755 hotel_ine/static/description/icon.png delete mode 100644 hotel_ine/views/inherited_hotel_room_view.xml delete mode 100644 hotel_ine/wizard/__init__.py delete mode 100644 hotel_ine/wizard/inewizard.py delete mode 100644 hotel_ine/wizard/inewizard.xml delete mode 100755 hotel_l10n_es/README.rst delete mode 100755 hotel_l10n_es/__init__.py delete mode 100755 hotel_l10n_es/__manifest__.py delete mode 100755 hotel_l10n_es/data/code.ine.csv delete mode 100755 hotel_l10n_es/data/report_viajero_paperformat.xml delete mode 100755 hotel_l10n_es/data/tourism.category.csv delete mode 100755 hotel_l10n_es/i18n/es.po delete mode 100755 hotel_l10n_es/models/__init__.py delete mode 100755 hotel_l10n_es/models/category_type.py delete mode 100755 hotel_l10n_es/models/code_ine.py delete mode 100755 hotel_l10n_es/models/inherit_hotel_checkin_partner.py delete mode 100644 hotel_l10n_es/models/inherit_hotel_reservation.py delete mode 100755 hotel_l10n_es/models/inherit_res_company.py delete mode 100755 hotel_l10n_es/models/inherit_res_partner.py delete mode 100755 hotel_l10n_es/report/report_parte_viajero.xml delete mode 100755 hotel_l10n_es/security/ir.model.access.csv delete mode 100755 hotel_l10n_es/static/description/icon.png delete mode 100755 hotel_l10n_es/static/src/css/hotel_l10n_es.css delete mode 100755 hotel_l10n_es/static/src/img/logo_bn.png delete mode 100755 hotel_l10n_es/static/src/img/watermark.jpg delete mode 100644 hotel_l10n_es/static/src/xml/hotel_l10n_es_templates.xml delete mode 100755 hotel_l10n_es/views/category_tourism.xml delete mode 100755 hotel_l10n_es/views/code_ine.xml delete mode 100755 hotel_l10n_es/views/hotel_l10n_es_hotel_name.xml delete mode 100755 hotel_l10n_es/views/inherit_hotel_checkin_partner_views.xml delete mode 100755 hotel_l10n_es/views/inherit_res_company.xml delete mode 100755 hotel_l10n_es/views/inherit_res_partner.xml delete mode 100644 hotel_l10n_es/views/inherited_hotel_reservation_views.xml delete mode 100755 hotel_l10n_es/views/report_viajero.xml delete mode 100755 hotel_l10n_es/views/report_viajero_data.xml delete mode 100755 hotel_l10n_es/views/report_viajero_document.xml delete mode 100755 hotel_l10n_es/views/report_viajero_document_new.xml delete mode 100755 hotel_l10n_es/views/report_viajero_head.xml delete mode 100755 hotel_l10n_es/wizard/__init__.py delete mode 100755 hotel_l10n_es/wizard/police_wizard.py delete mode 100755 hotel_l10n_es/wizard/police_wizard.xml delete mode 100755 hotel_roommatik/__init__.py delete mode 100755 hotel_roommatik/__manifest__.py delete mode 100644 hotel_roommatik/data/res_users_data.xml delete mode 100755 hotel_roommatik/models/__init__.py delete mode 100644 hotel_roommatik/models/inherited_account_payment.py delete mode 100644 hotel_roommatik/models/inherited_hotel_checkin_partner.py delete mode 100644 hotel_roommatik/models/inherited_hotel_reservation.py delete mode 100644 hotel_roommatik/models/inherited_hotel_room_type.py delete mode 100755 hotel_roommatik/models/inherited_res_partner.py delete mode 100755 hotel_roommatik/models/roommatik.py delete mode 100755 hotel_roommatik/static/description/icon.png delete mode 100644 hotel_roommatik/static/img/avatar.png delete mode 100644 kellys_daily_report/README.rst delete mode 100644 kellys_daily_report/__init__.py delete mode 100644 kellys_daily_report/__manifest__.py delete mode 100644 kellys_daily_report/data/menus.xml delete mode 100644 kellys_daily_report/data/report_kellys_paperformat.xml delete mode 100644 kellys_daily_report/models/__init__.py delete mode 100644 kellys_daily_report/models/kellysnames.py delete mode 100644 kellys_daily_report/report/report_kellys.xml delete mode 100644 kellys_daily_report/security/ir.model.access.csv delete mode 100644 kellys_daily_report/static/description/icon.png delete mode 100644 kellys_daily_report/static/src/css/kellys_daily_report.css delete mode 100644 kellys_daily_report/views/kellysnames.xml delete mode 100644 kellys_daily_report/wizard/__init__.py delete mode 100644 kellys_daily_report/wizard/kellys_daily_pdf.py delete mode 100644 kellys_daily_report/wizard/kellys_daily_pdf.xml delete mode 100644 kellys_daily_report/wizard/kellys_daily_rooms.py delete mode 100644 kellys_daily_report/wizard/kellys_daily_rooms.xml delete mode 100644 mail_reply_to_sender/__init__.py delete mode 100644 mail_reply_to_sender/__manifest__.py delete mode 100644 mail_reply_to_sender/models/__init__.py delete mode 100644 mail_reply_to_sender/models/ir_mail_server.py delete mode 100644 mail_reply_to_sender/static/description/banner.png delete mode 100755 mail_reply_to_sender/static/description/icon.png delete mode 100644 mail_reply_to_sender/static/description/index.html delete mode 100644 mail_reply_to_sender/static/description/mail_reply_to_1.png delete mode 100644 mail_reply_to_sender/static/description/mail_reply_to_2.png delete mode 100644 mail_reply_to_sender/static/description/reply_to.png create mode 120000 setup/pms/odoo/addons/pms create mode 100644 setup/pms/setup.py diff --git a/call_center_report/README.rst b/call_center_report/README.rst deleted file mode 100644 index ec06d16ac..000000000 --- a/call_center_report/README.rst +++ /dev/null @@ -1,6 +0,0 @@ -CALL CENTER REPORT -============= - -Export call center report in xls format - - diff --git a/call_center_report/__init__.py b/call_center_report/__init__.py deleted file mode 100644 index 351d1ee57..000000000 --- a/call_center_report/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018 Alexandre Díaz -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from . import wizard diff --git a/call_center_report/__manifest__.py b/call_center_report/__manifest__.py deleted file mode 100644 index 8204bbb7e..000000000 --- a/call_center_report/__manifest__.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018 Alexandre Díaz -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## - -{ - 'name': 'Call Center Report', - 'version': '1.0', - 'author': "Dario Lodeiros", - 'website': 'https://www.eiqui.com', - 'category': 'reports', - 'summary': "Export services and reservation report in xls format", - 'description': "Call Center Report", - 'depends': [ - 'hotel', - ], - 'external_dependencies': { - 'python': ['xlsxwriter'] - }, - 'data': [ - 'wizard/call_center_report.xml', - 'data/menus.xml', - ], - 'qweb': [], - 'test': [ - ], - - 'installable': True, - 'auto_install': False, - 'application': False, - 'license': 'AGPL-3', -} diff --git a/call_center_report/data/menus.xml b/call_center_report/data/menus.xml deleted file mode 100644 index d48c886eb..000000000 --- a/call_center_report/data/menus.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - diff --git a/call_center_report/i18n/es.po b/call_center_report/i18n/es.po deleted file mode 100644 index 0f9b0fa66..000000000 --- a/call_center_report/i18n/es.po +++ /dev/null @@ -1,171 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * cash_daily_report -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 10.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-05-20 07:33+0000\n" -"PO-Revision-Date: 2018-05-20 09:34+0200\n" -"Last-Translator: <>\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: \n" -"Language: es\n" -"X-Generator: Poedit 1.8.7.1\n" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:85 -#, python-format -msgid "Amount" -msgstr "Importe" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:78 -#: model:ir.ui.view,arch_db:cash_daily_report.view_cash_daily_report_wizard -#, python-format -msgid "Cash Daily Report" -msgstr "Informe de caja" - -#. module: cash_daily_report -#: model:ir.actions.act_window,name:cash_daily_report.action_open_cash_daily_report_wizard -#: model:ir.ui.menu,name:cash_daily_report.cash_daily_report_wizard -msgid "Cash Daily Report Wizard" -msgstr "Informe de caja diaria" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:82 -#, python-format -msgid "Client" -msgstr "Cliente" - -#. module: cash_daily_report -#: model:ir.ui.view,arch_db:cash_daily_report.view_cash_daily_report_wizard -msgid "Close" -msgstr "Cerrar" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_create_uid -msgid "Created by" -msgstr "Creado por" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_create_date -msgid "Created on" -msgstr "Creado en" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:83 -#, python-format -msgid "Date" -msgstr "Fecha" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_display_name -msgid "Display Name" -msgstr "Mostrar Nombre" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_date_end -msgid "End Date" -msgstr "Fecha finalización" - -#. module: cash_daily_report -#: model:ir.ui.view,arch_db:cash_daily_report.view_cash_daily_report_wizard -msgid "Generate XLS" -msgstr "Generar XLS" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_id -msgid "ID" -msgstr "ID" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:84 -#, python-format -msgid "Journal" -msgstr "Diario" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard___last_update -msgid "Last Modified on" -msgstr "Última modificación en" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_write_uid -msgid "Last Updated by" -msgstr "Última actualización por" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_write_date -msgid "Last Updated on" -msgstr "Última actualización en" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:80 -#, python-format -msgid "Name" -msgstr "Nombre" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:128 -#, python-format -msgid "Not Any Payments" -msgstr "No hay movimientos" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:81 -#, python-format -msgid "Reference" -msgstr "Referencia" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_date_start -msgid "Start Date" -msgstr "Fecha de inicio" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:143 -#, python-format -msgid "TOTAL" -msgstr "TOTAL" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:139 -#, python-format -msgid "TOTAL PAYMENT RETURNS" -msgstr "TOTAL DEVOLUCIONES" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:134 -#, python-format -msgid "TOTAL PAYMENTS" -msgstr "TOTAL PAGOS" - -#. module: cash_daily_report -#: model:ir.ui.menu,name:cash_daily_report.menu_account_finance_xls_reports -msgid "XLS Reports" -msgstr "XLS Reports" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_xls_binary -msgid "Xls binary" -msgstr "Xls archivo" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_xls_filename -msgid "Xls filename" -msgstr "Xls nombre de archivo" - -#. module: cash_daily_report -#: model:ir.model,name:cash_daily_report.model_cash_daily_report_wizard -msgid "cash.daily.report.wizard" -msgstr "cash.daily.report.wizard" - -#. module: cash_daily_report -#: model:ir.ui.view,arch_db:cash_daily_report.view_cash_daily_report_wizard -msgid "or" -msgstr "o" diff --git a/call_center_report/wizard/__init__.py b/call_center_report/wizard/__init__.py deleted file mode 100644 index f3c151d2d..000000000 --- a/call_center_report/wizard/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018 Alexandre Díaz -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from . import call_center_report diff --git a/call_center_report/wizard/call_center_report.py b/call_center_report/wizard/call_center_report.py deleted file mode 100644 index 114aee314..000000000 --- a/call_center_report/wizard/call_center_report.py +++ /dev/null @@ -1,348 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018 Alexandre Díaz -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from io import BytesIO -from datetime import datetime, date -import xlsxwriter -import base64 -from odoo import api, fields, models, _ -from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT - - -class CallCenterReportWizard(models.TransientModel): - _name = 'call.center.report.wizard' - - @api.model - def _get_default_date_start(self): - return datetime.now().strftime(DEFAULT_SERVER_DATE_FORMAT) - - @api.model - def _get_default_date_end(self): - return datetime.now().strftime(DEFAULT_SERVER_DATE_FORMAT) - - date_start = fields.Date("Start Date", default=_get_default_date_start) - date_end = fields.Date("End Date", default=_get_default_date_end) - xls_filename = fields.Char() - xls_binary = fields.Binary() - - @api.model - def _export(self): - date_format = "%d-%m-%Y" - time_format = "%H:%M" - file_data = BytesIO() - workbook = xlsxwriter.Workbook(file_data, { - 'strings_to_numbers': True, - 'default_date_format': 'dd/mm/yyyy' - }) - - company_id = self.env.user.company_id - workbook.set_properties({ - 'title': 'Exported data from ' + company_id.name, - 'subject': 'Payments Data from Odoo of ' + company_id.name, - 'author': 'Odoo', - 'manager': u'Call Center', - 'company': company_id.name, - 'category': 'Hoja de Calculo', - 'keywords': 'payments, odoo, data, ' + company_id.name, - 'comments': 'Created with Python in Odoo and XlsxWriter'}) - workbook.use_zip64() - - xls_cell_format_date = workbook.add_format({ - 'num_format': 'dd/mm/yyyy' - }) - xls_cell_format_money = workbook.add_format({ - 'num_format': '#,##0.00' - }) - xls_cell_format_header = workbook.add_format({ - 'bg_color': '#CCCCCC' - }) - - worksheet = workbook.add_worksheet(_('Call Center Report - Production')) - - worksheet.write('A1', _('Ficha'), xls_cell_format_header) - worksheet.write('B1', _('Fecha de Pedido'), xls_cell_format_header) - worksheet.write('C1', _('Cliente'), xls_cell_format_header) - worksheet.write('D1', _('Producto'), xls_cell_format_header) - worksheet.write('E1', _('Noches/Uds'), xls_cell_format_header) - worksheet.write('F1', _('Adultos'), xls_cell_format_header) - worksheet.write('G1', _('Checkin'), xls_cell_format_header) - worksheet.write('H1', _('In-Hora'), xls_cell_format_header) - worksheet.write('I1', _('Checkout'), xls_cell_format_header) - worksheet.write('J1', _('Creado por'), xls_cell_format_header) - worksheet.write('K1', _('Total'), xls_cell_format_header) - - worksheet.set_column('B:B', 20) - worksheet.set_column('C:C', 20) - worksheet.set_column('D:D', 20) - worksheet.set_column('E:E', 20) - worksheet.set_column('F:F', 13) - - reservations_obj = self.env['hotel.reservation'] - reservations = reservations_obj.search([ - ('checkin', '>=', self.date_start), - ('checkout', '<=', self.date_end), - ('state', '=', 'done'), - ('channel_type', '=', 'call'), - ('folio_id.pending_amount', '<', 1), - ]) - offset = 1 - total_reservation_amount = 0.0 - for k_res, v_res in enumerate(reservations): - checkin_date = datetime.strptime(v_res.checkin, DEFAULT_SERVER_DATE_FORMAT) - checkout_date = datetime.strptime(v_res.checkout, DEFAULT_SERVER_DATE_FORMAT) - worksheet.write(k_res+offset, 0, v_res.folio_id.name) - worksheet.write(k_res+offset, 1, v_res.folio_id.date_order, - xls_cell_format_date) - worksheet.write(k_res+offset, 2, v_res.partner_id.name) - worksheet.write(k_res+offset, 3, v_res.room_type_id.name) - worksheet.write(k_res+offset, 4, v_res.nights) - worksheet.write(k_res+offset, 5, v_res.adults) - worksheet.write(k_res+offset, 6, checkin_date.strftime(date_format), - xls_cell_format_date) - worksheet.write(k_res+offset, 7, v_res.arrival_hour) - worksheet.write(k_res+offset, 8, checkout_date.strftime(date_format), - xls_cell_format_date) - worksheet.write(k_res+offset, 9, v_res.create_uid.name) - worksheet.write(k_res+offset, 10, v_res.price_total, - xls_cell_format_money) - total_reservation_amount += v_res.price_total - - folio_ids = reservations.mapped('folio_id.id') - folios = self.env['hotel.folio'].browse(folio_ids) - services = self.env['hotel.service'].browse() - for folio in folios: - services += folio.service_ids.filtered(lambda r: - r.channel_type == 'call' and r.folio_id.pending_amount < 1) - offset += len(reservations) - total_service_amount = k_line = 0.0 - for k_service, v_service in enumerate(services): - worksheet.write(k_service+offset, 0, v_service.folio_id.name) - worksheet.write(k_service+offset, 1, v_service.folio_id.date_order, - xls_cell_format_date) - worksheet.write(k_service+offset, 2, v_service.folio_id.partner_id.name) - worksheet.write(k_service+offset, 3, v_service.product_id.name) - worksheet.write(k_service+offset, 4, v_service.product_qty) - worksheet.write(k_service+offset, 5, '') - worksheet.write(k_service+offset, 6, '') - worksheet.write(k_service+offset, 7, '') - worksheet.write(k_service+offset, 8, '') - worksheet.write(k_service+offset, 9, v_service .create_uid.name) - worksheet.write(k_service+offset, 10, v_service.price_total, - xls_cell_format_money) - total_service_amount += v_service.price_total - offset += len(services) - #~ if total_reservation_amount == 0 and total_service_amount == 0: - #~ raise UserError(_('No Hay reservas de Call Center')) - line = offset - if k_line: - line = k_line + offset - if total_reservation_amount > 0: - line += 1 - worksheet.write(line, 9, _('TOTAL RESERVAS')) - worksheet.write(line, 10, total_reservation_amount, - xls_cell_format_money) - if total_service_amount > 0: - line += 1 - worksheet.write(line, 9, _('TOTAL SERVICIOS')) - worksheet.write(line, 10, total_service_amount, - xls_cell_format_money) - line += 1 - worksheet.write(line, 9, _('TOTAL')) - worksheet.write(line, 10 , total_reservation_amount + total_service_amount, - xls_cell_format_money) - - worksheet = workbook.add_worksheet(_('Call Center Report - Sales')) - - worksheet.write('A1', _('Estado'), xls_cell_format_header) - worksheet.write('B1', _('Ficha'), xls_cell_format_header) - worksheet.write('C1', _('Fecha de Pedido'), xls_cell_format_header) - worksheet.write('D1', _('Cliente'), xls_cell_format_header) - worksheet.write('E1', _('Producto'), xls_cell_format_header) - worksheet.write('F1', _('Noches/Uds'), xls_cell_format_header) - worksheet.write('G1', _('Adultos'), xls_cell_format_header) - worksheet.write('H1', _('Checkin'), xls_cell_format_header) - worksheet.write('I1', _('In-Hora'), xls_cell_format_header) - worksheet.write('J1', _('Checkout'), xls_cell_format_header) - worksheet.write('K1', _('Creado por'), xls_cell_format_header) - worksheet.write('L1', _('Total'), xls_cell_format_header) - - worksheet.set_column('B:B', 20) - worksheet.set_column('C:C', 20) - worksheet.set_column('D:D', 20) - worksheet.set_column('E:E', 20) - worksheet.set_column('F:F', 13) - - reservations_obj = self.env['hotel.reservation'] - reservations = reservations_obj.search([ - ('folio_id.date_order', '>=', self.date_start), - ('folio_id.date_order', '<=', self.date_end), - ('channel_type','=','call'), - ]) - offset = 1 - total_reservation_amount = 0.0 - for k_res, v_res in enumerate(reservations): - checkin_date = datetime.strptime(v_res.checkin, DEFAULT_SERVER_DATE_FORMAT) - checkout_date = datetime.strptime(v_res.checkout, DEFAULT_SERVER_DATE_FORMAT) - worksheet.write(k_res+offset, 0, v_res.state) - worksheet.write(k_res+offset, 1, v_res.folio_id.name) - worksheet.write(k_res+offset, 2, v_res.folio_id.date_order, - xls_cell_format_date) - worksheet.write(k_res+offset, 3, v_res.partner_id.name) - worksheet.write(k_res+offset, 4, v_res.room_type_id.name) - worksheet.write(k_res+offset, 5, v_res.nights) - worksheet.write(k_res+offset, 6, v_res.adults) - worksheet.write(k_res+offset, 7, checkin_date.strftime(date_format), - xls_cell_format_date) - worksheet.write(k_res+offset, 8, v_res.arrival_hour) - worksheet.write(k_res+offset, 9, checkout_date.strftime(date_format), - xls_cell_format_date) - worksheet.write(k_res+offset, 10, v_res.create_uid.name) - worksheet.write(k_res+offset, 11, v_res.price_total, - xls_cell_format_money) - total_reservation_amount += v_res.price_total - - folio_ids = reservations.mapped('folio_id.id') - folios = self.env['hotel.folio'].browse(folio_ids) - services = self.env['hotel.service'].browse() - for folio in folios: - services += folio.service_ids.filtered(lambda r: - r.channel_type == 'call' and r.folio_id.pending_amount < 1) - offset += len(reservations) - total_service_amount = k_line = 0.0 - for k_service, v_service in enumerate(services): - worksheet.write(k_service+offset, 1, v_service.folio_id.state) - worksheet.write(k_service+offset, 1, v_service.folio_id.name) - worksheet.write(k_service+offset, 2, v_service.folio_id.date_order, - xls_cell_format_date) - worksheet.write(k_service+offset, 3, v_service.folio_id.partner_id.name) - worksheet.write(k_service+offset, 4, v_service.product_id.name) - worksheet.write(k_service+offset, 5, v_service.product_qty) - worksheet.write(k_service+offset, 6, '') - worksheet.write(k_service+offset, 7, '') - worksheet.write(k_service+offset, 8, '') - worksheet.write(k_service+offset, 9, '') - worksheet.write(k_service+offset, 10, v_service .create_uid.name) - worksheet.write(k_service+offset, 11, v_service.price_total, - xls_cell_format_money) - total_service_amount += v_service.price_total - offset += len(services) - #~ if total_reservation_amount == 0 and total_service_amount == 0: - #~ raise UserError(_('No Hay reservas de Call Center')) - line = offset - if k_line: - line = k_line + offset - if total_reservation_amount > 0: - line += 1 - worksheet.write(line, 10, _('TOTAL RESERVAS')) - worksheet.write(line, 11, total_reservation_amount, - xls_cell_format_money) - if total_service_amount > 0: - line += 1 - worksheet.write(line, 10, _('TOTAL SERVICIOS')) - worksheet.write(line, 11, total_service_amount, - xls_cell_format_money) - line += 1 - worksheet.write(line, 10, _('TOTAL')) - worksheet.write(line, 11 , total_reservation_amount + total_service_amount, - xls_cell_format_money) - - worksheet = workbook.add_worksheet(_('Call Center Report - Cancelations')) - - worksheet.write('A1', _('Estado'), xls_cell_format_header) - worksheet.write('B1', _('Ficha'), xls_cell_format_header) - worksheet.write('C1', _('Fecha de Pedido'), xls_cell_format_header) - worksheet.write('D1', _('Cliente'), xls_cell_format_header) - worksheet.write('E1', _('Producto'), xls_cell_format_header) - worksheet.write('F1', _('Noches/Uds'), xls_cell_format_header) - worksheet.write('G1', _('Adultos'), xls_cell_format_header) - worksheet.write('H1', _('Checkin'), xls_cell_format_header) - worksheet.write('I1', _('In-Hora'), xls_cell_format_header) - worksheet.write('J1', _('Checkout'), xls_cell_format_header) - worksheet.write('K1', _('Creado por'), xls_cell_format_header) - worksheet.write('K1', _('Cancelado en'), xls_cell_format_header) - worksheet.write('L1', _('Precio Final'), xls_cell_format_header) - worksheet.write('M1', _('Precio Original'), xls_cell_format_header) - - worksheet.set_column('B:B', 20) - worksheet.set_column('C:C', 20) - worksheet.set_column('D:D', 20) - worksheet.set_column('E:E', 20) - worksheet.set_column('F:F', 13) - - reservations_obj = self.env['hotel.reservation'] - reservations = reservations_obj.search([ - ('last_updated_res', '>=', self.date_start), - ('last_updated_res', '<=', self.date_end), - ('channel_type','=','call'), - ('state','=','cancelled'), - ]) - offset = 1 - total_reservation_amount = 0.0 - for k_res, v_res in enumerate(reservations): - checkin_date = datetime.strptime(v_res.checkin, DEFAULT_SERVER_DATE_FORMAT) - checkout_date = datetime.strptime(v_res.checkout, DEFAULT_SERVER_DATE_FORMAT) - worksheet.write(k_res+offset, 0, v_res.state) - worksheet.write(k_res+offset, 1, v_res.folio_id.name) - worksheet.write(k_res+offset, 2, v_res.folio_id.date_order, - xls_cell_format_date) - worksheet.write(k_res+offset, 3, v_res.partner_id.name) - worksheet.write(k_res+offset, 4, v_res.room_type_id.name) - worksheet.write(k_res+offset, 5, v_res.nights) - worksheet.write(k_res+offset, 6, v_res.adults) - worksheet.write(k_res+offset, 7, checkin_date.strftime(date_format), - xls_cell_format_date) - worksheet.write(k_res+offset, 8, v_res.arrival_hour) - worksheet.write(k_res+offset, 9, checkout_date.strftime(date_format), - xls_cell_format_date) - worksheet.write(k_res+offset, 10, v_res.create_uid.name) - worksheet.write(k_res+offset, 9, v_res.last_updated_res, - xls_cell_format_date) - worksheet.write(k_res+offset, 11, v_res.price_total, - xls_cell_format_money) - worksheet.write(k_res+offset, 12, v_res.price_total - v_res.discount, - xls_cell_format_money) - total_reservation_amount += v_res.price_total - - offset += len(reservations) - - #~ if total_reservation_amount == 0 and total_service_amount == 0: - #~ raise UserError(_('No Hay reservas de Call Center')) - line = offset - if k_line: - line = k_line + offset - if total_reservation_amount > 0: - line += 1 - worksheet.write(line, 11, _('TOTAL RESERVAS')) - worksheet.write(line, 12, total_reservation_amount, - xls_cell_format_money) - - workbook.close() - file_data.seek(0) - tnow = fields.Datetime.now().replace(' ', '_') - return { - 'xls_filename': 'call_%s.xlsx' %self.env.user.company_id.property_name, - 'xls_binary': base64.encodestring(file_data.read()), - } - - def export(self): - self.write(self._export()) - return { - "type": "ir.actions.do_nothing", - } diff --git a/call_center_report/wizard/call_center_report.xml b/call_center_report/wizard/call_center_report.xml deleted file mode 100644 index 598a137d1..000000000 --- a/call_center_report/wizard/call_center_report.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - call.center.report.wizard - call.center.report.wizard - -
- - - - - - - - - - - - - - -
-
-
-
-
- - - Call Center Report Wizard - ir.actions.act_window - call.center.report.wizard - - form - new - - -
diff --git a/cash_daily_report/README.rst b/cash_daily_report/README.rst deleted file mode 100644 index be9027c75..000000000 --- a/cash_daily_report/README.rst +++ /dev/null @@ -1,13 +0,0 @@ -CASH DAILY REPORT -============= - -Export payments report in xls format - - -Credits -======= - -Creator ------------- - -* Alexandre Díaz diff --git a/cash_daily_report/__init__.py b/cash_daily_report/__init__.py deleted file mode 100644 index 351d1ee57..000000000 --- a/cash_daily_report/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018 Alexandre Díaz -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from . import wizard diff --git a/cash_daily_report/__manifest__.py b/cash_daily_report/__manifest__.py deleted file mode 100644 index 33c4fc38a..000000000 --- a/cash_daily_report/__manifest__.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018 Alexandre Díaz -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## - -{ - 'name': 'Cash Daily Report', - 'version': '1.0', - 'author': "Alexandre Díaz ", - 'website': 'https://www.eiqui.com', - 'category': 'reports', - 'summary': "Export payments report in xls format", - 'description': "Cash Daily Report", - 'depends': [ - 'account', - 'account_payment_return', - 'hotel', - ], - 'external_dependencies': { - 'python': ['xlsxwriter'] - }, - 'data': [ - 'wizard/cash_daily_report.xml', - 'data/menus.xml', - 'data/cron_jobs.xml', - ], - 'qweb': [], - 'test': [ - ], - - 'installable': True, - 'auto_install': False, - 'application': False, - 'license': 'AGPL-3', -} diff --git a/cash_daily_report/data/cron_jobs.xml b/cash_daily_report/data/cron_jobs.xml deleted file mode 100644 index bb556d2f2..000000000 --- a/cash_daily_report/data/cron_jobs.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - Automatic Period Lock Date - 1 - - days - -1 - - code - - - model.automatic_period_lock_date() - - - - - - diff --git a/cash_daily_report/data/menus.xml b/cash_daily_report/data/menus.xml deleted file mode 100644 index ef93963a4..000000000 --- a/cash_daily_report/data/menus.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - Internal Transfers - account.payment - tree,kanban,form,graph - {'default_payment_type': 'transfer', 'search_default_transfers_filter': 1} - [] - - -

- Click to register a payment -

- Payments are used to register liquidity movements (send, collect or transfer money). - You can then process those payments by your own means or by using installed facilities. -

-
-
- - - -
diff --git a/cash_daily_report/i18n/es.po b/cash_daily_report/i18n/es.po deleted file mode 100644 index 9b4a07faa..000000000 --- a/cash_daily_report/i18n/es.po +++ /dev/null @@ -1,203 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * cash_daily_report -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 11.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-08-02 10:07+0000\n" -"PO-Revision-Date: 2019-08-02 10:07+0000\n" -"Last-Translator: <>\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:85 -#, python-format -msgid "Amount" -msgstr "Importe" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:78 -#: model:ir.ui.view,arch_db:cash_daily_report.view_cash_daily_report_wizard -#, python-format -msgid "Cash Daily Report" -msgstr "Informe de caja" - -#. module: cash_daily_report -#: model:ir.actions.act_window,name:cash_daily_report.action_open_cash_daily_report_wizard -#: model:ir.ui.menu,name:cash_daily_report.cash_daily_report_wizard -msgid "Cash Daily Report Wizard" -msgstr "Informe de caja diaria" - -#. module: cash_daily_report -#: model:ir.actions.act_window,help:cash_daily_report.action_account_payments_internal -msgid "Click to register a payment" -msgstr "Pulse para registrar un pago" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:82 -#, python-format -msgid "Client/Supplier" -msgstr "Client/Supplier" - -#. module: cash_daily_report -#: model:ir.ui.view,arch_db:cash_daily_report.view_cash_daily_report_wizard -msgid "Close" -msgstr "Cerrar" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_create_uid -msgid "Created by" -msgstr "Creado por" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_create_date -msgid "Created on" -msgstr "Creado en" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:83 -#, python-format -msgid "Date" -msgstr "Fecha" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_display_name -msgid "Display Name" -msgstr "Nombre mostrado" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_date_end -msgid "End Date" -msgstr "Fecha de finalización" - -#. module: cash_daily_report -#: model:ir.ui.view,arch_db:cash_daily_report.view_cash_daily_report_wizard -msgid "Generate XLS" -msgstr "Generar XLS" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_id -msgid "ID" -msgstr "ID (identificación)" - -#. module: cash_daily_report -#: model:ir.ui.menu,name:cash_daily_report.hotel_transfer_menu -msgid "Internal Transfer" -msgstr "Transferencias Internas" - -#. module: cash_daily_report -#: model:ir.actions.act_window,name:cash_daily_report.action_account_payments_internal -msgid "Internal Transfers" -msgstr "Internal Transfers" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:84 -#, python-format -msgid "Journal" -msgstr "Diario" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard___last_update -msgid "Last Modified on" -msgstr "Última modificación en" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_write_uid -msgid "Last Updated by" -msgstr "Última actualización de" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_write_date -msgid "Last Updated on" -msgstr "Última actualización en" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:80 -#, python-format -msgid "Name" -msgstr "Nombre" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:159 -#, python-format -msgid "Not Any Payments" -msgstr "No hay movimientos" - -#. module: cash_daily_report -#: model:ir.ui.menu,name:cash_daily_report.hotel_payments_menu -msgid "Payments" -msgstr "Pagos" - -#. module: cash_daily_report -#: model:ir.actions.act_window,help:cash_daily_report.action_account_payments_internal -msgid "Payments are used to register liquidity movements (send, collect or transfer money).\n" -" You can then process those payments by your own means or by using installed facilities." -msgstr "Los pagos se utilizan para registrar movimientos de liquidez (enviar, recibir o transferir dinero).\n" -"Puede procesar esos pagos por sus propios medios o utilizando los servicios instalados." - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:81 -#, python-format -msgid "Reference" -msgstr "Referencia" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_date_start -msgid "Start Date" -msgstr "Fecha de Inicio" - -#. module: cash_daily_report -#: model:ir.ui.menu,name:cash_daily_report.hotel_supplier_payment_menu -msgid "Supplier Payments" -msgstr "Pagos a Proveedor" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:216 -#, python-format -msgid "TOTAL" -msgstr "TOTAL" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:201 -#, python-format -msgid "TOTAL EXPENSES" -msgstr "TOTAL EXPENSES" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:185 -#, python-format -msgid "TOTAL PAYMENT RETURNS" -msgstr "TOTAL DEVOLUCIONES" - -#. module: cash_daily_report -#: code:addons/cash_daily_report/wizard/cash_daily_report.py:169 -#, python-format -msgid "TOTAL PAYMENTS" -msgstr "TOTAL PAGOS" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_xls_binary -msgid "Xls Binary" -msgstr "Xls Binary" - -#. module: cash_daily_report -#: model:ir.model.fields,field_description:cash_daily_report.field_cash_daily_report_wizard_xls_filename -msgid "Xls Filename" -msgstr "Xls Filename" - -#. module: cash_daily_report -#: model:ir.model,name:cash_daily_report.model_cash_daily_report_wizard -msgid "cash.daily.report.wizard" -msgstr "cash.daily.report.wizard" - -#. module: cash_daily_report -#: model:ir.ui.view,arch_db:cash_daily_report.view_cash_daily_report_wizard -msgid "or" -msgstr "o" - diff --git a/cash_daily_report/wizard/__init__.py b/cash_daily_report/wizard/__init__.py deleted file mode 100644 index 7fb535a50..000000000 --- a/cash_daily_report/wizard/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018 Alexandre Díaz -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from . import cash_daily_report diff --git a/cash_daily_report/wizard/cash_daily_report.py b/cash_daily_report/wizard/cash_daily_report.py deleted file mode 100644 index b08382146..000000000 --- a/cash_daily_report/wizard/cash_daily_report.py +++ /dev/null @@ -1,307 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018 Alexandre Díaz -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from io import BytesIO -import datetime -import xlsxwriter -import base64 -from odoo import api, fields, models, _ -from openerp.exceptions import UserError -from openerp.tools import DEFAULT_SERVER_DATE_FORMAT - - -class CashDailyReportWizard(models.TransientModel): - FILENAME = 'cash_daily_report.xls' - _name = 'cash.daily.report.wizard' - - @api.model - def automatic_period_lock_date(self): - # The secong month day close the mont previous - days = 2 - closeday = datetime.date.today().replace(day=days) - if datetime.date.today() >= closeday: - companies = self.env['res.company'].search([]) - for record in companies: - lastday = datetime.date.today().replace(day=1) + \ - datetime.timedelta(days=-1) - if record.period_lock_date != lastday: - record.write({ - 'period_lock_date': lastday - }) - - @api.model - @api.model - def _get_default_date_start(self): - return datetime.datetime.now().strftime(DEFAULT_SERVER_DATE_FORMAT) - - @api.model - def _get_default_date_end(self): - return datetime.datetime.now().strftime(DEFAULT_SERVER_DATE_FORMAT) - - date_start = fields.Date("Start Date", default=_get_default_date_start) - date_end = fields.Date("End Date", default=_get_default_date_end) - xls_filename = fields.Char() - xls_binary = fields.Binary() - - @api.model - def _export(self): - file_data = BytesIO() - workbook = xlsxwriter.Workbook(file_data, { - 'strings_to_numbers': True, - 'default_date_format': 'dd/mm/yyyy' - }) - - company_id = self.env.user.company_id - workbook.set_properties({ - 'title': 'Exported data from ' + company_id.name, - 'subject': 'Payments Data from Odoo of ' + company_id.name, - 'author': 'Odoo', - 'manager': u'Alexandre Díaz Cuadrado', - 'company': company_id.name, - 'category': 'Hoja de Calculo', - 'keywords': 'payments, odoo, data, ' + company_id.name, - 'comments': 'Created with Python in Odoo and XlsxWriter'}) - workbook.use_zip64() - - xls_cell_format_date = workbook.add_format({ - 'num_format': 'dd/mm/yyyy' - }) - xls_cell_format_money = workbook.add_format({ - 'num_format': '#,##0.00' - }) - xls_cell_format_header = workbook.add_format({ - 'bg_color': '#CCCCCC' - }) - - worksheet = workbook.add_worksheet(_('Cash Daily Report')) - - worksheet.write('A1', _('Name'), xls_cell_format_header) - worksheet.write('B1', _('Reference'), xls_cell_format_header) - worksheet.write('C1', _('Client/Supplier'), xls_cell_format_header) - worksheet.write('D1', _('Date'), xls_cell_format_header) - worksheet.write('E1', _('Journal'), xls_cell_format_header) - worksheet.write('F1', _('Amount'), xls_cell_format_header) - - worksheet.set_column('C:C', 50) - worksheet.set_column('D:D', 11) - - account_payments_obj = self.env['account.payment'] - account_payments = account_payments_obj.search([ - ('payment_date', '>=', self.date_start), - ('payment_date', '<=', self.date_end), - ]) - offset = 1 - total_account_payment_amount = 0.0 - total_account_payment = 0.0 - total_account_expenses = 0.0 - payment_journals = {} - expense_journals = {} - total_dates = {} - for k_payment, v_payment in enumerate(account_payments): - where = v_payment.partner_id.name - amount = v_payment.amount if v_payment.payment_type in ('inbound') \ - else -v_payment.amount - if v_payment.payment_type == 'transfer': - where = v_payment.destination_journal_id.name - total_account_payment += -amount - if v_payment.destination_journal_id.name not in payment_journals: - payment_journals.update({v_payment.destination_journal_id.name: -amount}) - else: - payment_journals[v_payment.destination_journal_id.name] += -amount - if v_payment.payment_date not in total_dates: - total_dates.update({v_payment.payment_date: {v_payment.destination_journal_id.name: -amount}}) - else: - if v_payment.destination_journal_id.name not in total_dates[v_payment.payment_date]: - total_dates[v_payment.payment_date].update({v_payment.destination_journal_id.name: -amount}) - else: - total_dates[v_payment.payment_date][v_payment.destination_journal_id.name] += -amount - if amount < 0: - total_account_expenses += -amount - if v_payment.journal_id.name not in expense_journals: - expense_journals.update({v_payment.journal_id.name: amount}) - else: - expense_journals[v_payment.journal_id.name] += amount - if v_payment.payment_date not in total_dates: - total_dates.update({v_payment.payment_date: {v_payment.journal_id.name: amount}}) - else: - if v_payment.journal_id.name not in total_dates[v_payment.payment_date]: - total_dates[v_payment.payment_date].update({v_payment.journal_id.name: amount}) - else: - total_dates[v_payment.payment_date][v_payment.journal_id.name] += amount - else: - total_account_payment += amount - if v_payment.journal_id.name not in payment_journals: - payment_journals.update({v_payment.journal_id.name: amount}) - else: - payment_journals[v_payment.journal_id.name] += amount - if v_payment.payment_date not in total_dates: - total_dates.update({v_payment.payment_date: {v_payment.journal_id.name: amount}}) - else: - if v_payment.journal_id.name not in total_dates[v_payment.payment_date]: - total_dates[v_payment.payment_date].update({v_payment.journal_id.name: amount}) - else: - total_dates[v_payment.payment_date][v_payment.journal_id.name] += amount - - worksheet.write(k_payment+offset, 0, v_payment.create_uid.login) - worksheet.write(k_payment+offset, 1, v_payment.communication) - worksheet.write(k_payment+offset, 2, where) - worksheet.write(k_payment+offset, 3, v_payment.payment_date, - xls_cell_format_date) - worksheet.write(k_payment+offset, 4, v_payment.journal_id.name) - worksheet.write(k_payment+offset, 5, amount, - xls_cell_format_money) - total_account_payment_amount += amount - - payment_returns_obj = self.env['payment.return'] - payment_returns = payment_returns_obj.search([ - ('date', '>=', self.date_start), - ('date', '<=', self.date_end), - ]) - offset += len(account_payments) - total_payment_returns_amount = k_line = 0.0 - return_journals = {} - for k_payment, v_payment in enumerate(payment_returns): - for k_line, v_line in enumerate(v_payment.line_ids): - if v_payment.journal_id.name not in return_journals: - return_journals.update({v_payment.journal_id.name: -v_line.amount}) - else: - return_journals[v_payment.journal_id.name] += -v_line.amount - - if v_payment.date not in total_dates: - total_dates.update({v_payment.date: {v_payment.journal_id.name: -v_line.amount}}) - else: - if v_payment.journal_id.name not in total_dates[v_payment.date]: - total_dates[v_payment.date].update({v_payment.journal_id.name: -v_line.amount}) - else: - total_dates[v_payment.date][v_payment.journal_id.name] += -v_line.amount - - worksheet.write(k_line+offset, 0, v_payment.create_uid.login) - worksheet.write(k_line+offset, 1, v_line.reference) - worksheet.write(k_line+offset, 2, v_line.partner_id.name) - worksheet.write(k_line+offset, 3, v_payment.date, - xls_cell_format_date) - worksheet.write(k_line+offset, 4, v_payment.journal_id.name) - worksheet.write(k_line+offset, 5, -v_line.amount, - xls_cell_format_money) - total_payment_returns_amount += -v_line.amount - offset += len(v_payment.line_ids) - if total_account_payment_amount == 0 and total_payment_returns_amount == 0: - raise UserError(_('Not Any Payments')) - line = offset - if k_line: - line = k_line + offset - - result_journals = {} - # NORMAL PAYMENTS - if total_account_payment != 0: - line += 1 - worksheet.write(line, 4, _('TOTAL PAYMENTS'), xls_cell_format_header) - worksheet.write(line, 5, total_account_payment, - xls_cell_format_header) - for journal in payment_journals: - line += 1 - worksheet.write(line, 4, _(journal)) - worksheet.write(line, 5, payment_journals[journal], - xls_cell_format_money) - if journal not in result_journals: - result_journals.update({journal: payment_journals[journal]}) - else: - result_journals[journal] += payment_journals[journal] - - # RETURNS - if total_payment_returns_amount != 0: - line += 1 - worksheet.write(line, 4, _('TOTAL PAYMENT RETURNS'), xls_cell_format_header) - worksheet.write(line, 5, total_payment_returns_amount, - xls_cell_format_header) - for journal in return_journals: - line += 1 - worksheet.write(line, 4, _(journal)) - worksheet.write(line, 5, return_journals[journal], - xls_cell_format_money) - if journal not in result_journals: - result_journals.update({journal: return_journals[journal]}) - else: - result_journals[journal] += return_journals[journal] - - # EXPENSES - if total_account_expenses != 0: - line += 1 - worksheet.write(line, 4, _('TOTAL EXPENSES'), xls_cell_format_header) - worksheet.write(line, 5, -total_account_expenses, - xls_cell_format_header) - for journal in expense_journals: - line += 1 - worksheet.write(line, 4, _(journal)) - worksheet.write(line, 5, -expense_journals[journal], - xls_cell_format_money) - if journal not in result_journals: - result_journals.update({journal: expense_journals[journal]}) - else: - result_journals[journal] += expense_journals[journal] - - #TOTALS - line += 1 - worksheet.write(line, 4, _('TOTAL'), xls_cell_format_header) - worksheet.write( - line, - 5, - total_account_payment + total_payment_returns_amount - total_account_expenses, - xls_cell_format_header) - for journal in result_journals: - line += 1 - worksheet.write(line, 4, _(journal)) - worksheet.write(line, 5, result_journals[journal], - xls_cell_format_money) - - worksheet = workbook.add_worksheet(_('Por dia')) - worksheet.write('A1', _('Date'), xls_cell_format_header) - columns = ('B1','C1','D1','E1','F1','G1','H1') - i = 0 - column_journal = {} - for journal in result_journals: - worksheet.write(columns[i], _(journal), xls_cell_format_header) - i += 1 - column_journal.update({journal: i}) - - worksheet.set_column('C:C', 50) - worksheet.set_column('D:D', 11) - - offset = 1 - total_dates = sorted(total_dates.items(), key=lambda x: x[0]) - for k_day, v_day in enumerate(total_dates): - worksheet.write(k_day+offset, 0, v_day[0]) - for journal in v_day[1]: - worksheet.write(k_day+offset, column_journal[journal], v_day[1][journal]) - - workbook.close() - file_data.seek(0) - tnow = fields.Datetime.now().replace(' ', '_') - return { - 'xls_filename': 'cash_daily_report_%s.xlsx' % tnow, - 'xls_binary': base64.encodestring(file_data.read()), - } - - - def export(self): - self.write(self._export()) - return { - "type": "ir.actions.do_nothing", - } diff --git a/cash_daily_report/wizard/cash_daily_report.xml b/cash_daily_report/wizard/cash_daily_report.xml deleted file mode 100644 index caab7f860..000000000 --- a/cash_daily_report/wizard/cash_daily_report.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - cash.daily.report.wizard - cash.daily.report.wizard - -
- - - - - - - - - - - - - - -
-
-
-
-
- - - Cash Daily Report Wizard - ir.actions.act_window - cash.daily.report.wizard - - form - new - - -
diff --git a/glasof_exporter/README.rst b/glasof_exporter/README.rst deleted file mode 100644 index 58b7e6f48..000000000 --- a/glasof_exporter/README.rst +++ /dev/null @@ -1,15 +0,0 @@ -GLASOF EXPORTER -============= - -** UNDER DEVELOPMENT: NOT USE IN PRODUCTION ** - -Export Odoo data to glasof xls format - - -Credits -======= - -Creator ------------- - -* Alexandre Díaz diff --git a/glasof_exporter/__init__.py b/glasof_exporter/__init__.py deleted file mode 100644 index 351d1ee57..000000000 --- a/glasof_exporter/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018 Alexandre Díaz -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from . import wizard diff --git a/glasof_exporter/__manifest__.py b/glasof_exporter/__manifest__.py deleted file mode 100644 index 475fb102f..000000000 --- a/glasof_exporter/__manifest__.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018 Alexandre Díaz -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## - -{ - 'name': 'Glasof Exporter', - 'version': '1.0', - 'author': "Alexandre Díaz ", - 'website': 'https://www.eiqui.com', - 'category': 'hotel/glasof', - 'summary': "Export Odoo Data to xls compatible with Glasof", - 'description': "Glasof Exporter", - 'depends': [ - 'account', - ], - 'external_dependencies': { - 'python': ['xlsxwriter'] - }, - 'data': [ - 'wizard/glasof_wizard.xml', - 'data/menus.xml', - ], - 'qweb': [], - 'test': [ - ], - - 'installable': True, - 'auto_install': False, - 'application': False, - 'license': 'AGPL-3', -} diff --git a/glasof_exporter/data/menus.xml b/glasof_exporter/data/menus.xml deleted file mode 100644 index 7b791eeaf..000000000 --- a/glasof_exporter/data/menus.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - diff --git a/glasof_exporter/wizard/__init__.py b/glasof_exporter/wizard/__init__.py deleted file mode 100644 index 2b033f952..000000000 --- a/glasof_exporter/wizard/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018 Alexandre Díaz -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from . import glasof_wizard diff --git a/glasof_exporter/wizard/glasof_wizard.py b/glasof_exporter/wizard/glasof_wizard.py deleted file mode 100644 index 1e9ddc12d..000000000 --- a/glasof_exporter/wizard/glasof_wizard.py +++ /dev/null @@ -1,256 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018 Alexandre Díaz -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from io import BytesIO -import xlsxwriter -import base64 -from odoo import api, fields, models, _ - - -class GlassofExporterWizard(models.TransientModel): - FILENAME = 'invoices_glasof.xls' - _name = 'glasof.exporter.wizard' - - date_start = fields.Date("Start Date") - date_end = fields.Date("End Date") - export_journals = fields.Boolean("Export Account Movements?", default=True) - export_invoices = fields.Boolean("Export Invoices?", default=True) - seat_num = fields.Integer("Seat Number Start", default=1) - xls_journals_filename = fields.Char() - xls_journals_binary = fields.Binary() - xls_invoices_filename = fields.Char() - xls_invoices_binary = fields.Binary() - - @api.model - def _export_journals(self): - file_data = BytesIO() - workbook = xlsxwriter.Workbook(file_data, { - 'strings_to_numbers': True, - 'default_date_format': 'dd/mm/yyyy' - }) - - company_id = self.env.user.company_id - workbook.set_properties({ - 'title': 'Exported data from ' + company_id.name, - 'subject': 'PMS Data from Odoo of ' + company_id.name, - 'author': 'Odoo ALDA PMS', - 'manager': 'Jose Luis Algara', - 'company': company_id.name, - 'category': 'Hoja de Calculo', - 'keywords': 'pms, odoo, alda, data, ' + company_id.name, - 'comments': 'Created with Python in Odoo and XlsxWriter'}) - workbook.use_zip64() - - xls_cell_format_seat = workbook.add_format({'num_format': '#'}) - xls_cell_format_date = workbook.add_format({ - 'num_format': 'dd/mm/yyyy' - }) - xls_cell_format_saccount = workbook.add_format({ - 'num_format': '000000' - }) - xls_cell_format_money = workbook.add_format({ - 'num_format': '#,##0.00' - }) - xls_cell_format_header = workbook.add_format({ - 'bg_color': '#CCCCCC' - }) - - worksheet = workbook.add_worksheet('Simples-1') - - worksheet.write('A1', _('Seat'), xls_cell_format_header) - worksheet.write('B1', _('Date'), xls_cell_format_header) - worksheet.write('C1', _('SubAccount'), xls_cell_format_header) - worksheet.write('D1', _('Description'), xls_cell_format_header) - worksheet.write('E1', _('Concept'), xls_cell_format_header) - worksheet.write('F1', _('Debit'), xls_cell_format_header) - worksheet.write('G1', _('Credit'), xls_cell_format_header) - worksheet.write('H1', _('Seat Type'), xls_cell_format_header) - - worksheet.set_column('B:B', 11) - worksheet.set_column('E:E', 50) - - account_move_obj = self.env['account.move'] - account_moves = account_move_obj.search([ - ('date', '>=', self.date_start), - ('date', '<=', self.date_end), - ]) - start_seat = self.seat_num - nrow = 1 - for move in account_moves: - nmove = True - for line in move.line_ids: - if line.journal_id.type in ('cash', 'bank'): - worksheet.write(nrow, 0, nmove and start_seat or '', - xls_cell_format_seat) - worksheet.write(nrow, 1, nmove and line.date or '', - xls_cell_format_date) - worksheet.write(nrow, 2, line.account_id.code, - xls_cell_format_saccount) - worksheet.write(nrow, 3, '') - worksheet.write(nrow, 4, line.ref and line.ref[:50] or '') - worksheet.write(nrow, 5, line.debit, xls_cell_format_money) - worksheet.write(nrow, 6, line.credit, - xls_cell_format_money) - worksheet.write(nrow, 7, '') - nmove = False - nrow += 1 - start_seat += 1 - - workbook.close() - file_data.seek(0) - tnow = fields.Datetime.now().replace(' ', '_') - return { - 'xls_journals_filename': 'journals_glasof_%s.xlsx' % tnow, - 'xls_journals_binary': base64.encodestring(file_data.read()), - } - - @api.model - def _export_invoices(self): - file_data = BytesIO() - workbook = xlsxwriter.Workbook(file_data, { - 'strings_to_numbers': True, - 'default_date_format': 'dd/mm/yyyy' - }) - - company_id = self.env.user.company_id - workbook.set_properties({ - 'title': 'Exported data from ' + company_id.name, - 'subject': 'PMS Data from Odoo of ' + company_id.name, - 'author': 'Odoo ALDA PMS', - 'manager': 'Jose Luis Algara', - 'company': company_id.name, - 'category': 'Hoja de Calculo', - 'keywords': 'pms, odoo, alda, data, ' + company_id.name, - 'comments': 'Created with Python in Odoo and XlsxWriter'}) - workbook.use_zip64() - - xls_cell_format_seat = workbook.add_format({'num_format': '#'}) - xls_cell_format_date = workbook.add_format({ - 'num_format': 'dd/mm/yyyy' - }) - xls_cell_format_saccount = workbook.add_format({ - 'num_format': '000000' - }) - xls_cell_format_money = workbook.add_format({ - 'num_format': '#,##0.00' - }) - xls_cell_format_odec = workbook.add_format({ - 'num_format': '#,#0.0' - }) - xls_cell_format_header = workbook.add_format({ - 'bg_color': '#CCCCCC' - }) - - worksheet = workbook.add_worksheet('ventas') - - account_inv_obj = self.env['account.invoice'] - account_invs = account_inv_obj.search([ - ('date', '>=', self.date_start), - ('date', '<=', self.date_end), - ]) - - nrow = 1 - for inv in account_invs: - if inv.partner_id.parent_id: - firstname = inv.partner_id.parent_id.firstname or '' - lastname = inv.partner_id.parent_id.lastname or '' - else: - firstname = inv.partner_id.firstname or '' - lastname = inv.partner_id.lastname or '' - - worksheet.write(nrow, 0, inv.number) - worksheet.write(nrow, 1, inv.date_invoice, xls_cell_format_date) - worksheet.write(nrow, 2, '') - worksheet.write(nrow, 3, inv.partner_id.vat and - inv.partner_id.vat[:2] or '') - worksheet.write(nrow, 4, inv.partner_id.vat and - inv.partner_id.vat[2:] or '') - worksheet.write(nrow, 5, lastname) - worksheet.write(nrow, 6, '') - worksheet.write(nrow, 7, firstname) - worksheet.write(nrow, 8, 705.0, xls_cell_format_odec) - worksheet.write(nrow, 9, inv.amount_untaxed, xls_cell_format_money) - if any(inv.tax_line_ids): - worksheet.write(nrow, - 10, - inv.tax_line_ids[0].tax_id.amount, - xls_cell_format_money) - else: - worksheet.write(nrow, 10, '') - worksheet.write(nrow, 11, inv.tax_line_ids and - inv.tax_line_ids[0].amount or '', - xls_cell_format_money) - worksheet.write(nrow, 12, '') - worksheet.write(nrow, 13, '') - worksheet.write(nrow, 14, '') - worksheet.write(nrow, 15, '') - worksheet.write(nrow, 16, '') - worksheet.write(nrow, 17, '') - worksheet.write(nrow, 18, '') - worksheet.write(nrow, 19, '') - worksheet.write(nrow, 20, '') - worksheet.write(nrow, 21, 'S') - worksheet.write(nrow, 22, '') - if inv.type == 'out_refund': - worksheet.write(nrow, 23, inv.invoice_origin) - else: - worksheet.write(nrow, 23, '') - worksheet.write(nrow, 24, '') - worksheet.write(nrow, 25, '') - worksheet.write(nrow, 27, '') - worksheet.write(nrow, 28, '') - worksheet.write(nrow, 29, '') - worksheet.write(nrow, 30, '') - worksheet.write(nrow, 31, '') - worksheet.write(nrow, 32, '') - worksheet.write(nrow, 33, '') - worksheet.write(nrow, 34, '') - worksheet.write(nrow, 35, '') - worksheet.write(nrow, 36, '') - worksheet.write(nrow, 37, '') - worksheet.write(nrow, 38, '') - worksheet.write(nrow, 39, '') - worksheet.write(nrow, 40, '') - worksheet.write(nrow, 41, '') - worksheet.write(nrow, 42, '') - worksheet.write(nrow, 43, '430') - nrow += 1 - - workbook.add_worksheet('compras') - workbook.close() - file_data.seek(0) - tnow = fields.Datetime.now().replace(' ', '_') - return { - 'xls_invoices_filename': 'invoices_glasof_%s.xlsx' % tnow, - 'xls_invoices_binary': base64.encodestring(file_data.read()), - } - - - def export(self): - towrite = {} - if self.export_journals: - towrite.update(self._export_journals()) - if self.export_invoices: - towrite.update(self._export_invoices()) - if any(towrite): - self.write(towrite) - return { - "type": "ir.actions.do_nothing", - } diff --git a/glasof_exporter/wizard/glasof_wizard.xml b/glasof_exporter/wizard/glasof_wizard.xml deleted file mode 100644 index 4b0939968..000000000 --- a/glasof_exporter/wizard/glasof_wizard.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - glasof.exporter.wizard - glasof.exporter.wizard - -
- - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
- - - Export to Glasof - ir.actions.act_window - glasof.exporter.wizard - - form - new - - -
diff --git a/hotel_calendar/README.rst b/hotel_calendar/README.rst deleted file mode 100644 index 9a77798c3..000000000 --- a/hotel_calendar/README.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. This file is going to be generated by oca-gen-addon-readme. Manual changes will be overwritten. - -HOTEL CALENDAR -============== -This module allows you to handle reservations, prices and restriction plans in a user friendly calendar view. - -Description ------------- -This module extends the functionality of roomdoo. - -The calendar displays your rooms in a range of dates. -The rooms can be filtered by class, type or even amenities creating new calendars for easily manage all your reservations. -You can manage prices and restrictions day by day using the calendar management view. - -This module adds two new view types: ``pms`` and ``mpms``. -The module also incorporates a longpolling for delivering instant notification when using the calendar. - -Installation ------------- -This module depends on modules ``hotel``, ``bus``, ``web``, ``calendar``, and ``web_widget_color``. -Ensure yourself to have all them in your addons list. - -Configuration -------------- -No action required. The module is pre-configured with default values. - -You will find the hotel calendar settings in `Settings > Users & Companies > Hotels > Your Hotel > Calendar Settings Tab`. - -Reservation colors can be configured by company in `Settings > Users & Companies > Companies > Your Company > Hotel Preferences Tab.` - -Usage ------ -To use this module, you need to: - -* Go to Hotel Calendar menu for managing reservations. -* Go to Hotel Calendar Management for managing prices and restrictions. -* Go to Hotel Management/Configuration/Calendars menu for managing calendar tabs. - -Shortcuts -_________ -* ``CTRL + LEFT MOUSE CLICK`` on a reservation - start reservation swap -* ``ESC`` - cancel active action (ie. swap) - - -Credits -------- - -Authors -_______ -- Alexandre Díaz - -Contributors -____________ -* Pablo Quesada - -Roadmap -------- -* [ ] Implement the calendar view as an Odoo native view type. -* [ ] Re-design the calendar using SVG or other Odoo native approach. - -Do you want to contribute? Please, do not hesitate to contact any of the authors or contributors. \ No newline at end of file diff --git a/hotel_calendar/__init__.py b/hotel_calendar/__init__.py deleted file mode 100644 index bc60815fd..000000000 --- a/hotel_calendar/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from . import models -from . import controllers diff --git a/hotel_calendar/__manifest__.py b/hotel_calendar/__manifest__.py deleted file mode 100644 index 3008030fa..000000000 --- a/hotel_calendar/__manifest__.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -{ - 'name': 'Roomdoo Calendar', - 'summary': 'A calendar view for user friendly handling your roomdoo property.', - 'version': '11.0.2.0', - 'development_status': 'Beta', - 'category': 'Generic Modules/Hotel Management', - 'website': 'https://github.com/hootel/hootel', - 'author': 'Alexandre Díaz ', - 'license': "AGPL-3", - 'application': False, - 'installable': True, - 'depends': [ - 'bus', - 'web', - 'calendar', - 'hotel', - 'web_widget_color', - ], - # 'external_dependencies': { - # 'python': [] - # }, - 'data': [ - 'views/general.xml', - 'views/actions.xml', - 'views/inherited_hotel_property_views.xml', - 'views/inherited_res_company_views.xml', - 'views/inherited_res_users_views.xml', - 'views/hotel_reservation_views.xml', - 'views/hotel_calendar_management_views.xml', - 'views/hotel_calendar_views.xml', - 'data/menus.xml', - 'security/ir.model.access.csv', - ], - 'qweb': [ - 'static/src/xml/hotel_calendar_management_view.xml', - 'static/src/xml/hotel_calendar_templates.xml', - 'static/src/xml/hotel_calendar_view.xml', - ], -} diff --git a/hotel_calendar/controllers/__init__.py b/hotel_calendar/controllers/__init__.py deleted file mode 100644 index de7342fae..000000000 --- a/hotel_calendar/controllers/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from . import bus diff --git a/hotel_calendar/controllers/bus.py b/hotel_calendar/controllers/bus.py deleted file mode 100644 index ebb36b2e8..000000000 --- a/hotel_calendar/controllers/bus.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo.http import request -from odoo.addons.bus.controllers.main import BusController - -HOTEL_BUS_CHANNEL_ID = 'hpublic' - - -# More info... -# https://github.com/odoo/odoo/commit/092cf33f93830daf5e704b964724bdf8586da8d9 -class Controller(BusController): - def _poll(self, dbname, channels, last, options): - if request.session.uid: - # registry, cr, uid, context = request.registry, request.cr, \ - # request.session.uid, request.context - channels = channels + [( - request.db, - 'hotel.reservation', - HOTEL_BUS_CHANNEL_ID - )] - return super(Controller, self)._poll(dbname, channels, last, options) diff --git a/hotel_calendar/data/menus.xml b/hotel_calendar/data/menus.xml deleted file mode 100644 index 9860b6d56..000000000 --- a/hotel_calendar/data/menus.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - Hotel Calendar - hotel.reservation - pms - - - - Hotel Calendar Management - hotel.calendar.management - mpms - - - - - - - - - diff --git a/hotel_calendar/i18n/es.po b/hotel_calendar/i18n/es.po deleted file mode 100644 index 110018e03..000000000 --- a/hotel_calendar/i18n/es.po +++ /dev/null @@ -1,1383 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * hotel_calendar -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 11.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-03-09 18:34+0000\n" -"PO-Revision-Date: 2019-03-10 10:05+0100\n" -"Last-Translator: <>\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"Language: es\n" -"X-Generator: Poedit 1.8.7.1\n" - -#. module: hotel_calendar -#: selection:res.users,npms_end_day_week_offset:0 -#: selection:res.users,pms_end_day_week_offset:0 -msgid "0 Days" -msgstr "0 Días" - -#. module: hotel_calendar -#: selection:res.users,npms_end_day_week_offset:0 -#: selection:res.users,pms_end_day_week_offset:0 -msgid "1 Days" -msgstr "1 Día" - -#. module: hotel_calendar -#: selection:res.users,npms_default_num_days:0 -#: selection:res.users,pms_default_num_days:0 -msgid "1 Month" -msgstr "1 Mes" - -#. module: hotel_calendar -#: selection:res.users,npms_default_num_days:0 -#: selection:res.users,pms_default_num_days:0 -msgid "1 Week" -msgstr "1 Semana" - -#. module: hotel_calendar -#: selection:res.users,npms_end_day_week_offset:0 -#: selection:res.users,pms_end_day_week_offset:0 -msgid "2 Days" -msgstr "2 Días" - -#. module: hotel_calendar -#: selection:res.users,npms_default_num_days:0 -#: selection:res.users,pms_default_num_days:0 -msgid "2 Weeks" -msgstr "2 Semanas" - -#. module: hotel_calendar -#: selection:res.users,npms_end_day_week_offset:0 -#: selection:res.users,pms_end_day_week_offset:0 -msgid "3 Days" -msgstr "3 Días" - -#. module: hotel_calendar -#: selection:res.users,npms_default_num_days:0 -#: selection:res.users,pms_default_num_days:0 -msgid "3 Weeks" -msgstr "3 Semanas" - -#. module: hotel_calendar -#: selection:res.users,npms_end_day_week_offset:0 -#: selection:res.users,pms_end_day_week_offset:0 -msgid "4 Days" -msgstr "4 Días" - -#. module: hotel_calendar -#: selection:res.users,npms_end_day_week_offset:0 -#: selection:res.users,pms_end_day_week_offset:0 -msgid "5 Days" -msgstr "5 Días" - -#. module: hotel_calendar -#: selection:res.users,npms_end_day_week_offset:0 -#: selection:res.users,pms_end_day_week_offset:0 -msgid "6 Days" -msgstr "6 Días" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:21 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:43 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:134 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:262 -#, python-format -msgid "Adults:" -msgstr "Adultos:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_management_view.xml:49 -#, python-format -msgid "All" -msgstr "Todo" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_npms_allowed_events_tags -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_pms_allowed_events_tags -msgid "Allow Calander Event Tags" -msgstr "Permitir Eventos etiquetados en el Calendario" - -#. module: hotel_calendar -#: selection:res.users,pms_type_move:0 -msgid "Allow Invalid" -msgstr "Permitir Inválidas" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_npms_end_day_week_offset -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_pms_end_day_week_offset -msgid "Also illuminate the previous" -msgstr "Sombrear también los previos" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_amenity_ids -msgid "Amenity" -msgstr "Característica" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:59 -#, python-format -msgid "Are you sure you want to change these prices?" -msgstr "Estás seguro de que quieres modificar estos precios?" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:53 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:67 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:81 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:89 -#, python-format -msgid "Are you sure you want to make this changes?" -msgstr "¿Estás seguo de que quieres confirmar estos cambios?" - -#. module: hotel_calendar -#: selection:res.users,pms_type_move:0 -msgid "Assisted" -msgstr "Asistido" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:117 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:276 -#, python-format -msgid "Availability" -msgstr "Disponibilidad" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:102 -#, python-format -msgid "Books" -msgstr "Reservas" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:115 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:274 -#, python-format -msgid "C. Arrival" -msgstr "Cerrar Llegada" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:114 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:273 -#, python-format -msgid "C. Departure" -msgstr "Cerrar Salida" - -#. module: hotel_calendar -#: model:ir.ui.view,arch_db:hotel_calendar.res_users_view_form -msgid "Calendar (PMS)" -msgstr "Calendario (PMS)" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_view.js:43 -#, python-format -msgid "Calendar MPMS" -msgstr "Calendario MPMS" - -#. module: hotel_calendar -#: model:ir.ui.view,arch_db:hotel_calendar.res_users_view_form -msgid "Calendar Management (Revenue PMS)" -msgstr "Calendario de Configuración (Revenue PMS)" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_view.js:163 -#, python-format -msgid "Calendar PMS" -msgstr "Calendario (PMS)" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:365 -#, python-format -msgid "Calendar Settings" -msgstr "Configuración Calendario" - -#. module: hotel_calendar -#: model:ir.ui.menu,name:hotel_calendar.hotel_calendar_record_menu -msgid "Calendars" -msgstr "Calendarios" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:130 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:289 -#, python-format -msgid "Cancel" -msgstr "Cancelar" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:72 -#, python-format -msgid "Cancelled" -msgstr "Canceladas" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:350 -#, python-format -msgid "Check-In:" -msgstr "Entrada:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:351 -#, python-format -msgid "Check-Out:" -msgstr "Salida:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:206 -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_stay -#, python-format -msgid "Checkin" -msgstr "Checkin" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:12 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:34 -#, python-format -msgid "Checkin:" -msgstr "Entrada:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_renderer.js:241 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:35 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:105 -#, python-format -msgid "Checkins" -msgstr "Checkins" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_checkout -msgid "Checkout" -msgstr "Checkout" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:15 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:37 -#, python-format -msgid "Checkout:" -msgstr "Salida" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:26 -#, python-format -msgid "Checkouts" -msgstr "Checkouts" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:135 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:263 -#, python-format -msgid "Children:" -msgstr "Niños:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:129 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:288 -#, python-format -msgid "Clone" -msgstr "Clonar" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:113 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:272 -#, python-format -msgid "Closed" -msgstr "Cerrado" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:335 -#, python-format -msgid "Closed Arrival:" -msgstr "Cerrar Llegada:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:336 -#, python-format -msgid "Closed Departure:" -msgstr "Cerrar Salida:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:334 -#, python-format -msgid "Closed:" -msgstr "Cerrado:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:122 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:281 -#, python-format -msgid "Clousure" -msgstr "Cerrado" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_reservation_reserve_color -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_reservation_reserve_color_text -msgid "Color" -msgstr "Color" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:648 -#, python-format -msgid "Confirm Reservation Changes" -msgstr "Confirmar cambios de reserva" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:568 -#, python-format -msgid "Confirm Reservation Swap" -msgstr "Confirmar intercambio de reservas" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:516 -#, python-format -msgid "Confirm Split Reservation" -msgstr "Confirmar división de reserva" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:547 -#, python-format -msgid "Confirm Unify Reservations" -msgstr "Confirmar fusión de reservas" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_reservation -msgid "Confirmed Reservation " -msgstr "Reservas Confirmada" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:274 -#, python-format -msgid "Continue" -msgstr "Continuar" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:127 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:286 -#, python-format -msgid "Copy" -msgstr "Copar" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:768 -#, python-format -msgid "Create: " -msgstr "Creado: " - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_bus_hotel_calendar_create_uid -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_create_uid -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_management_create_uid -msgid "Created by" -msgstr "Creado por" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_bus_hotel_calendar_create_date -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_create_date -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_management_create_date -msgid "Created on" -msgstr "Creado en" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_npms_default_num_days -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_pms_default_num_days -msgid "Default number of days" -msgstr "Num. por defecto de días" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_npms_denied_events_tags -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_pms_denied_events_tags -msgid "Deny Calander Event Tags" -msgstr "Denegar Eventos etiquetados en el Calendario" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_bus_hotel_calendar_display_name -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_display_name -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_management_display_name -msgid "Display Name" -msgstr "Nombre mostrado" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:81 -#, python-format -msgid "Divide" -msgstr "Dividir" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:378 -#, python-format -msgid "Divide Rooms by Capacity" -msgstr "Dividir las filas por capacidad" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_pms_divide_rooms_by_capacity -msgid "Divide rooms by capacity" -msgstr "Dividir las filas por capacidad" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:342 -#, python-format -msgid "Do you want to confirm this folio?" -msgstr "Deseas confirmar la ficha?" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:359 -#, python-format -msgid "Do you want to save these changes?" -msgstr "Deseas confirmar la ficha?" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_dontsell -msgid "Dont Sell" -msgstr "No vender" - -#. module: hotel_calendar -#: code:addons/hotel_calendar/models/inherited_hotel_reservation.py:106 -#, python-format -msgid "Email not provided" -msgstr "Email no proporcionado" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:279 -#, python-format -msgid "End" -msgstr "Fin" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_npms_end_day_week -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_pms_end_day_week -msgid "End day of week" -msgstr "Último día de Semana" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_management_view.xml:25 -#, python-format -msgid "FROM" -msgstr "DE" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:116 -#, python-format -msgid "Filters" -msgstr "Filtros" - -#. module: hotel_calendar -#: code:addons/hotel_calendar/models/inherited_hotel_folio.py:24 -#, python-format -msgid "Folio Deleted" -msgstr "Ficha Eliminada" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:214 -#, python-format -msgid "Folio Payments" -msgstr "Pagos en Ficha" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:146 -#, python-format -msgid "Folio Pending" -msgstr "Pendiente en Ficha" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_renderer.js:247 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:111 -#, python-format -msgid "Folios" -msgstr "Fichas" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:123 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:282 -#, python-format -msgid "Free Rooms" -msgstr "Habitaciones Libres" - -#. module: hotel_calendar -#: selection:res.users,npms_end_day_week:0 -#: selection:res.users,pms_end_day_week:0 -msgid "Friday" -msgstr "Viernes" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:358 -#, python-format -msgid "Have unsaved changes!" -msgstr "Tienes cambios sin guardar!" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_renderer.js:22 -#: model:ir.actions.act_window,name:hotel_calendar.action_hotel_calendar -#: model:ir.actions.act_window,name:hotel_calendar.hotel_calendar_action_form_tree -#: model:ir.ui.menu,name:hotel_calendar.hotel_calendar_menu -#: model:ir.ui.view,arch_db:hotel_calendar.hotel_calendar_view_form -#: model:ir.ui.view,arch_db:hotel_calendar.hotel_calendar_view_tree -#, python-format -msgid "Hotel Calendar" -msgstr "Calendario del Hotel" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:22 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:57 -#: model:ir.actions.act_window,name:hotel_calendar.action_hotel_calendar_management -#: model:ir.ui.menu,name:hotel_calendar.hotel_calendar_management_menu -#, python-format -msgid "Hotel Calendar Management" -msgstr "Calendario de Configuración del Hotel" - -#. module: hotel_calendar -#: model:ir.model,name:hotel_calendar.model_hotel_folio -msgid "Hotel Folio" -msgstr "Ficha de reservas" - -#. module: hotel_calendar -#: model:ir.model,name:hotel_calendar.model_hotel_reservation -msgid "Hotel Reservation" -msgstr "Reserva del hotel" - -#. module: hotel_calendar -#: model:ir.actions.act_window,name:hotel_calendar.hotel_reservation_action_checkin -msgid "Hotel folio checkin" -msgstr "Hotel Ficha Checkin" - -#. module: hotel_calendar -#: model:ir.actions.act_window,name:hotel_calendar.hotel_reservation_action_checkout -msgid "Hotel folio checkout" -msgstr "Hotel Ficha Checkout" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_bus_hotel_calendar_id -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_id -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_management_id -msgid "ID" -msgstr "ID (identificación)" - -#. module: hotel_calendar -#: code:addons/hotel_calendar/models/hotel_calendar_management.py:198 -#: code:addons/hotel_calendar/models/inherited_hotel_reservation.py:367 -#, python-format -msgid "Input Error: No dates defined!" -msgstr "Error de entrada: ¡no hay fechas definidas!" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:592 -#, python-format -msgid "Invalid Reservation Swap" -msgstr "Intercambio no válido" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:73 -#, python-format -msgid "Invalid Swap Operation, can't make this movement :/" -msgstr "No podemos hacer la operación, movimiento no válido de reservas :/" - -#. module: hotel_calendar -#: code:addons/hotel_calendar/models/inherited_hotel_reservation.py:452 -#, python-format -msgid "Invalid swap parameters" -msgstr "Valores incorrectos para el swap" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:221 -#, python-format -msgid "Invoice Folio" -msgstr "Facturar Ficha" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_renderer.js:244 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:108 -#, python-format -msgid "Invoices" -msgstr "Facturas" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_bus_hotel_calendar___last_update -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar___last_update -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_management___last_update -msgid "Last Modified on" -msgstr "Última modificación en" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_bus_hotel_calendar_write_uid -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_management_write_uid -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_write_uid -msgid "Last Updated by" -msgstr "Última actualización de" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_bus_hotel_calendar_write_date -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_management_write_date -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_write_date -msgid "Last Updated on" -msgstr "Última actualización en" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_management_view.xml:62 -#, python-format -msgid "Launch Massive Changes" -msgstr "Guardar Cambios Masivos" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_letter_reservation -msgid "Letter Confirmed Reservation " -msgstr "Texto de Reserva confirmada" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_letter_pre_reservation -msgid "Letter Pre-reservation" -msgstr "Texto de Reserva sin confirmar" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_letter_stay -msgid "Letter Checkin" -msgstr "Texto de Checkin (Dentro) " - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_letter_checkout -msgid "Letter Checkout" -msgstr "Texto de Checkout (Fuera)" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_letter_dontsell -msgid "Letter Dont Sell" -msgstr "Texto de Fuera de Servicio " - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_letter_to_assign -msgid "Letter Ota to Assign" -msgstr "Texto de Por Asignar" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_letter_reservation_pay -msgid "Letter Paid Reservation" -msgstr "Texto de Reserva Pagada" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_letter_payment_pending -msgid "Letter Payment Pending" -msgstr "Texto de Pagos Pendientes" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_letter_staff -msgid "Letter Staff" -msgstr "Texto de Staff" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_letter_stay_pay -msgid "Letter Stay Pay" -msgstr "Texto de Checkin Pagado" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_location_ids -msgid "Location" -msgstr "Ubicación" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_management_view.xml:51 -#, python-format -msgid "Low" -msgstr "Bajo" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:352 -#, python-format -msgid "Made by:" -msgstr "Hecho por:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:119 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:278 -#, python-format -msgid "Max. Stay" -msgstr "Max. Estancia" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:121 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:280 -#, python-format -msgid "Max. Stay Arrival" -msgstr "Max. Stay Arrival" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:333 -#, python-format -msgid "Max. Stay Arrival:" -msgstr "Max. Est. Llegada:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:332 -#, python-format -msgid "Max. Stay:" -msgstr "Max. Estancia:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_management_view.xml:50 -#, python-format -msgid "Medium" -msgstr "Medio" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:118 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:277 -#, python-format -msgid "Min. Stay" -msgstr "Min. Estancia" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:120 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:279 -#, python-format -msgid "Min. Stay Arrival" -msgstr "Min. Estancia Llegada" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:331 -#, python-format -msgid "Min. Stay:" -msgstr "Min. Estancia:" - -#. module: hotel_calendar -#: selection:res.users,npms_end_day_week:0 -#: selection:res.users,pms_end_day_week:0 -msgid "Monday" -msgstr "Lunes" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_name -msgid "Name" -msgstr "Nombre" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:97 -#, python-format -msgid "Name, Mail, Vat, Book..." -msgstr "Nombre, Mail, NIF, Nº Reserva..." - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:348 -#, python-format -msgid "Name:" -msgstr "Nombre:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:326 -#, python-format -msgid "Nights:" -msgstr "Noches" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:527 -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:558 -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:606 -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:678 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:448 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:590 -#, python-format -msgid "No" -msgstr "No" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:124 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:283 -#, python-format -msgid "No OTA" -msgstr "No OTA" - -#. module: hotel_calendar -#: code:addons/hotel_calendar/models/inherited_hotel_reservation.py:124 -#: code:addons/hotel_calendar/models/inherited_hotel_reservation.py:431 -#, python-format -msgid "No board services" -msgstr "Sin servicios incluidos" - -#. module: hotel_calendar -#: code:addons/hotel_calendar/models/inherited_hotel_reservation.py:120 -#: code:addons/hotel_calendar/models/inherited_hotel_reservation.py:428 -#, python-format -msgid "No reason given" -msgstr "Sin motivo explícito" - -#. module: hotel_calendar -#: selection:res.users,pms_type_move:0 -msgid "Normal" -msgstr "Normal" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:595 -#, python-format -msgid "Oops, Ok!" -msgstr "Oops, Ok!" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:112 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:271 -#, python-format -msgid "Open" -msgstr "Abrir" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:125 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:284 -#, python-format -msgid "Options" -msgstr "Opciones" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_to_assign -msgid "Ota Reservation to Assign" -msgstr "Reserva por Asignar" - -#. module: hotel_calendar -#: code:addons/hotel_calendar/models/inherited_hotel_reservation.py:78 -#: code:addons/hotel_calendar/models/inherited_hotel_reservation.py:101 -#: code:addons/hotel_calendar/models/inherited_hotel_reservation.py:400 -#, python-format -msgid "Out of service" -msgstr "Fuera de Servicio" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:62 -#, python-format -msgid "Overbook." -msgstr "Overbook." - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:321 -#, python-format -msgid "Overbooking Management" -msgstr "Gestión de Overbooking" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_stay_pay -msgid "Paid Checkin" -msgstr "Checkin Pagado" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_reservation_pay -msgid "Paid Reservation" -msgstr "Reserva Pagada" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:128 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:287 -#, python-format -msgid "Paste" -msgstr "Pegar" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_payment_pending -msgid "Payment Pending" -msgstr "Pagos Pendientes" - -#. module: hotel_calendar -#: code:addons/hotel_calendar/models/inherited_hotel_reservation.py:105 -#, python-format -msgid "Phone not provided" -msgstr "Teléfono no disponible" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_pre_reservation -msgid "Pre-reservation" -msgstr "Reserva sin Confirmar" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:116 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:275 -#, python-format -msgid "Price" -msgstr "Precio" - -#. module: hotel_calendar -#: model:ir.model,name:hotel_calendar.model_product_pricelist -msgid "Pricelist" -msgstr "Tarifas" - -#. module: hotel_calendar -#: model:ir.model,name:hotel_calendar.model_product_pricelist_item -msgid "Pricelist item" -msgstr "Elemento Tarifa" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:768 -#, python-format -msgid "Reservation" -msgstr "Reserva" - -#. module: hotel_calendar -#: code:addons/hotel_calendar/models/inherited_hotel_reservation.py:486 -#, python-format -msgid "Reservation Created" -msgstr "Reserva Creada" - -#. module: hotel_calendar -#: code:addons/hotel_calendar/models/inherited_hotel_reservation.py:500 -#, python-format -msgid "Reservation Deleted" -msgstr "Reserva Eliminada" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:216 -#, python-format -msgid "Reservation Payments" -msgstr "Pagos de la reserva" - -#. module: hotel_calendar -#: model:ir.ui.view,arch_db:hotel_calendar.res_users_view_form -msgid "Reservation States Colours" -msgstr "Colores por estado de la Reserva" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_pms_type_move -msgid "Reservation move mode" -msgstr "Modo de Movimiento de Reservas" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_renderer.js:238 -#, python-format -msgid "Reservations" -msgstr "Reservas" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:31 -#, python-format -msgid "Reserve changed:" -msgstr "Reserva modificada:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:9 -#, python-format -msgid "Reserve unchanged:" -msgstr "Reserva no modificada:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:126 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:285 -#, python-format -msgid "Reset" -msgstr "Restablecer" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_room_type_ids -msgid "Room Type" -msgstr "Tipo de Habitación" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:307 -#, python-format -msgid "Room Type:" -msgstr "Tipo de Habitación:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:18 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:40 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:349 -#, python-format -msgid "Room:" -msgstr "Habitación:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:99 -#, python-format -msgid "Sales Channel:" -msgstr "Canal de Venta:" - -#. module: hotel_calendar -#: selection:res.users,npms_end_day_week:0 -#: selection:res.users,pms_end_day_week:0 -msgid "Saturday" -msgstr "Sábado" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_management_view.xml:12 -#, python-format -msgid "Save Changes" -msgstr "Guardar Cambios" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_hotel_calendar_segmentation_ids -msgid "Segmentation" -msgstr "Segmentación" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:120 -#, python-format -msgid "Select Amenities..." -msgstr "Seleccionar caraterísticas..." - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:119 -#, python-format -msgid "Select Location..." -msgstr "Selecciones ubicación..." - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:118 -#, python-format -msgid "Select Segmentation..." -msgstr "Seleccionar Segmentación..." - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:121 -#, python-format -msgid "Select Type..." -msgstr "Seleccionar Tipo..." - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:210 -#, python-format -msgid "Send Reservation Email" -msgstr "Enviar Mail de Reserva" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:377 -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_pms_show_availability -#, python-format -msgid "Show Availability" -msgstr "Mostrar Disponibilidad" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:375 -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_pms_show_notifications -#, python-format -msgid "Show Notifications" -msgstr "Mostrar Notificaciones" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_pms_show_num_rooms -msgid "Show Num. Rooms" -msgstr "Num. Habitaciones a mostrar" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:376 -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_pms_show_pricelist -#, python-format -msgid "Show Pricelist" -msgstr "Mostrar Tarifas" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:80 -#, python-format -msgid "Split Reservation" -msgstr "Dividir Reserva" - -#. module: hotel_calendar -#: model:ir.model.fields,field_description:hotel_calendar.field_res_users_color_staff -msgid "Staff" -msgstr "Staff" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:284 -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:614 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:44 -#, python-format -msgid "Start Swap" -msgstr "Comenzar Intercambio" - -#. module: hotel_calendar -#: selection:res.users,npms_end_day_week:0 -#: selection:res.users,pms_end_day_week:0 -msgid "Sunday" -msgstr "Domingo" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:66 -#, python-format -msgid "Swap Reservations" -msgstr "Intercambio reservas" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_management_view.xml:34 -#, python-format -msgid "TO" -msgstr "A" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:105 -#, python-format -msgid "TODO: add call center information" -msgstr "TODO: add call center information" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:5 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:65 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:79 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:87 -#, python-format -msgid "The following changes will be made..." -msgstr "Los próximos cambios serán realizados..." - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:6 -#, python-format -msgid "This reservation belongs to a folio with more reservations!" -msgstr "Esta reserva pertenece a un folio con más reservas!" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:233 -#, python-format -msgid "This reservation is part of splitted reservation." -msgstr "Ésta reserva es solo una parte de una reserva dividida." - -#. module: hotel_calendar -#: selection:res.users,npms_end_day_week:0 -#: selection:res.users,pms_end_day_week:0 -msgid "Thursday" -msgstr "Jueves" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:373 -#, python-format -msgid "Toggle Advance Controls" -msgstr "Mostrar Controles Avanzados (Alt+x)" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:161 -#, python-format -msgid "Total Paid" -msgstr "Total Pagado" - -#. module: hotel_calendar -#: selection:res.users,npms_end_day_week:0 -#: selection:res.users,pms_end_day_week:0 -msgid "Tuesday" -msgstr "Martes" - -#. module: hotel_calendar -#: code:addons/hotel_calendar/models/inherited_hotel_reservation.py:417 -#: code:addons/hotel_calendar/models/inherited_hotel_reservation.py:418 -#, python-format -msgid "Undefined" -msgstr "Indefinido" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:90 -#, python-format -msgid "Unify" -msgstr "Fusionar" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:88 -#, python-format -msgid "Unify Reservations" -msgstr "Fusionar Reservas" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:436 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:578 -#, python-format -msgid "Unsaved Changes!" -msgstr "Tienes cambios sin guardar!" - -#. module: hotel_calendar -#: model:ir.model,name:hotel_calendar.model_res_users -msgid "Users" -msgstr "Usuarios" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:198 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:290 -#, python-format -msgid "View Folio Details" -msgstr "Ver detalles de la Ficha" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_management_view.xml:47 -#, python-format -msgid "View Mode:" -msgstr "Modo de Visualización:" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:202 -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_templates.xml:294 -#, python-format -msgid "View Reservation Details" -msgstr "Ver detalles de la reserva" - -#. module: hotel_calendar -#: selection:res.users,npms_end_day_week:0 -#: selection:res.users,pms_end_day_week:0 -msgid "Wednesday" -msgstr "Miércoles" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/xml/hotel_calendar_view.xml:52 -#, python-format -msgid "Wizard" -msgstr "Wizard" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:651 -#, python-format -msgid "Yes, change it" -msgstr "Sí, Cambiálo!" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js:439 -#: code:addons/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v.js:581 -#, python-format -msgid "Yes, save it" -msgstr "Sí, Guardálo!" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:519 -#, python-format -msgid "Yes, split it" -msgstr "Sí, Divídela!" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:571 -#, python-format -msgid "Yes, swap it" -msgstr "Sí, Intercámbialas!" - -#. module: hotel_calendar -#. openerp-web -#: code:addons/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js:550 -#, python-format -msgid "Yes, unify it" -msgstr "Sí, Fusionálas!" - -#. module: hotel_calendar -#: model:ir.model,name:hotel_calendar.model_bus_hotel_calendar -msgid "bus.hotel.calendar" -msgstr "bus.hotel.calendar" - -#. module: hotel_calendar -#: model:ir.model,name:hotel_calendar.model_hotel_calendar -msgid "hotel.calendar" -msgstr "hotel.calendar" - -#. module: hotel_calendar -#: model:ir.model,name:hotel_calendar.model_hotel_calendar_management -msgid "hotel.calendar.management" -msgstr "hotel.calendar.management" - -#. module: hotel_calendar -#: model:ir.model,name:hotel_calendar.model_hotel_room_type_restriction_item -msgid "hotel.room.type.restriction.item" -msgstr "hotel.room.type.restriction.item" - -#. module: hotel_calendar -#: model:ir.model,name:hotel_calendar.model_ir_actions_act_window_view -msgid "ir.actions.act_window.view" -msgstr "ir.actions.act_window.view" - -#. module: hotel_calendar -#: model:ir.model,name:hotel_calendar.model_ir_ui_view -msgid "ir.ui.view" -msgstr "ir.ui.view" diff --git a/hotel_calendar/models/__init__.py b/hotel_calendar/models/__init__.py deleted file mode 100644 index 714a80ba9..000000000 --- a/hotel_calendar/models/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from . import inherited_hotel_property -from . import hotel_calendar -from . import bus_hotel_calendar -from . import hotel_calendar_management -from . import inherited_hotel_reservation -from . import inherited_res_company -from . import inherited_res_users -from . import inherited_hotel_room_type_restriction_item -from . import inherited_product_pricelist_item -from . import inherited_hotel_folio -from . import ir_actions_act_window_view -from . import ir_ui_view diff --git a/hotel_calendar/models/bus_hotel_calendar.py b/hotel_calendar/models/bus_hotel_calendar.py deleted file mode 100644 index c0bffeef3..000000000 --- a/hotel_calendar/models/bus_hotel_calendar.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from datetime import datetime -from odoo.tools import DEFAULT_SERVER_DATE_FORMAT -from odoo import models, api -from odoo.addons.hotel_calendar.controllers.bus import HOTEL_BUS_CHANNEL_ID - - -class BusHotelCalendar(models.TransientModel): - _name = 'bus.hotel.calendar' - - ''' - action: - - create - - write - - unlink - - cancelled - ntype: - - notif : Show a normal notification - - warn : Show a warning notification - - noshow : Don't show any notification - ''' - # Business methods - @api.model - def _generate_reservation_notif(self, vals): - user_id = self.env['res.users'].browse(self.env.uid) - return { - 'type': 'reservation', - 'action': vals['action'], - 'subtype': vals['type'], - 'title': vals['title'], - 'username': user_id.partner_id.name, - 'userid': user_id.id, - 'reservation': { - 'room_id': vals['room_id'], - 'id': vals['reserv_id'], - 'name': vals['partner_name'], - 'adults': vals['adults'], - 'childer': vals['children'], - 'checkin': vals['checkin'], - 'checkout': vals['checkout'], - 'folio_id': vals['folio_id'], - 'bgcolor': vals['reserve_color'], - 'color': vals['reserve_color_text'], - 'splitted': vals['splitted'], - 'parent_reservation': vals['parent_reservation'], - 'room_name': vals['room_name'], - 'state': vals['state'], - 'only_read': False, - 'fix_days': vals['fix_days'], - 'fix_room': False, - 'overbooking': vals['overbooking'], - 'price_room_services_set': vals['price_room_services_set'], - 'amount_total': vals['pending_amount'] + vals['invoices_paid'], - 'real_dates': vals['real_dates'], - 'channel_type': vals['channel_type'], - }, - 'tooltip': { - 'folio_name': vals['folio_name'], - 'name': vals['partner_name'], - 'phone': vals['partner_phone'], - 'email': vals['partner_email'], - 'room_type_name': vals['room_type_name'], - 'adults': vals['adults'], - 'children': vals['children'], - 'checkin': vals['checkin'], - 'checkout': vals['checkout'], - 'arrival_hour': vals['arrival_hour'], - 'departure_hour': vals['departure_hour'], - 'price_room_services_set': vals['price_room_services_set'], - 'invoices_paid': vals['invoices_paid'], - 'pending_amount': vals['pending_amount'], - 'type': vals['reservation_type'], - 'closure_reason': vals['closure_reason'], - 'out_service_description': vals['out_service_description'], - 'splitted': vals['splitted'], - 'real_dates': vals['real_dates'], - 'channel_type': vals['channel_type'], - 'board_service_name': vals['board_service_name'], - 'services': vals['services'], - } - } - - @api.model - def _generate_pricelist_notification(self, vals): - date_dt = datetime.strptime(vals['date'], DEFAULT_SERVER_DATE_FORMAT) - return { - 'type': 'pricelist', - 'price': { - vals['pricelist_id']: [{ - 'days': { - date_dt.strftime("%d/%m/%Y"): vals['price'], - }, - 'room': vals['room_id'], - 'id': vals['id'], - }], - }, - } - - @api.model - def _generate_restriction_notification(self, vals): - date_dt = datetime.strptime(vals['date'], DEFAULT_SERVER_DATE_FORMAT) - return { - 'type': 'restriction', - 'restriction': { - vals['room_type_id']: { - date_dt.strftime("%d/%m/%Y"): [ - vals['min_stay'], - vals['min_stay_arrival'], - vals['max_stay'], - vals['max_stay_arrival'], - vals['closed'], - vals['closed_arrival'], - vals['closed_departure'], - vals['id'], - ], - }, - }, - } - - @api.model - def send_reservation_notification(self, vals): - notif = self._generate_reservation_notif(vals) - self.env['bus.bus'].sendone((self._cr.dbname, 'hotel.reservation', - HOTEL_BUS_CHANNEL_ID), notif) - - @api.model - def send_pricelist_notification(self, vals): - notif = self._generate_pricelist_notification(vals) - self.env['bus.bus'].sendone((self._cr.dbname, 'hotel.reservation', - HOTEL_BUS_CHANNEL_ID), notif) - - @api.model - def send_restriction_notification(self, vals): - notif = self._generate_restriction_notification(vals) - self.env['bus.bus'].sendone((self._cr.dbname, 'hotel.reservation', - HOTEL_BUS_CHANNEL_ID), notif) diff --git a/hotel_calendar/models/hotel_calendar.py b/hotel_calendar/models/hotel_calendar.py deleted file mode 100644 index 68938f2ff..000000000 --- a/hotel_calendar/models/hotel_calendar.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api - - -class HotelCalendar(models.Model): - """ Used to show and filter rooms and reservations in the PMS Calendar. """ - _name = 'hotel.calendar' - - # Default methods - @api.model - def _get_default_hotel(self): - return self.env.user.hotel_id - - # Fields declaration - name = fields.Char('Name', required=True) - hotel_id = fields.Many2one('hotel.property', 'Hotel', required=True, ondelete='restrict', - default=_get_default_hotel) - room_type_ids = fields.Many2many('hotel.room.type', string='Room Type') - segmentation_ids = fields.Many2many('hotel.room.type.class', string='Segmentation') - location_ids = fields.Many2many('hotel.floor', string='Location') - amenity_ids = fields.Many2many('hotel.amenity', string='Amenity') - diff --git a/hotel_calendar/models/hotel_calendar_management.py b/hotel_calendar/models/hotel_calendar_management.py deleted file mode 100644 index afa137340..000000000 --- a/hotel_calendar/models/hotel_calendar_management.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import logging -from datetime import timedelta -from odoo.tools import ( - DEFAULT_SERVER_DATE_FORMAT, - DEFAULT_SERVER_DATETIME_FORMAT) -from odoo import models, api, _, fields -from odoo.exceptions import AccessError, ValidationError -_logger = logging.getLogger(__name__) - - -class HotelCalendarManagement(models.TransientModel): - _name = 'hotel.calendar.management' - - # Business methods - - def get_hcalendar_settings(self): - return { - 'eday_week': self.env.user.hotel_id.pms_end_day_week, - 'eday_week_offset': self.env.user.hotel_id.pms_end_day_week_offset, - 'days': self.env.user.hotel_id.pms_default_num_days, - 'show_notifications': self.env.user.pms_show_notifications, - 'show_num_rooms': self.env.user.hotel_id.pms_show_num_rooms, - } - - @api.model - def _get_prices_values(self, price): - vals = { - 'fixed_price': price['price'], - } - return vals - - @api.model - def _get_restrictions_values(self, restriction): - vals = { - 'min_stay': restriction['min_stay'], - 'min_stay_arrival': restriction['min_stay_arrival'], - 'max_stay': restriction['max_stay'], - 'max_stay_arrival': restriction['max_stay_arrival'], - 'closed': restriction['closed'], - 'closed_arrival': restriction['closed_arrival'], - 'closed_departure': restriction['closed_departure'], - } - return vals - - @api.model - def _hcalendar_room_json_data(self, rooms): - json_data = [] - for room in rooms: - json_data.append({ - 'id': room.id, - 'name': room.name, - 'capacity': room.get_capacity(), - 'price': room.list_price, - 'total_rooms': room.total_rooms_count, - }) - return json_data - - @api.model - def _hcalendar_pricelist_json_data(self, prices): - json_data = {} - room_type_obj = self.env['hotel.room.type'] - for rec in prices: - room_type_id = room_type_obj.search([ - ('product_id.product_tmpl_id', '=', rec.product_tmpl_id.id) - ], limit=1) - if not room_type_id: - continue - - # TODO: date_end - date_start loop - json_data.setdefault(room_type_id.id, []).append({ - 'id': rec.id, - 'price': rec.fixed_price, - 'date': rec.date_start, - }) - return json_data - - @api.model - def _hcalendar_restriction_json_data(self, restrictions): - json_data = {} - for rec in restrictions: - json_data.setdefault(rec.room_type_id.id, []).append({ - 'id': rec.id, - 'date': rec.date, - 'min_stay': rec.min_stay, - 'min_stay_arrival': rec.min_stay_arrival, - 'max_stay': rec.max_stay, - 'max_stay_arrival': rec.max_stay_arrival, - 'closed': rec.closed, - 'closed_departure': rec.closed_departure, - 'closed_arrival': rec.closed_arrival, - }) - return json_data - - @api.model - def _hcalendar_events_json_data(self, dfrom, dto): - date_start = fields.Date.from_string(dfrom) - timedelta(days=1) - date_start_str = date_start.strftime(DEFAULT_SERVER_DATETIME_FORMAT) - user_id = self.env['res.users'].browse(self.env.uid) - domain = [] - if self.env.user.hotel_id.pms_allowed_events_tags: - domain.append(('categ_ids', 'in', self.env.user.hotel_id.pms_allowed_events_tags)) - if self.env.user.hotel_id.pms_denied_events_tags: - domain.append( - ('categ_ids', 'not in', self.env.user.hotel_id.pms_denied_events_tags)) - events_raw = self.env['calendar.event'].search(domain) - events_ll = self.env['calendar.event'].search([ - ('start', '<=', dto), - ('stop', '>=', date_start_str) - ]) - events_lr = self.env['calendar.event'].search([ - ('start', '>=', date_start_str), - ('stop', '<=', dto) - ]) - events = (events_ll | events_lr) & events_raw - json_data = [] - for event in events: - json_data.append([ - event.id, - event.name, - event.start, - event.location, - ]) - return json_data - - @api.model - def _hcalendar_get_count_reservations_json_data(self, dfrom, dto): - date_start = fields.Date.from_string(dfrom) - date_end = fields.Date.from_string(dto) - date_diff = abs((date_end - date_start).days) + 1 - room_type_obj = self.env['hotel.room.type'] - room_types = room_type_obj.search([]) - json_data = {} - - for room_type in room_types: - for i in range(0, date_diff): - cur_date = date_start + timedelta(days=i) - cur_date_str = cur_date.strftime(DEFAULT_SERVER_DATE_FORMAT) - self.env.cr.execute(''' - SELECT - hrl.id - FROM hotel_reservation_line AS hrl - WHERE date = %s - ''', ((cur_date_str),)) - line_ids = [r[0] for r in self.env.cr.fetchall()] - reservation_ids = self.env['hotel.reservation.line'].browse(line_ids).\ - mapped('reservation_id.id') - reservations = self.env['hotel.reservation'].\ - browse(reservation_ids).filtered( - lambda r: r.state != 'cancelled' - and not r.overbooking and not r.reselling - ) - reservations_rooms = reservations.mapped('room_id.id') - free_rooms = self.env['hotel.room'].search([ - ('id', 'not in', reservations_rooms), - ]) - rooms_linked = self.env['hotel.room.type'].search([ - ('id', '=', room_type.id) - ]).room_ids - free_rooms = free_rooms & rooms_linked - json_data.setdefault(room_type.id, []).append({ - 'date': cur_date_str, - 'num': len(free_rooms), - }) - - return json_data - - @api.model - def get_hcalendar_all_data(self, dfrom, dto, pricelist_id, restriction_id, - withRooms): - hotel_id = self.env.user.hotel_id.id - - if not dfrom or not dto: - raise ValidationError(_('Input Error: No dates defined!')) - vals = {} - # TODO: refactoring res.config.settings', 'default_pricelist_id' by the current hotel.property.pricelist_id - if not pricelist_id: - pricelist_id = self.env.user.hotel_id.default_pricelist_id.id - # TODO: refactoring res.config.settings', 'default_restriction_id by the current hotel.property.restriction_id - if not restriction_id: - restriction_id = self.env.user.hotel_id.default_restriction_id.id - - # TODO: ensure pricelist_id and restriction_id belong to the current hotel - vals.update({'pricelist_id': pricelist_id}) - vals.update({'restriction_id': restriction_id}) - - restriction_item_ids = self.env['hotel.room.type.restriction.item'].search([ - ('date', '>=', dfrom), ('date', '<=', dto), - ('restriction_id', '=', restriction_id), - ]) - pricelist_item_ids = self.env['product.pricelist.item'].search([ - ('date_start', '>=', dfrom), ('date_end', '<=', dto), - ('pricelist_id', '=', pricelist_id), - ('applied_on', '=', '1_product'), - ('compute_price', '=', 'fixed'), - ]) - - json_prices = self._hcalendar_pricelist_json_data(pricelist_item_ids) - json_rest = self._hcalendar_restriction_json_data(restriction_item_ids) - # TODO REVIEW: what are json_rc and json_events used for in calendar management ¿? - json_rc = self._hcalendar_get_count_reservations_json_data(dfrom, dto) - json_events = self._hcalendar_events_json_data(dfrom, dto) - vals.update({ - 'prices': json_prices or [], - 'restrictions': json_rest or [], - 'count_reservations': json_rc or [], - 'events': json_events or [], - }) - - if withRooms: - room_ids = self.env['hotel.room.type'].search([ - ('hotel_id', '=', hotel_id) - ], order='sequence ASC') or None - - if not room_ids: - raise AccessError( - _("Wrong hotel and company access settings for this user. " - "No room types found for hotel %s") % self.env.user.hotel_id.name) - - json_rooms = self._hcalendar_room_json_data(room_ids) - vals.update({'rooms': json_rooms or []}) - - return vals - - @api.model - def save_changes(self, pricelist_id, restriction_id, pricelist, - restrictions, availability=False): - room_type_obj = self.env['hotel.room.type'] - product_pricelist_item_obj = self.env['product.pricelist.item'] - room_type_rest_item_obj = self.env['hotel.room.type.restriction.item'] - - # Save Pricelist - for k_price in pricelist.keys(): - room_type_id = room_type_obj.browse([int(k_price)]) - room_type_prod_tmpl_id = room_type_id.product_id.product_tmpl_id - for price in pricelist[k_price]: - price_id = product_pricelist_item_obj.search([ - ('date_start', '>=', price['date']), - ('date_end', '<=', price['date']), - ('pricelist_id', '=', int(pricelist_id)), - ('applied_on', '=', '1_product'), - ('compute_price', '=', 'fixed'), - ('product_tmpl_id', '=', room_type_prod_tmpl_id.id), - ], limit=1) - vals = self._get_prices_values(price) - if not price_id: - vals.update({ - 'date_start': price['date'], - 'date_end': price['date'], - 'pricelist_id': int(pricelist_id), - 'applied_on': '1_product', - 'compute_price': 'fixed', - 'product_tmpl_id': room_type_prod_tmpl_id.id, - }) - price_id = product_pricelist_item_obj.create(vals) - else: - price_id.write(vals) - - # Save Restrictions - for k_res in restrictions.keys(): - for restriction in restrictions[k_res]: - res_id = room_type_rest_item_obj.search([ - ('date', '=', restriction['date']), - ('restriction_id', '=', int(restriction_id)), - ('room_type_id', '=', int(k_res)), - ], limit=1) - vals = self._get_restrictions_values(restriction) - if not res_id: - vals.update({ - 'date': restriction['date'], - 'restriction_id': int(restriction_id), - 'room_type_id': int(k_res), - }) - res_id = room_type_rest_item_obj.create(vals) - else: - res_id.write(vals) diff --git a/hotel_calendar/models/inherited_hotel_folio.py b/hotel_calendar/models/inherited_hotel_folio.py deleted file mode 100644 index 01929f3de..000000000 --- a/hotel_calendar/models/inherited_hotel_folio.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2018-2019 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, api, _ - - -class HotelFolio(models.Model): - _inherit = 'hotel.folio' - - # ORM overrides - - def write(self, vals): - ret = super(HotelFolio, self).write(vals) - fields_to_check = ('reservation_ids', 'service_ids', 'pending_amount') - fields_checked = [elm for elm in fields_to_check if elm in vals] - if any(fields_checked): - for record in self: - record.reservation_ids.send_bus_notification('write', 'noshow') - return ret - - - def unlink(self): - for record in self: - record.reservation_ids.send_bus_notification('unlink', 'warn', - _("Folio Deleted")) - return super(HotelFolio, self).unlink() - - # Business methods - - def compute_amount(self): - ret = super(HotelFolio, self).compute_amount() - with self.env.norecompute(): - for record in self: - record.reservation_ids.send_bus_notification('write', 'noshow') - return ret diff --git a/hotel_calendar/models/inherited_hotel_property.py b/hotel_calendar/models/inherited_hotel_property.py deleted file mode 100644 index 77a5093e7..000000000 --- a/hotel_calendar/models/inherited_hotel_property.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2019 Pablo Quesada -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields - - -class HotelProperty(models.Model): - _inherit = 'hotel.property' - - # Fields declaration - pms_show_num_rooms = fields.Integer('Number of rooms to show', - default=0) - pms_divide_rooms_by_capacity = fields.Boolean('Divide rooms by capacity', - default=True) - pms_end_day_week = fields.Selection([ - ('1', 'Monday'), - ('2', 'Tuesday'), - ('3', 'Wednesday'), - ('4', 'Thursday'), - ('5', 'Friday'), - ('6', 'Saturday'), - ('7', 'Sunday') - ], string='Highlight column of day', default='6') - pms_end_day_week_offset = fields.Selection([ - ('0', '0 Days'), - ('1', '1 Days'), - ('2', '2 Days'), - ('3', '3 Days'), - ('4', '4 Days'), - ('5', '5 Days'), - ('6', '6 Days') - ], string='Also illuminate the previous', default='0') - pms_default_num_days = fields.Selection([ - ('month', '1 Month'), - ('21', '3 Weeks'), - ('14', '2 Weeks'), - ('7', '1 Week') - ], string='Default number of days', default='month') - # TODO: review the use of the following option in the calendar js functions - pms_type_move = fields.Selection([ - ('normal', 'Normal'), - ('assisted', 'Assisted'), - ('allow_invalid', 'Allow Invalid') - ], string='Reservation move mode', default='normal') - - pms_allowed_events_tags = fields.Many2many( - 'calendar.event.type', - string="Allow Calendar Event Tags") - pms_denied_events_tags = fields.Many2many( - 'calendar.event.type', - string="Deny Calendar Event Tags") - diff --git a/hotel_calendar/models/inherited_hotel_reservation.py b/hotel_calendar/models/inherited_hotel_reservation.py deleted file mode 100644 index ba48b1b99..000000000 --- a/hotel_calendar/models/inherited_hotel_reservation.py +++ /dev/null @@ -1,521 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import logging -from datetime import timedelta -from odoo import models, fields, api, _ -from odoo.models import operator -from odoo.exceptions import AccessError, ValidationError -from odoo.tools import DEFAULT_SERVER_DATE_FORMAT -_logger = logging.getLogger(__name__) - - -class HotelReservation(models.Model): - _inherit = 'hotel.reservation' - - # Fields declaration - reserve_color = fields.Char(compute='_compute_color', string='Color', - store=True) - reserve_color_text = fields.Char(compute='_compute_color', string='Color', - store=True) - - # TODO: Add the following method into _compute_color - - def _generate_color(self): - self.ensure_one() - company_id = self.env.user.company_id - if self.reservation_type == 'staff': - reserv_color = company_id.color_staff - reserv_color_text = company_id.color_letter_staff - elif self.reservation_type == 'out': - reserv_color = company_id.color_dontsell - reserv_color_text = company_id.color_letter_dontsell - elif self.to_assign: - reserv_color = company_id.color_to_assign - reserv_color_text = company_id.color_letter_to_assign - elif self.state == 'draft': - reserv_color = company_id.color_pre_reservation - reserv_color_text = company_id.color_letter_pre_reservation - elif self.state == 'confirm': - if self.folio_id.pending_amount <= 0: - reserv_color = company_id.color_reservation_pay - reserv_color_text = company_id.color_letter_reservation_pay - else: - reserv_color = company_id.color_reservation - reserv_color_text = company_id.color_letter_reservation - elif self.state == 'booking': - if self.folio_id.pending_amount <= 0: - reserv_color = company_id.color_stay_pay - reserv_color_text = company_id.color_letter_stay_pay - else: - reserv_color = company_id.color_stay - reserv_color_text = company_id.color_letter_stay - else: - if self.folio_id.pending_amount <= 0: - reserv_color = company_id.color_checkout - reserv_color_text = company_id.color_letter_checkout - else: - reserv_color = company_id.color_payment_pending - reserv_color_text = company_id.color_letter_payment_pending - return reserv_color, reserv_color_text - - # Constraints and onchanges - @api.depends('state', 'reservation_type', 'folio_id.pending_amount', 'to_assign') - def _compute_color(self): - for record in self: - colors = record._generate_color() - record.update({ - 'reserve_color': colors[0], - 'reserve_color_text': colors[1], - }) - - # ORM overrides - @api.model - def create(self, vals): - reservation_id = super(HotelReservation, self).create(vals) - reservation_id.send_bus_notification('create', - 'notify', - _("Reservation Created")) - return reservation_id - - - def write(self, vals): - _logger.info("RESERV WRITE") - ret = super(HotelReservation, self).write(vals) - self.send_bus_notification('write', 'noshow') - return ret - - - def unlink(self): - self.send_bus_notification('unlink', - 'warn', - _("Reservation Deleted")) - return super(HotelReservation, self).unlink() - - # Business methods - @api.model - def _hcalendar_room_data(self, rooms): - _logger.warning('_found [%s] rooms for hotel [%s]', len(rooms), self.env.user.hotel_id.id) - # TODO: refactoring res.config.settings', 'default_pricelist_id' by the current hotel.property.pricelist_id - pricelist_id = self.env.user.hotel_id.default_pricelist_id.id - json_rooms = [ - { - 'id': room.id, - 'name': room.name, - 'capacity': room.capacity, - 'class_name': room.room_type_id.class_id.name, - 'class_id': room.room_type_id.class_id.id, - 'shared_id': room.shared_room_id, - 'price': room.room_type_id - and ['pricelist', room.room_type_id.id, pricelist_id, - room.room_type_id.name] or 0, - 'room_type_name': room.room_type_id.name, - 'room_type_id': room.room_type_id.id, - 'floor_id': room.floor_id.id, - 'amentity_ids': room.room_type_id.room_amenity_ids.ids, - } for room in rooms] - return json_rooms - - @api.model - def _hcalendar_reservation_data(self, reservations): - _logger.warning('_found [%s] reservations for hotel [%s]', len(reservations), self.env.user.hotel_id.id) - json_reservations = [] - json_reservation_tooltips = {} - for reserv in reservations: - json_reservations.append({ - 'room_id': reserv['room_id'], - 'id': reserv['id'], - 'name': reserv['closure_reason'] or _('Out of service') - if reserv['reservation_type'] == 'out' - else reserv['partner_name'], - 'adults': reserv['adults'], - 'childrens': reserv['children'], - 'checkin': reserv['checkin'], - 'checkout': reserv['checkout'], - 'folio_id': reserv['folio_id'], - 'bgcolor': reserv['reserve_color'], - 'color': reserv['reserve_color_text'], - 'splitted': reserv['splitted'], - 'parent_reservation': reserv['parent_reservation'] or False, - 'read_only': False, # Read-Only - 'fix_days': reserv['splitted'], # Fix Days - 'fix_room': False, # Fix Rooms - 'overbooking': reserv['overbooking'], - 'state': reserv['state'], - 'price_room_services_set': reserv['price_room_services_set'], - 'amount_total': reserv['amount_total'], - 'real_dates': [reserv['real_checkin'], reserv['real_checkout']], - 'channel_type': reserv['channel_type'], - }) - json_reservation_tooltips.update({ - reserv['id']: { - 'folio_name': reserv['folio_name'], - 'name': _('Out of service') - if reserv['reservation_type'] == 'out' - else reserv['partner_name'], - 'phone': reserv['mobile'] or reserv['phone'] - or _('Phone not provided'), - 'email': reserv['email'] or _('Email not provided'), - 'room_type_name': reserv['room_type'], - 'adults': reserv['adults'], - 'children': reserv['children'], - 'checkin': reserv['checkin'], - 'checkout': reserv['checkout'], - 'arrival_hour': reserv['arrival_hour'], - 'departure_hour': reserv['departure_hour'], - 'price_room_services_set': reserv['price_room_services_set'], - 'invoices_paid': reserv['invoices_paid'], - 'pending_amount': reserv['pending_amount'], - 'type': reserv['reservation_type'] or 'normal', - 'closure_reason': reserv['closure_reason'], - 'out_service_description': reserv['out_service_description'] - or _('No reason given'), - 'splitted': reserv['splitted'], - 'channel_type': reserv['channel_type'], - 'real_dates': [reserv['real_checkin'], reserv['real_checkout']], - 'board_service_name': reserv['board_service_name'] or _('No board services'), - 'services': reserv['services'], - } - }) - return json_reservations, json_reservation_tooltips - - @api.model - def _hcalendar_event_data(self, events): - # TODO: Filter events by hotel - json_events = [ - { - 'id': event.id, - 'name': event.name, - 'date': event.start, - 'location': event.location, - } for event in events] - return json_events - - @api.model - def _hcalendar_calendar_data(self, calendars): - _logger.warning('_found [%s] calendars for hotel [%s]', len(calendars), self.env.user.hotel_id.id) - return [ - { - 'id': calendar.id, - 'name': calendar.name, - 'segmentation_ids': calendar.segmentation_ids.ids, - 'location_ids': calendar.location_ids.ids, - 'amenity_ids': calendar.amenity_ids.ids, - 'room_type_ids': calendar.room_type_ids.ids, - } for calendar in calendars] - - @api.model - def get_hcalendar_reservations_data(self, dfrom_dt, dto_dt, rooms): - rdfrom_dt = dfrom_dt + timedelta(days=1) # Ignore checkout - rdfrom_str = rdfrom_dt.strftime(DEFAULT_SERVER_DATE_FORMAT) - dto_str = dto_dt.strftime(DEFAULT_SERVER_DATE_FORMAT) - self.env.cr.execute(''' - SELECT - hr.id, hr.room_id, hr.adults, hr.children, hr.checkin, hr.checkout, hr.reserve_color, hr.reserve_color_text, - hr.splitted, hr.parent_reservation, hr.overbooking, hr.state, hr.real_checkin, hr.real_checkout, - hr.out_service_description, hr.arrival_hour, hr.departure_hour, hr.channel_type, - hr.price_room_services_set, - - hf.id as folio_id, hf.name as folio_name, hf.reservation_type, hf.invoices_paid, hf.pending_amount, - hf.amount_total, - - rp.mobile, rp.phone, rp.email, rp.name as partner_name, - - pt.name as room_type, - - array_agg(pt2.name) FILTER (WHERE pt2.show_in_calendar = TRUE) as services, - - rcr.name as closure_reason, - - hbs.name as board_service_name - FROM hotel_reservation AS hr - LEFT JOIN hotel_folio AS hf ON hr.folio_id = hf.id - LEFT JOIN hotel_room_type AS hrt ON hr.room_type_id = hrt.id - LEFT JOIN product_product AS pp ON hrt.product_id = pp.id - LEFT JOIN product_template AS pt ON pp.product_tmpl_id = pt.id - LEFT JOIN res_partner AS rp ON hf.partner_id = rp.id - LEFT JOIN room_closure_reason as rcr - ON hf.closure_reason_id = rcr.id - LEFT JOIN hotel_board_service_room_type_rel AS hbsrt ON hr.board_service_room_id = hbsrt.id - LEFT JOIN hotel_board_service AS hbs ON hbsrt.hotel_board_service_id = hbs.id - LEFT JOIN hotel_service AS hs ON hr.id = hs.reservation_id - LEFT JOIN product_product AS pp2 ON hs.product_id = pp2.id - LEFT JOIN product_template AS pt2 ON pp2.product_tmpl_id = pt2.id - WHERE room_id IN %s AND ( - (checkin <= %s AND checkout >= %s AND checkout <= %s) - OR (checkin >= %s AND checkout <= %s) - OR (checkin >= %s AND checkin <= %s AND checkout >= %s) - OR (checkin <= %s AND checkout >= %s)) - GROUP BY hr.id, hf.id, pt.name, rcr.name, hbs.name, rp.mobile, rp.phone, rp.email, rp.name - ORDER BY checkin DESC, checkout ASC, adults DESC, children DESC - ''', (tuple(rooms.ids), - rdfrom_str, rdfrom_str, dto_str, - rdfrom_str, dto_str, - rdfrom_str, dto_str, dto_str, - rdfrom_str, dto_str)) - return self._hcalendar_reservation_data(self.env.cr.dictfetchall()) - - @api.model - def get_hcalendar_pricelist_data(self, dfrom_dt, dto_dt): - # TODO: refactoring res.config.settings', 'default_pricelist_id' by the current hotel.property.pricelist_id - pricelist_id = self.env.user.hotel_id.default_pricelist_id.id - hotel_id = self.env.user.hotel_id.id - room_types_ids = self.env['hotel.room.type'].search([ - ('hotel_id', '=', hotel_id) - ]) - - dfrom_str = dfrom_dt.strftime(DEFAULT_SERVER_DATE_FORMAT) - dto_str = dto_dt.strftime(DEFAULT_SERVER_DATE_FORMAT) - - self.env.cr.execute(''' - WITH RECURSIVE gen_table_days AS ( - SELECT hrt.id, %s::Date AS date, hrt.sequence - FROM hotel_room_type AS hrt - UNION ALL - SELECT hrt.id, (td.date + INTERVAL '1 day')::Date, hrt.sequence - FROM gen_table_days as td - LEFT JOIN hotel_room_type AS hrt ON hrt.id=td.id - WHERE td.date < %s - ) - SELECT - TO_CHAR(gtd.date, 'DD/MM/YYYY') as date, gtd.id as room_type_id, gtd.sequence, - pt.name, ppi.fixed_price as price, pt.list_price - FROM gen_table_days AS gtd - LEFT JOIN hotel_room_type AS hrt ON hrt.id = gtd.id - LEFT JOIN product_product AS pp ON pp.id = hrt.product_id - LEFT JOIN product_template AS pt ON pt.id = pp.product_tmpl_id - LEFT JOIN product_pricelist_item AS ppi ON ppi.date_start = gtd.date AND ppi.date_end = gtd.date AND ppi.product_tmpl_id = pt.id - WHERE gtd.id IN %s - ORDER BY gtd.id ASC, gtd.date ASC - ''', (dfrom_str, dto_str, tuple(room_types_ids.ids))) - query_results = self.env.cr.dictfetchall() - - json_data = {} - for results in query_results: - if results['room_type_id'] not in json_data: - json_data.setdefault(results['room_type_id'], {}).update({ - 'title': results['name'], - 'room': results['room_type_id'], - 'sequence': results['sequence'], - }) - json_data[results['room_type_id']].setdefault('days', {}).update({ - results['date']: results['price'] or results['list_price'] - }) - - json_data_by_sequence = list(json_data.values()) - json_data_by_sequence.sort(key=operator.itemgetter('sequence')) - - json_rooms_prices = {} - for prices in json_data_by_sequence: - json_rooms_prices.setdefault(pricelist_id, []).append(prices) - return json_rooms_prices - - @api.model - def get_hcalendar_restrictions_data(self, dfrom_dt, dto_dt): - """ Returns the room type restrictions between dfrom_dt and dto_dt - for the room types of the current_hotel within the default restriction plan - """ - hotel_id = self.env.user.hotel_id.id - restriction_id = self.env.user.hotel_id.default_restriction_id.id - - json_rooms_rests = {} - room_typed_ids = self.env['hotel.room.type'].search([ - ('hotel_id', '=', hotel_id) - ], order='sequence ASC') - - room_type_rest_obj = self.env['hotel.room.type.restriction.item'] - rtype_rest_ids = room_type_rest_obj.search([ - ('room_type_id', 'in', room_typed_ids.ids), - ('date', '>=', dfrom_dt), - ('date', '<=', dto_dt), - ('restriction_id', '=', restriction_id) - ]) - - for room_type in room_typed_ids: - days = {} - rest_ids = rtype_rest_ids.filtered( - lambda x: x.room_type_id == room_type) - for rest_id in rest_ids: - days.update({ - fields.Date.from_string(rest_id.date).strftime("%d/%m/%Y"): ( - rest_id.min_stay, - rest_id.min_stay_arrival, - rest_id.max_stay, - rest_id.max_stay_arrival, - rest_id.closed, - rest_id.closed_arrival, - rest_id.closed_departure) - }) - json_rooms_rests.update({room_type.id: days}) - return json_rooms_rests - - @api.model - def get_hcalendar_events_data(self, dfrom_dt, dto_dt): - user_id = self.env['res.users'].browse(self.env.uid) - domain = [ - '|', '&', - ('start', '<=', dto_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)), - ('stop', '>=', dfrom_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)), - '&', - ('start', '>=', dfrom_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)), - ('stop', '<=', dto_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)) - ] - if self.env.user.hotel_id.pms_allowed_events_tags: - domain.append(('categ_ids', 'in', self.env.user.hotel_id.pms_allowed_events_tags)) - if self.env.user.hotel_id.pms_denied_events_tags: - domain.append( - ('categ_ids', 'not in', self.env.user.hotel_id.pms_denied_events_tags)) - events_raw = self.env['calendar.event'].search(domain) - return self._hcalendar_event_data(events_raw) - - @api.model - def get_hcalendar_calendar_data(self): - hotel_id = self.env.user.hotel_id.id - calendars = self.env['hotel.calendar'].search([ - ('hotel_id', '=', hotel_id) - ]) - res = self._hcalendar_calendar_data(calendars) - return res - - @api.model - def get_hcalendar_all_data(self, dfrom, dto, withRooms=True): - if not dfrom or not dto: - raise ValidationError(_('Input Error: No dates defined!')) - - hotel_id = self.env.user.hotel_id.id - - dfrom_dt = fields.Date.from_string(dfrom) - dto_dt = fields.Date.from_string(dto) - - rooms = self.env['hotel.room'].search([ - ('hotel_id', '=', hotel_id) - ], order='sequence ASC') or None - - if not rooms: - raise AccessError( - _("No rooms found for hotel %s. " - "Please, review the rooms configured for this hotel.") % self.env.user.hotel_id.name) - - json_res, json_res_tooltips = self.get_hcalendar_reservations_data( - dfrom_dt, dto_dt, rooms) - - vals = { - 'rooms': withRooms and self._hcalendar_room_data(rooms) or [], - 'reservations': json_res, - 'tooltips': json_res_tooltips, - 'pricelist': self.get_hcalendar_pricelist_data(dfrom_dt, dto_dt), - 'restrictions': self.get_hcalendar_restrictions_data(dfrom_dt, - dto_dt), - 'events': self.get_hcalendar_events_data(dfrom_dt, dto_dt), - 'calendars': withRooms and self.get_hcalendar_calendar_data() - or [] - } - return vals - - @api.model - def get_hcalendar_settings(self): - type_move = self.env.user.hotel_id.pms_type_move - return { - 'divide_rooms_by_capacity': self.env.user.hotel_id.pms_divide_rooms_by_capacity, - 'eday_week': self.env.user.hotel_id.pms_end_day_week, - 'eday_week_offset': self.env.user.hotel_id.pms_end_day_week_offset, - 'days': self.env.user.hotel_id.pms_default_num_days, - 'allow_invalid_actions': type_move == 'allow_invalid', - 'assisted_movement': type_move == 'assisted', - 'default_arrival_hour': self.env.user.hotel_id.default_arrival_hour, - 'default_departure_hour': self.env.user.hotel_id.default_departure_hour, - 'show_notifications': self.env.user.pms_show_notifications, - 'show_pricelist': self.env.user.pms_show_pricelist, - 'show_availability': self.env.user.pms_show_availability, - 'show_num_rooms': self.env.user.hotel_id.pms_show_num_rooms, - } - - - def generate_bus_values(self, naction, ntype, ntitle=''): - self.ensure_one() - return { - 'action': naction, - 'type': ntype, - 'title': ntitle, - 'room_id': self.room_id.id, - 'reserv_id': self.id, - 'folio_name': self.folio_id.name, - 'partner_name': (self.closure_reason_id.name or _('Out of service')) - if self.reservation_type == 'out' else self.partner_id.name, - 'adults': self.adults, - 'children': self.children, - 'checkin': self.checkin, - 'checkout': self.checkout, - 'arrival_hour': self.arrival_hour, - 'departure_hour': self.departure_hour, - 'folio_id': self.folio_id.id, - 'reserve_color': self.reserve_color, - 'reserve_color_text': self.reserve_color_text, - 'splitted': self.splitted, - 'parent_reservation': self.parent_reservation - and self.parent_reservation.id or 0, - 'room_name': self.room_id.name, - 'room_type_name': self.room_type_id.name, - 'partner_phone': self.partner_id.mobile - or self.partner_id.phone or _('Undefined'), - 'partner_email': self.partner_id.email or _('Undefined'), - 'state': self.state, - 'fix_days': self.splitted, - 'overbooking': self.overbooking, - 'price_room_services_set': self.price_room_services_set, - 'invoices_paid': self.folio_id.invoices_paid, - 'pending_amount': self.folio_id.pending_amount, - 'reservation_type': self.reservation_type or 'normal', - 'closure_reason': self.closure_reason_id.name, - 'out_service_description': self.out_service_description - or _('No reason given'), - 'real_dates': [self.real_checkin, self.real_checkout], - 'channel_type': self.channel_type, - 'board_service_name': self.board_service_room_id.hotel_board_service_id.name or _('No board services'), - 'services': [service.product_id.name for service in self.service_ids - if service.product_id.show_in_calendar] or False, - } - - - def send_bus_notification(self, naction, ntype, ntitle=''): - hotel_cal_obj = self.env['bus.hotel.calendar'] - for record in self: - if not isinstance(record.id, models.NewId) \ - and not isinstance(record.folio_id.id, models.NewId) \ - and not isinstance(record.partner_id.id, models.NewId): - hotel_cal_obj.send_reservation_notification( - record.generate_bus_values(naction, ntype, ntitle)) - - @api.model - def swap_reservations(self, fromReservsIds, toReservsIds): - from_reservs = self.env['hotel.reservation'].browse(fromReservsIds) - to_reservs = self.env['hotel.reservation'].browse(toReservsIds) - - if not any(from_reservs) or not any(to_reservs): - raise ValidationError(_("Invalid swap parameters")) - - max_from_persons = max( - from_reservs.mapped(lambda x: x.adults)) - max_to_persons = max( - to_reservs.mapped(lambda x: x.adults)) - - from_room = from_reservs[0].room_id - to_room = to_reservs[0].room_id - from_overbooking = from_reservs[0].overbooking - to_overbooking = to_reservs[0].overbooking - - if max_from_persons > to_room.capacity or \ - max_to_persons > from_room.capacity: - raise ValidationError("Invalid swap operation: wrong capacity") - - for record in from_reservs: - record.with_context({'ignore_avail_restrictions': True}).write({ - 'room_id': to_room.id, - 'overbooking': to_overbooking, - }) - for record in to_reservs: - record.with_context({'ignore_avail_restrictions': True}).write({ - 'room_id': from_room.id, - 'overbooking': from_overbooking, - }) - - return True diff --git a/hotel_calendar/models/inherited_hotel_room_type_restriction_item.py b/hotel_calendar/models/inherited_hotel_room_type_restriction_item.py deleted file mode 100644 index d24cc7b5c..000000000 --- a/hotel_calendar/models/inherited_hotel_room_type_restriction_item.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import logging -from odoo import models, api -_logger = logging.getLogger(__name__) - - -class HotelRoomTypeRestrictionItem(models.Model): - _inherit = 'hotel.room.type.restriction.item' - - # ORM overrides - @api.model - def create(self, vals): - res = super(HotelRoomTypeRestrictionItem, self).create(vals) - if res.restriction_id.id == self.env.user.hotel_id.default_restriction_id.id: - self.env['bus.hotel.calendar'].send_restriction_notification({ - 'restriction_id': res.restriction_id.id, - 'date': res.date, - 'min_stay': res.min_stay, - 'min_stay_arrival': res.min_stay_arrival, - 'max_stay': res.max_stay, - 'max_stay_arrival': res.max_stay_arrival, - 'closed': res.closed, - 'closed_departure': res.closed_departure, - 'closed_arrival': res.closed_arrival, - 'room_type_id': res.room_type_id.id, - 'id': res.id, - }) - return res - - - def write(self, vals): - ret_vals = super(HotelRoomTypeRestrictionItem, self).write(vals) - bus_hotel_calendar_obj = self.env['bus.hotel.calendar'] - for record in self: - bus_hotel_calendar_obj.send_restriction_notification({ - 'restriction_id': record.restriction_id.id, - 'date': record.date, - 'min_stay': record.min_stay, - 'min_stay_arrival': record.min_stay_arrival, - 'max_stay': record.max_stay, - 'max_stay_arrival': record.max_stay_arrival, - 'closed': record.closed, - 'closed_departure': record.closed_departure, - 'closed_arrival': record.closed_arrival, - 'room_type_id': record.room_type_id.id, - 'id': record.id, - }) - return ret_vals - - - def unlink(self): - default_restriction_id = self.env.user.hotel_id.default_restriction_id.id - # Construct dictionary with relevant info of removed records - unlink_vals = [] - for record in self: - if record.restriction_id.id != default_restriction_id: - continue - unlink_vals.append({ - 'restriction_id': record.restriction_id.id, - 'date': record.date, - 'min_stay': 0, - 'min_stay_arrival': 0, - 'max_stay': 0, - 'max_stay_arrival': 0, - 'closed': False, - 'closed_departure': False, - 'closed_arrival': False, - 'room_type_id': record.room_type_id.id, - 'id': record.id, - }) - res = super(HotelRoomTypeRestrictionItem, self).unlink() - bus_hotel_calendar_obj = self.env['bus.hotel.calendar'] - for uval in unlink_vals: - bus_hotel_calendar_obj.send_restriction_notification(uval) - return res diff --git a/hotel_calendar/models/inherited_product_pricelist_item.py b/hotel_calendar/models/inherited_product_pricelist_item.py deleted file mode 100644 index e55cb0cb8..000000000 --- a/hotel_calendar/models/inherited_product_pricelist_item.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, api - - -class ProductPricelistItem(models.Model): - _inherit = 'product.pricelist.item' - - # ORM overrides - @api.model - def create(self, vals): - res = super(ProductPricelistItem, self).create(vals) - # TODO: refactoring res.config.settings', 'default_pricelist_id' by the current hotel.property.pricelist_id - pricelist_default_id = self.env['ir.default'].sudo().get( - 'res.config.settings', 'default_pricelist_id') - if pricelist_default_id: - pricelist_default_id = int(pricelist_default_id) - pricelist_id = res.pricelist_id.id - product_tmpl_id = res.product_tmpl_id.id - date_start = res.date_start - room_type = self.env['hotel.room.type'].search([ - ('product_id.product_tmpl_id', '=', product_tmpl_id) - ], limit=1) - if pricelist_id == pricelist_default_id and room_type: - prod = room_type.product_id.with_context( - quantity=1, - date=date_start, - pricelist=pricelist_id) - prod_price = prod.price - - self.env['bus.hotel.calendar'].send_pricelist_notification({ - 'pricelist_id': pricelist_id, - 'date': date_start, - 'room_id': room_type.id, - 'price': prod_price, - 'id': self.id, - }) - return res - - - def write(self, vals): - # TODO: refactoring res.config.settings', 'default_pricelist_id' by the current hotel.property.pricelist_id - pricelist_default_id = self.env['ir.default'].sudo().get( - 'res.config.settings', 'default_pricelist_id') - if pricelist_default_id: - pricelist_default_id = int(pricelist_default_id) - ret_vals = super(ProductPricelistItem, self).write(vals) - - bus_calendar_obj = self.env['bus.hotel.calendar'] - room_type_obj = self.env['hotel.room.type'] - if vals.get('fixed_price'): - for record in self: - pricelist_id = vals.get('pricelist_id') or \ - record.pricelist_id.id - if pricelist_id != pricelist_default_id: - continue - date_start = vals.get('date_start') or record.date_start - product_tmpl_id = vals.get('product_tmpl_id') or \ - record.product_tmpl_id.id - room_type = room_type_obj.search([ - ('product_id.product_tmpl_id', '=', product_tmpl_id) - ], limit=1) - - if room_type and date_start: - prod = room_type.product_id.with_context( - quantity=1, - date=date_start, - pricelist=pricelist_id) - prod_price = prod.price - - bus_calendar_obj.send_pricelist_notification({ - 'pricelist_id': pricelist_id, - 'date': date_start, - 'room_id': room_type.id, - 'price': prod_price, - 'id': record.id, - }) - return ret_vals - - - def unlink(self): - # TODO: refactoring res.config.settings', 'default_pricelist_id' by the current hotel.property.pricelist_id - pricelist_default_id = self.env['ir.default'].sudo().get( - 'res.config.settings', 'default_pricelist_id') - if pricelist_default_id: - pricelist_default_id = int(pricelist_default_id) - # Construct dictionary with relevant info of removed records - unlink_vals = [] - for record in self: - if record.pricelist_id.id != pricelist_default_id: - continue - room_type = self.env['hotel.room.type'].search([ - ('product_id.product_tmpl_id', '=', record.product_tmpl_id.id) - ], limit=1) - unlink_vals.append({ - 'pricelist_id': record.pricelist_id.id, - 'date': record.date_start, - 'room': room_type, - 'id': record.id, - }) - # Do Normal Stuff - res = super(ProductPricelistItem, self).unlink() - # Do extra operations - bus_calendar_obj = self.env['bus.hotel.calendar'] - for vals in unlink_vals: - pricelist_id = vals['pricelist_id'] - date_start = vals['date'] - room_type = vals['room'] - prod = room_type.product_id.with_context( - quantity=1, - date=date_start, - pricelist=pricelist_id) - - # Send Notification to update calendar pricelist - bus_calendar_obj.send_pricelist_notification({ - 'pricelist_id': pricelist_id, - 'date': date_start, - 'room_id': room_type.id, - 'price': prod.price, - 'id': vals['id'], - }) - return res diff --git a/hotel_calendar/models/inherited_res_company.py b/hotel_calendar/models/inherited_res_company.py deleted file mode 100644 index 34dcf8068..000000000 --- a/hotel_calendar/models/inherited_res_company.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2019 Pablo Quesada -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields - - -class ResCompany(models.Model): - _inherit = 'res.company' - - # Fields declaration - color_pre_reservation = fields.Char('Pre-reservation', default='#A24680') - color_reservation = fields.Char('Confirmed Reservation ', default='#7C7BAD') - color_reservation_pay = fields.Char('Paid Reservation', default='#584D76') - color_stay = fields.Char('Checkin', default='#FF4040') - color_stay_pay = fields.Char('Paid Checkin', default='#82BF07') - color_checkout = fields.Char('Checkout', default='#7E7E7E') - color_dontsell = fields.Char('Dont Sell', default='#000000') - color_staff = fields.Char('Staff', default='#C08686') - color_to_assign = fields.Char('Ota Reservation to Assign', default='#ED722E') - color_payment_pending = fields.Char('Payment Pending', default='#A24689') - color_letter_pre_reservation = fields.Char('Letter Pre-reservation', default='#FFFFFF') - color_letter_reservation = fields.Char('Letter Confirmed Reservation ', default='#FFFFFF') - color_letter_reservation_pay = fields.Char('Letter Paid Reservation', default='#FFFFFF') - color_letter_stay = fields.Char('Letter Checkin', default='#FFFFFF') - color_letter_stay_pay = fields.Char('Letter Stay Pay', default='#FFFFFF') - color_letter_checkout = fields.Char('Letter Checkout', default='#FFFFFF') - color_letter_dontsell = fields.Char('Letter Dont Sell', default='#FFFFFF') - color_letter_staff = fields.Char('Letter Staff', default='#FFFFFF') - color_letter_to_assign = fields.Char('Letter Ota to Assign', default='#FFFFFF') - color_letter_payment_pending = fields.Char('Letter Payment Pending', default='#FFFFFF') diff --git a/hotel_calendar/models/inherited_res_users.py b/hotel_calendar/models/inherited_res_users.py deleted file mode 100644 index 55725cf5b..000000000 --- a/hotel_calendar/models/inherited_res_users.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields - - -class ResUsers(models.Model): - _inherit = 'res.users' - - # Fields declaration - pms_show_notifications = fields.Boolean('Show Notifications', default=True) - pms_show_pricelist = fields.Boolean('Show Pricelist', default=True) - pms_show_availability = fields.Boolean('Show Availability', default=True) - - # ORM overrides - def __init__(self, pool, cr): - """ Override of __init__ to add access rights. - Access rights are disabled by default, but allowed on some specific - fields defined in self.SELF_{READ/WRITE}ABLE_FIELDS. - """ - super(ResUsers, self).__init__(pool, cr) - # duplicate list to avoid modifying the original reference - type(self).SELF_WRITEABLE_FIELDS = list(self.SELF_WRITEABLE_FIELDS) - type(self).SELF_WRITEABLE_FIELDS.extend([ - 'pms_show_notifications', - 'pms_show_pricelist', - 'pms_show_availability', - ]) - # duplicate list to avoid modifying the original reference - type(self).SELF_READABLE_FIELDS = list(self.SELF_READABLE_FIELDS) - type(self).SELF_READABLE_FIELDS.extend([ - 'pms_show_notifications', - 'pms_show_pricelist', - 'pms_show_availability', - ]) diff --git a/hotel_calendar/models/ir_actions_act_window_view.py b/hotel_calendar/models/ir_actions_act_window_view.py deleted file mode 100644 index 44022e3dc..000000000 --- a/hotel_calendar/models/ir_actions_act_window_view.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields - - -class ActWindowView(models.Model): - _inherit = 'ir.actions.act_window.view' - - # Fields declaration - view_mode = fields.Selection(selection_add=[('pms', "PMS"), ('mpms', 'Management PMS')]) diff --git a/hotel_calendar/models/ir_ui_view.py b/hotel_calendar/models/ir_ui_view.py deleted file mode 100644 index e878c9d6e..000000000 --- a/hotel_calendar/models/ir_ui_view.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields - - -class View(models.Model): - _inherit = 'ir.ui.view' - - # Fields declaration - type = fields.Selection(selection_add=[('pms', "PMS"), ('mpms', 'Management PMS')]) diff --git a/hotel_calendar/readme/CONFIGURE.rst b/hotel_calendar/readme/CONFIGURE.rst deleted file mode 100644 index a3c81f46a..000000000 --- a/hotel_calendar/readme/CONFIGURE.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. [ This file is optional, it should explain how to configure -the module before using it; it is aimed at advanced users. ] - -No action required. The module is pre-configured with default values. - -You will find the hotel calendar settings in `Settings > Users & Companies > Hotels > Your Hotel > Calendar Settings Tab`. - -Reservation colors can be configured by company in `Settings > Users & Companies > Companies > Your Company > Hotel Preferences Tab.` \ No newline at end of file diff --git a/hotel_calendar/readme/CONTRIBUTORS.rst b/hotel_calendar/readme/CONTRIBUTORS.rst deleted file mode 100644 index c0ca43ca9..000000000 --- a/hotel_calendar/readme/CONTRIBUTORS.rst +++ /dev/null @@ -1,2 +0,0 @@ -# authors are taken from the __manifest__.py file -* Pablo Quesada \ No newline at end of file diff --git a/hotel_calendar/readme/CREDITS.rst b/hotel_calendar/readme/CREDITS.rst deleted file mode 100644 index 61d2f78cc..000000000 --- a/hotel_calendar/readme/CREDITS.rst +++ /dev/null @@ -1,2 +0,0 @@ -.. [ This file is optional and contains additional credits, other than -authors, contributors, and maintainers. ] \ No newline at end of file diff --git a/hotel_calendar/readme/DESCRIPTION.rst b/hotel_calendar/readme/DESCRIPTION.rst deleted file mode 100644 index a44f8af2a..000000000 --- a/hotel_calendar/readme/DESCRIPTION.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. [ This file must be max 2-3 paragraphs, and is required. ] - -This module extends the functionality of roomdoo. - -The calendar displays your rooms in a range of dates. -The rooms can be filtered by class, type or even amenities creating new calendars for easily manage all your reservations. -You can manage prices and restrictions day by day using the calendar management view. - -This module adds two new view types: ``pms`` and ``mpms``. -The module also incorporates a longpolling for delivering instant notification when using the calendar. \ No newline at end of file diff --git a/hotel_calendar/readme/INSTALL.rst b/hotel_calendar/readme/INSTALL.rst deleted file mode 100644 index 96fe22619..000000000 --- a/hotel_calendar/readme/INSTALL.rst +++ /dev/null @@ -1,5 +0,0 @@ -.. [ This file must only be present if there are very specific -installation instructions, such as installing non-python dependencies. The audience is systems administrators. ] - -This module depends on modules ``hotel``, ``bus``, ``web``, ``calendar``, and ``web_widget_color``. -Ensure yourself to have all them in your addons list. \ No newline at end of file diff --git a/hotel_calendar/readme/ROADMAP.rst b/hotel_calendar/readme/ROADMAP.rst deleted file mode 100644 index 3f8c38c78..000000000 --- a/hotel_calendar/readme/ROADMAP.rst +++ /dev/null @@ -1,5 +0,0 @@ -.. [ Enumerate known caveats and future potential improvements. -It is mostly intended for end-users, and can also help potential new contributors discovering new features to implement. ] - -- [ ] Implement the calendar view as an Odoo native view type. -- [ ] Re-design the calendar using SVG or other Odoo native approach. \ No newline at end of file diff --git a/hotel_calendar/readme/USAGE.rst b/hotel_calendar/readme/USAGE.rst deleted file mode 100644 index e704b7dd3..000000000 --- a/hotel_calendar/readme/USAGE.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. [ This file must be present and contains the usage instructions -for end-users. As all other rst files included in the README, it MUST NOT contain reStructuredText sections -only body text (paragraphs, lists, tables, etc). Should you need a more elaborate structure to explain the addon, -please create a Sphinx documentation (which may include this file as a "quick start" section). ] - -To use this module, you need to: - -* Go to Hotel Calendar menu for managing reservations. -* Go to Hotel Calendar Management for managing prices and restrictions. -* Go to Hotel Management/Configuration/Calendars menu for managing calendar tabs. - -Shortcuts -* `CTRL + LEFT MOUSE CLICK`` on a reservation - Start reservation swap -* ``ESC`` - cancel active action (ie. swap) diff --git a/hotel_calendar/security/ir.model.access.csv b/hotel_calendar/security/ir.model.access.csv deleted file mode 100644 index c8305754e..000000000 --- a/hotel_calendar/security/ir.model.access.csv +++ /dev/null @@ -1,4 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_hotel_product_pricelist_item_call,hotel_calendar.pricelist_item_call,hotel_calendar.model_product_pricelist_item,hotel.group_hotel_call,1,1,1,1 -access_hotel_product_pricelist_item_user,hotel_calendar.pricelist_item_use,hotel_calendar.model_product_pricelist_item,hotel.group_hotel_user,1,1,1,1 -access_hotel_calendar,access_hotel_calendar,model_hotel_calendar,base.group_user,1,0,0,0 diff --git a/hotel_calendar/static/description/icon.png b/hotel_calendar/static/description/icon.png deleted file mode 100644 index 45ab4dc2931dc4e4d40244268a9a071e73a08778..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4155 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 diff --git a/hotel_calendar/static/description/icon_calendar_configurator.png b/hotel_calendar/static/description/icon_calendar_configurator.png deleted file mode 100644 index bce8eb8f191c8de2cbf5b05a070194e7079edbde..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5221 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 g`D^Hx{JOih - * Alexandre Díaz - */ - - -/* .openerp .oe-view-manager { - overflow: initial !important; -} - */ - -.o_hotel_calendar_view { - display: flex; - height: 100%; -} - -.nopadding { - padding: 0 !important; - margin: 0 !important; -} - - - -#pms-menu { - overflow: auto; - background-color: #f8f8f8; - height: 100%; - padding: 0 2.5em; -} - -#pms-menu .input-group span, #pms-menu input { - border-radius: 0; -} - -#pms-menu button { - border-radius: 0; - border: 1px solid lightgray; - font-size: x-small; -} - -#pms-calendar: { - flex: 1 1 auto; - overflow: auto; -} - -#pms-search { - position: fixed; - z-index: 9; -} - -#hcal_widget { - max-height: 100%; -} - -#multicalendar_panels { - background-color: white; - border-left: 1px solid #ddd; -} - -.nav-tabs > li > a { - border-radius: 0; -} - -.nav-tabs { - padding-top: 4px; -} - -/* BUTTON STATES */ -.navbar-default { - border-color: #f8f8f8; -} - - -.unify-enabled { - background-color: #43A200; - color: white; -} - -.divide-enabled { - background-color: #43A200; - color: white; -} - -.overbooking-enabled { - background-color: #43A200; - color: white; -} - -.cancelled-enabled { - background-color: #43A200; -} - -.swap-from { - background-color: #E7CF1D; - color: #7C7BAD; -} - -.swap-to { - background-color: #43A200; - color: white; -} - -.unify-enabled i, .divide-enabled i, .cancelled-enabled i, .overbooking-enabled i, .swap-to i { - color: white; -} - -.swap-from i { - color: #7C7BAD; -} - -/* END: BUTTON STATES */ - -input#bookings_search { - border-top-left-radius: 0; - border-top-right-radius: 0; - border: 1px solid lightgray; -} - -#pms-menu .menu-date-box { - margin-top: 2px !important; -} - -#pms-menu .menu-button-box, #pms-menu .menu-search-box, #pms-menu .menu-filter-box { - margin-top: 1em !important; -} - -#pms-menu .menu-filter-box .filter-record { - margin-top: 1em; -} - -#pms-menu .menu-filter-box h4 { - cursor: pointer; -} -#pms-menu .menu-filter-box h4 i { - transition: all 0.5s ease; -} - -#pms-menu .button-box { - text-align: left; - min-height: 3.5em; - padding: 3px; - transition: all 0.5s ease; - overflow: hidden; -} - -.overbooking-highlight { - color: white; - background-color: #FFA500; -} - -.overbooking-highlight i { - color: white; -} - -.multi-calendar-tab-plus { - background-color: darkgray; - font-weight: bold; - color: white; -} - -#pms-menu div[id^=btn_] { - padding: 2px; -} - -.warn-message { - text-align: center; - line-height: 100vh; - font-size: xx-large; - color: gray; -} - -/** SELECT 2 **/ -#pms-menu .select2-container, #pms-menu .select2-choice { - border-radius: 0; -} -#pms-menu .select2-choice { - height: 100%; - padding: 2px; -} - -/** BOOTSTRAP **/ -.badge-danger { - background-color: #d34f2a !important; -} - -/** ODOO **/ -.o_chat_window { - z-index: 8 !important; -} - -.o_button_icon { - display: inline-block; - width: 37px; - padding: 0px 3px; - text-align: center; - vertical-align: middle; - color: #7C7BAD; - font-size: 24px; -} - -.o_button_text { - display: inline-block; - vertical-align: middle; - max-width: 70%; - line-height: 1.2; - text-align: left; -} - -.text-hidden-xs { - overflow: hidden; - text-overflow: ellipsis; -} - -/** POPOVER **/ -.popover-content { - font-family: Garuda, sans-serif; - font-size: 12px; - padding: 0px; -} -.popover .h3, .popover h3 { - margin-top: 2px; - margin-bottom: 2px; -} -.popover { - max-width: 400px; - border-radius: 0px; - padding: 0px; -} -.popover .container { - max-width: 100%; -} -.popover .container p { - margin-top: 2px; - margin-bottom: 0px; -} -.popover .container p.email { - word-wrap: break-word; -} -.popover .container p.board { - margin-top: 0px; -} -.popover header { - font-size: 1.2em; -} - -.fa-1_5x { - font-size: 1.5em; -} -.fa-2_5x { - font-size: 2.5em; -} -.fa-text-inside { - font-family: Garuda, sans-serif; - font-size: 12px; -} -.popover .col-sm-2, .popover .col-sm-4, .popover .col-sm-6, .popover .col-sm-12 { - padding-top: 3px; - padding-bottom: 3px; -} -/* custom styles for popover info */ -.popover .circle { - height:35px; - min-width:35px; - line-height:35px; - border-radius:50px; - text-align:center; - color:#fff; - background:#777; - margin-right: .65em; - padding: 0 5px; -} - -.popover .bg-gray-lighter { - background-color: #ddd; - color: #777; -} -.popover .text-gray-dark { - color: #777; -} - - -/* Spacing in Bootstrap v4.0 */ -.mt-3 { - margin-top: 3px; -} -.mt-5 { - margin-top: 5px; -} -.mt-10 { - margin-top: 10px !important; -} -.my-10 { - margin-top: 10px !important; - margin-bottom: 10px !important; -} -.mt-25 { - margin-top: 25px; -} -.mr-5 { - margin-right: 5px; -} -.mx-15 { - margin-left: 15px; - margin-right: 15px; -} -.mx-25 { - margin-left: 25px; - margin-right: 25px; -} -.px-0 { - padding-left: 0px; - padding-right: 0px; -} -.py-5 { - padding-top: 5px; - padding-bottom: 5px; -} -.pt-9 { - padding-top: 9px; -} -.pb-3 { - padding-bottom: 3px; -} -.pb-10 { - padding-bottom: 10px; -} -.pl-5 { - padding-left: 5px; -} -.pr-0 { - padding-right: 0px; -} - -/* WARNING: The .row-eq-height class uses CSS3's flexbox layout mode, - which is not supported in Internet Explorer 9 and below. */ -.row-eq-height { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; -} - -/* TODO: Use Odoo Colours based on http://www.odoo.com/openerp_website/static/src/less/variables.less */ - -div.diagonal { - overflow: hidden; - background-color: #777; -} -.diagonal:before { - content: ""; - border-top: 700px solid #777; - border-right: 500px solid #ddd; - position: absolute; - left: 40%; - bottom: 0; -} -div.diagonal_pending_amount { - background-color: #A24689; -} -.diagonal_pending_amount::before { - border-top-color: #A24689; -} - -div.triangle-right { - overflow: hidden; - color: white; - background-color: #7c7bad; -} -.triangle-right:before { - content: ""; - position: absolute; - border: 26px solid #ddd; - height: 0; - width: 100%; - left: 47%; - top: 0; - bottom: 0; - margin: auto; - border-left-color: transparent; -} - -div.on-top { - position: inherit; -} -div.pull-right-custom { - float: right !important; - margin-right: 15px; - color: #777; - text-align: right; -} - -@keyframes blinker { - 50% { opacity: 0; } -} \ No newline at end of file diff --git a/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js b/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js deleted file mode 100644 index db93063ec..000000000 --- a/hotel_calendar/static/src/js/views/calendar/hotel_calendar_controller.js +++ /dev/null @@ -1,1113 +0,0 @@ -// Copyright 2018 Alexandre Díaz -// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('hotel_calendar.PMSCalendarController', function (require) { -"use strict"; - -var AbstractController = require('web.AbstractController'), - ViewDialogs = require('web.view_dialogs'), - Dialog = require('web.Dialog'), - Core = require('web.core'), - Bus = require('bus.bus').bus, - HotelConstants = require('hotel_calendar.Constants'), - MultiCalendar = require('hotel_calendar.MultiCalendar'), - - _t = Core._t, - QWeb = Core.qweb; - -var PMSCalendarController = AbstractController.extend({ - custom_events: _.extend({}, AbstractController.prototype.custom_events, { - onLoadViewFilters: '_onLoadViewFilters', - onViewAttached: '_onViewAttached', - onApplyFilters: '_onApplyFilters', - }), - - _last_dates: [], - - init: function (parent, model, renderer, params) { - this._super.apply(this, arguments); - this.displayName = params.displayName; - this.formViewId = params.formViewId; - this.context = params.context; - - this._multi_calendar = new MultiCalendar(this); - - Bus.on("notification", this, this._onBusNotification); - }, - - start: function() { - this._super.apply(this, arguments); - var self = this; - - this._multi_calendar.setElement(this.renderer.$el.find('#hcal_widget')); - this._multi_calendar.reset(); - this._multi_calendar.start(); - - this._assign_multi_calendar_events(); - this._load_calendar_settings(); - }, - - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- - savePricelist: function (calendar, pricelist_id, pricelist) { - var self = this; - var oparams = [pricelist_id, false, pricelist, {}, {}]; - this.model.save_changes(oparams).then(function(results){ - $(calendar.btnSaveChanges).removeClass('need-save'); - $(calendar.edtable).find('.hcal-input-changed').removeClass('hcal-input-changed'); - }); - }, - - updateReservations: function (calendar, ids, values, oldReserv, newReserv) { - var self = this; - return this.model.update_records(ids, values).then(function(result){ - // Remove OB Room Row? - if ((oldReserv.room.overbooking && !newReserv.room.overbooking) || (oldReserv.room.cancelled && !newReserv.room.cancelled)) { - self._multi_calendar.remove_extra_room_row(oldReserv, true); - } - }).fail(function(err, errev){ - calendar.replaceReservation(newReserv, oldReserv); - }); - }, - - swapReservations: function (fromIds, toIds, detail, refFromReservDiv, refToReservDiv) { - var self = this; - return this.model.swap_reservations(fromIds, toIds).then(function(results){ - var allReservs = detail.inReservs.concat(detail.outReservs); - for (var nreserv of allReservs) { - self.renderer.$el.find(nreserv._html).stop(true); - } - }).fail(function(err, errev){ - for (var nreserv of detail.inReservs) { - self.renderer.$el.find(nreserv._html).animate({'top': refFromReservDiv.style.top}, 'fast'); - } - for (var nreserv of detail.outReservs) { - self.renderer.$el.find(nreserv._html).animate({'top': refToReservDiv.style.top}, 'fast'); - } - - self._multi_calendar.swap_reservations(detail.outReservs, detail.inReservs); - }); - }, - - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - - _updateRecord: function (record) { - return this.model.updateRecord(record).then(this.reload.bind(this)); - }, - - _load_calendars: function (ev) { - var self = this; - - /** DO MAGIC **/ - var hcal_dates = this.renderer.get_view_filter_dates(); - var oparams = [ - hcal_dates[0].subtract(1, 'd').format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT), - hcal_dates[1].format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT) - ]; - - this.model.get_calendar_data(oparams).then(function(results){ - self._multi_calendar._days_tooltips = results['events']; - self._multi_calendar._reserv_tooltips = results['tooltips']; - var rooms = []; - for (var r of results['rooms']) { - var nroom = new HRoom( - r['id'], - r['name'], - r['capacity'], - r['class_name'], - r['shared'], - r['price'] - ); - nroom.addUserData({ - 'room_type_name': r['room_type_name'], - 'room_type_id': r['room_type_id'], - 'floor_id': r['floor_id'], - 'amenities': r['amenity_ids'], - 'class_id': r['class_id'], - }); - rooms.push(nroom); - } - - var reservs = []; - for (var r of results['reservations']) { - var nreserv = self._create_reservation_obj(r); - reservs.push(nreserv); - } - - var options = { - startDate: HotelCalendar.toMomentUTC(self._last_dates[0], HotelConstants.ODOO_DATETIME_MOMENT_FORMAT), - days: self._view_options['days'], - rooms: rooms, - endOfWeek: parseInt(self._view_options['eday_week']) || 6, - divideRoomsByCapacity: self._view_options['divide_rooms_by_capacity'] || false, - allowInvalidActions: self._view_options['allow_invalid_actions'] || false, - assistedMovement: self._view_options['assisted_movement'] || false, - showPricelist: self._view_options['show_pricelist'] || false, - showAvailability: self._view_options['show_availability'] || false, - showNumRooms: self._view_options['show_num_rooms'] || 0, - endOfWeekOffset: self._view_options['eday_week_offset'] || 0 - }; - - self._multi_calendar.set_options(options); - self._multi_calendar.set_datasets(results['pricelist'], results['restrictions'], reservs); - self._multi_calendar.set_base_element(self.renderer.$el[0]); - - for (var calendar_record of results['calendars']) { - var calendar_index = self._multi_calendar.create_calendar(calendar_record); - var domain = self._generate_calendar_filters_domain(calendar_record); - var calendar = self._multi_calendar.get_calendar(calendar_index+1); - calendar.setDomain(HotelCalendar.DOMAIN.ROOMS, domain); - } - - self._multi_calendar.set_active_calendar(self._multi_calendar._calendars.length-1); - self._update_buttons_counter(); - }); - }, - - _generate_calendar_filters_domain: function(calendar) { - var domain = []; - if (calendar['segmentation_ids'] && calendar['segmentation_ids'].length > 0) { - domain.push(['class_id', 'in', calendar['segmentation_ids']]); - } - if (calendar['location_ids'] && calendar['location_ids'].length > 0) { - domain.push(['floor_id', 'in', calendar['location_ids']]); - } - if (calendar['amenity_ids'] && calendar['amenity_ids'].length > 0) { - domain.push(['amenities', 'in', calendar['amenity_ids']]); - } - if (calendar['room_type_ids'] && calendar['room_type_ids'].length > 0) { - domain.push(['room_type_id', 'some', calendar['room_type_ids']]); - } - return domain; - }, - - _load_calendar_settings: function (ev) { - var self = this; - return this.model.get_hcalendar_settings().then(function(options){ - self._view_options = options; - - if (['xs', 'md'].indexOf(self._find_bootstrap_environment()) >= 0) { - self._view_options['days'] = 7; - } - - var date_begin = moment().local().startOf('day'); - var days = self._view_options['days']; - if (self._view_options['days'] === 'month') { - days = date_begin.daysInMonth(); - } - self._last_dates[0] = date_begin.clone(); - self._last_dates[1] = date_begin.clone().add(days, 'd'); - - var $dateTimePickerBegin = self.renderer.$el.find('#pms-menu #date_begin'); - var $dateEndDays = self.renderer.$el.find('#pms-menu #date_end_days'); - $dateTimePickerBegin.data("ignore_onchange", true); - $dateTimePickerBegin.data("DateTimePicker").date(date_begin); - $dateEndDays.val(self._view_options['days']); - - self._load_calendars(); - self._assign_view_events(); - }); - }, - - _reload_active_calendar: function() { - var self = this; - var active_calendar = this._multi_calendar.get_active_calendar(); - var filterDates = active_calendar.getDates(); - // Clip dates - var dfrom = filterDates[0].clone(), - dto = filterDates[1].clone(); - - if (filterDates[0].isBetween(this._last_dates[0], this._last_dates[1], 'days') && filterDates[1].isAfter(this._last_dates[1], 'day')) { - dfrom = this._last_dates[1].clone(); - } else if (this._last_dates[0].isBetween(filterDates[0], filterDates[1], 'days') && this._last_dates[1].isAfter(filterDates[0], 'day')) { - dto = this._last_dates[0].clone(); - } - - var oparams = [ - dfrom.subtract(1, 'd').format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT), - dto.format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT), - false - ]; - - this.model.get_calendar_data(oparams).then(function(results){ - var reservs = []; - for (var r of results['reservations']) { - var nreserv = self._create_reservation_obj(r); - reservs.push(nreserv); - } - - self._multi_calendar._reserv_tooltips = _.extend(this._multi_calendar._reserv_tooltips, results['tooltips']); - _.defer(function(){ - self._multi_calendar.merge_days_tooltips(results['events']); - self._multi_calendar.merge_pricelist(results['pricelist'], active_calendar); - self._multi_calendar.merge_restrictions(results['restrictions'], active_calendar); - self._multi_calendar.merge_reservations(reservs, active_calendar); - - self._multi_calendar._assign_extra_info(active_calendar); - self._update_buttons_counter(); - }); - }.bind(this)).then(function(){ - self._last_dates = filterDates; - }); - }, - - _assign_view_events: function() { - var self = this; - var $dateTimePickerBegin = this.renderer.$el.find('#pms-menu #date_begin'); - var $dateEndDays = this.renderer.$el.find('#pms-menu #date_end_days'); - $dateTimePickerBegin.on("dp.change", function (e) { - $dateTimePickerBegin.data("DateTimePicker").hide(); - self._on_change_filter_date(); - }); - $dateEndDays.on("change", function (e) { - self._on_change_filter_date(); - }); - - this.renderer.$el.find("#btn_swap > button").on('click', function(ev){ - var active_calendar = self._multi_calendar.get_active_calendar(); - var hcalSwapMode = active_calendar.getSwapMode(); - var $btn = $(this); - if (hcalSwapMode === HotelCalendar.MODE.NONE) { - active_calendar.setSwapMode(HotelCalendar.MODE.SWAP_FROM); - $("#btn_swap span.ntext").html(_t("Continue")); - $btn.removeClass('swap-to'); - $btn.addClass('swap-from'); - } else if (active_calendar.getReservationAction().inReservations.length > 0 && hcalSwapMode === HotelCalendar.MODE.SWAP_FROM) { - active_calendar.setSwapMode(HotelCalendar.MODE.SWAP_TO); - $("#btn_swap span.ntext").html(_t("End")); - $btn.removeClass('swap-from'); - $btn.addClass('swap-to'); - } else { - active_calendar.setSwapMode(HotelCalendar.MODE.NONE); - $("#btn_swap span.ntext").html(_t("Start Swap")); - $btn.removeClass('swap-from swap-to'); - } - }); - - this.renderer.$el.find('#pms-menu #btn_action_overbooking > button').on('click', function(ev){ - var active_calendar = self._multi_calendar.get_active_calendar(); - active_calendar.toggleOverbookingsVisibility(); - if (active_calendar.options.showOverbookings) { - $(this).addClass('overbooking-enabled'); - } else { - $(this).removeClass('overbooking-enabled'); - } - active_calendar.addReservations(_.reject(self._multi_calendar._dataset['reservations'], {overbooking:false})); - }); - - this.renderer.$el.find('#pms-menu #btn_action_cancelled > button').on('click', function(ev){ - var active_calendar = self._multi_calendar.get_active_calendar(); - active_calendar.toggleCancelledVisibility(); - if (active_calendar.options.showCancelled) { - $(this).addClass('cancelled-enabled'); - } else { - $(this).removeClass('cancelled-enabled'); - } - active_calendar.addReservations(_.reject(self._multi_calendar._dataset['reservations'], {cancelled:false})); - }); - - this.renderer.$el.find('#pms-menu #btn_action_divide > button').on('click', function(ev){ - var active_calendar = self._multi_calendar.get_active_calendar(); - var cur_mode = active_calendar.getSelectionMode(); - active_calendar.setSelectionMode(cur_mode===HotelCalendar.ACTION.DIVIDE?HotelCalendar.MODE.NONE:HotelCalendar.ACTION.DIVIDE); - }); - - this.renderer.$el.find('#pms-menu #btn_action_unify > button').on('click', function(ev){ - var active_calendar = self._multi_calendar.get_active_calendar(); - var cur_mode = active_calendar.getSelectionMode(); - active_calendar.setSelectionMode(cur_mode===HotelCalendar.ACTION.UNIFY?HotelCalendar.MODE.NONE:HotelCalendar.ACTION.UNIFY); - }); - - this.renderer.$el.find('#pms-menu #btn_save_calendar_record').on('click', function(ev){ - var active_calendar_record = self._multi_calendar.get_calendar_record(self._multi_calendar.get_active_index()); - - var name = self.renderer.$el.find('#pms-menu #calendar_name').val(); - var category = _.map(self.renderer.$el.find('#pms-menu #type_list').val(), function(item){ return +item; }); - var floor = _.map(self.renderer.$el.find('#pms-menu #floor_list').val(), function(item){ return +item; }); - var amenities = _.map(self.renderer.$el.find('#pms-menu #amenities_list').val(), function(item){ return +item; }); - var types = _.map(self.renderer.$el.find('#pms-menu #virtual_list').val(), function(item){ return +item; }); - var oparams = { - 'name': name, - 'segmentation_ids': [[6, false, category]], - 'location_ids': [[6, false, floor]], - 'amenity_ids': [[6, false, amenities]], - 'room_type_ids': [[6, false, types]], - } - - self._multi_calendar.update_active_tab_name(name); - self.model.update_or_create_calendar_record(active_calendar_record['id'], oparams).then(function(){ - active_calendar_record.name = name; - active_calendar_record.segmentation_ids = category; - active_calendar_record.location_ids = floor; - active_calendar_record.amenity_ids = amenities; - active_calendar_record.room_type_ids = types; - }).fail(function(){ - self._multi_calendar.update_active_tab_name(active_calendar_record.name); - }); - }); - - this.renderer.$el.find('#pms-menu #btn_reload_calendar_filters').on('click', function(ev){ - var active_calendar_record = self._multi_calendar.get_calendar_record(self._multi_calendar.get_active_index()); - self._multi_calendar.update_active_tab_name(active_calendar_record.name); - var $calendar_name = this.renderer.$el.find('#pms-menu .menu-filter-box #calendar_name'); - $calendar_name.val(active_calendar_record.name); - self._refresh_filters({ - 'class_id': active_calendar_record['segmentation_ids'], - 'floor_id': active_calendar_record['location_ids'], - 'amenities': active_calendar_record['amenity_ids'], - 'room_type_id': active_calendar_record['room_type_ids'], - }); - }); - - this.renderer.$el.find('#pms-menu .menu-filter-box #filters').on('show.bs.collapse', function(ev){ - self.renderer.$el.find('#pms-menu .menu-filter-box h4 i.fa').css({transform: 'rotate(90deg)'}); - }).on('hide.bs.collapse', function(ev){ - self.renderer.$el.find('#pms-menu .menu-filter-box h4 i.fa').css({transform: 'rotate(0deg)'}); - }); - this._multi_calendar.on('tab_changed', function(ev, active_index){ - if (active_index) { - self._refresh_view_options(active_index); - } - }); - }, - - _assign_multi_calendar_events: function() { - var self = this; - this._multi_calendar.on_calendar('hcalOnSavePricelist', function(ev){ - document.getElementById("btn_save_changes").disabled = true; - self.savePricelist(ev.detail.calendar_obj, ev.detail.pricelist_id, ev.detail.pricelist); - }); - - $('.hcal-reservation noselect').popover(); - var _destroy_and_clear_popover_mark = function(ev){ - $(".marked-as-having-a-popover").popover('destroy'); - $('.hcal-reservation').removeClass("marked-as-having-a-popover"); - }; - - /* destroy popover if mouse click is done out the popover */ - /* except if you click in the payment button */ - /* TODO: Review because this event is trigger anywhere, even if you click in other buttons! */ - $('html').on('click', function(e) { - if (!$(e.target).hasClass("marked-as-having-a-popover") && - !$(e.target).parents().is('.popover.in') && - (e.target.id !== 'payment_folio')) { - _destroy_and_clear_popover_mark(); - } - }); - this._multi_calendar.on_calendar('hcalOnClickReservation', function(ev){ - var active_calendar = self._multi_calendar.get_active_calendar(); - if ( active_calendar.getSelectionMode() !== HotelCalendar.MODE.NONE - || active_calendar.getSwapMode() !== HotelCalendar.MODE.NONE ) - { - return; - } - if (ev.detail.reservationObj) { - var tp = self._multi_calendar._reserv_tooltips[ev.detail.reservationObj.id]; - var qdict = self._generate_reservation_tooltip_dict(tp); - $(".marked-as-having-a-popover").popover('destroy'); - $(ev.detail.reservationDiv).addClass('marked-as-having-a-popover'); - var $reservationPopover = $(ev.detail.reservationDiv).popover({ - trigger: 'manual', - container: 'body', - animation: false, - html: true, - placement: 'auto bottom', - content: QWeb.render('HotelCalendar.TooltipReservation', qdict) - }).popover('show'); - /* add actions */ - $reservationPopover.data('bs.popover').tip().find(".btn_popover_open_folio").on('click', - {folio_id: ev.detail.reservationObj._userData.folio_id}, function(ev){ - _destroy_and_clear_popover_mark(); - self.do_action({ - type: 'ir.actions.act_window', - res_model: 'hotel.folio', - res_id: ev.data.folio_id, - views: [[false, 'form']], - }); - }); - $reservationPopover.data('bs.popover').tip().find(".btn_popover_open_reservation").on('click', - {reservation_id: ev.detail.reservationObj.id}, function(ev){ - _destroy_and_clear_popover_mark(); - self.do_action({ - type: 'ir.actions.act_window', - res_model: 'hotel.reservation', - res_id: ev.data.reservation_id, - views: [[false, 'form']] - }); - }); - $reservationPopover.data('bs.popover').tip().find(".btn_popover_open_payment_folio").on('click', - {reservation: ev.detail.reservationObj}, function(ev){ - if (ev.data.reservation.total_folio <= ev.data.reservation.total_reservation || - $('#payment_reservation').hasClass('in')) { - _destroy_and_clear_popover_mark(); - var x = self._rpc({ - model: 'hotel.reservation', - method: 'action_pay_folio', - args: [ev.data.reservation.id], - }).then(function (result){ - return self.do_action({ - name: result.name, - view_type: result.view_type, - view_mode: result.view_mode, - type: result.type, - res_model: result.res_model, - views: [[result.view_id, 'form']], - context: result.context, - target: result.target, - }); - }); - } else { - $('#payment_folio').css('color', '#A24689'); - $('#folio_pending_amount').css('animation', 'blinker 1s linear'); - $('#price_room').css('animation', 'blinker 1s linear'); - } - }); - $reservationPopover.data('bs.popover').tip().find(".btn_popover_open_payment_reservation").on('click', - {reservation_id: ev.detail.reservationObj.id}, function(ev){ - _destroy_and_clear_popover_mark(); - var x = self._rpc({ - model: 'hotel.reservation', - method: 'action_pay_reservation', - args: [ev.data.reservation_id], - }).then(function (result){ - return self.do_action({ - name: result.name, - view_type: result.view_type, - view_mode: result.view_mode, - type: result.type, - res_model: result.res_model, - views: [[result.view_id, 'form']], - context: result.context, - target: result.target, - }); - }); - }); - $reservationPopover.data('bs.popover').tip().find(".btn_popover_open_checkin").on('click', - {reservation_id: ev.detail.reservationObj.id}, function(ev){ - _destroy_and_clear_popover_mark(); - var x = self._rpc({ - model: 'hotel.reservation', - method: 'action_checks', - args: [ev.data.reservation_id], - }).then(function (result){ - return self.do_action(result); - }); - }); - $reservationPopover.data('bs.popover').tip().find(".btn_popover_open_invoice").on('click', - {reservation_id: ev.detail.reservationObj.id}, function(ev){ - _destroy_and_clear_popover_mark(); - var x = self._rpc({ - model: 'hotel.reservation', - method: 'open_invoices_reservation', - args: [ev.data.reservation_id], - }).then(function (result){ - return self.do_action(result); - }); - }); - $reservationPopover.data('bs.popover').tip().find(".btn_popover_close").on('click', function(ev){ - _destroy_and_clear_popover_mark(); - }); - } - }); - this._multi_calendar.on_calendar('hcalOnSplitReservation', function(ev){ - var qdict = {}; - var dialog = new Dialog(self, { - title: _t("Confirm Split Reservation"), - buttons: [ - { - text: _t("Yes, split it"), - classes: 'btn-primary', - close: true, - click: function () { - self.model.split_reservation(ev.detail.obj_id, ev.detail.nights); - } - }, - { - text: _t("No"), - close: true - } - ], - $content: QWeb.render('HotelCalendar.ConfirmSplitOperation', qdict) - }).open(); - }); - this._multi_calendar.on_calendar('hcalOnDblClickReservation', function(ev){ - //var res_id = ev.detail.reservationObj.getUserData('folio_id'); - $(ev.detail.reservationDiv).popover('destroy'); - self.do_action({ - type: 'ir.actions.act_window', - res_model: 'hotel.reservation', - res_id: ev.detail.reservationObj.id, - views: [[false, 'form']] - }); - }); - this._multi_calendar.on_calendar('hcalOnUnifyReservations', function(ev){ - var qdict = {}; - var dialog = new Dialog(self, { - title: _t("Confirm Unify Reservations"), - buttons: [ - { - text: _t("Yes, unify it"), - classes: 'btn-primary', - close: true, - click: function () { - self.model.unify_reservations(_.map(ev.detail.toUnify, 'id')); - } - }, - { - text: _t("No"), - close: true - } - ], - $content: QWeb.render('HotelCalendar.ConfirmUnifyOperation', qdict) - }).open(); - }); - this._multi_calendar.on_calendar('hcalOnSwapReservations', function(ev){ - var qdict = {}; - var dialog = new Dialog(self, { - title: _t("Confirm Reservation Swap"), - buttons: [ - { - text: _t("Yes, swap it"), - classes: 'btn-primary', - close: true, - click: function () { - if (ev.detail.calendar_obj.swapReservations(ev.detail.inReservs, ev.detail.outReservs)) { - var fromIds = _.pluck(ev.detail.inReservs, 'id'); - var toIds = _.pluck(ev.detail.outReservs, 'id'); - var refFromReservDiv = ev.detail.inReservs[0]._html; - var refToReservDiv = ev.detail.outReservs[0]._html; - - // Animate Movement - for (var nreserv of ev.detail.inReservs) { - $(nreserv._html).animate({'top': refToReservDiv.style.top}); - } - for (var nreserv of ev.detail.outReservs) { - $(nreserv._html).animate({'top': refFromReservDiv.style.top}); - } - self.swapReservations(fromIds, toIds, ev.detail, refFromReservDiv, refToReservDiv); - } else { - var qdict = {}; - var dialog = new Dialog(self, { - title: _t("Invalid Reservation Swap"), - buttons: [ - { - text: _t("Oops, Ok!"), - classes: 'btn-primary', - close: true - } - ], - $content: QWeb.render('HotelCalendar.InvalidSwapOperation', qdict) - }).open(); - } - } - }, - { - text: _t("No"), - close: true - } - ], - $content: QWeb.render('HotelCalendar.ConfirmSwapOperation', qdict) - }).open(); - }); - this._multi_calendar.on_calendar('hcalOnCancelSwapReservations', function(ev){ - $("#btn_swap span.ntext").html(_t("Start Swap")); - var $btn = $("#btn_swap > button"); - $btn.removeClass('swap-from swap-to'); - }); - this._multi_calendar.on_calendar('hcalOnChangeReservation', function(ev){ - var newReservation = ev.detail.newReserv; - var oldReservation = ev.detail.oldReserv; - var oldPrice = ev.detail.oldPrice; - var newPrice = ev.detail.newPrice; - var folio_id = newReservation.getUserData('folio_id'); - - var qdict = { - ncheckin: newReservation.startDate.clone().local().format(HotelConstants.L10N_DATE_MOMENT_FORMAT), - ncheckout: newReservation.endDate.clone().local().format(HotelConstants.L10N_DATE_MOMENT_FORMAT), - nroom: newReservation.room.number, - nprice: newPrice, - nadults: newReservation.adults, - ocheckin: oldReservation.startDate.clone().local().format(HotelConstants.L10N_DATE_MOMENT_FORMAT), - ocheckout: oldReservation.endDate.clone().local().format(HotelConstants.L10N_DATE_MOMENT_FORMAT), - oroom: oldReservation.room.number, - oprice: oldPrice, - oadults: oldReservation.adults - }; - - if (qdict['ncheckin'] !== qdict['ocheckin'] || qdict['ncheckout'] !== qdict['ocheckout'] - || qdict['nroom'] !== qdict['oroom'] || qdict['nadults'] !== qdict['oadults']) { - var linkedReservs = _.find(ev.detail.calendar_obj._reservations, function(item){ - return item.id !== newReservation.id && !item.unusedZone && item.getUserData('folio_id') === folio_id; - }); - qdict['hasReservsLinked'] = (linkedReservs && linkedReservs.length !== 0)?true:false; - - var hasChanged = false; - - var dialog = new Dialog(self, { - title: _t("Confirm Reservation Changes"), - buttons: [ - { - text: _t("Yes, change it"), - classes: 'btn-primary', - close: true, - disabled: !newReservation.id, - click: function () { - var roomId = newReservation.room.id; - if (newReservation.room.overbooking || newReservation.room.cancelled) { - roomId = +newReservation.room.id.substr(newReservation.room.id.indexOf('@')+1); - } - var write_values = { - 'checkin': newReservation.startDate.format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT), - 'checkout': newReservation.endDate.format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT), - 'room_id': roomId, - 'adults': newReservation.adults, - 'overbooking': newReservation.room.overbooking - }; - if (newReservation.room.cancelled) { - write_values['state'] = 'cancelled'; - } else if (!newReservation.room.cancelled && oldReservation.cancelled) { - write_values['state'] = 'draft'; - } - self.updateReservations(ev.detail.calendar_obj, [newReservation.id], - write_values, oldReservation, newReservation); - hasChanged = true; - } - }, - { - text: _t("No"), - close: true, - } - ], - $content: QWeb.render('HotelCalendar.ConfirmReservationChanges', qdict) - }).open(); - dialog.on('closed', this, function(e){ - if (!hasChanged) { - ev.detail.calendar_obj.replaceReservation(newReservation, oldReservation); - } - }); - } - }); - this._multi_calendar.on_calendar('hcalOnUpdateSelection', function(ev){ - for (var td of ev.detail.old_cells) { - $(td).tooltip('destroy'); - } - if (ev.detail.cells.length) { - var last_cell = ev.detail.cells[ev.detail.cells.length-1]; - var date_cell_start = HotelCalendar.toMoment(ev.detail.calendar_obj.etable.querySelector(`#${ev.detail.cells[0].dataset.hcalParentCell}`).dataset.hcalDate); - var date_cell_end = HotelCalendar.toMoment(ev.detail.calendar_obj.etable.querySelector(`#${last_cell.dataset.hcalParentCell}`).dataset.hcalDate).add(1, 'd'); - var parentRow = document.querySelector(`#${ev.detail.cells[0].dataset.hcalParentRow}`); - var room = ev.detail.calendar_obj.getRoom(parentRow.dataset.hcalRoomObjId); - if (room.overbooking || room.cancelled) { - return; - } - var nights = date_cell_end.diff(date_cell_start, 'days'); - var qdict = { - 'total_price': Number(ev.detail.totalPrice).toLocaleString(), - 'nights': nights - }; - $(last_cell).tooltip({ - animation: false, - html: true, - placement: 'top', - title: QWeb.render('HotelCalendar.TooltipSelection', qdict) - }).tooltip('show'); - } - }); - this._multi_calendar.on_calendar('hcalOnChangeSelectionMode', function(ev){ - var $btnDivide = this.renderer.$el.find('#pms-menu #btn_action_divide > button'); - var $btnUnify = this.renderer.$el.find('#pms-menu #btn_action_unify > button'); - if (ev.detail.newMode === HotelCalendar.ACTION.DIVIDE) { - $btnDivide.addClass('divide-enabled'); - } else { - $btnDivide.removeClass('divide-enabled'); - } - if (ev.detail.newMode === HotelCalendar.ACTION.UNIFY) { - $btnUnify.addClass('unify-enabled'); - } else { - $btnUnify.removeClass('unify-enabled'); - } - }.bind(this)); - this._multi_calendar.on_calendar('hcalOnChangeSelection', function(ev){ - var parentRow = document.querySelector(`#${ev.detail.cellStart.dataset.hcalParentRow}`); - var parentCellStart = document.querySelector(`#${ev.detail.cellStart.dataset.hcalParentCell}`); - var parentCellEnd = document.querySelector(`#${ev.detail.cellEnd.dataset.hcalParentCell}`); - var startDate = HotelCalendar.toMoment(parentCellStart.dataset.hcalDate); - var endDate = HotelCalendar.toMoment(parentCellEnd.dataset.hcalDate).add(1, 'd'); - var room = ev.detail.calendar_obj.getRoom(parentRow.dataset.hcalRoomObjId); - if (room.overbooking || room.cancelled) { - return; - } - var numBeds = (room.shared || ev.detail.calendar_obj.getOptions('divideRoomsByCapacity'))?(ev.detail.cellEnd.dataset.hcalBedNum - ev.detail.cellStart.dataset.hcalBedNum)+1:room.capacity; - if (numBeds <= 0) { - return; - } - - // Normalize Dates - if (startDate.isAfter(endDate)) { - var tt = endDate; - endDate = startDate; - startDate = tt; - } - - var def_arrival_hour = self._view_options['default_arrival_hour'].split(':'); - var def_departure_hour = self._view_options['default_departure_hour'].split(':'); - startDate.set({'hour': def_arrival_hour[0], 'minute': def_arrival_hour[1], 'second': 0}); - endDate.set({'hour': def_departure_hour[0], 'minute': def_departure_hour[1], 'second': 0}); - - var popCreate = new ViewDialogs.FormViewDialog(self, { - res_model: 'hotel.reservation', - context: { - 'default_checkin': startDate.utc().format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT), - 'default_checkout': endDate.utc().format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT), - 'default_adults': numBeds, - 'default_children': 0, - 'default_room_id': room.id, - 'default_room_type_id': room.getUserData('room_type_id'), - }, - title: _t("Create: ") + _t("Reservation"), - initial_view: "form", - disable_multiple_selection: true, - }).open(); - }); - - this._multi_calendar.on_calendar('hcalOnKeyPressed', function(ev){ - /* add actions */ - }); - - this._multi_calendar.on_calendar('hcalOnDateChanged', function(ev){ - var $dateTimePickerBegin = this.renderer.$el.find('#pms-menu #date_begin'); - $dateTimePickerBegin.data("ignore_onchange", true); - $dateTimePickerBegin.data("DateTimePicker").date(ev.detail.date_begin.local()); - this._reload_active_calendar(); - }.bind(this)); - }, - - _create_reservation_obj: function(json_reserv) { - var nreserv = new HReservation({ - 'id': json_reserv['id'], - 'room_id': json_reserv['room_id'], - 'title': json_reserv['name'], - 'adults': json_reserv['adults'], - 'childrens': json_reserv['childrens'], - 'startDate': HotelCalendar.toMoment(json_reserv['checkin'], HotelConstants.ODOO_DATETIME_MOMENT_FORMAT), - 'endDate': HotelCalendar.toMoment(json_reserv['checkout'], HotelConstants.ODOO_DATETIME_MOMENT_FORMAT), - 'color': json_reserv['bgcolor'], - 'colorText': json_reserv['color'], - 'splitted': json_reserv['splitted'] || false, - 'readOnly': json_reserv['read_only'] || false, - 'fixDays': json_reserv['fix_days'] || false, - 'fixRooms': json_reserv['fix_room'], - 'unusedZone': false, - 'linkedId': false, - 'overbooking': json_reserv['overbooking'], - 'cancelled': json_reserv['state'] === 'cancelled', - 'total_reservation': json_reserv['price_room_services_set'], - 'total_folio': json_reserv['amount_total'], - }); - nreserv.addUserData({ - 'folio_id': json_reserv['folio_id'], - 'parent_reservation': json_reserv['parent_reservation'], - 'realDates': [ - HotelCalendar.toMoment(json_reserv['real_dates'][0], HotelConstants.ODOO_DATETIME_MOMENT_FORMAT), - HotelCalendar.toMoment(json_reserv['real_dates'][1], HotelConstants.ODOO_DATETIME_MOMENT_FORMAT) - ] - }); - - return nreserv; - }, - - _generate_reservation_tooltip_dict: function(tp) { - return { - 'folio_name': tp['folio_name'], - 'name': tp['name'], - 'phone': tp['phone'], - 'email': tp['email'], - 'room_type_name': tp['room_type_name'], - 'adults': tp['adults'], - 'children': tp['children'], - 'checkin': HotelCalendar.toMomentUTC(tp['real_dates'][0], '').format("DD MMMM"), - 'checkin_day_of_week': HotelCalendar.toMomentUTC(tp['real_dates'][0], '').format("dddd"), - 'checkout': HotelCalendar.toMomentUTC(tp['real_dates'][1], '').format("DD MMMM"), - 'checkout_day_of_week': HotelCalendar.toMomentUTC(tp['real_dates'][1], '').format("dddd"), - 'arrival_hour': tp['arrival_hour'], - 'departure_hour': tp['departure_hour'], - 'price_room_services_set': Number(tp['price_room_services_set']).toLocaleString(), - 'invoices_paid': Number(tp['invoices_paid']).toLocaleString(), - 'pending_amount': Number(tp['pending_amount']).toLocaleString(), - 'reservation_type': tp['type'], - 'closure_reason': tp['closure_reason'], - 'out_service_description': tp['out_service_description'], - 'splitted': tp['splitted'], - 'channel_type': tp['channel_type'], - 'board_service_name': tp['board_service_name'], - 'services': tp['services'], - }; - }, - - _update_buttons_counter: function (ev) { - var self = this; - var active_calendar = this._multi_calendar.get_active_calendar(); - - var filterDates = active_calendar.getDates(); - var dfrom_fmt = filterDates[0].format(HotelConstants.ODOO_DATE_MOMENT_FORMAT), - dto_fmt = filterDates[1].format(HotelConstants.ODOO_DATE_MOMENT_FORMAT), - now_fmt = moment().format(HotelConstants.ODOO_DATE_MOMENT_FORMAT); - - var domain_checkouts = [ - ['real_checkout', '=', now_fmt], - ['state', 'in', ['booking']], - ['reservation_type', 'not in', ['out']] - ]; - var domain_checkins = [ - ['real_checkin', '=', now_fmt], - ['state', 'in', ['confirm']], - ['reservation_type', 'not in', ['out']] - ]; - var domain_overbookings = [ - ['real_checkin', '>=', dfrom_fmt], - ['overbooking', '=', true], ['state', 'not in', ['cancelled']] - ]; - var domain_cancelled = [ - '|', '&', - ['real_checkout', '>', dfrom_fmt], - ['real_checkout', '<', dto_fmt], - ['real_checkin', '>=', dfrom_fmt], - ['real_checkin', '<=', dto_fmt], - ['state', '=', 'cancelled'] - ]; - - $.when( - this.model.search_count(domain_checkouts), - this.model.search_count(domain_checkins), - this.model.search_count(domain_overbookings), - this.model.search_count(domain_cancelled), - ).then(function(a1, a2, a3, a4){ - self.renderer.update_buttons_counter(a1, a2, a3, a4); - }); - }, - - //-------------------------------------------------------------------------- - // Handlers - //-------------------------------------------------------------------------- - _onViewAttached: function (ev) { - this._multi_calendar.recalculate_reservation_positions(); - }, - - _onLoadViewFilters: function (ev) { - var self = this; - $.when( - this.model.get_room_type_class(), - this.model.get_floors(), - this.model.get_amenities(), - this.model.get_room_types() - ).then(function(a1, a2, a3, a4){ - self.renderer.loadViewFilters(a1, a2, a3, a4); - }); - }, - - _onBusNotification: function(notifications) { - var need_reload_pricelists = false; - var need_update_counters = false; - var nreservs = [] - for (var notif of notifications) { - if (notif[0][1] === 'hotel.reservation') { - switch (notif[1]['type']) { - case 'reservation': - var reserv = notif[1]['reservation']; - // Only show notifications of other users - // if (notif[1]['subtype'] !== 'noshow' && this._view_options['show_notifications'] && notif[1]['userid'] != this.dataset.context.uid) { - // var qdict = _.clone(reserv); - // qdict = _.extend(qdict, { - // 'checkin': HotelCalendar.toMomentUTC(qdict['checkin'], HotelConstants.ODOO_DATETIME_MOMENT_FORMAT).clone().local().format(HotelConstants.L10N_DATETIME_MOMENT_FORMAT), // UTC -> Local - // 'checkout': HotelCalendar.toMomentUTC(qdict['checkout'], HotelConstants.ODOO_DATETIME_MOMENT_FORMAT).clone().local().format(HotelConstants.L10N_DATETIME_MOMENT_FORMAT), // UTC -> Local - // 'username': notif[1]['username'], - // 'userid': notif[1]['userid'] - // }); - // var msg = QWeb.render('HotelCalendar.Notification', qdict); - // if (notif[1]['subtype'] === "notify") { - // this.do_notify(notif[1]['title'], msg, true); - // } else if (notif[1]['subtype'] === "warn") { - // this.do_warn(notif[1]['title'], msg, true); - // } - // } - - // Create/Update/Delete reservation - if (notif[1]['action'] === 'unlink') { - this._multi_calendar.remove_reservation(reserv['id']); - this._multi_calendar._reserv_tooltips = _.pick(this._multi_calendar._reserv_tooltips, function(value, key, obj){ return key != reserv['id']; }); - nreservs = _.reject(nreservs, function(item){ return item.id == reserv['id']; }); - } else { - nreservs = _.reject(nreservs, {'id': reserv['id']}); // Only like last changes - var nreserv = this._create_reservation_obj(reserv); - this._multi_calendar._reserv_tooltips[reserv['id']] = notif[1]['tooltip']; - nreservs.push(nreserv); - } - need_update_counters = true; - break; - case 'pricelist': - this._multi_calendar.merge_pricelist(notif[1]['price']); - break; - case 'restriction': - this._multi_calendar.merge_restrictions(notif[1]['restriction']); - break; - default: - // Do Nothing - } - } - } - if (nreservs.length > 0) { - this._multi_calendar.merge_reservations(nreservs); - } - if (need_update_counters) { - this._update_buttons_counter(); - } - }, - - _onApplyFilters: function() { - var category = _.map(this.renderer.$el.find('#pms-menu #type_list').val(), function(item){ return +item; }); - var floor = _.map(this.renderer.$el.find('#pms-menu #floor_list').val(), function(item){ return +item; }); - var amenities = _.map(this.renderer.$el.find('#pms-menu #amenities_list').val(), function(item){ return +item; }); - var virtual = _.map(this.renderer.$el.find('#pms-menu #virtual_list').val(), function(item){ return +item; }); - var domain = []; - if (category && category.length > 0) { - domain.push(['class_id', 'in', category]); - } - if (floor && floor.length > 0) { - domain.push(['floor_id', 'in', floor]); - } - if (amenities && amenities.length > 0) { - domain.push(['amenities', 'in', amenities]); - } - if (virtual && virtual.length > 0) { - domain.push(['room_type_id', 'some', virtual]); - } - this._multi_calendar.get_active_calendar().setDomain(HotelCalendar.DOMAIN.ROOMS, domain); - }, - - _on_change_filter_date: function() { - var $dateTimePickerBegin = this.renderer.$el.find('#pms-menu #date_begin'); - var $dateEndDays = this.renderer.$el.find('#pms-menu #date_end_days'); - - // FIXME: Hackish onchange ignore (Used when change dates from code) - if ($dateTimePickerBegin.data("ignore_onchange") || $dateEndDays.data("ignore_onchange")) { - $dateTimePickerBegin.data("ignore_onchange", false); - $dateEndDays.data("ignore_onchange", false); - return true; - } - - var date_begin = $dateTimePickerBegin.data("DateTimePicker").date().set({'hour': 0, 'minute': 0, 'second': 0}).clone(); - - var active_calendar = this._multi_calendar.get_active_calendar(); - if (active_calendar && date_begin) { - var days = $dateEndDays.val(); - if (days === 'month') { - days = date_begin.daysInMonth(); - } - var date_end = date_begin.clone().add(days, 'd'); - if (!date_begin.isSame(this._last_dates[0].clone(), 'd') || !date_end.isSame(this._last_dates[1].clone(), 'd')) { - active_calendar.setStartDate(date_begin, $dateEndDays.val(), false, function(){ - this._reload_active_calendar(); - }.bind(this)); - } - } - }, - - _refresh_view_options: function(active_index) { - var active_calendar = this._multi_calendar.get_calendar(active_index); - - /* Dates */ - var $dateTimePickerBegin = this.renderer.$el.find('#pms-menu #date_begin'); - var $dateEndDays = this.renderer.$el.find('#pms-menu #date_end_days'); - - var start_date = active_calendar.getOptions('startDate'); - start_date = start_date.clone().add(1, 'd'); - - $dateTimePickerBegin.data("ignore_onchange", true); - $dateTimePickerBegin.data("DateTimePicker").date(start_date.local()); - $dateEndDays.data("ignore_onchange", true); - $dateEndDays.val(active_calendar.getOptions('orig_days')); - $dateEndDays.trigger('change'); - - /* Overbooking */ - var $led = this.renderer.$el.find('#pms-menu #btn_action_overbooking > button'); - if (active_calendar.options.showOverbookings) { - $led.addClass('overbooking-enabled'); - } else { - $led.removeClass('overbooking-enabled'); - } - - /* Cancelled */ - $led = this.renderer.$el.find('#pms-menu #btn_action_cancelled > button'); - if (active_calendar.options.showCancelled) { - $led.addClass('cancelled-enabled'); - } else { - $led.removeClass('cancelled-enabled'); - } - - /* Divide */ - $led = this.renderer.$el.find('#pms-menu #btn_action_divide > button'); - if (active_calendar.getSelectionMode() === HotelCalendar.ACTION.DIVIDE) { - $led.addClass('divide-enabled'); - } else { - $led.removeClass('divide-enabled'); - } - - /* Unify Led */ - $led = this.renderer.$el.find('#pms-menu #btn_action_unify > button'); - if (active_calendar.getSelectionMode() === HotelCalendar.ACTION.UNIFY) { - $led.addClass('unify-enabled'); - } else { - $led.removeClass('unify-enabled'); - } - - /* Calendar Record */ - var active_calendar_record = this._multi_calendar.get_calendar_record(active_index); - var $calendar_name = this.renderer.$el.find('#pms-menu .menu-filter-box #calendar_name'); - $calendar_name.val(active_calendar_record['name']); - - /* Calendar Filters */ - this._refresh_filters(this._multi_calendar.get_active_filters()); - this._update_buttons_counter(); - }, - - _refresh_filters: function(calendar_filters) { - var $segmentation = this.renderer.$el.find('#pms-menu #type_list'); - var $location = this.renderer.$el.find('#pms-menu #floor_list'); - var $amenities = this.renderer.$el.find('#pms-menu #amenities_list'); - var $types = this.renderer.$el.find('#pms-menu #virtual_list'); - $segmentation.val(calendar_filters['class_id']); - $segmentation.trigger('change'); - $location.val(calendar_filters['floor_id']); - $location.trigger('change'); - $amenities.val(calendar_filters['amenities']); - $amenities.trigger('change'); - $types.val(calendar_filters['room_type_id']); - $types.trigger('change'); - }, - - //-------------------------------------------------------------------------- - // Helpers - //-------------------------------------------------------------------------- - _find_bootstrap_environment: function() { - var envs = ['xs', 'sm', 'md', 'lg']; - - var $el = $('
'); - $el.appendTo($('body')); - - for (var i = envs.length - 1; i >= 0; i--) { - var env = envs[i]; - - $el.addClass('hidden-'+env); - if ($el.is(':hidden')) { - $el.remove(); - return env; - } - } - }, -}); - -return PMSCalendarController; - -}); diff --git a/hotel_calendar/static/src/js/views/calendar/hotel_calendar_model.js b/hotel_calendar/static/src/js/views/calendar/hotel_calendar_model.js deleted file mode 100644 index 96eb3b4aa..000000000 --- a/hotel_calendar/static/src/js/views/calendar/hotel_calendar_model.js +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2018 Alexandre Díaz -// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('hotel_calendar.PMSCalendarModel', function (require) { -"use strict"; - -var AbstractModel = require('web.AbstractModel'), - Context = require('web.Context'), - Core = require('web.core'), - FieldUtils = require('web.field_utils'), - Session = require('web.session'); - - -return AbstractModel.extend({ - init: function () { - this._super.apply(this, arguments); - }, - - load: function (params) { - this.modelName = params.modelName; - this.modelManagementName = 'hotel.calendar.management' - }, - - swap_reservations: function(fromIds, toIds) { - return this._rpc({ - model: this.modelName, - method: 'swap_reservations', - args: [fromIds, toIds], - context: Session.user_context, - }); - }, - - get_calendar_data: function(oparams) { - var dialog = bootbox.dialog({ - message: '
Getting Calendar Data From Server...
', - onEscape: false, - closeButton: false, - size: 'small', - backdrop: false, - }); - return this._rpc({ - model: this.modelName, - method: 'get_hcalendar_all_data', - args: oparams, - context: Session.user_context, - }, { - xhr: function () { - var xhr = new window.XMLHttpRequest(); - //Download progress - xhr.addEventListener("readystatechange", function() { - if (this.readyState == this.DONE) { - console.log(`[HotelCalendar] Downloaded ${(parseInt(xhr.getResponseHeader("Content-Length"), 10)/1024).toFixed(3)}KiB of data`); - } - }, false); - return xhr; - }, - success: function() { - dialog.modal('hide'); - }, - shadow: true, - }); - }, - - get_hcalendar_settings: function() { - return this._rpc({ - model: this.modelName, - method: 'get_hcalendar_settings', - args: [false], - }); - }, - - get_room_types: function() { - var domain = [['hotel_id', '=', Session.hotel_id]]; - return this._rpc({ - model: 'hotel.room.type', - method: 'search_read', - args: [domain, ['id','name']], - context: Session.user_context, - }); - }, - get_floors: function() { - var domain = [('|', - ['hotel_ids', 'in', Session.hotel_id], - ['hotel_ids', '=', false] - )]; - return this._rpc({ - model: 'hotel.floor', - method: 'search_read', - args: [domain, ['id','name']], - context: Session.user_context, - }); - }, - get_amenities: function() { - var domain = [('|', - ['hotel_ids', 'in', Session.hotel_id], - ['hotel_ids', '=', false] - )]; - // TODO: Filter rooms by amenities is not working - return this._rpc({ - model: 'hotel.amenity', - method: 'search_read', - args: [domain, ['id','name']], - context: Session.user_context, - }); - }, - get_room_type_class: function() { - var domain = [('|', - ['hotel_ids', 'in', Session.hotel_id], - ['hotel_ids', '=', false] - )]; - return this._rpc({ - model: 'hotel.room.type.class', - method: 'search_read', - args: [domain, ['id','name']], - context: Session.user_context, - }); - }, - - search_count: function(domain) { - domain.push(['hotel_id', '=', Session.hotel_id]); - return this._rpc({ - model: this.modelName, - method: 'search_count', - args: [domain], - context: Session.user_context, - }); - }, - - update_records: function(ids, vals) { - return this._rpc({ - model: this.modelName, - method: 'write', - args: [ids, vals], - context: Session.user_context, - }); - }, - - update_or_create_calendar_record: function(ids, vals) { - if (!ids) { - return this._rpc({ - model: 'hotel.calendar', - method: 'create', - args: [vals], - context: Session.user_context, - }); - } - return this._rpc({ - model: 'hotel.calendar', - method: 'write', - args: [ids, vals], - context: Session.user_context, - }); - }, - - folio_search_count: function(domain) { - return this._rpc({ - model: 'hotel.folio', - method: 'search_count', - args: [domain], - context: Session.user_context, - }); - }, - - split_reservation: function(id, nights) { - return this._rpc({ - model: this.modelName, - method: 'split', - args: [[id], nights], - context: Session.user_context, - }) - }, - - unify_reservations: function(reserv_ids) { - return this._rpc({ - model: this.modelName, - method: 'unify_ids', - args: [reserv_ids], - context: Session.user_context, - }) - }, - - save_changes: function(params) { - // params.splice(0, 0, false); // FIXME: ID=False because first parameter its an integer - return this._rpc({ - model: 'hotel.calendar.management', - method: 'save_changes', - args: params, - context: Session.user_context, - }) - } -}); - -}); diff --git a/hotel_calendar/static/src/js/views/calendar/hotel_calendar_renderer.js b/hotel_calendar/static/src/js/views/calendar/hotel_calendar_renderer.js deleted file mode 100644 index 74f1d7de5..000000000 --- a/hotel_calendar/static/src/js/views/calendar/hotel_calendar_renderer.js +++ /dev/null @@ -1,284 +0,0 @@ -/* global $, odoo, _, HotelCalendar, moment */ -// Copyright 2018 Alexandre Díaz -// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('hotel_calendar.PMSCalendarRenderer', function (require) { -"use strict"; - -var Core = require('web.core'), - ViewDialogs = require('web.view_dialogs'), - Dialog = require('web.Dialog'), - Session = require('web.session'), - AbstractRenderer = require('web.AbstractRenderer'), - HotelConstants = require('hotel_calendar.Constants'), - //Formats = require('web.formats'), - - _t = Core._t, - _lt = Core._lt, - QWeb = Core.qweb; - -var HotelCalendarView = AbstractRenderer.extend({ - /** VIEW OPTIONS **/ - template: "hotel_calendar.HotelCalendarView", - display_name: _lt('Hotel Calendar'), - icon: 'fa fa-map-marker', - searchable: false, - searchview_hidden: true, - - - /** VIEW METHODS **/ - init: function(parent, state, params) { - this._super.apply(this, arguments); - }, - - start: function () { - this.init_calendar_view(); - return this._super(); - }, - - on_attach_callback: function() { - this._super(); - - if (!this._is_visible) { - // FIXME: Workaround for restore "lost" reservations (Drawn when the view is hidden) - this.trigger_up('onViewAttached'); - } - }, - - /** CUSTOM METHODS **/ - get_view_filter_dates: function () { - var $dateTimePickerBegin = this.$el.find('#pms-menu #date_begin'); - var $dateEndDays = this.$el.find('#pms-menu #date_end_days'); - var date_begin = $dateTimePickerBegin.data("DateTimePicker").date().clone(); - var days = $dateEndDays.val(); - if (days === 'month') { - days = date_begin.daysInMonth(); - } - var date_end = date_begin.clone().add(days, 'd'); - return [date_begin, date_end]; - }, - - update_buttons_counter: function(ncheckouts, ncheckins, noverbookings, ncancelled) { - var self = this; - // Checkouts Button - var $ninfo = self.$el.find('#pms-menu #btn_action_checkout span.ninfo'); - $ninfo.text(ncheckouts); - - // Checkins Button - $ninfo = self.$el.find('#pms-menu #btn_action_checkin span.ninfo'); - $ninfo.text(ncheckins); - - // OverBookings - $ninfo = self.$el.find('#pms-menu #btn_action_overbooking span.ninfo'); - $ninfo.text(noverbookings); - if (noverbookings) { - $ninfo.parent().parent().addClass('overbooking-highlight'); - } else { - $ninfo.parent().parent().removeClass('overbooking-highlight'); - } - - // Cancelled - $ninfo = self.$el.find('#pms-menu #btn_action_cancelled span.ninfo'); - $ninfo.text(ncancelled); - if (ncancelled) { - $ninfo.parent().parent().addClass('cancelled-highlight'); - } else { - $ninfo.parent().parent().removeClass('cancelled-highlight'); - } - }, - - init_calendar_view: function(){ - var self = this; - - /** VIEW CONTROLS INITIALIZATION **/ - // DATE TIME PICKERS - var DTPickerOptions = { - viewMode: 'months', - icons : { - time: 'fa fa-clock-o', - date: 'fa fa-calendar', - up: 'fa fa-chevron-up', - down: 'fa fa-chevron-down' - }, - //language : moment.locale(), - locale : moment.locale(), - format : HotelConstants.L10N_DATE_MOMENT_FORMAT, - widgetPositioning:{ - horizontal: 'auto', - vertical: 'bottom' - } - }; - var $dateTimePickerBegin = this.$el.find('#pms-menu #date_begin'); - var $dateEndDays = this.$el.find('#pms-menu #date_end_days'); - $dateTimePickerBegin.datetimepicker(DTPickerOptions); - $dateEndDays.select2({ - data: [ - {id:7, text: '1w'}, - {id:12, text: '2w'}, - {id:21, text: '3w'}, - {id:'month', text: '1m'}, - {id:60, text: '2m'}, - {id:90, text: '3m'}, - ], - allowClear: true, - minimumResultsForSearch: -1 - }); - - /* TOUCH EVENTS */ - this.$el.on('touchstart', function(ev){ - var orgEvent = ev.originalEvent; - this._mouseEventStartPos = [orgEvent.touches[0].screenX, orgEvent.touches[0].screenY]; - }); - this.$el.on('touchend', function(ev){ - var orgEvent = ev.originalEvent; - if (orgEvent.changedTouches.length > 2) { - var mousePos = [orgEvent.changedTouches[0].screenX, orgEvent.changedTouches[0].screenY]; - var mouseDiffX = mousePos[0] - this._mouseEventStartPos[0]; - var moveLength = 40; - var date_begin = false; - var days = orgEvent.changedTouches.length == 3 && 7 || 1; - if (mouseDiffX < -moveLength) { - date_begin = $dateTimePickerBegin.data("DateTimePicker").date().set({'hour': 0, 'minute': 0, 'second': 0}).clone().add(days, 'd'); - } - else if (mouseDiffX > moveLength) { - date_begin = $dateTimePickerBegin.data("DateTimePicker").date().set({'hour': 0, 'minute': 0, 'second': 0}).clone().subtract(days, 'd'); - } - if (date_begin) { - $dateTimePickerBegin.data("DateTimePicker").date(date_begin); - } - } - }); - - /* BUTTONS */ - var $button = this.$el.find('#pms-menu #btn_action_bookings'); - $button.on('click', function(ev){ self._open_search_tree('book'); }); - $button = this.$el.find('#pms-menu #btn_action_checkins'); - $button.on('click', function(ev){ self._open_search_tree('checkin'); }); - $button = this.$el.find('#pms-menu #btn_action_invoices'); - $button.on('click', function(ev){ self._open_search_tree('invoice'); }); - $button = this.$el.find('#pms-menu #btn_action_folios'); - $button.on('click', function(ev){ self._open_search_tree('folio'); }); - // $button = this.$el.find('#pms-menu #bookings_search'); - // $button.on('keypress', function(ev){ - // if (ev.keyCode === 13) { - // self._open_bookings_tree(); - // } - // }); - - this.$el.find("button[data-action]").on('click', function(ev){ - self.do_action(this.dataset.action); - }); - - return $.when( - this.trigger_up('onUpdateButtonsCounter'), - this.trigger_up('onLoadViewFilters'), - ); - }, - - loadViewFilters: function(resultsHotelRoomType, resultsHotelFloor, resultsHotelRoomAmenities, resultsHotelVirtualRooms) { - var $list = this.$el.find('#pms-menu #type_list'); - $list.html(''); - resultsHotelRoomType.forEach(function(item, index){ - $list.append(``); - }); - $list.select2(); - $list.on('change', function(ev){ - this.trigger_up('onApplyFilters'); - }.bind(this)); - - // Get Floors - $list = this.$el.find('#pms-menu #floor_list'); - $list.html(''); - resultsHotelFloor.forEach(function(item, index){ - $list.append(``); - }); - $list.select2(); - $list.on('change', function(ev){ - this.trigger_up('onApplyFilters'); - }.bind(this)); - - // Get Amenities - $list = this.$el.find('#pms-menu #amenities_list'); - $list.html(''); - resultsHotelRoomAmenities.forEach(function(item, index){ - $list.append(``); - }); - $list.select2(); - $list.on('change', function(ev){ - this.trigger_up('onApplyFilters'); - }.bind(this)); - - // Get Virtual Rooms - $list = this.$el.find('#pms-menu #virtual_list'); - $list.html(''); - resultsHotelVirtualRooms.forEach(function(item, index){ - $list.append(``); - }); - $list.select2(); - $list.on('change', function(ev){ - this.trigger_up('onApplyFilters'); - }.bind(this)); - }, - - _generate_search_domain: function(tsearch, type) { - var domain = []; - domain.push('|', '|', '|', '|', - ['partner_id.name', 'ilike', tsearch], - ['partner_id.mobile', 'ilike', tsearch], - ['partner_id.vat', 'ilike', tsearch], - ['partner_id.email', 'ilike', tsearch], - ['partner_id.phone', 'ilike', tsearch]); - if (type === 'invoice') { - domain.splice(0, 0, '|'); - domain.push(['number', 'ilike', tsearch]); - } - return domain; - }, - - _generate_search_res_model: function(type) { - var model = ''; - var title = ''; - if (type === 'book') { - model = 'hotel.reservation'; - title = _t('Reservations'); - } else if (type === 'checkin') { - model = 'hotel.checkin.partner'; - title = _t('Checkins'); - } else if (type === 'invoice') { - model = 'account.invoice'; - title = _t('Invoices'); - } else if (type === 'folio') { - model = 'hotel.folio' - title = _t('Folios'); - } - return [model, title]; - }, - - _open_search_tree: function(type) { - var $elm = this.$el.find('#pms-menu #bookings_search'); - var searchQuery = $elm.val(); - var domain = false; - if (searchQuery) { - domain = this._generate_search_domain(searchQuery, type); - } else { - domain = []; - } - - var [model, title] = this._generate_search_res_model(type); - - this.do_action({ - type: 'ir.actions.act_window', - view_mode: 'form', - view_type: 'tree,form', - res_model: model, - views: [[false, 'list'], [false, 'form']], - domain: domain, - name: searchQuery?`${title} for ${searchQuery}`:`All ${title}` - }); - - $elm.val(''); - }, -}); - -return HotelCalendarView; - -}); diff --git a/hotel_calendar/static/src/js/views/calendar/hotel_calendar_view.js b/hotel_calendar/static/src/js/views/calendar/hotel_calendar_view.js deleted file mode 100644 index 757f38e50..000000000 --- a/hotel_calendar/static/src/js/views/calendar/hotel_calendar_view.js +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2018 Alexandre Díaz -// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('hotel_calendar.PMSCalendarView', function (require) { -"use strict"; - -var AbstractView = require('web.AbstractView'), - PMSCalendarModel = require('hotel_calendar.PMSCalendarModel'), - PMSCalendarController = require('hotel_calendar.PMSCalendarController'), - PMSCalendarRenderer = require('hotel_calendar.PMSCalendarRenderer'), - ViewRegistry = require('web.view_registry'), - SystrayMenu = require('web.SystrayMenu'), - ControlPanel = require('web.ControlPanel'), - Widget = require('web.Widget'), - Session = require('web.session'), - Core = require('web.core'), - - _lt = Core._lt, - QWeb = Core.qweb; - -/* HIDE CONTROL PANEL */ -/* FIXME: Look's like a hackish solution */ -ControlPanel.include({ - update: function(status, options) { - if (typeof options === 'undefined') { - options = {}; - } - if (typeof options.toHide === 'undefined') - options.toHide = false; - var action_stack = this.getParent().action_stack; - if (action_stack && action_stack.length) { - var active_action = action_stack[action_stack.length-1]; - if (active_action.widget && active_action.widget.active_view && - active_action.widget.active_view.type === 'pms'){ - options.toHide = true; - } - } - this._super(status, options); - this._toggle_visibility(!options.toHide); - } -}); - -/** SYSTRAY **/ -var CalendarMenu = Widget.extend({ - template: 'HotelCalendar.SettingsMenu', - events: { - "click a[data-action]": "perform_callback", - }, - - start: function(){ - this.$dropdown = this.$(".o_calendar_settings_dropdown"); - return $.when( - this._rpc({ - model: 'res.users', - method: 'read', - args: [[Session.uid], ["pms_show_notifications", "pms_show_pricelist", "pms_show_availability"]], - context: Session.user_context, - }) - ).then(function(result) { - this._show_notifications = result[0]['pms_show_notifications']; - this._show_pricelist = result[0]['pms_show_pricelist']; - this._show_availability = result[0]['pms_show_availability']; - return this.update(); - }.bind(this)); - }, - - perform_callback: function (evt) { - evt.preventDefault(); - var params = $(evt.target).data(); - var callback = params.action; - - if (callback && this[callback]) { - this[callback](params, evt); - } else { - console.warn("No handler for ", callback); - } - }, - - update: function() { - // var view_type = this.getParent().getParent()._current_state.view_type; - // if (view_type === 'pms') { - // this.do_show(); - this.$dropdown - .empty() - .append(QWeb.render('HotelCalendar.SettingsMenu.Global', { - manager: this, - })); - // } - // else { - // this.do_hide(); - // } - return $.when(); - }, - - toggle_show_notification: function() { - this._show_notifications = !this._show_notifications; - this._rpc({ - model: 'res.users', - method: 'write', - args: [[Session.uid], {pms_show_notifications: this._show_notifications}], - context: Session.user_context, - }).then(function () { - window.location.reload(); - }); - }, - - toggle_show_pricelist: function() { - this._show_pricelist = !this._show_pricelist; - this._rpc({ - model: 'res.users', - method: 'write', - args: [[Session.uid], {pms_show_pricelist: this._show_pricelist}], - context: Session.user_context, - }).then(function () { - window.location.reload(); - }); - }, - - toggle_show_availability: function() { - this._show_availability = !this._show_availability; - this._rpc({ - model: 'res.users', - method: 'write', - args: [[Session.uid], {pms_show_availability: this._show_availability}], - context: Session.user_context, - }).then(function () { - window.location.reload(); - }); - }, -}); - -var PMSCalendarView = AbstractView.extend({ - display_name: _lt('Calendar PMS'), - icon: 'fa-calendar', - //jsLibs: [], - cssLibs: ['/hotel_calendar/static/src/lib/hcalendar/css/hcalendar.css'], - config: { - Model: PMSCalendarModel, - Controller: PMSCalendarController, - Renderer: PMSCalendarRenderer, - }, - - init: function (viewInfo, params) { - this._super.apply(this, arguments); - var arch = viewInfo.arch; - var fields = viewInfo.fields; - var attrs = arch.attrs; - - // If form_view_id is set, then the calendar view will open a form view - // with this id, when it needs to edit or create an event. - this.controllerParams.formViewId = - attrs.form_view_id ? parseInt(attrs.form_view_id, 10) : false; - if (!this.controllerParams.formViewId && params.action) { - var formViewDescr = _.find(params.action.views, function (v) { - return v[1] === 'form'; - }); - if (formViewDescr) { - this.controllerParams.formViewId = formViewDescr[0]; - } - } - - this.controllerParams.readonlyFormViewId = !attrs.readonly_form_view_id || !utils.toBoolElse(attrs.readonly_form_view_id, true) ? false : attrs.readonly_form_view_id; - this.controllerParams.context = params.context || {}; - this.controllerParams.displayName = params.action && params.action.name; - - this.loadParams.fields = fields; - this.loadParams.fieldsInfo = viewInfo.fieldsInfo; - this.loadParams.creatable = false; - - this.loadParams.mode = attrs.mode; - }, -}); - -SystrayMenu.Items.push(CalendarMenu); -ViewRegistry.add('pms', PMSCalendarView); -//Core.view_registry.add('pms', HotelCalendarView); - -return PMSCalendarView; - -}); diff --git a/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_controller.js b/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_controller.js deleted file mode 100644 index 08bbdb913..000000000 --- a/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_controller.js +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2018 Alexandre Díaz -// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('hotel_calendar.MPMSCalendarController', function (require) { -"use strict"; - -var AbstractController = require('web.AbstractController'), - Core = require('web.core'), - Bus = require('bus.bus').bus, - HotelConstants = require('hotel_calendar.Constants'), - - _t = Core._t, - QWeb = Core.qweb; - -var MPMSCalendarController = AbstractController.extend({ - custom_events: _.extend({}, AbstractController.prototype.custom_events, { - viewUpdated: '_onViewUpdated', - onSaveChanges: '_onSaveChanges', - onLoadCalendar: '_onLoadCalendar', - onLoadCalendarSettings: '_onLoadCalendarSettings', - onLoadNewContentCalendar: '_onLoadNewContentCalendar', - }), - /** - * @override - * @param {Widget} parent - * @param {AbstractModel} model - * @param {AbstractRenderer} renderer - * @param {Object} params - */ - init: function (parent, model, renderer, params) { - this._super.apply(this, arguments); - this.displayName = params.displayName; - this.formViewId = params.formViewId; - this.context = params.context; - - Bus.on("notification", this, this._onBusNotification); - }, - - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- - - - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - - /** - * @param {Object} record - * @param {integer} record.id - * @returns {Deferred} - */ - _updateRecord: function (record) { - return this.model.updateRecord(record).then(this.reload.bind(this)); - }, - - //-------------------------------------------------------------------------- - // Handlers - //-------------------------------------------------------------------------- - _onSaveChanges: function (ev) { - var self = this; - this.model.save_changes(_.toArray(ev.data)).then(function(results){ - self.renderer.resetSaveState(); - }); - }, - - _onLoadNewContentCalendar: function (ev) { - var self = this; - var params = this.renderer.generate_params(); - var oparams = [params['dates'][0], params['dates'][1], params['prices'], params['restrictions'], false]; - - this.model.get_hcalendar_data(oparams).then(function(results){ - self.renderer._days_tooltips = results['events']; - self.renderer._hcalendar.setData(results['prices'], results['restrictions'], results['availability'], results['count_reservations']); - self.renderer._assign_extra_info(); - }); - this.renderer._last_dates = params['dates']; - this.renderer.$CalendarHeaderDays = this.renderer.$el.find("div.table-room_type-data-header"); - this.renderer._on_scroll(); // FIXME: Workaround for update sticky header - }, - - _onLoadCalendar: function (ev) { - var self = this; - - /** DO MAGIC **/ - var params = this.renderer.generate_params(); - var oparams = [params['dates'][0], params['dates'][1], false, false, true]; - this.model.get_hcalendar_data(oparams).then(function(results){ - self.renderer._days_tooltips = results['events']; - var rooms = []; - for (var r of results['rooms']) { - var nroom = new HRoomType( - r['id'], - r['name'], - r['capacity'], - r['price'], - ); - rooms.push(nroom); - } - - // Get Pricelists - self.renderer._pricelist_id = results['pricelist_id']; - self.renderer._restriction_id = results['restriction_id']; - $.when( - self.model.get_pricelist_plans(), - self.model.get_restriction_plans(), - ).then(function(a1, a2){ - self.renderer.loadViewFilters(a1, a2); - }) - - self.renderer.create_calendar(rooms); - self.renderer.setCalendarData(results['prices'], results['restrictions'], results['availability'], results['count_reservations']); - }); - }, - - _onLoadCalendarSettings: function (ev) { - var self = this; - this.model.get_hcalendar_settings().then(function(results){ - self.renderer.setHCalendarSettings(results); - }); - }, - - _onBusNotification: function (notifications) { - if (!this.renderer._hcalendar) { - return; - } - for (var notif of notifications) { - if (notif[0][1] === 'hotel.reservation') { - switch (notif[1]['type']) { - case 'pricelist': - var prices = notif[1]['price']; - var pricelist_id = Object.keys(prices)[0]; - var pr = {}; - for (var price of prices[pricelist_id]) { - pr[price['room']] = []; - var days = Object.keys(price['days']); - for (var day of days) { - var dt = HotelCalendarManagement.toMoment(day); - pr[price['room']].push({ - 'date': dt.format(HotelConstants.ODOO_DATE_MOMENT_FORMAT), - 'price': price['days'][day], - 'id': price['id'] - }); - } - } - this.renderer._hcalendar.addPricelist(pr); - break; - case 'restriction': - // FIXME: Expected one day and one room_type - var restriction = notif[1]['restriction']; - var room_type = Object.keys(restriction)[0]; - var day = Object.keys(restriction[room_type])[0]; - var dt = HotelCalendarManagement.toMoment(day); - var rest = {}; - rest[room_type] = [{ - 'date': dt.format(HotelConstants.ODOO_DATE_MOMENT_FORMAT), - 'min_stay': restriction[room_type][day][0], - 'min_stay_arrival': restriction[room_type][day][1], - 'max_stay': restriction[room_type][day][2], - 'max_stay_arrival': restriction[room_type][day][3], - 'closed': restriction[room_type][day][4], - 'closed_arrival': restriction[room_type][day][5], - 'closed_departure': restriction[room_type][day][6], - 'id': restriction[room_type][day][7] - }]; - this.renderer._hcalendar.addRestrictions(rest); - break; - } - } - } - }, - -}); - -return MPMSCalendarController; - -}); diff --git a/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_model.js b/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_model.js deleted file mode 100644 index 7167dbd32..000000000 --- a/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_model.js +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2018 Alexandre Díaz -// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('hotel_calendar.MPMSCalendarModel', function (require) { -"use strict"; - -var AbstractModel = require('web.AbstractModel'), - Context = require('web.Context'), - Core = require('web.core'), - FieldUtils = require('web.field_utils'), - Session = require('web.session'); - - -return AbstractModel.extend({ - init: function () { - this._super.apply(this, arguments); - this.end_date = null; - }, - - load: function (params) { - this.modelName = params.modelName; - }, - - save_changes: function (params) { - // params.splice(0, 0, false); // FIXME: ID=False because first parameter its an integer - return this._rpc({ - model: this.modelName, - method: 'save_changes', - args: params, - context: Session.user_context, - }); - }, - - get_hcalendar_data: function (params) { - return this._rpc({ - model: this.modelName, - method: 'get_hcalendar_all_data', - args: params, - context: Session.user_context, - }); - }, - - get_pricelist_plans: function () { - var domain = [['pricelist_type', '=', 'daily']]; - domain.push('|',['hotel_ids', 'in', Session.hotel_id], - ['hotel_ids', '=', false]); - return this._rpc({ - model: 'product.pricelist', - method: 'search_read', - args: [domain, ['id','name']], - context: Session.user_context, - }); - }, - - get_restriction_plans: function () { - var domain = [['hotel_id', '=', Session.hotel_id]]; - return this._rpc({ - model: 'hotel.room.type.restriction', - method: 'search_read', - args: [domain, ['id','name']], - context: Session.user_context, - }); - }, - - get_hcalendar_settings: function () { - return this._rpc({ - model: this.modelName, - method: 'get_hcalendar_settings', - args: [false], - }); - }, -}); -}); diff --git a/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js b/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js deleted file mode 100644 index be035d910..000000000 --- a/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_renderer.js +++ /dev/null @@ -1,512 +0,0 @@ -/* global $, odoo, _, HotelCalendar, moment */ -// Copyright 2018 Alexandre Díaz -// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('hotel_calendar.MPMSCalendarRenderer', function (require) { -"use strict"; - -var Core = require('web.core'), - ViewDialogs = require('web.view_dialogs'), - Dialog = require('web.Dialog'), - Session = require('web.session'), - AbstractRenderer = require('web.AbstractRenderer'), - HotelConstants = require('hotel_calendar.Constants'), - //Formats = require('web.formats'), - - _t = Core._t, - _lt = Core._lt, - QWeb = Core.qweb; - -var HotelCalendarManagementView = AbstractRenderer.extend({ - /** VIEW OPTIONS **/ - template: "hotel_calendar.HotelCalendarManagementView", - display_name: _lt('Hotel Calendar Management'), - icon: 'fa fa-map-marker', - searchable: false, - searchview_hidden: true, - - // Custom Options - _view_options: {}, - _hcalendar: null, - _last_dates: [false, false], - _pricelist_id: null, - _restriction_id: null, - _days_tooltips: [], - - - /** VIEW METHODS **/ - init: function(parent, state, params) { - this._super.apply(this, arguments); - - this.model = params.model; - }, - - start: function () { - var self = this; - return this._super().then(function() { - self.init_calendar_view(); - $(window).trigger('resize'); - }); - }, - - do_show: function() { - if (this.$ehcal) { - this.$ehcal.show(); - $('.o_content').css('overflow', 'hidden'); - } - this.do_push_state({}); - return this._super(); - }, - do_hide: function () { - if (this.$ehcal) { - this.$ehcal.hide(); - $('.o_content').css('overflow', ''); - } - return this._super(); - }, - - destroy: function () { - return this._super.apply(this, arguments); - }, - - /** CUSTOM METHODS **/ - get_values_to_save: function() { - var btn_save = this.$el.find('#btn_save_changes'); - if (!btn_save.hasClass('need-save')) { - return false; - } - - var pricelist = this._hcalendar.getPricelist(true); - var restrictions = this._hcalendar.getRestrictions(true); - - var params = this.generate_params(); - return [params['prices'], params['restrictions'], pricelist, restrictions]; - }, - - save_changes: function() { - var oparams = this.get_values_to_save(); - if (oparams) { - this.trigger_up('onSaveChanges', oparams); - } - }, - - resetSaveState: function() { - this.$el.find('#btn_save_changes').removeClass('need-save'); - $('.hcal-management-record-changed').removeClass('hcal-management-record-changed'); - $('.hcal-management-input-changed').removeClass('hcal-management-input-changed'); - }, - - create_calendar: function(rooms) { - var self = this; - // CALENDAR - if (this._hcalendar) { - delete this._hcalendar; - } - this.$ehcal.empty(); - - var options = { - rooms: rooms, - days: self._view_options['days'], - endOfWeek: parseInt(self._view_options['eday_week']) || 6, - endOfWeekOffset: self._view_options['eday_week_offset'] || 0, - dateFormatLong: HotelConstants.ODOO_DATETIME_MOMENT_FORMAT, - dateFormatShort: HotelConstants.ODOO_DATE_MOMENT_FORMAT, - translations: { - 'Open': _t('Open'), - 'Closed': _t('Closed'), - 'C. Departure': _t('C. Departure'), - 'C. Arrival': _t('C. Arrival'), - 'Price': _t('Price'), - 'Availability': _t('Availability'), - 'Min. Stay': _t('Min. Stay'), - 'Max. Stay': _t('Max. Stay'), - 'Min. Stay Arrival': _t('Min. Stay Arrival'), - 'Max. Stay Arrival': _t('Max. Stay Arrival'), - 'Clousure': _t('Clousure'), - 'Free Rooms': _t('Free Rooms'), - 'No OTA': _t('No OTA'), - 'Options': _t('Options'), - 'Reset': _t('Reset'), - 'Copy': _t('Copy'), - 'Paste': _t('Paste'), - 'Clone': _t('Clone'), - 'Cancel': _t('Cancel') - } - }; - - this._hcalendar = new HotelCalendarManagement('#hcal_management_widget', options, this.$el[0]); - this._assignHCalendarEvents(); - - this.$CalendarHeaderDays = this.$el.find("div.table-room_type-data-header"); - - // Sticky Header Days - $('.o_content').scroll(this._on_scroll.bind(this)); - - // Initialize Save Button state to disable - document.getElementById("btn_save_changes").disabled = true; - }, - - setCalendarData: function (prices, restrictions, availability, count_reservations) { - this._hcalendar.setData(prices, restrictions, availability, count_reservations); - this._assign_extra_info(); - }, - - _assignHCalendarEvents: function () { - var self = this; - this._hcalendar.addEventListener('hcOnChangeDate', function(ev){ - var date_begin = moment(ev.detail.newDate); - var $dateTimePickerBegin = self.$el.find('#mpms-search #date_begin'); - $dateTimePickerBegin.data("ignore_onchange", true); - $dateTimePickerBegin.data("DateTimePicker").date(date_begin); - self.reload_hcalendar_management(); - }); - this._hcalendar.addEventListener('hcmOnInputChanged', function(ev){ - var btn_save = self.$el.find('#btn_save_changes'); - if (self._hcalendar.hasChangesToSave()) { - btn_save.addClass('need-save'); - document.getElementById("btn_save_changes").disabled = false; - } else { - btn_save.removeClass('need-save'); - document.getElementById("btn_save_changes").disabled = true; - } - }); - }, - - _on_scroll: function() { - var curScrollPos = $('.o_content').scrollTop(); - if (curScrollPos > 0) { - this.$CalendarHeaderDays.css({ - top: `${curScrollPos-this.$ehcal.position().top}px`, - position: 'sticky' - }); - } else { - this.$CalendarHeaderDays.css({ - top: '0px', - position: 'initial' - }); - } - }, - - loadViewFilters: function (resultsPricelist, resultsRestrictions) { - var self = this; - - var $list = self.$el.find('#mpms-search #price_list'); - $list.html(''); - resultsPricelist.forEach(function(item, index){ - $list.append(``); - }); - $list.select2(); - $list.on('change', function(ev){ - self._check_unsaved_changes(function(){ - self.reload_hcalendar_management(); - }); - }); - - $list = self.$el.find('#mpms-search #restriction_list'); - $list.html(''); - resultsRestrictions.forEach(function(item, index){ - $list.append(``); - }); - $list.select2(); - $list.on('change', function(ev){ - self._check_unsaved_changes(function(){ - self.reload_hcalendar_management(); - }); - }); - - $list = self.$el.find('#mpms-search #mode_list'); - $list.select2({ - minimumResultsForSearch: -1 - }); - $list.on('change', function(ev){ - var mode = HotelCalendarManagement.MODE.ALL; - if (this.value === 'low') { - mode = HotelCalendarManagement.MODE.LOW; - } else if (this.value === 'medium') { - mode = HotelCalendarManagement.MODE.MEDIUM; - } - self._hcalendar.setMode(mode); - }); - }, - - call_action: function(action) { - this.do_action(action); - }, - - init_calendar_view: function(){ - var self = this; - - this.$ehcal = this.$el.find("div#hcal_management_widget"); - - /** VIEW CONTROLS INITIALIZATION **/ - // DATE TIME PICKERS - var DTPickerOptions = { - viewMode: 'months', - icons : { - time: 'fa fa-clock-o', - date: 'fa fa-calendar', - up: 'fa fa-chevron-up', - down: 'fa fa-chevron-down' - }, - locale : moment.locale(), - format : HotelConstants.L10N_DATE_MOMENT_FORMAT, - //disabledHours: [0, 1, 2, 3, 4, 5, 6, 7, 8, 18, 19, 20, 21, 22, 23] - }; - - var $dateTimePickerBegin = this.$el.find('#mpms-search #date_begin'); - $dateTimePickerBegin.datetimepicker(DTPickerOptions); - $dateTimePickerBegin.on("dp.change", function (e) { - $dateTimePickerBegin.data("DateTimePicker").hide(); // TODO: Odoo uses old datetimepicker version - self.on_change_filter_date(e, true); - }); - - var $dateEndDays = this.$el.find('#mpms-search #date_end_days'); - $dateEndDays.select2({ - data: [ - {id:7, text: '1w'}, - {id:12, text: '2w'}, - {id:21, text: '3w'}, - {id:'month', text: '1m'}, - {id:60, text: '2m'}, - {id:90, text: '3m'}, - ], - allowClear: true, - minimumResultsForSearch: -1 - }); - - $dateEndDays.on("change", function (e) { - self.on_change_filter_date(); - }); - - // View Events - this.$el.find("#mpms-search #cal-pag-prev-plus").on('click', function(ev){ - // FIXME: Ugly repeated code. Change place. - var $dateTimePickerBegin = self.$el.find('#mpms-search #date_begin'); - var date_begin = $dateTimePickerBegin.data("DateTimePicker").date().subtract(14, 'd'); - $dateTimePickerBegin.data("ignore_onchange", true); - $dateTimePickerBegin.data("DateTimePicker").date(date_begin); - self.on_change_filter_date(ev, true); - ev.preventDefault(); - }); - this.$el.find("#mpms-search #cal-pag-prev").on('click', function(ev){ - // FIXME: Ugly repeated code. Change place. - var $dateTimePickerBegin = self.$el.find('#mpms-search #date_begin'); - var date_begin = $dateTimePickerBegin.data("DateTimePicker").date().subtract(7, 'd'); - $dateTimePickerBegin.data("ignore_onchange", true); - $dateTimePickerBegin.data("DateTimePicker").date(date_begin); - self.on_change_filter_date(ev, true); - ev.preventDefault(); - }); - this.$el.find("#mpms-search #cal-pag-next-plus").on('click', function(ev){ - // FIXME: Ugly repeated code. Change place. - var $dateTimePickerBegin = self.$el.find('#mpms-search #date_begin'); - var date_begin = $dateTimePickerBegin.data("DateTimePicker").date().add(14, 'd'); - $dateTimePickerBegin.data("ignore_onchange", true); - $dateTimePickerBegin.data("DateTimePicker").date(date_begin); - self.on_change_filter_date(ev, true); - ev.preventDefault(); - }); - this.$el.find("#mpms-search #cal-pag-next").on('click', function(ev){ - // FIXME: Ugly repeated code. Change place. - var $dateTimePickerBegin = self.$el.find('#mpms-search #date_begin'); - var date_begin = $dateTimePickerBegin.data("DateTimePicker").date().add(7, 'd'); - $dateTimePickerBegin.data("ignore_onchange", true); - $dateTimePickerBegin.data("DateTimePicker").date(date_begin); - self.on_change_filter_date(ev, true); - ev.preventDefault(); - }); - this.$el.find("#mpms-search #cal-pag-selector").on('click', function(ev){ - // FIXME: Ugly repeated code. Change place. - var $dateTimePickerBegin = self.$el.find('#mpms-search #date_begin'); - var date_begin = moment().startOf('day'); - $dateTimePickerBegin.data("ignore_onchange", true); - $dateTimePickerBegin.data("DateTimePicker").date(date_begin); - self.on_change_filter_date(ev, true); - ev.preventDefault(); - }); - - // Save Button - this.$el.find("#btn_save_changes").on('click', function(ev) { - document.getElementById(this.id).disabled = true; - self.save_changes(); - }); - - // Launch Massive Changes - this.$el.find("#btn_massive_changes").on('click', function(ev){ - self.call_action("hotel.action_hotel_massive_change"); - }); - - /** RENDER CALENDAR **/ - this.trigger_up('onLoadCalendarSettings'); - }, - - setHCalendarSettings: function (results) { - this._view_options = results; - var date_begin = moment().startOf('day'); - if (['xs', 'md'].indexOf(this._findBootstrapEnvironment()) >= 0) { - this._view_options['days'] = 7; - } else { - this._view_options['days'] = (this._view_options['days'] !== 'month')?parseInt(this._view_options['days']):date_begin.daysInMonth(); - } - - var $dateTimePickerBegin = this.$el.find('#mpms-search #date_begin'); - $dateTimePickerBegin.data("DateTimePicker").date(date_begin); - - var $dateEndDays = this.$el.find('#mpms-search #date_end_days'); - $dateEndDays.val('month'); - $dateEndDays.trigger('change'); - - this._last_dates = this.generate_params()['dates']; - this.trigger_up('onLoadCalendar'); - }, - - on_change_filter_date: function(ev, isStartDate) { - var self = this; - isStartDate = isStartDate || false; - var $dateTimePickerBegin = this.$el.find('#mpms-search #date_begin'); - var $dateEndDays = this.$el.find('#mpms-search #date_end_days'); - - // FIXME: Hackish onchange ignore (Used when change dates from code) - if ($dateTimePickerBegin.data("ignore_onchange") || $dateEndDays.data("ignore_onchange")) { - $dateTimePickerBegin.data("ignore_onchange", false); - $dateEndDays.data("ignore_onchange", false); - return true; - } - - var date_begin = $dateTimePickerBegin.data("DateTimePicker").date().set({'hour': 0, 'minute': 0, 'second': 0}).clone(); - - if (this._hcalendar && date_begin) { - var days = $dateEndDays.val(); - if (days === 'month') { - days = date_begin.daysInMonth(); - } - var date_end = date_begin.set({'hour': 23, 'minute': 59, 'second': 59}).clone().add(days, 'd'); - - this._check_unsaved_changes(function(){ - self._hcalendar.setStartDate(date_begin, self._hcalendar.getDateDiffDays(date_begin, date_end)); - self.reload_hcalendar_management(); - }); - } - }, - - reload_hcalendar_management: function() { - this.trigger_up('onLoadNewContentCalendar'); - }, - - generate_params: function() { - var fullDomain = []; - var prices = this.$el.find('#mpms-search #price_list').val(); - var restrictions = this.$el.find('#mpms-search #restriction_list').val(); - - var $dateTimePickerBegin = this.$el.find('#mpms-search #date_begin'); - var $dateEndDays = this.$el.find('#mpms-search #date_end_days'); - - var date_begin = $dateTimePickerBegin.data("DateTimePicker").date().set({'hour': 0, 'minute': 0, 'second': 0}).clone(); - - var days = $dateEndDays.val(); - if (days === 'month') { - days = date_begin.daysInMonth(); - } - var date_end = date_begin.set({'hour': 23, 'minute': 59, 'second': 59}).clone().add(days, 'd'); - return { - 'dates': [date_begin, date_end], - 'prices': prices, - 'restrictions': restrictions - }; - }, - - _check_unsaved_changes: function(fnCallback) { - var self = this; - var btn_save = this.$el.find("#btn_save_changes"); - if (!btn_save.hasClass('need-save')) { - btn_save.removeClass('need-save'); - document.getElementById("btn_save_changes").disabled = true; - fnCallback(); - return; - } - - new Dialog(self, { - title: _t("Unsaved Changes!"), - buttons: [ - { - text: _t("Yes, save it"), - classes: 'btn-primary', - close: true, - click: function() { - document.getElementById("btn_save_changes").disabled = true; - self.save_changes(); - fnCallback(); - } - }, - { - text: _t("No"), - close: true, - click: function() { - btn_save.removeClass('need-save'); - document.getElementById("btn_save_changes").disabled = true; - fnCallback(); - } - } - ], - $content: QWeb.render('HotelCalendarManagement.UnsavedChanges', {}) - }).open(); - }, - - _findBootstrapEnvironment: function() { - var envs = ['xs', 'sm', 'md', 'lg']; - - var $el = $('
'); - $el.appendTo($('body')); - - for (var i = envs.length - 1; i >= 0; i--) { - var env = envs[i]; - - $el.addClass('hidden-'+env); - if ($el.is(':hidden')) { - $el.remove(); - return env; - } - } - }, - - _assign_extra_info: function() { - var self = this; - - $(this._hcalendar.etableHeader).find('.hcal-cell-header-day').each(function(index, elm){ - var $elm = $(elm); - var cdate = HotelCalendarManagement.toMoment($elm.data('hcalDate'), HotelConstants.L10N_DATE_MOMENT_FORMAT); - var data = _.filter(self._days_tooltips, function(item) { - var ndate = HotelCalendarManagement.toMoment(item[2], HotelConstants.ODOO_DATE_MOMENT_FORMAT); - return ndate.isSame(cdate, 'd'); - }); - if (data.length > 0) { - $elm.addClass('hcal-event-day'); - $elm.on("mouseenter", function(data){ - var $this = $(this); - if (data.length > 0) { - var qdict = { - 'date': $this.data('hcalDate'), - 'events': _.map(data, function(item){ - return { - 'name': item[1], - 'date': item[2], - 'location': item[3] - }; - }) - }; - $this.attr('title', ''); - $this.tooltip({ - animation: true, - html: true, - placement: 'bottom', - title: QWeb.render('HotelCalendar.TooltipEvent', qdict) - }).tooltip('show'); - } - }.bind(elm, data)); - } - }); - }, -}); - -return HotelCalendarManagementView; - -}); diff --git a/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v_deprecated.js b/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v_deprecated.js deleted file mode 100644 index 096866cb2..000000000 --- a/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_v_deprecated.js +++ /dev/null @@ -1,661 +0,0 @@ -/* global $, odoo, _, HotelCalendarManagement, moment */ -// Copyright 2018 Alexandre Díaz -// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('hotel_calendar.HotelCalendarManagementView', function (require) { -"use strict"; - -var Core = require('web.core'), - Bus = require('bus.bus').bus, - //Data = require('web.data'), - Time = require('web.time'), - Model = require('web.DataModel'), - View = require('web.View'), - Widgets = require('web_calendar.widgets'), - //Common = require('web.form_common'), - //Pyeval = require('web.pyeval'), - ActionManager = require('web.ActionManager'), - Utils = require('web.utils'), - Dialog = require('web.Dialog'), - //Ajax = require('web.ajax'), - ControlPanel = require('web.ControlPanel'), - //Session = require('web.session'), - formats = require('web.formats'), - - _t = Core._t, - _lt = Core._lt, - QWeb = Core.qweb, - l10n = _t.database.parameters, - - ODOO_DATETIME_MOMENT_FORMAT = "YYYY-MM-DD HH:mm:ss", - ODOO_DATE_MOMENT_FORMAT = "YYYY-MM-DD", - L10N_DATE_MOMENT_FORMAT = "DD/MM/YYYY", //FIXME: Time.strftime_to_moment_format(l10n.date_format); - L10N_DATETIME_MOMENT_FORMAT = L10N_DATE_MOMENT_FORMAT + ' ' + Time.strftime_to_moment_format(l10n.time_format); - - -/* HIDE CONTROL PANEL */ -/* FIXME: Look's like a hackish solution */ -ControlPanel.include({ - update: function(status, options) { - if (typeof options.toHide === 'undefined') - options.toHide = false; - var action_stack = this.getParent().action_stack; - if (action_stack && action_stack.length) { - var active_action = action_stack[action_stack.length-1]; - if (active_action.widget && active_action.widget.active_view && - active_action.widget.active_view.type === 'mpms'){ - options.toHide = true; - } - } - this._super(status, options); - this._toggle_visibility(!options.toHide); - } -}); - -var HotelCalendarManagementView = View.extend({ - /** VIEW OPTIONS **/ - template: "hotel_calendar.HotelCalendarManagementView", - display_name: _lt('Hotel Calendar Management'), - icon: 'fa fa-map-marker', - //view_type: "mpms", - searchable: false, - searchview_hidden: true, - quick_create_instance: Widgets.QuickCreate, - defaults: _.extend({}, View.prototype.defaults, { - confirm_on_delete: true, - }), - - // Custom Options - _model: null, - _hcalendar: null, - _action_manager: null, - _last_dates: [false, false], - _pricelist_id: null, - _restriction_id: null, - _days_tooltips: [], - - /** VIEW METHODS **/ - init: function(parent, dataset, fields_view, options) { - this._super.apply(this, arguments); - this.shown = $.Deferred(); - this.dataset = dataset; - this.model = dataset.model; - this.view_type = 'mpms'; - this.selected_filters = []; - this.mutex = new Utils.Mutex(); - this._model = new Model(this.dataset.model); - this._action_manager = this.findAncestor(function(ancestor){ return ancestor instanceof ActionManager; }); - - Bus.on("notification", this, this._on_bus_signal); - }, - - start: function () { - this.shown.done(this._do_show_init.bind(this)); - return this._super(); - }, - - _do_show_init: function () { - this.init_calendar_view().then(function() { - $(window).trigger('resize'); - }); - }, - - do_show: function() { - if (this.$ehcal) { - this.$ehcal.show(); - $('.o_content').css('overflow', 'hidden'); - } - this.do_push_state({}); - this.shown.resolve(); - return this._super(); - }, - do_hide: function () { - if (this.$ehcal) { - this.$ehcal.hide(); - $('.o_content').css('overflow', ''); - } - return this._super(); - }, - - destroy: function () { - return this._super.apply(this, arguments); - }, - - /** CUSTOM METHODS **/ - save_changes: function() { - var self = this; - var btn_save = this.$el.find('#btn_save_changes'); - if (!btn_save.hasClass('need-save')) { - return; - } - - var pricelist = this._hcalendar.getPricelist(true); - var restrictions = this._hcalendar.getRestrictions(true); - var availability = this._hcalendar.getAvailability(true); - - var params = this.generate_params(); - var oparams = [false, params['prices'], params['restrictions'], pricelist, restrictions, availability]; - this._model.call('save_changes', oparams).then(function(results){ - btn_save.removeClass('need-save'); - $('.hcal-management-record-changed').removeClass('hcal-management-record-changed'); - $('.hcal-management-input-changed').removeClass('hcal-management-input-changed'); - }); - }, - - create_calendar: function(options) { - var self = this; - // CALENDAR - if (this._hcalendar) { - delete this._hcalendar; - } - - this.$ehcal.empty(); - - this._hcalendar = new HotelCalendarManagement('#hcal_management_widget', options, this.$el[0]); - this._hcalendar.addEventListener('hcOnChangeDate', function(ev){ - var date_begin = moment(ev.detail.newDate); - var days = self._hcalendar.getOptions('days')-1; - var date_end = date_begin.clone().add(days, 'd'); - - var $dateTimePickerBegin = self.$el.find('#mpms-search #date_begin'); - var $dateTimePickerEnd = self.$el.find('#mpms-search #date_end'); - $dateTimePickerBegin.data("ignore_onchange", true); - $dateTimePickerBegin.data("DateTimePicker").setDate(date_begin); - $dateTimePickerEnd.data("DateTimePicker").setDate(date_end); - - self.reload_hcalendar_management(); - }); - this._hcalendar.addEventListener('hcmOnInputChanged', function(ev){ - var btn_save = self.$el.find('#btn_save_changes'); - if (self._hcalendar.hasChangesToSave()) { - btn_save.addClass('need-save'); - } else { - btn_save.removeClass('need-save'); - } - }); - - this.$CalendarHeaderDays = this.$el.find("div.table-room_type-data-header"); - - // Sticky Header Days - this.$ehcal.scroll(this._on_scroll.bind(this)); - }, - - _on_scroll: function() { - var curScrollPos = this.$ehcal.scrollTop(); - if (curScrollPos > 0) { - this.$CalendarHeaderDays.css({ - top: `${curScrollPos}px`, - position: 'sticky' - }); - } else { - this.$CalendarHeaderDays.css({ - top: '0px', - position: 'initial' - }); - } - }, - - generate_hotel_calendar: function(){ - var self = this; - debugger; - /** DO MAGIC **/ - var params = this.generate_params(); - var oparams = [params['dates'][0], params['dates'][1], false, false, true]; - this._model.call('get_hcalendar_all_data', oparams).then(function(results){ - self._days_tooltips = results['events']; - var rooms = []; - for (var r of results['rooms']) { - var nroom = new HRoomType( - r[0], // Id - r[1], // Name - r[2], // Capacity - r[3], // Price - ); - rooms.push(nroom); - } - - // Get Pricelists - self._pricelist_id = results['pricelist_id']; - new Model('product.pricelist').query(['id','name']).all().then(function(resultsPricelist){ - var $list = self.$el.find('#mpms-search #price_list'); - $list.html(''); - resultsPricelist.forEach(function(item, index){ - $list.append(``); - }); - $list.select2(); - $list.on('change', function(ev){ - self._check_unsaved_changes(function(){ - self.reload_hcalendar_management(); - }); - }); - }); - - // Get Restrictions - self._restriction_id = results['restriction_id']; - new Model('hotel.room.type.restriction').query(['id','name']).all().then(function(resultsRestrictions){ - var $list = self.$el.find('#mpms-search #restriction_list'); - $list.html(''); - resultsRestrictions.forEach(function(item, index){ - $list.append(``); - }); - $list.select2(); - $list.on('change', function(ev){ - self._check_unsaved_changes(function(){ - self.reload_hcalendar_management(); - }); - }); - }); - - // Calendar Mode - var $list = self.$el.find('#mpms-search #mode_list'); - $list.select2({ - minimumResultsForSearch: -1 - }); - $list.on('change', function(ev){ - var mode = HotelCalendarManagement.MODE.ALL; - if (this.value === 'low') { - mode = HotelCalendarManagement.MODE.LOW; - } else if (this.value === 'medium') { - mode = HotelCalendarManagement.MODE.MEDIUM; - } - self._hcalendar.setMode(mode); - }); - - self.create_calendar({ - rooms: rooms, - days: self._view_options['days'], - endOfWeek: parseInt(self._view_options['eday_week']) || 6, - endOfWeekOffset: self._view_options['eday_week_offset'] || 0, - dateFormatLong: ODOO_DATETIME_MOMENT_FORMAT, - dateFormatShort: ODOO_DATE_MOMENT_FORMAT, - translations: { - 'Open': _t('Open'), - 'Closed': _t('Closed'), - 'C. Departure': _t('C. Departure'), - 'C. Arrival': _t('C. Arrival'), - 'Price': _t('Price'), - 'Availability': _t('Availability'), - 'Min. Stay': _t('Min. Stay'), - 'Max. Stay': _t('Max. Stay'), - 'Min. Stay Arrival': _t('Min. Stay Arrival'), - 'Max. Stay Arrival': _t('Max. Stay Arrival'), - 'Clousure': _t('Clousure'), - 'Free Rooms': _t('Free Rooms'), - 'No OTA': _t('No OTA'), - 'Options': _t('Options'), - 'Reset': _t('Reset'), - 'Copy': _t('Copy'), - 'Paste': _t('Paste'), - 'Clone': _t('Clone'), - 'Cancel': _t('Cancel') - } - }); - self._hcalendar.setData(results['prices'], results['restrictions'], results['availability'], results['count_reservations']); - self._assign_extra_info(); - }); - }, - - call_action: function(action) { - this._action_manager.do_action(action); - }, - - init_calendar_view: function(){ - var self = this; - - this.$ehcal = this.$el.find("div#hcal_management_widget"); - - /** VIEW CONTROLS INITIALIZATION **/ - // DATE TIME PICKERS - var l10nn = _t.database.parameters - var DTPickerOptions = { - viewMode: 'months', - icons : { - time: 'fa fa-clock-o', - date: 'fa fa-calendar', - up: 'fa fa-chevron-up', - down: 'fa fa-chevron-down' - }, - language : moment.locale(), - format : L10N_DATE_MOMENT_FORMAT, - disabledHours: true // TODO: Odoo uses old datetimepicker version - }; - var $dateTimePickerBegin = this.$el.find('#mpms-search #date_begin'); - var $dateTimePickerEnd = this.$el.find('#mpms-search #date_end'); - $dateTimePickerBegin.datetimepicker(DTPickerOptions); - $dateTimePickerEnd.datetimepicker($.extend({}, DTPickerOptions, { 'useCurrent': false })); - $dateTimePickerBegin.on("dp.change", function (e) { - $dateTimePickerEnd.data("DateTimePicker").setMinDate(e.date.add(3,'d')); - $dateTimePickerEnd.data("DateTimePicker").setMaxDate(e.date.add(2,'M')); - $dateTimePickerBegin.data("DateTimePicker").hide(); // TODO: Odoo uses old datetimepicker version - self.on_change_filter_date(e, true); - }); - $dateTimePickerEnd.on("dp.change", function (e) { - self.on_change_filter_date(e, false); - $dateTimePickerEnd.data("DateTimePicker").hide(); // TODO: Odoo uses old datetimepicker version - }); - - // var date_begin = moment().startOf('day'); - // var date_end = date_begin.clone().add(this._view_options['days'], 'd').endOf('day'); - // $dateTimePickerBegin.data("ignore_onchange", true); - // $dateTimePickerBegin.data("DateTimePicker").setDate(date_begin); - // $dateTimePickerEnd.data("DateTimePicker").setDate(date_end); - // this._last_dates = this.generate_params()['dates']; - - // View Events - this.$el.find("#mpms-search #cal-pag-prev-plus").on('click', function(ev){ - // FIXME: Ugly repeated code. Change place. - var $dateTimePickerBegin = self.$el.find('#mpms-search #date_begin'); - var $dateTimePickerEnd = self.$el.find('#mpms-search #date_end'); - //var days = moment($dateTimePickerBegin.data("DateTimePicker").getDate()).clone().local().daysInMonth(); - var date_begin = $dateTimePickerBegin.data("DateTimePicker").getDate().subtract(14, 'd'); - var date_end = $dateTimePickerEnd.data("DateTimePicker").getDate().subtract(14, 'd'); - $dateTimePickerBegin.data("ignore_onchange", true); - $dateTimePickerBegin.data("DateTimePicker").setDate(date_begin); - $dateTimePickerEnd.data("DateTimePicker").setDate(date_end); - - ev.preventDefault(); - }); - this.$el.find("#mpms-search #cal-pag-prev").on('click', function(ev){ - // FIXME: Ugly repeated code. Change place. - var $dateTimePickerBegin = self.$el.find('#mpms-search #date_begin'); - var $dateTimePickerEnd = self.$el.find('#mpms-search #date_end'); - var date_begin = $dateTimePickerBegin.data("DateTimePicker").getDate().subtract(7, 'd'); - var date_end = $dateTimePickerEnd.data("DateTimePicker").getDate().subtract(7, 'd'); - $dateTimePickerBegin.data("ignore_onchange", true); - $dateTimePickerBegin.data("DateTimePicker").setDate(date_begin); - $dateTimePickerEnd.data("DateTimePicker").setDate(date_end); - - ev.preventDefault(); - }); - this.$el.find("#mpms-search #cal-pag-next-plus").on('click', function(ev){ - // FIXME: Ugly repeated code. Change place. - var $dateTimePickerBegin = self.$el.find('#mpms-search #date_begin'); - var $dateTimePickerEnd = self.$el.find('#mpms-search #date_end'); - //var days = moment($dateTimePickerBegin.data("DateTimePicker").getDate()).clone().local().daysInMonth(); - var date_begin = $dateTimePickerBegin.data("DateTimePicker").getDate().add(14, 'd'); - var date_end = $dateTimePickerEnd.data("DateTimePicker").getDate().add(14, 'd'); - $dateTimePickerBegin.data("ignore_onchange", true); - $dateTimePickerBegin.data("DateTimePicker").setDate(date_begin); - $dateTimePickerEnd.data("DateTimePicker").setDate(date_end); - - ev.preventDefault(); - }); - this.$el.find("#mpms-search #cal-pag-next").on('click', function(ev){ - // FIXME: Ugly repeated code. Change place. - var $dateTimePickerBegin = self.$el.find('#mpms-search #date_begin'); - var $dateTimePickerEnd = self.$el.find('#mpms-search #date_end'); - var date_begin = $dateTimePickerBegin.data("DateTimePicker").getDate().add(7, 'd'); - var date_end = $dateTimePickerEnd.data("DateTimePicker").getDate().add(7, 'd'); - $dateTimePickerBegin.data("ignore_onchange", true); - $dateTimePickerBegin.data("DateTimePicker").setDate(date_begin); - $dateTimePickerEnd.data("DateTimePicker").setDate(date_end); - - ev.preventDefault(); - }); - this.$el.find("#mpms-search #cal-pag-selector").on('click', function(ev){ - // FIXME: Ugly repeated code. Change place. - var $dateTimePickerBegin = self.$el.find('#mpms-search #date_begin'); - var $dateTimePickerEnd = self.$el.find('#mpms-search #date_end'); - var date_begin = moment().startOf('day'); - var date_end = date_begin.clone().add(self._view_options['days'], 'd').endOf('day'); - $dateTimePickerBegin.data("ignore_onchange", true); - $dateTimePickerBegin.data("DateTimePicker").setDate(date_begin); - $dateTimePickerEnd.data("DateTimePicker").setDate(date_end); - - ev.preventDefault(); - }); - - // Save Button - this.$el.find("#btn_save_changes").on('click', function(ev){ - self.save_changes(); - }); - - // Launch Massive Changes - this.$el.find("#btn_massive_changes").on('click', function(ev){ - self.call_action("hotel.action_hotel_massive_change"); - }); - - /** RENDER CALENDAR **/ - this._model.call('get_hcalendar_settings', [false]).then(function(results){ - self._view_options = results; - var date_begin = moment().startOf('day'); - if (['xs', 'md'].indexOf(self._findBootstrapEnvironment()) >= 0) { - self._view_options['days'] = 7; - } else { - self._view_options['days'] = (self._view_options['days'] !== 'month')?parseInt(self._view_options['days']):date_begin.daysInMonth(); - } - var date_end = date_begin.clone().add(self._view_options['days'], 'd').endOf('day'); - var $dateTimePickerBegin = self.$el.find('#mpms-search #date_begin'); - var $dateTimePickerEnd = self.$el.find('#mpms-search #date_end'); - //$dateTimePickerBegin.data("ignore_onchange", true); - $dateTimePickerBegin.data("DateTimePicker").setDate(date_begin); - //$dateTimePickerEnd.data("ignore_onchange", true); - $dateTimePickerEnd.data("DateTimePicker").setDate(date_end); - self._last_dates = self.generate_params()['dates']; - - self.generate_hotel_calendar(); - }); - - return $.when(); - }, - - on_change_filter_date: function(ev, isStartDate) { - var self = this; - isStartDate = isStartDate || false; - var $dateTimePickerBegin = this.$el.find('#mpms-search #date_begin'); - var $dateTimePickerEnd = this.$el.find('#mpms-search #date_end'); - - // FIXME: Hackish onchange ignore (Used when change dates from code) - if ($dateTimePickerBegin.data("ignore_onchange") || $dateTimePickerEnd.data("ignore_onchange")) { - $dateTimePickerBegin.data("ignore_onchange", false); - $dateTimePickerEnd.data("ignore_onchange", false) - return true; - } - - var date_begin = $dateTimePickerBegin.data("DateTimePicker").getDate().set({'hour': 0, 'minute': 0, 'second': 0}).clone(); - - if (this._hcalendar && date_begin) { - if (isStartDate) { - var ndate_end = date_begin.clone().add(this._view_options['days'], 'd'); - $dateTimePickerEnd.data("ignore_onchange", true); - $dateTimePickerEnd.data("DateTimePicker").setDate(ndate_end.local()); - } - - var date_end = $dateTimePickerEnd.data("DateTimePicker").getDate().set({'hour': 23, 'minute': 59, 'second': 59}).clone(); - - this._check_unsaved_changes(function(){ - self._hcalendar.setStartDate(date_begin, self._hcalendar.getDateDiffDays(date_begin, date_end)); - self.reload_hcalendar_management(); - }); - } - }, - - _on_bus_signal: function(notifications) { - if (!this._hcalendar) { - return; - } - for (var notif of notifications) { - if (notif[0][1] === 'hotel.reservation') { - switch (notif[1]['type']) { - case 'availability': - var avail = notif[1]['availability']; - var room_type = Object.keys(avail)[0]; - var day = Object.keys(avail[room_type])[0]; - var dt = HotelCalendarManagement.toMoment(day); - var availability = {}; - availability[room_type] = [{ - 'date': dt.format(ODOO_DATE_MOMENT_FORMAT), - 'avail': avail[room_type][day][0], - 'no_ota': avail[room_type][day][1], - 'id': avail[room_type][day][2] - }]; - this._hcalendar.addAvailability(availability); - break; - case 'pricelist': - var prices = notif[1]['price']; - var pricelist_id = Object.keys(prices)[0]; - var pr = {}; - for (var price of prices[pricelist_id]) { - pr[price['room']] = []; - var days = Object.keys(price['days']); - for (var day of days) { - var dt = HotelCalendarManagement.toMoment(day); - pr[price['room']].push({ - 'date': dt.format(ODOO_DATE_MOMENT_FORMAT), - 'price': price['days'][day], - 'id': price['id'] - }); - } - } - this._hcalendar.addPricelist(pr); - break; - case 'restriction': - // FIXME: Expected one day and one room_type - var restriction = notif[1]['restriction']; - var room_type = Object.keys(restriction)[0]; - var day = Object.keys(restriction[room_type])[0]; - var dt = HotelCalendarManagement.toMoment(day); - var rest = {}; - rest[room_type] = [{ - 'date': dt.format(ODOO_DATE_MOMENT_FORMAT), - 'min_stay': restriction[room_type][day][0], - 'min_stay_arrival': restriction[room_type][day][1], - 'max_stay': restriction[room_type][day][2], - 'max_stay_arrival': restriction[room_type][day][3], - 'closed': restriction[room_type][day][4], - 'closed_arrival': restriction[room_type][day][5], - 'closed_departure': restriction[room_type][day][6], - 'id': restriction[room_type][day][7] - }]; - this._hcalendar.addRestrictions(rest); - break; - } - } - } - }, - - reload_hcalendar_management: function() { - var self = this; - var params = this.generate_params(); - var oparams = [params['dates'][0], params['dates'][1], params['prices'], params['restrictions'], false]; - this._model.call('get_hcalendar_all_data', oparams).then(function(results){ - self._days_tooltips = results['events']; - self._hcalendar.setData(results['prices'], results['restrictions'], results['availability'], results['count_reservations']); - self._assign_extra_info(); - }); - this._last_dates = params['dates']; - this.$CalendarHeaderDays = this.$el.find("div.table-room_type-data-header"); - this._on_scroll(); // FIXME: Workaround for update sticky header - }, - - generate_params: function() { - var fullDomain = []; - var prices = this.$el.find('#mpms-search #price_list').val(); - var restrictions = this.$el.find('#mpms-search #restriction_list').val(); - - var $dateTimePickerBegin = this.$el.find('#mpms-search #date_begin'); - var $dateTimePickerEnd = this.$el.find('#mpms-search #date_end'); - - var date_begin = $dateTimePickerBegin.data("DateTimePicker").getDate().set({'hour': 0, 'minute': 0, 'second': 0}).clone().utc().format(ODOO_DATE_MOMENT_FORMAT); - var date_end = $dateTimePickerEnd.data("DateTimePicker").getDate().set({'hour': 23, 'minute': 59, 'second': 59}).clone().utc().format(ODOO_DATE_MOMENT_FORMAT); - - return { - 'dates': [date_begin, date_end], - 'prices': prices, - 'restrictions': restrictions - }; - }, - - _check_unsaved_changes: function(fnCallback) { - var self = this; - var btn_save = this.$el.find("#btn_save_changes"); - if (!btn_save.hasClass('need-save')) { - btn_save.removeClass('need-save'); - fnCallback(); - return; - } - - new Dialog(self, { - title: _t("Unsaved Changes!"), - buttons: [ - { - text: _t("Yes, save it"), - classes: 'btn-primary', - close: true, - click: function() { - self.save_changes(); - fnCallback(); - } - }, - { - text: _t("No"), - close: true, - click: function() { - btn_save.removeClass('need-save'); - fnCallback(); - } - } - ], - $content: QWeb.render('HotelCalendarManagement.UnsavedChanges', {}) - }).open(); - }, - - _findBootstrapEnvironment: function() { - var envs = ['xs', 'sm', 'md', 'lg']; - - var $el = $('
'); - $el.appendTo($('body')); - - for (var i = envs.length - 1; i >= 0; i--) { - var env = envs[i]; - - $el.addClass('hidden-'+env); - if ($el.is(':hidden')) { - $el.remove(); - return env; - } - } - }, - - _assign_extra_info: function() { - var self = this; - - $(this._hcalendar.etableHeader).find('.hcal-cell-header-day').each(function(index, elm){ - var $elm = $(elm); - var cdate = HotelCalendar.toMoment($elm.data('hcalDate'), L10N_DATE_MOMENT_FORMAT); - var data = _.filter(self._days_tooltips, function(item) { - var ndate = HotelCalendar.toMoment(item[2], ODOO_DATE_MOMENT_FORMAT); - return ndate.isSame(cdate, 'd'); - }); - if (data.length > 0) { - $elm.addClass('hcal-event-day'); - $elm.on("mouseenter", function(data){ - var $this = $(this); - if (data.length > 0) { - var qdict = { - 'date': $this.data('hcalDate'), - 'events': _.map(data, function(item){ - return { - 'name': item[1], - 'date': item[2], - 'location': item[3] - }; - }) - }; - $this.attr('title', ''); - $this.tooltip({ - animation: true, - html: true, - placement: 'bottom', - title: QWeb.render('HotelCalendar.TooltipEvent', qdict) - }).tooltip('show'); - } - }.bind(elm, data)); - } - }); - }, -}); - -Core.view_registry.add('mpms', HotelCalendarManagementView); -return HotelCalendarManagementView; - -}); diff --git a/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_view.js b/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_view.js deleted file mode 100644 index 9e3af7d12..000000000 --- a/hotel_calendar/static/src/js/views/calendar_management/hotel_calendar_management_view.js +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2018 Alexandre Díaz -// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('hotel_calendar.MPMSCalendarView', function (require) { -"use strict"; - -var AbstractView = require('web.AbstractView'), - MPMSCalendarModel = require('hotel_calendar.MPMSCalendarModel'), - MPMSCalendarController = require('hotel_calendar.MPMSCalendarController'), - MPMSCalendarRenderer = require('hotel_calendar.MPMSCalendarRenderer'), - ViewRegistry = require('web.view_registry'), - SystrayMenu = require('web.SystrayMenu'), - ControlPanel = require('web.ControlPanel'), - Widget = require('web.Widget'), - Session = require('web.session'), - Core = require('web.core'), - - _lt = Core._lt, - QWeb = Core.qweb; - -/* HIDE CONTROL PANEL */ -/* FIXME: Look's like a hackish solution */ -ControlPanel.include({ - update: function(status, options) { - if (typeof options === 'undefined') { - options = {}; - } - if (typeof options.toHide === 'undefined') - options.toHide = false; - var action_stack = this.getParent().action_stack; - if (action_stack && action_stack.length) { - var active_action = action_stack[action_stack.length-1]; - if (active_action.widget && active_action.widget.active_view && - active_action.widget.active_view.type === 'mpms'){ - options.toHide = true; - } - } - this._super(status, options); - this._toggle_visibility(!options.toHide); - } -}); - -var MPMSCalendarView = AbstractView.extend({ - display_name: _lt('Calendar MPMS'), - icon: 'fa-calendar', - jsLibs: ['/hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js'], - cssLibs: [ - '/hotel_calendar/static/src/lib/hcalendar/css/hcalendar.css', - '/hotel_calendar/static/src/lib/hcalendar/css/hcalendar_management.css' - ], - config: { - Model: MPMSCalendarModel, - Controller: MPMSCalendarController, - Renderer: MPMSCalendarRenderer, - }, - - init: function (viewInfo, params) { - this._super.apply(this, arguments); - var arch = viewInfo.arch; - var fields = viewInfo.fields; - var attrs = arch.attrs; - - // If form_view_id is set, then the calendar view will open a form view - // with this id, when it needs to edit or create an event. - this.controllerParams.formViewId = - attrs.form_view_id ? parseInt(attrs.form_view_id, 10) : false; - if (!this.controllerParams.formViewId && params.action) { - var formViewDescr = _.find(params.action.views, function (v) { - return v[1] === 'form'; - }); - if (formViewDescr) { - this.controllerParams.formViewId = formViewDescr[0]; - } - } - - this.controllerParams.readonlyFormViewId = !attrs.readonly_form_view_id || !utils.toBoolElse(attrs.readonly_form_view_id, true) ? false : attrs.readonly_form_view_id; - this.controllerParams.context = params.context || {}; - this.controllerParams.displayName = params.action && params.action.name; - - this.rendererParams.model = viewInfo.model; - - this.loadParams.fields = fields; - this.loadParams.fieldsInfo = viewInfo.fieldsInfo; - this.loadParams.creatable = false; - - this.loadParams.mode = attrs.mode; - }, -}); - -ViewRegistry.add('mpms', MPMSCalendarView); - -return MPMSCalendarView; - -}); diff --git a/hotel_calendar/static/src/js/views/constants.js b/hotel_calendar/static/src/js/views/constants.js deleted file mode 100644 index f52526b83..000000000 --- a/hotel_calendar/static/src/js/views/constants.js +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2018 Alexandre Díaz -// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('hotel_calendar.Constants', function (require) { -"use strict"; - - var Core = require('web.core'), - Time = require('web.time'), - - l10n = Core._t.database.parameters; - - return { - ODOO_DATE_MOMENT_FORMAT: 'YYYY-MM-DD', - ODOO_DATETIME_MOMENT_FORMAT: 'YYYY-MM-DD HH:mm:ss', - L10N_DATE_MOMENT_FORMAT: "DD/MM/YYYY", //FIXME: Time.strftime_to_moment_format(l10n.date_format), - L10N_DATETIME_MOMENT_FORMAT: 'DD/MM/YYYY ' + Time.strftime_to_moment_format(l10n.time_format), - - CURRENCY_SYMBOL: "€", - }; - -}); diff --git a/hotel_calendar/static/src/js/widgets/MultiCalendar.js b/hotel_calendar/static/src/js/widgets/MultiCalendar.js deleted file mode 100644 index 086cde1f5..000000000 --- a/hotel_calendar/static/src/js/widgets/MultiCalendar.js +++ /dev/null @@ -1,398 +0,0 @@ -// Copyright 2018 Alexandre Díaz -// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('hotel_calendar.MultiCalendar', function(require) { - 'use strict'; - - var core = require('web.core'); - var session = require('web.session'); - var Widget = require('web.Widget'); - var HotelConstants = require('hotel_calendar.Constants'); - - var QWeb = core.qweb; - - var MultiCalendar = Widget.extend({ - _calendars: [], - _calendar_records: [], - _active_index: -1, - _events: {}, - _tabs: [], - _dataset: {}, - _base: null, - - - init: function(parent) { - this._super.apply(this, arguments); - }, - - start: function() { - this._super.apply(this, arguments); - - this._create_tabs_panel(); - }, - - on: function(event_name, callback) { - this.$el.on(event_name, callback.bind(this)); - }, - - reset: function() { - for (var i in this._calendars) { - delete this._calendars[i]; - } - for (var [$tab, $panel] of this._tabs) { - $tab.remove(); - $panel.remove(); - } - $('#multicalendar_tabs').remove(); - $('#multicalendar_panels').remove(); - this._calendars = []; - this._calendar_records = []; - this._tabs = []; - this._datasets = {}; - this._active_index = -1; - this._events = {}; - }, - - get_calendar: function(index) { - return this._calendars[index-1]; - }, - - get_calendar_record: function(index) { - return this._calendar_records[index-1]; - }, - - get_tab: function(index) { - return this._tabs[index]; - }, - - get_active_index: function() { - return this._active_index; - }, - - get_active_calendar: function() { - return this._calendars[this._active_index-1]; - }, - - get_active_tab: function() { - return this._tabs[this._active_index]; - }, - - update_active_tab_name: function(name) { - var [$tab, $panel] = this.get_tab(this.get_active_index()); - $tab.text(name); - }, - - get_active_filters: function() { - var calendar = this.get_active_calendar(); - var domain = calendar.getDomain(HotelCalendar.DOMAIN.ROOMS); - - var filters = {}; - for (var rule of domain) { - filters[rule[0]] = rule[2]; - } - - return filters; - }, - - recalculate_reservation_positions: function() { - var active_calendar = this.get_active_calendar(); - if (active_calendar) { - setTimeout(function(calendar){ - calendar._updateOffsets(); - calendar._updateReservations(false); - }.bind(this, active_calendar), 200); - } - }, - - remove_reservation: function(reserv_id) { - this._dataset['reservations'] = _.reject(this._dataset['reservations'], {id: reserv_id}); - for (var calendar of this._calendars) { - var reserv = calendar.getReservation(reserv_id); - if (reserv) { - calendar.removeReservation(reserv); - } - } - }, - - remove_extra_room_row: function(reserv, only_active_calendar) { - if (only_active_calendar) { - this.get_active_calendar().removeExtraRoomRow(reserv); - } else { - for (var calendar of this._calendars) { - calendar.removeExtraRoomRow(reserv); - } - } - }, - - swap_reservations: function(outReservs, inReservs) { - for (var calendar of this._calendars) { - calendar.swapReservations(outReservs, inReservs); - } - }, - - set_active_calendar: function(index) { - this._tabs[index+1][0].tab('show'); - }, - - set_datasets: function(pricelist, restrictions, reservations) { - this._dataset = { - pricelist: pricelist, - restrictions: restrictions, - reservations: reservations, - }; - }, - - set_options: function(options) { - this._options = options; - }, - - set_base_element: function(element) { - this._base = element; - }, - - merge_pricelist: function(pricelist, calendar) { - var keys = _.keys(pricelist); - for (var k of keys) { - var pr = pricelist[k]; - for (var pr_k in pr) { - var pr_item = pricelist[k][pr_k]; - var pr_fk = _.findKey(this._dataset['pricelist'][k], {'room': pr_item.room}); - if (pr_fk) { - this._dataset['pricelist'][k][pr_fk].room = pr_item.room; - this._dataset['pricelist'][k][pr_fk].days = _.extend(this._dataset['pricelist'][k][pr_fk].days, pr_item.days); - if (pr_item.title) { - this._dataset['pricelist'][k][pr_fk].title = pr_item.title; - } - } else { - if (!(k in this._dataset['pricelist'])) { - this._dataset['pricelist'][k] = []; - } - this._dataset['pricelist'][k].push({ - 'room': pr_item.room, - 'days': pr_item.days, - 'title': pr_item.title - }); - } - } - } - - if (!calendar) { - for (var calendar of this._calendars) { - calendar.setPricelist(this._dataset['pricelist']); - } - } else { - calendar.setPricelist(this._dataset['pricelist']); - } - }, - - merge_restrictions: function(restrictions, calendar) { - var room_type_ids = Object.keys(restrictions); - for (var vid of room_type_ids) { - if (vid in this._dataset['restrictions']) { - this._dataset['restrictions'][vid] = _.extend(this._dataset['restrictions'][vid], restrictions[vid]); - } - else { - this._dataset['restrictions'][vid] = restrictions[vid]; - } - } - - if (!calendar) { - for (var calendar of this._calendars) { - calendar.setRestrictions(this._dataset['restrictions']); - } - } else { - calendar.setRestrictions(this._dataset['restrictions']); - } - }, - - merge_reservations: function(reservations, calendar) { - for (var r of reservations) { - var rindex = _.findKey(this._dataset['reservations'], {'id': r.id}); - if (rindex) { - this._dataset['reservations'][rindex] = r; - } else { - this._dataset['reservations'].push(r); - } - } - - if (!calendar) { - for (var calendar of this._calendars) { - calendar.addReservations(reservations); - } - } else { - calendar.addReservations(reservations); - } - }, - - merge_days_tooltips: function(new_tooltips) { - for (var nt of new_tooltips) { - var fnt = _.find(this._days_tooltips, function(item) { return item['id'] === nt['id']}); - if (fnt) { - fnt = nt; - } else { - this._days_tooltips.push(nt); - } - } - }, - - create_calendar: function(calendar_record) { - var [$tab, $panel] = this._create_tab(calendar_record['name'], `calendar-pane-${calendar_record['name']}`); - var calendar = new HotelCalendar( - $panel[0], - this._options, - this._dataset['pricelist'], - this._dataset['restrictions'], - this._base); - this._assign_calendar_events(calendar); - this._assign_extra_info(calendar); - calendar.setReservations(this._dataset['reservations']); - this._calendars.push(calendar); - this._calendar_records.push(calendar_record); - return this._calendars.length-1; - }, - - on_calendar: function(event_name, callback) { - this._events[event_name] = callback; - }, - - - _create_tab: function(name, id, options) { - var self = this; - var sanitized_id = this._sanitizeId(id); - - var $tab = $('', _.extend({ - id: this._sanitizeId(name), - href: `#${sanitized_id}`, - text: name, - role: 'tab', - }, options)).data('tabindex', this._tabs.length).appendTo($('
  • ').prependTo(this.$tabs)); - $tab.on('shown.bs.tab', function(ev){ - self._active_index = $(ev.target).data('tabindex'); - self.recalculate_reservation_positions(); - if (ev.relatedTarget) { - var prev_index = $(ev.relatedTarget).data('tabindex'); - if (prev_index) { - self.get_calendar(prev_index).cancelSwap(); - } - } - - self.$el.trigger('tab_changed', [self._active_index]); - }); - $tab[0].dataset.toggle = 'tab'; - var $panel = $('
    ', { - id: sanitized_id, - class: 'tab-pane', - role: 'tabpanel' - }).appendTo(this.$tabs_content); - - this._tabs.push([$tab, $panel]); - return this._tabs[this._tabs.length-1]; - }, - - _create_tabs_panel: function() { - var self = this; - this.$el.empty(); - this.$tabs = $('
      ', { - class: 'nav nav-tabs', - id: 'multicalendar_tabs' - }).appendTo(this.$el); - this.$tabs_content = $('
      ', { - class: 'tab-content', - id: 'multicalendar_panels' - }).appendTo(this.$el); - - // '+' Tab - var [$tab, $panel] = this._create_tab('+', 'default', {class: 'multi-calendar-tab-plus'}); - $tab.on('shown.bs.tab', function(ev){ - ev.preventDefault(); - var calendar_record = { - id: false, - name: `Calendar #${self._calendars.length}`, - segmentation_ids: [], - location_ids: [], - amenity_ids: [], - room_type_ids: [] - }; - var new_calendar_id = self.create_calendar(calendar_record); - self.set_active_calendar(new_calendar_id); - }); - $('

      ', { - class: 'warn-message', - text: "NO CALENDAR DEFINED!", - }).appendTo($panel); - }, - - _assign_calendar_events: function(calendar) { - for (var event_name in this._events) { - calendar.addEventListener(event_name, this._events[event_name]); - } - }, - - _assign_extra_info: function(calendar) { - var self = this; - $(calendar.etable).find('.hcal-cell-room-type-group-item.btn-hcal-left').on("mouseenter", function(){ - var $this = $(this); - var room = calendar.getRoom($this.parent().data("hcalRoomObjId")); - if (room.overbooking) { - $this.tooltip({ - animation: true, - html: true, - placement: 'right', - title: QWeb.render('HotelCalendar.TooltipRoomOverbooking', {'name': room.number}) - }).tooltip('show'); - } else { - var qdict = { - 'room_type_name': room.getUserData('room_type_name'), - 'name': room.number - }; - $this.tooltip({ - animation: true, - html: true, - placement: 'right', - title: QWeb.render('HotelCalendar.TooltipRoom', qdict) - }).tooltip('show'); - } - }); - - $(calendar.etableHeader).find('.hcal-cell-header-day').each(function(index, elm){ - var $elm = $(elm); - var cdate = HotelCalendar.toMoment($elm.data('hcalDate'), HotelConstants.L10N_DATE_MOMENT_FORMAT); - var data = _.filter(self._days_tooltips, function(item) { - var ndate = HotelCalendar.toMoment(item['date'], HotelConstants.ODOO_DATE_MOMENT_FORMAT); - return ndate.isSame(cdate, 'd'); - }); - if (data.length > 0) { - $elm.addClass('hcal-event-day'); - $elm.append(""); - $elm.on("mouseenter", function(data){ - var $this = $(this); - if (data.length > 0) { - var qdict = { - 'date': $this.data('hcalDate'), - 'events': _.map(data, function(item){ - return { - 'name': item['name'], - 'date': item['date'], - 'location': item['location'] - }; - }) - }; - $this.attr('title', ''); - $this.tooltip({ - animation: true, - html: true, - placement: 'bottom', - title: QWeb.render('HotelCalendar.TooltipEvent', qdict) - }).tooltip('show'); - } - }.bind(elm, data)); - } - }); - }, - - _sanitizeId: function(/*String*/str) { - return str.replace(/[^a-zA-Z0-9\-_]/g, '_'); - }, - }); - - return MultiCalendar; -}); diff --git a/hotel_calendar/static/src/lib/bootbox.js b/hotel_calendar/static/src/lib/bootbox.js deleted file mode 100644 index 3e8312a85..000000000 --- a/hotel_calendar/static/src/lib/bootbox.js +++ /dev/null @@ -1,985 +0,0 @@ -/** - * bootbox.js [v4.4.0] - * - * http://bootboxjs.com/license.txt - */ - -// @see https://github.com/makeusabrew/bootbox/issues/180 -// @see https://github.com/makeusabrew/bootbox/issues/186 -(function (root, factory) { - - "use strict"; - if (typeof define === "function" && define.amd) { - // AMD. Register as an anonymous module. - define(["jquery"], factory); - } else if (typeof exports === "object") { - // Node. Does not work with strict CommonJS, but - // only CommonJS-like environments that support module.exports, - // like Node. - module.exports = factory(require("jquery")); - } else { - // Browser globals (root is window) - root.bootbox = factory(root.jQuery); - } - -}(this, function init($, undefined) { - - "use strict"; - - // the base DOM structure needed to create a modal - var templates = { - dialog: - "

      ", - header: - "", - footer: - "", - closeButton: - "", - form: - "
      ", - inputs: { - text: - "", - textarea: - "", - email: - "", - select: - "", - checkbox: - "
      ", - date: - "", - time: - "", - number: - "", - password: - "" - } - }; - - var defaults = { - // default language - locale: "en", - // show backdrop or not. Default to static so user has to interact with dialog - backdrop: "static", - // animate the modal in/out - animate: true, - // additional class string applied to the top level dialog - className: null, - // whether or not to include a close button - closeButton: true, - // show the dialog immediately by default - show: true, - // dialog container - container: "body" - }; - - // our public object; augmented after our private API - var exports = {}; - - /** - * @private - */ - function _t(key) { - var locale = locales[defaults.locale]; - return locale ? locale[key] : locales.en[key]; - } - - function processCallback(e, dialog, callback) { - e.stopPropagation(); - e.preventDefault(); - - // by default we assume a callback will get rid of the dialog, - // although it is given the opportunity to override this - - // so, if the callback can be invoked and it *explicitly returns false* - // then we'll set a flag to keep the dialog active... - var preserveDialog = $.isFunction(callback) && callback.call(dialog, e) === false; - - // ... otherwise we'll bin it - if (!preserveDialog) { - dialog.modal("hide"); - } - } - - function getKeyLength(obj) { - // @TODO defer to Object.keys(x).length if available? - var k, t = 0; - for (k in obj) { - t ++; - } - return t; - } - - function each(collection, iterator) { - var index = 0; - $.each(collection, function(key, value) { - iterator(key, value, index++); - }); - } - - function sanitize(options) { - var buttons; - var total; - - if (typeof options !== "object") { - throw new Error("Please supply an object of options"); - } - - if (!options.message) { - throw new Error("Please specify a message"); - } - - // make sure any supplied options take precedence over defaults - options = $.extend({}, defaults, options); - - if (!options.buttons) { - options.buttons = {}; - } - - buttons = options.buttons; - - total = getKeyLength(buttons); - - each(buttons, function(key, button, index) { - - if ($.isFunction(button)) { - // short form, assume value is our callback. Since button - // isn't an object it isn't a reference either so re-assign it - button = buttons[key] = { - callback: button - }; - } - - // before any further checks make sure by now button is the correct type - if ($.type(button) !== "object") { - throw new Error("button with key " + key + " must be an object"); - } - - if (!button.label) { - // the lack of an explicit label means we'll assume the key is good enough - button.label = key; - } - - if (!button.className) { - if (total <= 2 && index === total-1) { - // always add a primary to the main option in a two-button dialog - button.className = "btn-primary"; - } else { - button.className = "btn-default"; - } - } - }); - - return options; - } - - /** - * map a flexible set of arguments into a single returned object - * if args.length is already one just return it, otherwise - * use the properties argument to map the unnamed args to - * object properties - * so in the latter case: - * mapArguments(["foo", $.noop], ["message", "callback"]) - * -> { message: "foo", callback: $.noop } - */ - function mapArguments(args, properties) { - var argn = args.length; - var options = {}; - - if (argn < 1 || argn > 2) { - throw new Error("Invalid argument length"); - } - - if (argn === 2 || typeof args[0] === "string") { - options[properties[0]] = args[0]; - options[properties[1]] = args[1]; - } else { - options = args[0]; - } - - return options; - } - - /** - * merge a set of default dialog options with user supplied arguments - */ - function mergeArguments(defaults, args, properties) { - return $.extend( - // deep merge - true, - // ensure the target is an empty, unreferenced object - {}, - // the base options object for this type of dialog (often just buttons) - defaults, - // args could be an object or array; if it's an array properties will - // map it to a proper options object - mapArguments( - args, - properties - ) - ); - } - - /** - * this entry-level method makes heavy use of composition to take a simple - * range of inputs and return valid options suitable for passing to bootbox.dialog - */ - function mergeDialogOptions(className, labels, properties, args) { - // build up a base set of dialog properties - var baseOptions = { - className: "bootbox-" + className, - buttons: createLabels.apply(null, labels) - }; - - // ensure the buttons properties generated, *after* merging - // with user args are still valid against the supplied labels - return validateButtons( - // merge the generated base properties with user supplied arguments - mergeArguments( - baseOptions, - args, - // if args.length > 1, properties specify how each arg maps to an object key - properties - ), - labels - ); - } - - /** - * from a given list of arguments return a suitable object of button labels - * all this does is normalise the given labels and translate them where possible - * e.g. "ok", "confirm" -> { ok: "OK, cancel: "Annuleren" } - */ - function createLabels() { - var buttons = {}; - - for (var i = 0, j = arguments.length; i < j; i++) { - var argument = arguments[i]; - var key = argument.toLowerCase(); - var value = argument.toUpperCase(); - - buttons[key] = { - label: _t(value) - }; - } - - return buttons; - } - - function validateButtons(options, buttons) { - var allowedButtons = {}; - each(buttons, function(key, value) { - allowedButtons[value] = true; - }); - - each(options.buttons, function(key) { - if (allowedButtons[key] === undefined) { - throw new Error("button key " + key + " is not allowed (options are " + buttons.join("\n") + ")"); - } - }); - - return options; - } - - exports.alert = function() { - var options; - - options = mergeDialogOptions("alert", ["ok"], ["message", "callback"], arguments); - - if (options.callback && !$.isFunction(options.callback)) { - throw new Error("alert requires callback property to be a function when provided"); - } - - /** - * overrides - */ - options.buttons.ok.callback = options.onEscape = function() { - if ($.isFunction(options.callback)) { - return options.callback.call(this); - } - return true; - }; - - return exports.dialog(options); - }; - - exports.confirm = function() { - var options; - - options = mergeDialogOptions("confirm", ["cancel", "confirm"], ["message", "callback"], arguments); - - /** - * overrides; undo anything the user tried to set they shouldn't have - */ - options.buttons.cancel.callback = options.onEscape = function() { - return options.callback.call(this, false); - }; - - options.buttons.confirm.callback = function() { - return options.callback.call(this, true); - }; - - // confirm specific validation - if (!$.isFunction(options.callback)) { - throw new Error("confirm requires a callback"); - } - - return exports.dialog(options); - }; - - exports.prompt = function() { - var options; - var defaults; - var dialog; - var form; - var input; - var shouldShow; - var inputOptions; - - // we have to create our form first otherwise - // its value is undefined when gearing up our options - // @TODO this could be solved by allowing message to - // be a function instead... - form = $(templates.form); - - // prompt defaults are more complex than others in that - // users can override more defaults - // @TODO I don't like that prompt has to do a lot of heavy - // lifting which mergeDialogOptions can *almost* support already - // just because of 'value' and 'inputType' - can we refactor? - defaults = { - className: "bootbox-prompt", - buttons: createLabels("cancel", "confirm"), - value: "", - inputType: "text" - }; - - options = validateButtons( - mergeArguments(defaults, arguments, ["title", "callback"]), - ["cancel", "confirm"] - ); - - // capture the user's show value; we always set this to false before - // spawning the dialog to give us a chance to attach some handlers to - // it, but we need to make sure we respect a preference not to show it - shouldShow = (options.show === undefined) ? true : options.show; - - /** - * overrides; undo anything the user tried to set they shouldn't have - */ - options.message = form; - - options.buttons.cancel.callback = options.onEscape = function() { - return options.callback.call(this, null); - }; - - options.buttons.confirm.callback = function() { - var value; - - switch (options.inputType) { - case "text": - case "textarea": - case "email": - case "select": - case "date": - case "time": - case "number": - case "password": - value = input.val(); - break; - - case "checkbox": - var checkedItems = input.find("input:checked"); - - // we assume that checkboxes are always multiple, - // hence we default to an empty array - value = []; - - each(checkedItems, function(_, item) { - value.push($(item).val()); - }); - break; - } - - return options.callback.call(this, value); - }; - - options.show = false; - - // prompt specific validation - if (!options.title) { - throw new Error("prompt requires a title"); - } - - if (!$.isFunction(options.callback)) { - throw new Error("prompt requires a callback"); - } - - if (!templates.inputs[options.inputType]) { - throw new Error("invalid prompt type"); - } - - // create the input based on the supplied type - input = $(templates.inputs[options.inputType]); - - switch (options.inputType) { - case "text": - case "textarea": - case "email": - case "date": - case "time": - case "number": - case "password": - input.val(options.value); - break; - - case "select": - var groups = {}; - inputOptions = options.inputOptions || []; - - if (!$.isArray(inputOptions)) { - throw new Error("Please pass an array of input options"); - } - - if (!inputOptions.length) { - throw new Error("prompt with select requires options"); - } - - each(inputOptions, function(_, option) { - - // assume the element to attach to is the input... - var elem = input; - - if (option.value === undefined || option.text === undefined) { - throw new Error("given options in wrong format"); - } - - // ... but override that element if this option sits in a group - - if (option.group) { - // initialise group if necessary - if (!groups[option.group]) { - groups[option.group] = $("").attr("label", option.group); - } - - elem = groups[option.group]; - } - - elem.append(""); - }); - - each(groups, function(_, group) { - input.append(group); - }); - - // safe to set a select's value as per a normal input - input.val(options.value); - break; - - case "checkbox": - var values = $.isArray(options.value) ? options.value : [options.value]; - inputOptions = options.inputOptions || []; - - if (!inputOptions.length) { - throw new Error("prompt with checkbox requires options"); - } - - if (!inputOptions[0].value || !inputOptions[0].text) { - throw new Error("given options in wrong format"); - } - - // checkboxes have to nest within a containing element, so - // they break the rules a bit and we end up re-assigning - // our 'input' element to this container instead - input = $("
      "); - - each(inputOptions, function(_, option) { - var checkbox = $(templates.inputs[options.inputType]); - - checkbox.find("input").attr("value", option.value); - checkbox.find("label").append(option.text); - - // we've ensured values is an array so we can always iterate over it - each(values, function(_, value) { - if (value === option.value) { - checkbox.find("input").prop("checked", true); - } - }); - - input.append(checkbox); - }); - break; - } - - // @TODO provide an attributes option instead - // and simply map that as keys: vals - if (options.placeholder) { - input.attr("placeholder", options.placeholder); - } - - if (options.pattern) { - input.attr("pattern", options.pattern); - } - - if (options.maxlength) { - input.attr("maxlength", options.maxlength); - } - - // now place it in our form - form.append(input); - - form.on("submit", function(e) { - e.preventDefault(); - // Fix for SammyJS (or similar JS routing library) hijacking the form post. - e.stopPropagation(); - // @TODO can we actually click *the* button object instead? - // e.g. buttons.confirm.click() or similar - dialog.find(".btn-primary").click(); - }); - - dialog = exports.dialog(options); - - // clear the existing handler focusing the submit button... - dialog.off("shown.bs.modal"); - - // ...and replace it with one focusing our input, if possible - dialog.on("shown.bs.modal", function() { - // need the closure here since input isn't - // an object otherwise - input.focus(); - }); - - if (shouldShow === true) { - dialog.modal("show"); - } - - return dialog; - }; - - exports.dialog = function(options) { - options = sanitize(options); - - var dialog = $(templates.dialog); - var innerDialog = dialog.find(".modal-dialog"); - var body = dialog.find(".modal-body"); - var buttons = options.buttons; - var buttonStr = ""; - var callbacks = { - onEscape: options.onEscape - }; - - if ($.fn.modal === undefined) { - throw new Error( - "$.fn.modal is not defined; please double check you have included " + - "the Bootstrap JavaScript library. See http://getbootstrap.com/javascript/ " + - "for more details." - ); - } - - each(buttons, function(key, button) { - - // @TODO I don't like this string appending to itself; bit dirty. Needs reworking - // can we just build up button elements instead? slower but neater. Then button - // can just become a template too - buttonStr += ""; - callbacks[key] = button.callback; - }); - - body.find(".bootbox-body").html(options.message); - - if (options.animate === true) { - dialog.addClass("fade"); - } - - if (options.className) { - dialog.addClass(options.className); - } - - if (options.size === "large") { - innerDialog.addClass("modal-lg"); - } else if (options.size === "small") { - innerDialog.addClass("modal-sm"); - } - - if (options.title) { - body.before(templates.header); - } - - if (options.closeButton) { - var closeButton = $(templates.closeButton); - - if (options.title) { - dialog.find(".modal-header").prepend(closeButton); - } else { - closeButton.css("margin-top", "-10px").prependTo(body); - } - } - - if (options.title) { - dialog.find(".modal-title").html(options.title); - } - - if (buttonStr.length) { - body.after(templates.footer); - dialog.find(".modal-footer").html(buttonStr); - } - - - /** - * Bootstrap event listeners; used handle extra - * setup & teardown required after the underlying - * modal has performed certain actions - */ - - dialog.on("hidden.bs.modal", function(e) { - // ensure we don't accidentally intercept hidden events triggered - // by children of the current dialog. We shouldn't anymore now BS - // namespaces its events; but still worth doing - if (e.target === this) { - dialog.remove(); - } - }); - - /* - dialog.on("show.bs.modal", function() { - // sadly this doesn't work; show is called *just* before - // the backdrop is added so we'd need a setTimeout hack or - // otherwise... leaving in as would be nice - if (options.backdrop) { - dialog.next(".modal-backdrop").addClass("bootbox-backdrop"); - } - }); - */ - - dialog.on("shown.bs.modal", function() { - dialog.find(".btn-primary:first").focus(); - }); - - /** - * Bootbox event listeners; experimental and may not last - * just an attempt to decouple some behaviours from their - * respective triggers - */ - - if (options.backdrop !== "static") { - // A boolean true/false according to the Bootstrap docs - // should show a dialog the user can dismiss by clicking on - // the background. - // We always only ever pass static/false to the actual - // $.modal function because with `true` we can't trap - // this event (the .modal-backdrop swallows it) - // However, we still want to sort of respect true - // and invoke the escape mechanism instead - dialog.on("click.dismiss.bs.modal", function(e) { - // @NOTE: the target varies in >= 3.3.x releases since the modal backdrop - // moved *inside* the outer dialog rather than *alongside* it - if (dialog.children(".modal-backdrop").length) { - e.currentTarget = dialog.children(".modal-backdrop").get(0); - } - - if (e.target !== e.currentTarget) { - return; - } - - dialog.trigger("escape.close.bb"); - }); - } - - dialog.on("escape.close.bb", function(e) { - if (callbacks.onEscape) { - processCallback(e, dialog, callbacks.onEscape); - } - }); - - /** - * Standard jQuery event listeners; used to handle user - * interaction with our dialog - */ - - dialog.on("click", ".modal-footer button", function(e) { - var callbackKey = $(this).data("bb-handler"); - - processCallback(e, dialog, callbacks[callbackKey]); - }); - - dialog.on("click", ".bootbox-close-button", function(e) { - // onEscape might be falsy but that's fine; the fact is - // if the user has managed to click the close button we - // have to close the dialog, callback or not - processCallback(e, dialog, callbacks.onEscape); - }); - - dialog.on("keyup", function(e) { - if (e.which === 27) { - dialog.trigger("escape.close.bb"); - } - }); - - // the remainder of this method simply deals with adding our - // dialogent to the DOM, augmenting it with Bootstrap's modal - // functionality and then giving the resulting object back - // to our caller - - $(options.container).append(dialog); - - dialog.modal({ - backdrop: options.backdrop ? "static": false, - keyboard: false, - show: false - }); - - if (options.show) { - dialog.modal("show"); - } - - // @TODO should we return the raw element here or should - // we wrap it in an object on which we can expose some neater - // methods, e.g. var d = bootbox.alert(); d.hide(); instead - // of d.modal("hide"); - - /* - function BBDialog(elem) { - this.elem = elem; - } - - BBDialog.prototype = { - hide: function() { - return this.elem.modal("hide"); - }, - show: function() { - return this.elem.modal("show"); - } - }; - */ - - return dialog; - - }; - - exports.setDefaults = function() { - var values = {}; - - if (arguments.length === 2) { - // allow passing of single key/value... - values[arguments[0]] = arguments[1]; - } else { - // ... and as an object too - values = arguments[0]; - } - - $.extend(defaults, values); - }; - - exports.hideAll = function() { - $(".bootbox").modal("hide"); - - return exports; - }; - - - /** - * standard locales. Please add more according to ISO 639-1 standard. Multiple language variants are - * unlikely to be required. If this gets too large it can be split out into separate JS files. - */ - var locales = { - bg_BG : { - OK : "Ок", - CANCEL : "Отказ", - CONFIRM : "Потвърждавам" - }, - br : { - OK : "OK", - CANCEL : "Cancelar", - CONFIRM : "Sim" - }, - cs : { - OK : "OK", - CANCEL : "Zrušit", - CONFIRM : "Potvrdit" - }, - da : { - OK : "OK", - CANCEL : "Annuller", - CONFIRM : "Accepter" - }, - de : { - OK : "OK", - CANCEL : "Abbrechen", - CONFIRM : "Akzeptieren" - }, - el : { - OK : "Εντάξει", - CANCEL : "Ακύρωση", - CONFIRM : "Επιβεβαίωση" - }, - en : { - OK : "OK", - CANCEL : "Cancel", - CONFIRM : "OK" - }, - es : { - OK : "OK", - CANCEL : "Cancelar", - CONFIRM : "Aceptar" - }, - et : { - OK : "OK", - CANCEL : "Katkesta", - CONFIRM : "OK" - }, - fa : { - OK : "قبول", - CANCEL : "لغو", - CONFIRM : "تایید" - }, - fi : { - OK : "OK", - CANCEL : "Peruuta", - CONFIRM : "OK" - }, - fr : { - OK : "OK", - CANCEL : "Annuler", - CONFIRM : "D'accord" - }, - he : { - OK : "אישור", - CANCEL : "ביטול", - CONFIRM : "אישור" - }, - hu : { - OK : "OK", - CANCEL : "Mégsem", - CONFIRM : "Megerősít" - }, - hr : { - OK : "OK", - CANCEL : "Odustani", - CONFIRM : "Potvrdi" - }, - id : { - OK : "OK", - CANCEL : "Batal", - CONFIRM : "OK" - }, - it : { - OK : "OK", - CANCEL : "Annulla", - CONFIRM : "Conferma" - }, - ja : { - OK : "OK", - CANCEL : "キャンセル", - CONFIRM : "確認" - }, - lt : { - OK : "Gerai", - CANCEL : "Atšaukti", - CONFIRM : "Patvirtinti" - }, - lv : { - OK : "Labi", - CANCEL : "Atcelt", - CONFIRM : "Apstiprināt" - }, - nl : { - OK : "OK", - CANCEL : "Annuleren", - CONFIRM : "Accepteren" - }, - no : { - OK : "OK", - CANCEL : "Avbryt", - CONFIRM : "OK" - }, - pl : { - OK : "OK", - CANCEL : "Anuluj", - CONFIRM : "Potwierdź" - }, - pt : { - OK : "OK", - CANCEL : "Cancelar", - CONFIRM : "Confirmar" - }, - ru : { - OK : "OK", - CANCEL : "Отмена", - CONFIRM : "Применить" - }, - sq : { - OK : "OK", - CANCEL : "Anulo", - CONFIRM : "Prano" - }, - sv : { - OK : "OK", - CANCEL : "Avbryt", - CONFIRM : "OK" - }, - th : { - OK : "ตกลง", - CANCEL : "ยกเลิก", - CONFIRM : "ยืนยัน" - }, - tr : { - OK : "Tamam", - CANCEL : "İptal", - CONFIRM : "Onayla" - }, - zh_CN : { - OK : "OK", - CANCEL : "取消", - CONFIRM : "确认" - }, - zh_TW : { - OK : "OK", - CANCEL : "取消", - CONFIRM : "確認" - } - }; - - exports.addLocale = function(name, values) { - $.each(["OK", "CANCEL", "CONFIRM"], function(_, v) { - if (!values[v]) { - throw new Error("Please supply a translation for '" + v + "'"); - } - }); - - locales[name] = { - OK: values.OK, - CANCEL: values.CANCEL, - CONFIRM: values.CONFIRM - }; - - return exports; - }; - - exports.removeLocale = function(name) { - delete locales[name]; - - return exports; - }; - - exports.setLocale = function(name) { - return exports.setDefaults("locale", name); - }; - - exports.init = function(_$) { - return init(_$ || $); - }; - - return exports; -})); diff --git a/hotel_calendar/static/src/lib/hcalendar/css/hcalendar.css b/hotel_calendar/static/src/lib/hcalendar/css/hcalendar.css deleted file mode 100644 index 6543527bf..000000000 --- a/hotel_calendar/static/src/lib/hcalendar/css/hcalendar.css +++ /dev/null @@ -1,537 +0,0 @@ -/* - * Hotel Calendar JS v0.0.1a - 2017-2018 - * GNU Public License - * Aloxa Solucions S.L. - * Alexandre Díaz - */ - - -/** ANIMATIONS **/ -@keyframes cell-invalid { - 0% { - background: #ff0000; - background: repeating-linear-gradient(141deg,#ff0000 10%, #ff0000 40%, rgba(255,0,0,0.51) 30%, rgba(255,0,0,0.51) 50%); - background: -webkit-repeating-linear-gradient(141deg,#ff0000 10%, #ff0000 40%, rgba(255,0,0,0.51) 30%, rgba(255,0,0,0.51) 50%); - background: -moz-repeating-linear-gradient(141deg,#ff0000 10%, #ff0000 40%, rgba(255,0,0,0.51) 30%, rgba(255,0,0,0.51) 50%); - } - 50% { - background: #ff0000; - background: repeating-linear-gradient(141deg,#ff0000 10%, #ff0000 40%, rgba(255,0,0,0.51) 30%, rgba(255,0,0,0.51) 50%); - background: -webkit-repeating-linear-gradient(141deg,#ff0000 10%, #ff0000 35%, rgba(255,0,0,0.51) 30%, rgba(255,0,0,0.51) 50%); - background: -moz-repeating-linear-gradient(141deg,#ff0000 10%, #ff0000 40%, rgba(255,0,0,0.51) 30%, rgba(255,0,0,0.51) 50%); - } - 100% { - background: #ff0000; - background: repeating-linear-gradient(141deg,#ff0000 10%, #ff0000 30%, rgba(255,0,0,0.51) 30%, rgba(255,0,0,0.51) 50%); - background: -webkit-repeating-linear-gradient(141deg,#ff0000 10%, #ff0000 30%, rgba(255,0,0,0.51) 30%, rgba(255,0,0,0.51) 50%); - background: -moz-repeating-linear-gradient(141deg,#ff0000 10%, #ff0000 30%, rgba(255,0,0,0.51) 30%, rgba(255,0,0,0.51) 50%); - } -} - -#hcal_load { - width: 100%; - height: 90vh; - text-align: center; - line-height: 90vh; - color: darkgray; -} -#hcal_load > span { - line-height: 24px; - display: inline-block; - vertical-align: middle; -} - -#cal-pag-prev-plus, #cal-pag-prev, #cal-pag-selector, #cal-pag-next, #cal-pag-next-plus { - min-height: 0px; -} - -.hcalendar-container { - display: flex; - flex-flow: column; -} - -.table-reservations-header { - order: 1; - flex-grow: 1; - overflow-y: scroll; - overflow-x: hidden; -} -.table-reservations { - order: 2; - flex-grow: 2; - overflow-y: scroll; - overflow-x: hidden; - position: relative; - max-height: 57vh; -} -.table-calcs-header { - order: 3; - flex-grow: 1; - overflow-y: scroll; - overflow-x: hidden; -} -.table-calcs { - order: 4; - flex-grow: 2; - overflow-y: scroll; - overflow-x: hidden; - font-size: 12px; - max-height: 20vh; -} -.table-calcs input, .table-reservations-header input { - border-radius: 0; - border: 1px solid lightgray; - width: 100%; -} -.table-calcs input { - text-align: center; -} - -.btn-hcal { } -.btn-hcal.hcal-cell-current-day { - background-color: #7c7bad66; - color: #654a37; -} -.btn-hcal.hcal-cell-end-week { - background-color: #EDEDED83; -} -.btn-hcal-3d { - border: 1px solid #eaeaea; - border-top-width: 0; - border-bottom-width: 0; - /*border-color: white black black white !important;*/ -} -.btn-hcal-flat { - background-color: white; - border: 1px solid #eaeaea; -} -.btn-hcal-left { - background-color: white; - border: 1px solid #eaeaea; - border-left-width: 0; -} - -.hcal-warn-ob-indicator { - position: absolute; - background-color: red; - color: yellow; - border: 1px solid black; - z-index: 9; - width: 25px; - height: 20px; - text-align: center; -} - -.table-calcs-header .hcal-table, .table-reservations-header .hcal-table { - height: 46px; -} - -.hcal-table, .hcal-table-day { - border-collapse: initial !important; - width: 100%; - table-layout: fixed; -} -.hcal-table > tbody { - max-height: 50vh; - overflow: scroll; -} -.hcal-table td { - text-align: center; - min-width: 100px; - white-space: nowrap; - overflow: hidden; -} -.hcal-table a { - text-decoration: none; -} -.hcal-table tr:hover td:not(.hcal-unused-zone):not(.hcal-cell-highlight):not(.hcal-cell-current-day):not(.hcal-cell-end-week):not(.btn-hcal):not(.hcal-cell-invalid) { - /*background-color: #F6F6F6; - border: 5px solid red;*/ -} - -.hcal-table tr:hover td.hcal-cell-room-type-group-item { - background-color: #7c7bad80; - color: white; -} - -.hcal-restriction-room-day { - background-color: #9b18704d !important; -} - -.hcal-table-day { - height: 100%; - border-collapse: collapse !important; - border: 0 dotted #eaeaea; - border-width: 1px 0; -} -/*.hcal-table-day tr:first-child td{ - border: 1px solid #727272 !important; - border-width: 0 1px 0 0 !important; -} -.hcal-table tr:not(:last-child) .hcal-table-day tr:last-child td { - border-style: solid; - border-color: #727272 !important; - border-bottom-width: 1px !important; -} -.hcal-table-day tr:not(:last-child):not(:first-child) td { - border-width: 0; -}*/ -.hcal-table-day td { - padding: 2px; - height: 3em; - font-size: 7px; - vertical-align: middle; - font-weight: bold; - border: 0.5px solid #eaeaea !important; -} -.hcal-table-day td:hover:not(.hcal-cell-highlight):not(.hcal-cell-invalid) { - background-color: #FCFEE1 !important; -} - -.hcal-cell-current-day { - background-color: #7C7BADA5; -} -.hcal-cell-end-week { - background-color: #EDEDED83; -} - -.hcal-cell-day-selector { - text-align: center; - vertical-align: center; - max-width: 140px; -} -.hcal-cell-day-selector a { - font-size: 22px; - padding: 0 0.5em; -} -.hcal-cell-day-selector span { - cursor: pointer; -} -.hcal-cell-day-selector input { - border: 1px solid lightgray; - width: 120px; - font-size: large; - text-align: center; -} - -.hcal-cell-month { - overflow: hidden; - max-width: 0; - text-align: center !important; - vertical-align: middle; - white-space: nowrap; - font-size: 12px; -} -.hcal-cell-month:nth-child(n+3) { - border-left-width: 2px !important; -} - -.hcal-cell-start-month { - border-left-width: 2px !important; -} - -.hcal-room-type:hover { - cursor:pointer; -} - -.hcal-cell-highlight { - background-color: #F8FD9C; -} - -.hcal-cell-invalid { - background-color: #e58e92; -} - - -.hcal-cell-room-type-group-item-day { - padding: 0 !important; - height: 100%; -} - -.hcal-input-changed { - background-color: rgb(237,110,110); - border: 1px solid gray; -} - -.hcal-cell-room-type-group-item-day-occupied { - /*background-color: #227eaf;*/ -} -.hcal-cell-room-type-group-item-day-occupied[data-hcal-reservation-cell-type=soft-start] { - background-color: #729fcf; - /*border: 2px solid #3465a4;*/ -} - -.hcal-cell-pagination button { - padding: 0.1em; - border-radius: 0; - background-color: initial; -} - -#btn_save_changes.need-save { - color: yellow; - background: orange; -} - -.hcal-hidden { - display: none; -} - -.hcal-reservation-unselect { - opacity: 0.3; - pointer-events: none; -} - -.hcal-reservation { - position: absolute; - text-align: center; - /*background-color: #729fcf;*/ - /*transform: skewX(-25deg);*/ - /*border-radius: 5px;*/ - border: 0 double #3465a4; - color: white; - white-space: nowrap; - overflow: hidden; - z-index: 8; -} -.hcal-reservation:hover { - background-color: #4e97bf; -} -.hcal-reservation span { - position: relative; -} - -.hcal-reservation-splitted { - border-width: 0 6px; - border-style: solid; -} - -.hcal-reservation-invalid { - background-color: #c8543b !important; - border-color: #6c3624 !important; -} -.hcal-reservation-invalid:hover { - background-color: #f5b595 !important; - border-color: #c8543b !important; -} - -.hcal-reservation-foreground { - pointer-events: none; - opacity: 0.9; - color: transparent !important; - background: repeating-linear-gradient( - 45deg, - #606dbc, - #606dbc 10px, - #465298 10px, - #465298 20px - ); -} - -.hcal-reservation-invalid-swap { - pointer-events: none; - opacity: 0.9; - color: transparent !important; - background: repeating-linear-gradient( - 45deg, - #BA6359, - #BA6359 10px, - #dfe066 10px, - #dfe066 20px - ); -} - -.hcal-reservation-invalid-unify { - pointer-events: none; - opacity: 0.9; - color: transparent !important; - background: repeating-linear-gradient( - 45deg, - #BA6359, - #BA6359 10px, - #dfe066 10px, - #dfe066 20px - ); -} - -.hcal-reservation-action { - border: 2px dashed #3465a4; - opacity: 0.9; - pointer-events: none; - z-index:10; -} - -.hcal-reservation-readonly:not(.hcal-unused-zone) { - border: 2px solid #99995b; - color: white !important; - font-weight: bold; - background: #ffee00; - background: repeating-radial-gradient(circle farthest-corner at right center, #ffee00 0%, #2c2c2c 5%, #ffff00 10%, #2c2c2c 20%, #2c2c2c 100%); - background: -webkit-repeating-radial-gradient(circle farthest-corner at right center, #ffee00 0%, #2c2c2c 5%, #ffff00 10%, #2c2c2c 20%, #2c2c2c 100%); - background: -moz-repeating-radial-gradient(circle farthest-corner at right center, #ffee00 0%, #2c2c2c 5%, #ffff00 10%, #2c2c2c 20%, #2c2c2c 100%); -} - -.hcal-reservation-unify-selected { - background-color: #005B96 !important; - border-color: #99bdd5 !important; -} - -.hcal-reservation-swap-in-selected { - background-color: #005B96 !important; - border-color: #99bdd5 !important; -} - -.hcal-reservation-swap-out-selected { - border-color: #005B96 !important; - background-color: #99bdd5 !important; -} - -.hcal-reservation-unify-selected { - background-color: #005B96 !important; - border-color: #99bdd5 !important; -} - -.hcal-reservation-to-divide { - pointer-events: none; -} - -.hcal-reservation-divide-l { - background-color: transparent; - border: 2px dashed black; - cursor: copy; - pointer-events: none; - border-color: black; - border-right-style: solid; - position: absolute; - z-index: 9; -} - -.hcal-reservation-divide-r { - background-color: transparent; - border: 2px dashed black; - cursor: copy; - pointer-events: none; - border-color: black; - border-left-style: solid; - position: absolute; - z-index: 9; -} - -.hcal-row-room-type-group-item { - text-align: center; -} - -tr.hcal-row-room-type-group-overbooking-item td { - background-color: #fccc9f; -} - -tr.hcal-row-room-type-group-cancelled-item td { - background-color: #fcebeb; -} - -.hcal-cell-month-day { - text-align: center !important; -} - -.hcal-cell-room-type-group-day { - text-align: center !important; -} - -.hcal-table-type-group-day { - border-collapse: collapse; - width:100%; -} -.hcal-table-type-group-day td { - border-width: 0; -} -.hcal-table-type-group-day tr:not(:last-child) td { - border-bottom-width: 1px; -} - -.hcal-cell-room-type { - cursor: pointer; -} -td.hcal-cell-room-type { - border-right-width: 2px; -} - -td.hcal-cell-room-type-group-day, td.hcal-cell-room-type { - border-top-width: 2px; -} -td.hcal-cell-room-type-group-day { - padding: 0; -} - -td.hcal-cell-room-type-group-item { - text-align: center !important; - vertical-align: middle; - font-size: smaller; - white-space: nowrap; - text-overflow: ellipsis; -} -td.hcal-cell-room-type-group-item:last-child { - border-right-width: 2px; -} - -.hcal-cell-type-group-day-free { - text-align: center; - font-weight: bold; - padding: 0 !important; -} -.hcal-cell-type-group-day-price { - text-align: center; -} - -td.hcal-cell-header-day { - padding: 0; - vertical-align: middle; - font-size: 10px; -} - -td.hcal-cell-month-day-occupied { - padding: 0; - text-align: center; -} - -.hcal-cell-detail-room-free-type-group-item-day, -.hcal-cell-detail-room-free-total-group-item-day, -.hcal-cell-detail-room-perc-occup-group-item-day, -.hcal-cell-detail-room-price-type-group-item-day, -.hcal-cell-detail-room-min-stay-group-item-day { - border: 1px solid lightgray; -} -.hcal-cell-detail-room-min-stay-group-item-day { - border-color: #307CB0 lightgray lightgray lightgray; - border-width: 2px 1px 1px 1px; -} - -.hcal-cell-detail-room-group-item { - white-space: nowrap; - text-align: right !important; - text-overflow: '...'; -} - -/* FIXME: Workaround for work with other currencies */ -.hcal-cell-detail-room-group-item[data-currency-symbol='€'] { - text-overflow: '... €'; -} - -.hcal-unused-zone { - border-radius: 0px; -} - -.input-price { - width: 100%; - border-style: none !important; - border-radius: 0 !important; - text-align: center; -} - -.noselect { - -webkit-touch-callout: none; /* iOS Safari */ - -webkit-user-select: none; /* Safari */ - -khtml-user-select: none; /* Konqueror HTML */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* Internet Explorer/Edge */ - user-select: none; /* Non-prefixed version, currently - supported by Chrome and Opera */ -} diff --git a/hotel_calendar/static/src/lib/hcalendar/css/hcalendar_management.css b/hotel_calendar/static/src/lib/hcalendar/css/hcalendar_management.css deleted file mode 100644 index 74bfe841d..000000000 --- a/hotel_calendar/static/src/lib/hcalendar/css/hcalendar_management.css +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Hotel Calendar Management JS v0.0.1a - 2017 - * GNU Public License - * Aloxa Solucions S.L. - * Alexandre Díaz - */ - - #pms-search-cal-pag { - text-align: center; - } - -#mpms-search { - padding: 0.5em; -} - -.hcal-management-table, .hcal-management-table-day { - border-collapse: collapse !important; - table-layout: fixed; -} -.hcal-management-table > tbody { - max-height: 50vh; - overflow: scroll; -} -.hcal-management-table > thead > tr > td { - /*text-align: center !important;*/ - width: 110px !important; - vertical-align: center; - min-width: 110px; - height: 1.5em; - min-height: 1.5em; - white-space: nowrap; - /*overflow: hidden;*/ - border: 1px solid black !important; -} -.hcal-management-table > tbody > tr > td { - /*text-align: center !important;*/ - width: 110px !important; - vertical-align: center; - min-width: 110px; - height: 130px; - min-height: 100px; - white-space: nowrap; - /*overflow: hidden;*/ - padding: 5px !important; - border: 1px solid black; -} -.hcal-management-table thead td { - text-align: center !important; -} -.hcal-management-table a { - text-decoration: none; -} -.hcal-management-table tr:hover td:not(.hcal-cell-highlight):not(.hcal-cell-current-day):not(.hcal-cell-end-week):not(.btn-hcal):not(.hcal-cell-invalid) { - background-color: #F6F6F6; -} - -.hcal-management-table tr:hover td.hcal-cell-room-type-group-item { - background-color: #9bcd9b; - color: black; -} - -.hcal-management-table input[type='edit'] { - width: 100%; - font-size: 10px; - text-align: center; -} -.hcal-management-table select { - font-size: 9px; -} - -.hcal-management-table-day { - height: 100%; - border-collapse: collapse !important; -} -.hcal-management-table-day td { - padding: 2px; - height: 2.3em; - font-size: 7px; - vertical-align: middle; - font-weight: bold; - border: 0; -} -.hcal-management-table-day td:hover:not(.hcal-cell-highlight):not(.hcal-cell-invalid) { - background-color: #FCFEE1 !important; -} - -.hcal-management-low > tbody > tr > td, .hcal-management-low > table > tbody > tr > td { - height: 58px !important; -} - -.hcal-management-medium > tbody > tr > td, .hcal-management-medium > table > tbody > tr > td { - height: 80px !important; -} - -.hcal-management-medium tr[name='rest_b'], .hcal-management-medium tr[name='rest_c'], - .hcal-management-low tr[name='rest_a'], .hcal-management-low tr[name='rest_b'], .hcal-management-low tr[name='rest_c'] { - display: none; - visibility: hidden; -} - -.table-room_types { - flex: 1 1 auto; - margin-top: 1.2em; -} - -.table-room_type-data-header { - z-index: 1; -} - -.table-room_type-data-header .hcal-management-table tr td { - background-color: white; -} - -#hcal_management_widget { - overflow: auto; - display: flex; -} - -#hcal-management-container-dd { - overflow: auto; - flex: 1 1 auto; -} - -#btn_save_changes, #btn_massive_changes { - height: 56px; -} -#btn_save_changes i, #btn_massive_changes i { - top: 22%; -} - -.hcal-management-input-changed { - background-color: rgb(237,110,110); - border: 1px solid gray; -} - -.hcal-management-record-changed { - background-color: #FFFF66 !important; -} - -table.hcal-management-table input { - border: 1px solid lightgray; -} - -.hcal-border-radius-right { - border-top-right-radius: 25px; - border-bottom-right-radius: 25px; -} - -.hcal-border-radius-left { - border-top-left-radius: 25px; - border-bottom-left-radius: 25px; -} - -.hcal-management-record-options { - padding: 0.5em; -} - -.filter-title { - overflow: hidden; - text-overflow: ellipsis; -} - -button.hcal-management-input { - font-size: 9px; - border: 1px solid gray; - padding: 0.5em 2em 0.2em 2em; - border-radius: 3px; - margin: 0.2em; - - background: rgba(255,255,255,1); - background: -moz-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(227,227,227,1) 47%, rgba(194,194,194,1) 100%); - background: -webkit-gradient(left top, left bottom, color-stop(0%, rgba(255,255,255,1)), color-stop(47%, rgba(227,227,227,1)), color-stop(100%, rgba(194,194,194,1))); - background: -webkit-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(227,227,227,1) 47%, rgba(194,194,194,1) 100%); - background: -o-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(227,227,227,1) 47%, rgba(194,194,194,1) 100%); - background: -ms-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(227,227,227,1) 47%, rgba(194,194,194,1) 100%); - background: linear-gradient(to bottom, rgba(255,255,255,1) 0%, rgba(227,227,227,1) 47%, rgba(194,194,194,1) 100%); - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#c2c2c2', GradientType=0 ); -} -button.hcal-management-input:hover { - background: rgba(255,255,255,1); - background: -moz-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(246,246,246,1) 47%, rgba(237,237,237,1) 100%); - background: -webkit-gradient(left top, left bottom, color-stop(0%, rgba(255,255,255,1)), color-stop(47%, rgba(246,246,246,1)), color-stop(100%, rgba(237,237,237,1))); - background: -webkit-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(246,246,246,1) 47%, rgba(237,237,237,1) 100%); - background: -o-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(246,246,246,1) 47%, rgba(237,237,237,1) 100%); - background: -ms-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(246,246,246,1) 47%, rgba(237,237,237,1) 100%); - background: linear-gradient(to bottom, rgba(255,255,255,1) 0%, rgba(246,246,246,1) 47%, rgba(237,237,237,1) 100%); - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#ededed', GradientType=0 ); -} -button.hcal-management-input-active { - background: rgba(245,215,93,1) !important; - background: -moz-linear-gradient(top, rgba(245,215,93,1) 0%, rgba(245,165,44,1) 47%, rgba(255,153,0,1) 100%) !important; - background: -webkit-gradient(left top, left bottom, color-stop(0%, rgba(245,215,93,1)), color-stop(47%, rgba(245,165,44,1)), color-stop(100%, rgba(255,153,0,1))) !important; - background: -webkit-linear-gradient(top, rgba(245,215,93,1) 0%, rgba(245,165,44,1) 47%, rgba(255,153,0,1) 100%) !important; - background: -o-linear-gradient(top, rgba(245,215,93,1) 0%, rgba(245,165,44,1) 47%, rgba(255,153,0,1) 100%) !important; - background: -ms-linear-gradient(top, rgba(245,215,93,1) 0%, rgba(245,165,44,1) 47%, rgba(255,153,0,1) 100%) !important; - background: linear-gradient(to bottom, rgba(245,215,93,1) 0%, rgba(245,165,44,1) 47%, rgba(255,153,0,1) 100%) !important; - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f5d75d', endColorstr='#ff9900', GradientType=0 ) !important; -} diff --git a/hotel_calendar/static/src/lib/hcalendar/js/hcalendar.js b/hotel_calendar/static/src/lib/hcalendar/js/hcalendar.js deleted file mode 100644 index c4efbc271..000000000 --- a/hotel_calendar/static/src/lib/hcalendar/js/hcalendar.js +++ /dev/null @@ -1,3126 +0,0 @@ -/* global _, moment */ -'use strict'; -/* - * Hotel Calendar JS - 2017-2018 - * GNU Public License - * Alexandre Díaz - * - * Dependencies: - * - moment - * - underscore - * - awesomeicons - * - bootstrap - * - jquery - */ - -function HotelCalendar(/*String*/querySelector, /*Dictionary*/options, /*List*/pricelist, /*restrictions*/restrictions, /*HTMLObject?*/_base) { - if (window === this) { - return new HotelCalendar(querySelector, options, pricelist, _base); - } - - this.$base = (_base === 'undefined') ? document : _base; - - if (typeof querySelector === 'string') { - this.e = this.$base.querySelector(querySelector); - if (!this.e) { - return false; - } - } else if (typeof querySelector === 'object') { - this.e = querySelector; - } else { - return { - Version: '0.5', - Author: "Alexandre Díaz", - Created: "20/04/2017", - Updated: "19/07/2018" - }; - } - - /** Strings **/ - this._strings = { - 'Save Changes': 'Save Changes' - }; - - /** Options **/ - var now_utc = moment(new Date()).utc(); - this.options = _.extend({ - startDate: now_utc, - days: now_utc.daysInMonth(), - rooms: [], - room_types: [], - room_classes: [], - allowInvalidActions: false, - assistedMovement: false, - endOfWeek: 6, - endOfWeekOffset: 0, - divideRoomsByCapacity: false, - currencySymbol: '€', - showPricelist: false, - showAvailability: false, - showNumRooms: 0, - paginatorStepsMin: 1, - paginatorStepsMax: 15, - showOverbookings: false, - showCancelled: false, - }, options); - - this.options.startDate = this.options.startDate.clone(); - this.options.startDate.subtract('1', 'd'); - this.options.orig_days = this.options.days; - this.options.days = this.parseDays(this.options.days) + 1; - this.options.rooms = _.map(this.options.rooms, function(item){ return item.clone(); }); - // Check correct values - if (this.options.rooms.length > 0 && !(this.options.rooms[0] instanceof HRoom)) { - this.options.rooms = []; - console.warn("[Hotel Calendar][init] Invalid Room definiton!"); - } - - /** Internal Values **/ - this._pricelist = pricelist || []; // Store Prices - this._pricelist_id = -1; // Store Price Plan ID (Because can be edited) - this._restrictions = restrictions || {}; // Store Restrictions - this._reservations = []; // Store Reservations - this._reservationsMap = {}; // Store Reservations Mapped by Room for Search Purposes - this._modeSwap = HotelCalendar.MODE.NONE; // Store Swap Mode - this._selectionMode = HotelCalendar.MODE.NONE; - this._endDate = this.options.startDate.clone().add(this.options.days, 'd'); // Store End Calendar Day - this._tableCreated = false; // Store Flag to Know Calendar Creation - this._cellSelection = {start:false, end:false, current:false}; // Store Info About Selected Cells - this._lazyModeReservationsSelection = false; // Store Info About Timer for Selection Action - this._domains = {}; // Store domains for filter rooms & reservations - this._divideDivs = false; - this._extraRowIndicators = ['EX-', '/#']; - - // Support - var self = this; - this._supportsPassive = false; - try { - var opts = Object.defineProperty({}, 'passive', { - get: function() { - self._supportsPassive = true; - } - }); - window.addEventListener("testPassive", null, opts); - window.removeEventListener("testPassive", null, opts); - } catch (e) {} - - // Calculate Capacities - this._roomCapacityTotal = 0; - this._roomCapacities = {}; - this._roomsMap = _.groupBy(this.options.rooms, 'type'); - var room_types = this.getRoomTypes(); - for (var rt of room_types) { - this._roomsMap[rt] = _.filter(this._roomsMap[rt], {overbooking: false, cancelled: false}); - this._roomCapacities[rt] = _.reduce(this._roomsMap[rt], function(memo, tr){ return memo + (tr.shared?tr.capacity:1); }, 0); - this._roomCapacityTotal += this._roomCapacities[rt]; - } - - /***/ - this._reset_action_reservation(); - if (!this._create()) { - return false; - } - - - /** Main Events **/ - document.addEventListener('mouseup', this.onMainMouseUp.bind(this), false); - document.addEventListener('touchend', this.onMainMouseUp.bind(this), false); - document.addEventListener('keyup', this.onMainKeyUp.bind(this), false); - document.addEventListener('keydown', this.onMainKeyDown.bind(this), false); - window.addEventListener('resize', _.debounce(this.onMainResize.bind(this), 300), false); - - return this; -} - -HotelCalendar.prototype = { - /** PUBLIC MEMBERS **/ - addEventListener: function(/*String*/event, /*Function*/callback) { - this.e.addEventListener(event, callback); - }, - - //==== CALENDAR - setStartDate: function(/*String,MomentObject*/date, /*Int*/days, /*Bool*/fullUpdate, /*Functions*/callback) { - if (moment.isMoment(date)) { - this.options.startDate = date; - } else if (typeof date === 'string'){ - this.options.startDate = HotelCalendar.toMoment(date); - } else { - console.warn("[Hotel Calendar][setStartDate] Invalid date format!"); - return; - } - - this.options.startDate.subtract('1','d'); - if (typeof days !== 'undefined') { - this.options.orig_days = days; - this.options.days = this.parseDays(days) + 1; - } - this._endDate = this.options.startDate.clone().add(this.options.days, 'd'); - - /*this.e.dispatchEvent(new CustomEvent( - 'hcOnChangeDate', - {'detail': {'prevDate':curDate, 'newDate': $this.options.startDate}}));*/ - this._updateView(!fullUpdate, callback); - }, - - getOptions: function(/*String?*/key) { - if (typeof key !== 'undefined') { - return this.options[key]; - } - return this.options; - }, - - parseDays: function(/*Int/String*/days) { - if (days === 'month') { - return moment().daysInMonth(); - } - return +days; - }, - - toggleOverbookingsVisibility: function(/*Bool*/show) { - this.options.showOverbookings = !this.options.showOverbookings; - }, - - toggleCancelledVisibility: function(/*Bool*/show) { - this.options.showCancelled = !this.options.showCancelled; - }, - - setSwapMode: function(/*Int*/mode) { - if (mode !== this._modeSwap) { - this._modeSwap = mode; - if (this._modeSwap === HotelCalendar.MODE.NONE) { - this._dispatchSwapReservations(); - this._reset_action_reservation(); - } else { - this.setSelectionMode(HotelCalendar.MODE.NONE); - } - - this._updateHighlightSwapReservations(); - } - }, - - setSelectionMode: function(/*Int*/mode) { - if (this._modeSwap === HotelCalendar.MODE.NONE) { - this._selectionMode = mode; - if (this._selectionMode === HotelCalendar.ACTION.DIVIDE) { - this.reservationAction.action = HotelCalendar.ACTION.DIVIDE; - for (var reserv of this._reservations) { - reserv._html.classList.add('hcal-reservation-to-divide'); - } - } else if (this._selectionMode === HotelCalendar.ACTION.UNIFY) { - this.reservationAction.action = HotelCalendar.ACTION.UNIFY; - this.reservationAction.toUnify = []; - } else { - for (var reserv of this._reservations) { - reserv._html.classList.remove('hcal-reservation-to-divide'); - } - if (this._divideDivs) { - this._divideDivs[0].remove(); - this._divideDivs[1].remove(); - this._divideDivs = false; - } - - - this._dispatchUnifyReservations(); - this._reset_action_reservation(); - this._updateHighlightUnifyReservations(); - } - - this._dispatchEvent('hcalOnChangeSelectionMode', { - 'newMode': this._selectionMode, - }); - } - }, - - getSelectionMode: function() { - return this._selectionMode; - }, - - getSwapMode: function() { - return this._modeSwap; - }, - - cancelSwap: function() { - if (this._modeSwap !== HotelCalendar.MODE.NONE) { - this._modeSwap = HotelCalendar.MODE.NONE; - this._dispatchEvent('hcalOnCancelSwapReservations'); - this._reset_action_reservation(); - this._updateHighlightSwapReservations(); - } - }, - - _updateOffsets: function() { - this._etableOffset = this.loopedOffsetOptimized(this.etable); - this._eOffset = this.loopedOffsetOptimized(this.e); - this._edivrOffset = this.loopedOffsetOptimized(this.edivr); - }, - - //==== DOMAINS - setDomain: function(/*Int*/section, /*Array*/domain) { - if (this._domains[section] !== domain) { - this._domains[section] = domain; - if (section === HotelCalendar.DOMAIN.RESERVATIONS) { - this._filterReservations(); - } else if (section === HotelCalendar.DOMAIN.ROOMS) { - this._filterRooms(); - } - } - }, - - getDomain: function(/*Int*/section) { - return this._domains[section] || []; - }, - - //==== RESERVATIONS - _filterReservations: function() { - for (var r of this._reservations) { - r._active = this._in_domain(r, this._domains[HotelCalendar.DOMAIN.RESERVATIONS]); - this._updateReservation(r, true); - } - - //_.defer(function(){ this._updateReservationOccupation() }.bind(this)); - }, - - getReservationAction: function() { - return this.reservationAction; - }, - - getReservation: function(/*Int,String*/id) { - return _.find(this._reservations, function(item){ return item.id == id; }); - }, - - // getReservationDiv: function(/*HReservationObject*/reservationObj) { - // var reservDiv = this.e.querySelector(`div.hcal-reservation[data-hcal-reservation-obj-id='${reservationObj.id}']`); - // return reservDiv; - // }, - - setReservations: function(/*List*/reservations) { - for (var reservation of this._reservations) { - this.removeReservation(reservation); - } - - this._reservations = []; - this.addReservations(reservations); - }, - - addReservations: function(/*List*/reservations) { - reservations = reservations || []; - - if (reservations.length > 0 && !(reservations[0] instanceof HReservation)) { - console.warn("[HotelCalendar][addReservations] Invalid Reservation definition!"); - } else { - var isCalendarEmpty = (this._reservations.length>0); - // Merge - var addedReservations = []; - for (var r of reservations) { - var rindex = _.findKey(this._reservations, {'id': r.id}); - if ((!this.options.showOverbookings && r.overbooking) || (!this.options.showCancelled && r.cancelled)) { - if (rindex) { - this.removeReservation(this._reservations[rindex]); - } - continue; - } - - var hasCreatedExtraRows = false; - r = r.clone(); // HOT-FIX: Multi-Calendar Support - r.room = this.getRoom(r.room_id, r.overbooking || r.cancelled, r.id); - // need create a overbooking row? - if (!r.room) { - if (r.overbooking || r.cancelled) { - r.room = this.createExtraRoom(this.getRoom(r.room_id), r.id, { - overbooking: r.overbooking, - cancelled: r.cancelled, - }); - this.createExtraRoomRow(r.room); - hasCreatedExtraRows = true; - } else { - console.warn(`Can't found the room '${r.room_id}' for the reservation '${r.id}' (${r.title})!`); - continue; - } - } - - if (rindex) { - var reserv = this._reservations[rindex]; - r._html = reserv._html; - if ((reserv.overbooking && !r.overbooking) || (reserv.cancelled && !r.cancelled)) { - if (this.getReservationsByRoom(reserv.room).length === 1) { - this.removeExtraRoomRow(reserv); - } - } - this._reservations[rindex] = r; - if (!r.unusedZone) { - this._cleanUnusedZones(r); - } - } else { - this._reservations.push(r); - } - - addedReservations.push(r); - } - - // Create & Render New Reservations - _.defer(function(reservs){ - // Update offsets (New Rooms change positions?) - this._updateOffsets(); - - var unusedZones = this._createUnusedZones(reservs); - // Add Unused Zones - this._reservations = this._reservations.concat(unusedZones); - // Create Map - this._updateReservationsMap(); - - var toAssignEvents = []; - reservs = reservs.concat(unusedZones); - for (var r of reservs) { - r._active = this._in_domain(r, this._domains[HotelCalendar.DOMAIN.RESERVATIONS]); - this._calcReservationCellLimits(r); - if (r._html) { - r._html.innerText = r.title; - } else if (r._limits.isValid()) { - r._html = document.createElement('div'); - r._html.dataset.hcalReservationObjId = r.id; - r._html.classList.add('hcal-reservation'); - r._html.classList.add('noselect'); - r._html.innerText = r.title; - this.edivr.appendChild(r._html); - - if (r.unusedZone) { - r._html.classList.add('hcal-unused-zone'); - } else { - toAssignEvents.push(r._html); - } - } - this._updateReservation(r); - } - - this._assignReservationsEvents(toAssignEvents); - }.bind(this), addedReservations); - - _.defer(function(){ this._updateReservationOccupation(); }.bind(this)); - } - }, - - removeReservation: function(/*HReservationObject*/reserv) { - if (reserv) { - // Remove all related content... - var elms = [reserv._html, this.e.querySelector(`.hcal-warn-ob-indicator[data-hcal-reservation-obj-id='${reserv.id}']`)]; - for (var elm of elms) { - if (elm && elm.parentNode) { - elm.parentNode.removeChild(elm); - } - } - // Remove OB Row - if (reserv.overbooking || reserv.cancelled) { - if (this.getReservationsByRoom(reserv.room).length === 1) { - this.removeExtraRoomRow(reserv); - } - } - // Remove Unused Zones - if (!reserv.unusedZone) { - this._cleanUnusedZones(reserv); - } - - this._reservations = _.reject(this._reservations, {id: reserv.id}); - this._updateReservationsMap(); - } else { - console.warn(`[HotelCalendar][removeReservation] Can't remove '${reserv.id}' reservation!`); - } - }, - - getReservationsByDay: function(/*MomentObject*/day, /*Bool?*/noCheckouts, /*Bool?*/includeUnusedZones, /*Int?*/nroom, /*Int?*/nbed, /*HReservation?*/ignoreThis) { - var inclusivity = noCheckouts?'[)':'[]'; - - if (typeof nroom !== 'undefined') { - return _.filter(this._reservationsMap[nroom], function(item){ - return day.isBetween(item.startDate, item.endDate, 'day', inclusivity) && - (typeof nbed === 'undefined' || item._beds.includes(nbed)) && - ((includeUnusedZones && item.unusedZone) || !item.unusedZone) && - item !== ignoreThis && !item.overbooking && !item.cancelled; - }); - } else { - return _.filter(this._reservations, function(item){ - return day.isBetween(item.startDate, item.endDate, 'day', inclusivity) && - (typeof nbed === 'undefined' || item._beds.includes(nbed)) && - ((includeUnusedZones && item.unusedZone) || !item.unusedZone) && - item !== ignoreThis && !item.overbooking && !item.cancelled; - }); - } - }, - - getReservationsByRoom: function(/*Int,HRoomObject*/room, /*Boolean*/includeUnusedZones) { - if (!(room instanceof HRoom)) { room = this.getRoom(room); } - if (room) { - return _.filter(this._reservationsMap[room.id], function(item){ - return (includeUnusedZones || (!includeUnusedZones && !item.unusedZone)); - }); - } - - return []; - }, - - _updateReservationsMap: function() { - this._reservationsMap = {}; - this._reservations.map(function(current){ - if (!(current.room.id in this._reservationsMap)) { - this._reservationsMap[current.room.id] = []; - } - this._reservationsMap[current.room.id].push(current); - }.bind(this)); - }, - - _calcReservationCellLimits: function(/*HReservationObject*/reservation, /*Int?*/nbed, /*Bool?*/notCheck) { - var limits = new HLimit(); - if (!reservation.startDate || !reservation.endDate || - (!reservation.startDate.isBetween(this.options.startDate, this._endDate, 'day', '[]') && - !reservation.endDate.isBetween(this.options.startDate, this._endDate, 'day', '[]') && - !reservation.startDate.isBefore(this.options.startDate, 'day', '()') && - !reservation.endDate.isAfter(this._endDate, 'day', '()'))) { - return limits; - } - - var notFound; - do { - notFound = false; - - // Num of beds - var bedNum; - if (typeof nbed === 'undefined') { - if (reservation._beds && reservation._beds.length) { - bedNum = reservation._beds[0]; - } else { - bedNum = (reservation.unusedZone)?1:0; - } - } else { - bedNum = nbed; - } - - // Search Initial Cell - if (reservation.startDate.clone().local().isSameOrAfter(this.options.startDate, 'd')) { - reservation._drawModes[0] = 'hard-start'; - limits.left = this.getCell(reservation.startDate.clone().local(), - reservation.room, - bedNum); - } - else { - reservation._drawModes[0] = 'soft-start'; - limits.left = this.getCell(this.options.startDate.clone().local(), - reservation.room, - bedNum); - } - - // More Beds? - var rpersons = (reservation.room.shared || this.options.divideRoomsByCapacity)?reservation.room.capacity:1; - var reservPersons = reservation.getTotalPersons(false); - if ((reservation.room.shared || this.options.divideRoomsByCapacity) && reservPersons > 1 && bedNum+reservPersons <= rpersons) { - bedNum += reservPersons-1; - } - - // Search End Cell - if (reservation.endDate.clone().subtract(1, 'd').local().isSameOrBefore(this._endDate, 'd')) { - reservation._drawModes[1] = 'hard-end'; - limits.right = this.getCell(reservation.endDate.clone().subtract(1, 'd').local(), - reservation.room, - bedNum); - } - else { - reservation._drawModes[1] = 'soft-end'; - limits.right = this.getCell(this._endDate.clone().local(), - reservation.room, - bedNum); - } - - // Exists other reservation in the same place? - if (!notCheck && limits.isValid()) { - var diff_date = this.getDateDiffDays(reservation.startDate, reservation.endDate); - var numBeds = +limits.right.dataset.hcalBedNum - +limits.left.dataset.hcalBedNum; - var ndate = reservation.startDate.clone().local(); - for (var i=0; i= this.options.endOfWeek-this.options.endOfWeekOffset && dd_local.format('e') <= this.options.endOfWeek) { - cell.classList.add('hcal-cell-end-week'); - } - } - - // Update Reservations Position - var bounds = this.loopedOffsetOptimized(row); - var cheight = bounds.height; - var start_index = _.indexOf(this.options.rooms, ex_room) + 1; - for (var i=start_index; i 0) - humantext += `Min. Stay: ${restr[0]}\n`; - if (restr[1] > 0) - humantext += `Min. Stay Arrival: ${restr[1]}\n`; - if (restr[2] > 0) - humantext += `Max. Stay: ${restr[2]}\n`; - if (restr[3] > 0) - humantext += `Max. Stay Arrival: ${restr[3]}\n`; - if (restr[4]) - humantext += `Closed: ${restr[4]}\n`; - if (restr[5]) - humantext += `Closed Arrival: ${restr[5]}\n`; - if (restr[6]) - humantext += `Closed Departure: ${restr[6]}`; - cell.title = humantext; - } - else { - cell.classList.remove('hcal-restriction-room-day'); - cell.title = ''; - } - } - } - } - } - } - } - }, - - //==== DETAIL CALCS - calcDayRoomTypeReservations: function(/*String,MomentObject*/day, /*String*/room_type) { - var day = HotelCalendar.toMoment(day); - if (!day) { return false; } - - var num_rooms = this._roomCapacities[room_type]; - num_rooms -= _.reduce(this.getDayRoomTypeReservations(day, room_type), function(memo, r){ return memo + ((r.room && r.room.shared)?r.getTotalPersons(false):1); }, 0); - return num_rooms; - }, - - calcDayRoomTotalReservations: function(/*String,MomentObject*/day) { - var day = HotelCalendar.toMoment(day); - if (!day) { return false; } - - var num_rooms = this._roomCapacityTotal; - num_rooms -= _.reduce(this.getReservationsByDay(day, true), function(memo, r){ return memo + ((r.room && r.room.shared)?r.getTotalPersons(false):1); }, 0); - return num_rooms; - }, - - - /** PRIVATE MEMBERS **/ - //==== MAIN FUNCTIONS - _reset_action_reservation: function() { - if (this._lazyModeReservationsSelection) { - clearTimeout(this._lazyModeReservationsSelection); - this._lazyModeReservationsSelection = false; - } - - this.reservationAction = { - action: HotelCalendar.ACTION.NONE, - reservation: null, - oldReservationObj: null, - newReservationObj: null, - mousePos: false, - inReservations: [], - outReservations: [], - }; - }, - - get_normalized_rooms_: function() { - var rooms = {}; - if (this.options.rooms) { - var keys = Object.keys(this.options.rooms); - - for (var r of this.options.rooms) { - rooms[r.number] = [r.type, r.capacity]; - } - } - return rooms; - }, - - //==== RENDER FUNCTIONS - _create: function() { - var $this = this; - while (this.e.hasChildNodes()) { - this.e.removeChild(this.e.lastChild); - } - - if (this._tableCreated) { - console.warn("[Hotel Calendar][_create] Already created!"); - return false; - } - - var scrollThrottle = _.throttle(this._updateOBIndicators.bind(this), 100); - - - this.edivcontainer = document.createElement("div"); - this.edivcontainer.classList.add('hcalendar-container'); - - // Reservations Table - this.edivrh = document.createElement("div"); - this.edivrh.classList.add('table-reservations-header'); - this.edivcontainer.appendChild(this.edivrh); - this.etableHeader = document.createElement("table"); - this.etableHeader.classList.add('hcal-table'); - this.etableHeader.classList.add('noselect'); - this.edivrh.appendChild(this.etableHeader); - this.edivr = document.createElement("div"); - this.edivr.classList.add('table-reservations'); - this.edivcontainer.appendChild(this.edivr); - this.etable = document.createElement("table"); - this.etable.classList.add('hcal-table'); - this.etable.classList.add('noselect'); - this.edivr.appendChild(this.etable); - this.edivr.addEventListener("scroll", scrollThrottle, false); - // Detail Calcs Table - this.edivch = document.createElement("div"); - this.edivch.classList.add('table-calcs-header'); - this.edivcontainer.appendChild(this.edivch); - this.edtableHeader = document.createElement("table"); - this.edtableHeader.classList.add('hcal-table'); - this.edtableHeader.classList.add('noselect'); - this.edivch.appendChild(this.edtableHeader); - this.edivc = document.createElement("div"); - this.edivc.classList.add('table-calcs'); - this.edivcontainer.appendChild(this.edivc); - this.edtable = document.createElement("table"); - this.edtable.classList.add('hcal-table'); - this.edtable.classList.add('noselect'); - this.edivc.appendChild(this.edtable); - - var observer = new MutationObserver(function(mutationsList){ - $this._updateOBIndicators(); - }); - observer.observe(this.edivr, { childList: true }); - - this.e.appendChild(this.edivcontainer); - - this._updateView(); - //_.defer(function(self){ self._updateView(); }, this); - this._tableCreated = true; - - return true; - }, - - _generateTableDay: function(/*HTMLObject*/parentCell, /*HRoomObject*/room) { - var $this = this; - var table = document.createElement("table"); - table.classList.add('hcal-table-day'); - table.classList.add('noselect'); - var row = false; - var cell = false; - var num = ((room.shared || this.options.divideRoomsByCapacity)?room.capacity:1); - for (var i=0; i${dd_local.format('D')}`; - cell.setAttribute('title', dd_local.format('dddd')) - var day = +dd_local.format('D'); - if (day == 1) { - cell.classList.add('hcal-cell-start-month'); - cur_month = dd_local.format('MMMM'); - months[cur_month] = {}; - months[cur_month].year = dd_local.format('YYYY'); - months[cur_month].colspan = 0; - } - if (dd_local.isSame(now, 'day')) { - cell.classList.add('hcal-cell-current-day'); - } else if (dd_local.format('e') >= this.options.endOfWeek-this.options.endOfWeekOffset && dd_local.format('e') <= this.options.endOfWeek) { - cell.classList.add('hcal-cell-end-week'); - } - ++months[cur_month].colspan; - } - // Render Months - var month_keys = Object.keys(months); - for (var m of month_keys) { - var cell_month = row_init.insertCell(); - cell_month.setAttribute('colspan', months[m].colspan); - cell_month.innerText = m+' '+months[m].year; - cell_month.classList.add('hcal-cell-month'); - cell_month.classList.add('btn-hcal'); - cell_month.classList.add('btn-hcal-3d'); - } - - /** ROOM LINES **/ - var tbody = document.createElement("tbody"); - this.etable.appendChild(tbody); - for (var itemRoom of this.options.rooms) { - // Room Number - row = tbody.insertRow(); - row.dataset.hcalRoomObjId = itemRoom.id; - row.classList.add('hcal-row-room-type-group-item'); - if ((this.options.showOverbookings && itemRoom.overbooking) || (this.options.showCancelled && itemRoom.cancelled)) { - var reservId = this.parseExtraRoomId(itemRoom.id)[0]; - var cnumber = this.getExtraRoomRealNumber(itemRoom); - row.setAttribute('id', this._sanitizeId(`ROW_${cnumber}_${itemRoom.type}_EXTRA${reservId}`)); - row.classList.add('hcal-row-room-type-group-overbooking-item'); - } else { - row.setAttribute('id', $this._sanitizeId(`ROW_${itemRoom.number}_${itemRoom.type}`)); - } - cell = row.insertCell(); - cell.textContent = itemRoom.number; - cell.classList.add('hcal-cell-room-type-group-item'); - cell.classList.add('btn-hcal'); - cell.classList.add('btn-hcal-left'); - cell.setAttribute('colspan', '3'); - /* - cell = row.insertCell(); - cell.textContent = itemRoom.type; - cell.classList.add('hcal-cell-room-type-group-item'); - cell.classList.add('btn-hcal'); - cell.classList.add('btn-hcal-flat'); - */ - for (var i=0; i<=$this.options.days; i++) { - var dd = $this.options.startDate.clone().local().startOf('day').add(i,'d').utc(); - var dd_local = dd.clone().local(); - cell = row.insertCell(); - cell.setAttribute('id', $this._sanitizeId(`${itemRoom.type}_${itemRoom.number}_${dd_local.format(HotelCalendar.DATE_FORMAT_SHORT_)}`)); - cell.classList.add('hcal-cell-room-type-group-item-day'); - cell.dataset.hcalParentRow = row.getAttribute('id'); - cell.dataset.hcalDate = dd_local.format(HotelCalendar.DATE_FORMAT_SHORT_); - cell.dataset.hcalRoomObjId = itemRoom.id; - // Generate Interactive Table - cell.appendChild($this._generateTableDay(cell, itemRoom)); - //cell.innerHTML = dd.format("DD"); - var day = +dd_local.format("D"); - if (day == 1) { - cell.classList.add('hcal-cell-start-month'); - } - if (dd_local.isSame(now, 'day')) { - cell.classList.add('hcal-cell-current-day'); - } else if (dd_local.format('e') >= this.options.endOfWeek-this.options.endOfWeekOffset && dd_local.format('e') <= this.options.endOfWeek) { - cell.classList.add('hcal-cell-end-week'); - } - } - - itemRoom._html = row; - } - - this._filterRooms(); - this._calcViewHeight(); - }, - - _calcViewHeight: function() { - if (this.options.showNumRooms > 0) { - var rows = this.edivr.querySelectorAll('tr.hcal-row-room-type-group-item'); - var cheight = 0.0; - for (var i=0; i${dd_local.format('D')}`; - cell.setAttribute('title', dd_local.format("dddd")) - var day = +dd_local.format("D"); - if (day == 1) { - cell.classList.add('hcal-cell-start-month'); - } - if (dd_local.isSame(now, 'day')) { - cell.classList.add('hcal-cell-current-day'); - } else if (dd_local.format('e') >= this.options.endOfWeek-this.options.endOfWeekOffset && dd_local.format('e') <= this.options.endOfWeek) { - cell.classList.add('hcal-cell-end-week'); - } - } - - /** DETAIL LINES **/ - var tbody = document.createElement("tbody"); - this.edtable.appendChild(tbody); - if (this.options.showAvailability) { - // Rooms Free Types - if (this.options.rooms) { - var room_types = this.getRoomTypes(); - for (var rt of room_types) { - if (rt || room_types.length > 1) { - row = tbody.insertRow(); - row.setAttribute('id', this._sanitizeId(`ROW_DETAIL_FREE_TYPE_${rt}`)); - row.dataset.hcalRoomType = rt; - row.classList.add('hcal-row-detail-room-free-type-group-item'); - cell = row.insertCell(); - cell.textContent = rt; - cell.classList.add('hcal-cell-detail-room-free-type-group-item'); - cell.classList.add('btn-hcal'); - cell.classList.add('btn-hcal-left'); - cell.setAttribute("colspan", "3"); - for (var i=0; i<=this.options.days; i++) { - var dd = this.options.startDate.clone().local().startOf('day').add(i,'d').utc(); - var dd_local = dd.clone().local(); - cell = row.insertCell(); - cell.setAttribute('id', this._sanitizeId(`CELL_FREE_${rt}_${dd_local.format(HotelCalendar.DATE_FORMAT_SHORT_)}`)); - cell.classList.add('hcal-cell-detail-room-free-type-group-item-day'); - cell.dataset.hcalParentRow = row.getAttribute('id'); - cell.dataset.hcalDate = dd_local.format(HotelCalendar.DATE_FORMAT_SHORT_); - cell.textContent = '0'; - var day = +dd_local.format("D"); - if (day == 1) { - cell.classList.add('hcal-cell-start-month'); - } - if (dd_local.isSame(now, 'day')) { - cell.classList.add('hcal-cell-current-day'); - } else if (dd_local.format('e') >= this.options.endOfWeek-this.options.endOfWeekOffset && dd_local.format('e') <= this.options.endOfWeek) { - cell.classList.add('hcal-cell-end-week'); - } - } - } - } - } - // Total Free - row = tbody.insertRow(); - row.setAttribute('id', "ROW_DETAIL_TOTAL_FREE"); - row.classList.add('hcal-row-detail-room-free-total-group-item'); - cell = row.insertCell(); - cell.textContent = 'FREE TOTAL'; - cell.classList.add('hcal-cell-detail-room-free-total-group-item'); - cell.classList.add('btn-hcal'); - cell.classList.add('btn-hcal-left'); - cell.setAttribute("colspan", "3"); - for (var i=0; i<=this.options.days; i++) { - var dd = this.options.startDate.clone().local().startOf('day').add(i,'d').utc(); - var dd_local = dd.clone().local(); - cell = row.insertCell(); - cell.setAttribute('id', this._sanitizeId(`CELL_DETAIL_TOTAL_FREE_${dd_local.format(HotelCalendar.DATE_FORMAT_SHORT_)}`)); - cell.classList.add('hcal-cell-detail-room-free-total-group-item-day'); - cell.dataset.hcalParentRow = row.getAttribute('id'); - cell.dataset.hcalDate = dd_local.format(HotelCalendar.DATE_FORMAT_SHORT_); - cell.textContent = '0'; - var day = +dd_local.format("D"); - if (day == 1) { - cell.classList.add('hcal-cell-start-month'); - } - if (dd_local.isSame(now, 'day')) { - cell.classList.add('hcal-cell-current-day'); - } else if (dd_local.format('e') >= this.options.endOfWeek-this.options.endOfWeekOffset && dd_local.format('e') <= this.options.endOfWeek) { - cell.classList.add('hcal-cell-end-week'); - } - } - // Percentage Occupied - row = tbody.insertRow(); - row.setAttribute('id', "ROW_DETAIL_PERC_OCCUP"); - row.classList.add('hcal-row-detail-room-perc-occup-group-item'); - cell = row.insertCell(); - cell.textContent = '% OCCUP.'; - cell.classList.add('hcal-cell-detail-room-perc-occup-group-item'); - cell.classList.add('btn-hcal'); - cell.classList.add('btn-hcal-left'); - cell.setAttribute("colspan", "3"); - for (var i=0; i<=this.options.days; i++) { - var dd = this.options.startDate.clone().local().startOf('day').add(i,'d').utc(); - var dd_local = dd.clone().local(); - cell = row.insertCell(); - cell.setAttribute('id', this._sanitizeId(`CELL_DETAIL_PERC_OCCUP_${dd_local.format(HotelCalendar.DATE_FORMAT_SHORT_)}`)); - cell.classList.add('hcal-cell-detail-room-perc-occup-group-item-day'); - cell.dataset.hcalParentRow = row.getAttribute('id'); - cell.dataset.hcalDate = dd_local.format(HotelCalendar.DATE_FORMAT_SHORT_); - cell.textContent = '0'; - var day = +dd_local.format("D"); - if (day == 1) { - cell.classList.add('hcal-cell-start-month'); - } - if (dd_local.isSame(now, 'day')) { - cell.classList.add('hcal-cell-current-day'); - } else if (dd_local.format('e') >= this.options.endOfWeek-this.options.endOfWeekOffset && dd_local.format('e') <= this.options.endOfWeek) { - cell.classList.add('hcal-cell-end-week'); - } - } - } - // Rooms Pricelist - this._pricelist_id = _.keys(this._pricelist)[0]; - if (this.options.showPricelist && this._pricelist) { - //var pricelists_keys = _.keys(this._pricelist) - //for (var key of pricelists_keys) { - var key = this._pricelist_id; - var pricelist = this._pricelist[key]; - for (var listitem of pricelist) { - row = tbody.insertRow(); - row.setAttribute('id', this._sanitizeId(`ROW_DETAIL_PRICE_ROOM_${key}_${listitem.room}`)); - row.dataset.hcalPricelist = key; - row.dataset.hcalRoomTypeId = listitem.room - row.classList.add('hcal-row-detail-room-price-group-item'); - cell = row.insertCell(); - var span = document.createElement('span'); - cell.title = cell.textContent = listitem.title + ' ' + this.options.currencySymbol; - cell.classList.add('hcal-cell-detail-room-group-item', 'btn-hcal', 'btn-hcal-left'); - cell.dataset.currencySymbol = this.options.currencySymbol; - cell.setAttribute("colspan", "3"); - for (var i=0; i<=$this.options.days; i++) { - var dd = this.options.startDate.clone().local().startOf('day').add(i,'d').utc(); - var dd_local = dd.clone().local(); - cell = row.insertCell(); - cell.setAttribute('id', this._sanitizeId(`CELL_PRICE_${key}_${listitem.room}_${dd_local.format(HotelCalendar.DATE_FORMAT_SHORT_)}`)); - cell.classList.add('hcal-cell-detail-room-price-group-item-day'); - cell.dataset.hcalParentRow = row.getAttribute('id'); - cell.dataset.hcalDate = dd_local.format(HotelCalendar.DATE_FORMAT_SHORT_); - var day = +dd_local.format("D"); - if (day == 1) { - cell.classList.add('hcal-cell-start-month'); - } - if (dd_local.isSame(now, 'day')) { - cell.classList.add('hcal-cell-current-day'); - } else if (dd_local.format('e') >= this.options.endOfWeek-this.options.endOfWeekOffset && dd_local.format('e') <= this.options.endOfWeek) { - cell.classList.add('hcal-cell-end-week'); - } - - var input = document.createElement('input'); - input.setAttribute('id', this._sanitizeId(`INPUT_PRICE_${key}_${listitem.room}_${dd_local.format(HotelCalendar.DATE_FORMAT_SHORT_)}`)); - input.setAttribute('type', 'edit'); - input.setAttribute('title', 'Price'); - input.setAttribute('name', 'room_type_price_day'); - input.dataset.hcalParentCell = cell.getAttribute('id'); - var dd_fmrt = dd_local.format(HotelCalendar.DATE_FORMAT_SHORT_); - input.dataset.orgValue = input.value = _.has(listitem['days'], dd_fmrt)?Number(listitem['days'][dd_fmrt]).toLocaleString():'...'; - input.addEventListener('change', function(ev){ $this._onInputChange(ev, this); }, false); - cell.appendChild(input); - } - } - //} - } - }, - - //==== UPDATE FUNCTIONS - _updateView: function(/*Bool*/notData, /*function*/callback) { - this._createTableReservationDays(); - if (typeof callback !== 'undefined') { - callback(); - } - this._updateCellSelection(); - this._createTableDetailDays(); - - _.defer(function(self){ - self._updateOffsets(); - self._updateReservations(true); - if (!notData) { - _.defer(function(self){ - self._updateRestrictions(); - self._updatePriceList(); - self._updateReservationOccupation(); - }, self); - } - }, this); - // if (!notData) { - // _.defer(function(self){ - // self._createTableDetailDays(); - // self._updateRestrictions(); - // self._updatePriceList(); - // self._updateReservationOccupation(); - // }, this); - // } - }, - - _updateOBIndicators: function() { - var mainBounds = this._edivrOffset; - for (var reserv of this._reservations) { - if (reserv.overbooking && reserv._html) { - var eOffset = this._eOffset; - var bounds = this.loopedOffsetOptimized(reserv._html); - if (bounds.top > mainBounds.height) { - var warnDiv = this.e.querySelector(`div.hcal-warn-ob-indicator[data-hcal-reservation-obj-id='${reserv.id}']`); - if (!warnDiv) { - var warnDiv = document.createElement("DIV"); - warnDiv.innerHTML = ""; - warnDiv.classList.add('hcal-warn-ob-indicator'); - warnDiv.style.borderTopLeftRadius = warnDiv.style.borderTopRightRadius = "50px"; - warnDiv.dataset.hcalReservationObjId = reserv.id; - this.edivcontainer.appendChild(warnDiv); - var warnComputedStyle = window.getComputedStyle(warnDiv, null); - warnDiv.style.top = `${mainBounds.height - eOffset.top - parseInt(warnComputedStyle.getPropertyValue("height"), 10)}px`; - warnDiv.style.left = `${(bounds.left + (bounds.right - bounds.left)/2.0 - parseInt(warnComputedStyle.getPropertyValue("width"), 10)/2.0) - mainBounds.left}px`; - } - } else if (bounds.height < mainBounds.top) { - var warnDiv = this.e.querySelector(`div.hcal-warn-ob-indicator[data-hcal-reservation-obj-id='${reserv.id}']`); - if (!warnDiv) { - var warnDiv = document.createElement("DIV"); - warnDiv.innerHTML = ""; - warnDiv.classList.add('hcal-warn-ob-indicator'); - warnDiv.style.borderBottomLeftRadius = warnDiv.style.borderBottomRightRadius = "50px"; - warnDiv.style.top = `${mainBounds.top - eOffset.top}px`; - warnDiv.dataset.hcalReservationObjId = reserv.id; - this.edivcontainer.appendChild(warnDiv); - var warnComputedStyle = window.getComputedStyle(warnDiv, null); - warnDiv.style.left = `${(bounds.left + (bounds.right - bounds.left)/2.0 - parseInt(warnComputedStyle.getPropertyValue("width"), 10)/2.0) - mainBounds.left}px`; - } - } else { - var warnDiv = this.e.querySelector(`div.hcal-warn-ob-indicator[data-hcal-reservation-obj-id='${reserv.id}']`); - if (warnDiv) { - warnDiv.parentNode.removeChild(warnDiv); - } - } - } - } - }, - - _updateHighlightUnifyReservations: function() { - var $this = this; - if (!this.reservationAction.toUnify || this.reservationAction.toUnify.length === 0) { - var elms = this.e.querySelectorAll("div.hcal-reservation-invalid-unify"); - for (var elm of elms) { elm.classList.remove('hcal-reservation-invalid-unify'); } - elms = this.e.querySelectorAll("div.hcal-reservation-unify-selected"); - for (var elm of elms) { elm.classList.remove('hcal-reservation-unify-selected'); } - } - else { - var dateLimits = this.getDateLimits(this.reservationAction.toUnify, false); - var refUnifyReservation = this.reservationAction.toUnify[0]; - var uniRoom = refUnifyReservation?refUnifyReservation.room.id:false; - for (var nreserv of this._reservations) { - if (nreserv.unusedZone || nreserv._html.classList.contains('hcal-reservation-unify-selected')) { - continue; - } - - // Invalid? - if (nreserv.room.id !== uniRoom || (!nreserv.startDate.isSame(dateLimits[1], 'day') && !nreserv.endDate.isSame(dateLimits[0], 'day')) || - (nreserv.id !== refUnifyReservation.id && nreserv.getUserData('parent_reservation') !== refUnifyReservation.id)) - { - nreserv._html.classList.add('hcal-reservation-invalid-unify'); - } - else { - nreserv._html.classList.remove('hcal-reservation-invalid-unify'); - } - } - } - }, - - _updateHighlightSwapReservations: function() { - var $this = this; - if (this.reservationAction.inReservations.length === 0 && this.reservationAction.outReservations.length === 0) { - var elms = this.e.querySelectorAll("div.hcal-reservation-invalid-swap"); - for (var elm of elms) { elm.classList.remove('hcal-reservation-invalid-swap'); } - elms = this.e.querySelectorAll("div.hcal-reservation-swap-in-selected"); - for (var elm of elms) { elm.classList.remove('hcal-reservation-swap-in-selected'); } - elms = this.e.querySelectorAll("div.hcal-reservation-swap-out-selected"); - for (var elm of elms) { elm.classList.remove('hcal-reservation-swap-out-selected'); } - } - else { - var dateLimits = this.getDateLimits(this.reservationAction.inReservations, true); - var refInReservation = this.reservationAction.inReservations[0]; - var refOutReservation = this.reservationAction.outReservations[0]; - var inCapacity = refInReservation?refInReservation.room.capacity:false; - var outCapacity = refOutReservation?refOutReservation.room.capacity:false; - var realDateLimits = this.getFreeDatesByRoom(dateLimits[0], dateLimits[1], refInReservation?refInReservation.room.id:refOutReservation.room.id); - for (var nreserv of this._reservations) { - if (nreserv.unusedZone || nreserv._html.classList.contains('hcal-reservation-swap-in-selected') || nreserv._html.classList.contains('hcal-reservation-swap-out-selected')) { - continue; - } - - // Invalid capacity - var totalReservPerson = nreserv.getTotalPersons(false); - if (totalReservPerson > inCapacity || (outCapacity && totalReservPerson > outCapacity) || nreserv.room.capacity < refInReservation.getTotalPersons(false)) - { - nreserv._html.classList.add('hcal-reservation-invalid-swap'); - } - else if (this._modeSwap === HotelCalendar.MODE.SWAP_FROM && this.reservationAction.inReservations.length !== 0 && refInReservation.room.id !== nreserv.room.id) { - if (!_.find(this.reservationAction.outReservations, {'id': nreserv.linkedId})) { - nreserv._html.classList.add('hcal-reservation-invalid-swap'); - } - } else if (this._modeSwap === HotelCalendar.MODE.SWAP_TO && this.reservationAction.outReservations.length !== 0 && refOutReservation.room.id !== nreserv.room.id) { - if (!_.find(this.reservationAction.inReservations, {'id': nreserv.linkedId})) { - nreserv._html.classList.add('hcal-reservation-invalid-swap'); - } - } - // Invalid reservations out of dates - else if (nreserv.startDate.isBefore(realDateLimits[0], 'day') || nreserv.endDate.clone().subtract('1', 'd').isAfter(realDateLimits[1], 'day')) { - if (nreserv.room.id !== refInReservation.room.id) { - nreserv._html.classList.add('hcal-reservation-invalid-swap'); - } - } - else { - nreserv._html.classList.remove('hcal-reservation-invalid-swap'); - } - } - } - }, - - _updateHighlightInvalidZones: function(/*HReservation*/reserv) { - if (typeof reserv === 'undefined') { - var elms = this.etable.querySelectorAll("td[data-hcal-date] table td"); - for (var tdCell of elms) { - tdCell.classList.remove('hcal-cell-invalid'); - } - return; - } - - if (reserv.readOnly) { - var elms = this.etable.querySelectorAll("td[data-hcal-date] table td"); - for (var tdCell of elms) { - tdCell.classList.add('hcal-cell-invalid'); - } - } else if (reserv.fixDays) { - var limitLeftDate = this.etable.querySelector(`#${reserv._limits.left.dataset.hcalParentCell}`).dataset.hcalDate; - var limitRightDate = this.etable.querySelector(`#${reserv._limits.right.dataset.hcalParentCell}`).dataset.hcalDate; - var limitLeftDateMoment = HotelCalendar.toMoment(limitLeftDate); - var limitRightDateMoment = HotelCalendar.toMoment(limitRightDate); - var diff_date = this.getDateDiffDays(limitLeftDateMoment, limitRightDateMoment); - var date = limitLeftDateMoment.clone().startOf('day'); - var selector = []; - for (var i=0; i<=diff_date; i++) { - selector.push("not([data-hcal-date='"+date.format(HotelCalendar.DATE_FORMAT_SHORT_)+"'])"); - date.add(1, 'd'); - } - if (selector.length) { - var elms = this.etable.querySelectorAll(`td:${selector.join(':')}`+ ' table td'); - for (var tdCell of elms) { - tdCell.classList.add('hcal-cell-invalid'); - } - } - } else if (reserv.fixRooms) { - var parentCell = this.etable.querySelector(`#${reserv._limits.left.dataset.hcalParentCell}`); - var parent_row = parentCell.dataset.hcalParentRow; - var elms = this.etable.querySelectorAll("td:not([data-hcal-parent-row='"+parent_row+"']) table td"); - for (var tdCell of elms) { - tdCell.classList.add('hcal-cell-invalid'); - } - } else { - var limitLeftDate = this.etable.querySelector(`#${reserv._limits.left.dataset.hcalParentCell}`).dataset.hcalDate; - var limitRightDate = this.etable.querySelector(`#${reserv._limits.right.dataset.hcalParentCell}`).dataset.hcalDate; - var limitLeftDateMoment = HotelCalendar.toMoment(limitLeftDate); - var limitRightDateMoment = HotelCalendar.toMoment(limitRightDate); - var diff_date = this.getDateDiffDays(limitLeftDateMoment, limitRightDateMoment)+1; - if (reserv._drawModes[1] === 'hard-end') { --diff_date; } - var date = limitLeftDateMoment.clone().startOf('day'); - var selector = []; - for (var i=0; i<=diff_date; i++) { - selector.push("td[data-hcal-date='"+date.format(HotelCalendar.DATE_FORMAT_SHORT_)+"'] table td"); - date.add(1, 'd'); - } - if (selector.length) { - var elms = this.etable.querySelectorAll(`${selector.join(', ')}`); - for (var tdCell of elms) { - tdCell.classList.add('hcal-cell-highlight'); - } - } - } - }, - - _updateScroll: function(/*HTMLObject*/reservationDiv) { - var reservBounds = this.loopedOffsetOptimized(reservationDiv); - var mainBounds = this._edivrOffset; - var eOffset = this._eOffset; - var bottom = mainBounds.height - eOffset.top; - var top = mainBounds.top + eOffset.top; - var offset = 10.0; - var scrollDisp = 10.0; - if (reservBounds.height >= bottom-offset) { - this.edivr.scrollBy(0, scrollDisp); - } - else if (reservBounds.top <= top+offset) { - this.edivr.scrollBy(0, -scrollDisp); - } - }, - - //==== SELECTION - _updateCellSelection: function() { - // Clear all - var highlighted_td = this.etable.querySelectorAll('td.hcal-cell-highlight'); - for (var td of highlighted_td) { - td.classList.remove('hcal-cell-highlight'); - td.textContent = ''; - } - - // Highlight Selected - if (this._cellSelection.current) { - this._cellSelection.current.classList.add('hcal-cell-highlight'); - } - // Highlight Range Cells - var cells = false; - var total_price = 0.0; - var limits = new HLimit(this._cellSelection.start, - this._cellSelection.end?this._cellSelection.end:this._cellSelection.current); - if (limits.isValid()) { - // Normalize - // TODO: Multi-Directional Selection. Now only support normal or inverse. - var limitLeftDate = HotelCalendar.toMoment(this.etable.querySelector(`#${limits.left.dataset.hcalParentCell}`).dataset.hcalDate); - var limitRightDate = HotelCalendar.toMoment(this.etable.querySelector(`#${limits.right.dataset.hcalParentCell}`).dataset.hcalDate); - if (limitLeftDate.isAfter(limitRightDate)) { - limits.swap(); - } - cells = this.getCells(limits); - for (var c of cells) { - var parentRow = this.$base.querySelector(`#${c.dataset.hcalParentRow}`); - var room = this.getRoom(parentRow.dataset.hcalRoomObjId); - if (room.overbooking || room.cancelled) { - continue; - } - c.classList.add('hcal-cell-highlight'); - if (this._pricelist) { - // FIXME: Normalize data calendar (gmt) vs extra info (utc) - var date_cell = HotelCalendar.toMoment(this.etable.querySelector(`#${c.dataset.hcalParentCell}`).dataset.hcalDate); - var room_price = this.getRoomPrice(parentRow.dataset.hcalRoomObjId, date_cell); - c.textContent = room_price + ' ' + this.options.currencySymbol; - if (!room.shared && c.dataset.hcalBedNum > limits.left.dataset.hcalBedNum) { - c.style.color = 'lightgray'; - } - else { - c.style.color = 'black'; - total_price += room_price; - } - } - } - } - - this._dispatchEvent( - 'hcalOnUpdateSelection', - { - 'limits': limits, - 'cells': cells, - 'old_cells': highlighted_td, - 'totalPrice': total_price - }); - }, - - _resetCellSelection: function() { - this._cellSelection = { current: false, end: false, start: false }; - }, - - //==== RESERVATIONS - _updateDivReservation: function(/*HReservationObject*/reserv, /*Bool?*/noRefresh) { - if (!reserv._limits.isValid() || !reserv._html) { - return; - } - - if (reserv.readOnly) { - reserv._html.classList.add('hcal-reservation-readonly'); - } else { - reserv._html.classList.remove('hcal-reservation-readonly'); - } - - if (reserv.room._active) { - reserv._html.classList.remove('hcal-hidden'); - } else { - reserv._html.classList.add('hcal-hidden'); - } - - if (reserv._active) { - reserv._html.classList.remove('hcal-reservation-unselect'); - } else { - reserv._html.classList.add('hcal-reservation-unselect'); - } - - if (!noRefresh) { - var boundsInit = this.loopedOffsetOptimized(reserv._limits.left); - var boundsEnd = this.loopedOffsetOptimized(reserv._limits.right); - var divHeight = (boundsEnd.top+boundsEnd.height)-boundsInit.top-4; - var has_changed = false; - - var reservStyles = { - backgroundColor: reserv.color, - color: reserv.colorText, - lineHeight: `${divHeight}px`, - fontSize: '12px', - top: `${boundsInit.top-this._etableOffset.top+2}px`, - left: `${boundsInit.left-this._etableOffset.left+2}px`, - width: `${(boundsEnd.left-boundsInit.left)+boundsEnd.width-4}px`, - height: `${divHeight}px`, - borderLeftWidth: '', - borderLeftStyle: '', - borderRightWidth: '', - borderRightStyle: '', - }; - - if (reserv._drawModes[0] === 'soft-start') { - has_changed = true; - reservStyles.borderLeftWidth = '3px'; - reservStyles.borderLeftStyle = 'double'; - reservStyles.left = `${boundsInit.left-this._etableOffset.left}px`; - reservStyles.width = `${(boundsEnd.left-boundsInit.left)+boundsEnd.width-2}px`; - } else if (reserv.splitted && reserv.startDate.isSame(reserv.getUserData('realDates')[0], 'day')) { - has_changed = true; - reservStyles.borderLeftWidth = '0'; - reservStyles.width = `${(boundsEnd.left-boundsInit.left)+boundsEnd.width-2}px`; - } - - if (reserv._drawModes[1] === 'soft-end') { - has_changed = true; - reservStyles.borderRightWidth = '3px'; - reservStyles.borderRightStyle = 'double'; - reservStyles.width = `${(boundsEnd.left-boundsInit.left)+boundsEnd.width-2}px`; - } else if (reserv.splitted && reserv.endDate.isSame(reserv.getUserData('realDates')[1], 'day')) { - has_changed = true; - reservStyles.borderRightWidth = '0'; - reservStyles.left = `${boundsInit.left-this._etableOffset.left-1}px`; - reservStyles.width = `${(boundsEnd.left-boundsInit.left)+boundsEnd.width-1}px`; - } - - if (reserv.splitted) { - reserv._html.classList.add('hcal-reservation-splitted'); - // 1. Use reservation ID as seed - // 2. Use sinusiudal function - // 3. Only use positive values (This decrease longitude) - // 4. Use the first 5 decimals to make the integer value - // 5. Get integer value (Bitwise tilde method) - // TODO: Improve pseudo-random number generator - var magicNumber = ~~(Math.abs(Math.sin((reserv.getUserData('parent_reservation') || reserv.id))) * 100000); - var bbColor = this._intToRgb(magicNumber); - reservStyles.borderColor = `rgb(${bbColor[0]},${bbColor[1]},${bbColor[2]})`; - - if (!has_changed) { - reservStyles.left = `${boundsInit.left-this._etableOffset.left-1}px`; - reservStyles.width = `${(boundsEnd.left-boundsInit.left)+boundsEnd.width+2}px`; - } - } else { - reserv._html.classList.remove('hcal-reservation-splitted'); - } - - Object.assign(reserv._html.style, reservStyles); - } - }, - - swapReservations: function(/*List HReservationObject*/fromReservations, /*List HReservationObject*/toReservations) { - if (fromReservations.length === 0 || toReservations.length === 0) { - console.warn("[HotelCalendar][swapReservations] Invalid Swap Operation!"); - return false; - } - var fromDateLimits = this.getDateLimits(fromReservations, true); - var fromRealDateLimits = this.getFreeDatesByRoom(fromDateLimits[0], fromDateLimits[1], fromReservations[0].room.id); - var toDateLimits = this.getDateLimits(toReservations, true); - var toRealDateLimits = this.getFreeDatesByRoom(toDateLimits[0], toDateLimits[1], toReservations[0].room.id); - - if (fromDateLimits[0].clone().local().isSameOrAfter(toRealDateLimits[0].clone().local(), 'd') && fromDateLimits[1].clone().local().isSameOrBefore(toRealDateLimits[1].clone().local(), 'd') && - toDateLimits[0].clone().local().isSameOrAfter(fromRealDateLimits[0].clone().local(), 'd') && toDateLimits[1].clone().local().isSameOrBefore(fromRealDateLimits[1].clone().local(), 'd')) - { - // Change some critical values - var refFromReservs = fromReservations[0]; - var refToReservs = toReservations[0]; - var refFromRoom = refFromReservs.room; - var refToRoom = refToReservs.room; - var fromRoomRow = this.getExtraRoomRow(refFromReservs); - var toRoomRow = this.getExtraRoomRow(refToReservs); - var refFromRoomNewId = (refFromRoom.overbooking||refFromRoom.cancelled)?this.parseExtraRoomId(refFromRoom.id)[1]:refFromRoom.id; - refFromRoomNewId = `${refToReservs.id}@${refFromRoomNewId}`; - var refToRoomNewId = (refToRoom.overbooking||refToRoom.cancelled)?this.parseExtraRoomId(refToRoom.id)[1]:refToRoom.id; - refToRoomNewId = `${refFromReservs.id}@${refToRoomNewId}`; - - if (refFromRoom.overbooking || refFromRoom.cancelled) { - var cnumber = this.getExtraRoomRealNumber(refFromRoom); - refFromRoom.id = refFromRoomNewId; - var newRowId = `${this._sanitizeId(`ROW_${cnumber}_${refToRoom.type}_EXTRA${refToReservs.id}`)}`; - var elms = fromRoomRow.querySelectorAll(`td[data-hcal-parent-row='${fromRoomRow.id}']`); - for (var elm of elms) { elm.dataset.hcalParentRow = newRowId; } - fromRoomRow.setAttribute('id', `${newRowId}`); - fromRoomRow.dataset.hcalRoomObjId = refFromRoom.id; - } - if (refToRoom.overbooking || refToRoom.cancelled) { - var cnumber = this.getExtraRoomRealNumber(refToRoom); - refToRoom.id = refToRoomNewId; - var newRowId = `${this._sanitizeId(`ROW_${cnumber}_${refFromRoom.type}_EXTRA${refFromReservs.id}`)}`; - var elms = toRoomRow.querySelectorAll(`td[data-hcal-parent-row='${toRoomRow.id}']`); - for (var elm of elms) { elm.dataset.hcalParentRow = newRowId; } - toRoomRow.setAttribute('id', `${newRowId}`); - toRoomRow.dataset.hcalRoomObjId = refToRoom.id; - } - - for (var nreserv of fromReservations) { - nreserv.cancelled = refToRoom.cancelled; - nreserv.overbooking = refToRoom.overbooking; - nreserv.room = refToRoom; - } - for (var nreserv of toReservations) { - nreserv.cancelled = refFromRoom.cancelled; - nreserv.overbooking = refFromRoom.overbooking; - nreserv.room = refFromRoom; - } - } else { - console.warn("[HotelCalendar][swapReservations] Invalid Swap Operation!"); - return false; - } - - return true; - }, - - _dispatchSwapReservations: function() { - if (this.reservationAction.inReservations.length > 0 && this.reservationAction.outReservations.length > 0) { - this._dispatchEvent( - 'hcalOnSwapReservations', - { - 'inReservs': this.reservationAction.inReservations || [], - 'outReservs': this.reservationAction.outReservations || [], - } - ); - } - }, - - _dispatchUnifyReservations: function() { - if (this.reservationAction.hasOwnProperty('toUnify') && this.reservationAction.toUnify.length > 1) { - this._dispatchEvent( - 'hcalOnUnifyReservations', - { - 'toUnify': this.reservationAction.toUnify || [], - } - ); - } - }, - - replaceReservation: function(/*HReservationObject*/reservationObj, /*HReservationObject*/newReservationObj) { - if (!reservationObj._html) { - console.warn("[Hotel Calendar][updateReservation_] Invalid Reservation Object"); - return; - } - - var index = _.findKey(this._reservations, {'id': reservationObj.id}); - delete this._reservations[index]; - this._reservations[index] = newReservationObj; - reservationObj._html.dataset.hcalReservationObjId = newReservationObj.id; - this._updateReservationsMap(); - this._updateDivReservation(newReservationObj); - - var linkedReservations = this.getLinkedReservations(newReservationObj); - for (var lr of linkedReservations) { - lr.startDate = newReservationObj.startDate.clone(); - lr.endDate = newReservationObj.endDate.clone(); - - if (lr._html) { - this._calcReservationCellLimits(lr); - this._updateDivReservation(lr); - } - } - _.defer(function(){ this._updateReservationOccupation(); }.bind(this)); - }, - - getLinkedReservations: function(/*HReservationObject*/reservationObj) { - return _.reject(this._reservations, function(item){ return item.linkedId !== reservationObj.id; }); - }, - - _updateReservation: function(/*HReservationObject*/reservationObj, /*Bool?*/noRefresh) { - // Fill - if (reservationObj._limits.isValid()) { - this._updateDivReservation(reservationObj, noRefresh); - } else { - console.warn(`[Hotel Calendar][_updateReservation] Can't place reservation ID@${reservationObj.id} [${reservationObj.startDate.format(HotelCalendar.DATE_FORMAT_LONG_)} --> ${reservationObj.endDate.format(HotelCalendar.DATE_FORMAT_LONG_)}]`); - this.removeReservation(reservationObj); - } - }, - - _updateReservations: function(/*Bool*/updateLimits) { - for (var reservation of this._reservations){ - if (updateLimits) { - this._calcReservationCellLimits(reservation); - } - this._updateReservation(reservation); - } - //this._assignReservationsEvents(); - //this._updateReservationOccupation(); - this._updateOBIndicators(); - }, - - _assignReservationsEvents: function(reservDivs) { - var $this = this; - reservDivs = reservDivs || this.e.querySelectorAll('div.hcal-reservation'); - for (var rdiv of reservDivs) { - var bounds = this.loopedOffsetOptimized(rdiv); - rdiv.addEventListener('mousemove', function(ev){ - var posAction = $this._getRerservationPositionAction(this, ev.layerX, ev.layerY); - if (posAction == HotelCalendar.ACTION.MOVE_LEFT || posAction == HotelCalendar.ACTION.MOVE_RIGHT) { - this.style.cursor = 'col-resize'; - } else if (posAction == HotelCalendar.ACTION.MOVE_DOWN) { - this.style.cursor = 'ns-resize'; - } else { - this.style.cursor = 'pointer'; - } - }, false); - var _funcEvent = function(ev){ - if ($this._isLeftButtonPressed(ev)) { - // MODE UNIFY RESERVATIONS - if ($this._selectionMode === HotelCalendar.ACTION.UNIFY) { - var reserv = $this.getReservation(this.dataset.hcalReservationObjId); - var dateLimits = $this.getDateLimits($this.reservationAction.toUnify, false); - var refUnifyReserv = ($this.reservationAction.toUnify.length > 0)?$this.reservationAction.toUnify[0]:false; - if ($this.reservationAction.toUnify.indexOf(reserv) != -1) { - $this.reservationAction.toUnify = _.reject($this.reservationAction.toUnify, function(item){ return item === reserv}); - this.classList.remove('hcal-reservation-unify-selected'); - } - else { - $this.reservationAction.toUnify.push(reserv); - this.classList.add('hcal-reservation-unify-selected'); - } - $this._updateHighlightUnifyReservations(); - } - else { - // ENABLE SWAP SELECTION - if (ev.ctrlKey || $this._modeSwap === HotelCalendar.MODE.SWAP_FROM) { - $this.reservationAction.action = HotelCalendar.ACTION.SWAP; - $this.setSwapMode(HotelCalendar.MODE.SWAP_FROM); - } - // MODE SWAP RESERVATIONS - if ($this.reservationAction.action === HotelCalendar.ACTION.SWAP) { - var reserv = $this.getReservation(this.dataset.hcalReservationObjId); - var refFromReserv = ($this.reservationAction.inReservations.length > 0)?$this.reservationAction.inReservations[0]:false; - var refToReserv = ($this.reservationAction.outReservations.length > 0)?$this.reservationAction.outReservations[0]:false; - - if (ev.ctrlKey || $this._modeSwap === HotelCalendar.MODE.SWAP_FROM) { - var canAdd = !((!refFromReserv && refToReserv && reserv.room.id === refToReserv.room.id) || (refFromReserv && reserv.room.id !== refFromReserv.room.id)); - // Can unselect - if ($this.reservationAction.inReservations.indexOf(reserv) != -1 && (($this.reservationAction.outReservations.length > 0 && $this.reservationAction.inReservations.length > 1) || $this.reservationAction.outReservations.length === 0)) { - $this.reservationAction.inReservations = _.reject($this.reservationAction.inReservations, function(item){ return item === reserv}); - this.classList.remove('hcal-reservation-swap-in-selected'); - } - // Can't add a 'out' reservation in 'in' list - else if ($this.reservationAction.outReservations.indexOf(reserv) == -1 && canAdd) { - $this.reservationAction.inReservations.push(reserv); - this.classList.add('hcal-reservation-swap-in-selected'); - } - } else if (!ev.ctrlKey || $this._modeSwap === HotelCalendar.MODE.SWAP_TO) { - $this.setSwapMode(HotelCalendar.MODE.SWAP_TO); - var canAdd = !((!refToReserv && refFromReserv && reserv.room.id === refFromReserv.room.id) || (refToReserv && reserv.room.id !== refToReserv.room.id)); - // Can unselect - if ($this.reservationAction.outReservations.indexOf(reserv) != -1) { - $this.reservationAction.outReservations = _.reject($this.reservationAction.outReservations, function(item){ return item === reserv; }); - this.classList.remove('hcal-reservation-swap-out-selected'); - } - // Can't add a 'in' reservation in 'out' list - else if ($this.reservationAction.inReservations.indexOf(reserv) == -1 && canAdd) { - $this.reservationAction.outReservations.push(reserv); - this.classList.add('hcal-reservation-swap-out-selected'); - } - } - $this._updateHighlightSwapReservations(); - } - // MODE RESIZE/MOVE RESERVATION - else if (!$this.reservationAction.reservation) { - $this.reservationAction = { - reservation: this, - mousePos: [ev.x, ev.y], - action: $this._getRerservationPositionAction(this, ev.layerX, ev.layerY), - inReservations: [], - outReservations: [], - }; - - // FIXME: Workaround for lazy selection operation - if ($this._lazyModeReservationsSelection) { - clearTimeout($this._lazyModeReservationsSelection); - $this._lazyModeReservationsSelection = false; - } - - $this._lazyModeReservationsSelection = setTimeout(function($this){ - var reserv = $this.getReservation(this.dataset.hcalReservationObjId); - $this._updateHighlightInvalidZones(reserv); - if (reserv.readOnly || (reserv.fixDays && ($this.reservationAction.action == HotelCalendar.ACTION.MOVE_LEFT - || $this.reservationAction.action == HotelCalendar.ACTION.MOVE_RIGHT))) { - $this.reservationAction.action = HotelCalendar.ACTION.NONE; - return false; - } - var affectedReservations = [reserv].concat($this.getLinkedReservations(reserv)); - for (var areserv of affectedReservations) { - if (areserv._html) { - areserv._html.classList.add('hcal-reservation-action'); - } - } - - var otherReservs = _.difference($this._reservations, affectedReservations); - for (var oreserv of otherReservs) { - if (oreserv._html) { - oreserv._html.classList.add('hcal-reservation-foreground'); - } - } - - $this._lazyModeReservationsSelection = false; - }.bind(this, $this), 175); - } - } - } - }; - rdiv.addEventListener('mousedown', _funcEvent, this._supportsPassive ? { passive: true } : false); - rdiv.addEventListener('touchstart', _funcEvent, this._supportsPassive ? { passive: true } : false); - rdiv.addEventListener('click', function(ev){ - $this._dispatchEvent( - 'hcalOnClickReservation', - { - 'event': ev, - 'reservationDiv': this, - 'reservationObj': $this.getReservation(this.dataset.hcalReservationObjId) - }); - }, false); - rdiv.addEventListener('dblclick', function(ev){ - $this._dispatchEvent( - 'hcalOnDblClickReservation', - { - 'event': ev, - 'reservationDiv': this, - 'reservationObj': $this.getReservation(this.dataset.hcalReservationObjId) - }); - }, false); - /* - rdiv.addEventListener('mouseenter', function(ev){ - $this._dispatchEvent( - 'hcalOnMouseEnterReservation', - { - 'event': ev, - 'reservationDiv': this, - 'reservationObj': $this.getReservation(this.dataset.hcalReservationObjId) - }); - }, false); - rdiv.addEventListener('mouseleave', function(ev){ - $this._dispatchEvent( - 'hcalOnMouseLeaveReservation', - { - 'event': ev, - 'reservationDiv': this, - 'reservationObj': $this.getReservation(this.dataset.hcalReservationObjId) - }); - }, false); - */ - } - }, - - _getRerservationPositionAction: function(/*HTMLObject*/elm, /*Int*/posX, /*Int*/posY) { - var bounds = this.loopedOffsetOptimized(elm); - if (posX <= 4) { return HotelCalendar.ACTION.MOVE_LEFT; } - else if (posX >= bounds.width-8) { return HotelCalendar.ACTION.MOVE_RIGHT; } - else if (posY >= bounds.height-4) { return HotelCalendar.ACTION.MOVE_DOWN; } - return HotelCalendar.ACTION.MOVE_ALL; - }, - - _cleanUnusedZones: function(/*HReservationObject*/reserv) { - var unusedReservs = this.getLinkedReservations(reserv); - for (var unusedZone of unusedReservs) { - if (unusedZone._html && unusedZone._html.parentNode) { - unusedZone._html.parentNode.removeChild(unusedZone._html); - } - } - this._reservations = _.reject(this._reservations, {unusedZone: true, linkedId: reserv.id}); - }, - - _createUnusedZones: function(/*Array*/reservs) { - var nreservs = []; - for (var reserv of reservs) { - if (!reserv.unusedZone) { - var unused_id = 0; - var numBeds = reserv.getTotalPersons(false); - for (var e=numBeds; e reservationObj.room.capacity)) { - return false; - } - - if (reservationObj.room.id in this._reservationsMap) { - for (var r of this._reservationsMap[reservationObj.room.id]) { - if (!r.unusedZone && r !== reservationObj && reservationObj.room.number == r.room.number && - (_.difference(reservationObj._beds, r._beds).length != reservationObj._beds.length || this.options.divideRoomsByCapacity) && - (r.startDate.isBetween(reservationObj.startDate, reservationObj.endDate, 'day', '[)') || - r.endDate.isBetween(reservationObj.startDate, reservationObj.endDate, 'day', '(]') || - (reservationObj.startDate.isSameOrAfter(r.startDate, 'day') && reservationObj.endDate.isSameOrBefore(r.endDate, 'day')))) { - return false; - } - } - } - - return true; - }, - - getDates: function() { - return [this.options.startDate.clone(), this._endDate.clone()]; - }, - - //==== EVENT FUNCTIONS - _onInputChange: function(/*EventObject*/ev, /*HTMLObject*/elm) { - //var parentCell = this.edtable.querySelector(`#${elm.dataset.hcalParentCell}`); - //var parentRow = this.edtable.querySelector(`#${parentCell.dataset.hcalParentRow}`); - var value = elm.value; - var orgValue = elm.dataset.orgValue; - var name = elm.getAttribute('name'); - - if (name === 'room_type_price_day') { - if (!this._isNumeric(value)) { - elm.style.backgroundColor = 'red'; - } else if (orgValue !== value) { - elm.classList.add('hcal-input-changed'); - elm.style.backgroundColor = ''; - } else { - elm.classList.remove('hcal-input-changed'); - if (value == 0) { - elm.style.backgroundColor = 'rgb(255, 174, 174)'; - } - } - } - - var parentCell = this.edtable.querySelector(`#${elm.dataset.hcalParentCell}`); - var parentRow = this.edtable.querySelector(`#${parentCell.dataset.hcalParentRow}`); - var vals = { - 'room_type_id': +parentRow.dataset.hcalRoomTypeId, - 'date': HotelCalendar.toMoment(parentCell.dataset.hcalDate), - 'price': value, - 'old_price': orgValue, - 'pricelist_id': +parentRow.dataset.hcalPricelist - }; - //this.updateRoomTypePrice(vals['pricelist_id'], vals['room_type_id'], vals['date'], vals['price']); - this._dispatchEvent('hcalOnPricelistChanged', vals); - - if (this.edivc.querySelector('.hcal-input-changed') !== null) - { - this.btnSaveChanges.classList.add('need-save'); - this.btnSaveChanges.disabled = false; - } else { - this.btnSaveChanges.classList.remove('need-save'); - this.btnSaveChanges.disabled = true; - } - }, - - _onCellMouseUp: function(ev) { - if (this._selectionMode === HotelCalendar.ACTION.DIVIDE) { - if (this.reservationAction.reservation) { - var realEndDate = this.reservationAction.endDate.clone().subtract(1, 'd'); - if (this.reservationAction.action === HotelCalendar.ACTION.DIVIDE && !this.reservationAction.date.isSame(realEndDate, 'day')) { - var diff = this.getDateDiffDays(this.reservationAction.date, realEndDate); - this._dispatchEvent('hcalOnSplitReservation', { - reservation: this.reservationAction.reservation, - obj_id: this.reservationAction.obj_id, - date: this.reservationAction.date, - nights: diff - }) - this._reset_action_reservation(); - this.setSelectionMode(HotelCalendar.ACTION.NONE); - } - } - } - else if (this.reservationAction.action !== HotelCalendar.ACTION.NONE) { - return; - } - else if (this._cellSelection.start && - this._cellSelection.start.dataset.hcalParentRow === ev.target.dataset.hcalParentRow) { - this._cellSelection.end = ev.target; - this._dispatchEvent( - 'hcalOnChangeSelection', - { - 'cellStart': this._cellSelection.start, - 'cellEnd': this._cellSelection.end - }); - } - }, - - _onCellMouseDown: function(ev) { - if (this._selectionMode === HotelCalendar.ACTION.DIVIDE && this._splitReservation) { - this.reservationAction = { - reservation: this._splitReservation._html, - obj_id: this._splitReservation.id, - endDate: this._splitReservation.endDate, - action: this._selectionMode, - date: this._splitDate, - }; - this._splitReservation = false; - this._splitDate = false; - } else if ($(".marked-as-having-a-popover").length === 1) { - // TODO: better call _destroy_and_clear_popover_mark defined in hotel_calendar_controller.js - $(".marked-as-having-a-popover").popover('destroy'); - $('.hcal-reservation').removeClass("marked-as-having-a-popover"); - } else { - // FIXME: Prevent multiple clicks in a row - this._cellSelection.start = this._cellSelection.current = ev.target; - this._cellSelection.end = false; - this._updateCellSelection(); - } - }, - - _onCellMouseEnter: function(ev) { - var date_cell = HotelCalendar.toMoment(this.etable.querySelector(`#${ev.target.dataset.hcalParentCell}`).dataset.hcalDate); - var reserv; - if (this.reservationAction.reservation) { - reserv = this.getReservation(this.reservationAction.reservation.dataset.hcalReservationObjId); - if (!this.reservationAction.oldReservationObj) { - this.reservationAction.oldReservationObj = reserv.clone(); - this.reservationAction.daysOffset = this.getDateDiffDays(reserv.startDate.clone().local(), date_cell); - if (this.reservationAction.daysOffset < 0 ) { - this.reservationAction.daysOffset = 0; - } - } - } - if (this._selectionMode === HotelCalendar.MODE.NONE && this._isLeftButtonPressed(ev)) { - var toRoom = undefined; - var needUpdate = false; - if (!this.reservationAction.reservation) { - if (this._cellSelection.start && this._cellSelection.start.dataset.hcalParentRow === ev.target.dataset.hcalParentRow) { - this._cellSelection.current = ev.target; - } - this._updateCellSelection(); - } else if (this.reservationAction.mousePos) { - // workarround for not trigger reservation change - var a = this.reservationAction.mousePos[0] - ev.x; - var b = this.reservationAction.mousePos[1] - ev.y; - //var dist = Math.sqrt(a*a + b*b); - if (this.reservationAction.action == HotelCalendar.ACTION.MOVE_RIGHT) { - if (reserv.fixDays) { - this._reset_action_reservation(); - return true; - } - if (!date_cell.isAfter(reserv.startDate, 'd')) { - date_cell = reserv.startDate.clone().startOf('day'); - } - if (!this.reservationAction.oldReservationObj) { - this.reservationAction.oldReservationObj = reserv.clone(); - } - reserv.endDate.set({'date': date_cell.date(), 'month': date_cell.month(), 'year': date_cell.year()}).add(1, 'd'); - this.reservationAction.newReservationObj = reserv; - needUpdate = true; - } else if (this.reservationAction.action == HotelCalendar.ACTION.MOVE_LEFT) { - if (reserv.fixDays) { - this._reset_action_reservation(); - return true; - } - var ndate = reserv.endDate.clone().endOf('day').subtract(1, 'd'); - if (!date_cell.isBefore(ndate, 'd')) { - date_cell = ndate; - } - if (!this.reservationAction.oldReservationObj) { - this.reservationAction.oldReservationObj = reserv.clone(); - } - reserv.startDate.set({'date': date_cell.date(), 'month': date_cell.month(), 'year': date_cell.year()}); - this.reservationAction.newReservationObj = reserv; - needUpdate = true; - } else if (this.reservationAction.action == HotelCalendar.ACTION.MOVE_DOWN) { - var parentRow = ev.target.parentNode.parentNode.parentNode.parentNode; - var room = this.getRoom(parentRow.dataset.hcalRoomObjId); - - if (room.id === reserv.room.id) { - if (!this.reservationAction.oldReservationObj) { - this.reservationAction.oldReservationObj = reserv.clone(); - } - reserv.adults = +ev.target.dataset.hcalBedNum + 1; - this.reservationAction.newReservationObj = reserv; - needUpdate = true; - } - } else if (this.reservationAction.action == HotelCalendar.ACTION.MOVE_ALL) { - // Relative Movement - date_cell.subtract(this.reservationAction.daysOffset, 'd'); - - var parentRow = ev.target.parentNode.parentNode.parentNode.parentNode; - var room = this.getRoom(parentRow.dataset.hcalRoomObjId); - reserv.room = room; - var diff_date = this.getDateDiffDays(reserv.startDate, reserv.endDate); - reserv.startDate.set({'date': date_cell.date(), 'month': date_cell.month(), 'year': date_cell.year()}); - var date_end = reserv.startDate.clone().add(diff_date, 'd'); - reserv.endDate.set({'date': date_end.date(), 'month': date_end.month(), 'year': date_end.year()}); - this.reservationAction.newReservationObj = reserv; - toRoom = +ev.target.dataset.hcalBedNum; - needUpdate = true; - } - } - - if (needUpdate && reserv) { - _.defer(function(r){ this._updateScroll(r._html); }.bind(this), reserv) - - var affectedReservations = [reserv].concat(this.getLinkedReservations(this.reservationAction.newReservationObj)); - for (var areserv of affectedReservations) { - if (areserv !== reserv) { - areserv.startDate = reserv.startDate.clone(); - areserv.endDate = reserv.endDate.clone(); - } - - if (areserv._html) { - if (areserv.unusedZone) { - areserv._html.style.visibility = 'hidden'; - continue; - } - _.defer(function(ro, r, tro){ - this._calcReservationCellLimits( - r, - r===ro?tro:undefined, - !this.options.assistedMovement); - this._updateDivReservation(r); - - if (!r._limits.isValid() || !this.checkReservationPlace(r) || - (r.fixRooms && this.reservationAction.oldReservationObj.room.id != r.room.id) || - (r.fixDays && !this.reservationAction.oldReservationObj.startDate.isSame(r.startDate, 'day'))) { - r._html.classList.add('hcal-reservation-invalid'); - } - else { - r._html.classList.remove('hcal-reservation-invalid'); - } - }.bind(this), reserv, areserv, toRoom); - } - } - } - } else if (this._selectionMode === HotelCalendar.ACTION.DIVIDE) { - var parentRow = ev.target.parentNode.parentNode.parentNode.parentNode; - var room_id = parentRow.dataset.hcalRoomObjId; - var reservs = this.getReservationsByDay(date_cell, true, false, room_id); - if (this._divideDivs) { - this._divideDivs[0].remove(); - this._divideDivs[1].remove(); - this._divideDivs = false; - } - if (reservs.length) { - this._splitReservation = reservs[0]; - var defStyle = { - top: this._splitReservation._html.style.top, - left: this._splitReservation._html.style.left, - height: this._splitReservation._html.style.height, - }; - this._divideDivs = [ - $('
      ', {class: 'hcal-reservation-divide-l', css: defStyle}).appendTo(this.edivr), - $('
      ', {class: 'hcal-reservation-divide-r', css: defStyle}).appendTo(this.edivr) - ]; - var diff = this.getDateDiffDays(this._splitReservation.startDate, date_cell); - var boundsCell = false; - var beginCell = this.loopedOffsetOptimized(this._splitReservation._limits.left); - var endCell = this.loopedOffsetOptimized(this._splitReservation._limits.right); - this._splitDate = date_cell.clone(); - if (date_cell.isSame(this._splitReservation.endDate.clone().subtract(1, 'd'), 'day')) { - this._splitDate.subtract(1, 'd'); - var tcell = this.getCell(this._splitDate, this._splitReservation.room, 0); - if (tcell) { - boundsCell = this.loopedOffsetOptimized(tcell); - } else { - boundsCell = false; - this._splitReservation = false; - this._splitDate = false; - } - } else { - boundsCell = this.loopedOffsetOptimized(ev.target); - } - if (boundsCell) { - this._divideDivs[0][0].style.width = `${(boundsCell.left-beginCell.left)+boundsCell.width}px`; - this._divideDivs[1][0].style.left = `${(boundsCell.left-this._etableOffset.left)+boundsCell.width}px`; - this._divideDivs[1][0].style.width = `${(endCell.left-boundsCell.left)}px`; - } - } else { - this._splitReservation = false; - this._splitDate = false; - } - } - }, - - onMainKeyUp: function(/*EventObject*/ev) { - if (this.reservationAction.action === HotelCalendar.ACTION.SWAP || this.getSwapMode() !== HotelCalendar.MODE.NONE) { - var needReset = false; - if (ev.keyCode === 27) { - this.cancelSwap(); - } - else if (ev.keyCode === 13) { - this._dispatchSwapReservations(); - this._reset_action_reservation(); - this._updateHighlightSwapReservations(); - this._modeSwap = HotelCalendar.MODE.NONE; - } - else if (ev.keyCode === 17 && this.getSwapMode() === HotelCalendar.MODE.SWAP_FROM) { - this.setSwapMode(HotelCalendar.MODE.SWAP_TO); - } - } else if (this._selectionMode !== HotelCalendar.MODE.NONE) { - if (this._selectionMode === HotelCalendar.ACTION.UNIFY && (ev.keyCode === 13 || ev.keyCode === 27)) { - if (ev.keyCode === 13) { - this._dispatchUnifyReservations(); - } - this._reset_action_reservation(); - this._updateHighlightUnifyReservations(); - } - - if (ev.keyCode === 27 || ev.keyCode === 13) { - this.setSelectionMode(HotelCalendar.MODE.NONE); - } - } - }, - - onMainKeyDown: function(/*EventObject*/ev) { - if (this.reservationAction.action === HotelCalendar.ACTION.SWAP || this.getSwapMode() !== HotelCalendar.MODE.NONE) { - if (ev.keyCode === 17 && this.getSwapMode() === HotelCalendar.MODE.SWAP_TO) { - this.setSwapMode(HotelCalendar.MODE.SWAP_FROM); - } - } - }, - - onMainMouseUp: function(/*EventObject*/ev) { - if (this._lazyModeReservationsSelection) { - clearTimeout(this._lazyModeReservationsSelection); - this._lazyModeReservationsSelection = false; - } - _.defer(function(ev){ - if (this.reservationAction.reservation) { - var reservDiv = this.reservationAction.reservation; - reservDiv.classList.remove('hcal-reservation-action'); - this._updateHighlightInvalidZones(); - - var rdivs = this.e.querySelectorAll('div.hcal-reservation.hcal-reservation-foreground'); - for (var rd of rdivs) { rd.classList.remove('hcal-reservation-foreground'); } - - var reserv = this.getReservation(reservDiv.dataset.hcalReservationObjId); - var linkedReservations = this.getLinkedReservations(reserv); - var hasInvalidLink = false; - for (var r of linkedReservations) { - if (r._html) { - hasInvalidLink = !hasInvalidLink && r._html.classList.contains('hcal-reservation-invalid'); - r._html.classList.remove('hcal-reservation-action'); - r._html.classList.remove('hcal-reservation-invalid'); - r._html.style.visibility = ''; - } - } - - if (this.reservationAction.oldReservationObj && this.reservationAction.newReservationObj) { - if (!this.options.allowInvalidActions && (reservDiv.classList.contains('hcal-reservation-invalid') || hasInvalidLink)) { - this.replaceReservation(this.reservationAction.newReservationObj, this.reservationAction.oldReservationObj); - } else { - var oldReservation = this.reservationAction.oldReservationObj; - var newReservation = this.reservationAction.newReservationObj; - // Calc Old Reservation Price - var oldDiff = this.getDateDiffDays(oldReservation.startDate, oldReservation.endDate); - var oldPrice = 0.0 - for (var e=0; e> 16) & 255, (RGBint >> 8) & 255, RGBint & 255]; - }, - - _hueToRgb: function(/*Int*/v1, /*Int*/v2, /*Int*/h) { - if (h<0.0) { h+=1; } - if (h>1.0) { h-=1; } - if ((6.0*h) < 1.0) { return v1+(v2-v1)*6.0*h; } - if ((2.0*h) < 1.0) { return v2; } - if ((3.0*h) < 2.0) { return v1+(v2-v1)*((2.0/3.0)-h)*6.0; } - return v1; - }, - - _hslToRgb: function(/*Int*/h, /*Int*/s, /*Int*/l) { - if (s == 0.0) { - return [l,l,l]; - } - var v2 = l<0.5?l*(1.0+s):(l+s)-(s*l); - var v1 = 2.0*l-v2; - return [ - this._hueToRgb(v1,v2,h+(1.0/3.0)), - this._hueToRgb(v1,v2,h), - this._hueToRgb(v1,v2,h-(1.0/3.0))]; - }, - - _RGBToHex: function(/*Int*/r, /*Int*/g, /*Int*/b){ - var bin = r << 16 | g << 8 | b; - return (function(h){ - return new Array(7-h.length).join("0")+h; - })(bin.toString(16).toUpperCase()); - }, - - _hexToRGB: function(/*Int*/hex){ - var r = hex >> 16; - var g = hex >> 8 & 0xFF; - var b = hex & 0xFF; - return [r,g,b]; - }, - - _generateColor: function(/*Int*/value, /*Int*/max, /*Int*/offset, /*Bool*/reverse, /*Bool*/strmode) { - var rgb = [offset,1.0,0.5]; - if (value > max) { - if (!strmode) { - return rgb; - } - return "rgb("+Math.floor(rgb[0]*255)+","+Math.floor(rgb[1]*255)+","+Math.floor(rgb[2]*255)+")"; - } - if (reverse) { - value = max-value; - } - rgb = this._hslToRgb(((max-value)*offset)/max, 1.0, 0.8); - if (!strmode) { - return rgb; - } - return "rgb("+Math.floor(rgb[0]*255)+","+Math.floor(rgb[1]*255)+","+Math.floor(rgb[2]*255)+")"; - } -}; - -/** CONSTANTS **/ -HotelCalendar.DOMAIN = { NONE: -1, RESERVATIONS: 0, ROOMS: 1 }; -HotelCalendar.ACTION = { NONE: -1, MOVE_ALL: 0, MOVE_LEFT: 1, MOVE_RIGHT: 2, MOVE_DOWN: 3, SWAP: 4, DIVIDE: 5, UNIFY: 6 }; -HotelCalendar.MODE = { NONE: -1, SWAP_FROM: 0, SWAP_TO: 1 }; -HotelCalendar.DATE_FORMAT_SHORT_ = 'DD/MM/YYYY'; -HotelCalendar.DATE_FORMAT_LONG_ = HotelCalendar.DATE_FORMAT_SHORT_ + ' HH:mm:ss'; -/** STATIC METHODS **/ -HotelCalendar.toMoment = function(/*String,MomentObject*/ndate, /*String*/format) { - if (moment.isMoment(ndate)) { - return ndate; - } else if (typeof ndate === 'string' || ndate instanceof Date) { - ndate = moment(ndate, typeof format==='undefined'?HotelCalendar.DATE_FORMAT_LONG_:format); - if (moment.isMoment(ndate)) { - return ndate; - } - } - - //debugger; - console.warn('[Hotel Calendar][toMoment] Invalid date format!'); - return false; -} -HotelCalendar.toMomentUTC = function(/*String,MomentObject*/ndate, /*String*/format) { - if (moment.isMoment(ndate)) { - return ndate; - } else if (typeof ndate === 'string' || ndate instanceof Date) { - ndate = moment.utc(ndate, (typeof format==='undefined'?HotelCalendar.DATE_FORMAT_LONG_:format)); - if (moment.isMoment(ndate)) { - return ndate; - } - } - - //debugger; - console.warn('[Hotel Calendar][toMomentUTC] Invalid date format!'); - return false; -} - - -/** ROOM OBJECT **/ -function HRoom(/*Int*/id, /*String*/number, /*Int*/capacity, /*String*/type, /*Bool*/shared, /*List*/price) { - this.id = id || -1; - this.number = number || -1; - this.capacity = capacity || 1; - this.type = type || ''; - this.shared = shared; - this.price = price || false; - this.overbooking = false; - this.cancelled = false; - - this._html = false; - this._active = true; - this._userData = {}; -} -HRoom.prototype = { - clearUserData: function() { this._userData = {}; }, - getUserData: function(/*String?*/key) { - if (typeof key === 'undefined') { - return this._userData; - } - return key in this._userData && this._userData[key] || null; - }, - addUserData: function(/*Dictionary*/data) { - if (!_.isObject(data)) { - console.warn("[Hotel Calendar][HRoom][setUserData] Invalid Data! Need be a object!"); - } else { - this._userData = _.extend(this._userData, data); - } - }, - clone: function() { - var nroom = new HRoom( - this.id, - this.number, - this.capacity, - this.type, - this.shared, - this.price - ); - nroom.overbooking = this.overbooking; - nroom.cancelled = this.cancelled; - nroom._html = this._html; - nroom._active = this._active; - nroom.addUserData(this.getUserData()); - return nroom; - } -}; - -/** RESERVATION OBJECT **/ -function HReservation(/*Dictionary*/rValues) { - if (typeof rValues.room_id === 'undefined' && typeof rValues.room === 'undefined') { - delete this; - console.warn("[Hotel Calendar][HReservation] room can't be empty!"); - return; - } - - this.id = rValues.id; - this.room_id = rValues.room_id; - this.adults = rValues.adults || 1; - this.childrens = rValues.childrens || 0; - this.title = rValues.title || ''; - this.startDate = rValues.startDate || null; - this.endDate = rValues.endDate || null; - this.color = rValues.color || '#000'; - this.colorText = rValues.colorText || '#FFF'; - this.readOnly = rValues.readOnly || false; - this.fixRooms = rValues.fixRooms || false; - this.fixDays = rValues.fixDays || false; - this.unusedZone = rValues.unusedZone || false; - this.linkedId = rValues.linkedId || -1; - this.splitted = rValues.splitted || false; - this.overbooking = rValues.overbooking || false; - this.cancelled = rValues.cancelled || false; - this.room = rValues.room || null; - this.total_reservation = rValues.total_reservation || 0; - this.total_folio = rValues.total_folio || 0; - - this._drawModes = ['hard-start', 'hard-end']; - this._html = false; - this._limits = new HLimit(); - this._beds = []; - this._active = true; - this._userData = {}; -} -HReservation.prototype = { - setRoom: function(/*HRoomObject*/room) { this.room = room; }, - setStartDate: function(/*String,MomentObject*/date) { this.startDate = HotelCalendar.toMoment(date); }, - setEndDate: function(/*String,MomentObject*/date) { this.endDate = HotelCalendar.toMoment(date); }, - - clearUserData: function() { this._userData = {}; }, - getUserData: function(/*String?*/key) { - if (typeof key === 'undefined') { - return this._userData; - } - return key in this._userData && this._userData[key] || null; - }, - addUserData: function(/*Dictionary*/data) { - if (!_.isObject(data)) { - console.warn("[Hotel Calendar][HReservation][setUserData] Invalid Data! Need be a object!"); - } else { - this._userData = _.extend(this._userData, data); - } - }, - getTotalPersons: function(/*Boolean*/countChildrens) { - var persons = this.adults; - if (countChildrens) { - persons += this.childrens; - } - return persons; - }, - clone: function() { - var nreserv = new HReservation({ - 'id': this.id, - 'room': this.room?this.room.clone():null, - 'adults': this.adults, - 'childrens': this.childrens, - 'title': this.title, - 'startDate': this.startDate.clone(), - 'endDate': this.endDate.clone(), - 'color': this.color, - 'colorText': this.colorText, - 'readOnly': this.readOnly, - 'fixRooms': this.fixRooms, - 'fixDays': this.fixDays, - 'unusedZone': this.unusedZone, - 'linkedId': this.linkedId, - 'splitted': this.splitted, - 'overbooking': this.overbooking, - 'cancelled': this.cancelled, - 'room_id': this.room_id, - 'total_reservation': this.total_reservation, - 'total_folio': this.total_folio, - }); - nreserv._beds = _.clone(this._beds); - nreserv._html = this._html; - nreserv._drawModes = _.clone(this._drawModes); - nreserv._limits = this._limits.clone(); - nreserv._active = this._active; - nreserv.addUserData(this.getUserData()); - return nreserv; - } -}; - -/** LIMIT OBJECT **/ -function HLimit(/*HTMLObject*/left, /*HMTLObject*/right) { - this.left = left; - this.right = right; -} -HLimit.prototype = { - isSame: function() { - return this.left == this.right; - }, - isValid: function() { - return this.left && this.right; - }, - swap: function() { - var tt = this.left; - this.left = this.right; - this.right = tt; - }, - clone: function() { - return new HLimit(this.left, this.right); - } -}; diff --git a/hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js b/hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js deleted file mode 100644 index 35d28cca1..000000000 --- a/hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js +++ /dev/null @@ -1,1147 +0,0 @@ -/* global _, moment */ -'use strict'; -/* - * Hotel Calendar Management JS v0.0.1a - 2017-2018 - * GNU Public License - * Alexandre Díaz - * - * Dependencies: - * - moment - * - underscore - * - jquery !shit - * - bootbox !shit - * - bootstrap !shit - */ - -function HotelCalendarManagement(/*String*/querySelector, /*Dictionary*/options, /*HTMLObject?*/_base) { - if (window === this) { - return new HotelCalendarManagement(querySelector, options, _base); - } - - this.$base = (_base === 'undefined') ? document : _base; - - if (typeof querySelector === 'string') { - this.e = this.$base.querySelector(querySelector); - if (!this.e) { - return false; - } - } else if (typeof querySelector === 'object') { - this.e = querySelector; - } else { - return { - Version: '0.0.1a', - Author: "Alexandre Díaz", - Created: "24/09/2017", - Updated: "21/04/2018" - }; - } - - /** Strings **/ - this._strings = { - 'Open': 'Open', - 'Closed': 'Closed', - 'C. Departure': 'C. Departure', - 'C. Arrival': 'C. Arrival', - 'Price': 'Price', - 'Availability': 'Availability', - 'Min. Stay': 'Min. Stay', - 'Max. Stay': 'Max. Stay', - 'Min. Stay Arrival': 'Min. Stay Arrival', - 'Max. Stay Arrival': 'Max. Stay Arrival', - 'Clousure': 'Clousure', - 'Free Rooms': 'Free Rooms', - 'No OTA': 'No OTA', - 'Options': 'Options', - 'Reset': 'Reset', - 'Copy': 'Copy', - 'Paste': 'Paste', - 'Clone': 'Clone', - 'Cancel': 'Cancel' - }; - - /** Options **/ - if (!options) { options = {}; } - this.options = { - startDate: moment(options.startDate || new Date()), - days: options.days || moment(options.startDate || new Date()).daysInMonth(), - rooms: options.rooms || [], - endOfWeek: options.endOfWeek || 6, - endOfWeekOffset: options.endOfWeekOffset || 0, - currencySymbol: options.currencySymbol || '€', - dateFormatLong: options.dateFormat || 'YYYY-MM-DD HH:mm:ss', - dateFormatShort: options.dateFormat || 'YYYY-MM-DD', - translations: options.translations || [] - }; - - // Check correct values - if (this.options.rooms.length > 0 && !(this.options.rooms[0] instanceof HRoomType)) { - this.options.rooms = []; - console.warn("[Hotel Calendar Management][init] Invalid Room definiton!"); - } - - // Merge Transalations - for (var key in this.options.translations) { - this._strings[key] = this.options.translations[key]; - } - - /** Internal Values **/ - this.tableCreated = false; - this._pricelist = {}; - this._restrictions = {}; - this._availability = {}; - this._copy_values = {}; - this._mode = HotelCalendarManagement.MODE.ALL; - - /***/ - if (!this._create()) { - return false; - } - - return this; -} - -HotelCalendarManagement.prototype = { - /** PUBLIC MEMBERS **/ - addEventListener: function(/*String*/event, /*Function*/callback) { - this.e.addEventListener(event, callback); - }, - - hasChangesToSave: function() { - return this.e.querySelector('.hcal-management-record-changed') !== null; - }, - - //==== CALENDAR - setStartDate: function(/*String,MomentObject*/date, /*Int?*/days) { - var curDate = this.options.startDate; - if (moment.isMoment(date)) { - this.options.startDate = date; - } else if (typeof date === 'string'){ - this.options.startDate = moment(date); - } else { - console.warn("[Hotel Calendar Management][setStartDate] Invalid date format!"); - return; - } - - if (typeof days !== 'undefined') { - this.options.days = days; - } - - /*this.e.dispatchEvent(new CustomEvent( - 'hcOnChangeDate', - {'detail': {'prevDate':curDate, 'newDate': $this.options.startDate}}));*/ - this._updateView(); - }, - - getOptions: function(/*String?*/key) { - if (typeof key !== 'undefined') { - return this.options[key]; - } - return this.options; - }, - - setMode: function(/*Int*/mode) { - if (typeof mode === 'undefined') { - mode = this._mode; - } - if (mode === HotelCalendarManagement.MODE.LOW) { - this.etable.classList.remove('hcal-management-medium'); - this.etable.classList.add('hcal-management-low'); - this.edivrhl.classList.remove('hcal-management-medium'); - this.edivrhl.classList.add('hcal-management-low'); - this._mode = HotelCalendarManagement.MODE.LOW; - } else if (mode === HotelCalendarManagement.MODE.MEDIUM) { - this.etable.classList.remove('hcal-management-low'); - this.etable.classList.add('hcal-management-medium'); - this.edivrhl.classList.remove('hcal-management-low'); - this.edivrhl.classList.add('hcal-management-medium'); - this._mode = HotelCalendarManagement.MODE.MEDIUM; - } else { - this.etable.classList.remove('hcal-management-low'); - this.etable.classList.remove('hcal-management-medium'); - this.edivrhl.classList.remove('hcal-management-low'); - this.edivrhl.classList.remove('hcal-management-medium'); - this._mode = HotelCalendarManagement.MODE.ALL; - } - }, - - - /** PRIVATE MEMBERS **/ - //==== MAIN FUNCTIONS - _create: function() { - this.e.innerHTML = ""; - if (this.tableCreated) { - console.warn("[Hotel Calendar Management] Already created!"); - return false; - } - - /** Main Table **/ - this.etable = document.createElement("table"); - this.etable.classList.add('hcal-management-table'); - this.etable.classList.add('noselect'); - this.e.appendChild(this.etable); - this._updateView(); - this.tableCreated = true; - - return true; - }, - - _generateTableDay: function(/*HTMLObject*/parentCell) { - var $this = this; - var table = document.createElement("table"); - table.classList.add('hcal-management-table-day'); - table.classList.add('noselect'); - var row = false; - var cell = false; - var telm = false; - var roomId = $this.$base.querySelector(`#${parentCell.dataset.hcalParentRow}`).dataset.hcalRoomObjId; - var room = $this.getRoom(roomId); - var dateCell = HotelCalendarManagement.toMoment(parentCell.dataset.hcalDate); - var dateShortStr = dateCell.format(HotelCalendarManagement._DATE_FORMAT_SHORT); - - row = table.insertRow(); - row.setAttribute('name', 'price'); - - cell = row.insertCell(); - cell.setAttribute('colspan', '4'); - telm = document.createElement("input"); - telm.setAttribute('id', this._sanitizeId(`PRICE_${roomId}_${dateShortStr}`)); - telm.setAttribute('name', 'price'); - telm.setAttribute('type', 'edit'); - telm.setAttribute('title', this._t('Price')); - telm.value = room.price; - telm.dataset.orgValue = room.price; - telm.dataset.hcalParentCell = parentCell.getAttribute('id'); - telm.classList.add('hcal-management-input'); - telm.addEventListener('change', function(ev){ $this.onInputChange(ev, this); }, false); - cell.appendChild(telm); - - row = table.insertRow(); - row.setAttribute('name', 'avail'); - row.style.display = 'none'; - - cell = row.insertCell(); - cell.setAttribute('colspan', '1'); - telm = document.createElement("input"); - telm.setAttribute('id', this._sanitizeId(`QUOTA_${roomId}_${dateShortStr}`)); - telm.setAttribute('name', 'quota'); - telm.setAttribute('type', 'edit'); - telm.setAttribute('title', this._t('Availability Quota')); - telm.value = telm.dataset.orgValue = 0; - telm.dataset.hcalParentCell = parentCell.getAttribute('id'); - telm.classList.add('hcal-management-input'); - telm.addEventListener('change', function(ev){ $this.onInputChange(ev, this); }, false); - cell.appendChild(telm); - - cell = row.insertCell(); - cell.setAttribute('colspan', '1'); - telm = document.createElement("input"); - telm.setAttribute('id', this._sanitizeId(`MAX_AVAIL_${roomId}_${dateShortStr}`)); - telm.setAttribute('name', 'max_avail'); - telm.setAttribute('type', 'edit'); - telm.setAttribute('title', this._t('Max. Availability')); - telm.value = telm.dataset.orgValue = 0; - telm.dataset.hcalParentCell = parentCell.getAttribute('id'); - telm.classList.add('hcal-management-input'); - telm.addEventListener('change', function(ev){ $this.onInputChange(ev, this); }, false); - cell.appendChild(telm); - - cell = row.insertCell(); - cell.setAttribute('colspan', '1'); - telm = document.createElement("input"); - telm.setAttribute('id', this._sanitizeId(`FREE_ROOMS_${roomId}_${dateShortStr}`)); - telm.setAttribute('name', 'free_rooms'); - telm.setAttribute('type', 'edit'); - telm.setAttribute('title', this._t('Free Rooms')); - telm.setAttribute('readonly', 'readonly'); - telm.setAttribute('disabled', 'disabled'); - telm.style.backgroundColor = 'lightgray'; - telm.dataset.hcalParentCell = parentCell.getAttribute('id'); - cell.appendChild(telm); - - cell = row.insertCell(); - cell.setAttribute('colspan', '1'); - telm = document.createElement("input"); - telm.setAttribute('id', this._sanitizeId(`CHANNEL_AVAIL_${roomId}_${dateShortStr}`)); - telm.setAttribute('name', 'channel_avail'); - telm.setAttribute('type', 'edit'); - telm.setAttribute('title', this._t('Channel Availability')); - telm.setAttribute('readonly', 'readonly'); - telm.setAttribute('disabled', 'disabled'); - telm.value = telm.dataset.orgValue = room.channel_avail; - telm.dataset.hcalParentCell = parentCell.getAttribute('id'); - telm.classList.add('hcal-management-input'); - cell.appendChild(telm); - - row = table.insertRow(); - row.setAttribute('name', 'rest_a'); - - cell = row.insertCell(); - telm = document.createElement("input"); - telm.setAttribute('id', this._sanitizeId(`MIN_STAY_${roomId}_${dateShortStr}`)); - telm.setAttribute('name', 'min_stay'); - telm.setAttribute('type', 'edit'); - telm.setAttribute('title', this._t('Min. Stay')); - telm.dataset.orgValue = telm.value = 0; - telm.dataset.hcalParentCell = parentCell.getAttribute('id'); - telm.classList.add('hcal-management-input'); - telm.classList.add('hcal-border-radius-left'); - telm.addEventListener('change', function(ev){ $this.onInputChange(ev, this); }, false); - cell.appendChild(telm); - - cell = row.insertCell(); - telm = document.createElement("input"); - telm.setAttribute('id', this._sanitizeId(`MAX_STAY_${roomId}_${dateShortStr}`)); - telm.setAttribute('name', 'max_stay'); - telm.setAttribute('type', 'edit'); - telm.setAttribute('title', this._t('Max. Stay')); - telm.dataset.orgValue = telm.value = 0; - telm.dataset.hcalParentCell = parentCell.getAttribute('id'); - telm.classList.add('hcal-management-input'); - telm.classList.add('hcal-border-radius-right'); - telm.addEventListener('change', function(ev){ $this.onInputChange(ev, this); }, false); - cell.appendChild(telm); - - cell = row.insertCell(); - telm = document.createElement("input"); - telm.setAttribute('id', this._sanitizeId(`MIN_STAY_ARRIVAL_${roomId}_${dateShortStr}`)); - telm.setAttribute('name', 'min_stay_arrival'); - telm.setAttribute('type', 'edit'); - telm.setAttribute('title', this._t('Min. Stay Arrival')); - telm.dataset.orgValue = telm.value = 0; - telm.dataset.hcalParentCell = parentCell.getAttribute('id'); - telm.classList.add('hcal-management-input'); - telm.classList.add('hcal-border-radius-left'); - telm.addEventListener('change', function(ev){ $this.onInputChange(ev, this); }, false); - cell.appendChild(telm); - - cell = row.insertCell(); - telm = document.createElement("input"); - telm.setAttribute('id', this._sanitizeId(`MAX_STAY_ARRIVAL_${roomId}_${dateShortStr}`)); - telm.setAttribute('name', 'max_stay_arrival'); - telm.setAttribute('type', 'edit'); - telm.setAttribute('title', this._t('Max. Stay Arrival')); - telm.dataset.orgValue = telm.value = 0; - telm.dataset.hcalParentCell = parentCell.getAttribute('id'); - telm.classList.add('hcal-management-input'); - telm.classList.add('hcal-border-radius-right'); - telm.addEventListener('change', function(ev){ $this.onInputChange(ev, this); }, false); - cell.appendChild(telm); - - row = table.insertRow(); - row.setAttribute('name', 'rest_b'); - cell = row.insertCell(); - cell.setAttribute('colspan', '3'); - telm = document.createElement("select"); - telm.classList.add('hcal-management-input'); - telm.addEventListener('change', function(ev){ $this.onInputChange(ev, this); }, false); - telm.setAttribute('id', this._sanitizeId(`CLOUSURE_${roomId}_${dateShortStr}`)); - telm.setAttribute('name', 'clousure'); - telm.setAttribute('title', this._t('Closure')); - telm.dataset.orgValue = 'open'; - telm.dataset.hcalParentCell = parentCell.getAttribute('id'); - var selectOpt = document.createElement("option"); - selectOpt.value = "open"; - selectOpt.textContent = this._t("Open"); - telm.appendChild(selectOpt); - selectOpt = document.createElement("option"); - selectOpt.value = "closed"; - selectOpt.textContent = this._t("Closed"); - telm.appendChild(selectOpt); - selectOpt = document.createElement("option"); - selectOpt.value = "closed_departure"; - selectOpt.textContent = this._t("C. Departure"); - telm.appendChild(selectOpt); - selectOpt = document.createElement("option"); - selectOpt.value = "closed_arrival"; - selectOpt.textContent = this._t("C. Arrival"); - telm.appendChild(selectOpt); - cell.appendChild(telm); - - row = table.insertRow(); - row.setAttribute('name', 'rest_c'); - - cell = row.insertCell(); - cell.style.textAlign = 'center'; - cell.setAttribute('colspan', '4'); - telm = document.createElement("button"); - telm.setAttribute('id', this._sanitizeId(`NO_OTA_${roomId}_${dateShortStr}`)); - telm.setAttribute('name', 'no_ota'); - telm.setAttribute('title', this._t('No OTA')); - telm.innerHTML = "No OTA"; - telm.dataset.orgValue = telm.dataset.state = 'false'; - telm.dataset.hcalParentCell = parentCell.getAttribute('id'); - telm.classList.add('hcal-management-input', 'pull-left'); - telm.addEventListener('click', function(ev){ $this.onInputChange(ev, this); }, false); - cell.appendChild(telm); - telm = document.createElement("span"); - telm.setAttribute('id', this._sanitizeId(`OPTIONS_${roomId}_${dateShortStr}`)); - telm.setAttribute('name', 'options'); - telm.setAttribute('title', this._t('Options')); - telm.classList.add('dropdown', 'pull-right', 'hcal-management-record-options'); - telm.innerHTML = ` - - `; - telm.dataset.hcalParentCell = parentCell.getAttribute('id'); - cell.appendChild(telm); - - cell.querySelector('.hcal-record-option-clone').addEventListener('click', function(ev){ $this.onOptionsRecord(ev, this); }, false); - cell.querySelector('.hcal-record-option-reset').addEventListener('click', function(ev){ $this.onOptionsRecord(ev, this); }, false); - cell.querySelector('.hcal-record-option-copy').addEventListener('click', function(ev){ $this.onOptionsRecord(ev, this); }, false); - cell.querySelector('.hcal-record-option-paste').addEventListener('click', function(ev){ $this.onOptionsRecord(ev, this); }, false); - - - parentCell.appendChild(table); - - return table; - }, - - _getCell: function(/*HRoomObject*/room, /*DateTimeObject*/sdate) { - return this.e.querySelector(`#${this._sanitizeId(`${room.name}_${room.id}_${sdate.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`)}`); - }, - - setData: function(prices, restrictions, avail, count_free_rooms) { - //this._updateView(); - if (typeof prices !== 'undefined' && prices) { - this._pricelist = prices; - this._updatePriceList(); - } - if (typeof restrictions !== 'undefined' && restrictions) { - this._restrictions = restrictions; - this._updateRestrictions(); - } - if (typeof avail !== 'undefined' && avail) { - this.setAvailability(avail); - } - if (typeof count_free_rooms !== 'undefined' && count_free_rooms) { - this._free_rooms = count_free_rooms; - this._updateNumFreeRooms(); - } - }, - - setAvailability: function(avails) { - this._availability = avails; - if (this._availability) { - for (var elm of this.etable.querySelectorAll("tr[name=avail]")) { - elm.style.display = ""; - } - } - this._updateAvailability(); - }, - - //==== ROOMS - getRoom: function(/*String*/id) { - return _.find(this.options.rooms, function(item){ return item.id == id; }); - }, - - //==== RENDER FUNCTIONS - _create_table_data_days: function() { - var $this = this; - while (this.e.hasChildNodes()) { - this.e.removeChild(this.e.lastChild); - } - - // RoomType Names - this.edivrhl = document.createElement("div"); - this.edivrhl.classList.add('table-room_types'); - this.e.appendChild(this.edivrhl); - this.etableRooms = document.createElement("table"); - this.etableRooms.classList.add('hcal-management-table'); - this.etableRooms.classList.add('noselect'); - this.edivrhl.appendChild(this.etableRooms); - - // Container: Days + Data - this.edivm = document.createElement("div"); - this.edivm.setAttribute('id', 'hcal-management-container-dd'); - this.e.appendChild(this.edivm); - // Days - this.edivrh = document.createElement("div"); - this.edivrh.classList.add('table-room_type-data-header'); - this.edivm.appendChild(this.edivrh); - this.etableHeader = document.createElement("table"); - this.etableHeader.classList.add('hcal-management-table'); - this.etableHeader.classList.add('noselect'); - this.edivrh.appendChild(this.etableHeader); - // Data - this.edivr = document.createElement("div"); - this.edivr.classList.add('table-room_type-data'); - this.edivm.appendChild(this.edivr); - this.etable = document.createElement("table"); - this.etable.classList.add('hcal-management-table'); - this.etable.classList.add('noselect'); - this.edivr.appendChild(this.etable); - - /** TABLE HEADER **/ - var thead = this.etableHeader.createTHead(); - - // Render Next Days - var row = thead.insertRow(); - var now = moment().local(); - for (var i=0; i<=this.options.days; i++) { - var dd = this.options.startDate.clone().add(i,'d'); - var dd_local = dd.clone().local(); - var cell = row.insertCell(); - cell.setAttribute('id', this._sanitizeId(`hday_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`)); - cell.classList.add('hcal-cell-header-day'); - cell.classList.add('btn-hcal'); - cell.classList.add('btn-hcal-3d'); - cell.dataset.hcalDate = dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT); - cell.textContent = dd.format('D') + ' ' + dd.format('ddd') + ' (' + dd.format('MMM') + "'" + dd.format('YY') + ')'; - cell.setAttribute('title', dd.format('dddd')) - var day = +dd_local.format('D'); - if (day == 1) { - cell.classList.add('hcal-cell-start-month'); - } - if (dd_local.isSame(now, 'day')) { - cell.classList.add('hcal-cell-current-day'); - } else if (dd_local.format('e') >= this.options.endOfWeek-this.options.endOfWeekOffset && dd_local.format('e') <= this.options.endOfWeek) { - cell.classList.add('hcal-cell-end-week'); - } - } - - /** ROOM LINES **/ - var tbody = document.createElement("tbody"); - this.etableRooms.appendChild(tbody); - this.options.rooms.forEach(function(itemRoom, indexRoom){ - row = tbody.insertRow(); - cell = row.insertCell(); - cell.textContent = itemRoom.name; - cell.setAttribute('colspan', 2); - cell.classList.add('hcal-cell-room-type-group-item'); - cell.classList.add('btn-hcal'); - cell.classList.add('btn-hcal-3d'); - }); - - /** ROOM DATA LINES **/ - var tbody = document.createElement("tbody"); - this.etable.appendChild(tbody); - this.options.rooms.forEach(function(itemRoom, indexRoom){ - // Room Number - row = tbody.insertRow(); - row.setAttribute('id', $this._sanitizeId(`ROW_${itemRoom.name}_${itemRoom.id}`)); - row.dataset.hcalRoomObjId = itemRoom.id; - row.classList.add('hcal-row-room-type-group-item'); - for (var i=0; i<=$this.options.days; i++) { - var dd = $this.options.startDate.clone().add(i,'d'); - var dd_local = dd.clone().local(); - cell = row.insertCell(); - cell.setAttribute('id', $this._sanitizeId(`${itemRoom.name}_${itemRoom.id}_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`)); - cell.classList.add('hcal-cell-room-type-group-item-day'); - cell.dataset.hcalParentRow = row.getAttribute('id'); - cell.dataset.hcalDate = dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT); - // Generate Interactive Table - cell.appendChild($this._generateTableDay(cell)); - //cell.innerHTML = dd.format("DD"); - var day = +dd_local.format("D"); - if (day == 1) { - cell.classList.add('hcal-cell-start-month'); - } - if (dd_local.isSame(now, 'day')) { - cell.classList.add('hcal-cell-current-day'); - } else if (dd_local.format('e') >= $this.options.endOfWeek-$this.options.endOfWeekOffset && dd_local.format('e') <= $this.options.endOfWeek) { - cell.classList.add('hcal-cell-end-week'); - } - } - }); - }, - - //==== PRICELIST - addPricelist: function(/*Object*/pricelist) { - var room_type_ids = Object.keys(pricelist); - for (var vid of room_type_ids) { - if (vid in this._pricelist) { - for (var price of pricelist[vid]) { - var index = _.findIndex(this._pricelist[vid], {date: price['date']}); - if (index >= 0) { - this._pricelist[vid][index] = price; - } else { - this._pricelist[vid].push(price); - } - } - } - else { - this._pricelist[vid] = pricelist[vid]; - } - } - this._updatePriceList(); - }, - - _updatePriceList: function() { - var keys = Object.keys(this._pricelist); - for (var room_typeId of keys) { - for (var price of this._pricelist[room_typeId]) { - var dd = HotelCalendarManagement.toMoment(price.date, this.options.dateFormatShort); - var inputId = this._sanitizeId(`PRICE_${room_typeId}_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`); - var input = this.etable.querySelector(`#${inputId}`); - if (input && !input.classList.contains('hcal-management-input-changed')) { - input.dataset.orgValue = price.price; - input.value = price.price; - } - } - } - }, - - getPricelist: function(onlyNew) { - var data = {}; - for (var room of this.options.rooms) { - for (var i=0; i<=this.options.days; i++) { - var ndate = this.options.startDate.clone().add(i, 'd'); - var ndateStr = ndate.format(HotelCalendarManagement._DATE_FORMAT_SHORT); - var inputId = this._sanitizeId(`PRICE_${room.id}_${ndateStr}`); - var input = this.etable.querySelector(`#${inputId}`); - if (!onlyNew || (onlyNew && input.value !== input.dataset.orgValue)) { - if (!(room.id in data)) { data[room.id] = []; } - data[room.id].push({ - 'date': ndate.format('YYYY-MM-DD'), - 'price': input.value - }); - } - } - } - return data; - }, - - //==== RESTRICTIONS - addRestrictions: function(/*Object*/restrictions) { - var room_type_ids = Object.keys(restrictions); - for (var vid of room_type_ids) { - if (vid in this._restrictions) { - for (var rest of restrictions[vid]) { - var index = _.findIndex(this._restrictions[vid], {date: rest['date']}); - if (index >= 0) { - this._restrictions[vid][index] = rest; - } else { - this._restrictions[vid].push(rest); - } - } - } - else { - this._restrictions[vid] = restrictions[vid]; - } - } - this._updateRestrictions(); - }, - - _updateRestrictions: function() { - var keys = Object.keys(this._restrictions); - for (var room_typeId of keys) { - var room = this.getRoom(room_typeId); - for (var restriction of this._restrictions[room_typeId]) { - var dd = HotelCalendarManagement.toMoment(restriction.date, this.options.dateFormatShort); - var inputIds = [ - this._sanitizeId(`MIN_STAY_${room_typeId}_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`), restriction.min_stay, - this._sanitizeId(`MIN_STAY_ARRIVAL_${room_typeId}_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`), restriction.min_stay_arrival, - this._sanitizeId(`MAX_STAY_${room_typeId}_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`), restriction.max_stay, - this._sanitizeId(`MAX_STAY_ARRIVAL_${room_typeId}_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`), restriction.max_stay_arrival, - ]; - for (var i=0; i= 0) { - this._availability[vid][index] = avail; - } else { - this._availability[vid].push(avail); - } - } - } - else { - this._availability[vid] = availability[vid]; - } - } - this._updateAvailability(); - }, - - _updateAvailability: function() { - var keys = Object.keys(this._availability); - for (var room_typeId of keys) { - for (var avail of this._availability[room_typeId]) { - var dd = HotelCalendarManagement.toMoment(avail.date, this.options.dateFormatShort); - var inputIds = [ - `QUOTA_${room_typeId}_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`, avail.quota, - `MAX_AVAIL_${room_typeId}_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`, avail.max_avail, - `CHANNEL_AVAIL_${room_typeId}_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`, avail.channel_avail, - `NO_OTA_${room_typeId}_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`, avail.no_ota - ]; - for (var i=0; i - - - -
      - Price: ${copy_values['price']}
      - Availability: ${copy_values['avail']}
      - Min. Stay: ${copy_values['min_stay']}
      - Max. Stay: ${copy_values['max_stay']}
      - Min. Stay Arrival: ${copy_values['min_stay_arrival']}
      - Max. Stay Arrival: ${copy_values['max_stay_arrival']}
      - Clousure: ${copy_values['clousure']}
      - No OTA: ${copy_values['no_ota']}
      -
      - - - - - FROM
      -
      - - - - -
      - - - TO
      -
      - - - - -
      - - - - - - - - - - `, - buttons: { - confirm : { - label: $this._t('Clone'), - className: "btn-success", - callback: function() { - var date_begin = $('table#hcal-management-clone-dates #date_begin').data("DateTimePicker").getDate().set({'hour': 0, 'minute': 0, 'second': 0}).clone(); - var date_end = $('table#hcal-management-clone-dates #date_end').data("DateTimePicker").getDate().set({'hour': 0, 'minute': 0, 'second': 0}).clone(); - var diff_days = $this.getDateDiffDays(date_begin, date_end) + 1; - var same_day = $('table#hcal-management-clone-dates #same_day').is(':checked'); - var cell_date = HotelCalendarManagement.toMoment(parentCell.dataset.hcalDate); - - var ndate = date_begin.clone(); - for (var i=0; i0)for(c in rd)d=rd[c],e=b[d],p(e)||(a[d]=e);return a} -// Moment prototype object -function r(b){q(this,b),this._d=new Date(null!=b._d?b._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)), -// Prevent infinite loop in case updateOffset creates new moment -// objects. -sd===!1&&(sd=!0,a.updateOffset(this),sd=!1)}function s(a){return a instanceof r||null!=a&&null!=a._isAMomentObject}function t(a){return a<0?Math.ceil(a)||0:Math.floor(a)}function u(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=t(b)),c} -// compare two arrays, return the number of differences -function v(a,b,c){var d,e=Math.min(a.length,b.length),f=Math.abs(a.length-b.length),g=0;for(d=0;d0?"future":"past"];return z(c)?c(b):c.replace(/%s/i,b)}function J(a,b){var c=a.toLowerCase();Dd[c]=Dd[c+"s"]=Dd[b]=a}function K(a){return"string"==typeof a?Dd[a]||Dd[a.toLowerCase()]:void 0}function L(a){var b,c,d={};for(c in a)i(a,c)&&(b=K(c),b&&(d[b]=a[c]));return d}function M(a,b){Ed[a]=b}function N(a){var b=[];for(var c in a)b.push({unit:c,priority:Ed[c]});return b.sort(function(a,b){return a.priority-b.priority}),b}function O(b,c){return function(d){return null!=d?(Q(this,b,d),a.updateOffset(this,c),this):P(this,b)}}function P(a,b){return a.isValid()?a._d["get"+(a._isUTC?"UTC":"")+b]():NaN}function Q(a,b,c){a.isValid()&&a._d["set"+(a._isUTC?"UTC":"")+b](c)} -// MOMENTS -function R(a){return a=K(a),z(this[a])?this[a]():this}function S(a,b){if("object"==typeof a){a=L(a);for(var c=N(a),d=0;d=0;return(f?c?"+":"":"-")+Math.pow(10,Math.max(0,e)).toString().substr(1)+d} -// token: 'M' -// padded: ['MM', 2] -// ordinal: 'Mo' -// callback: function () { this.month() + 1 } -function U(a,b,c,d){var e=d;"string"==typeof d&&(e=function(){return this[d]()}),a&&(Id[a]=e),b&&(Id[b[0]]=function(){return T(e.apply(this,arguments),b[1],b[2])}),c&&(Id[c]=function(){return this.localeData().ordinal(e.apply(this,arguments),a)})}function V(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function W(a){var b,c,d=a.match(Fd);for(b=0,c=d.length;b=0&&Gd.test(a);)a=a.replace(Gd,c),Gd.lastIndex=0,d-=1;return a}function Z(a,b,c){$d[a]=z(b)?b:function(a,d){return a&&c?c:b}}function $(a,b){return i($d,a)?$d[a](b._strict,b._locale):new RegExp(_(a))} -// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript -function _(a){return aa(a.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e}))}function aa(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function ba(a,b){var c,d=b;for("string"==typeof a&&(a=[a]),f(b)&&(d=function(a,c){c[b]=u(a)}),c=0;c=0&&isFinite(h.getFullYear())&&h.setFullYear(a),h}function ta(a){var b=new Date(Date.UTC.apply(null,arguments)); -//the Date.UTC function remaps years 0-99 to 1900-1999 -return a<100&&a>=0&&isFinite(b.getUTCFullYear())&&b.setUTCFullYear(a),b} -// start-of-first-week - start-of-year -function ua(a,b,c){var// first-week day -- which january is always in the first week (4 for iso, 1 for other) -d=7+b-c, -// first-week day local weekday -- which local weekday is fwd -e=(7+ta(a,0,d).getUTCDay()-b)%7;return-e+d-1} -//http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday -function va(a,b,c,d,e){var f,g,h=(7+c-d)%7,i=ua(a,d,e),j=1+7*(b-1)+h+i;return j<=0?(f=a-1,g=pa(f)+j):j>pa(a)?(f=a+1,g=j-pa(a)):(f=a,g=j),{year:f,dayOfYear:g}}function wa(a,b,c){var d,e,f=ua(a.year(),b,c),g=Math.floor((a.dayOfYear()-f-1)/7)+1;return g<1?(e=a.year()-1,d=g+xa(e,b,c)):g>xa(a.year(),b,c)?(d=g-xa(a.year(),b,c),e=a.year()+1):(e=a.year(),d=g),{week:d,year:e}}function xa(a,b,c){var d=ua(a,b,c),e=ua(a+1,b,c);return(pa(a)-d+e)/7} -// HELPERS -// LOCALES -function ya(a){return wa(a,this._week.dow,this._week.doy).week}function za(){return this._week.dow}function Aa(){return this._week.doy} -// MOMENTS -function Ba(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")}function Ca(a){var b=wa(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")} -// HELPERS -function Da(a,b){return"string"!=typeof a?a:isNaN(a)?(a=b.weekdaysParse(a),"number"==typeof a?a:null):parseInt(a,10)}function Ea(a,b){return"string"==typeof a?b.weekdaysParse(a)%7||7:isNaN(a)?null:a}function Fa(a,b){return a?c(this._weekdays)?this._weekdays[a.day()]:this._weekdays[this._weekdays.isFormat.test(b)?"format":"standalone"][a.day()]:this._weekdays}function Ga(a){return a?this._weekdaysShort[a.day()]:this._weekdaysShort}function Ha(a){return a?this._weekdaysMin[a.day()]:this._weekdaysMin}function Ia(a,b,c){var d,e,f,g=a.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],d=0;d<7;++d)f=k([2e3,1]).day(d),this._minWeekdaysParse[d]=this.weekdaysMin(f,"").toLocaleLowerCase(),this._shortWeekdaysParse[d]=this.weekdaysShort(f,"").toLocaleLowerCase(),this._weekdaysParse[d]=this.weekdays(f,"").toLocaleLowerCase();return c?"dddd"===b?(e=je.call(this._weekdaysParse,g),e!==-1?e:null):"ddd"===b?(e=je.call(this._shortWeekdaysParse,g),e!==-1?e:null):(e=je.call(this._minWeekdaysParse,g),e!==-1?e:null):"dddd"===b?(e=je.call(this._weekdaysParse,g),e!==-1?e:(e=je.call(this._shortWeekdaysParse,g),e!==-1?e:(e=je.call(this._minWeekdaysParse,g),e!==-1?e:null))):"ddd"===b?(e=je.call(this._shortWeekdaysParse,g),e!==-1?e:(e=je.call(this._weekdaysParse,g),e!==-1?e:(e=je.call(this._minWeekdaysParse,g),e!==-1?e:null))):(e=je.call(this._minWeekdaysParse,g),e!==-1?e:(e=je.call(this._weekdaysParse,g),e!==-1?e:(e=je.call(this._shortWeekdaysParse,g),e!==-1?e:null)))}function Ja(a,b,c){var d,e,f;if(this._weekdaysParseExact)return Ia.call(this,a,b,c);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),d=0;d<7;d++){ -// test the regex -if( -// make the regex if we don't have it already -e=k([2e3,1]).day(d),c&&!this._fullWeekdaysParse[d]&&(this._fullWeekdaysParse[d]=new RegExp("^"+this.weekdays(e,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[d]=new RegExp("^"+this.weekdaysShort(e,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[d]=new RegExp("^"+this.weekdaysMin(e,"").replace(".",".?")+"$","i")),this._weekdaysParse[d]||(f="^"+this.weekdays(e,"")+"|^"+this.weekdaysShort(e,"")+"|^"+this.weekdaysMin(e,""),this._weekdaysParse[d]=new RegExp(f.replace(".",""),"i")),c&&"dddd"===b&&this._fullWeekdaysParse[d].test(a))return d;if(c&&"ddd"===b&&this._shortWeekdaysParse[d].test(a))return d;if(c&&"dd"===b&&this._minWeekdaysParse[d].test(a))return d;if(!c&&this._weekdaysParse[d].test(a))return d}} -// MOMENTS -function Ka(a){if(!this.isValid())return null!=a?this:NaN;var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=Da(a,this.localeData()),this.add(a-b,"d")):b}function La(a){if(!this.isValid())return null!=a?this:NaN;var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")}function Ma(a){if(!this.isValid())return null!=a?this:NaN; -// behaves the same as moment#day except -// as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) -// as a setter, sunday should belong to the previous week. -if(null!=a){var b=Ea(a,this.localeData());return this.day(this.day()%7?b:b-7)}return this.day()||7}function Na(a){return this._weekdaysParseExact?(i(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysStrictRegex:this._weekdaysRegex):(i(this,"_weekdaysRegex")||(this._weekdaysRegex=ue),this._weekdaysStrictRegex&&a?this._weekdaysStrictRegex:this._weekdaysRegex)}function Oa(a){return this._weekdaysParseExact?(i(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(i(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=ve),this._weekdaysShortStrictRegex&&a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)}function Pa(a){return this._weekdaysParseExact?(i(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(i(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=we),this._weekdaysMinStrictRegex&&a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)}function Qa(){function a(a,b){return b.length-a.length}var b,c,d,e,f,g=[],h=[],i=[],j=[];for(b=0;b<7;b++) -// make the regex if we don't have it already -c=k([2e3,1]).day(b),d=this.weekdaysMin(c,""),e=this.weekdaysShort(c,""),f=this.weekdays(c,""),g.push(d),h.push(e),i.push(f),j.push(d),j.push(e),j.push(f);for( -// Sorting makes sure if one weekday (or abbr) is a prefix of another it -// will match the longer piece. -g.sort(a),h.sort(a),i.sort(a),j.sort(a),b=0;b<7;b++)h[b]=aa(h[b]),i[b]=aa(i[b]),j[b]=aa(j[b]);this._weekdaysRegex=new RegExp("^("+j.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+h.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+g.join("|")+")","i")} -// FORMATTING -function Ra(){return this.hours()%12||12}function Sa(){return this.hours()||24}function Ta(a,b){U(a,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),b)})} -// PARSING -function Ua(a,b){return b._meridiemParse} -// LOCALES -function Va(a){ -// IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays -// Using charAt should be more compatible. -return"p"===(a+"").toLowerCase().charAt(0)}function Wa(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"}function Xa(a){return a?a.toLowerCase().replace("_","-"):a} -// pick the locale from the array -// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each -// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root -function Ya(a){for(var b,c,d,e,f=0;f0;){if(d=Za(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&v(e,c,!0)>=b-1) -//the next array item is better than a shallower substring of this one -break;b--}f++}return null}function Za(a){var b=null; -// TODO: Find a better way to register and load all the locales in Node -if(!Be[a]&&"undefined"!=typeof module&&module&&module.exports)try{b=xe._abbr,require("./locale/"+a), -// because defineLocale currently also sets the global locale, we -// want to undo that for lazy loaded locales -$a(b)}catch(a){}return Be[a]} -// This function will load locale and then set the global locale. If -// no arguments are passed in, it will simply return the current global -// locale key. -function $a(a,b){var c; -// moment.duration._locale = moment._locale = data; -return a&&(c=p(b)?bb(a):_a(a,b),c&&(xe=c)),xe._abbr}function _a(a,b){if(null!==b){var c=Ae;if(b.abbr=a,null!=Be[a])y("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),c=Be[a]._config;else if(null!=b.parentLocale){if(null==Be[b.parentLocale])return Ce[b.parentLocale]||(Ce[b.parentLocale]=[]),Ce[b.parentLocale].push({name:a,config:b}),null;c=Be[b.parentLocale]._config} -// backwards compat for now: also set the locale -// make sure we set the locale AFTER all child locales have been -// created, so we won't end up with the child locale set. -return Be[a]=new C(B(c,b)),Ce[a]&&Ce[a].forEach(function(a){_a(a.name,a.config)}),$a(a),Be[a]} -// useful for testing -return delete Be[a],null}function ab(a,b){if(null!=b){var c,d=Ae; -// MERGE -null!=Be[a]&&(d=Be[a]._config),b=B(d,b),c=new C(b),c.parentLocale=Be[a],Be[a]=c, -// backwards compat for now: also set the locale -$a(a)}else -// pass null for config to unupdate, useful for tests -null!=Be[a]&&(null!=Be[a].parentLocale?Be[a]=Be[a].parentLocale:null!=Be[a]&&delete Be[a]);return Be[a]} -// returns locale data -function bb(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return xe;if(!c(a)){if( -//short-circuit everything else -b=Za(a))return b;a=[a]}return Ya(a)}function cb(){return wd(Be)}function db(a){var b,c=a._a;return c&&m(a).overflow===-2&&(b=c[be]<0||c[be]>11?be:c[ce]<1||c[ce]>ea(c[ae],c[be])?ce:c[de]<0||c[de]>24||24===c[de]&&(0!==c[ee]||0!==c[fe]||0!==c[ge])?de:c[ee]<0||c[ee]>59?ee:c[fe]<0||c[fe]>59?fe:c[ge]<0||c[ge]>999?ge:-1,m(a)._overflowDayOfYear&&(bce)&&(b=ce),m(a)._overflowWeeks&&b===-1&&(b=he),m(a)._overflowWeekday&&b===-1&&(b=ie),m(a).overflow=b),a} -// date from iso format -function eb(a){var b,c,d,e,f,g,h=a._i,i=De.exec(h)||Ee.exec(h);if(i){for(m(a).iso=!0,b=0,c=Ge.length;bpa(e)&&(m(a)._overflowDayOfYear=!0),c=ta(e,0,a._dayOfYear),a._a[be]=c.getUTCMonth(),a._a[ce]=c.getUTCDate()),b=0;b<3&&null==a._a[b];++b)a._a[b]=f[b]=d[b]; -// Zero out whatever was not defaulted, including time -for(;b<7;b++)a._a[b]=f[b]=null==a._a[b]?2===b?1:0:a._a[b]; -// Check for 24:00:00.000 -24===a._a[de]&&0===a._a[ee]&&0===a._a[fe]&&0===a._a[ge]&&(a._nextDay=!0,a._a[de]=0),a._d=(a._useUTC?ta:sa).apply(null,f), -// Apply timezone offset from input. The actual utcOffset can be changed -// with parseZone. -null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()-a._tzm),a._nextDay&&(a._a[de]=24)}}function jb(a){var b,c,d,e,f,g,h,i;if(b=a._w,null!=b.GG||null!=b.W||null!=b.E)f=1,g=4, -// TODO: We need to take the current isoWeekYear, but that depends on -// how we interpret now (local, utc, fixed offset). So create -// a now version of current config (take local/utc/offset flags, and -// create now). -c=gb(b.GG,a._a[ae],wa(sb(),1,4).year),d=gb(b.W,1),e=gb(b.E,1),(e<1||e>7)&&(i=!0);else{f=a._locale._week.dow,g=a._locale._week.doy;var j=wa(sb(),f,g);c=gb(b.gg,a._a[ae],j.year), -// Default to current week. -d=gb(b.w,j.week),null!=b.d?( -// weekday -- low day numbers are considered next week -e=b.d,(e<0||e>6)&&(i=!0)):null!=b.e?( -// local weekday -- counting starts from begining of week -e=b.e+f,(b.e<0||b.e>6)&&(i=!0)): -// default to begining of week -e=f}d<1||d>xa(c,f,g)?m(a)._overflowWeeks=!0:null!=i?m(a)._overflowWeekday=!0:(h=va(c,d,e,f,g),a._a[ae]=h.year,a._dayOfYear=h.dayOfYear)} -// date from string and format string -function kb(b){ -// TODO: Move this to another part of the creation flow to prevent circular deps -if(b._f===a.ISO_8601)return void eb(b);b._a=[],m(b).empty=!0; -// This array is used to make a Date, either with `new Date` or `Date.UTC` -var c,d,e,f,g,h=""+b._i,i=h.length,j=0;for(e=Y(b._f,b._locale).match(Fd)||[],c=0;c0&&m(b).unusedInput.push(g),h=h.slice(h.indexOf(d)+d.length),j+=d.length), -// don't parse if it's not a known token -Id[f]?(d?m(b).empty=!1:m(b).unusedTokens.push(f),da(f,d,b)):b._strict&&!d&&m(b).unusedTokens.push(f); -// add remaining unparsed input length to the string -m(b).charsLeftOver=i-j,h.length>0&&m(b).unusedInput.push(h), -// clear _12h flag if hour is <= 12 -b._a[de]<=12&&m(b).bigHour===!0&&b._a[de]>0&&(m(b).bigHour=void 0),m(b).parsedDateParts=b._a.slice(0),m(b).meridiem=b._meridiem, -// handle meridiem -b._a[de]=lb(b._locale,b._a[de],b._meridiem),ib(b),db(b)}function lb(a,b,c){var d; -// Fallback -return null==c?b:null!=a.meridiemHour?a.meridiemHour(b,c):null!=a.isPM?(d=a.isPM(c),d&&b<12&&(b+=12),d||12!==b||(b=0),b):b} -// date from string and array of format strings -function mb(a){var b,c,d,e,f;if(0===a._f.length)return m(a).invalidFormat=!0,void(a._d=new Date(NaN));for(e=0;e -// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset -// +0200, so we adjust the time as needed, to be valid. -// -// Keeping the time actually adds/subtracts (one hour) -// from the actual represented time. That is why we call updateOffset -// a second time. In case it wants us to change the offset again -// _changeInProgress == true case, then we have to adjust, because -// there is no such time in the given timezone. -function Db(b,c){var d,e=this._offset||0;if(!this.isValid())return null!=b?this:NaN;if(null!=b){if("string"==typeof b){if(b=Ab(Xd,b),null===b)return this}else Math.abs(b)<16&&(b=60*b);return!this._isUTC&&c&&(d=Cb(this)),this._offset=b,this._isUTC=!0,null!=d&&this.add(d,"m"),e!==b&&(!c||this._changeInProgress?Tb(this,Ob(b-e,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this}return this._isUTC?e:Cb(this)}function Eb(a,b){return null!=a?("string"!=typeof a&&(a=-a),this.utcOffset(a,b),this):-this.utcOffset()}function Fb(a){return this.utcOffset(0,a)}function Gb(a){return this._isUTC&&(this.utcOffset(0,a),this._isUTC=!1,a&&this.subtract(Cb(this),"m")),this}function Hb(){if(null!=this._tzm)this.utcOffset(this._tzm);else if("string"==typeof this._i){var a=Ab(Wd,this._i);null!=a?this.utcOffset(a):this.utcOffset(0,!0)}return this}function Ib(a){return!!this.isValid()&&(a=a?sb(a).utcOffset():0,(this.utcOffset()-a)%60===0)}function Jb(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Kb(){if(!p(this._isDSTShifted))return this._isDSTShifted;var a={};if(q(a,this),a=pb(a),a._a){var b=a._isUTC?k(a._a):sb(a._a);this._isDSTShifted=this.isValid()&&v(a._a,b.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Lb(){return!!this.isValid()&&!this._isUTC}function Mb(){return!!this.isValid()&&this._isUTC}function Nb(){return!!this.isValid()&&(this._isUTC&&0===this._offset)}function Ob(a,b){var c,d,e,g=a, -// matching against regexp is expensive, do it on demand -h=null;// checks for null or undefined -return xb(a)?g={ms:a._milliseconds,d:a._days,M:a._months}:f(a)?(g={},b?g[b]=a:g.milliseconds=a):(h=Ne.exec(a))?(c="-"===h[1]?-1:1,g={y:0,d:u(h[ce])*c,h:u(h[de])*c,m:u(h[ee])*c,s:u(h[fe])*c,ms:u(yb(1e3*h[ge]))*c}):(h=Oe.exec(a))?(c="-"===h[1]?-1:1,g={y:Pb(h[2],c),M:Pb(h[3],c),w:Pb(h[4],c),d:Pb(h[5],c),h:Pb(h[6],c),m:Pb(h[7],c),s:Pb(h[8],c)}):null==g?g={}:"object"==typeof g&&("from"in g||"to"in g)&&(e=Rb(sb(g.from),sb(g.to)),g={},g.ms=e.milliseconds,g.M=e.months),d=new wb(g),xb(a)&&i(a,"_locale")&&(d._locale=a._locale),d}function Pb(a,b){ -// We'd normally use ~~inp for this, but unfortunately it also -// converts floats to ints. -// inp may be undefined, so careful calling replace on it. -var c=a&&parseFloat(a.replace(",",".")); -// apply sign while we're at it -return(isNaN(c)?0:c)*b}function Qb(a,b){var c={milliseconds:0,months:0};return c.months=b.month()-a.month()+12*(b.year()-a.year()),a.clone().add(c.months,"M").isAfter(b)&&--c.months,c.milliseconds=+b-+a.clone().add(c.months,"M"),c}function Rb(a,b){var c;return a.isValid()&&b.isValid()?(b=Bb(b,a),a.isBefore(b)?c=Qb(a,b):(c=Qb(b,a),c.milliseconds=-c.milliseconds,c.months=-c.months),c):{milliseconds:0,months:0}} -// TODO: remove 'name' arg after deprecation is removed -function Sb(a,b){return function(c,d){var e,f; -//invert the arguments, but complain about it -return null===d||isNaN(+d)||(y(b,"moment()."+b+"(period, number) is deprecated. Please use moment()."+b+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),f=c,c=d,d=f),c="string"==typeof c?+c:c,e=Ob(c,d),Tb(this,e,a),this}}function Tb(b,c,d,e){var f=c._milliseconds,g=yb(c._days),h=yb(c._months);b.isValid()&&(e=null==e||e,f&&b._d.setTime(b._d.valueOf()+f*d),g&&Q(b,"Date",P(b,"Date")+g*d),h&&ja(b,P(b,"Month")+h*d),e&&a.updateOffset(b,g||h))}function Ub(a,b){var c=a.diff(b,"days",!0);return c<-6?"sameElse":c<-1?"lastWeek":c<0?"lastDay":c<1?"sameDay":c<2?"nextDay":c<7?"nextWeek":"sameElse"}function Vb(b,c){ -// We want to compare the start of today, vs this. -// Getting start-of-today depends on whether we're local/utc/offset or not. -var d=b||sb(),e=Bb(d,this).startOf("day"),f=a.calendarFormat(this,e)||"sameElse",g=c&&(z(c[f])?c[f].call(this,d):c[f]);return this.format(g||this.localeData().calendar(f,this,sb(d)))}function Wb(){return new r(this)}function Xb(a,b){var c=s(a)?a:sb(a);return!(!this.isValid()||!c.isValid())&&(b=K(p(b)?"millisecond":b),"millisecond"===b?this.valueOf()>c.valueOf():c.valueOf()f&&(b=f),Fc.call(this,a,b,c,d,e))}function Fc(a,b,c,d,e){var f=va(a,b,c,d,e),g=ta(f.year,0,f.dayOfYear);return this.year(g.getUTCFullYear()),this.month(g.getUTCMonth()),this.date(g.getUTCDate()),this} -// MOMENTS -function Gc(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)} -// HELPERS -// MOMENTS -function Hc(a){var b=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")}function Ic(a,b){b[ge]=u(1e3*("0."+a))} -// MOMENTS -function Jc(){return this._isUTC?"UTC":""}function Kc(){return this._isUTC?"Coordinated Universal Time":""}function Lc(a){return sb(1e3*a)}function Mc(){return sb.apply(null,arguments).parseZone()}function Nc(a){return a}function Oc(a,b,c,d){var e=bb(),f=k().set(d,b);return e[c](f,a)}function Pc(a,b,c){if(f(a)&&(b=a,a=void 0),a=a||"",null!=b)return Oc(a,b,c,"month");var d,e=[];for(d=0;d<12;d++)e[d]=Oc(a,d,c,"month");return e} -// () -// (5) -// (fmt, 5) -// (fmt) -// (true) -// (true, 5) -// (true, fmt, 5) -// (true, fmt) -function Qc(a,b,c,d){"boolean"==typeof a?(f(b)&&(c=b,b=void 0),b=b||""):(b=a,c=b,a=!1,f(b)&&(c=b,b=void 0),b=b||"");var e=bb(),g=a?e._week.dow:0;if(null!=c)return Oc(b,(c+g)%7,d,"day");var h,i=[];for(h=0;h<7;h++)i[h]=Oc(b,(h+g)%7,d,"day");return i}function Rc(a,b){return Pc(a,b,"months")}function Sc(a,b){return Pc(a,b,"monthsShort")}function Tc(a,b,c){return Qc(a,b,c,"weekdays")}function Uc(a,b,c){return Qc(a,b,c,"weekdaysShort")}function Vc(a,b,c){return Qc(a,b,c,"weekdaysMin")}function Wc(){var a=this._data;return this._milliseconds=Ze(this._milliseconds),this._days=Ze(this._days),this._months=Ze(this._months),a.milliseconds=Ze(a.milliseconds),a.seconds=Ze(a.seconds),a.minutes=Ze(a.minutes),a.hours=Ze(a.hours),a.months=Ze(a.months),a.years=Ze(a.years),this}function Xc(a,b,c,d){var e=Ob(b,c);return a._milliseconds+=d*e._milliseconds,a._days+=d*e._days,a._months+=d*e._months,a._bubble()} -// supports only 2.0-style add(1, 's') or add(duration) -function Yc(a,b){return Xc(this,a,b,1)} -// supports only 2.0-style subtract(1, 's') or subtract(duration) -function Zc(a,b){return Xc(this,a,b,-1)}function $c(a){return a<0?Math.floor(a):Math.ceil(a)}function _c(){var a,b,c,d,e,f=this._milliseconds,g=this._days,h=this._months,i=this._data; -// if we have a mix of positive and negative values, bubble down first -// check: https://github.com/moment/moment/issues/2166 -// The following code bubbles up values, see the tests for -// examples of what that means. -// convert days to months -// 12 months -> 1 year -return f>=0&&g>=0&&h>=0||f<=0&&g<=0&&h<=0||(f+=864e5*$c(bd(h)+g),g=0,h=0),i.milliseconds=f%1e3,a=t(f/1e3),i.seconds=a%60,b=t(a/60),i.minutes=b%60,c=t(b/60),i.hours=c%24,g+=t(c/24),e=t(ad(g)),h+=e,g-=$c(bd(e)),d=t(h/12),h%=12,i.days=g,i.months=h,i.years=d,this}function ad(a){ -// 400 years have 146097 days (taking into account leap year rules) -// 400 years have 12 months === 4800 -return 4800*a/146097}function bd(a){ -// the reverse of daysToMonths -return 146097*a/4800}function cd(a){var b,c,d=this._milliseconds;if(a=K(a),"month"===a||"year"===a)return b=this._days+d/864e5,c=this._months+ad(b),"month"===a?c:c/12;switch( -// handle milliseconds separately because of floating point math errors (issue #1867) -b=this._days+Math.round(bd(this._months)),a){case"week":return b/7+d/6048e5;case"day":return b+d/864e5;case"hour":return 24*b+d/36e5;case"minute":return 1440*b+d/6e4;case"second":return 86400*b+d/1e3; -// Math.floor prevents floating point math errors here -case"millisecond":return Math.floor(864e5*b)+d;default:throw new Error("Unknown unit "+a)}} -// TODO: Use this.as('ms')? -function dd(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*u(this._months/12)}function ed(a){return function(){return this.as(a)}}function fd(a){return a=K(a),this[a+"s"]()}function gd(a){return function(){return this._data[a]}}function hd(){return t(this.days()/7)} -// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize -function id(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function jd(a,b,c){var d=Ob(a).abs(),e=of(d.as("s")),f=of(d.as("m")),g=of(d.as("h")),h=of(d.as("d")),i=of(d.as("M")),j=of(d.as("y")),k=e0,k[4]=c,id.apply(null,k)} -// This function allows you to set the rounding function for relative time strings -function kd(a){return void 0===a?of:"function"==typeof a&&(of=a,!0)} -// This function allows you to set a threshold for relative time strings -function ld(a,b){return void 0!==pf[a]&&(void 0===b?pf[a]:(pf[a]=b,!0))}function md(a){var b=this.localeData(),c=jd(this,!a,b);return a&&(c=b.pastFuture(+this,c)),b.postformat(c)}function nd(){ -// for ISO strings we do not use the normal bubbling rules: -// * milliseconds bubble up until they become hours -// * days do not bubble at all -// * months bubble up until they become years -// This is because there is no context-free conversion between hours and days -// (think of clock changes) -// and also not between days and months (28-31 days per month) -var a,b,c,d=qf(this._milliseconds)/1e3,e=qf(this._days),f=qf(this._months); -// 3600 seconds -> 60 minutes -> 1 hour -a=t(d/60),b=t(a/60),d%=60,a%=60, -// 12 months -> 1 year -c=t(f/12),f%=12; -// inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js -var g=c,h=f,i=e,j=b,k=a,l=d,m=this.asSeconds();return m?(m<0?"-":"")+"P"+(g?g+"Y":"")+(h?h+"M":"")+(i?i+"D":"")+(j||k||l?"T":"")+(j?j+"H":"")+(k?k+"M":"")+(l?l+"S":""):"P0D"}var od,pd;pd=Array.prototype.some?Array.prototype.some:function(a){for(var b=Object(this),c=b.length>>>0,d=0;d68?1900:2e3)}; -// MOMENTS -var pe=O("FullYear",!0); -// FORMATTING -U("w",["ww",2],"wo","week"),U("W",["WW",2],"Wo","isoWeek"), -// ALIASES -J("week","w"),J("isoWeek","W"), -// PRIORITIES -M("week",5),M("isoWeek",5), -// PARSING -Z("w",Od),Z("ww",Od,Kd),Z("W",Od),Z("WW",Od,Kd),ca(["w","ww","W","WW"],function(a,b,c,d){b[d.substr(0,1)]=u(a)});var qe={dow:0,// Sunday is the first day of the week. -doy:6}; -// FORMATTING -U("d",0,"do","day"),U("dd",0,0,function(a){return this.localeData().weekdaysMin(this,a)}),U("ddd",0,0,function(a){return this.localeData().weekdaysShort(this,a)}),U("dddd",0,0,function(a){return this.localeData().weekdays(this,a)}),U("e",0,0,"weekday"),U("E",0,0,"isoWeekday"), -// ALIASES -J("day","d"),J("weekday","e"),J("isoWeekday","E"), -// PRIORITY -M("day",11),M("weekday",11),M("isoWeekday",11), -// PARSING -Z("d",Od),Z("e",Od),Z("E",Od),Z("dd",function(a,b){return b.weekdaysMinRegex(a)}),Z("ddd",function(a,b){return b.weekdaysShortRegex(a)}),Z("dddd",function(a,b){return b.weekdaysRegex(a)}),ca(["dd","ddd","dddd"],function(a,b,c,d){var e=c._locale.weekdaysParse(a,d,c._strict); -// if we didn't get a weekday name, mark the date as invalid -null!=e?b.d=e:m(c).invalidWeekday=a}),ca(["d","e","E"],function(a,b,c,d){b[d]=u(a)}); -// LOCALES -var re="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),se="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),te="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),ue=Zd,ve=Zd,we=Zd;U("H",["HH",2],0,"hour"),U("h",["hh",2],0,Ra),U("k",["kk",2],0,Sa),U("hmm",0,0,function(){return""+Ra.apply(this)+T(this.minutes(),2)}),U("hmmss",0,0,function(){return""+Ra.apply(this)+T(this.minutes(),2)+T(this.seconds(),2)}),U("Hmm",0,0,function(){return""+this.hours()+T(this.minutes(),2)}),U("Hmmss",0,0,function(){return""+this.hours()+T(this.minutes(),2)+T(this.seconds(),2)}),Ta("a",!0),Ta("A",!1), -// ALIASES -J("hour","h"), -// PRIORITY -M("hour",13),Z("a",Ua),Z("A",Ua),Z("H",Od),Z("h",Od),Z("HH",Od,Kd),Z("hh",Od,Kd),Z("hmm",Pd),Z("hmmss",Qd),Z("Hmm",Pd),Z("Hmmss",Qd),ba(["H","HH"],de),ba(["a","A"],function(a,b,c){c._isPm=c._locale.isPM(a),c._meridiem=a}),ba(["h","hh"],function(a,b,c){b[de]=u(a),m(c).bigHour=!0}),ba("hmm",function(a,b,c){var d=a.length-2;b[de]=u(a.substr(0,d)),b[ee]=u(a.substr(d)),m(c).bigHour=!0}),ba("hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[de]=u(a.substr(0,d)),b[ee]=u(a.substr(d,2)),b[fe]=u(a.substr(e)),m(c).bigHour=!0}),ba("Hmm",function(a,b,c){var d=a.length-2;b[de]=u(a.substr(0,d)),b[ee]=u(a.substr(d))}),ba("Hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[de]=u(a.substr(0,d)),b[ee]=u(a.substr(d,2)),b[fe]=u(a.substr(e))});var xe,ye=/[ap]\.?m?\.?/i,ze=O("Hours",!0),Ae={calendar:xd,longDateFormat:yd,invalidDate:zd,ordinal:Ad,ordinalParse:Bd,relativeTime:Cd,months:le,monthsShort:me,week:qe,weekdays:re,weekdaysMin:te,weekdaysShort:se,meridiemParse:ye},Be={},Ce={},De=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Ee=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Fe=/Z|[+-]\d\d(?::?\d\d)?/,Ge=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/], -// YYYYMM is NOT allowed by the standard -["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],He=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Ie=/^\/?Date\((\-?\d+)/i;a.createFromInputFallback=x("value provided is not in a recognized ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(a){a._d=new Date(a._i+(a._useUTC?" UTC":""))}), -// constant that refers to the ISO standard -a.ISO_8601=function(){};var Je=x("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var a=sb.apply(null,arguments);return this.isValid()&&a.isValid()?athis?this:a:o()}),Le=function(){return Date.now?Date.now():+new Date};zb("Z",":"),zb("ZZ",""), -// PARSING -Z("Z",Xd),Z("ZZ",Xd),ba(["Z","ZZ"],function(a,b,c){c._useUTC=!0,c._tzm=Ab(Xd,a)}); -// HELPERS -// timezone chunker -// '+10:00' > ['10', '00'] -// '-1530' > ['-15', '30'] -var Me=/([\+\-]|\d\d)/gi; -// HOOKS -// This function will be called whenever a moment is mutated. -// It is intended to keep the offset in sync with the timezone. -a.updateOffset=function(){}; -// ASP.NET json date format regex -var Ne=/^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,Oe=/^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;Ob.fn=wb.prototype;var Pe=Sb(1,"add"),Qe=Sb(-1,"subtract");a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",a.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var Re=x("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(a){return void 0===a?this.localeData():this.locale(a)}); -// FORMATTING -U(0,["gg",2],0,function(){return this.weekYear()%100}),U(0,["GG",2],0,function(){return this.isoWeekYear()%100}),zc("gggg","weekYear"),zc("ggggg","weekYear"),zc("GGGG","isoWeekYear"),zc("GGGGG","isoWeekYear"), -// ALIASES -J("weekYear","gg"),J("isoWeekYear","GG"), -// PRIORITY -M("weekYear",1),M("isoWeekYear",1), -// PARSING -Z("G",Vd),Z("g",Vd),Z("GG",Od,Kd),Z("gg",Od,Kd),Z("GGGG",Sd,Md),Z("gggg",Sd,Md),Z("GGGGG",Td,Nd),Z("ggggg",Td,Nd),ca(["gggg","ggggg","GGGG","GGGGG"],function(a,b,c,d){b[d.substr(0,2)]=u(a)}),ca(["gg","GG"],function(b,c,d,e){c[e]=a.parseTwoDigitYear(b)}), -// FORMATTING -U("Q",0,"Qo","quarter"), -// ALIASES -J("quarter","Q"), -// PRIORITY -M("quarter",7), -// PARSING -Z("Q",Jd),ba("Q",function(a,b){b[be]=3*(u(a)-1)}), -// FORMATTING -U("D",["DD",2],"Do","date"), -// ALIASES -J("date","D"), -// PRIOROITY -M("date",9), -// PARSING -Z("D",Od),Z("DD",Od,Kd),Z("Do",function(a,b){return a?b._ordinalParse:b._ordinalParseLenient}),ba(["D","DD"],ce),ba("Do",function(a,b){b[ce]=u(a.match(Od)[0],10)}); -// MOMENTS -var Se=O("Date",!0); -// FORMATTING -U("DDD",["DDDD",3],"DDDo","dayOfYear"), -// ALIASES -J("dayOfYear","DDD"), -// PRIORITY -M("dayOfYear",4), -// PARSING -Z("DDD",Rd),Z("DDDD",Ld),ba(["DDD","DDDD"],function(a,b,c){c._dayOfYear=u(a)}), -// FORMATTING -U("m",["mm",2],0,"minute"), -// ALIASES -J("minute","m"), -// PRIORITY -M("minute",14), -// PARSING -Z("m",Od),Z("mm",Od,Kd),ba(["m","mm"],ee); -// MOMENTS -var Te=O("Minutes",!1); -// FORMATTING -U("s",["ss",2],0,"second"), -// ALIASES -J("second","s"), -// PRIORITY -M("second",15), -// PARSING -Z("s",Od),Z("ss",Od,Kd),ba(["s","ss"],fe); -// MOMENTS -var Ue=O("Seconds",!1); -// FORMATTING -U("S",0,0,function(){return~~(this.millisecond()/100)}),U(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),U(0,["SSS",3],0,"millisecond"),U(0,["SSSS",4],0,function(){return 10*this.millisecond()}),U(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),U(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),U(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),U(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),U(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}), -// ALIASES -J("millisecond","ms"), -// PRIORITY -M("millisecond",16), -// PARSING -Z("S",Rd,Jd),Z("SS",Rd,Kd),Z("SSS",Rd,Ld);var Ve;for(Ve="SSSS";Ve.length<=9;Ve+="S")Z(Ve,Ud);for(Ve="S";Ve.length<=9;Ve+="S")ba(Ve,Ic); -// MOMENTS -var We=O("Milliseconds",!1); -// FORMATTING -U("z",0,0,"zoneAbbr"),U("zz",0,0,"zoneName");var Xe=r.prototype;Xe.add=Pe,Xe.calendar=Vb,Xe.clone=Wb,Xe.diff=bc,Xe.endOf=oc,Xe.format=gc,Xe.from=hc,Xe.fromNow=ic,Xe.to=jc,Xe.toNow=kc,Xe.get=R,Xe.invalidAt=xc,Xe.isAfter=Xb,Xe.isBefore=Yb,Xe.isBetween=Zb,Xe.isSame=$b,Xe.isSameOrAfter=_b,Xe.isSameOrBefore=ac,Xe.isValid=vc,Xe.lang=Re,Xe.locale=lc,Xe.localeData=mc,Xe.max=Ke,Xe.min=Je,Xe.parsingFlags=wc,Xe.set=S,Xe.startOf=nc,Xe.subtract=Qe,Xe.toArray=sc,Xe.toObject=tc,Xe.toDate=rc,Xe.toISOString=ec,Xe.inspect=fc,Xe.toJSON=uc,Xe.toString=dc,Xe.unix=qc,Xe.valueOf=pc,Xe.creationData=yc, -// Year -Xe.year=pe,Xe.isLeapYear=ra, -// Week Year -Xe.weekYear=Ac,Xe.isoWeekYear=Bc, -// Quarter -Xe.quarter=Xe.quarters=Gc, -// Month -Xe.month=ka,Xe.daysInMonth=la, -// Week -Xe.week=Xe.weeks=Ba,Xe.isoWeek=Xe.isoWeeks=Ca,Xe.weeksInYear=Dc,Xe.isoWeeksInYear=Cc, -// Day -Xe.date=Se,Xe.day=Xe.days=Ka,Xe.weekday=La,Xe.isoWeekday=Ma,Xe.dayOfYear=Hc, -// Hour -Xe.hour=Xe.hours=ze, -// Minute -Xe.minute=Xe.minutes=Te, -// Second -Xe.second=Xe.seconds=Ue, -// Millisecond -Xe.millisecond=Xe.milliseconds=We, -// Offset -Xe.utcOffset=Db,Xe.utc=Fb,Xe.local=Gb,Xe.parseZone=Hb,Xe.hasAlignedHourOffset=Ib,Xe.isDST=Jb,Xe.isLocal=Lb,Xe.isUtcOffset=Mb,Xe.isUtc=Nb,Xe.isUTC=Nb, -// Timezone -Xe.zoneAbbr=Jc,Xe.zoneName=Kc, -// Deprecations -Xe.dates=x("dates accessor is deprecated. Use date instead.",Se),Xe.months=x("months accessor is deprecated. Use month instead",ka),Xe.years=x("years accessor is deprecated. Use year instead",pe),Xe.zone=x("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",Eb),Xe.isDSTShifted=x("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",Kb);var Ye=C.prototype;Ye.calendar=D,Ye.longDateFormat=E,Ye.invalidDate=F,Ye.ordinal=G,Ye.preparse=Nc,Ye.postformat=Nc,Ye.relativeTime=H,Ye.pastFuture=I,Ye.set=A, -// Month -Ye.months=fa,Ye.monthsShort=ga,Ye.monthsParse=ia,Ye.monthsRegex=na,Ye.monthsShortRegex=ma, -// Week -Ye.week=ya,Ye.firstDayOfYear=Aa,Ye.firstDayOfWeek=za, -// Day of Week -Ye.weekdays=Fa,Ye.weekdaysMin=Ha,Ye.weekdaysShort=Ga,Ye.weekdaysParse=Ja,Ye.weekdaysRegex=Na,Ye.weekdaysShortRegex=Oa,Ye.weekdaysMinRegex=Pa, -// Hours -Ye.isPM=Va,Ye.meridiem=Wa,$a("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(a){var b=a%10,c=1===u(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}), -// Side effect imports -a.lang=x("moment.lang is deprecated. Use moment.locale instead.",$a),a.langData=x("moment.langData is deprecated. Use moment.localeData instead.",bb);var Ze=Math.abs,$e=ed("ms"),_e=ed("s"),af=ed("m"),bf=ed("h"),cf=ed("d"),df=ed("w"),ef=ed("M"),ff=ed("y"),gf=gd("milliseconds"),hf=gd("seconds"),jf=gd("minutes"),kf=gd("hours"),lf=gd("days"),mf=gd("months"),nf=gd("years"),of=Math.round,pf={s:45,// seconds to minute -m:45,// minutes to hour -h:22,// hours to day -d:26,// days to month -M:11},qf=Math.abs,rf=wb.prototype; -// Deprecations -// Side effect imports -// FORMATTING -// PARSING -// Side effect imports -return rf.abs=Wc,rf.add=Yc,rf.subtract=Zc,rf.as=cd,rf.asMilliseconds=$e,rf.asSeconds=_e,rf.asMinutes=af,rf.asHours=bf,rf.asDays=cf,rf.asWeeks=df,rf.asMonths=ef,rf.asYears=ff,rf.valueOf=dd,rf._bubble=_c,rf.get=fd,rf.milliseconds=gf,rf.seconds=hf,rf.minutes=jf,rf.hours=kf,rf.days=lf,rf.weeks=hd,rf.months=mf,rf.years=nf,rf.humanize=md,rf.toISOString=nd,rf.toString=nd,rf.toJSON=nd,rf.locale=lc,rf.localeData=mc,rf.toIsoString=x("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",nd),rf.lang=Re,U("X",0,0,"unix"),U("x",0,0,"valueOf"),Z("x",Vd),Z("X",Yd),ba("X",function(a,b,c){c._d=new Date(1e3*parseFloat(a,10))}),ba("x",function(a,b,c){c._d=new Date(u(a))}),a.version="2.17.1",b(sb),a.fn=Xe,a.min=ub,a.max=vb,a.now=Le,a.utc=k,a.unix=Lc,a.months=Rc,a.isDate=g,a.locale=$a,a.invalid=o,a.duration=Ob,a.isMoment=s,a.weekdays=Tc,a.parseZone=Mc,a.localeData=bb,a.isDuration=xb,a.monthsShort=Sc,a.weekdaysMin=Vc,a.defineLocale=_a,a.updateLocale=ab,a.locales=cb,a.weekdaysShort=Uc,a.normalizeUnits=K,a.relativeTimeRounding=kd,a.relativeTimeThreshold=ld,a.calendarFormat=Ub,a.prototype=Xe,a}); \ No newline at end of file diff --git a/hotel_calendar/static/src/xml/hotel_calendar_management_view.xml b/hotel_calendar/static/src/xml/hotel_calendar_management_view.xml deleted file mode 100644 index c75e7ee68..000000000 --- a/hotel_calendar/static/src/xml/hotel_calendar_management_view.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - -
      -
      - -
      -
      -
      -
      - - - diff --git a/hotel_calendar/static/src/xml/hotel_calendar_templates.xml b/hotel_calendar/static/src/xml/hotel_calendar_templates.xml deleted file mode 100644 index 4b579fafc..000000000 --- a/hotel_calendar/static/src/xml/hotel_calendar_templates.xml +++ /dev/null @@ -1,378 +0,0 @@ - diff --git a/hotel_calendar/static/src/xml/hotel_calendar_view.xml b/hotel_calendar/static/src/xml/hotel_calendar_view.xml deleted file mode 100644 index 832bb8bd0..000000000 --- a/hotel_calendar/static/src/xml/hotel_calendar_view.xml +++ /dev/null @@ -1,146 +0,0 @@ - - - - -
      - -
      -
      -
      -
      - - - diff --git a/hotel_calendar/tests/__init__.py b/hotel_calendar/tests/__init__.py deleted file mode 100644 index c9580b6f0..000000000 --- a/hotel_calendar/tests/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). diff --git a/hotel_calendar/tests/common.py b/hotel_calendar/tests/common.py deleted file mode 100644 index a0e771b14..000000000 --- a/hotel_calendar/tests/common.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2017 Solucións Aloxa S.L. -# Alexandre Díaz -# -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from odoo.addons.hotel.tests.common import TestHotel - - -class TestHotelCalendar(TestHotel): - - @classmethod - def setUpClass(cls): - super(TestHotelCalendar, cls).setUpClass() - - # Minimal Hotel Calendar Configuration - cls.tz_hotel = 'Europe/Madrid' - cls.default_pricelist_id = cls.pricelist_1.id - cls.default_restriction_id = cls.restriction_1.id - cls.env['ir.default'].sudo().set_default('res.config.settings', - 'default_arrival_hour', - '14:00') - cls.env['ir.default'].sudo().set_default('res.config.settings', - 'default_departure_hour', - '12:00') diff --git a/hotel_calendar/tests/test_management_calendar.py b/hotel_calendar/tests/test_management_calendar.py deleted file mode 100644 index 9a4b8b0e6..000000000 --- a/hotel_calendar/tests/test_management_calendar.py +++ /dev/null @@ -1,424 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2017 Solucións Aloxa S.L. -# Alexandre Díaz -# -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from datetime import timedelta -from openerp.tools import ( - DEFAULT_SERVER_DATETIME_FORMAT, - DEFAULT_SERVER_DATE_FORMAT) -from openerp.exceptions import ValidationError -from .common import TestHotelCalendar -from odoo.addons.hotel import date_utils -import logging -_logger = logging.getLogger(__name__) - - -class TestManagementCalendar(TestHotelCalendar): - - def test_calendar_prices(self): - now_utc_dt = date_utils.now() - adv_utc_dt = now_utc_dt + timedelta(days=15) - - room_types = (self.hotel_room_type_budget, self.hotel_room_type_special) - - hotel_cal_mngt_obj = self.env['hotel.calendar.management'].sudo( - self.user_hotel_manager) - - hcal_data = hotel_cal_mngt_obj.get_hcalendar_all_data( - now_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - adv_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - self.default_pricelist_id, - self.default_restriction_id, - True) - for room_type in room_types: - for k_pr, v_pr in hcal_data['prices'].iteritems(): - if k_pr == room_type.id: # Only Check Test Cases - for k_info, v_info in enumerate(v_pr): - if k_info >= len(self.prices_tmp[room_type.id]): - break - self.assertEqual(v_info['price'], - self.prices_tmp[room_type.id][k_info], - "Hotel Calendar Management Prices \ - doesn't match!") - - # REMOVE PRICES - prices_obj = self.env['product.pricelist.item'].sudo( - self.user_hotel_manager) - prod_tmpl_ids = ( - self.hotel_room_type_budget.product_id.product_tmpl_id.id, - self.hotel_room_type_special.product_id.product_tmpl_id.id - ) - pr_ids = prices_obj.search([ - ('pricelist_id', '=', self.default_pricelist_id), - ('product_tmpl_id', 'in', prod_tmpl_ids), - ]) - pr_ids.sudo(self.user_hotel_manager).unlink() - - hcal_data = hotel_cal_mngt_obj.get_hcalendar_all_data( - now_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - adv_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - self.default_pricelist_id, - self.default_restriction_id, - True) - self.assertFalse(any(hcal_data['prices']), "Hotel Calendar Management \ - Prices doesn't match after remove!") - - def test_calendar_restrictions(self): - now_utc_dt = date_utils.now() - adv_utc_dt = now_utc_dt + timedelta(days=15) - room_types = (self.hotel_room_type_budget, self.hotel_room_type_special) - - hotel_cal_mngt_obj = self.env['hotel.calendar.management'].sudo( - self.user_hotel_manager) - - hcal_data = hotel_cal_mngt_obj.get_hcalendar_all_data( - now_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - adv_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - self.default_pricelist_id, - self.default_restriction_id, - True) - for room_type in room_types: - for k_pr, v_pr in hcal_data['restrictions'].iteritems(): - if k_pr == room_type.id: # Only Check Test Cases - for k_info, v_info in enumerate(v_pr): - rest_items = self.restrictions_min_stay_tmp[room_type.id] - if k_info >= len(rest_items): - break - self.assertEqual( - v_info['min_stay'], - self.restrictions_min_stay_tmp[room_type.id][k_info], - "Hotel Calendar Management Restrictions \ - doesn't match!") - - # REMOVE RESTRICTIONS - rest_it_obj = self.env['hotel.room.type.restriction.item'].sudo( - self.user_hotel_manager) - rest_ids = rest_it_obj.search([ - ('restriction_id', '=', self.default_restriction_id), - ('room_type_id', 'in', (self.hotel_room_type_budget.id, - self.hotel_room_type_special.id)), - ]) - rest_ids.sudo(self.user_hotel_manager).unlink() - - hcal_data = hotel_cal_mngt_obj.get_hcalendar_all_data( - now_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - adv_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - self.default_pricelist_id, - self.default_restriction_id, - True) - self.assertFalse( - any(hcal_data['restrictions']), - "Hotel Calendar Management Restrictions doesn't match \ - after remove!") - - def test_calendar_availability(self): - now_utc_dt = date_utils.now() - adv_utc_dt = now_utc_dt + timedelta(days=6) - room_types = (self.hotel_room_type_budget, self.hotel_room_type_special) - - hotel_cal_mngt_obj = self.env['hotel.calendar.management'].sudo( - self.user_hotel_manager) - room_type_avail_obj = self.env['hotel.room.type.availability'].sudo( - self.user_hotel_manager) - - hcal_data = hotel_cal_mngt_obj.get_hcalendar_all_data( - now_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - adv_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - self.default_pricelist_id, - self.default_restriction_id, - True) - for room_type in room_types: - for k_pr, v_pr in hcal_data['availability'].iteritems(): - if k_pr == room_type.id: # Only Check Test Cases - for k_info, v_info in enumerate(v_pr): - if k_info >= len(self.avails_tmp[room_type.id]): - break - self.assertEqual( - v_info['avail'], - self.avails_tmp[room_type.id][k_info], - "Hotel Calendar Management Availability \ - doesn't match!") - - # CHANGE AVAIL - avail_ids = room_type_avail_obj.search([ - ('room_type_id', 'in', (self.hotel_room_type_budget.id, - self.hotel_room_type_special.id)), - ]) - for avail_id in avail_ids: - avail_id.sudo(self.user_hotel_manager).write({'avail': 1}) - hcal_data = hotel_cal_mngt_obj.get_hcalendar_all_data( - now_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - adv_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - self.default_pricelist_id, - self.default_restriction_id, - True) - for room_type in room_types: - for k_pr, v_pr in hcal_data['availability'].iteritems(): - if k_pr == room_type.id: # Only Check Test Cases - for k_info, v_info in enumerate(v_pr): - self.assertEqual( - v_info['avail'], - 1, - "Hotel Calendar Management Availability \ - doesn't match!") - - # REMOVE AVAIL - avail_ids = room_type_avail_obj.search([ - ('room_type_id', 'in', (self.hotel_room_type_budget.id, - self.hotel_room_type_special.id)), - ]) - avail_ids.sudo(self.user_hotel_manager).unlink() - - hcal_data = hotel_cal_mngt_obj.get_hcalendar_all_data( - now_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - adv_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - self.default_pricelist_id, - self.default_restriction_id, - True) - for room_type in room_types: - for k_pr, v_pr in hcal_data['availability'].iteritems(): - if k_pr == room_type.id: # Only Check Test Cases - for k_info, v_info in enumerate(v_pr): - self.assertEqual( - v_info['avail'], - room_type.max_real_rooms, - "Hotel Calendar Management Availability \ - doesn't match!") - - def test_save_changes(self): - now_utc_dt = date_utils.now() - adv_utc_dt = now_utc_dt + timedelta(days=3) - room_types = (self.hotel_room_type_budget,) - - hotel_cal_mngt_obj = self.env['hotel.calendar.management'].sudo( - self.user_hotel_manager) - - # Generate new prices - prices = (144.0, 170.0, 30.0, 50.0) - cprices = {} - for k_item, v_item in enumerate(prices): - ndate_utc_dt = now_utc_dt + timedelta(days=k_item) - cprices.setdefault(self.hotel_room_type_budget.id, []).append({ - 'date': ndate_utc_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT), - 'price': v_item - }) - - # Generate new restrictions - restrictions = { - 'min_stay': (3, 2, 4, 1), - 'max_stay': (5, 8, 9, 3), - 'min_stay_arrival': (2, 3, 6, 2), - 'max_stay_arrival': (4, 7, 7, 4), - 'closed_departure': (False, True, False, True), - 'closed_arrival': (True, False, False, False), - 'closed': (False, False, True, True), - } - crestrictions = {} - for i in range(0, 4): - ndate_utc_dt = now_utc_dt + timedelta(days=i) - crestrictions.setdefault(self.hotel_room_type_budget.id, []).append({ - 'date': ndate_utc_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT), - 'closed_arrival': restrictions['closed_arrival'][i], - 'max_stay': restrictions['max_stay'][i], - 'min_stay': restrictions['min_stay'][i], - 'closed_departure': restrictions['closed_departure'][i], - 'closed': restrictions['closed'][i], - 'min_stay_arrival': restrictions['min_stay_arrival'][i], - 'max_stay_arrival': restrictions['max_stay_arrival'][i], - }) - - # Generate new availability - avails = (1, 2, 2, 1) - cavails = {} - for k_item, v_item in enumerate(avails): - ndate_utc_dt = now_utc_dt + timedelta(days=k_item) - ndate_dt = date_utils.dt_as_timezone(ndate_utc_dt, self.tz_hotel) - cavails.setdefault(self.hotel_room_type_budget.id, []).append({ - 'date': ndate_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - 'avail': v_item, - 'no_ota': False, - }) - - # Save new values - hotel_cal_mngt_obj.save_changes( - self.default_pricelist_id, - self.default_restriction_id, - cprices, - crestrictions, - cavails) - - # Check data integrity - hcal_data = hotel_cal_mngt_obj.get_hcalendar_all_data( - now_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - adv_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - self.default_pricelist_id, - self.default_restriction_id, - True) - - for room_type in room_types: - for k_pr, v_pr in hcal_data['availability'].iteritems(): - if k_pr == room_type.id: # Only Check Test Cases - for k_info, v_info in enumerate(v_pr): - self.assertEqual(v_info['avail'], - avails[k_info], - "Hotel Calendar Management \ - Availability doesn't match!") - for k_pr, v_pr in hcal_data['restrictions'].iteritems(): - if k_pr == room_type.id: # Only Check Test Cases - for k_info, v_info in enumerate(v_pr): - self.assertEqual(v_info['min_stay'], - restrictions['min_stay'][k_info], - "Hotel Calendar Management \ - Restrictions doesn't match!") - self.assertEqual(v_info['max_stay'], - restrictions['max_stay'][k_info], - "Hotel Calendar Management \ - Restrictions doesn't match!") - self.assertEqual( - v_info['min_stay_arrival'], - restrictions['min_stay_arrival'][k_info], - "Hotel Calendar Management Restrictions \ - doesn't match!") - self.assertEqual( - v_info['max_stay_arrival'], - restrictions['max_stay_arrival'][k_info], - "Hotel Calendar Management Restrictions \ - doesn't match!") - self.assertEqual( - v_info['closed_departure'], - restrictions['closed_departure'][k_info], - "Hotel Calendar Management Restrictions \ - doesn't match!") - self.assertEqual( - v_info['closed_arrival'], - restrictions['closed_arrival'][k_info], - "Hotel Calendar Management Restrictions \ - doesn't match!") - self.assertEqual( - v_info['closed'], - restrictions['closed'][k_info], - "Hotel Calendar Management Restrictions \ - doesn't match!") - for k_pr, v_pr in hcal_data['prices'].iteritems(): - if k_pr == room_type.id: # Only Check Test Cases - for k_info, v_info in enumerate(v_pr): - self.assertEqual(v_info['price'], - prices[k_info], "Hotel Calendar \ - Management Prices doesn't match!") - - def test_calendar_reservations(self): - now_utc_dt = date_utils.now() - adv_utc_dt = now_utc_dt + timedelta(days=15) - room_types = (self.hotel_room_type_budget,) - - hotel_cal_mngt_obj = self.env['hotel.calendar.management'].sudo( - self.user_hotel_manager) - - reserv_start_utc_dt = now_utc_dt + timedelta(days=3) - reserv_end_utc_dt = reserv_start_utc_dt + timedelta(days=3) - folio = self.create_folio(self.user_hotel_manager, self.partner_2) - reservation = self.create_reservation( - self.user_hotel_manager, - folio, - reserv_start_utc_dt, - reserv_end_utc_dt, - self.hotel_room_simple_100, - "Reservation Test #1") - - hcal_data = hotel_cal_mngt_obj.get_hcalendar_all_data( - now_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - adv_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - self.default_pricelist_id, - self.default_restriction_id, - True) - - avail_end_utc_dt = reserv_end_utc_dt - timedelta(days=1) - for room_type in room_types: - for k_pr, v_pr in hcal_data['count_reservations'].iteritems(): - if k_pr == room_type.id: # Only Check Test Cases - for k_info, v_info in enumerate(v_pr): - ndate = date_utils.get_datetime(v_info['date']) - if date_utils.date_in(ndate, - reserv_start_utc_dt, - avail_end_utc_dt) == 0: - self.assertEqual(v_info['num'], - 1, - "Hotel Calendar Management \ - Availability doesn't match!") - - def test_invalid_input_calendar_data(self): - now_utc_dt = date_utils.now() - adv_utc_dt = now_utc_dt + timedelta(days=15) - - hotel_cal_mngt_obj = self.env['hotel.calendar.management'].sudo( - self.user_hotel_manager) - - with self.assertRaises(ValidationError): - hcal_data = hotel_cal_mngt_obj.get_hcalendar_all_data( - False, - adv_utc_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT), - self.default_pricelist_id, - self.default_restriction_id, - True) - with self.assertRaises(ValidationError): - hcal_data = hotel_cal_mngt_obj.get_hcalendar_all_data( - now_utc_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT), - False, - self.default_pricelist_id, - self.default_restriction_id, - True) - with self.assertRaises(ValidationError): - hcal_data = hotel_cal_mngt_obj.get_hcalendar_all_data( - False, - False, - self.default_pricelist_id, - self.default_restriction_id, - True) - hcal_data = hotel_cal_mngt_obj.get_hcalendar_all_data( - now_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - adv_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - False, - False, - True) - self.assertTrue(any(hcal_data), "Hotel Calendar invalid default \ - management default models!") - - def test_calendar_settings(self): - hotel_cal_mngt_obj = self.env['hotel.calendar.management'].sudo( - self.user_hotel_manager) - settings = hotel_cal_mngt_obj.get_hcalendar_settings() - self.assertTrue(settings, "Hotel Calendar invalid settings") - - self.assertEqual(settings['eday_week'], - self.user_hotel_manager.npms_end_day_week, - "Hotel Calendar invalid settings") - self.assertEqual(settings['eday_week_offset'], - self.user_hotel_manager.npms_end_day_week_offset, - "Hotel Calendar invalid settings") - self.assertEqual(settings['days'], - self.user_hotel_manager.npms_default_num_days, - "Hotel Calendar invalid settings") - self.assertEqual(settings['show_notifications'], - self.user_hotel_manager.pms_show_notifications, - "Hotel Calendar invalid settings") - self.assertEqual(settings['show_num_rooms'], - self.user_hotel_manager.pms_show_num_rooms, - "Hotel Calendar invalid settings") diff --git a/hotel_calendar/tests/test_product_pricelist.py b/hotel_calendar/tests/test_product_pricelist.py deleted file mode 100644 index b1a86099a..000000000 --- a/hotel_calendar/tests/test_product_pricelist.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018 Alexandre Díaz -# -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from openerp.tools import DEFAULT_SERVER_DATE_FORMAT -from .common import TestHotelCalendar -from odoo.addons.hotel import date_utils - - -class TestProductPricelist(TestHotelCalendar): - - def test_update_price(self): - now_utc_dt = date_utils.now() - now_utc_str = now_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT) - - room_type_tmpl_id = self.hotel_room_type_special.product_id.product_tmpl_id - - pritem_obj = self.env['product.pricelist.item'] - plitem = pritem_obj.search([ - ('pricelist_id', '=', self.default_pricelist_id), - ('product_tmpl_id', '=', room_type_tmpl_id.id), - ('date_start', '=', now_utc_str), - ('date_end', '=', now_utc_str), - ('applied_on', '=', '1_product'), - ('compute_price', '=', 'fixed') - ]) - old_price = plitem.fixed_price - - self.pricelist_1.update_price( - self.hotel_room_type_special.id, - now_utc_str, - 999.9) - - plitem = pritem_obj.search([ - ('pricelist_id', '=', self.default_pricelist_id), - ('product_tmpl_id', '=', room_type_tmpl_id.id), - ('date_start', '=', now_utc_str), - ('date_end', '=', now_utc_str), - ('applied_on', '=', '1_product'), - ('compute_price', '=', 'fixed') - ]) - new_price = plitem.fixed_price - - self.assertNotEqual(old_price, - new_price, - "Hotel Calendar can't change price") - self.assertEqual(new_price, - 999.9, - "Hotel Calendar can't change price") diff --git a/hotel_calendar/tests/test_reservations_calendar.py b/hotel_calendar/tests/test_reservations_calendar.py deleted file mode 100644 index fb149f2f1..000000000 --- a/hotel_calendar/tests/test_reservations_calendar.py +++ /dev/null @@ -1,289 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2017 Solucións Aloxa S.L. -# Alexandre Díaz -# -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -import datetime -from datetime import timedelta -from odoo import fields -from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT -from openerp.exceptions import ValidationError -from .common import TestHotelCalendar -from odoo.addons.hotel import date_utils -import pytz - - -class TestReservationsCalendar(TestHotelCalendar): - - def test_calendar_pricelist(self): - now_utc_dt = date_utils.now() - real_start_utc_dt = (now_utc_dt - timedelta(days=1)) - adv_utc_dt = now_utc_dt + timedelta(days=15) - - hotel_reserv_obj = self.env['hotel.reservation'].sudo( - self.user_hotel_manager) - - hcal_data = hotel_reserv_obj.get_hcalendar_all_data( - now_utc_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT), - adv_utc_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)) - - # Check Pricelist Integrity - for k_pr, v_pr in hcal_data['pricelist'].iteritems(): - for room_type_pr in v_pr: - # Only Check Test Cases - if room_type_pr['room'] in self.prices_tmp.keys(): - sorted_dates = sorted( - room_type_pr['days'].keys(), - key=lambda x: datetime.datetime.strptime(x, '%d/%m/%Y') - ) - init_date_dt = datetime.datetime.strptime( - sorted_dates[0], - '%d/%m/%Y').replace(tzinfo=pytz.utc) - end_date_dt = datetime.datetime.strptime( - sorted_dates[-1], - '%d/%m/%Y').replace(tzinfo=pytz.utc) - - self.assertEqual(real_start_utc_dt, init_date_dt, - "Hotel Calendar don't start in \ - the correct date!") - self.assertEqual(adv_utc_dt, end_date_dt, - "Hotel Calendar don't end in \ - the correct date!") - - room_type_prices = self.prices_tmp[room_type_pr['room']] - for k_price, v_price in enumerate(room_type_prices): - self.assertEqual( - v_price, - room_type_pr['days'][sorted_dates[k_price+1]], - "Hotel Calendar Pricelist doesn't match!") - - # Check Pricelist Integrity after unlink - pricelist_item_obj = self.env['product.pricelist.item'].sudo( - self.user_hotel_manager) - pr_ids = pricelist_item_obj.search([ - ('pricelist_id', '=', self.default_pricelist_id), - ('product_tmpl_id', 'in', ( - self.hotel_room_type_budget.product_id.product_tmpl_id.id, - self.hotel_room_type_special.product_id.product_tmpl_id.id)), - ]) - pr_ids.sudo(self.user_hotel_manager).unlink() - reserv_obj = self.env['hotel.reservation'].sudo( - self.user_hotel_manager) - hcal_data = reserv_obj.get_hcalendar_all_data( - now_utc_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT), - adv_utc_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)) - room_types = (self.hotel_room_type_budget, self.hotel_room_type_special) - for room_type in room_types: - for k_pr, v_pr in hcal_data['pricelist'].iteritems(): - for room_type_pr in v_pr: - if room_type_pr['room'] == room_type.id: # Only Check Test Cases - self.assertEqual( - room_type.list_price, - room_type_pr['days'][sorted_dates[k_price+1]], - "Hotel Calendar Pricelist doesn't \ - match after remove!") - - def test_calendar_reservations(self): - now_utc_dt = date_utils.now() - adv_utc_dt = now_utc_dt + timedelta(days=15) - - hotel_reserv_obj = self.env['hotel.reservation'].sudo( - self.user_hotel_manager) - - def is_reservation_listed(reservation_id): - hcal_data = hotel_reserv_obj.get_hcalendar_all_data( - now_utc_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT), - adv_utc_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)) - # TODO: Perhaps not the best way to do this test... :/ - hasReservationTest = False - for reserv in hcal_data['reservations']: - if reserv[1] == reservation_id: - hasReservationTest = True - break - return hasReservationTest - - # CREATE COMPLETE RESERVATION (3 Nigths) - reserv_start_utc_dt = now_utc_dt + timedelta(days=3) - reserv_end_utc_dt = reserv_start_utc_dt + timedelta(days=3) - folio = self.create_folio(self.user_hotel_manager, self.partner_2) - reservation = self.create_reservation( - self.user_hotel_manager, - folio, - reserv_start_utc_dt, - reserv_end_utc_dt, - self.hotel_room_double_200, - "Reservation Test #1") - - # CHECK SUCCESSFULL CREATION - self.assertTrue(is_reservation_listed(reservation.id), - "Hotel Calendar can't found test reservation!") - - # CONFIRM FOLIO - folio.sudo(self.user_hotel_manager).action_confirm() - self.assertTrue(is_reservation_listed(reservation.id), - "Hotel Calendar can't found test reservation!") - - # CALENDAR LIMITS - now_utc_dt_tmp = now_utc_dt - adv_utc_dt_tmp = adv_utc_dt - # Start after reservation end - now_utc_dt = reserv_end_utc_dt + timedelta(days=2) - adv_utc_dt = now_utc_dt + timedelta(days=15) - self.assertFalse( - is_reservation_listed(reservation.id), - "Hotel Calendar found test reservation but expected not found it!") - - # Ends before reservation start - adv_utc_dt = reserv_start_utc_dt - timedelta(days=1) - now_utc_dt = adv_utc_dt - timedelta(days=15) - self.assertFalse( - is_reservation_listed(reservation.id), - "Hotel Calendar found test reservation but expected not found it!") - now_utc_dt = now_utc_dt_tmp - adv_utc_dt = adv_utc_dt_tmp - - # Start in the middle of the reservation days - now_utc_dt = reserv_end_utc_dt - timedelta(days=1) - adv_utc_dt = now_utc_dt + timedelta(days=15) - self.assertTrue( - is_reservation_listed(reservation.id), - "Hotel Calendar can't found test reservation!") - now_utc_dt = now_utc_dt_tmp - adv_utc_dt = adv_utc_dt_tmp - - # CANCEL FOLIO - folio.sudo(self.user_hotel_manager).action_cancel() - self.assertFalse( - is_reservation_listed(reservation.id), - "Hotel Calendar can't found test reservation!") - - # REMOVE FOLIO - folio.sudo().unlink() # FIXME: Can't use: self.user_hotel_manager - self.assertFalse( - is_reservation_listed(reservation.id), - "Hotel Calendar can't found test reservation!") - - def test_invalid_input_calendar_data(self): - now_utc_dt = date_utils.now() - adv_utc_dt = now_utc_dt + timedelta(days=15) - - hotel_reserv_obj = self.env['hotel.reservation'].sudo( - self.user_hotel_manager) - - with self.assertRaises(ValidationError): - hcal_data = hotel_reserv_obj.get_hcalendar_all_data( - False, - adv_utc_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)) - with self.assertRaises(ValidationError): - hcal_data = hotel_reserv_obj.get_hcalendar_all_data( - now_utc_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT), - False) - with self.assertRaises(ValidationError): - hcal_data = hotel_reserv_obj.get_hcalendar_all_data( - False, - False) - - def test_calendar_settings(self): - hcal_options = self.env['hotel.reservation'].sudo( - self.user_hotel_manager).get_hcalendar_settings() - - self.assertEqual(hcal_options['divide_rooms_by_capacity'], - self.user_hotel_manager.pms_divide_rooms_by_capacity, - "Hotel Calendar Invalid Options!") - self.assertEqual(hcal_options['eday_week'], - self.user_hotel_manager.pms_end_day_week, - "Hotel Calendar Invalid Options!") - self.assertEqual(hcal_options['days'], - self.user_hotel_manager.pms_default_num_days, - "Hotel Calendar Invalid Options!") - self.assertEqual( - hcal_options['allow_invalid_actions'], - self.user_hotel_manager.pms_type_move == 'allow_invalid', - "Hotel Calendar Invalid Options!") - self.assertEqual( - hcal_options['assisted_movement'], - self.user_hotel_manager.pms_type_move == 'assisted', - "Hotel Calendar Invalid Options!") - default_arrival_hour = self.env['ir.default'].sudo().get( - 'res.config.settings', 'default_arrival_hour') - self.assertEqual(hcal_options['default_arrival_hour'], - default_arrival_hour, - "Hotel Calendar Invalid Options!") - default_departure_hour = self.env['ir.default'].sudo().get( - 'res.config.settings', 'default_departure_hour') - self.assertEqual(hcal_options['default_departure_hour'], - default_departure_hour, - "Hotel Calendar Invalid Options!") - self.assertEqual(hcal_options['show_notifications'], - self.user_hotel_manager.pms_show_notifications, - "Hotel Calendar Invalid Options!") - self.assertEqual(hcal_options['show_num_rooms'], - self.user_hotel_manager.pms_show_num_rooms, - "Hotel Calendar Invalid Options!") - self.assertEqual(hcal_options['show_pricelist'], - self.user_hotel_manager.pms_show_pricelist, - "Hotel Calendar Invalid Options!") - self.assertEqual(hcal_options['show_availability'], - self.user_hotel_manager.pms_show_availability, - "Hotel Calendar Invalid Options!") - - def test_swap_reservation(self): - hcal_reserv_obj = self.env['hotel.reservation'].sudo( - self.user_hotel_manager) - now_utc_dt = date_utils.now() - - # CREATE RESERVATIONS - reserv_start_utc_dt = now_utc_dt + timedelta(days=3) - reserv_end_utc_dt = reserv_start_utc_dt + timedelta(days=3) - folio_a = self.create_folio(self.user_hotel_manager, self.partner_2) - reservation_a = self.create_reservation( - self.user_hotel_manager, - folio_a, - reserv_start_utc_dt, - reserv_end_utc_dt, - self.hotel_room_double_200, - "Reservation Test #1") - self.assertTrue(reservation_a, - "Hotel Calendar create test reservation!") - folio_a.sudo(self.user_hotel_manager).action_confirm() - - folio_b = self.create_folio(self.user_hotel_manager, self.partner_2) - reservation_b = self.create_reservation( - self.user_hotel_manager, - folio_b, - reserv_start_utc_dt, - reserv_end_utc_dt, - self.hotel_room_simple_101, - "Reservation Test #2") - self.assertTrue(reservation_b, - "Hotel Calendar can't create test reservation!") - folio_b.sudo(self.user_hotel_manager).action_confirm() - - self.assertTrue( - hcal_reserv_obj.swap_reservations(reservation_a.ids, - reservation_b.ids), - "Hotel Calendar invalid swap operation" - ) - self.assertEqual(reservation_a.product_id.id, - self.hotel_room_simple_101.product_id.id, - "Hotel Calendar wrong swap operation") - self.assertEqual(reservation_b.product_id.id, - self.hotel_room_double_200.product_id.id, - "Hotel Calendar wrong swap operation") diff --git a/hotel_calendar/views/actions.xml b/hotel_calendar/views/actions.xml deleted file mode 100644 index 9bdf06417..000000000 --- a/hotel_calendar/views/actions.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - Hotel folio checkin - hotel.reservation - tree,form - [('real_checkin','=', datetime.datetime.now().strftime('%Y-%m-%d')), - ('state', 'in', ['confirm']), - ('reservation_type', 'not in', ['out'])] - - - - Hotel folio checkout - hotel.reservation - tree,form - [('real_checkout','=', datetime.datetime.now().strftime('%Y-%m-%d')), - ('state', 'in', ['booking']), - ('reservation_type', 'not in', ['out'])] - - - - Hotel Calendar - hotel.calendar - tree,form - - - diff --git a/hotel_calendar/views/general.xml b/hotel_calendar/views/general.xml deleted file mode 100644 index 3ab647615..000000000 --- a/hotel_calendar/views/general.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - diff --git a/hotel_calendar/views/hotel_calendar_management_views.xml b/hotel_calendar/views/hotel_calendar_management_views.xml deleted file mode 100644 index 6d12c3abb..000000000 --- a/hotel_calendar/views/hotel_calendar_management_views.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - hotel.calendar.management.mpms - hotel.calendar.management - - - - - - diff --git a/hotel_calendar/views/hotel_calendar_views.xml b/hotel_calendar/views/hotel_calendar_views.xml deleted file mode 100644 index 24a3c8bea..000000000 --- a/hotel_calendar/views/hotel_calendar_views.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - hotel.calendar.form - hotel.calendar - -
      - - - - - - - - - - - - -
      -
      -
      - - - - hotel.calendar.tree - hotel.calendar - - - - - - - - -
      diff --git a/hotel_calendar/views/hotel_reservation_views.xml b/hotel_calendar/views/hotel_reservation_views.xml deleted file mode 100644 index 6c32e0a8b..000000000 --- a/hotel_calendar/views/hotel_reservation_views.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - hotel.reservation.pms - hotel.reservation - - - - - - diff --git a/hotel_calendar/views/inherited_hotel_property_views.xml b/hotel_calendar/views/inherited_hotel_property_views.xml deleted file mode 100644 index b774bcbd4..000000000 --- a/hotel_calendar/views/inherited_hotel_property_views.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - hotel.property - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/hotel_calendar/views/inherited_hotel_room_type_views.xml b/hotel_calendar/views/inherited_hotel_room_type_views.xml deleted file mode 100644 index 9726a0d71..000000000 --- a/hotel_calendar/views/inherited_hotel_room_type_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - hotel.room.type - - - - - - - - - diff --git a/hotel_calendar/views/inherited_hotel_room_views.xml b/hotel_calendar/views/inherited_hotel_room_views.xml deleted file mode 100644 index 3d84684cb..000000000 --- a/hotel_calendar/views/inherited_hotel_room_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - hotel.room - - - - - - - - - diff --git a/hotel_calendar/views/inherited_res_company_views.xml b/hotel_calendar/views/inherited_res_company_views.xml deleted file mode 100644 index de31ddf29..000000000 --- a/hotel_calendar/views/inherited_res_company_views.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - view.company.form - res.company - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/hotel_calendar/views/inherited_res_users_views.xml b/hotel_calendar/views/inherited_res_users_views.xml deleted file mode 100644 index 12c33f265..000000000 --- a/hotel_calendar/views/inherited_res_users_views.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - res.users - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/hotel_calendar_channel_connector/README.md b/hotel_calendar_channel_connector/README.md deleted file mode 100644 index a102d4230..000000000 --- a/hotel_calendar_channel_connector/README.md +++ /dev/null @@ -1,12 +0,0 @@ -HOTEL CALENDAR CHANNEL CONNECTOR -=================================== -Unify 'hotel_calendar' and 'hotel_channel_connector' - - -Credits -======= - -Creator ------------- - -* Alexandre Díaz diff --git a/hotel_calendar_channel_connector/__init__.py b/hotel_calendar_channel_connector/__init__.py deleted file mode 100644 index 572903d95..000000000 --- a/hotel_calendar_channel_connector/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from . import models diff --git a/hotel_calendar_channel_connector/__manifest__.py b/hotel_calendar_channel_connector/__manifest__.py deleted file mode 100644 index a3cbe0835..000000000 --- a/hotel_calendar_channel_connector/__manifest__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -{ - 'name': 'Hotel Calendar Channel Connector', - 'version': '11.0.2.0', - 'author': "Alexandre Díaz ", - 'website': 'https://github.com/hootel/hootel', - 'category': 'hotel/addon', - 'summary': "Hotel Calendar Channel Connector", - 'description': "Unify 'hotel_calendar' and 'hotel_channel_connector'", - 'depends': [ - 'hotel_calendar', - 'hotel_channel_connector', - ], - 'external_dependencies': { - 'python': [] - }, - 'data': [ - 'views/hotel_reservation.xml', - 'views/general.xml', - 'views/actions.xml', - ], - 'qweb': [ - 'static/src/xml/*.xml', - ], - 'test': [ - ], - - 'installable': True, - 'auto_install': True, - 'license': 'AGPL-3', -} diff --git a/hotel_calendar_channel_connector/i18n/es.po b/hotel_calendar_channel_connector/i18n/es.po deleted file mode 100644 index 9bcd96d2e..000000000 --- a/hotel_calendar_channel_connector/i18n/es.po +++ /dev/null @@ -1,88 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * hotel_calendar_channel_connector -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 11.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-03-13 21:59+0000\n" -"PO-Revision-Date: 2019-03-13 23:01+0100\n" -"Last-Translator: <>\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"Language: es\n" -"X-Generator: Poedit 1.8.7.1\n" - -#. module: hotel_calendar_channel_connector -#: model:ir.actions.act_window,name:hotel_calendar_channel_connector.calendar_channel_connector_issues_action -msgid "Channel Connector Issues" -msgstr "Aviso de Conexión" - -#. module: hotel_calendar_channel_connector -#. openerp-web -#: code:addons/hotel_calendar_channel_connector/static/src/xml/hotel_calendar_view.xml:30 -#, python-format -msgid "Channel:" -msgstr "Canal:" - -#. module: hotel_calendar_channel_connector -#: model:ir.model,name:hotel_calendar_channel_connector.model_hotel_reservation -msgid "Hotel Reservation" -msgstr "Reserva del hotel" - -#. module: hotel_calendar_channel_connector -#. openerp-web -#: code:addons/hotel_calendar_channel_connector/static/src/xml/hotel_calendar_view.xml:11 -#, python-format -msgid "Issues" -msgstr "Avisos" - -#. module: hotel_calendar_channel_connector -#: model:ir.ui.view,arch_db:hotel_calendar_channel_connector.hotel_reservation_view_form -msgid "Mark as Read" -msgstr "Marcar como leído" - -#. module: hotel_calendar_channel_connector -#: model:ir.actions.act_window,name:hotel_calendar_channel_connector.hotel_reservation_action_manager_request -msgid "Reservations to Assign from Channel" -msgstr "Reservas por asignar" - -#. module: hotel_calendar_channel_connector -#. openerp-web -#: code:addons/hotel_calendar_channel_connector/static/src/xml/hotel_calendar_templates.xml:4 -#, python-format -msgid "Section:" -msgstr "Sección:" - -#. module: hotel_calendar_channel_connector -#. openerp-web -#: code:addons/hotel_calendar_channel_connector/static/src/xml/hotel_calendar_view.xml:20 -#, python-format -msgid "To Assign" -msgstr "Por Asignar" - -#. module: hotel_calendar_channel_connector -#. openerp-web -#: code:addons/hotel_calendar_channel_connector/static/src/xml/hotel_calendar_templates.xml:11 -#, python-format -msgid "WuBook:" -msgstr "WuBook:" - -#. module: hotel_calendar_channel_connector -#: model:ir.model,name:hotel_calendar_channel_connector.model_bus_hotel_calendar -msgid "bus.hotel.calendar" -msgstr "bus.hotel.calendar" - -#. module: hotel_calendar_channel_connector -#: model:ir.model,name:hotel_calendar_channel_connector.model_hotel_calendar_management -msgid "hotel.calendar.management" -msgstr "hotel.calendar.management" - -#. module: hotel_calendar_channel_connector -#: model:ir.model,name:hotel_calendar_channel_connector.model_hotel_room_type_availability -msgid "hotel.room.type.availability" -msgstr "hotel.room.type.availability" diff --git a/hotel_calendar_channel_connector/models/__init__.py b/hotel_calendar_channel_connector/models/__init__.py deleted file mode 100644 index b67f6cb10..000000000 --- a/hotel_calendar_channel_connector/models/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright 2018-2019 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from . import inherited_hotel_reservation -from . import inherited_bus_hotel_calendar -from . import inherited_hotel_calendar_management -from . import inherited_hotel_room_type_availability diff --git a/hotel_calendar_channel_connector/models/inherited_bus_hotel_calendar.py b/hotel_calendar_channel_connector/models/inherited_bus_hotel_calendar.py deleted file mode 100644 index 19c3f56bb..000000000 --- a/hotel_calendar_channel_connector/models/inherited_bus_hotel_calendar.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright 2018-2019 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import logging -from datetime import datetime -from odoo.tools import DEFAULT_SERVER_DATE_FORMAT -from odoo import models, api -from odoo.addons.hotel_calendar.controllers.bus import HOTEL_BUS_CHANNEL_ID -_logger = logging.getLogger(__name__) - - -class BusHotelCalendar(models.TransientModel): - _inherit = 'bus.hotel.calendar' - - @api.model - def _generate_reservation_notif(self, vals): - notif = super(BusHotelCalendar, self)._generate_reservation_notif(vals) - reserv = self.env['hotel.reservation'].browse(vals['reserv_id']) - if any(reserv.channel_bind_ids): - notif['tooltip'].update({ - 'ota_name': reserv.channel_bind_ids[0].ota_id.name, - 'ota_reservation_id': reserv.channel_bind_ids[0].ota_reservation_id, - 'external_id': reserv.channel_bind_ids[0].external_id, - }) - elif reserv.splitted and reserv.parent_reservation.channel_bind_ids: - # chunks in splitted reservation has not channel_bind_ids - notif['tooltip'].update({ - 'ota_name': reserv.parent_reservation.channel_bind_ids[0].ota_id.name, - 'ota_reservation_id': reserv.parent_reservation.channel_bind_ids[0].ota_reservation_id, - 'external_id': reserv.parent_reservation.channel_bind_ids[0].external_id, - }) - return notif - - @api.model - def _generate_availability_notification(self, vals): - date_dt = datetime.strptime(vals['date'], DEFAULT_SERVER_DATE_FORMAT) - return { - 'type': 'availability', - 'availability': { - vals['room_type_id']: { - date_dt.strftime("%d/%m/%Y"): { - 'quota': vals['quota'], - 'max_avail': vals['max_avail'], - 'id': vals['id'], - 'no_ota': vals['no_ota'], - 'channel_avail': vals['channel_avail'], - }, - }, - }, - } - - @api.model - def send_availability_notification(self, vals): - notif = self._generate_availability_notification(vals) - self.env['bus.bus'].sendone((self._cr.dbname, 'hotel.reservation', - HOTEL_BUS_CHANNEL_ID), notif) - - @api.model - def _generate_issue_notification(self, ntype, title, issue_id, section, - message): - user_id = self.env['res.users'].browse(self.env.uid) - return { - 'type': 'issue', - 'subtype': ntype, - 'title': title, - 'username': user_id.partner_id.name, - 'userid': user_id.id, - 'issue': { - 'issue_id': issue_id, - 'section': section.upper(), - 'message': message, - }, - } - - @api.model - def send_issue_notification(self, ntype, title, issue_id, section, message): - notif = self._generate_issue_notification(ntype, title, issue_id, section, message) - self.env['bus.bus'].sendone( - (self._cr.dbname, 'hotel.reservation', - HOTEL_BUS_CHANNEL_ID), notif) - diff --git a/hotel_calendar_channel_connector/models/inherited_hotel_calendar_management.py b/hotel_calendar_channel_connector/models/inherited_hotel_calendar_management.py deleted file mode 100644 index b447e5a23..000000000 --- a/hotel_calendar_channel_connector/models/inherited_hotel_calendar_management.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright 2019 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from datetime import timedelta -from odoo import models, api, fields -from odoo.tools import DEFAULT_SERVER_DATE_FORMAT - - -class HotelCalendarManagement(models.TransientModel): - _inherit = 'hotel.calendar.management' - - @api.model - def _hcalendar_availability_json_data(self, dfrom, dto): - date_start = fields.Date.from_string(dfrom) - date_end = fields.Date.from_string(dto) - date_diff = abs((date_end - date_start).days) + 1 - hotel_room_type_avail_obj = self.env['hotel.room.type.availability'] - room_types = self.env['hotel.room.type'].search([]) - json_data = {} - - for room_type in room_types: - json_data[room_type.id] = [] - for i in range(0, date_diff): - cur_date = date_start + timedelta(days=i) - cur_date_str = cur_date.strftime(DEFAULT_SERVER_DATE_FORMAT) - avail = hotel_room_type_avail_obj.search([ - ('date', '=', cur_date_str), - ('room_type_id', '=', room_type.id) - ]) - json_data[room_type.id].append( - self._generate_avalaibility_data(room_type, cur_date_str, avail)) - return json_data - - @api.model - def _generate_avalaibility_data(self, room_type, date, avail): - avalaibility_data = { - 'id': False, - 'date': date, - 'no_ota': False, - 'quota': room_type.channel_bind_ids.default_quota, - 'max_avail': room_type.channel_bind_ids.default_max_avail, - 'channel_avail': room_type.channel_bind_ids.default_availability - } - if avail: - avalaibility_data = { - 'id': avail.id, - 'date': avail.date, - 'no_ota': avail.no_ota, - 'quota': avail.quota, - 'max_avail': avail.max_avail, - 'channel_avail': avail.channel_bind_ids.channel_avail - } - return avalaibility_data - - @api.model - def _get_availability_values(self, vals): - vals = { - 'quota': vals['quota'], - 'max_avail': vals['max_avail'], - 'no_ota': vals['no_ota'], - } - return vals - - - def save_changes(self, pricelist_id, restriction_id, pricelist, - restrictions, availability={}): - res = super(HotelCalendarManagement, self).save_changes( - pricelist_id, - restriction_id, - pricelist, - restrictions, - availability=availability) - - room_type_obj = self.env['hotel.room.type'] - room_type_avail_obj = self.env['hotel.room.type.availability'] - # Save Availability - for k_avail in availability.keys(): - room_type_id = room_type_obj.browse(int(k_avail)) - for avail in availability[k_avail]: - vals = self._get_availability_values(avail) - avail_id = room_type_avail_obj.search([ - ('date', '=', avail['date']), - ('room_type_id', '=', room_type_id.id), - ], limit=1) - if not avail_id: - vals.update({ - 'date': avail['date'], - 'room_type_id': room_type_id.id, - }) - avail_id = room_type_avail_obj.with_context({ - 'mail_create_nosubscribe': True, - }).create(vals) - else: - avail_id.write(vals) - - self.env['channel.backend'].cron_push_changes() - return res - - @api.model - def get_hcalendar_all_data(self, dfrom, dto, pricelist_id, restriction_id, - withRooms): - res = super(HotelCalendarManagement, self).get_hcalendar_all_data( - dfrom, dto, pricelist_id, restriction_id, withRooms) - json_avails = self._hcalendar_availability_json_data(dfrom, dto) - res.update({ - 'availability': json_avails or [], - }) - return res diff --git a/hotel_calendar_channel_connector/models/inherited_hotel_reservation.py b/hotel_calendar_channel_connector/models/inherited_hotel_reservation.py deleted file mode 100644 index 1b2ca9477..000000000 --- a/hotel_calendar_channel_connector/models/inherited_hotel_reservation.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import logging -from odoo import models, api -_logger = logging.getLogger(__name__) - - -class HotelReservation(models.Model): - _inherit = "hotel.reservation" - - - def _hcalendar_reservation_data(self, reservations): - vals = super(HotelReservation, self)._hcalendar_reservation_data(reservations) - # TODO: Improve performance by doing a SQL as in get_hcalendar_reservations_data() - hotel_reservation_obj = self.env['hotel.reservation'] - for v_rval in vals[0]: - reserv = hotel_reservation_obj.browse(v_rval['id']) - v_rval.update({ - 'fix_days': reserv.splitted or reserv.is_from_ota, - }) - # Update tooltips - if any(reserv.channel_bind_ids): - vals[1][reserv.id].update({ - 'ota_name': reserv.channel_bind_ids[0].ota_id.name, - 'ota_reservation_id': reserv.channel_bind_ids[0].ota_reservation_id, - 'external_id': reserv.channel_bind_ids[0].external_id, - }) - elif reserv.splitted and reserv.parent_reservation.channel_bind_ids: - # chunks in splitted reservation has not channel_bind_ids - vals[1][reserv.id].update({ - 'ota_name': reserv.parent_reservation.channel_bind_ids[0].ota_id.name, - 'ota_reservation_id': reserv.parent_reservation.channel_bind_ids[0].ota_reservation_id, - 'external_id': reserv.parent_reservation.channel_bind_ids[0].external_id, - }) - # REVIEW: What happens if the reservation is splitted and no parent with channel_bind_ids ¿? - return vals - - - def generate_bus_values(self, naction, ntype, ntitle=''): - self.ensure_one() - vals = super(HotelReservation, self).generate_bus_values(naction, ntype, ntitle) - vals.update({ - 'fix_days': self.splitted or self.is_from_ota, - }) - if any(self.channel_bind_ids): - vals.update({ - 'ota_name': self.channel_bind_ids[0].ota_id.name, - 'ota_reservation_id': self.channel_bind_ids[0].ota_reservation_id, - 'external_id': self.channel_bind_ids[0].external_id, - }) - return vals - - - def confirm(self): - for record in self: - if record.to_assign: - record.write({'to_assign': False}) - return super(HotelReservation, self).confirm() diff --git a/hotel_calendar_channel_connector/models/inherited_hotel_room_type_availability.py b/hotel_calendar_channel_connector/models/inherited_hotel_room_type_availability.py deleted file mode 100644 index 247cf6b78..000000000 --- a/hotel_calendar_channel_connector/models/inherited_hotel_room_type_availability.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2018-2019 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, api - - -class HotelRoomTypeAvailability(models.Model): - _inherit = 'hotel.room.type.availability' - - def _prepare_notif_values(self, record): - return { - 'date': record.date, - 'quota': record.quota, - 'no_ota': record.no_ota, - 'max_avail': record.max_avail, - 'room_type_id': record.room_type_id.id, - 'id': record.id, - 'channel_avail': record.channel_bind_ids.channel_avail, - } - - @api.model - def create(self, vals): - res = super(HotelRoomTypeAvailability, self).create(vals) - self.env['bus.hotel.calendar'].send_availability_notification( - self._prepare_notif_values(res) - ) - return res - - - def write(self, vals): - ret_vals = super(HotelRoomTypeAvailability, self).write(vals) - bus_hotel_calendar_obj = self.env['bus.hotel.calendar'] - for record in self: - bus_hotel_calendar_obj.send_availability_notification( - self._prepare_notif_values(record) - ) - return ret_vals - - - def unlink(self): - # Construct dictionary with relevant info of removed records - unlink_vals = [] - for record in self: - unlink_vals.append( - self._prepare_notif_values(record) - ) - res = super(HotelRoomTypeAvailability, self).unlink() - bus_hotel_calendar_obj = self.env['bus.hotel.calendar'] - for uval in unlink_vals: - bus_hotel_calendar_obj.send_availability_notification(uval) - return res diff --git a/hotel_calendar_channel_connector/readme/CONTRIBUTORS.rst b/hotel_calendar_channel_connector/readme/CONTRIBUTORS.rst deleted file mode 100644 index 55873688c..000000000 --- a/hotel_calendar_channel_connector/readme/CONTRIBUTORS.rst +++ /dev/null @@ -1 +0,0 @@ -* Alexandre Díaz diff --git a/hotel_calendar_channel_connector/readme/DESCRIPTION.rst b/hotel_calendar_channel_connector/readme/DESCRIPTION.rst deleted file mode 100644 index 10d829e13..000000000 --- a/hotel_calendar_channel_connector/readme/DESCRIPTION.rst +++ /dev/null @@ -1,7 +0,0 @@ -This module unify 'hotel_calendar' and 'hotel_channel_connector' - -Features: - - * Add 'cloud' button on calendar - * Adds restrictions to reservation management - * Call connector diff --git a/hotel_calendar_channel_connector/readme/ROADMAP.rst b/hotel_calendar_channel_connector/readme/ROADMAP.rst deleted file mode 100644 index e69de29bb..000000000 diff --git a/hotel_calendar_channel_connector/readme/USAGE.rst b/hotel_calendar_channel_connector/readme/USAGE.rst deleted file mode 100644 index ede695e0e..000000000 --- a/hotel_calendar_channel_connector/readme/USAGE.rst +++ /dev/null @@ -1 +0,0 @@ -No configuration required diff --git a/hotel_calendar_channel_connector/static/description/eiqui_logo.png b/hotel_calendar_channel_connector/static/description/eiqui_logo.png deleted file mode 100644 index ca3bd8c1321a85719347fea0ba79a2320b29eb69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6240 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 DYa-VG diff --git a/hotel_calendar_channel_connector/static/description/index.html b/hotel_calendar_channel_connector/static/description/index.html deleted file mode 100644 index 9022331a1..000000000 --- a/hotel_calendar_channel_connector/static/description/index.html +++ /dev/null @@ -1,22 +0,0 @@ -
      -
      -
      -
      -

      HOTEL CALENDAR WUBOOK

      -

      Hotel Calendar WuBook

      -
      -
      -

      Hotel Calendar WuBook

      -
      - -
      - -
      -
      - - - -
      -
      -
      -
      diff --git a/hotel_calendar_channel_connector/static/sfx/book_cancelled.mp3 b/hotel_calendar_channel_connector/static/sfx/book_cancelled.mp3 deleted file mode 100644 index 10ffab8e7833cc8875b8d2c9950e52d74b395ed2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10152 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 - * Alexandre Díaz - */ - - /** ANIMATIONS **/ - @keyframes channel-manager-changes { - 0% { - color: red; - } - 5% { - transform: rotate(10deg); - } - 10% { - transform: rotate(0deg); - } - 15% { - transform: rotate(-10deg); - } - 20% { - transform: rotate(0deg); - } - 100% { - color: yellow; - } -} -@keyframes channel-manager-changes-text { - from { - color: white; - } - to { - color: black; - } -} - -#pms-menu #btn_channel_manager_request .incoming i { - animation-name: channel-manager-changes; - animation-duration: 2s; - animation-iteration-count: infinite; - animation-direction: alternate; -} - -#pms-menu #btn_channel_manager_request .button-highlight { - color: white; - background-color: #FFA500; -} - -#pms-menu #btn_channel_manager_request .button-highlight i { - color: white; -} \ No newline at end of file diff --git a/hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_controller.js b/hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_controller.js deleted file mode 100644 index 266291905..000000000 --- a/hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_controller.js +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2018 Alexandre Díaz -// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('hotel_calendar_channel_connector.PMSHotelCalendarController', function (require) { -"use strict"; - -var PMSCalendarController = require('hotel_calendar.PMSCalendarController'); -var Core = require('web.core'); - -var QWeb = Core.qweb; - -var PMSHotelCalendarController = PMSCalendarController.include({ - _sounds: [], - SOUNDS: { NONE: 0, BOOK_NEW:1, BOOK_CANCELLED:2 }, - - init: function (parent, model, renderer, params) { - this._super.apply(this, arguments); - - this._sounds[this.SOUNDS.BOOK_NEW] = new Audio('hotel_calendar_channel_connector/static/sfx/book_new.mp3'); - this._sounds[this.SOUNDS.BOOK_CANCELLED] = new Audio('hotel_calendar_channel_connector/static/sfx/book_cancelled.mp3'); - }, - - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- - - - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - _play_sound: function(/*int*/SoundID) { - this._sounds[SoundID].play(); - }, - - _generate_reservation_tooltip_dict: function(tp) { - var qdict = this._super(tp); - qdict['ota_name'] = tp['ota_name']; - qdict['ota_reservation_id'] = tp['ota_reservation_id']; - qdict['external_id'] = tp['external_id']; - return qdict; - }, - - //-------------------------------------------------------------------------- - // Handlers - //-------------------------------------------------------------------------- - _update_buttons_counter: function (ev) { - this._super(ev); - var self = this; - var domain_reservations = [['to_assign', '=', true]]; - // FIXME: invalid domain search. Issues are in hotel_channel_connector_issue; - // var domain_issues = [['to_assign', '=', true]]; - var domain_issues = []; - $.when( - this.model.search_count(domain_reservations), - this.model.search_count(domain_issues), - ).then(function(a1, a2){ - self.renderer.update_buttons_counter_channel_connector(a1, a2); - }); - }, - - _onBusNotification: function(notifications) { - for (var notif of notifications) { - if (notif[0][1] === 'hotel.reservation') { - if (notif[1]['type'] === 'issue') { - if (notif[1]['userid'] === this.dataset.context.uid) { - continue; - } - - var issue = notif[1]['issue']; - var qdict = issue; - var msg = QWeb.render('HotelCalendarChannelConnector.NotificationIssue', qdict); - if (notif[1]['subtype'] === 'notify') { - this.do_notify(notif[1]['title'], msg, true); - } else if (notif[1]['subtype'] === 'warn') { - this.do_warn(notif[1]['title'], msg, true); - } - } - else if (notif[1]['type'] === 'reservation') { - var reserv = notif[1]['reservation']; - if (reserv['channel_type'] == 'web') { - if (notif[1]['action'] === 'create') { - this._play_sound(this.SOUNDS.BOOK_NEW); - } else if (notif[1]['action'] !== 'unlink' && reserv['state'] === 'cancelled') { - this._play_sound(this.SOUNDS.BOOK_CANCELLED); - } - } - } - } - } - this._super(notifications); - }, - - _refresh_view_options: function(active_index) { - /* btn_channel_manager_request */ - this._super(active_index); - - }, - -}); - -return PMSHotelCalendarController; - -}); diff --git a/hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_management_controller.js b/hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_management_controller.js deleted file mode 100644 index 89ed396e2..000000000 --- a/hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_management_controller.js +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2019 Alexandre Díaz -// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('hotel_calendar_channel_connector.MPMSCalendarController', function (require) { -"use strict"; - -var MPMSCalendarController = require('hotel_calendar.MPMSCalendarController'); -var HotelConstants = require('hotel_calendar.Constants'); -var Core = require('web.core'); - -var QWeb = Core.qweb; - - -var MPMSCalendarController = MPMSCalendarController.include({ - - _onBusNotification: function (notifications) { - this._super.apply(this, arguments); - if (!this.renderer._hcalendar) { return; } - - for (var notif of notifications) { - if (notif[0][1] === 'hotel.reservation') { - switch (notif[1]['type']) { - case 'availability': - var avail = notif[1]['availability']; - var room_type = Object.keys(avail)[0]; - var day = Object.keys(avail[room_type])[0]; - var dt = HotelCalendarManagement.toMoment(day); - var availability = {}; - availability[room_type] = [{ - 'date': dt.format(HotelConstants.ODOO_DATE_MOMENT_FORMAT), - 'quota': avail[room_type][day]['quota'], - 'max_avail': avail[room_type][day]['max_avail'], - 'no_ota': avail[room_type][day]['no_ota'], - 'id': avail[room_type][day]['id'], - 'channel_avail': avail[room_type][day]['channel_avail'] - }]; - this.renderer._hcalendar.addAvailability(availability); - break; - } - } - } - }, - -}); - -return MPMSCalendarController; - -}); diff --git a/hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_management_renderer.js b/hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_management_renderer.js deleted file mode 100644 index 494d629f5..000000000 --- a/hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_management_renderer.js +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019 Alexandre Díaz -// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('hotel_calendar_channel_connector.MPMSCalendarRenderer', function (require) { -"use strict"; - -var MPMSCalendarRenderer = require('hotel_calendar.MPMSCalendarRenderer'); -var Core = require('web.core'); - -var QWeb = Core.qweb; - - -var MPMSCalendarRenderer = MPMSCalendarRenderer.include({ - - /** CUSTOM METHODS **/ - get_values_to_save: function() { - var values = this._super.apply(this, arguments); - if (values) { - var availability = this._hcalendar.getAvailability(true); - values.push(availability); - } - return values; - }, - -}); - -return MPMSCalendarRenderer; - -}); diff --git a/hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_renderer.js b/hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_renderer.js deleted file mode 100644 index 9c5c1b84d..000000000 --- a/hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_renderer.js +++ /dev/null @@ -1,66 +0,0 @@ -/* global odoo, $ */ -// Copyright 2018 Alexandre Díaz -// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('hotel_calendar_channel_connector.PMSHotelCalendarRenderer', function (require) { - 'use strict'; - - var PMSCalendarRenderer = require('hotel_calendar.PMSCalendarRenderer'); - - var PMSHotelCalendarRenderer = PMSCalendarRenderer.include({ - - update_buttons_counter_channel_connector: function (nreservations, nissues) { - // Cloud Reservations - var $text = this.$el.find('#btn_channel_manager_request .cloud-text'); - $text.text(nreservations); - if (nreservations > 0) { - $text.parent().parent().addClass('button-highlight'); - $text.parent().addClass('incoming'); - } else { - $text.parent().parent().removeClass('button-highlight'); - $text.parent().removeClass('incoming'); - } - - // Issues - var $ninfo = this.$el.find('#pms-menu #btn_action_issues div.ninfo'); - $ninfo.text(nissues); - if (nissues) { - $ninfo.parent().parent().addClass('button-highlight'); - } else { - $ninfo.parent().parent().removeClass('button-highlight'); - } - }, - - init_calendar_view: function () { - var self = this; - return this._super().then(function () { - self.$el.find('#btn_channel_manager_request').on('click', function (ev) { - self.do_action("hotel_calendar_channel_connector.hotel_reservation_action_manager_request"); - }); - }); - }, - - _generate_search_domain: function(tsearch, type) { - var domain = this._super(tsearch, type); - - if (type === 'book') { - domain.splice(0, 0, '|'); - domain.push('|', - ['channel_bind_ids.external_id', 'ilike', tsearch], - ['channel_bind_ids.ota_reservation_id', 'ilike', tsearch]); - } - - return domain; - }, - - _generate_search_res_model: function(type) { - var [model, title] = this._super(type); - if (type === 'book') { - model = 'hotel.reservation'; - } - return [model, title]; - }, - - }); - - return PMSHotelCalendarRenderer; -}); diff --git a/hotel_calendar_channel_connector/static/src/xml/hotel_calendar_templates.xml b/hotel_calendar_channel_connector/static/src/xml/hotel_calendar_templates.xml deleted file mode 100644 index 9210f655e..000000000 --- a/hotel_calendar_channel_connector/static/src/xml/hotel_calendar_templates.xml +++ /dev/null @@ -1,25 +0,0 @@ - diff --git a/hotel_calendar_channel_connector/static/src/xml/hotel_calendar_view.xml b/hotel_calendar_channel_connector/static/src/xml/hotel_calendar_view.xml deleted file mode 100644 index 53cc525aa..000000000 --- a/hotel_calendar_channel_connector/static/src/xml/hotel_calendar_view.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - -
      - -
      -
      - -
      -
      -
      - - - - -
    • Channel:
    • -
      -
      -
      - -
      diff --git a/hotel_calendar_channel_connector/tests/__init__.py b/hotel_calendar_channel_connector/tests/__init__.py deleted file mode 100644 index 9920f3f1e..000000000 --- a/hotel_calendar_channel_connector/tests/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -#from . import test_folio diff --git a/hotel_calendar_channel_connector/tests/test_folio.py b/hotel_calendar_channel_connector/tests/test_folio.py deleted file mode 100644 index 824d9a05f..000000000 --- a/hotel_calendar_channel_connector/tests/test_folio.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from datetime import timedelta -from odoo.addons.hotel_calendar.tests.common import TestHotelCalendar -from odoo.addons.hotel import date_utils - - -class TestHotelReservations(TestHotelCalendar): - - def test_cancel_folio(self): - now_utc_dt = date_utils.now() diff --git a/hotel_calendar_channel_connector/views/actions.xml b/hotel_calendar_channel_connector/views/actions.xml deleted file mode 100644 index ee379e1f8..000000000 --- a/hotel_calendar_channel_connector/views/actions.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - Reservations to Assign from Channel - hotel.reservation - tree,form - - [('to_assign','=',True)] - - - - - - Channel Connector Issues - hotel.channel.connector.issue - tree,form - [('to_read','=',True)] - - - - diff --git a/hotel_calendar_channel_connector/views/general.xml b/hotel_calendar_channel_connector/views/general.xml deleted file mode 100644 index e221334b0..000000000 --- a/hotel_calendar_channel_connector/views/general.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - diff --git a/hotel_calendar_channel_connector/views/hotel_reservation.xml b/hotel_calendar_channel_connector/views/hotel_reservation.xml deleted file mode 100644 index 5c1d7aa76..000000000 --- a/hotel_calendar_channel_connector/views/hotel_reservation.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - hotel.toassign.reservation.tree - hotel.reservation - - - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - channel.backend.tree - channel.backend - - - - - - - - - - Hotel Channel Backends - channel.backend - tree,form - - - - diff --git a/hotel_channel_connector/views/channel_hotel_reservation_views.xml b/hotel_channel_connector/views/channel_hotel_reservation_views.xml deleted file mode 100644 index 10bed411d..000000000 --- a/hotel_channel_connector/views/channel_hotel_reservation_views.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - channel.hotel.reservation.form - channel.hotel.reservation - -
      - - - - - - - -
      -
      -
      - - - channel.hotel.reservation.tree - channel.hotel.reservation - - - - - - - -
      diff --git a/hotel_channel_connector/views/channel_hotel_room_type_availability_views.xml b/hotel_channel_connector/views/channel_hotel_room_type_availability_views.xml deleted file mode 100644 index 1ded8cfed..000000000 --- a/hotel_channel_connector/views/channel_hotel_room_type_availability_views.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - channel.hotel.room.type.availability.form - channel.hotel.room.type.availability - -
      - - - - - - - - - - - - -
      -
      -
      - - - channel.hotel.room.type.availability.tree - channel.hotel.room.type.availability - - - - - - - - - -
      diff --git a/hotel_channel_connector/views/channel_hotel_room_type_restriction_item_views.xml b/hotel_channel_connector/views/channel_hotel_room_type_restriction_item_views.xml deleted file mode 100644 index 697439ae8..000000000 --- a/hotel_channel_connector/views/channel_hotel_room_type_restriction_item_views.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - channel.hotel.room.type.restriction.item.form - channel.hotel.room.type.restriction.item - -
      - - - - - - - -
      -
      -
      - - - channel.hotel.room.type.restriction.item.tree - channel.hotel.room.type.restriction.item - - - - - - - -
      diff --git a/hotel_channel_connector/views/channel_hotel_room_type_restriction_views.xml b/hotel_channel_connector/views/channel_hotel_room_type_restriction_views.xml deleted file mode 100644 index b468bce5e..000000000 --- a/hotel_channel_connector/views/channel_hotel_room_type_restriction_views.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - channel.hotel.room.type.restriction.form - channel.hotel.room.type.restriction - -
      - - - - - - - -
      -
      -
      - - - channel.hotel.room.type.restriction.tree - channel.hotel.room.type.restriction - - - - - - - - - Hotel Channel Connector Bindings - channel.hotel.room.type.restriction - tree,form - [] - - -
      diff --git a/hotel_channel_connector/views/channel_hotel_room_type_views.xml b/hotel_channel_connector/views/channel_hotel_room_type_views.xml deleted file mode 100644 index af7fe19ac..000000000 --- a/hotel_channel_connector/views/channel_hotel_room_type_views.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - channel.hotel.room.type.form - channel.hotel.room.type - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      -
      - - - channel.hotel.room.type.tree - channel.hotel.room.type - - - - - - - - - Hotel Channel Connector Bindings - channel.hotel.room.type - tree,form - [] - -
      diff --git a/hotel_channel_connector/views/channel_ota_info_views.xml b/hotel_channel_connector/views/channel_ota_info_views.xml deleted file mode 100644 index b0afa0036..000000000 --- a/hotel_channel_connector/views/channel_ota_info_views.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - channel.ota.info.form - channel.ota.info - -
      - - - - - - - - - - -
      -
      -
      - - - - channel.ota.info.tree - channel.ota.info - - - - - - - - -
      diff --git a/hotel_channel_connector/views/channel_product_pricelist_item_views.xml b/hotel_channel_connector/views/channel_product_pricelist_item_views.xml deleted file mode 100644 index 65329ae92..000000000 --- a/hotel_channel_connector/views/channel_product_pricelist_item_views.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - channel.product.pricelist.item.form - channel.product.pricelist.item - -
      - - - - -
      -
      -
      - - - channel.hotel.product.pricelist.item.tree - channel.product.pricelist.item - - - - - - - -
      diff --git a/hotel_channel_connector/views/channel_product_pricelist_views.xml b/hotel_channel_connector/views/channel_product_pricelist_views.xml deleted file mode 100644 index b0ae78574..000000000 --- a/hotel_channel_connector/views/channel_product_pricelist_views.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - channel.product.pricelist.form - channel.product.pricelist - -
      - - - - - - - - - -
      -
      -
      - - - channel.hotel.product.pricelist.tree - channel.product.pricelist - - - - - - - - - Hotel Channel Connector Bindings - channel.product.pricelist - tree,form - [] - - -
      diff --git a/hotel_channel_connector/views/hotel_channel_connector_issue_views.xml b/hotel_channel_connector/views/hotel_channel_connector_issue_views.xml deleted file mode 100644 index 0ed44f164..000000000 --- a/hotel_channel_connector/views/hotel_channel_connector_issue_views.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - hotel.channel.connector.issue.form - hotel.channel.connector.issue - -
      - -
      - -
      - - - - - - - - - - - - - - - -
      -
      -
      -
      - - - - hotel.channel.connector.issue.tree - hotel.channel.connector.issue - tree - - - - - - - - - - - - - - - - hotel.channel.connector.issue.search - hotel.channel.connector.issue - - - - - - - - - - - - - - - - Hotel Channel Connector Issues - hotel.channel.connector.issue - tree,form - - {"search_default_to_read":True} - - -
      diff --git a/hotel_channel_connector/views/hotel_room_type_availability_views.xml b/hotel_channel_connector/views/hotel_room_type_availability_views.xml deleted file mode 100644 index b7ac713cc..000000000 --- a/hotel_channel_connector/views/hotel_room_type_availability_views.xml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - hotel.room.type.availability.form - hotel.room.type.availability - -
      - - - -

      -
      - - - - - - - - - - - - - - - - -
      -
      - - -
      -
      -
      -
      - - - - hotel.room.type.availability.tree - hotel.room.type.availability - - - - - - - - - - - - - - Room Type Availability - hotel.room.type.availability - tree,form - - - - -
      diff --git a/hotel_channel_connector/views/inherited_hotel_folio_views.xml b/hotel_channel_connector/views/inherited_hotel_folio_views.xml deleted file mode 100644 index de8bb3a13..000000000 --- a/hotel_channel_connector/views/inherited_hotel_folio_views.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - hotel.folio - - - - - - - - - - - diff --git a/hotel_channel_connector/views/inherited_hotel_reservation_views.xml b/hotel_channel_connector/views/inherited_hotel_reservation_views.xml deleted file mode 100644 index 5a9c97e58..000000000 --- a/hotel_channel_connector/views/inherited_hotel_reservation_views.xml +++ /dev/null @@ -1,118 +0,0 @@ - - - - - hotel.reservation - - - - - - - - - - - - - - - - - - - - - - - - - - - - {'readonly': [('is_from_ota','=',True),('able_to_modify_channel','=',False)]} - - - {'readonly': [('is_from_ota','=',True),('able_to_modify_channel','=',False)]} - - - {'readonly': [('is_from_ota','=',True),('able_to_modify_channel','=',False)]} - - - {'readonly': [('is_from_ota','=',True),('able_to_modify_channel','=',False)]} - - - {'readonly': [('is_from_ota','=',True),('able_to_modify_channel','=',False)]} - - - {'readonly': [('parent.is_from_ota','=',True),('parent.able_to_modify_channel','=',False)]} - True - - - - - - - - hotel.reservation - - - - - - - - - - - {'readonly': [('is_from_ota', '!=', False)]} - - - {'readonly': [('is_from_ota', '!=', False)]} - - - - - - - hotel.reservation - - - - - - - - - - - - - - - - view.hotel.reservation.graph - hotel.reservation - - - - - - - - - - - view.hotel.pivot.graph - hotel.reservation - - - - - - - - - - diff --git a/hotel_channel_connector/views/inherited_hotel_room_type_restriction_item_views.xml b/hotel_channel_connector/views/inherited_hotel_room_type_restriction_item_views.xml deleted file mode 100644 index 45f134e40..000000000 --- a/hotel_channel_connector/views/inherited_hotel_room_type_restriction_item_views.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - hotel.room.type.restriction.item - - - - - - - - - - - - - - - - - - - diff --git a/hotel_channel_connector/views/inherited_hotel_room_type_restriction_views.xml b/hotel_channel_connector/views/inherited_hotel_room_type_restriction_views.xml deleted file mode 100644 index 730b622f3..000000000 --- a/hotel_channel_connector/views/inherited_hotel_room_type_restriction_views.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - hotel.room.type.restriction - - - - - - - - - - - - - - - - - - - - - - - - - - -
      - -
      - - - - - - - budget.tree (in hotel_data_bi) - budget - - - - - - - - - - - - diff --git a/hotel_data_bi/views/inherit_res_company.xml b/hotel_data_bi/views/inherit_res_company.xml deleted file mode 100644 index 7f610c7e0..000000000 --- a/hotel_data_bi/views/inherit_res_company.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - databi.config.view_company_form - res.company - - - - - - - - - - - - - - diff --git a/hotel_door_codes/README.rst b/hotel_door_codes/README.rst deleted file mode 100644 index 39b189275..000000000 --- a/hotel_door_codes/README.rst +++ /dev/null @@ -1,13 +0,0 @@ -DOOR CODES -========== - -Generate HOTEL DOOR CODES - - -Credits -======= - -Creator ------------- - -* Jose Luis Algara Toledo diff --git a/hotel_door_codes/__init__.py b/hotel_door_codes/__init__.py deleted file mode 100644 index d81805226..000000000 --- a/hotel_door_codes/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018-2019 Jose Luis Algara Toledo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from . import models -from . import wizard diff --git a/hotel_door_codes/__manifest__.py b/hotel_door_codes/__manifest__.py deleted file mode 100644 index b08654307..000000000 --- a/hotel_door_codes/__manifest__.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Odoo, Open Source Management Solution -# Copyright (C) 2018-2019 Jose Luis Algara Toledo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## - -{ - 'name': 'Hotel Door Codes', - 'version': '3.0', - 'author': "Jose Luis Algara Toledo ", - 'website': 'https://www.aldahotels.com', - 'category': 'hotel code', - 'summary': "Generate Hotel door codes, in Pseudo random system", - 'description': "Hotel Door Codes. Now multihotel", - 'depends': [ - 'hotel', - # 'hotel_l10n_es' - ], - 'data': [ - 'wizard/door_code.xml', - 'data/menus.xml', - 'views/inherit_hotel_property.xml', - 'views/inherit_hotel_reservation.xml', - # 'views/inherit_report_viajero.xml', - ], - 'qweb': [], - 'test': [ - ], - 'installable': True, - 'auto_install': False, - 'application': False, - 'license': 'AGPL-3', -} diff --git a/hotel_door_codes/data/menus.xml b/hotel_door_codes/data/menus.xml deleted file mode 100644 index ca4aa8714..000000000 --- a/hotel_door_codes/data/menus.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/hotel_door_codes/i18n/es.po b/hotel_door_codes/i18n/es.po deleted file mode 100644 index 2dc9270f6..000000000 --- a/hotel_door_codes/i18n/es.po +++ /dev/null @@ -1,162 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * hotel_door_codes -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 11.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-27 12:40+0000\n" -"PO-Revision-Date: 2019-11-27 17:33+0100\n" -"Last-Translator: Jose Luis Algara Toledo \n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: \n" -"Language: es\n" -"X-Generator: Poedit 2.0.6\n" - -#. module: hotel_door_codes -#: code:addons/hotel_door_codes/wizard/door_code.py:66 -#, python-format -msgid " to:" -msgstr " a:" - -#. module: hotel_door_codes -#: code:addons/hotel_door_codes/models/inherit_hotel_reservation.py:51 -#, python-format -msgid " to: " -msgstr " a: " - -#. module: hotel_door_codes -#: model:ir.model.fields,field_description:hotel_door_codes.field_hotel_reservation_box_number -msgid "Box Number" -msgstr "Numero de caja" - -#. module: hotel_door_codes -#: model:ir.model.fields,field_description:hotel_door_codes.field_hotel_reservation_box_code -msgid "Box code" -msgstr "Código de caja" - -#. module: hotel_door_codes -#: model:ir.model.fields,field_description:hotel_door_codes.field_hotel_property_postcode -msgid "Characters after the code" -msgstr "Caracteres después del código" - -#. module: hotel_door_codes -#: model:ir.model.fields,field_description:hotel_door_codes.field_hotel_property_precode -msgid "Characters before the door code" -msgstr "Caracteres antes del código de la puerta" - -#. module: hotel_door_codes -#: model:ir.ui.menu,name:hotel_door_codes.menu_hotel_door_code -msgid "Check door codes" -msgstr "Ver códigos de puertas" - -#. module: hotel_door_codes -#: model:ir.model,name:hotel_door_codes.model_res_company -msgid "Companies" -msgstr "Empresas" - -#. module: hotel_door_codes -#: model:ir.model.fields,field_description:hotel_door_codes.field_door_code_create_uid -msgid "Created by" -msgstr "Creado por" - -#. module: hotel_door_codes -#: model:ir.model.fields,field_description:hotel_door_codes.field_door_code_create_date -msgid "Created on" -msgstr "Creado en" - -#. module: hotel_door_codes -#: model:ir.model.fields,field_description:hotel_door_codes.field_door_code_display_name -msgid "Display Name" -msgstr "Nombre para mostrar" - -#. module: hotel_door_codes -#: model:ir.actions.act_window,name:hotel_door_codes.door_code_act -#: model:ir.model,name:hotel_door_codes.model_door_code -#: model:ir.ui.view,arch_db:hotel_door_codes.hotel_door_code_view -msgid "Door Code Generator" -msgstr "Generador de códigos de puerta" - -#. module: hotel_door_codes -#: model:ir.model.fields,field_description:hotel_door_codes.field_hotel_reservation_door_codes -#: model:ir.ui.view,arch_db:hotel_door_codes.door_code_view_property_form -msgid "Door Codes" -msgstr "Códigos de puerta" - -#. module: hotel_door_codes -#: model:ir.model.fields,field_description:hotel_door_codes.field_door_code_door_code -msgid "Door code" -msgstr "Código de puerta" - -#. module: hotel_door_codes -#: model:ir.model.fields,field_description:hotel_door_codes.field_door_code_date_end -msgid "End of period" -msgstr "Fin del periodo" - -#. module: hotel_door_codes -#: code:addons/hotel_door_codes/models/inherit_hotel_reservation.py:42 -#: code:addons/hotel_door_codes/wizard/door_code.py:57 -#, python-format -msgid "Entry Code: " -msgstr "Código de entrada: " - -#. module: hotel_door_codes -#: model:ir.ui.view,arch_db:hotel_door_codes.door_code_reservation_form -msgid "Entry Codes" -msgstr "Códigos de entrada" - -#. module: hotel_door_codes -#: model:ir.ui.view,arch_db:hotel_door_codes.hotel_door_code_view -msgid "Generate Code" -msgstr "Generar códigos" - -#. module: hotel_door_codes -#: model:ir.model,name:hotel_door_codes.model_hotel_property -msgid "Hotel" -msgstr "Hotel" - -#. module: hotel_door_codes -#: model:ir.model,name:hotel_door_codes.model_hotel_reservation -msgid "Hotel Reservation" -msgstr "Hotel Reservation" - -#. module: hotel_door_codes -#: model:ir.model.fields,field_description:hotel_door_codes.field_door_code_id -msgid "ID" -msgstr "ID" - -#. module: hotel_door_codes -#: code:addons/hotel_door_codes/wizard/door_code.py:64 -#, python-format -msgid "It will change on " -msgstr "Cambiará el " - -#. module: hotel_door_codes -#: code:addons/hotel_door_codes/models/inherit_hotel_reservation.py:49 -#, python-format -msgid "It will change on Monday " -msgstr "Cambiará el lunes " - -#. module: hotel_door_codes -#: model:ir.model.fields,field_description:hotel_door_codes.field_door_code___last_update -msgid "Last Modified on" -msgstr "Última modificación el" - -#. module: hotel_door_codes -#: model:ir.model.fields,field_description:hotel_door_codes.field_door_code_write_uid -msgid "Last Updated by" -msgstr "Última actualización por" - -#. module: hotel_door_codes -#: model:ir.model.fields,field_description:hotel_door_codes.field_door_code_write_date -msgid "Last Updated on" -msgstr "Ultima actualización en" - -#. module: hotel_door_codes -#: model:ir.model.fields,field_description:hotel_door_codes.field_door_code_date_start -msgid "Start of the period" -msgstr "Comienzo del periodo" diff --git a/hotel_door_codes/models/__init__.py b/hotel_door_codes/models/__init__.py deleted file mode 100644 index 6ec88ba49..000000000 --- a/hotel_door_codes/models/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018-2019 Alda Hotels -# Jose Luis Algara -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from . import inherit_hotel_property -from . import inherit_hotel_reservation diff --git a/hotel_door_codes/models/inherit_hotel_property.py b/hotel_door_codes/models/inherit_hotel_property.py deleted file mode 100644 index adad5943b..000000000 --- a/hotel_door_codes/models/inherit_hotel_property.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018-2019 Alda Hotels -# Jose Luis Algara -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from openerp import models, fields - - -class InheritHotelProperty(models.Model): - _inherit = 'hotel.property' - - precode = fields.Char('Characters before the door code', default=' ') - postcode = fields.Char('Characters after the code', default=' ') diff --git a/hotel_door_codes/models/inherit_hotel_reservation.py b/hotel_door_codes/models/inherit_hotel_reservation.py deleted file mode 100644 index 1353a279c..000000000 --- a/hotel_door_codes/models/inherit_hotel_reservation.py +++ /dev/null @@ -1,79 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018-2019 Alda Hotels -# Jose Luis Algara -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from openerp import models, fields, api, _ -from datetime import datetime, timedelta -from openerp.tools import DEFAULT_SERVER_DATE_FORMAT - - -class Inherit_hotel_reservation(models.Model): - _inherit = 'hotel.reservation' - - # Default methods - - def _compute_door_codes(self): - for res in self: - entrada = datetime.strptime( - res.checkin[:10], DEFAULT_SERVER_DATE_FORMAT) - if datetime.weekday(entrada) == 0: - entrada = entrada + timedelta(days=1) - salida = datetime.strptime( - res.checkout[:10], DEFAULT_SERVER_DATE_FORMAT) - if datetime.weekday(salida) == 0: - salida = salida - timedelta(days=1) - codes = (_('Entry Code: ') + - '' + - res.doorcode4(datetime.strftime(entrada, "%Y-%m-%d")) + - '') - while entrada <= salida: - if datetime.weekday(entrada) == 0: - codes += ("
      " + - _('It will change on Monday ') + - datetime.strftime(entrada, "%d-%m-%Y") + - _(' to: ') + - '' + - res.doorcode4(datetime.strftime( - entrada, "%Y-%m-%d")) + - '') - entrada = entrada + timedelta(days=1) - res.door_codes = codes - - # Fields declaration - door_codes = fields.Html( - 'Door Codes', - compute='_compute_door_codes') - box_number = fields.Integer('Box Number') - box_code = fields.Char('Box code') - - - def doorcode4(self, fecha): - # Calculate de Door Code... need a date in String format "%Y-%m-%d" - compan = self.env.user.hotel_id - if not compan.precode: - compan.precode = " " - if not compan.postcode: - compan.postcode = " " - d = datetime.strptime(fecha, DEFAULT_SERVER_DATE_FORMAT) - dia_semana = datetime.weekday(d) # Dias a restar y ponerlo en lunes - d = d - timedelta(days=dia_semana) - dtxt = d.strftime('%s.%%06d') % d.microsecond - dtxt = compan.precode + dtxt[4:8] + compan.postcode - return dtxt diff --git a/hotel_door_codes/static/description/icon.png b/hotel_door_codes/static/description/icon.png deleted file mode 100644 index 0d7c99f06fafe4f012cab618e056e8652ec140d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24484 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 KjTy_-E#^N^!qF`N diff --git a/hotel_door_codes/views/inherit_hotel_property.xml b/hotel_door_codes/views/inherit_hotel_property.xml deleted file mode 100644 index 602a1c46e..000000000 --- a/hotel_door_codes/views/inherit_hotel_property.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - door_code.config.view_property_form - hotel.property - - - - - - - - - - - - diff --git a/hotel_door_codes/views/inherit_hotel_reservation.xml b/hotel_door_codes/views/inherit_hotel_reservation.xml deleted file mode 100644 index cc66dfea7..000000000 --- a/hotel_door_codes/views/inherit_hotel_reservation.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - door_code.reservation_form - hotel.reservation - - - - - - - - - - - - - - - diff --git a/hotel_door_codes/wizard/__init__.py b/hotel_door_codes/wizard/__init__.py deleted file mode 100644 index 180966b56..000000000 --- a/hotel_door_codes/wizard/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018-2019 Jose Luis Algara Toledo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from . import door_code diff --git a/hotel_door_codes/wizard/door_code.py b/hotel_door_codes/wizard/door_code.py deleted file mode 100644 index 2a4a8416c..000000000 --- a/hotel_door_codes/wizard/door_code.py +++ /dev/null @@ -1,77 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Odoo, Open Source Management Solution -# Copyright (C) 2018-2019 Jose Luis Algara Toledo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from datetime import datetime, timedelta -from odoo import api, fields, models, _ -from odoo.tools import DEFAULT_SERVER_DATE_FORMAT - - -class DoorCodeWizard(models.TransientModel): - _name = 'door_code' - _description = 'Door Code Generator' - - # Default methods - - def _get_default_date_start(self): - return datetime.now().strftime(DEFAULT_SERVER_DATE_FORMAT) - - # Fields declaration - date_start = fields.Date( - "Start of the period", - default=_get_default_date_start) - date_end = fields.Date( - "End of period", - default=_get_default_date_start) - door_code = fields.Html("Door code") - - - def check_code(self): - reservation = self.env['hotel.reservation'] - - entrada = datetime.strptime( - self.date_start, DEFAULT_SERVER_DATE_FORMAT) - if datetime.weekday(entrada) == 0: - entrada = entrada + timedelta(days=1) - salida = datetime.strptime( - self.date_end, DEFAULT_SERVER_DATE_FORMAT) - if datetime.weekday(salida) == 0: - salida = salida - timedelta(days=1) - codes = (_('Entry Code: ') + - '' + - reservation.doorcode4(self.date_start) + - '') - while entrada <= salida: - if datetime.weekday(entrada) == 0: - codes += ("
      " + - _('It will change on ') + - datetime.strftime(entrada, "%d-%m-%Y") + - _(' to:') + - '' + - reservation.doorcode4(datetime.strftime( - entrada, "%Y-%m-%d")) + - '') - entrada = entrada + timedelta(days=1) - - return self.write({ - 'door_code': codes, - 'name': 'Ya te digo', - 'clear_breadcrumb': True, - 'target': 'current', - }) diff --git a/hotel_door_codes/wizard/door_code.xml b/hotel_door_codes/wizard/door_code.xml deleted file mode 100644 index ea1fc061c..000000000 --- a/hotel_door_codes/wizard/door_code.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - Door codes - door_code - -
      - - - - - - - - - - -
      -
      -
      -
      -
      -
      - -
      -
      diff --git a/hotel_ine/__init__.py b/hotel_ine/__init__.py deleted file mode 100644 index c6369ae7d..000000000 --- a/hotel_ine/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2019 Alda Hotels -# Jose Luis Algara -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from . import wizard -from . import models diff --git a/hotel_ine/__manifest__.py b/hotel_ine/__manifest__.py deleted file mode 100644 index 00247262e..000000000 --- a/hotel_ine/__manifest__.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Odoo, Open Source Management Solution -# Copyright (C) 2019 Jose Luis Algara Toledo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -{ - 'name': 'Hotel Ine', - 'description': """ - Create de INE Report""", - 'version': '1.0.0', - 'license': 'AGPL-3', - 'summary': "Export hotel data for INE report", - 'author': "Jose Luis Algara (Alda hotels) ", - 'website': 'www.aldahotels.com', - 'depends': ['hotel', 'hotel_l10n_es'], - 'category': 'hotel/ine', - 'data': [ - 'wizard/inewizard.xml', - 'views/inherited_hotel_room_view.xml', - ], - 'demo': [ - ], - 'installable': True, - 'auto_install': False, - 'application': False, -} diff --git a/hotel_ine/models/__init__.py b/hotel_ine/models/__init__.py deleted file mode 100644 index c96e3b034..000000000 --- a/hotel_ine/models/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import inherited_hotel_room diff --git a/hotel_ine/models/inherited_hotel_room.py b/hotel_ine/models/inherited_hotel_room.py deleted file mode 100644 index cbd11348a..000000000 --- a/hotel_ine/models/inherited_hotel_room.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright 2019 Jose Luis Algara -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields - - -class HotelRoom(models.Model): - _inherit = 'hotel.room' - - in_ine = fields.Boolean('Included in the INE statistics', default=True) diff --git a/hotel_ine/static/description/icon.png b/hotel_ine/static/description/icon.png deleted file mode 100755 index c066d3473223c2250d2b12a81407b1fa68bc0470..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24576 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 - - - - hotel.room - - - - - - - - - - - - - diff --git a/hotel_ine/wizard/__init__.py b/hotel_ine/wizard/__init__.py deleted file mode 100644 index 53a5cd324..000000000 --- a/hotel_ine/wizard/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2019 Alda Hotels -# Jose Luis Algara -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## - -from . import inewizard diff --git a/hotel_ine/wizard/inewizard.py b/hotel_ine/wizard/inewizard.py deleted file mode 100644 index 56bd0962b..000000000 --- a/hotel_ine/wizard/inewizard.py +++ /dev/null @@ -1,321 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2017-19 Alda Hotels -# Jose Luis Algara -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp import models, fields, api -import base64 -import datetime -import calendar -import xml.etree.cElementTree as ET -from odoo.exceptions import UserError -import logging -_logger = logging.getLogger(__name__) - - -def get_years(): - year_list = [] - for i in range(2017, get_year()+1): - year_list.append((i, str(i))) - return year_list - - -def get_year(): - now = datetime.datetime.now() - return int(now.year) - - -def get_month(): - now = datetime.datetime.now() - month = int(now.month)-1 - if month <= 0: - month = 12 - return month - - -class Wizard(models.TransientModel): - _name = 'ine.wizard' - - txt_filename = fields.Char() - txt_binary = fields.Binary() - - ine_month = fields.Selection([(1, 'January'), (2, 'February'), - (3, 'March'), (4, 'April'), - (5, 'May'), (6, 'June'), (7, 'July'), - (8, 'August'), (9, 'September'), - (10, 'October'), (11, 'November'), - (12, 'December'), ], - string='Month', default=get_month()) - ine_year = fields.Selection(get_years(), default=get_year(), string='Year') - - adr_screen = fields.Char() - rev_screen = fields.Char() - - def generate_file(self): - _logger.warning("Start Export INE XML file") - last_day = calendar.monthrange(self.ine_year, self.ine_month)[1] - ine_start_search = datetime.date(self.ine_year, self.ine_month, 1) - ine_end_search = ine_start_search + datetime.timedelta(days=last_day) - compan = self.env.user.company_id - active_room = self.env['hotel.room'].search([('in_ine', '=', True)]) - message = "" - if not compan.property_name: - message = 'The NAME of the property is not established' - if not compan.name: - message = 'The NAME of the company is not established' - if not compan.vat: - message = 'The VAT is not established' - if not compan.ine_tourism: - message = 'The tourism number of the property is not established' - if message != "": - raise UserError(message) - return - encuesta = ET.Element("ENCUESTA") - cabezera = ET.SubElement(encuesta, "CABECERA") - fecha = ET.SubElement(cabezera, "FECHA_REFERENCIA") - ET.SubElement(fecha, "MES").text = "%02d" % (self.ine_month) - ET.SubElement(fecha, "ANYO").text = str(self.ine_year) - ET.SubElement(cabezera, "DIAS_ABIERTO_MES_REFERENCIA").text = ( - str(last_day)) - ET.SubElement(cabezera, "RAZON_SOCIAL").text = compan.name - ET.SubElement(cabezera, "NOMBRE_ESTABLECIMIENTO").text = ( - compan.property_name) - ET.SubElement(cabezera, "CIF_NIF").text = compan.vat[2:].strip() - ET.SubElement(cabezera, "NUMERO_REGISTRO").text = compan.ine_tourism - ET.SubElement(cabezera, "DIRECCION").text = compan.street - ET.SubElement(cabezera, "CODIGO_POSTAL").text = compan.zip - ET.SubElement(cabezera, "LOCALIDAD").text = compan.city - ET.SubElement(cabezera, "MUNICIPIO").text = compan.city - ET.SubElement(cabezera, "PROVINCIA" - ).text = compan.state_id.display_name - ET.SubElement(cabezera, "TELEFONO_1").text = ( - compan.phone.replace(' ', '')[0:12]) - ET.SubElement(cabezera, "TIPO").text = ( - compan.ine_category_id.category_type) - ET.SubElement(cabezera, "CATEGORIA").text = compan.ine_category_id.name - - ET.SubElement(cabezera, "HABITACIONES").text = str(len(active_room)) - ET.SubElement(cabezera, "PLAZAS_DISPONIBLES_SIN_SUPLETORIAS" - ).text = str(compan.ine_seats) - ET.SubElement(cabezera, "URL").text = compan.website - alojamiento = ET.SubElement(encuesta, "ALOJAMIENTO") - - all_room_nights = self.env['hotel.reservation.line'].search([ - ('date', '>=', ine_start_search), - ('date', '<', ine_end_search), - ('reservation_id.room_id.in_ine', '=', True), - ('reservation_id.state', '!=', "cancelled"), - ('reservation_id.reservation_type', '=', 'normal'), - ]) - room_nights = all_room_nights.filtered( - lambda n: (self.get_codeine(n.reservation_id))) - - # Creating the empty dictionary system - dic_tabla = [] - for room_night in room_nights: - ine_code = self.get_codeine(room_night.reservation_id) - if not next((item for item in dic_tabla if item["ine"] == ine_code), False): - for x in range(1, last_day+1): - dic_tabla.append({'ine': ine_code, - 'dia': x, - 'entradas': 0, - 'salidas': 0, - 'pernocta': 0 - }) - - # Adding overnight stays per day and INE code - pernocta_total = [] - for dia in range(1, last_day+1): - pernocta_total.append(0) - for room_night in room_nights.filtered( - lambda x: x.date == str(self.ine_year)+'-'+str( - self.ine_month).zfill(2)+'-'+str(dia).zfill(2)): - ine_code = self.get_codeine(room_night.reservation_id) - for idx, val in enumerate(dic_tabla): - if val['ine'] == ine_code and val['dia'] == dia: - dic_tabla[idx]['pernocta'] += room_night.reservation_id.adults - - # Calculating outputs and entries - last_stay = 0 - for idx, row in enumerate(dic_tabla): - if dic_tabla[idx]['dia'] == 1: - last_stay = 0 - - if last_stay < dic_tabla[idx]['pernocta']: - dic_tabla[idx]['entradas'] += dic_tabla[idx]['pernocta'] - last_stay - elif last_stay > dic_tabla[idx]['pernocta']: - dic_tabla[idx]['salidas'] += last_stay - dic_tabla[idx][ - 'pernocta'] - # _logger.warning("%s: %s Perenocta: %s In: %s Out: %s Last=%s", dic_tabla[idx]['ine'], dic_tabla[idx]['dia'], dic_tabla[idx]['pernocta'], dic_tabla[idx]['entradas'], dic_tabla[idx]['salidas'],last_stay) - last_stay = dic_tabla[idx]['pernocta'] - pernocta_total[(dic_tabla[idx]['dia'])-1] += dic_tabla[idx]['pernocta'] - - # "Print" outputs and entries - ine_residen = "" - for idx, row in enumerate(dic_tabla): - if ine_residen != dic_tabla[idx]['ine']: - ine_residen = dic_tabla[idx]['ine'] - residencia = ET.SubElement(alojamiento, "RESIDENCIA") - if len(dic_tabla[idx]['ine']) > 3: - ET.SubElement(residencia, "ID_PROVINCIA_ISLA" - ).text = str(dic_tabla[idx]['ine']) - else: - ET.SubElement(residencia, "ID_PAIS" - ).text = str(dic_tabla[idx]['ine']) - if ((dic_tabla[idx]['entradas'] != 0) - or (dic_tabla[idx]['salidas'] != 0) - or (dic_tabla[idx]['pernocta'] != 0)): - movimiento = ET.SubElement(residencia, "MOVIMIENTO") - ET.SubElement(movimiento, "N_DIA").text = ( - "%02d" % dic_tabla[idx]['dia']) - ET.SubElement(movimiento, "ENTRADAS").text = str( - dic_tabla[idx]['entradas']) - ET.SubElement(movimiento, "SALIDAS").text = str( - dic_tabla[idx]['salidas']) - ET.SubElement(movimiento, "PERNOCTACIONES").text = str( - dic_tabla[idx]['pernocta']) - - habitaciones = ET.SubElement(encuesta, "HABITACIONES") - # Bucle de HABITACIONES_MOVIMIENTO - - ingresos = 0 - habitaci = 0 - hab_vend = 0 - for dia in range(1, last_day+1): - suple = 0 - doble = 0 - dindi = 0 - otras = 0 - - habitaci += len(active_room) - habitaci -= self.env['hotel.reservation.line'].search([ - ('date', '=', str(self.ine_year)+'-'+str( - self.ine_month).zfill(2)+'-'+str(dia).zfill(2)), - ('reservation_id.reservation_type', '!=', 'normal'), - ], count=True) - for room_night in room_nights.filtered(lambda x: x.date == str( - self.ine_year)+'-'+str( - self.ine_month).zfill(2)+'-'+str( - dia).zfill(2)): - ingresos += room_night.price - hab_vend += 1 - - if room_night.reservation_id.room_id.capacity == 2: - if room_night.reservation_id.adults == 1: - dindi += 1 - else: - doble += 1 - else: - otras += 1 - if len(room_night.reservation_id.service_ids): - for service in room_night.reservation_id.service_ids: - if service.product_id.is_extra_bed: - suple += 1 - - # Here, we correct the extra beds - if pernocta_total[dia-1] > suple + compan.ine_seats: - suple = pernocta_total[dia-1] - compan.ine_seats - - habitaciones_m = ET.SubElement(habitaciones, - "HABITACIONES_MOVIMIENTO") - ET.SubElement(habitaciones_m, - "HABITACIONES_N_DIA").text = "%02d" % (dia) - ET.SubElement(habitaciones_m, - "PLAZAS_SUPLETORIAS").text = str(suple) - ET.SubElement(habitaciones_m, - "HABITACIONES_DOBLES_USO_DOBLE").text = str(doble) - ET.SubElement(habitaciones_m, - "HABITACIONES_DOBLES_USO_INDIVIDUAL").text = str( - dindi) - ET.SubElement(habitaciones_m, - "HABITACIONES_OTRAS").text = str(otras) - - precios = ET.SubElement(encuesta, "PRECIOS") - ET.SubElement(precios, - "REVPAR_MENSUAL").text = str(round(ingresos/habitaci, 2)) - ET.SubElement(precios, - "ADR_MENSUAL").text = str(round(ingresos/hab_vend, 2)) - ET.SubElement(precios, "ADR_TOUROPERADOR_TRADICIONAL").text = '0' - ET.SubElement(precios, - "PCTN_HABITACIONES_OCUPADAS_TOUROPERADOR_TRADICIONAL" - ).text = '0' - ET.SubElement(precios, - "ADR_TOUROPERADOR_ONLINE").text = '0' - ET.SubElement(precios, - "PCTN_HABITACIONES_OCUPADAS_TOUROPERADOR_ONLINE" - ).text = '0' - ET.SubElement(precios, - "ADR_EMPRESAS").text = '0' - ET.SubElement(precios, - "PCTN_HABITACIONES_OCUPADAS_EMPRESAS").text = '0' - ET.SubElement(precios, - "ADR_AGENCIA_DE_VIAJE_TRADICIONAL").text = '0' - ET.SubElement(precios, - "PCTN_HABITACIONES_OCUPADAS_AGENCIA_TRADICIONAL" - ).text = '0' - ET.SubElement(precios, "ADR_AGENCIA_DE_VIAJE_ONLINE").text = '0' - ET.SubElement(precios, - "PCTN_HABITACIONES_OCUPADAS_AGENCIA_ONLINE" - ).text = '0' - ET.SubElement(precios, "ADR_PARTICULARES").text = '0' - ET.SubElement(precios, - "PCTN_HABITACIONES_OCUPADAS_PARTICULARES").text = '0' - ET.SubElement(precios, - "ADR_GRUPOS").text = '0' - ET.SubElement(precios, - "PCTN_HABITACIONES_OCUPADAS_GRUPOS").text = '0' - ET.SubElement(precios, "ADR_INTERNET").text = '0' - ET.SubElement(precios, - "PCTN_HABITACIONES_OCUPADAS_INTERNET").text = '0' - ET.SubElement(precios, "ADR_OTROS").text = '0' - ET.SubElement(precios, - "PCTN_HABITACIONES_OCUPADAS_OTROS").text = '0' - - personal = ET.SubElement(encuesta, "PERSONAL_OCUPADO") - ET.SubElement(personal, "PERSONAL_NO_REMUNERADO").text = '0' - ET.SubElement(personal, - "PERSONAL_REMUNERADO_FIJO").text = str( - compan.ine_permanent_staff) - ET.SubElement(personal, - "PERSONAL_REMUNERADO_EVENTUAL").text = str( - compan.ine_eventual_staff) - - xmlstr = '' - xmlstr += ET.tostring(encuesta).decode('utf-8') - return self.write({ - 'txt_filename': 'INE_'+str(self.ine_month)+'_'+str( - self.ine_year) + '.' + 'xml', - 'adr_screen': 'ADR en el mes de la encuesta: '+str( - round(ingresos/habitaci, 2)) + '€ y ', - 'rev_screen': ' RevPar : '+str(round(ingresos/hab_vend, 2))+'€', - 'txt_binary': base64.encodestring(xmlstr.encode()) - }) - - @api.model - def get_codeine(self, reserva): - response = False - code = reserva[0].partner_id.code_ine_id - if code: - response = code.code - else: - for l in reserva[0].folio_id.checkin_partner_ids: - if l.code_ine_id: - response = l.code_ine_id.code - return response diff --git a/hotel_ine/wizard/inewizard.xml b/hotel_ine/wizard/inewizard.xml deleted file mode 100644 index 02a326938..000000000 --- a/hotel_ine/wizard/inewizard.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - INE File Download - ine.wizard - -
      - - - - - -
      -
      - - -
      - - -
      -
      - -
      - - -
      - - - - - - - - diff --git a/hotel_l10n_es/README.rst b/hotel_l10n_es/README.rst deleted file mode 100755 index bb1595313..000000000 --- a/hotel_l10n_es/README.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :alt: License: AGPL-3 - -Alda Hotels PMS-Checkin -============= -Checkin for Alda Hostels - - -Use -=== -Instal in Odoo and checkin - -Remember to complete the "hotel settings" configuration fields in the company file. - -Credits -======= - -Creator ------------- - -* Jose Luis Algara -* Darío Lodeiros diff --git a/hotel_l10n_es/__init__.py b/hotel_l10n_es/__init__.py deleted file mode 100755 index 18db0383d..000000000 --- a/hotel_l10n_es/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2017 Alda Hotels -# Jose Luis Algara -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## - -from . import models -from . import wizard diff --git a/hotel_l10n_es/__manifest__.py b/hotel_l10n_es/__manifest__.py deleted file mode 100755 index 5b9c8e978..000000000 --- a/hotel_l10n_es/__manifest__.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2017 Alda Hotels -# Jose Luis Algara -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## - - -{ - 'name': 'Hotel l10n_es', - 'version': '9.0.0.3', - 'author': "Jose Luis Algara", - 'website': "http://www.aldahotels.com", - 'category': 'Hotel', - 'summary': "", - 'description': "", - 'depends': [ - 'hotel', - 'partner_contact_gender', - 'partner_contact_birthdate', - 'partner_firstname', - 'partner_vat_unique', - ], - 'data': [ - 'data/code.ine.csv', - 'data/tourism.category.csv', - 'data/report_viajero_paperformat.xml', - 'report/report_parte_viajero.xml', - 'views/report_viajero.xml', - 'wizard/police_wizard.xml', - 'views/category_tourism.xml', - 'views/code_ine.xml', - 'views/inherit_res_company.xml', - 'views/inherit_hotel_checkin_partner_views.xml', - 'security/ir.model.access.csv', - 'views/inherit_res_partner.xml', - 'views/inherited_hotel_reservation_views.xml', - 'views/report_viajero_document.xml', - 'views/report_viajero_head.xml', - 'views/report_viajero_data.xml', - 'views/report_viajero.xml', - 'views/hotel_l10n_es_hotel_name.xml' - ], - 'test': [ - ], - 'css': ['static/src/css/hotel_l10n_es.css'], - 'installable': True, - 'auto_install': False, - 'application': False, - 'license': 'AGPL-3', -} diff --git a/hotel_l10n_es/data/code.ine.csv b/hotel_l10n_es/data/code.ine.csv deleted file mode 100755 index 29f4a84a2..000000000 --- a/hotel_l10n_es/data/code.ine.csv +++ /dev/null @@ -1,309 +0,0 @@ -"id","code","name" -"__export__.code_ine_1","AFG","Afganistán" -"__export__.code_ine_2","ALB","Albania" -"__export__.code_ine_3","DEU","Alemania" -"__export__.code_ine_4","AND","Andorra" -"__export__.code_ine_5","AGO","Angola" -"__export__.code_ine_6","AIA","Anguila" -"__export__.code_ine_7","ATG","Antigua y Barbuda" -"__export__.code_ine_8","ANT","Antillas Neerlandesas" -"__export__.code_ine_9","ATA","Antártida" -"__export__.code_ine_10","SAU","Arabia Saudita" -"__export__.code_ine_11","DZA","Argelia" -"__export__.code_ine_12","ARG","Argentina" -"__export__.code_ine_13","ARM","Armenia" -"__export__.code_ine_14","ABW","Aruba" -"__export__.code_ine_15","AUS","Australia" -"__export__.code_ine_16","AUT","Austria" -"__export__.code_ine_17","AZE","Azerbaiyán" -"__export__.code_ine_18","BHS","Bahamas" -"__export__.code_ine_19","BHR","Bahrein" -"__export__.code_ine_20","BGD","Bangladesh" -"__export__.code_ine_21","BRB","Barbados" -"__export__.code_ine_22","BLZ","Belice" -"__export__.code_ine_23","BEN","Benin" -"__export__.code_ine_24","BMU","Bermudas" -"__export__.code_ine_25","BTN","Bhután" -"__export__.code_ine_26","BLR","Bielorrusia" -"__export__.code_ine_27","BOL","Bolivia" -"__export__.code_ine_28","BIH","Bosnia-Herzegovina" -"__export__.code_ine_29","BWA","Botswana" -"__export__.code_ine_30","BRA","Brasil" -"__export__.code_ine_31","BRN","Brunéi" -"__export__.code_ine_32","BGR","Bulgaria" -"__export__.code_ine_33","BFA","Burkina Fasso" -"__export__.code_ine_34","BDI","Burundi" -"__export__.code_ine_35","BEL","Bélgica" -"__export__.code_ine_36","CPV","Cabo Verde" -"__export__.code_ine_37","KHM","Camboya" -"__export__.code_ine_38","CMR","Camerún" -"__export__.code_ine_39","CAN","Canadá" -"__export__.code_ine_40","TCD","Chad" -"__export__.code_ine_41","CHL","Chile" -"__export__.code_ine_42","CHN","China" -"__export__.code_ine_43","CYP","Chipre" -"__export__.code_ine_44","COL","Colombia" -"__export__.code_ine_45","COM","Comoras" -"__export__.code_ine_46","COG","Congo, República del" -"__export__.code_ine_47","COD","Congo, República Democrática del" -"__export__.code_ine_48","PRK","Corea, Rep. Popular Democrática" -"__export__.code_ine_49","KOR","Corea, República de" -"__export__.code_ine_50","CIV","Costa de Marfil" -"__export__.code_ine_51","CRI","Costa Rica" -"__export__.code_ine_52","HRV","Croacia" -"__export__.code_ine_53","CUB","Cuba" -"__export__.code_ine_54","DNK","Dinamarca" -"__export__.code_ine_55","DMA","Dominica" -"__export__.code_ine_56","ECU","Ecuador" -"__export__.code_ine_57","EGY","Egipto" -"__export__.code_ine_58","SLV","El Salvador" -"__export__.code_ine_59","ARE","Emiratos Arabes Unidos" -"__export__.code_ine_60","ERI","Eritrea" -"__export__.code_ine_61","SVK","Eslovaquia" -"__export__.code_ine_62","SVN","Eslovenia" -"__export__.code_ine_63","USA","Estados Unidos de América" -"__export__.code_ine_64","EST","Estonia" -"__export__.code_ine_65","ETH","Etiopía" -"__export__.code_ine_66","PHL","Filipinas" -"__export__.code_ine_67","FIN","Finlandia" -"__export__.code_ine_68","FRA","Francia" -"__export__.code_ine_69","GAB","Gabón" -"__export__.code_ine_70","GMB","Gambia" -"__export__.code_ine_71","GEO","Georgia" -"__export__.code_ine_72","GHA","Ghana" -"__export__.code_ine_73","GIB","Gibraltar" -"__export__.code_ine_74","GRD","Granada" -"__export__.code_ine_75","GRC","Grecia" -"__export__.code_ine_76","GRL","Groenlandia" -"__export__.code_ine_77","GLP","Guadalupe" -"__export__.code_ine_78","GUM","Guam" -"__export__.code_ine_79","GTM","Guatemala" -"__export__.code_ine_80","GUF","Guayana Francesa" -"__export__.code_ine_81","GIN","Guinea" -"__export__.code_ine_82","GNQ","Guinea Ecuatorial" -"__export__.code_ine_83","GNB","Guinea-Bissau" -"__export__.code_ine_84","GUY","Guyana" -"__export__.code_ine_85","HTI","Haití" -"__export__.code_ine_86","HND","Honduras" -"__export__.code_ine_87","HKG","Hong-Kong" -"__export__.code_ine_88","HUN","Hungría" -"__export__.code_ine_89","IND","India" -"__export__.code_ine_90","IDN","Indonesia" -"__export__.code_ine_91","IRQ","Irak" -"__export__.code_ine_92","IRL","Irlanda" -"__export__.code_ine_93","IRN","Irán" -"__export__.code_ine_94","BVT","Isla Bouvert" -"__export__.code_ine_95","GGY","Isla de Guernesey" -"__export__.code_ine_96","JEY","Isla de Jersey" -"__export__.code_ine_97","IMN","Isla de Man" -"__export__.code_ine_98","CXR","Isla de Navidad" -"__export__.code_ine_99","ISL","Islandia" -"__export__.code_ine_100","CYM","Islas Caimán" -"__export__.code_ine_101","CCK","Islas Cocos" -"__export__.code_ine_102","COK","Islas Cook" -"__export__.code_ine_103","FLK","Islas Falkland (Malvinas)" -"__export__.code_ine_104","FRO","Islas Feroé" -"__export__.code_ine_105","FJI","Islas Fidji" -"__export__.code_ine_106","SGS","Islas Georgias del Sur y Sandwich" -"__export__.code_ine_107","HMD","Islas Heard e Mcdonald" -"__export__.code_ine_108","MNP","Islas Marianas del Norte" -"__export__.code_ine_109","MHL","Islas Marshall" -"__export__.code_ine_110","UMI","Islas Menores de EEUU" -"__export__.code_ine_111","NFK","Islas Norfolk" -"__export__.code_ine_112","PCN","Islas Pitcairn" -"__export__.code_ine_113","SLB","Islas Salomón" -"__export__.code_ine_114","TCA","Islas Turcas y Caicos" -"__export__.code_ine_115","VGB","Islas Vírgenes Británicas" -"__export__.code_ine_116","VIR","Islas Vírgenes de los EEUU" -"__export__.code_ine_117","WLF","Islas Wallis y Futura" -"__export__.code_ine_118","ALA","Islas Åland" -"__export__.code_ine_119","ISR","Israel" -"__export__.code_ine_120","ITA","Italia" -"__export__.code_ine_121","JAM","Jamaica" -"__export__.code_ine_122","JPN","Japón" -"__export__.code_ine_123","JOR","Jordania" -"__export__.code_ine_124","KAZ","Kazajstán" -"__export__.code_ine_125","KEN","Kenia" -"__export__.code_ine_126","KGZ","Kirguistán" -"__export__.code_ine_127","KIR","Kiribati" -"__export__.code_ine_128","KWT","Kuwait" -"__export__.code_ine_129","LAO","Laos" -"__export__.code_ine_130","LSO","Lesotho" -"__export__.code_ine_131","LVA","Letonia" -"__export__.code_ine_132","LBY","Libia" -"__export__.code_ine_133","LBR","Libéria" -"__export__.code_ine_134","LIE","Liechtenstein" -"__export__.code_ine_135","LTU","Lituania" -"__export__.code_ine_136","LUX","Luxemburgo" -"__export__.code_ine_137","LBN","Líbano" -"__export__.code_ine_138","MAC","Macao" -"__export__.code_ine_139","MKD","Macedonia, ARY" -"__export__.code_ine_140","MDG","Madagascar" -"__export__.code_ine_141","MYS","Malasia" -"__export__.code_ine_142","MWI","Malawi" -"__export__.code_ine_143","MDV","Maldivas" -"__export__.code_ine_144","MLT","Malta" -"__export__.code_ine_145","MLI","Malí" -"__export__.code_ine_146","MAR","Marruecos" -"__export__.code_ine_147","MTQ","Martinica" -"__export__.code_ine_148","MUS","Mauricio" -"__export__.code_ine_149","MRT","Mauritania" -"__export__.code_ine_150","MYT","Mayotte" -"__export__.code_ine_151","FSM","Micronesia" -"__export__.code_ine_152","MDA","Moldavia" -"__export__.code_ine_153","MNG","Mongolia" -"__export__.code_ine_154","MNE","Montenegro" -"__export__.code_ine_155","MSR","Montserrat" -"__export__.code_ine_156","MOZ","Mozambique" -"__export__.code_ine_157","MMR","Myanmar" -"__export__.code_ine_158","MEX","México" -"__export__.code_ine_159","MCO","Mónaco" -"__export__.code_ine_160","NAM","Namibia" -"__export__.code_ine_161","NRU","Naurú" -"__export__.code_ine_162","NPL","Nepal" -"__export__.code_ine_163","NIC","Nicaragua" -"__export__.code_ine_164","NGA","Nigeria" -"__export__.code_ine_165","NIU","Niue" -"__export__.code_ine_166","NOR","Noruega" -"__export__.code_ine_167","NCL","Nueva Caledonia" -"__export__.code_ine_168","NZL","Nueva Zelanda" -"__export__.code_ine_169","NER","Níger" -"__export__.code_ine_170","OMN","Omán" -"__export__.code_ine_171","PAK","Pakistán" -"__export__.code_ine_172","PLW","Palau" -"__export__.code_ine_173","PSE","Palestina, Territorio ocupado" -"__export__.code_ine_174","PAN","Panamá" -"__export__.code_ine_175","PNG","Papua Nueva Guinea" -"__export__.code_ine_176","PRY","Paraguay" -"__export__.code_ine_177","NLD","Países Bajos" -"__export__.code_ine_178","PER","Perú" -"__export__.code_ine_179","PYF","Polinesia Francesa" -"__export__.code_ine_180","POL","Polonia" -"__export__.code_ine_181","PRT","Portugal" -"__export__.code_ine_182","PRI","Puerto Rico" -"__export__.code_ine_183","QAT","Qatar" -"__export__.code_ine_184","GBR","Reino Unido" -"__export__.code_ine_185","CAF","República Centroafricana" -"__export__.code_ine_186","CZE","República Checa" -"__export__.code_ine_187","DOM","República Dominicana" -"__export__.code_ine_188","REU","Reunión" -"__export__.code_ine_189","ROU","Rumania" -"__export__.code_ine_190","RUS","Rusia" -"__export__.code_ine_191","RWA","Rwanda" -"__export__.code_ine_192","ESH","Sahara Occidental" -"__export__.code_ine_193","KNA","Saint Kitts y Nevis" -"__export__.code_ine_194","WSM","Samoa" -"__export__.code_ine_195","ASM","Samoa Americana" -"__export__.code_ine_196","BLM","San Bartolomé" -"__export__.code_ine_197","SMR","San Marino" -"__export__.code_ine_198","MAF","San Martín" -"__export__.code_ine_199","SPM","San Pedro y Miquelón" -"__export__.code_ine_200","VCT","San Vicente y las Granadinas" -"__export__.code_ine_201","SHN","Santa Elena" -"__export__.code_ine_202","LCA","Santa Lucía" -"__export__.code_ine_203","STP","Santo Tomé y Príncipe" -"__export__.code_ine_204","SEN","Senegal" -"__export__.code_ine_205","SRB","Serbia" -"__export__.code_ine_206","SYC","Seychelles" -"__export__.code_ine_207","SLE","Sierra Leona" -"__export__.code_ine_208","SGP","Singapur" -"__export__.code_ine_209","SYR","Siria" -"__export__.code_ine_210","SOM","Somalia" -"__export__.code_ine_211","LKA","Sri Lanka" -"__export__.code_ine_212","SWZ","Suazilandia" -"__export__.code_ine_213","ZAF","Sudáfrica" -"__export__.code_ine_214","SDN","Sudán" -"__export__.code_ine_215","SWE","Suecia" -"__export__.code_ine_216","CHE","Suiza" -"__export__.code_ine_217","SUR","Suriname" -"__export__.code_ine_218","SJM","Svalbard e Islas de Jan Mayen" -"__export__.code_ine_219","THA","Tailandia" -"__export__.code_ine_220","TWN","Taiwán" -"__export__.code_ine_221","TZA","Tanzania" -"__export__.code_ine_222","TJK","Tayikistan" -"__export__.code_ine_223","IOT","Terr. Británico del Oc. Indico" -"__export__.code_ine_224","ATF","Tierras Australes Francesas" -"__export__.code_ine_225","TLS","Timor Oriental" -"__export__.code_ine_226","TGO","Togo" -"__export__.code_ine_227","TKL","Tokelau" -"__export__.code_ine_228","TON","Tonga" -"__export__.code_ine_229","TTO","Trinidad y Tobago" -"__export__.code_ine_230","TKM","Turkmenistán" -"__export__.code_ine_231","TUR","Turquía" -"__export__.code_ine_232","TUV","Tuvalu" -"__export__.code_ine_233","TUN","Túnez" -"__export__.code_ine_234","UKR","Ucrania" -"__export__.code_ine_235","UGA","Uganda" -"__export__.code_ine_236","URY","Uruguay" -"__export__.code_ine_237","UZB","Uzbekistán" -"__export__.code_ine_238","VUT","Vanuatu" -"__export__.code_ine_239","VAT","Vaticano, Santa Sede" -"__export__.code_ine_240","VEN","Venezuela" -"__export__.code_ine_241","VNM","Vietnam" -"__export__.code_ine_242","YEM","Yemen" -"__export__.code_ine_243","DJI","Yibuti" -"__export__.code_ine_244","ZMB","Zambia" -"__export__.code_ine_245","ZWE","Zimbabwe" -"__export__.code_ine_246","KOS","Kosovo" -"__export__.code_ine_247","ES111","A Coruña" -"__export__.code_ine_248","ES112","Lugo" -"__export__.code_ine_249","ES113","Ourense" -"__export__.code_ine_250","ES114","Pontevedra" -"__export__.code_ine_251","ES120","Asturias" -"__export__.code_ine_252","ES130","Cantabria" -"__export__.code_ine_253","ES211","Araba/Álava" -"__export__.code_ine_254","ES212","Gipuzkoa" -"__export__.code_ine_255","ES213","Bizkaia" -"__export__.code_ine_256","ES220","Navarra" -"__export__.code_ine_257","ES230","La Rioja" -"__export__.code_ine_258","ES241","Huesca" -"__export__.code_ine_259","ES242","Teruel" -"__export__.code_ine_260","ES243","Zaragoza" -"__export__.code_ine_261","ES300","Madrid" -"__export__.code_ine_262","ES411","Ávila" -"__export__.code_ine_263","ES412","Burgos" -"__export__.code_ine_264","ES413","León" -"__export__.code_ine_265","ES414","Palencia" -"__export__.code_ine_266","ES415","Salamanca" -"__export__.code_ine_267","ES416","Segovia" -"__export__.code_ine_268","ES417","Soria" -"__export__.code_ine_269","ES418","Valladolid" -"__export__.code_ine_270","ES419","Zamora" -"__export__.code_ine_271","ES421","Albacete" -"__export__.code_ine_272","ES422","Ciudad Real" -"__export__.code_ine_273","ES423","Cuenca" -"__export__.code_ine_274","ES424","Guadalajara" -"__export__.code_ine_275","ES425","Toledo" -"__export__.code_ine_276","ES431","Badajoz" -"__export__.code_ine_277","ES432","Cáceres" -"__export__.code_ine_278","ES511","Barcelona" -"__export__.code_ine_279","ES512","Girona" -"__export__.code_ine_280","ES513","Lleida" -"__export__.code_ine_281","ES514","Tarragona" -"__export__.code_ine_282","ES521","Alicante / Alacant" -"__export__.code_ine_283","ES522","Castellón / Castelló" -"__export__.code_ine_284","ES523","Valencia / València" -"__export__.code_ine_285","ES530","Illes Balears" -"__export__.code_ine_286","ES531","Eivissa y Formentera" -"__export__.code_ine_287","ES532","Mallorca" -"__export__.code_ine_288","ES533","Menorca" -"__export__.code_ine_289","ES611","Almería" -"__export__.code_ine_290","ES612","Cádiz" -"__export__.code_ine_291","ES613","Córdoba" -"__export__.code_ine_292","ES614","Granada" -"__export__.code_ine_293","ES615","Huelva" -"__export__.code_ine_294","ES616","Jaén" -"__export__.code_ine_295","ES617","Málaga" -"__export__.code_ine_296","ES618","Sevilla" -"__export__.code_ine_297","ES620","Murcia" -"__export__.code_ine_298","ES630","Ceuta" -"__export__.code_ine_299","ES640","Melilla" -"__export__.code_ine_300","ES701","Las Palmas" -"__export__.code_ine_301","ES702","Santa Cruz de Tenerife" -"__export__.code_ine_302","ES703","El Hierro" -"__export__.code_ine_303","ES704","Fuerteventura" -"__export__.code_ine_304","ES705","Gran Canaria" -"__export__.code_ine_305","ES706","La Gomera" -"__export__.code_ine_306","ES707","La Palma" -"__export__.code_ine_307","ES708","Lanzarote" -"__export__.code_ine_308","ES709","Tenerife" diff --git a/hotel_l10n_es/data/report_viajero_paperformat.xml b/hotel_l10n_es/data/report_viajero_paperformat.xml deleted file mode 100755 index 3d1cfb282..000000000 --- a/hotel_l10n_es/data/report_viajero_paperformat.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - Parte de Viajero - - custom - 200 - 75 - Portrait - 1 - 3 - 0 - 0 - - 1 - 201 - - - - diff --git a/hotel_l10n_es/data/tourism.category.csv b/hotel_l10n_es/data/tourism.category.csv deleted file mode 100755 index f91ca57d3..000000000 --- a/hotel_l10n_es/data/tourism.category.csv +++ /dev/null @@ -1,54 +0,0 @@ -"id","name","category_type" -"__export__.category_1","H1","Hoteles" -"__export__.category_2","H2","Hoteles" -"__export__.category_3","H3","Hoteles" -"__export__.category_4","H4","Hoteles" -"__export__.category_5","H5","Hoteles" -"__export__.category_6","AP","Hoteles-apartamentos" -"__export__.category_7","HA","Hoteles-apartamentos" -"__export__.category_8","HA1","Hoteles-apartamentos" -"__export__.category_9","HA2","Hoteles-apartamentos" -"__export__.category_10","HA3","Hoteles-apartamentos" -"__export__.category_11","HA4","Hoteles-apartamentos" -"__export__.category_12","HA5","Hoteles-apartamentos" -"__export__.category_13","HR","Hoteles-residencias" -"__export__.category_14","HR1","Hoteles-residencias" -"__export__.category_15","HR2","Hoteles-residencias" -"__export__.category_16","HR3","Hoteles-residencias" -"__export__.category_17","HR4","Hoteles-residencias" -"__export__.category_18","HR5","Hoteles-residencias" -"__export__.category_19","M1","Moteles" -"__export__.category_20","M2","Moteles" -"__export__.category_21","M3","Moteles" -"__export__.category_22","PN3","Paradores Nacionales" -"__export__.category_23","PN4","Paradores Nacionales" -"__export__.category_24","PN5","Paradores Nacionales" -"__export__.category_25","CV1","Ciudades de vacaciones" -"__export__.category_26","CV2","Ciudades de vacaciones" -"__export__.category_27","CV3","Ciudades de vacaciones" -"__export__.category_28","RA1","Residencias-apartamentos" -"__export__.category_29","RA2","Residencias-apartamentos" -"__export__.category_30","RA3","Residencias-apartamentos" -"__export__.category_31","RA4","Residencias-apartamentos" -"__export__.category_32","HS","Hostales" -"__export__.category_33","HS1","Hostales" -"__export__.category_34","HS2","Hostales" -"__export__.category_35","HS3","Hostales" -"__export__.category_36","HSR","Hostales" -"__export__.category_37","HSR1","Hostales" -"__export__.category_38","HSR2","Hostales" -"__export__.category_39","HSR3","Hostales" -"__export__.category_40","HSE","Hostales generales" -"__export__.category_41","HSG","Hostales especiales" -"__export__.category_42","CH","Casas de Huéspedes" -"__export__.category_43","CH1","Casas de Huéspedes" -"__export__.category_44","F1","Fondas" -"__export__.category_45","F2","Fondas" -"__export__.category_46","F3","Fondas" -"__export__.category_47","P","Pensiones" -"__export__.category_48","P1","Pensiones" -"__export__.category_49","P2","Pensiones" -"__export__.category_50","P3","Pensiones" -"__export__.category_51","PA","Pensiones" -"__export__.category_52","PT","Pensiones" -"__export__.category_53","Otras","Otros" diff --git a/hotel_l10n_es/i18n/es.po b/hotel_l10n_es/i18n/es.po deleted file mode 100755 index 85a97a2f3..000000000 --- a/hotel_l10n_es/i18n/es.po +++ /dev/null @@ -1,812 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * hotel_l10n_es -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 11.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-04-28 21:46+0000\n" -"PO-Revision-Date: 2019-04-28 23:52+0200\n" -"Last-Translator: <>\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: \n" -"Language: es\n" -"X-Generator: Poedit 1.8.7.1\n" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/wizard/police_wizard.py:113 -#, python-format -msgid " records added from " -msgstr "registros añadidos desde" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/wizard/police_wizard.py:114 -#, python-format -msgid " records processed." -msgstr "registros procesados." - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_document -msgid ", at" -msgstr ", a" - -#. module: hotel_l10n_es -#: selection:ine.wizard,ine_year:0 -msgid "2017" -msgstr "2017" - -#. module: hotel_l10n_es -#: selection:ine.wizard,ine_year:0 -msgid "2018" -msgstr "2018" - -#. module: hotel_l10n_es -#: selection:ine.wizard,ine_year:0 -msgid "2019" -msgstr "2019" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_document -msgid "COPY TO THE USER" -msgstr "COPIA PARA EL USUARIO" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/wizard/ine_wizard.py:432 -#, python-format -msgid "ADR in the month of the survey: " -msgstr "ADR en el mes de la solicitud:" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.ine_wizard_form -msgid "ADR y RevPar" -msgstr "ADR y RevPar" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.view_police_download -msgid "Abrir web de la Guardia Civil para entregar el fichero generado:" -msgstr "Abrir web de la Guardia Civil para entregar el fichero generado:" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.view_police_download -msgid "Abrir web de la Polícia para entregar el fichero generado:" -msgstr "Abrir web de la Polícia para entregar el fichero generado:" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.ine_wizard_form -msgid "Abrir web del I.N.E.:" -msgstr "Abrir web del I.N.E.:" - -#. module: hotel_l10n_es -#: model:ir.actions.act_window,name:hotel_l10n_es.action_ine_download -msgid "Action INE File Download" -msgstr "Descargar archivo INE" - -#. module: hotel_l10n_es -#: model:ir.actions.act_window,name:hotel_l10n_es.action_police_download -msgid "Action Police File Download" -msgstr "Descargar archivo policía" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_ine_wizard_adr_screen -msgid "Adr Screen" -msgstr "Adr Screen" - -#. module: hotel_l10n_es -#: selection:ine.wizard,ine_month:0 -msgid "April" -msgstr "Abril" - -#. module: hotel_l10n_es -#: selection:ine.wizard,ine_month:0 -msgid "August" -msgstr "Agosto" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_company_ine_seats -msgid "Beds available" -msgstr "Camas disponibles" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_hotel_checkin_partner_birthdate_date -msgid "Birhdate" -msgstr "Fecha de Nacimiento" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.hotel_checkin_partner_reservation_view_tree -#: model:ir.ui.view,arch_db:hotel_l10n_es.hotel_checkin_partner_view_tree -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_data -msgid "Birthdate" -msgstr "F. de nacimiento" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_head -msgid "CIF:" -msgstr "CIF:" - -#. module: hotel_l10n_es -#: selection:res.partner,document_type:0 -msgid "Carta o Doc. de Identidad" -msgstr "Carta o Doc. de Identidad" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_tourism_category_name -msgid "Category" -msgstr "Categoría" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_tourism_category_category_type -msgid "Category type" -msgstr "Tipo de Categoría" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_code_ine_code -msgid "Code" -msgstr "Código" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_hotel_checkin_partner_code_ine_id -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_partner_code_ine_id -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_users_code_ine_id -msgid "Code Ine" -msgstr "Code Ine" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.view_partner_form -msgid "Code in INE" -msgstr "Código en INE" - -#. module: hotel_l10n_es -#: model:ir.model,name:hotel_l10n_es.model_res_company -msgid "Companies" -msgstr "Compañías" - -#. module: hotel_l10n_es -#: model:ir.model,name:hotel_l10n_es.model_res_partner -msgid "Contact" -msgstr "Contacto" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_police_wizard_download_num -msgid "Correlative number" -msgstr "Núm Correlativo" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/models/inherit_res_partner.py:51 -#: model:ir.model.fields,help:hotel_l10n_es.field_hotel_checkin_partner_code_ine_id -#: model:ir.model.fields,help:hotel_l10n_es.field_res_partner_code_ine_id -#: model:ir.model.fields,help:hotel_l10n_es.field_res_users_code_ine_id -#, python-format -msgid "Country or province of origin. Used for INE statistics." -msgstr "País o provincia de origen. Usado para estadísticas INE." - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.view_police_download -msgid "Create Police File" -msgstr "Archivo de Policía Creado" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_code_ine_create_uid -#: model:ir.model.fields,field_description:hotel_l10n_es.field_ine_wizard_create_uid -#: model:ir.model.fields,field_description:hotel_l10n_es.field_police_wizard_create_uid -#: model:ir.model.fields,field_description:hotel_l10n_es.field_tourism_category_create_uid -msgid "Created by" -msgstr "Creado por" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_code_ine_create_date -#: model:ir.model.fields,field_description:hotel_l10n_es.field_ine_wizard_create_date -#: model:ir.model.fields,field_description:hotel_l10n_es.field_police_wizard_create_date -#: model:ir.model.fields,field_description:hotel_l10n_es.field_tourism_category_create_date -msgid "Created on" -msgstr "Creado en" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.view_partner_form -msgid "Customer Link" -msgstr "Enlace Cliente" - -#. module: hotel_l10n_es -#: selection:res.partner,document_type:0 -msgid "DNI" -msgstr "DNI" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_police_wizard_download_date -msgid "Date" -msgstr "Fecha" - -#. module: hotel_l10n_es -#: selection:ine.wizard,ine_month:0 -msgid "December" -msgstr "Diciembre" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_code_ine_display_name -#: model:ir.model.fields,field_description:hotel_l10n_es.field_ine_wizard_display_name -#: model:ir.model.fields,field_description:hotel_l10n_es.field_police_wizard_display_name -#: model:ir.model.fields,field_description:hotel_l10n_es.field_tourism_category_display_name -msgid "Display Name" -msgstr "Nombre mostrado" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.hotel_checkin_partner_reservation_view_tree -#: model:ir.ui.view,arch_db:hotel_l10n_es.hotel_checkin_partner_view_tree -msgid "Doc. Number" -msgstr "Núm. de Documento" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_hotel_checkin_partner_document_type -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_partner_document_type -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_users_document_type -msgid "Doc. type" -msgstr "Tipo de Documento" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_hotel_checkin_partner_document_expedition_date -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_partner_document_expedition_date -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_users_document_expedition_date -msgid "Document expedition date" -msgstr "Fecha de Expedición" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_hotel_checkin_partner_document_number -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_partner_document_number -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_users_document_number -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_data -msgid "Document number" -msgstr "Nº de documento:" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_head -msgid "Document number:" -msgstr "Nº de documento:" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/wizard/ine_wizard.py:142 -#, python-format -msgid "ERROR: Usuario sin codigo de INE: " -msgstr "ERROR: Usuario sin codigo de INE: " - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_document -msgid "" -"En nombre de la empresa\n" -" GRUPO ALDA HOTELS\n" -" compuesto por Alda Rías Baixas SL, Alda Compostela SL, Alda Castilla SL, Hoteles Rías Altas SL, Comphostel Gestión Patrimonial SL y Consultores Hoteleros Integrales SL, tratamos la información que nos facilita, con el fin de prestarle el servicio\n" -" solicitado y realizar la facturación del mismo. Se conservarán mientras se mantenga la relación comercial o durante los años necesarios para cumplir con las obligaciones legales. No se cederán a terceros salvo en los casos en que exista una\n" -" obligación legal. Usted tiene derecho a obtener información sobre el tratamiento de sus datos personales, acceder, rectificar los inexactos o solicitar su supresión cuando ya no sean necesarios, en la dirección\n" -" protecciondatos@aldahotels.com\n" -" Asimismo hemos solicitado que confirme esta autorización para ofrecerle nuestros servicios y poder fidelizarle como cliente." -msgstr "" -"En nombre de la empresa\n" -" GRUPO ALDA HOTELS\n" -" compuesto por Alda Rías Baixas SL, Alda Compostela SL, Alda Castilla SL, Hoteles Rías Altas SL, Comphostel Gestión Patrimonial SL y Consultores Hoteleros Integrales SL, tratamos la información que nos facilita, con el fin de prestarle el servicio\n" -" solicitado y realizar la facturación del mismo. Se conservarán mientras se mantenga la relación comercial o durante los años necesarios para cumplir con las obligaciones legales. No se cederán a terceros salvo en los casos en que exista una\n" -" obligación legal. Usted tiene derecho a obtener información sobre el tratamiento de sus datos personales, acceder, rectificar los inexactos o solicitar su supresión cuando ya no sean necesarios, en la dirección\n" -" protecciondatos@aldahotels.com\n" -" Asimismo hemos solicitado que confirme esta autorización para ofrecerle nuestros servicios y poder fidelizarle como cliente." - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_document -msgid "" -"En nombre de la empresa\n" -" GRUPO ALDA HOTELS\n" -" compuesto por Alda Rías Baixas SL, Alda Compostela SL, Alda Castilla SL, Hoteles Rías Altas SL, Comphostel Gestión Patrimonial SL y Consultores Hoteleros Integrales SL, tratamos la información que nos facilita, con el fin de prestarle el servicio\n" -" solicitado y realizar la facturación del mismo. Se conservarán mientras se mantenga la relación comercial o durante los años necesarios para cumplir con las obligaciones legales. No se cederán a terceros salvo en los casos en que exista una\n" -" obligación legal. Usted tiene derecho a obtener información sobre el tratamiento de sus datos personales, acceder, rectificar los inexactos o solicitar su supresión cuando ya no sean necesarios, en la dirección\n" -" protecciondatos@aldahotels.com\n" -" Asimismo solicito confirme esta autorización para ofrecerle nuestros servicios y poder fidelizarle como cliente.
      Si ___ No ___" -msgstr "" -"En nombre de la empresa\n" -" GRUPO ALDA HOTELS\n" -" compuesto por Alda Rías Baixas SL, Alda Compostela SL, Alda Castilla SL, Hoteles Rías Altas SL, Comphostel Gestión Patrimonial SL y Consultores Hoteleros Integrales SL, tratamos la información que nos facilita, con el fin de prestarle el servicio\n" -" solicitado y realizar la facturación del mismo. Se conservarán mientras se mantenga la relación comercial o durante los años necesarios para cumplir con las obligaciones legales. No se cederán a terceros salvo en los casos en que exista una\n" -" obligación legal. Usted tiene derecho a obtener información sobre el tratamiento de sus datos personales, acceder, rectificar los inexactos o solicitar su supresión cuando ya no sean necesarios, en la dirección\n" -" protecciondatos@aldahotels.com\n" -" Asimismo solicito confirme esta autorización para ofrecerle nuestros servicios y poder fidelizarle como cliente.
      Si ___ No ___" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_data -msgid "Entry date" -msgstr "Fecha de entrada" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_police_wizard_error_partner -msgid "Error Partner" -msgstr "Error Cliente" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_company_ine_eventual_staff -msgid "Eventual Staff" -msgstr "Staff eventual" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_data -msgid "Exit date" -msgstr "Fecha Salida" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.hotel_checkin_partner_reservation_view_tree -#: model:ir.ui.view,arch_db:hotel_l10n_es.hotel_checkin_partner_view_tree -msgid "Exp. Date" -msgstr "Fecha de Expedición" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_data -msgid "Expedition date" -msgstr "Fecha expedición" - -#. module: hotel_l10n_es -#: selection:ine.wizard,ine_month:0 -msgid "February" -msgstr "Febrero" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/wizard/police_wizard.py:123 -#, python-format -msgid "File not generated by configuration error." -msgstr "Archivo no generado por errores de configuración." - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_hotel_checkin_partner_firstname -msgid "First name" -msgstr "Nombre" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/models/inherit_res_partner.py:235 -#, python-format -msgid "For safety reasons, you cannot merge more than 3 contacts together. You can re-open the wizard several times if needed." -msgstr "Por razones de seguridad no se pudo fusionar a los contactos" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_hotel_checkin_partner_gender -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_data -msgid "Gender" -msgstr "Género" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.ine_wizard_form -msgid "Generar estadistica para el I.N.E." -msgstr "Generar estadistica para el I.N.E." - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.view_police_download -msgid "Generate Police file" -msgstr "Generar fichero para la Policía" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.ine_wizard_form -msgid "Generate file" -msgstr "Archivo creado!" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/wizard/police_wizard.py:118 -#, python-format -msgid "Generated file. Download it and give it to the police." -msgstr "Archivo creado!. Descargalo para entregarlo a la Policía." - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.view_police_download -msgid "Guardia Civil " -msgstr "Guardia Civil " - -#. module: hotel_l10n_es -#: model:ir.model,name:hotel_l10n_es.model_hotel_reservation -msgid "Hotel Reservation" -msgstr "Reserva del hotel" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.view_company_form -msgid "Hotel Settings" -msgstr "Configuración del Hotel" - -#. module: hotel_l10n_es -#: model:ir.model.fields,help:hotel_l10n_es.field_res_company_ine_category_id -msgid "Hotel category in the Ministry of Tourism. Used for INE statistics." -msgstr "Categoría del Hotel en el Ministerio de Turismo. Usado para las estadísticas del INE." - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_code_ine_id -#: model:ir.model.fields,field_description:hotel_l10n_es.field_ine_wizard_id -#: model:ir.model.fields,field_description:hotel_l10n_es.field_police_wizard_id -#: model:ir.model.fields,field_description:hotel_l10n_es.field_tourism_category_id -msgid "ID" -msgstr "ID (identificación)" - -#. module: hotel_l10n_es -#: model:ir.ui.menu,name:hotel_l10n_es.menu_ine_download -msgid "INE File Download" -msgstr "Descargar Archivo INE" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.view_company_form -msgid "INE and Police data" -msgstr "INE y Datos Policía" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_company_checkin_img -msgid "Image in checkin" -msgstr "Imagen en Checkin" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/models/inherit_hotel_checkin_partner.py:102 -#, python-format -msgid "Incorrect DNI" -msgstr "DNI Incorrecto" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_company_ine_category_id -msgid "Ine Category" -msgstr "Categoría Ine" - -#. module: hotel_l10n_es -#: selection:ine.wizard,ine_month:0 -msgid "January" -msgstr "Enero" - -#. module: hotel_l10n_es -#: selection:ine.wizard,ine_month:0 -msgid "July" -msgstr "Julio" - -#. module: hotel_l10n_es -#: selection:ine.wizard,ine_month:0 -msgid "June" -msgstr "Junio" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_code_ine___last_update -#: model:ir.model.fields,field_description:hotel_l10n_es.field_ine_wizard___last_update -#: model:ir.model.fields,field_description:hotel_l10n_es.field_police_wizard___last_update -#: model:ir.model.fields,field_description:hotel_l10n_es.field_tourism_category___last_update -msgid "Last Modified on" -msgstr "Última modificación en" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_code_ine_write_uid -#: model:ir.model.fields,field_description:hotel_l10n_es.field_ine_wizard_write_uid -#: model:ir.model.fields,field_description:hotel_l10n_es.field_police_wizard_write_uid -#: model:ir.model.fields,field_description:hotel_l10n_es.field_tourism_category_write_uid -msgid "Last Updated by" -msgstr "Última actualización de" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_code_ine_write_date -#: model:ir.model.fields,field_description:hotel_l10n_es.field_ine_wizard_write_date -#: model:ir.model.fields,field_description:hotel_l10n_es.field_police_wizard_write_date -#: model:ir.model.fields,field_description:hotel_l10n_es.field_tourism_category_write_date -msgid "Last Updated on" -msgstr "Última actualización en" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_hotel_checkin_partner_lastname -msgid "Last name" -msgstr "Apellido" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_police_wizard_log_police -msgid "Log Police" -msgstr "Log Policía" - -#. module: hotel_l10n_es -#: selection:ine.wizard,ine_month:0 -msgid "March" -msgstr "Marzo" - -#. module: hotel_l10n_es -#: selection:ine.wizard,ine_month:0 -msgid "May" -msgstr "Mayo" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/models/inherit_res_partner.py:259 -#, python-format -msgid "Merged with the following partners:" -msgstr "Fusionado con los siguientes contactos:" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_ine_wizard_ine_month -msgid "Month" -msgstr "Mes" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_hotel_checkin_partner_name -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_data -msgid "Name" -msgstr "Nombre" - -#. module: hotel_l10n_es -#: model:ir.model.fields,help:hotel_l10n_es.field_res_company_property_name -msgid "Name of the Hotel/Property." -msgstr "Nombre del Hotel/Propiedad." - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/wizard/ine_wizard.py:440 -#, python-format -msgid "No data in this month" -msgstr "No hay información este mes" - -#. module: hotel_l10n_es -#: selection:ine.wizard,ine_month:0 -msgid "November" -msgstr "Noviembre" - -#. module: hotel_l10n_es -#: model:ir.model.fields,help:hotel_l10n_es.field_police_wizard_download_num -msgid "Number provided by the police" -msgstr "Núm. provisto por la policía" - -#. module: hotel_l10n_es -#: selection:ine.wizard,ine_month:0 -msgid "October" -msgstr "Octubre" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_data -msgid "Origin" -msgstr "Origen" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_head -msgid "PART OF TRAVELERS ENTRY" -msgstr "PARTE DE ENTRADA DE VIAJEROS" - -#. module: hotel_l10n_es -#: model:ir.actions.report,name:hotel_l10n_es.action_report_viajero -msgid "Parte de Viajero" -msgstr "Parte de Viajero" - -#. module: hotel_l10n_es -#: selection:res.partner,document_type:0 -msgid "Pasaporte" -msgstr "Pasaporte" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_company_ine_permanent_staff -msgid "Permanent Staff" -msgstr "Permanent Staff" - -#. module: hotel_l10n_es -#: selection:res.partner,document_type:0 -msgid "Permiso Residencia Español" -msgstr "Permiso Residencia Español" - -#. module: hotel_l10n_es -#: selection:res.partner,document_type:0 -msgid "Permiso Residencia Europeo" -msgstr "Permiso Residencia Europeo" - -#. module: hotel_l10n_es -#: selection:res.partner,document_type:0 -msgid "Permiso de Conducir" -msgstr "Permiso de Conducir" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_code_ine_name -msgid "Place" -msgstr "Sitio" - -#. module: hotel_l10n_es -#: model:ir.ui.menu,name:hotel_l10n_es.menu_police_download -msgid "Police File Download" -msgstr "Desacargar Archivo Policía" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_company_police_number -msgid "Police number" -msgstr "Número Policía" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.view_police_download -msgid "Policia " -msgstr "Policia " - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.ine_wizard_form -msgid "Presentar encuesta I.N.E. " -msgstr "Presentar encuesta I.N.E. " - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.hotel_reservation_view_form -msgid "Print All Checkins" -msgstr "Imprimir todos los Checkins" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.hotel_checkin_partner_view_form -#: model:ir.ui.view,arch_db:hotel_l10n_es.hotel_checkin_partner_view_tree -msgid "Print in PDF" -msgstr "Imprimir en PDF" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/wizard/police_wizard.py:110 -#, python-format -msgid "Problem generating the file. Checkin without data, or incorrect data: " -msgstr "Problema generando el archivo. Checkin sin datos, o con datos incorrectos: " - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_company_property_name -msgid "Property name" -msgstr "Nombre propiedad" - -#. module: hotel_l10n_es -#: model:ir.model.fields,help:hotel_l10n_es.field_res_company_ine_tourism -msgid "Registration number in the Ministry of Tourism. Used for INE statistics." -msgstr "Núm. de Registro en el Ministerio de Turismo. Usado en las estadísticas del INE." - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_ine_wizard_rev_screen -msgid "Rev Screen" -msgstr "Rev Screen" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_company_ine_rooms -msgid "Rooms Available" -msgstr "Habitaciones Disponibles" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.hotel_checkin_partner_reservation_view_tree -msgid "Search" -msgstr "Búsqueda" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/models/inherit_res_partner.py:44 -#: model:ir.model.fields,help:hotel_l10n_es.field_hotel_checkin_partner_document_type -#: model:ir.model.fields,help:hotel_l10n_es.field_res_partner_document_type -#: model:ir.model.fields,help:hotel_l10n_es.field_res_users_document_type -#, python-format -msgid "Select a valid document type" -msgstr "Seleccione un tipo de documento válido" - -#. module: hotel_l10n_es -#: selection:ine.wizard,ine_month:0 -msgid "September" -msgstr "Septiembre" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_data -msgid "Surnames" -msgstr "Apellidos" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_head -msgid "TRAVELER'S DOCUMENT" -msgstr "DOCUMENTO DEL VIAJERO" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/models/inherit_res_partner.py:186 -#, python-format -msgid "The Document Number %s already exists in another partner." -msgstr "El número de Documento %s ya existe en otro contacto confirmado." - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.view_partner_form -msgid "This partner was deactivated and merged with another client to avoid duplicity, you can see the active client in" -msgstr "Este cliente fue desactivado y fusionado con otro cliente para evitar duplicados, puedes ver el cliente activo en" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/models/inherit_hotel_checkin_partner.py:125 -#, python-format -msgid "To perform the checkin the following data are missing: %s" -msgstr "Para realizar el checkin faltan los siguientes datos: %s" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/models/inherit_hotel_checkin_partner.py:129 -#, python-format -msgid "To perform the checkin the segmentation is required" -msgstr "Para realizar el checkin es olbigatoria la segmentación de la reserva" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_head -msgid "Total amount (Reservation Card):" -msgstr "Importe total (Ficha de Reserva):" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_res_company_ine_tourism -msgid "Tourism number" -msgstr "Núm. de Turismo" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_document -msgid "Traveler's signature" -msgstr "Firma del viajero" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_ine_wizard_txt_binary -#: model:ir.model.fields,field_description:hotel_l10n_es.field_police_wizard_txt_binary -msgid "Txt Binary" -msgstr "Txt Binary" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_ine_wizard_txt_filename -#: model:ir.model.fields,field_description:hotel_l10n_es.field_police_wizard_txt_filename -msgid "Txt Filename" -msgstr "Txt Nombre de archivo" - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_police_wizard_txt_message -msgid "Txt Message" -msgstr "Txt Mensaje" - -#. module: hotel_l10n_es -#: model:ir.ui.view,arch_db:hotel_l10n_es.report_viajero_data -msgid "Type" -msgstr "Tipo" - -#. module: hotel_l10n_es -#: model:ir.model.fields,help:hotel_l10n_es.field_res_company_ine_eventual_staff -#: model:ir.model.fields,help:hotel_l10n_es.field_res_company_ine_permanent_staff -#: model:ir.model.fields,help:hotel_l10n_es.field_res_company_ine_rooms -#: model:ir.model.fields,help:hotel_l10n_es.field_res_company_ine_seats -msgid "Used for INE statistics." -msgstr "Usado en las estadísticas del INE." - -#. module: hotel_l10n_es -#: model:ir.model.fields,help:hotel_l10n_es.field_res_company_police_number -msgid "Used to generate the name of the file that will be given to the police. 10 Caracters" -msgstr "Usado para general el nombre del archivo que será entregado a la policía. 10 Caracteres." - -#. module: hotel_l10n_es -#: model:ir.model.fields,field_description:hotel_l10n_es.field_ine_wizard_ine_year -msgid "Year" -msgstr "Año" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/models/inherit_res_partner.py:242 -#, python-format -msgid "You cannot merge a contact with one of his parent." -msgstr "No se puede fusionar un contacto con su empresa relacionada." - -#. module: hotel_l10n_es -#: model:ir.actions.act_window,name:hotel_l10n_es.action_code_ine -msgid "action.code.ine" -msgstr "action.code.ine" - -#. module: hotel_l10n_es -#: model:ir.actions.act_window,name:hotel_l10n_es.action_tourism_category -msgid "action.tourism.category" -msgstr "Categorías de Turismo" - -#. module: hotel_l10n_es -#: model:ir.model,name:hotel_l10n_es.model_code_ine -msgid "code.ine" -msgstr "code.ine" - -#. module: hotel_l10n_es -#: model:ir.model,name:hotel_l10n_es.model_hotel_checkin_partner -msgid "hotel.checkin.partner" -msgstr "Checkins" - -#. module: hotel_l10n_es -#: model:ir.model,name:hotel_l10n_es.model_ine_wizard -msgid "ine.wizard" -msgstr "ine.wizard" - -#. module: hotel_l10n_es -#: model:ir.ui.menu,name:hotel_l10n_es.menu_code_ine -msgid "menu.code.ine" -msgstr "menu.code.ine" - -#. module: hotel_l10n_es -#: model:ir.ui.menu,name:hotel_l10n_es.menu_tourism_category -msgid "menu.tourism.category" -msgstr "Categorías de Turismo" - -#. module: hotel_l10n_es -#: model:ir.model,name:hotel_l10n_es.model_police_wizard -msgid "police.wizard" -msgstr "police.wizard" - -#. module: hotel_l10n_es -#: model:ir.model,name:hotel_l10n_es.model_tourism_category -msgid "tourism.category" -msgstr "Categoría de Turismo" - -#. module: hotel_l10n_es -#: code:addons/hotel_l10n_es/wizard/ine_wizard.py:433 -#, python-format -msgid "€ and " -msgstr "€ y" diff --git a/hotel_l10n_es/models/__init__.py b/hotel_l10n_es/models/__init__.py deleted file mode 100755 index 9a800ac78..000000000 --- a/hotel_l10n_es/models/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2017 Alda Hotels -# Jose Luis Algara -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## - -from . import category_type -from . import code_ine -from . import inherit_res_company -from . import inherit_res_partner -from . import inherit_hotel_checkin_partner -from . import inherit_hotel_reservation diff --git a/hotel_l10n_es/models/category_type.py b/hotel_l10n_es/models/category_type.py deleted file mode 100755 index 9f59cfbe3..000000000 --- a/hotel_l10n_es/models/category_type.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2017 Alda Hotels -# Jose Luis Algara -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from odoo import models, fields, api - - -class TourismCategory(models.Model): - _name = 'tourism.category' - - name = fields.Char('Category', required=True) - category_type = fields.Char('Category type', required=True) - - - def name_get(self): - data = [] - for record in self: - display_value = record.category_type + " (" + record.name + ") " - data.append((record.id, display_value)) - return data diff --git a/hotel_l10n_es/models/code_ine.py b/hotel_l10n_es/models/code_ine.py deleted file mode 100755 index 7d00be2d4..000000000 --- a/hotel_l10n_es/models/code_ine.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2017 Alda Hotels -# Jose Luis Algara -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp import models, fields, api - - -class CodeIne(models.Model): - _name = 'code.ine' - - name = fields.Char('Place', required=True) - code = fields.Char('Code', required=True) - - - def name_get(self): - data = [] - for record in self: - subcode = record.code - if len(record.code) > 3: - subcode = 'ESP' - display_value = record.name + " (" + subcode + ")" - data.append((record.id, display_value)) - return data diff --git a/hotel_l10n_es/models/inherit_hotel_checkin_partner.py b/hotel_l10n_es/models/inherit_hotel_checkin_partner.py deleted file mode 100755 index f8cb4cfb5..000000000 --- a/hotel_l10n_es/models/inherit_hotel_checkin_partner.py +++ /dev/null @@ -1,127 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2017 Alda Hotels -# Jose Luis Algara -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from openerp import models, fields, api, _ -from odoo.exceptions import UserError -import logging -_logger = logging.getLogger(__name__) - - -class HotelCheckinPartner(models.Model): - _inherit = 'hotel.checkin.partner' - - document_type = fields.Selection(related='partner_id.document_type') - document_number = fields.Char(related='partner_id.document_number') - document_expedition_date = fields.Date( - related='partner_id.document_expedition_date') - gender = fields.Selection('Gender', related='partner_id.gender') - birthdate_date = fields.Date('Birhdate', - related='partner_id.birthdate_date') - code_ine_id = fields.Many2one(related="partner_id.code_ine_id") - name = fields.Char(related='partner_id.name') - lastname = fields.Char(related='partner_id.lastname') - firstname = fields.Char(related='partner_id.firstname') - - @api.model - def create(self, vals): - if not vals.get('partner_id'): - name = self.env['res.partner']._get_computed_name( - vals.get('lastname'), - vals.get('firstname') - ) - partner = self.env['res.partner'].create({ - 'name': name, - }) - vals.update({'partner_id': partner.id}) - vals.pop('firstname') - vals.pop('lastname') - return super(HotelCheckinPartner, self).create(vals) - - - def write(self, vals): - for record in self: - if not vals.get('partner_id') and not record.partner_id: - name = self.env['res.partner']._get_computed_name( - vals.get('lastname'), - vals.get('firstname') - ) - partner = self.env['res.partner'].create({ - 'name': name, - }) - record.update({'partner_id': partner.id}) - vals.pop('firstname') - vals.pop('lastname') - return super(HotelCheckinPartner, self).write(vals) - - - def action_on_board(self): - self.check_required_fields() - return super(HotelCheckinPartner, self).action_on_board() - - @api.model - def check_dni(self, dni): - digits = "TRWAGMYFPDXBNJZSQVHLCKE" - dig_ext = "XYZ" - reemp_dig_ext = {'X': '0', 'Y': '1', 'Z': '2'} - numbers = "1234567890" - dni = dni.upper() - if len(dni) == 9: - dig_control = dni[8] - dni = dni[:8] - if dni[0] in dig_ext: - dni = dni.replace(dni[0], reemp_dig_ext[dni[0]]) - return len(dni) == len([n for n in dni if n in numbers]) \ - and digits[int(dni) % 23] == dig_control - else: - return False - - @api.onchange('document_number', 'document_type') - def onchange_document_number(self): - for record in self: - if record.document_type == 'D' and record.document_number: - if not record.check_dni(record.document_number): - record.document_number = False - raise UserError(_('Incorrect DNI')) - if not record.partner_id and record.document_number: - partner = self.env['res.partner'].search([ - ('document_number', '=', record.document_number) - ], limit=1) - if partner: - record.update({'partner_id': partner}) - - - def check_required_fields(self): - for record in self: - missing_fields = [] - required_fields = ['document_type', 'document_number', - 'document_expedition_date', 'gender', - 'birthdate_date', 'code_ine_id', - 'lastname', 'firstname'] - for field in required_fields: - if not record[field]: - missing_fields.append(record._fields[field].string) - if missing_fields: - raise UserError( - _('To perform the checkin the following data are missing:\ - %s') % (', '.join(missing_fields))) - if not record.reservation_id.segmentation_ids: - raise UserError( - _('To perform the checkin the segmentation is required')) diff --git a/hotel_l10n_es/models/inherit_hotel_reservation.py b/hotel_l10n_es/models/inherit_hotel_reservation.py deleted file mode 100644 index 640e5f199..000000000 --- a/hotel_l10n_es/models/inherit_hotel_reservation.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2017 Darío Lodeiros -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from openerp import models, api - - -class HotelReservation(models.Model): - _inherit = 'hotel.reservation' - - - def print_all_checkins(self): - checkins = self.env['hotel.checkin.partner'] - for record in self: - checkins += record.checkin_partner_ids.filtered( - lambda s: s.state in ('booking', 'done')) - if checkins: - return self.env.ref('hotel_l10n_es.action_report_viajero').\ - report_action(checkins) diff --git a/hotel_l10n_es/models/inherit_res_company.py b/hotel_l10n_es/models/inherit_res_company.py deleted file mode 100755 index 4e70c4b01..000000000 --- a/hotel_l10n_es/models/inherit_res_company.py +++ /dev/null @@ -1,57 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2017 Alda Hotels -# Jose Luis Algara -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -import base64 -from odoo import models, fields -from odoo import modules - - -def get_default_img(): - with open(modules.get_module_resource('hotel_l10n_es', 'static/src/img', - 'logo_bn.png'), - 'rb') as f: - return base64.b64encode(f.read()) - - -class Inherit_res_company(models.Model): - _inherit = 'res.company' - - property_name = fields.Char('Property name', - help='Name of the Hotel/Property.') - ine_tourism = fields.Char('Tourism number', - help='Registration number in the Ministry of \ - Tourism. Used for INE statistics.') - ine_rooms = fields.Integer('Rooms Available', default=0, - help='Used for INE statistics.') - ine_seats = fields.Integer('Beds available', default=0, - help='Used for INE statistics.') - ine_permanent_staff = fields.Integer('Permanent Staff', default=0, - help='Used for INE statistics.') - ine_eventual_staff = fields.Integer('Eventual Staff', default=0, - help='Used for INE statistics.') - police_number = fields.Char('Police number', size=10, - help='Used to generate the name of the file that \ - will be given to the police. 10 Caracters') - ine_category_id = fields.Many2one('tourism.category', - help='Hotel category in the Ministry of \ - Tourism. Used for INE statistics.') - checkin_img = fields.Binary(string="Image in checkin", - default=get_default_img()) diff --git a/hotel_l10n_es/models/inherit_res_partner.py b/hotel_l10n_es/models/inherit_res_partner.py deleted file mode 100755 index d9102b6d9..000000000 --- a/hotel_l10n_es/models/inherit_res_partner.py +++ /dev/null @@ -1,450 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2017 Alda Hotels -# Jose Luis Algara -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -import functools -import itertools -import logging -import psycopg2 - -from odoo import api, fields, models, _ -from odoo.osv.expression import get_unaccent_wrapper -from odoo.exceptions import ValidationError, UserError -from odoo.tools import mute_logger -_logger = logging.getLogger(__name__) - - -class ResPartner(models.Model): - _inherit = 'res.partner' - - document_type = fields.Selection([ - ('D', 'DNI'), - ('P', 'Pasaporte'), - ('C', 'Permiso de Conducir'), - ('I', 'Carta o Doc. de Identidad'), - ('N', 'Permiso Residencia Español'), - ('X', 'Permiso Residencia Europeo')], - help=_('Select a valid document type'), - string='Doc. type', - ) - document_number = fields.Char('Document number', index=True) - document_expedition_date = fields.Date('Document expedition date') - code_ine_id = fields.Many2one('code.ine', - help=_('Country or province of origin. Used for INE statistics.')) - unconfirmed = fields.Boolean('Unconfirmed', default=True) - main_partner_id = fields.Many2one('res.partner') - - @api.model - def name_search(self, name, args=None, operator='ilike', limit=100): - if not args: - args = [] - domain = ['|', - ('document_number', operator, name), - ('vat', operator, name), - ] - partners = self.search(domain + args, limit=limit,) - res = partners.name_get() - if limit: - limit_rest = limit - len(partners) - else: - limit_rest = limit - if limit_rest or not limit: - args += [('id', 'not in', partners.ids)] - res += super(ResPartner, self).name_search( - name, args=args, operator=operator, limit=limit_rest) - return res - - @api.model - def _get_duplicated_ids(self, partner): - partner_ids = [] - if partner.vat: - partner_ids += self.env['res.partner'].search([ - ('vat', '=', partner.vat), - ('parent_id', '=', False) - ]).ids - if partner.document_number: - partner_ids += self.env['res.partner'].search([ - ('document_number', '=', partner.document_number), - ('child_ids', '=', False) - ]).ids - if partner_ids: - return partner_ids - - def _merge_fields(self): - duplicated_fields = ['vat', 'document_number'] - return duplicated_fields - - - def write(self, vals): - if vals.get('vat') and not self._context.get( - "ignore_vat_update", False): - for record in self: - vat = vals.get('vat') - if self.env.context.get('company_id'): - company = self.env['res.company'].browse(self.env.context['company_id']) - else: - company = self.env.user.company_id - if company.vat_check_vies: - # force full VIES online check - check_func = self.vies_vat_check - else: - # quick and partial off-line checksum validation - check_func = self.simple_vat_check - vat_country, vat_number = self._split_vat(vat) - #check with country code as prefix of the TIN - if not check_func(vat_country, vat_number): - country_id = vals.get('country_id') or record.country_id.id - vals['vat'] = record.fix_eu_vat_number(country_id, vat) - return super(ResPartner, self).write(vals) - - @api.constrains('country_id') - def update_vat_code_country(self): - for record in self: - country_id = record.country_id.id - vat = record.vat - #check with country code as prefix of the TIN - if vat: - if self.env.context.get('company_id'): - company = self.env['res.company'].browse(self.env.context['company_id']) - else: - company = self.env.user.company_id - if company.vat_check_vies: - # force full VIES online check - check_func = self.vies_vat_check - else: - # quick and partial off-line checksum validation - check_func = self.simple_vat_check - vat_country, vat_number = self._split_vat(vat) - if not check_func(vat_country, vat_number): - vat_with_code = record.fix_eu_vat_number(country_id, vat) - if country_id and vat != vat_with_code: - record.with_context({'ignore_vat_update': True}).write({ - 'vat': vat_with_code - }) - - @api.constrains('vat', 'commercial_partner_country_id') - def check_vat(self): - spain = self.env['res.country'].search([ - ('code', '=', 'ES') - ]) - from_spain = False - for partner in self: - if partner.country_id == spain: - from_spain = True - if from_spain: - return super(ResPartner, self).check_vat() - - @api.constrains('vat') - def _check_vat_unique(self): - for record in self: - if record.unconfirmed: - if record.vat: - record.update({'unconfirmed': False}) - partner_ids = self.env['res.partner'].search([ - ('vat', '=', record.vat), - ('parent_id', '=', False) - ]).ids - if len(partner_ids) > 1: - partners = self.env['res.partner'].browse(partner_ids) - record._merge(partners._ids) - else: - return super(ResPartner, self)._check_vat_unique() - return True - - @api.constrains('document_number', 'document_type') - def _check_document_number_unique(self): - for record in self: - if not record.document_number: - continue - if record.unconfirmed: - if record.document_number: - record.update({'unconfirmed': False}) - partner_ids = self.env['res.partner'].search([ - ('document_number', '=', record.document_number), - ]).ids - if len(partner_ids) > 1: - partners = self.env['res.partner'].browse(partner_ids) - record._merge(partners._ids) - if not record.parent_id and record.document_type == 'D' \ - and not record.vat: - record.update({ - 'vat': record.document_number, - }) - else: - results = self.env['res.partner'].search_count([ - ('document_type', '=', record.document_type), - ('document_number', '=', record.document_number), - ('id', '!=', record.id) - ]) - if results: - raise ValidationError(_( - "The Document Number %s already exists in another " - "partner.") % record.document_number) - - - def open_main_partner(self): - self.ensure_one() - action = self.env.ref('base.action_partner_form').read()[0] - if self.main_partner_id: - action['views'] = [(self.env.ref('base.view_partner_form').id, 'form')] - action['res_id'] = self.main_partner_id.id - else: - action = {'type': 'ir.actions.act_window_close'} - return action - - def _get_fk_on(self, table): - """ return a list of many2one relation with the given table. - :param table : the name of the sql table to return relations - :returns a list of tuple 'table name', 'column name'. - """ - query = """ - SELECT cl1.relname as table, att1.attname as column - FROM pg_constraint as con, pg_class as cl1, pg_class as cl2, pg_attribute as att1, pg_attribute as att2 - WHERE con.conrelid = cl1.oid - AND con.confrelid = cl2.oid - AND array_lower(con.conkey, 1) = 1 - AND con.conkey[1] = att1.attnum - AND att1.attrelid = cl1.oid - AND cl2.relname = %s - AND att2.attname = 'id' - AND array_lower(con.confkey, 1) = 1 - AND con.confkey[1] = att2.attnum - AND att2.attrelid = cl2.oid - AND con.contype = 'f' - """ - self._cr.execute(query, (table,)) - return self._cr.fetchall() - - def _merge(self, partner_ids, dst_partner=None): - """ private implementation of merge partner - :param partner_ids : ids of partner to merge - :param dst_partner : record of destination res.partner - """ - partner = self.env['res.partner'] - partner_ids = partner.browse(partner_ids).exists() - if len(partner_ids) < 2: - return - - if len(partner_ids) > 3: - raise UserError(_("For safety reasons, you cannot merge more than 3 contacts together. You can re-open the wizard several times if needed.")) - - # check if the list of partners to merge contains child/parent relation - child_ids = self.env['res.partner'] - for partner_id in partner_ids: - child_ids |= partner.search([('id', 'child_of', [partner_id.id])]) - partner_id - if partner_ids & child_ids: - raise UserError(_("You cannot merge a contact with one of his parent.")) - - # remove dst_partner from partners to merge - if dst_partner and dst_partner in partner_ids: - src_partners = partner_ids - dst_partner - else: - ordered_partners = self._get_ordered_partner(partner_ids.ids) - dst_partner = ordered_partners[-1] - src_partners = ordered_partners[:-1] - _logger.info("dst_partner: %s", dst_partner.id) - - # call sub methods to do the merge - self._update_foreign_keys(src_partners, dst_partner) - self._update_reference_fields(src_partners, dst_partner) - self._update_values(src_partners, dst_partner) - - _logger.info('(uid = %s) merged the partners %r with %s', self._uid, src_partners.ids, dst_partner.id) - dst_partner.message_post(body='%s %s' % (_("Merged with the following partners:"), ", ".join('%s <%s> (ID %s)' % (p.name, p.email or 'n/a', p.id) for p in src_partners))) - - return dst_partner - - @api.model - def _update_foreign_keys(self, src_partners, dst_partner): - """ Update all foreign key from the src_partner to dst_partner. All many2one fields will be updated. - :param src_partners : merge source res.partner recordset (does not include destination one) - :param dst_partner : record of destination res.partner - """ - _logger.debug('_update_foreign_keys for dst_partner: %s for src_partners: %s', dst_partner.id, str(src_partners.ids)) - - # find the many2one relation to a partner - partner = self.env['res.partner'] - relations = self._get_fk_on('res_partner') - - for table, column in relations: - # get list of columns of current table (exept the current fk column) - query = "SELECT column_name FROM information_schema.columns WHERE table_name LIKE '%s'" % (table) - self._cr.execute(query, ()) - columns = [] - for data in self._cr.fetchall(): - if data[0] != column: - columns.append(data[0]) - - # do the update for the current table/column in SQL - query_dic = { - 'table': table, - 'column': column, - 'value': columns[0], - } - if len(columns) <= 1: - # unique key treated - query = """ - UPDATE "%(table)s" as ___tu - SET %(column)s = %%s - WHERE - %(column)s = %%s AND - NOT EXISTS ( - SELECT 1 - FROM "%(table)s" as ___tw - WHERE - %(column)s = %%s AND - ___tu.%(value)s = ___tw.%(value)s - )""" % query_dic - for partner in src_partners: - self._cr.execute(query, (dst_partner.id, partner.id, dst_partner.id)) - else: - try: - with mute_logger('odoo.sql_db'), self._cr.savepoint(): - query = 'UPDATE "%(table)s" SET %(column)s = %%s WHERE %(column)s IN %%s' % query_dic - self._cr.execute(query, (dst_partner.id, tuple(src_partners.ids),)) - - # handle the recursivity with parent relation - if column == partner._parent_name and table == 'res_partner': - query = """ - WITH RECURSIVE cycle(id, parent_id) AS ( - SELECT id, parent_id FROM res_partner - UNION - SELECT cycle.id, res_partner.parent_id - FROM res_partner, cycle - WHERE res_partner.id = cycle.parent_id AND - cycle.id != cycle.parent_id - ) - SELECT id FROM cycle WHERE id = parent_id AND id = %s - """ - self._cr.execute(query, (dst_partner.id,)) - # NOTE JEM : shouldn't we fetch the data ? - except psycopg2.Error: - # updating fails, most likely due to a violated unique constraint - # keeping record with nonexistent partner_id is useless, better delete it - query = 'DELETE FROM "%(table)s" WHERE "%(column)s" IN %%s' % query_dic - self._cr.execute(query, (tuple(src_partners.ids),)) - - @api.model - def _update_reference_fields(self, src_partners, dst_partner): - """ Update all reference fields from the src_partner to dst_partner. - :param src_partners : merge source res.partner recordset (does not include destination one) - :param dst_partner : record of destination res.partner - """ - _logger.debug('_update_reference_fields for dst_partner: %s for src_partners: %r', dst_partner.id, src_partners.ids) - - def update_records(model, src, field_model='model', field_id='res_id'): - Model = self.env[model] if model in self.env else None - if Model is None: - return - records = Model.sudo().search([(field_model, '=', 'res.partner'), (field_id, '=', src.id)]) - try: - with mute_logger('odoo.sql_db'), self._cr.savepoint(): - return records.sudo().write({field_id: dst_partner.id}) - except psycopg2.Error: - # updating fails, most likely due to a violated unique constraint - # keeping record with nonexistent partner_id is useless, better delete it - return records.sudo().unlink() - - update_records = functools.partial(update_records) - - for partner in src_partners: - update_records('calendar', src=partner, field_model='model_id.model') - update_records('ir.attachment', src=partner, field_model='res_model') - update_records('mail.followers', src=partner, field_model='res_model') - update_records('mail.message', src=partner) - update_records('ir.model.data', src=partner) - - records = self.env['ir.model.fields'].search([('ttype', '=', 'reference')]) - for record in records.sudo(): - try: - Model = self.env[record.model] - field = Model._fields[record.name] - except KeyError: - # unknown model or field => skip - continue - - if field.compute is not None: - continue - - for partner in src_partners: - records_ref = Model.sudo().search([(record.name, '=', 'res.partner,%d' % partner.id)]) - values = { - record.name: 'res.partner,%d' % dst_partner.id, - } - records_ref.sudo().write(values) - - @api.model - def _update_values(self, src_partners, dst_partner): - """ Update values of dst_partner with the ones from the src_partners. - :param src_partners : recordset of source res.partner - :param dst_partner : record of destination res.partner - """ - _logger.debug('_update_values for dst_partner: %s for src_partners: %r', dst_partner.id, src_partners.ids) - - model_fields = dst_partner.fields_get().keys() - - def write_serializer(item): - if isinstance(item, models.BaseModel): - return item.id - else: - return item - # get all fields that are not computed or x2many - values = dict() - for column in model_fields: - field = dst_partner._fields[column] - if field.type not in ('many2many', 'one2many') and field.compute is None: - for item in itertools.chain(src_partners, [dst_partner]): - if item[column]: - values[column] = write_serializer(item[column]) - # remove fields that can not be updated (id and parent_id) - values.pop('id', None) - parent_id = values.pop('parent_id', None) - src_partners.update({ - 'active': False, - 'document_number': False, - 'vat': False, - 'main_partner_id': dst_partner.id}) - dst_partner.write(values) - # try to update the parent_id - if parent_id and parent_id != dst_partner.id: - try: - dst_partner.write({'parent_id': parent_id}) - except ValidationError: - _logger.info('Skip recursive partner hierarchies for parent_id %s of partner: %s', parent_id, dst_partner.id) - - @api.model - def _get_ordered_partner(self, partner_ids): - """ Helper : returns a `res.partner` recordset ordered by create_date/active fields - :param partner_ids : list of partner ids to sort - """ - return self.env['res.partner'].browse(partner_ids).sorted( - key=lambda p: (p.active, (p.create_date or '')), - reverse=True, - ) - - - def _compute_models(self): - """ Compute the different models needed by the system if you want to exclude some partners. """ - model_mapping = {} - if self.exclude_contact: - model_mapping['res.users'] = 'partner_id' - if 'account.move.line' in self.env and self.exclude_journal_item: - model_mapping['account.move.line'] = 'partner_id' - return model_mapping diff --git a/hotel_l10n_es/report/report_parte_viajero.xml b/hotel_l10n_es/report/report_parte_viajero.xml deleted file mode 100755 index cfaaf4b2d..000000000 --- a/hotel_l10n_es/report/report_parte_viajero.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - diff --git a/hotel_l10n_es/security/ir.model.access.csv b/hotel_l10n_es/security/ir.model.access.csv deleted file mode 100755 index 7a6b2cc1c..000000000 --- a/hotel_l10n_es/security/ir.model.access.csv +++ /dev/null @@ -1,7 +0,0 @@ -"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -"access_hotel_l10n_es.model_category_user","hotell10n.category_ine.user","hotel_l10n_es.model_tourism_category","hotel.group_hotel_user",1,0,0,0 -"access_hotel_l10n_es.code_ine_user","hotell10n.code_ine.user","hotel_l10n_es.model_code_ine","hotel.group_hotel_user",1,0,0,0 -"access_hotel_l10n_es.model_category_manager","hotell10n.category_ine.manager","hotel_l10n_es.model_tourism_category","hotel.group_hotel_manager",1,1,1,1 -"access_hotel_l10n_es.code_ine_manager","hotell10n.code_ine.manager","hotel_l10n_es.model_code_ine","hotel.group_hotel_manager",1,1,1,1 -"access_hotel_l10n_es.model_category_call","hotell10n.category_ine.call","hotel_l10n_es.model_tourism_category","hotel.group_hotel_call",1,0,0,0 -"access_hotel_l10n_es.code_ine_call","hotell10n.code_ine.call","hotel_l10n_es.model_code_ine","hotel.group_hotel_call",1,0,0,0 diff --git a/hotel_l10n_es/static/description/icon.png b/hotel_l10n_es/static/description/icon.png deleted file mode 100755 index 367e0ad234cc47c00bf2e0b28b70d5b4a894fe88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19995 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 - - - diff --git a/hotel_l10n_es/views/category_tourism.xml b/hotel_l10n_es/views/category_tourism.xml deleted file mode 100755 index fff2798a2..000000000 --- a/hotel_l10n_es/views/category_tourism.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - tourism.category.view.form - tourism.category - -

      - - - - - - - - - - - - - -
      - - - - - tourism.category.view.tree - tourism.category - - - - - - - - diff --git a/hotel_l10n_es/views/code_ine.xml b/hotel_l10n_es/views/code_ine.xml deleted file mode 100755 index d2f5b831b..000000000 --- a/hotel_l10n_es/views/code_ine.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - code.ine.view.form - code.ine - -
      - - - - - - - - - - - - - -
      -
      -
      - - - code.ine.view.tree - code.ine - - - - - - - - -
      diff --git a/hotel_l10n_es/views/hotel_l10n_es_hotel_name.xml b/hotel_l10n_es/views/hotel_l10n_es_hotel_name.xml deleted file mode 100755 index c808e8ebd..000000000 --- a/hotel_l10n_es/views/hotel_l10n_es_hotel_name.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/hotel_l10n_es/views/inherit_hotel_checkin_partner_views.xml b/hotel_l10n_es/views/inherit_hotel_checkin_partner_views.xml deleted file mode 100755 index 853dd6c65..000000000 --- a/hotel_l10n_es/views/inherit_hotel_checkin_partner_views.xml +++ /dev/null @@ -1,109 +0,0 @@ - - - - - hotel.checkin.partner.view.form - hotel.checkin.partner - - - - - - - - - - - - - - - -
      - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/hotel_l10n_es/views/inherited_hotel_reservation_views.xml b/hotel_l10n_es/views/inherited_hotel_reservation_views.xml deleted file mode 100644 index bbf2979e7..000000000 --- a/hotel_l10n_es/views/inherited_hotel_reservation_views.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - hotel.reservation - - - -
      - -
      -

      -

      -
      -
      - -
      -

      Abrir web de la Polícia para entregar el fichero generado: Policia

      -

      Abrir web de la Guardia Civil para entregar el fichero generado: Guardia Civil

      -
      -
      - - - - - - - - diff --git a/hotel_roommatik/__init__.py b/hotel_roommatik/__init__.py deleted file mode 100755 index 0650744f6..000000000 --- a/hotel_roommatik/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import models diff --git a/hotel_roommatik/__manifest__.py b/hotel_roommatik/__manifest__.py deleted file mode 100755 index 30719f197..000000000 --- a/hotel_roommatik/__manifest__.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2018 Jose Luis Algara (Alda hotels) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -{ - 'name': 'Hotel RoomMatik', - 'description': """ - Integration of Hootel with the RoomMatik kiosk""", - 'summary': """ - The integration of Hootel with the RoomMatik kiosk. - A series of requests/responses that provide the basic - information needed by the kiosk.""", - 'version': '11.0.1.0.0', - 'license': 'AGPL-3', - 'author': 'Jose Luis Algara (Alda hotels) ', - 'website': 'https://www.aldahotels.com', - 'category': 'Generic Modules/Hotel Management', - 'depends': [ - 'hotel', - 'partner_contact_gender', - 'partner_contact_birthdate', - 'base_iso3166', - 'base_location', - ], - 'data': [ - 'data/res_users_data.xml' - ], - 'demo': [ - ], -} diff --git a/hotel_roommatik/data/res_users_data.xml b/hotel_roommatik/data/res_users_data.xml deleted file mode 100644 index d22fa0178..000000000 --- a/hotel_roommatik/data/res_users_data.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - Roommatik - - - - - - - roommatik@roommatik.com - - - - - - - --
      -Roommatik]]>
      - -
      -
      -
      diff --git a/hotel_roommatik/models/__init__.py b/hotel_roommatik/models/__init__.py deleted file mode 100755 index 6a96de1e6..000000000 --- a/hotel_roommatik/models/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from . import inherited_hotel_checkin_partner -from . import inherited_res_partner -from . import inherited_hotel_room_type -from . import roommatik -from . import inherited_hotel_reservation -from . import inherited_account_payment diff --git a/hotel_roommatik/models/inherited_account_payment.py b/hotel_roommatik/models/inherited_account_payment.py deleted file mode 100644 index e6c2ef37e..000000000 --- a/hotel_roommatik/models/inherited_account_payment.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2017 Dario Lodeiros -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo.exceptions import except_orm -from odoo import models, fields, api, _ - -class AccountPayment(models.Model): - _inherit = 'account.payment' - - @api.model - def rm_add_payment(self, code, payment): - reservation = self.env['hotel.reservation'].search([ - '|', ('localizator', '=', code), - ('folio_id.name', '=', code)]) - if not reservation: - return False - if reservation: - for cashpay in payment['CashPayments']: - vals = { - 'journal_id': 7, # TODO:config setting - 'partner_id': reservation.partner_invoice_id.id, - 'amount': cashpay['Amount'], - 'payment_date': cashpay['DateTime'], - 'communication': reservation.name, - 'folio_id': reservation.folio_id.id, - 'payment_type': 'inbound', - 'payment_method_id': 1, - 'partner_type': 'customer', - 'state': 'draft', - } - pay = self.create(vals) - for cashpay in payment['CreditCardPayments']: - vals = { - 'journal_id': 15, # TODO:config setting - 'partner_id': reservation.partner_invoice_id.id, - 'amount': cashpay['Amount'], - 'payment_date': cashpay['DateTime'], - 'communication': reservation.name, - 'folio_id': reservation.folio_id.id, - 'payment_type': 'inbound', - 'payment_method_id': 1, - 'partner_type': 'customer', - 'state': 'draft', - } - pay = self.create(vals) - pay.post() - return True diff --git a/hotel_roommatik/models/inherited_hotel_checkin_partner.py b/hotel_roommatik/models/inherited_hotel_checkin_partner.py deleted file mode 100644 index d3e83a21e..000000000 --- a/hotel_roommatik/models/inherited_hotel_checkin_partner.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright 2019 Jose Luis Algara (Alda hotels) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -import json -from odoo import api, models -from odoo.addons.hotel_roommatik.models.roommatik import ( - DEFAULT_ROOMMATIK_DATE_FORMAT, - DEFAULT_ROOMMATIK_DATETIME_FORMAT) -from datetime import datetime -import logging - - -class HotelCheckinPartner(models.Model): - - _inherit = 'hotel.checkin.partner' - - @api.model - def rm_checkin_partner(self, stay): - _logger = logging.getLogger(__name__) - if not stay.get('ReservationCode'): - reservation_obj = self.env['hotel.reservation'] - vals = { - 'checkin': stay["Arrival"], - 'checkout': stay["Departure"], - 'adults': stay['Adults'], - 'arrival_hour': stay['Arrival_hour'], - 'room_type_id': stay['RoomType']['Id'], - 'partner_id': stay["Customers"][0]["Id"], - 'segmentation_ids': [(6, 0, [stay['Segmentation']])], - 'channel_type': 'virtualdoor', - } - reservation_rm = reservation_obj.create(vals) - stay['ReservationCode'] = reservation_rm.localizator - else: - reservation_rm = self.env['hotel.reservation'].search([ - ('localizator', '=', stay['ReservationCode']) - ]) - total_chekins = reservation_rm.checkin_partner_pending_count - stay['Total'] = reservation_rm.folio_pending_amount - stay['Paid'] = reservation_rm._computed_deposit_roommatik(stay['ReservationCode']) - if total_chekins > 0 and len(stay["Customers"]) <= total_chekins: - _logger.info('ROOMMATIK checkin %s customer in %s Reservation.', - total_chekins, - reservation_rm.id) - for room_partner in stay["Customers"]: - if room_partner['Address']['Nationality'] == 'ESP': - code_ine = room_partner['Address']['Province'] - else: - code_ine = room_partner['Address']['Nationality'] - province = self.env['code.ine'].search( - [('name', '=', code_ine)], limit=1) - code_ine = province.id - - checkin_partner_val = { - 'folio_id': reservation_rm.folio_id.id, - 'reservation_id': reservation_rm.id, - 'partner_id': room_partner["Id"], - 'enter_date': stay["Arrival"], - 'exit_date': stay["Departure"], - 'code_ine_id': code_ine, - } - try: - record = self.env['hotel.checkin.partner'].create( - checkin_partner_val) - _logger.info('ROOMMATIK check-in partner: %s in \ - (%s Reservation) ID:%s.', - checkin_partner_val['partner_id'], - checkin_partner_val['reservation_id'], - record.id) - if not record.reservation_id.segmentation_ids: - record.reservation_id.update({ - 'segmentation_ids': [(6, 0, [stay['Segmentation']])] - }) - record.action_on_board() - stay['Id'] = record.id - stay['Room'] = {} - stay['Room']['Id'] = reservation_rm.room_id.id - stay['Room']['Name'] = reservation_rm.room_id.name - json_response = stay - except Exception as e: - error_name = 'Error not create Checkin ' - error_name += str(e) - json_response = {'State': error_name} - _logger.error('ROOMMATIK writing %s in reservation: %s).', - checkin_partner_val['partner_id'], - checkin_partner_val['reservation_id']) - return json_response - - else: - json_response = {'State': 'Error checkin_partner_pending_count \ - values do not match.'} - _logger.error('ROOMMATIK checkin pending count do not match for \ - Reservation ID %s.', reservation_rm.id) - json_response = json.dumps(json_response) - return json_response - - @api.model - def rm_get_stay(self, code): - # BUSQUEDA POR LOCALIZADOR - checkin_partner = self.search([('id', '=', code)]) - # TODO: refactoring 'res.config.settings', 'default_arrival_hour' by the current self.env.user.hotel_id.arrival_hour - default_arrival_hour = self.env['ir.default'].sudo().get( - 'res.config.settings', 'default_arrival_hour') - # TODO: refactoring 'res.config.settings', 'default_departure_hour' by the current self.env.user.hotel_id.departure_hour - default_departure_hour = self.env['ir.default'].sudo().get( - 'res.config.settings', 'default_departure_hour') - if any(checkin_partner): - arrival = checkin_partner.enter_date or default_arrival_hour - departure = checkin_partner.exit_date or default_departure_hour - stay = {'Code': checkin_partner.id} - stay['Id'] = checkin_partner.id - stay['Room'] = {} - stay['Room']['Id'] = checkin_partner.reservation_id.room_id.id - stay['Room']['Name'] = checkin_partner.reservation_id.room_id.name - stay['RoomType'] = {} - stay['RoomType']['Id'] = checkin_partner.reservation_id.room_type_id.id - stay['RoomType']['Name'] = checkin_partner.reservation_id.room_type_id.name - stay['Arrival'] = arrival - stay['Departure'] = departure - stay['Customers'] = [] - for idx, cpi in enumerate(checkin_partner.reservation_id.checkin_partner_ids): - stay['Customers'].append({'Customer': {}}) - stay['Customers'][idx]['Customer'] = self.env[ - 'res.partner'].rm_get_a_customer(cpi.partner_id.id) - stay['TimeInterval'] = {} - stay['TimeInterval']['Id'] = {} - stay['TimeInterval']['Name'] = {} - stay['TimeInterval']['Minutes'] = {} - stay['Adults'] = checkin_partner.reservation_id.adults - stay['ReservationCode'] = checkin_partner.reservation_id.localizator - stay['Total'] = checkin_partner.reservation_id.price_total - stay['Paid'] = checkin_partner.reservation_id.folio_id.invoices_paid - stay['Outstanding'] = checkin_partner.reservation_id.folio_id.pending_amount - stay['Taxable'] = checkin_partner.reservation_id.price_tax - - else: - stay = {'Code': ""} - - json_response = json.dumps(stay) - return json_response diff --git a/hotel_roommatik/models/inherited_hotel_reservation.py b/hotel_roommatik/models/inherited_hotel_reservation.py deleted file mode 100644 index 1938f3612..000000000 --- a/hotel_roommatik/models/inherited_hotel_reservation.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright 2019 Jose Luis Algara (Alda hotels) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import api, models -import json -import logging -_logger = logging.getLogger(__name__) - - -class HotelReservation(models.Model): - - _inherit = 'hotel.reservation' - - @api.model - def _computed_deposit_roommatik(self, rm_localizator): - reservations = self.env['hotel.reservation'].search([ - ('localizator', '=', rm_localizator)]) - folio = reservations[0].folio_id - # We dont have the payments by room, that's why we have to computed - # the proportional deposit part if the folio has more rooms that the - # reservations code (this happens when in the same folio are - # reservations with different checkins/outs convinations) - if len(folio.reservation_ids) > len(reservations) and folio.invoices_paid > 0: - - total_reservations = sum(reservations.mapped('price_total')) - paid_in_folio = folio.invoices_paid - total_in_folio = folio.amount_total - deposit = total_reservations * paid_in_folio / total_in_folio - return deposit - return folio.invoices_paid - - @api.model - def rm_get_reservation(self, code): - # Search by localizator - reservations = self._get_reservations_roommatik(code) - reservations = reservations.filtered( - lambda x: x.state in ('draft', 'confirm')) - if any(reservations): - # TODO: refactoring 'res.config.settings', 'default_arrival_hour' by the current self.env.user.hotel_id.arrival_hour - default_arrival_hour = self.env['ir.default'].sudo().get( - 'res.config.settings', 'default_arrival_hour') - checkin = "%s %s" % (reservations[0].checkin, - default_arrival_hour) - # TODO: refactoring 'res.config.settings', 'default_departure_hour' by the current self.env.user.hotel_id.departure_hour - default_departure_hour = self.env['ir.default'].sudo().get( - 'res.config.settings', 'default_departure_hour') - checkout = "%s %s" % (reservations[0].checkout, - default_departure_hour) - _logger.info('ROOMMATIK serving Folio: %s', reservations.ids) - json_response = { - 'Reservation': { - 'Id': reservations[0].localizator, - 'Arrival': checkin, - 'Departure': checkout, - 'Deposit': self._computed_deposit_roommatik(code) - } - } - for i, line in enumerate(reservations): - total_chekins = line.checkin_partner_pending_count - json_response['Reservation'].setdefault('Rooms', []).append({ - 'Id': line.id, - 'Adults': line.adults, - 'IsAvailable': total_chekins > 0, - # IsAvailable “false” Rooms not need check-in - 'Price': line.price_total, - 'RoomTypeId': line.room_type_id.id, - 'RoomTypeName': line.room_type_id.name, - 'RoomName': line.room_id.name, - }) - else: - _logger.warning('ROOMMATIK Not Found reservation search %s', code) - json_response = {'Error': 'Not Found ' + str(code)} - return json.dumps(json_response) - - @api.model - def _get_reservations_roommatik(self, code): - return self.env['hotel.reservation'].search([ - '|', ('localizator', '=', code), - ('folio_id.name', '=', code)]) diff --git a/hotel_roommatik/models/inherited_hotel_room_type.py b/hotel_roommatik/models/inherited_hotel_room_type.py deleted file mode 100644 index d6110edb4..000000000 --- a/hotel_roommatik/models/inherited_hotel_room_type.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright 2019 Jose Luis Algara (Alda hotels) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import api, models, fields -from datetime import datetime, timedelta -import json -from odoo.addons.hotel_roommatik.models.roommatik import ( - DEFAULT_ROOMMATIK_DATE_FORMAT,) -import logging -_logger = logging.getLogger(__name__) - - -class HotelRoomType(models.Model): - - _inherit = "hotel.room.type" - - @api.model - def rm_get_all_room_type_rates(self): - room_types = self.env['hotel.room.type'].search([]) - # TODO: refactoring 'res.config.settings', 'default_departure_hour' by the current self.env.user.hotel_id - tz_hotel = self.env['ir.default'].sudo().get( - 'res.config.settings', 'tz_hotel') - dfrom = fields.Date.context_today(self.with_context( - tz=tz_hotel)) - dto = (fields.Date.from_string(dfrom) + timedelta(days=1)).strftime( - DEFAULT_ROOMMATIK_DATE_FORMAT) - room_type_rates = [] - for room_type in room_types: - free_rooms = self.check_availability_room_type(dfrom, dto, - room_type.id) - rates = self.get_rate_room_types( - room_type_ids=room_type.id, - date_from=dfrom, - days=1, - partner_id=False) - room_type_rates.append({ - "RoomType": { - "Id": room_type.id, - "Name": room_type.name, - "GuestNumber": room_type.get_capacity() - }, - "TimeInterval": { - "Id": "1", - "Name": "1 day", - "Minutes": "1440" - }, - "Price": rates[room_type.id][0].get('price'), - "IsAvailable": any(free_rooms), - }) - json_response = json.dumps(room_type_rates) - return json_response - - @api.model - def rm_get_prices(self, start_date, number_intervals, - room_type, guest_number): - start_date = fields.Date.from_string(start_date) - end_date = start_date + timedelta(days=int(number_intervals)) - dfrom = start_date.strftime( - DEFAULT_ROOMMATIK_DATE_FORMAT) - dto = end_date.strftime( - DEFAULT_ROOMMATIK_DATE_FORMAT) - free_rooms = self.check_availability_room_type(dfrom, dto, - room_type.id) - if free_rooms: - rates = self.get_rate_room_types( - room_type_ids=room_type.id, - date_from=dfrom, - days=int(number_intervals), - partner_id=False) - return [item['price'] for item in rates.get(room_type.id)] - return [] diff --git a/hotel_roommatik/models/inherited_res_partner.py b/hotel_roommatik/models/inherited_res_partner.py deleted file mode 100755 index c806401da..000000000 --- a/hotel_roommatik/models/inherited_res_partner.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright 2019 Jose Luis Algara (Alda hotels) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -import json -from odoo import api, models -from datetime import datetime -import logging -from odoo.addons.hotel_roommatik.models.roommatik import ( - DEFAULT_ROOMMATIK_DATE_FORMAT) - - -class ResPartner(models.Model): - - _inherit = 'res.partner' - - @api.model - def rm_add_customer(self, customer): - # RoomMatik API CREACIÓN DE CLIENTE - _logger = logging.getLogger(__name__) - - partner_res = self.env['res.partner'].search([( - 'document_number', '=', - customer['IdentityDocument']['Number'])]) - - json_response = {'Id': 0} - write_customer = False - if any(partner_res): - # Change customer data - try: - partner_res[0].update(self.rm_prepare_customer(customer)) - write_customer = partner_res[0] - _logger.info('ROOMMATIK %s exist in BD [ %s ] Rewriting', - partner_res[0].document_number, - partner_res[0].id,) - except Exception as e: - if 'args' in e.__dir__(): - error_name = e.args - else: - error_name = e.name - else: - # Create new customer - try: - self.create(self.rm_prepare_customer(customer)) - _logger.info('ROOMMATIK Created %s Name: %s', - customer['IdentityDocument']['Number'], - customer['FirstName']) - write_customer = self.env['res.partner'].search([ - ('document_number', '=', - customer['IdentityDocument']['Number'])]) - except Exception as e: - if 'args' in e.__dir__(): - error_name = e.args - else: - error_name = e.name - - partner_res = self.env['res.partner'].search([( - 'document_number', '=', - customer['IdentityDocument']['Number'])]) - partner_res.unlink() - - - if write_customer: - json_response = self.rm_get_a_customer(write_customer.id) - json_response = json.dumps(json_response) - return json_response - else: - _logger.error(error_name) - return [False, error_name] - - def rm_prepare_customer(self, customer): - zip = self.env['res.better.zip'].search([ - ('name', 'ilike', customer['Address']['ZipCode'])]) - # Check Sex string - if customer['Sex'] not in {'male', 'female'}: - customer['Sex'] = '' - # Check state_id - state = self.env['res.country.state'].search([ - ('name', 'ilike', customer['Address']['Province'])]) - if not state and zip: - state = zip.state_id - country = self.env['res.country'].search([ - ('code_alpha3', '=', customer['Address']['Country'])]) - if not country and zip: - country = zip.country_id - # Create Street2s - street_2 = customer['Address']['House'] - street_2 += ' ' + customer['Address']['Flat'] - street_2 += ' ' + customer['Address']['Number'] - metadata = { - 'firstname': customer['FirstName'], - 'lastname': customer['LastName1'] + ' ' + customer['LastName2'], - 'lastname2': '', - 'birthdate_date': datetime.strptime( - customer['Birthday'], DEFAULT_ROOMMATIK_DATE_FORMAT).date(), - 'gender': customer['Sex'], - 'zip': zip, - 'city': customer['Address']['City'], - 'street': customer['Address']['Street'], - 'street2': street_2, - 'state_id': state.id if state else False, - 'country_id': country.id if country else False, - 'phone': customer['Contact']['Telephone'], - 'mobile': customer['Contact']['Mobile'], - 'email': customer['Contact']['Email'], - 'document_number': customer['IdentityDocument']['Number'], - 'document_type': customer['IdentityDocument']['Type'], - 'document_expedition_date': datetime.strptime( - customer['IdentityDocument']['ExpeditionDate'], - DEFAULT_ROOMMATIK_DATE_FORMAT).date(), - } - return {k: v for k, v in metadata.items() if v != ""} - - def rm_get_a_customer(self, customer): - # Prepare a Customer for RoomMatik - partner = self.search([('id', '=', customer)]) - response = {} - response['Id'] = partner.id - response['FirstName'] = partner.firstname - response['LastName1'] = partner.lastname - response['LastName2'] = '' - response['Birthday'] = partner.birthdate_date - response['Sex'] = partner.gender - response['Address'] = { - # 'Nationality': 'xxxxx' - 'Country': partner.country_id.code_alpha3, - 'ZipCode': partner.zip if partner.zip else "", - 'City': partner.city if partner.city else "", - 'Street': partner.street if partner.street else "", - 'House': partner.street2 if partner.street2 else "", - # 'Flat': "xxxxxxx", - # 'Number': "xxxxxxx", - 'Province': partner.state_id.name if partner.state_id.name else "", - } - response['IdentityDocument'] = { - 'Number': partner.document_number, - 'Type': partner.document_type, - 'ExpiryDate': "", - 'ExpeditionDate': partner.document_expedition_date, - } - response['Contact'] = { - 'Telephone': partner.phone if partner.phone else "", - # 'Fax': 'xxxxxxx', - 'Mobile': partner.mobile if partner.mobile else "", - 'Email': partner.email if partner.email else "", - } - return response diff --git a/hotel_roommatik/models/roommatik.py b/hotel_roommatik/models/roommatik.py deleted file mode 100755 index 01e55ae17..000000000 --- a/hotel_roommatik/models/roommatik.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright 2019 Jose Luis Algara (Alda hotels) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -import json -from datetime import datetime -from odoo import api, models, fields -# from odoo.tools import ( -# DEFAULT_SERVER_DATE_FORMAT, -# DEFAULT_SERVER_DATETIME_FORMAT) -import logging -_logger = logging.getLogger(__name__) - -DEFAULT_ROOMMATIK_DATE_FORMAT = "%Y-%m-%d" -DEFAULT_ROOMMATIK_TIME_FORMAT = "%H:%M:%S" -DEFAULT_ROOMMATIK_DATETIME_FORMAT = "%s %s" % ( - DEFAULT_ROOMMATIK_DATE_FORMAT, - DEFAULT_ROOMMATIK_TIME_FORMAT) - -class RoomMatik(models.Model): - _name = 'roommatik.api' - - @api.model - def rm_get_date(self): - # RoomMatik API Gets the current business date/time. (MANDATORY) - # TODO: refactoring 'res.config.settings', 'default_departure_hour' by the current self.env.user.hotel_id - tz_hotel = self.env['ir.default'].sudo().get( - 'res.config.settings', 'tz_hotel') - self_tz = self.with_context(tz=tz_hotel) - mynow = fields.Datetime.context_timestamp(self_tz, datetime.now()).\ - strftime(DEFAULT_ROOMMATIK_DATETIME_FORMAT) - json_response = { - 'dateTime': mynow - } - json_response = json.dumps(json_response) - return json_response - - @api.model - def rm_get_reservation(self, reservation_code): - # RoomMatik Gets a reservation ready for check-in - # through the provided code. (MANDATORY) - apidata = self.env['hotel.reservation'] - return apidata.sudo().rm_get_reservation(reservation_code) - - @api.model - def rm_add_customer(self, customer): - # RoomMatik API Adds a new PMS customer through the provided parameters - # Addition will be ok if the returned customer has ID. (MANDATORY) - _logger.info('ROOMMATIK Customer Creation') - apidata = self.env['res.partner'] - return apidata.sudo().rm_add_customer(customer) - - @api.model - def rm_checkin_partner(self, stay): - # RoomMatik API Check-in a stay. - # Addition will be ok if the returned stay has ID. (MANDATORY) - _logger.info('ROOMMATIK Check-IN') - apidata = self.env['hotel.checkin.partner'] - return apidata.sudo().rm_checkin_partner(stay) - - @api.model - def rm_get_stay(self, check_in_code): - # RoomMatik API Gets stay information through check-in code - # (if code is related to a current stay) - # (MANDATORY for check-out kiosk) - apidata = self.env['hotel.checkin.partner'] - return apidata.sudo().rm_get_stay(check_in_code) - - @api.model - def rm_get_all_room_type_rates(self): - # Gets the current room rates and availability. (MANDATORY) - # return ArrayOfRoomTypeRate - _logger.info('ROOMMATIK Get Rooms and Rates') - apidata = self.env['hotel.room.type'] - return apidata.sudo().rm_get_all_room_type_rates() - - @api.model - def rm_get_prices(self, start_date, number_intervals, room_type, guest_number): - # Gets some prices related to different dates of the same stay. - # return ArrayOfDecimal - apidata = self.env['hotel.room.type'] - room_type = apidata.browse(int(room_type)) - _logger.info('ROOMMATIK Get Prices') - if not room_type: - return {'State': 'Error Room Type not Found'} - return apidata.sudo().rm_get_prices(start_date, number_intervals, room_type, guest_number) - - @api.model - def rm_get_segmentation(self): - # Gets segmentation list - # return ArrayOfSegmentation - segmentations = self.env['res.partner.category'].sudo().search([]) - _logger.info('ROOMMATIK Get segmentation') - response = [] - for segmentation in segmentations: - response.append({ - "Segmentation": { - "Id": segmentation.id, - "Name": segmentation.display_name, - }, - }) - json_response = json.dumps(response) - return json_response - - @api.model - def rm_add_payment(self, code, payment): - apidata = self.env['account.payment'] - return apidata.sudo().rm_add_payment(code, payment) - # Debug Stop ------------------- - # import wdb; wdb.set_trace() - # Debug Stop ------------------- diff --git a/hotel_roommatik/static/description/icon.png b/hotel_roommatik/static/description/icon.png deleted file mode 100755 index 2f06baf5512a6218c8e1ec8d112061675e7fcbaa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44610 zcmaI71yqz>+cr#yC>>G~B1m_4h~&`2kdngC-QA!lA>EQptXQ#Icq5@2pZejuo}U~u)5pW z15YC$2#dMf8yQM5r`*6xkK+-$BhSWIY|BYMx3EV^1q% z0TU`QQA%NVL0|$KsIw8JyN$K2lc2i@)!%sqf#2a@vr$q09pY>yLiNw4v=mh+-`P1r zDS23VS&TV2I4Jo9SUGt3xj1>4DLL6Wc-Ytl*f`i(*f|6_*abP+DgXUP1+3<1Vk!uh zl>T=u;FAcIxwEsqARC*Tn;WYe7ptA485@UyfB+jiCmSax3owGk$-~y!$eqR3iTXb? zNJ5>A9WCsgE$nP5;WHY2uyb)1p#paL&k=0w|2wU%)4yT@1jgoWWY5OI$__u$e+DWl z{{IiPvH9<4CucD9|L*sHoY)ECVGm^kL!Ing9F2jCGo^+vWiR;75o+XY=LoU0v;L11 zRm|<2?VQZ*>?z;9`ztj{dQA&k6FWC2hQH4!DhkTkIyoEJ8bjqIMW}#TSS>6}1mE%s z@bJBT$15$#`;LP{iXZqSA`wJzsDA|_}}-!{lCul@3SWV`(Akd*Js&)&9K2Q_y2R*|M?0) z5d7EwLKpb(U+_b10mM53&=!1t)QNylSRp6*7UDj)-|XQ`Hko{7F!@F2w&f^+<$WV_ zZ%o4^3mXn{z!PGwC%?pH4f&BxI0Fo|hi)IZoQ7_NqQ52wn;f* zW+6uB3sgISxj&&KxBAFSzT|NCCVpZCv9{qL!LHi2wk7k=9~`;E_bx?{E45*#BPZ?6 z>&KqI3hJNwE|B$~-yn$apW=moKT)dx{eb{R`TG+F=L7`*|7Qq74xBm={>LE*vIKws z_CF0l_{#YAZ~xNz}jnll)tA~0ZV3Dl`|o!K-oa;- z3V!;U3E{6C(gSr65#XDzd$AAv1*I+RpF`r6S$+bf_Gu;WtR1%u>92V08DTua72I?UqNTTeBAs~dKH9r9sYX|{m@;6m2Xr2(Lsw3Xp z%3n&BUgvu8??J@tl?Gp8h>4B=IvE(fnM2Ff>El%XO@wlNBT@3t)|_`kwb+S$9X6|? zYo7^TT$iC+<{HvGCbjmwviwK5hLFhXck)h;9UV>=JeR&JD7m!l%a0FDNbUeA06_UG zmAt#!-u5%t{Y~;Z^GCd8mngI1xd<2h! zzkjFq>F6j$m;wD70VYy3nKE?0U6{3~gehpQ@3&UJB<^Q_EuwkYKdZO}ztAbfXhwvZ zC(b_$UN)Acy?i<2uQUtJ9ovtHU<1u6tve9{g8F-Fak6Rq`{u;2j77ij;(n8W5k9a+ zewxcnP})Hzaz*i%34+5U*EuTE*M1|W{ln`5HI3p|TDfD+6N(EzeCVq20qJC*0XN`I z3oN!|CF134o0nDvtOkSRqdZ+sZ%qB!!@8#a4*+18XSRE@=uTW1qu!au#j|Y_{%!9X z&lr3jfU_Ml!>|35{6X$e=%_0Co?v=~BM<4Hqq*I+0(!w(tS1QWcmSqWUaJsi>#!3) zEQ~I-n$WwI0Be1N^myDZq$Vq_KJb3hL_`48wk?(>4{yIO;6dWE- z8LZlw;DOLjzU7>4wn!d*{dz(q4i0SKUS}#;p&Pk^2f{t7rE1d!@nw=X}>f|lKH7TXf|B7yP3+FJW1DkFc&LjkMrG z+Oy)%I3Z4NkS;M{z8#ig3`r!K05k#^AWcK~D&6iVDZ^7E|6+gW@mnD`^_z2fl4QJu z5I|@E9>nuyIr(`mOl4A&w?sI_t6AeggF!naVXL^@qK)4){$AqY23RBgX7HLfV^%`Y zB_1s|1*&B^3D;Y7!*@^wf4y)frd9zb&pB(xva;C}5yJ~vhW;o0zaPJ9io>yqz``Z~ zTx0pp2uZf(&cU^bxItb~J4yvO7hIHWtS;m__J>}k2@Nh5dj>R%@x8~n=|Qdm!htJ+ zJc)W8>hqf5oTlG4aiZf!nh4euaDI9qy6^g9!0N(kpWwJX;pv@#X=3#hwI!{>M@E79 z<_W@as~ry`Ia8NyZAM86Ieb1piwC(B*ACy;d%QTb3j+86z{Lvq!-=VrTBZ^1 zD8m}07&b{A(j6yy0-Wko*b_a3wuH6CsmdP9LL&&(>+{`_)0mOYqcoAAB?QAFI9-1i zHww43x7_WNPsh(A_BTI}#~ZkoU_Q4O^Nrf6Y3@MkQxaMHi)lz40P)~Z{=AZUP{0kkOmvvjuS);`I7M<6aDqua{I z!ZCia*4s0*mA~ZSucX@5wXm>P!a}>xyRHFfA~?Z=pNmiM%@crrflby;&~jO^-~NOzivT^uV-jF7aWBU?2e+XLlv-HEr*g`XV)b@+=d<};s zQEVbYHNk?m(l$bAfHD0kmh8l?Botki*O0=|X5Q8DJxxr^nAT`@y2S8AMrhDy#DO8b z$3ZiOsGXhp2`=**>AdxDwhsa>Yt^x=EGJh6MXlC_5?BnlZ)Zy<_vRnIGj-wHFJ_W- zLi&Cy7fi%OasbvH&Q%*Pm&`4Rb@e}xJVj(IH)4#p%rK(*tsli8{|_E!9Hl2`d^QHQ zkJDx`(vc(!{*q?XFZ0Pr$Upj3^I5gIxCqx7?OK#FROebgN10JR2h0%h4r?C`@b>(b zqSw0T)TFBOgsn8oii>NmbbZOU}wW|I>NAG$rxwJ&@UVlFK;x9@)RHx_%U48nMotq zc&1W`t(Q=Fwc(r`bsh4yr6mqas+?&B7hn|O7qJ#j^R>r!q>7lvO#RRihVogh^bE5< zILXs|moHT7TqqGT5UJGLxFSA3iaj=j9qhq91dkR4)O4f?2Zh{vr}@kCI`@~TZvd4C z;7L5GE}fTeU}Xm_uftYR2mudr4tygQa_i~>3v#xX`g;XZvLAD2fAGBJ{c*pXCWe}g z6n8dz-~05^)z3v!TGyN&&<4&UWB@qWT#OH9t#b?6{0`r;JsdIW#a0L*unDh@?92Sz zaeh(2uLR77H8i_|lY{T7=gelj=>5pVN*_WWOr_NJr|t_1IU6aa83KDEz_w=?L&7aW zVmhB=V|YvCwg?M%aR~=mQ)ZFw-D~HLRhbuI)6xKX1}D)Kbijvg15o=oX-6+C__pL> z3p8y+E}_wei8I02JN$S!U=0{HmrjDeU~1FgEmV#gwSKtR`N?pGMyrEZnXQtqtUmIPXT7tfv`bU{-GbOdIhAcL@_o4bcS5q}9_4CsVEow7M@kBC0tt{pQ8a&l-^cJ5y7ts<$zD(_m5A z+i|v`!n8tbdEqR_kZVjjfoA$S;WKxqFX z1VPh0fs7tWrWHa<8t5$D8Tvg80!IVpj0#CzvZ@XF;Xq$~tPwyu%ov)-jN$^Bi{HIt z4BDD!>z#*;P{p0yWOC5z5>>;9@+|8dwS|P~p-FV~Uh_4_ck^b$=gb4tq|X)tjTr~i z=WFtNy2tr9{{f^lO^s?vP;J%l9wsyq_(Qj4w>wtwJYr<9PvrLF9>fJNoxVKM<&sJ5`)XbpD|~Yn>%T z*vU>me5^2f0d%GqTB8`X~$j zjPcS0B)Rao1Ls*a{Gn{B{x8+}8L;uW+e00yAc5jpWp%NLA%({W@7tJeuznPv9pqL! z)yNLdr5NTJ$k74k0E|-eNi~d7-Hw|FgOyESC{MdG;DK|8?+m?}0L@dd*vt7my;Lbq z55e|rJcz~kO)V`gl#cdo;a|hW#RzyxM?ylj-n(w`U^P5)wU~z3!VIgwURKao0(<=G zv3-nZHz1|bU?|}50j`XE1PB82)b|>j6-1eWDq=%Nct-o`Do%T9rrK#bjfyd!9Qq%7 z#SsvDYaZHyK1$CJ%-|FZKLt>izP-Pa{;2twYQCOamTUGVRPDzH-PMf?!?&wl3uHa$ zJt6Sptr8M6?E*GMNp{92S$i92g@h6~Vu5PJ@W5SR$px*NX*@{L)_gm0j1ypQ5fMQ_ zuKT@9jrP(k4S(M!)t^<}m*W#~35jJF5yIRF0FQ@wGQwWLt0MfX)3a;|3uk5<{1PB8 zndcER`^^HNpU{$E>)_1Zh$EMp25t!a-;>4{fG<9mnoe2Mnv-^XZR0Fv-BK5JtJzA4+&_vg=yvv;3bOzo!^T_ThqXVM9}H ztqdPG`|#Tme^Qknc^Z#75H&4VL{~tW+S0?}7f@Th(XmiG^TDX(Rr>BLd-^i}!j?y< z&tjd06meePH;p#(i9Q3W` zqVDe->_$P-`LusynOS3$_aiy{O#W2acYp&FKNEPnSCJ=kcB!K7YBXzp=Op8G)gcUUDT+Gd#k4DwR$5ZU-MX9!xWNo-2wd}k zAV%v*qyA-9jCX#{1k7z2AjQX)5!*Eus9?U7qzV&Tf{#XJRH4=6*u2ws13M!=(EtpD zM5Dn%v6OhPAT|lW_#@>Ew7(xgHyn=utr#3NE9YeY$=dq7j)qj*L+7Jh8Rf#6inG0* zt)PHrLL>KpXz*G2&OPh#=VQrnCt$X=&wXxAeiYB}gIF~c>_0Iwco_MCWE%7|+)+?qk>ECJ;zU}u!BlARph+sh*>btnQ`KGe(Z_&6#5 z=Ji=Nxb@^K7pbhq8*cvVtwQvgqlRj06%;AqObg1*4Lh0wxukoo z%79v$Hybo|+0Jz#dNznA+eqd0(ll$TNrRZi+0$Xw$HK6_v?a9e?CwJCpa6W)iC!v{ z4s1=v$)2i7$LF`s4q>{|-E5{HNZfJ0;&RW4@cNyl41#Xh#Y0@hJ{_{pmF;phHSJR$ zB;L2_fH_UIf~C7YfDHj%oz! zsT5&fGy@j5HVc%ig#E0Ah3iYqRV*7u!zWFENpX4$x?&42eeb=R7qQavBw$OgNDZ2N zKDwu$XgI008vQ8trM5y2X?-;`@Xhl3WX0&8`Gie|v%aSqLP#r~!OPiABld4g=*fMI zG&7!!`5AIWfP_W2_Z-M*K9ER5vhm!rvPL@2IG&OyQ9@Cfs|PDoNV2W9sde zs0imVC#R(vlMqVHAl^T#Em%=EtDX#5#YG+C8hPDDP#P{vFe90b$DnXApBMVDLDOSY z_;HL2zf%t)U=M(McpS5zu>Yy#@1#O*7WzM>`(%Zsmn#z!spK|rdIaJfRkWirSC4)wq6i+|od$Abn3-H#ltf7@2Mq z5OBVL@-sd@k#sMj31wDXT&&|Ar7Z1yVp19fk@Di#zsGGWpyw!@?GDi#N^=!1eJqze z3s3n>e$^lyTIJJ>&&0GrszZ@PO6G|Q#V^tE^m2ZOd`5;^jb!^x;~VGodLLd`?5$dR zDPVC`OdxA=lVydiRq0;&8`Xp35WHl3r4Mgez>G{rprUpl{CBhVWxo@K^R zgF9(hmIU;%BDrHKf)eja7BHVTAk@tiNPge+zU{iu=g-AhSDro1ygj`lo7$g0+_H<$ zAqAYalH+_YhqqsomQFZ4hqmS7^O~Gn*;NF*q!H>URuj#atRE~&F7Gd$wlT%qf&is} ztM6w1tK0b==pZJRZx#r4ongwB{>|64WWPgbYe=y4$?A%YP;bvwyNDDdJ=*ufU1zDh znkjxofWL3?`&7T8tdZ&Y)Y7*v+W|K4SFP77e8~UtFm^f?{j{;Z6_o3l`RiglEUS5D zmYtXJPG7L`bvO_6i-YQhM@n_G^PyR;Mf5W=Mjb?e{u%ncHDTW@{Yc?>Yb(%YR3FxS zENN2RT0wNYHwG`2PyJnGx=t$Xv@ zy97S#v^_g(Wb?xYB&WYOds&~1nTCtNn&;?}K(I$0?ak%KwN*|H-e5IMeYt{`jpw`z z7?r3$-!PTJ`NAW}UXj@w5=h~y_Lk1^Yrmli3gnjS6(y9nG(0{*ypMS7j6%H$$sCUQ zY)ihajPdOJ>g3z+a93#=bTbnztzVc?_PgN-_8c8-qcX@hSGTtkx!_mmukf1$QYDS> zN!<{4#3&n7!+Pw5=A!u1G7njz_m$xl*x(DnI_gVjdE+c+VSGhkCHD-lHG^75 z%By@m7hseHl$=NZZE2dZ@2#1W>5NvzZibMzoZ1fHiY>il1iR&!uHQC@0UT&Y@7M7~ zDYjeN{mciSK4>qKb{vC;@QCmvdP%xNf-PfWn)z)t?b!h>FfEq1I$OurUFPQWhdcCm z{RK;-lMyA_fz7Y#xL)rFO)z`o2Z_JRjLG>uTVLNaX(4y7bU%-Mdohn3w0rA?B9Ofm z{-nC^dDOu3XAr3#?M@F!~#Pqe)gzus6J?&ICO*9mwL7>#WK{aB|eo1@QFKj{@mV(YN-@t5^s?_ z1?&ZlmecqSB0Q%lZ3j4{Gi2F#QJPBL6~FEA!&K_;BCSvDV%a7w1Tsdku(5^)mtreZ zW5!ZCjL29Q%{Wt=X=0#XHhxOw+&PJ9 zY4K95^im)YAYpctVfUAN8Lxi<*JSen6z^WFim9v=F0X|eRCG-54a$AJbeNh?N~Dp` zT0%|Lv9~YovzTtt_#v>9ekYf1+H+-tr`pl=`{XXkiTC{WuWxCKOK2#V`Ps*(p3;?W z?S?Alcm)LZ0jlJmG&|zgOW_h(GG{ge>F;h&v zvHh8&06z}_<5dc02|o=L)N1|#!ps&gDV=x-%Kq(q=i`G?ZMola`ijPjh?#5x4QDF} z6pQ=9`@J-wx==Lo6_VFud%>@s0?C-dtCSSM1j5LfOvQ`!nC4~GL_E8zlFgs`L8}ef zbv)~Y|HMw}ntvklLZdgJ=$(l|tu9%|ZoXQ7+QXR(hqeJDhSa0&oI$ivx zjP$sg>uRA6CM%l_Wt$poEVzFoSFRT}`U8?as%%;PG`P4;aW5_oN-;DLk(Wovn$uST z)C8rg*9cK>Yo`dm+$VWUIfTU-g^|X$9q9}MDHoQ-{8{nb1|RQz2O6o=-@>|D!j*MN z`+L(?TFxNJJ~5$e@^|kb8+@(Vta^iXeZK)a2V_Y6iQ-a#0@TG2w=)XmX3HlCASo*8 zq`mTLX#~6${0i@r-u!Qn(pl0+Z<3mVd;(YkjC&=4EplHMxowM7S zuqh<^(AJJE+$y8(x}vdmAX-+!T2@w{tc*jktdGab^(MT=Lhe;nx>b7ehqUF+I#6zO zWih8l&p%l^$;M=i7!S*?;rDF-F(v&+aNzP&j_0`;*z z0~}FpcH$4sKB2taE*RD*>8D0CZnn!msA&nC5rM$V5+E0YbX#UCTl-^Vj^e6M=Y4*$ zv))Fw2@^Bc_h}xHv77^|WB2Fh2YRytLNe09(WYTrea=dnRWA*+pU3rf(PPwb0N{Pa zz&xdQ==m1*qkQ1!X-ylo3qUo|Xh$;i?~~Dmv2z{`Aze7s;Oe@nbFf--5GPv4`svC3 z;%a-l$-EB`IA=sHwI0TL>P_0W?V4bPV#f4mW?5i0ig(8M(|vqOyj2-c^d+B|w|#mm zAdm`xFgD8|;Fpl{Q?GbP12x`Z^UH>N^N90TIcV18*?4k=zRqiZD)UJXL|MPcdk-wM zE}YU-M7UyMwaoj>E?F}cNiSr?HHmQcP0!!%PGk1>V>T}it{X30JUyMvZweNvVxr~{ z4lMM_sn5Ud%36Ybot)%Ci|c(56=4djT3mYLXJaM8$AwuoS0PC7CRQXZ_2_7C?|C*) zP9M2AfN-fY)oK?vQmCVhGD!xVEd-!qtl>d*V8w$6F>_A&K0# zgoC=toJ=U*Qr}hjLms-jgxV|{4{^?an5DDY^!$8LK`ZY#crkHC?;m-I^+}j^TFp)3x@}l ze5?=D1@Wk=xnm2dao#;O2L_;F6YVMLVcdV5h3xOq2APGXGkZ-jOEKw+HKQlV$>^18 zH9N1{3v5j@O<|B5^BdC#GaD&$4)8DfENc2-PpG)aJ<;F z2ncY4Zpa?kxov|0AFng_mI&kNs(Bi?sEBCPWYwvYo{=dNkHCeWargAN+_^f9ps6n+ zf{};vc;wK#FtX6fWIHzFP`gq1W zR*j;PZtQQJMX8zLR(hxqOarOOM?NNIP+q>Vvr}}xHr4mWUHow~p4A{DvAFn6aEPko zdyn3Yj`se+&El^k_YO)5RUf8KQ4B}c(Bv((6MGr&{5xS5w>L;K_*6p1-+~4N87Rg zgq3d}Md>9@MMNwnwtjSfe>*-VU!QJ@Zfrz`@;h>B8cjo!Pf!F z;`3U6JTQ?Yf14OEdMx!UyU5ku2C6nX&;8!JoAw+Z+cb{d!x-M2q?s-kw^Kq<4mz`B z*?{V|g+kirbF}!>oYlUKo9J)QFDPzUa5k%U==ycv>~$jH0Wf#s#9H}r+%|C_i;nw4 z^%PePZgwMH>_ffeLJ3O_UKpnX&$k4kmoOZpNA>xlQ6H=YvEiXX6E=akDp76nP~_eDRuf#Phm66?~t;ALKT zPBVV>C&2T>1h@dg{*5Q4*-sA%mhtu$ItyGa$R=_2m}ujo<}@ zP1(ug-(93z^N};liJLMgQp?R7l(W+F=RQYQ7rJMkiovj}V3`AD`TSM1)~PH~Z4N=- zPv}%@EYa3y*jbD}sgIT9ou)g0lq8zdTT36BnD7yMP-%y{{^;ZYl?e^4)wSrQ?2c$* zq@}6D$>N#KIup}Jmmx)hj}V);aQDonMOE9FR3^n4i=w*E{?rK~w!c?RPPtJWoblss z^h12FwbJFq0S_^maur^Kxr*jl$(@8o%zsS*ySeUdRRql(&G+6vr{3!X645z)#J)c+C%AZB4185RR~-5> z_A|GDi#`C7L7n+`yKyflhR1E$+5PC zG3EGtIh%QTT14>Xa6`$=0NMKOjU9TREJ3&>mrGw?9OTpo^~(b@;hOYCf2bh-@Vw~q zt6#HTXnjzZh(hhIXtwXot09 zc_=^pJ%7LB-UEd&LmBhgeoAJLj*WOeP`$n7toR=Iu0XIHJ6JPu)}o6NF$Yb0BY|75 zVSA-^)|}SmvfvVmfGQ7uMAvhlMGtbvbf)*WcOMf(MJ7Y5ppPMnNxnf^`b`n(!O0LIdUb~CSKZ4loqLp*Gr0I? z=}AdI^+2eF%>SBMoMxdJLOyvCc{$Z97usEo3=mDckeX9VdTc-Wop=-W^~h*gfbYDF znZLrWYqKWYvgO0_8$nPvq}xg6*F9Xdbnx~7RmWr4=;+7n>_!CRW+m|lf;ipJ#Q23n z;gMuEw&hogTj=p_M7a8@kC(~gd(1a#mK1%xPUDT)_U}QS5a&Bf@-rx^i*w(uRWQC^ zYuVQ7Kw9013J6P-hH=;4#3ZInxzz|ZaBXcE(tT=Vz@~mZ2=wmljB;=+i5g3WNW%L_olVa=~mX7W2Gd%=5(g`1nT_2*fV@bus1D`p@^AP@T_s=9S}(ly~GHV zbQ=2fYqB_Ew&N)5)AxLPE7uR-kGD>bzs5mpHlMh*7sPnqQ!vx#>=rc0$cXLKb3%0j zln^<-a-!P3Wos)S>wMhhd-Oc=$x$AH2yxp_=-U*o;lXBgD{O>jrK7HN8ET)a0gmxT zH=X-{9zIY9QvzkAB(r|{BrG~&chn>La^xuTQ}Os!-@vB3WktY}qlX;$bZjlq{`hts zPqfDGZcle1;Ng5^`vJ7g< zpjM;iVpz6qITRk{Q+pVvRy!3c5SJG7u2lT4b<%=FTm&Ba^M2%Wagib2pUzuj{wQ~J zp5HuEKg5Q>RKi@zMY(;b+W>0OzOpeX=fxKsJs=YntRG&T;dYPd3+hWNhW3EoAofH#gJK zcm<-RydkA<(zK>82s6a8Kw_vgi1^FHl7*#v-vhYp^IFV))(Sbtk04~t ztzei3w^=RQ=U>}Lz4M0RX@4nV4}XX#L--rK^# zNQg&j28DwWqyRcUo8LGxcu9H$>D$aky!X3uzCHCu=$Awt1qUxKd+`YDbvq=tzkcPZ z!dni?z1|AnN3C$=w3x&40IFGXJ~l^d9Zn4|r+?p;BJQ-ZJfl(v+G!0Z>h^yjE10gK zh8Tpo278Ahe}Vr=nC4Jfl<46KHD*btK*^6WnVOcFjnq3GfB*1%qlKzlFN82Y>bY;#0)&B8e01d1DOP%eVJWQock?sl%`!`X zV~xarIFH&QS>BC3H&i{Opf~;@9^|C;&ON_%>EvYDV4uF0;k%8`H)Zr^O=fPQ>eE;0 z37h@s=#g}a@3BQY`oGlh!~iv&;SvHN7N-i!`ipSzJu+6b634PNBpYVr?veZ;u&>To z9T@M+!o~DC4`+X(gKVk-yQf5(OMH&}jX)-m97XqNcY(JoIy%O)I?F_xmSB6vGwB>>(sF2yft-R%^LKyrl> z?)cS5q63|rANS^)rmCA zK*M^iBURn8NfXOrF(HMGX8Elxjs7uV8o|0YR6UnF$Z^3!QhHmu#PQ3nM~hM^M-qr$?N3*223+?+MK}(cBKHX4brH zwlD|}Q`j`w3YnFjAF_GBG-s5%GwNASG8gIOz-ge%;R zCCVtqH30WLduycBj#vlJ_ak`BxZ=(!k zd8pr8!%b~!1B%{$iSOK5mR_+rleu!H9iR9=bRzornn1RGsYpxags0##G4U$J#t94Y zCE`;UmFa&!zF0^0geq9~i~E5Lzh>6*(V&(oD7;Kh`xW)swPq~aUJyuah4ec(xSN}q zCnf4C^^$D->_$&C25cfFXJ7mVQ^<0U7k;fsT(Z-ADR*IpJfJ7a7~7e!aSFGOV=(%M zdK7rPwShK3*3(PAXkFZ0!Ig3Bj9EFT&)1>iplr!Fs{xd_2M3ZqByOYVYXP>IMALII?S>YcD_%8bGz`BY~xX0}m!OrtHiQPbFFNY5Ebkl+#gXjs+3|f(YnR+>unsz}&_WPkO z*we@0XvUnEr_nq3`j&|Hjbnd*ql83=uM18h?b8|OcVfl9b&mzHFcK#Uk-_5*zLK!6{V=U@A%*_5^VM*5 zm69Ub5lhdaeUYT>B;k`CG2-~`Dpv#7uM|b}tS4Nly*2I?XMr|f9200-I`+~d&z{8M zW|jgZvV2tUSm!JzqFMgN#mx5uIK0EF%iOu#`+m0J9Hbn6AQ=S-FuXK>*kv9A6?A$4!b(`m0bAK1s` z-XN9gD~)~~3Uyzy-3U}}9V&e|I%x_t6l)z6G<+Kbw4%p0kO3KjCA?lSrpY)+RYiiH z`OZOfxn<<1=Y9gUh=uwNNz&`5gRdI*!BNUafg}9^4$IzFT zY-~8@6jaN=7FebxF*e(GzfJ1-vF}fZB{bNkG8jL9);O4v>ph5mN$&MVu*S_>p|1kV zG)B9XrcTpVYo=r;IYu$XJojt)6SkgR5{?cMDKg4tzKzgc`6b#ri}5RtA?0Pl!%{7~ zOd0dJnP_d6??TtUTitTwAOUB1M`9ysJaLoLY#Rf*QrBC*T0{iS-f1BmR2W3!^6Tl zbkVaz^IG@gaLTeZ#jEA>yUnd$C51T>@>l}m1pzTH>RgdvqHvt#CVJkCn$vBuvND(Q zUOpf?ET4=38IG>HF6K)u@8REM8Cqux~_#t^a-&|b9z0m?xU)SXY!fwp?U9;-^zNYjKQ zDtQmgzOu^^Hb~1*0&oKrFEi-slqCObnXFnIOT~^D_+@iW#cb`jt{?OEpGWe7H^W?`} z`Ed}5_IHXH%30P%s#m*ScFH;Qtsayj#;=AenT^*x)J@_%>GT>0@5=K$QNH}6q-kHH zszqq_Rn=9zv3ErNy!p;H7{ZbvF*U@^2FfjUYfk zYzP^0STHggV25$jGN&0j9aV8V@p>JTtHm#+DAmPCv>HXnP_RyGUlnr2OZLR&9e!W^ zdiZNdTiEHQLBG2=mouc5X<+mI3wrmw8;)2Ekk(``W!W&)a}u=BVfYMxvoVscjYnZ< z@V@W@Xr!Uef!&{*ASA)HmHlk7?6$AMGJZHk{(?cmLMWiH4 zeFbDooleJ1^t$B@4A7QkErf8^7_g2jOZ~7pH)`!$l+hq>DWEk!TTSxIE2 ztasHD{LY77EpBA4Nbw@|utk%tr)_@#IAP7oyDY`!tLc`d)a7&>1U`vCR)5v7=-c&! zBOEFWu4Q_v`X4{KKdg$@CVq-%%r!M&US(W5GN_mIdywGoD7~%+9rP~q^4YE#b=~Tk z9eU@2S+wkcw@Xg)#Dtf58)4CfR;)c6-6*S$O;cI(g34V$gQzjjB#>flD`?X|js_CZ z9@#gr&Ql1|gXGn0Nxggfl*@0t1naqtwd0YB?lr>cbIY5Zkd)oP<4^agsxLsk&7MhM zjng#gxN3_;Gt&ac`%?(O zGN%Oi?KJ~r)BL=qw-@Gib`^BFlUEJX-1Co$VuQneJY4hLP$KWr#3f9+l*i`2;XSKF z_m_*c`<4BpTtZIDo`fRrQDg4N>w#7`(GPml&jGu_ZwK*VFSHtsisWxtjrYzk=FBoIRzJVKy7sW%UVF1lgEU8I0sYb5T zFGo*%S_95>0Ozb4V?@v0)U=D$lAIiDcsUuakJ+VgD`^zOH<{qwNcG>C8d4g$~eZYO_@Bq3m;U7 zupEc1Me|C0t6PSUop+NiO%}Jy?ZNDf2sp60F=;7piOHGs^Q_JhJ+jdc<7RAbEOKvZ zj4u=Aj{G`KY#-4cEPjDtzEYZJZac}U?XSP{^0lzr9lZx9u*@V#G<9qE95v1EyC$wf z^#&8&_7x&OIX4OcFQ$oA*PIg*_OE~eAR9tH9Q(fC`zI2E6kg@UrsdwMwzk|kHxDKa ze#KO4cDfAj(WTte61C#>l7I94&}fWHsPb^S2$C|E#LuB4Jh}|Lh>(4oZwcjPV%2k( zZXS+^yaVFJ(u=+Wq<^amG1`^ca@zr>k^2o|`n1*Lm1=j-j+$IOe1|%pVy`-f1O0%P zzx!Q2u#BeE;Fq5p(R_H(=?zl?LYG4?0S)FRdja4*#tq$SgVj)Y-^Y)X-F`qwQ-3il z7hasrxN9&c?a19M%w3mZ#$m$bTzh_bn&FW~y5f;xlXfv-C zmiZ-QYfE6Lq~}i&2QQZ$L*!=$xkb6vw^&MwJ~NX7Efq-V@V6whD@L)$@C$wKYNJ>< zWh$fGx_a=f4?DjQMP{_rn0r0j3AxGth$`a#EV{y4$DRKq%7*M!~2oUQY3Qf&d;_V#nKnZ1aM&Y#fj7x0X5{1>g5c^2hrL0^0pId*=I z7RuOJtod$-6BA|26fTcrnzKZZyqa6|y+Y*rfU5>*DLqx@?gbFrRhm}P>Afhbd*(xL zOtU8Wv1=Z_fFWPrAdr=vrmDx) zxoCC?LOz4oQenZTm%{CG^79k++BA<{d?|~D4maE7^#R=|^G3HtjnC{5Dy)CFAKT+G z#%|HlM&A#VMt`fnSd#`jbU=XAUpOK9dc78(7mSlxG%lLur)b#a3cu3Vb%B2cvln6M zXln~z+g23(+4HgdkbRn$?#{0wTdAmJILPh*lzWpJ(%mI?X<`?3z|LHyPDaA=Q+vQR zLbX*vliEyMRo>7$1E5@w%e^Q6q~0eCn*Q8BZLI#r6t~-yV-h&1?sFQw@dbY@!8ae*coZw-HB7XE)Vc57}yCutGprr1ZS5QXSIv)E2jH+BDg>1^7<~D7{=Y$Cte=d zT|+#}WDek_TDD3p?2Pn$!;j31Gt!1Z5y)LJc!wbb3!f&n8#X$!CI6zpPf+8#7N&D? zOTGU{?Ucx2+n)IC_TqpdxpG}~AIUi~oOcMFYThCK&#<$8Qam2lFX&U5n+gozyZM;& zoAOhyiVM%fyID&NzTf^!Xl+`!VzG%?_z|1l z3rZl%kD7U1h`==ab1g|o(o2-J4i=#)uO(b7Ff)06UVXWkbv#DMh?Q$aKR;-jLYg25 zmR1L*Vb?Y#n_uQ#8_^1j@OqFB*p zQ;p7FR%b5_8Sr`zC7^J-9CW%ZNjrX1bEcmVbVrVl7jPZ+{`>dOjcIxkQPpw9mn5mM zT5qK$<()hLw6Na34Zd7|dXfF|%d6)+cjYZKjo+hs;-nEoyze?TzRaiIu)O!Vvc&bE zsP}+Gg7{YV7U*POK)Uv;#iX&JUY!Nj5`_4(K}hrqeHIm&Cj&ucB_;y(#ViOZ$j|y` zcvX4*=k@YYM}&z28N;b5#Y2o$H~#OV*gzYC<+`9_v%dQE64kIB&@WikW%E-hnSfBd zxdf61Qf=kh$%8)EOnsjYLgkit(-&|J(0(3%{bzwrjDNPEl*bBwSD~B!gNO(%_@o;a zSTz#ZR{z^U)E?Dx$4F$Za>OxJeCN?`h_W}UZI1NNx&_bxZu2&9H>_K-iqzNZL_BG~ z(BYZ)=+5=a!H3@x+`V{@t0|=5Pc$RVUSVW+s%;y?%j0kK#(cFGRV`i+5aP1FjRxiE zFf%YwwqgUF2!POKZAk7A9~bi8E_6Bm!v)KS{gr<_T8)E!f-3+BcNyGS_L!;s&}GmAVWh`dA9#0*?LaEbsTBviF7%bL)!ZQpTJ+ei8Hv4L}D5au@O^ zep;=9ji3)vwYMHXwf=)|;$!9UqUXHe&C#KA%KtrvZoY>;*qKhK>RX zyydiTjp)=*?%J#4jMeZ|7Ui|xyp)A;%0 z;@3eb))>@Epau7!k@Z2TC1G(U4XE9Hb9-&zd9FDkz3CeG3PFK3LjH7ntvfWuKkNC_O6SUhx+xG!^_k+7Zl?3Y>JNBi`?9hh z0$PITem)*fON^RS$J=Qcp$vSF6OYfs4Y1fa6ZqxAnp$_vN%>Q7ZQ>Gt2d&gE=n|0IX?1+3=qp z+lRjk`=$^Rd~|Ow=3p(LOhk)W@SWhO^rUse&GrK5*KtO@svp@z~!g|d#H?7)1O z5&FL;>};x$)BEP%&|VKboffuh8Be+@b*SBHOVc1{7dDn6Dw+ZRg_K+>kSCwZjPLfZ zNccOGFDruU!Mdq+W4T6%{$h&vFBP&4MWYI%^q)_ycZwP??8#wPR;hWmLFPBOgU2*= zxxCkPkqd0IHaB1GCF=eEnLIQ+q*}Moy5uPosC-x3pjfJ_mf8A^PboDc$Y){Ok!%-< zli_)3r+-#C0dxT`nX=bcUjB^L7u7g}S$L(1vo<6AbP(VJNEhxe5O)TvrA;~9Ivqkl z_U-6pvQ(;;^JS<9_t5Z;&$17C$EO3`R9L&NRs3yfvK{e2W2xlfqGsZ9=*?N%)QtHU zSUmvfqsnj%1qRv>UyCO1%`@iYhFsDGUmu-PR91E0Y>jqwpiZH6Dgl!^W8pxdv0H!H zyTLDH12>+!fnRfMkMA~A4|V;s{{WupxtO~f-qWztltgsR71)Rrr~&iB2}Q$DmX(W* z2_sypHLHGQxdW}whQ3`vKFodgfDZE}`=y?vj+wqvI>wp$0p{#O1+#~r*~pm5I@RJI z?6)H2!92+%%x@0$3AM3(HEZ*tABJ5tlINu9b8e8aL65p=<_>GG(1Q15O91^~I|mN5 zS@oc+15V7Aj@et9X?V7mkzw=i7KC@z?85%Y(mm_7IBn%RKW0>2@rRUxJ2v2VLkLg z@`*reU;l3x&Hoti`3D)D*2{-pzFrw?S=0&6MGt9df|TS8;VusS^?K|GXxMHXAqdF-t8$#w}p3*v2SsRSQ@N{jk&}q$J@b2_Ut5qrY z+XmJ%)DMy+0xj7OU#A22TwUyKcJ`8bEnA5!D0xjKmz*Z3*%X!7*BK8=6!EqXF=l8B z{Lq=xj+)ucxp^OnSGOJ4DuH4G==i~%VQ9Iz66NEaQl>)KrYC&Z925N!AZ8u;%f3V3 zeiOjOlnay+dTDX3B4U16iDx%eIiPaen^;YEIrsosfiuXbGNYk!ilQxoSj06-_j2yD zko!sq5>vCW;vF{=rMPL4L6aw6e6Hl|+){_QE zgR2sGpjoIN0r#+<>&I@JKsy3N)tC(_?=lJ!KY3u`kgt63>oX@GFHD~PC4w&w@~dnz zk0VyVUW22pv?}VZZE_wI+Sk}Rp>wNcb!pr>z=|gL4KC;gQeCqWM4(3Wm{@yfx8FK) zur<9mUztxt%*1g)kS8FvSNDJ#Z18T9jr4$kIWYsXeP{EL7GF<^RbSuU-q%s-yBF4k z@Btp~HC(s&_V)J%ZY5q}Zi3)(dVx{_ba<2&o|Jbtx4IrA ziYXPx!va%|iTr=fWb0i4TZ{Mq z$e%x7J;U8JQYvhTDCC57{%7L-wQv0 zzl!ma*9g7%i|)cyfRMfT=J6qBvw8WXq!V|gz`DEM3f{vW5Er%d;rMqbp@6x8ZO)Hu z$`-k&GP2{^{RzgLohYi#oc2hU>I)v#Ac#8y1_A4T^ZCuv4X@T$g)m#;;Z)~W!1V0U z;`)nWS>&JG%ZxyDSS*f!MN>Q&{Q5nH$3josvNr26*9Hs>>)|0vBFQ%Ly}&hRosiC* zn|4ksDF*NiTUUFYLr}E)jUQG6JCA08;H`>d_95FZ;o3*0z;W|mALVtld>m_uJ?{VW zvIdIK!<=8G4px!pvtd%cBkJ=`Cj<3%26%;hNO#0ogGne|*Qzc#0OJ9aRl-%9OU_oJKMyYxe_6KLF17CnmlZX zHWa7%FWtqPrr4#V)IL|N-*?j!q_8~o_lhlwi3^z50JAomU7h{jx$JrrHR_iMYwTS$ z9CT7!k}4XiI4Ww>$Jx}nOdn{-H8c~jAx?Zi)Ez}b#mR1%O=6&V#y#t|pMWmA26pa^ zwI+!IQ!8tkuMleFG9K~53JJw(lBAMV-g)w{(x_=2 z?lQ|BwZ*L=vpS?JUMc>GF55bygC}8ZQrF63+v02pA`VF!1itadOX5!PR#SYCk*vzc z&`xT0KGyeFc!2Gzcas8p=75z+l>c*q?Rd}mNvmqmlnN2A!sdoV4HtLIXuya=R%ox! z!rzO3!Uy{Q(BBo8OtxKz_9^ASa!rBK5zwSq%784FTXdUVnro^5xO?R^Bvz45lRc!H z=%QAjg#0}Rr(+AkF@L74F0+o;+{{Y}0b`DoFb+OFW=1MW?FvE*YD9*-s z>~s@t*o0s>x}stx~6RPtGKc>Eh*RHyJaEBDB(q~W3QY^f1-BIo~5jNh(W z*lZC^13u`yC;4o>y}!QCDeBQgn0&xv&i&iuu;BbR{Lgo<<`xOAvxHI1$fR{9^ z=%FhBH>~@tyqygBI|nM~NWdwr4Ix+D`Q>(#rKKqX9ZzP{UXQK#MzHiW^KLBHS-m|j zB6biN!d)ga;OkkzKtagLzWgY3Z@k{D(Ydl;V}l_{4-bzHD=AW;V3OwM24GH*3{czX z^#B#~9ncx9K!x0?55zm5Km_c1smCZm^{Qz-xmd8b)MEqMM{hE{?J|Kq6+bn2y|7e!N{VC!Two zoUE>OpKzq4nBVxn;zqlS64^4?j(a^a zDTHqCBkEGkcZYQ~p4#}eiD&>NrWFW3%ZMbm|EE_@Ly?m2J+`zs?rRYFO z)1h(ww74-nm7SW;tqfk_f>Y9GFv3k&_cx)5(*Kh!ErQD6hwq%Kw{@8(&?K6Vm&vjy z!&MJ0S;)_7+cbKqc$1Dyo!OY@8+~K-6|=JkxWUr?QiBz{vWHa0NkAUk9r-IDE*b$L zZ+?0PaPN>Lc7C*Dnt*^P3$qo0Y+qV8Q<;eh0zrhFt&gaR0ZFD6TzSc%j%&2Gz zR1X8&5nDdJ54!2IU~`_VQaFDyh05G#*PnPo=VliP{b>ID!4B=ABB(OkkJk`Jr*bu* zAN|Y|Q}O)Qq_of5fS;VEgW|NAnX>XT&)JA$Owv|dl7!C1aEqJGM;^ybBAQ=+#(l#$ zO!^X~i(jISuWBpH{)!I!O;FSmrKJ7f)^@3IPbH`ARM>Q|w3~WIhER*8Ov6aZ?~&uU zLHqQP@L#Zd32ZE!!%ZEJPkP0!d+y-?nQ1V1WoKz5-0<4JSV|HPF5DHMzCG@##|7D) zv&L>+h$uazVSK+gJb6kHJh-|D5~{1Jach23y}H$6)t5Z?va<{`Kpwz}O7#!yue>Q3 zhk+k5`Rnjk4BmE3l$=dV=4p5b^6&}Cj{~bU?4sIT>C^S2)x+RVfyJ2b9flR zm9Q5gB4T!9cj3zGLjp9?ExuK^0b+Vld@W;PK8LgW#Io}cRXea{%lf=b!Uxe?OPAKu zk@Wo*RZ$-NFZopRyuWSl&nhh=`oX)CJrmjng^)G@!Ee)&)mp7bVQ@u*M9Cv0 zEN30^G^gmUSFomL$D@2jQ$_U*e`fm_^ryX*O$rCpmngtnA^R7iKI#4n-aS(D9|fj_ zPz2&x#osExr30V)S~vtzU!1v+C)5J?a>lv?2G!SBqTt!tY8Qag({G9pMuJeT@7KuC3*8 z_uXSn%$rHu8VQ+Rs){8}v61mg9pj>L{9=|SQkVoL8N4c)?HkzKV+i45>X#ljHE`;M ze-)O>U7FJiEnL3VL+H;5KC192cvhEhOq-T!JGK*7;M!!3TNHsT%s=)G@>zW%UWhm7Fs8BSfc6{-V&Z@2P=cUOBE^YqK5 z%T1(5dD*?24_Z486-&*>7vZJr_JBC%IvEcA1Y6>?WSD3*4wi0+VAZw=Ng;(f`+2+F*4)cg0(?j&{FEA zf?|Z^miN*(CQlGU@uUpQS55mz1yu5{GehxMZwXnzFPl!>KTlcaGT4!A>(F+R70jdSsTC z^$I?aMo1v!BjXDgn|34n-40pi2LnR5RdVr%%C*+6jGCJ7S#_xG44J@9 zR2To9;?d{ue|^?zbK2M2*o3YVEQ)a_S0fDWI60B`?>JFaP`gi@SLFp?h{fUcJz5C( z>uO)rpN{B|k$?~67@qyezZ5p+qs9BkH^@Z}O&0A$3dYoJ;k(1y4M|_Xo3^+>zlaYC zoKJ zg{1q^F26L*&Lp(aPAKCVO4%!vr^jG0&@{Ee@mX&O>*XCt$Q4q6^?+~0Owcrv-r9=i z0%cD@d#@Cg|1RIQvojr?I$N|w&U8-NaGC~pW_Jjeu+W0^8p-KjrOmnzT{Z6H+Bi{$ zv~sjAE4I2sW?kFU;`m=`5ZQpfzU1y0VbcgC{j*rsfBtPs!X1Wsa&}@oB+@b-n7|s* zn(!~cA6+0G^+zI7s8Xil^wahOOsvi=jIKWAAT6ekj90MV{V7y?e9_@?Cp;7pQOg;D z9_P$YAb5}?)1tG0-F!oZuW1DWy2Bz2O^Nk7v>7K)eX;lW?~ zu9&PSn&ps%+DQC1Q}Wyi6XW>E>|rY-Kbppl%y>wV*Gc;Sj_w}3XDbtqW32vCdqQ$m zni2r77}~ze%3FJg9AzYRq)kv+aA#LL+0Z0l!69`Kd&*=UVB01{^Ng##*nPKfMKx4; zihB^Q%Fm>s7K`-!tW--qyDZ;u{i)`~!^@JErpF4Y!`&4agp?TLO$g~zW0>g6x?0$D zFvqM~?CZLl+a{M(SZ4kJ8}nPi6bn1Ees;G5EW6?_JI{|gy!Ycm$>monnDPW`&e|3; zGAX-SGgmxtFx7nm2E>pB2OXr#3daZK^J=t;z2YpM&LY6US$H$|JJ`$!YYogJywH zLp$`{ktJ^1)w5B_yWi>t&ZHPhQWv45l>j<+s%1N%cTyW>WxZ+*q2xcs!^z3d(5?G= z#Es?Y86Wug^UpI~(Zp4|onjArM9p}u$qf>nwoGgGvYbt^QC<{2+`ZYBtYlVQC{QyF z+w+%e44_D7R6RZ;0M$xedQ6zd_!X-#swYx9r_l5Bvt3n9Qeba9gX7Cl?FI#2@0DN4 z&G&KW{wzt8WnmoQvhk*wWroUkoyH6W4EpOl0mw|4i&;;4#+_QfG~s#IvlEAy1(m7X zs#`_PGu05c3>4+~Su`)L_VbiT&_*Wcef2l|U`9@@MlN}%e0^!tD~u zs4KZMa=G2#e7<}L1W>ep?*y8%_9Tr-c>QmFgkR7QiBM75^z3xEw_AlEUJJTEYt&;) zQiWeaASPNcwTxNG{8R!wvyY#R|6IM6eolzh`8UMY++nLQ2!TB7PrjXLC*FgaK~_Ft zrdRKv_1|4s(f%(_;gXTouJmQ|atNxH&m zWbtDg#CFv-iuj}ww+R;#rCBEi^{J-&$O>Ld{^$C zo1hkBM0-pg}+qDUwMa-vyiBXV44nj*_) z-y?e3)UHA10W$-x#nPhKzSlr za&4AGHtmQa?njB>n8qKvQ{`2QMO(h2ojm+xbP=|U``@`@zki^8`7rTa>8rRE&kRO} z?e1^;Iu6@U!NTw`9R?PV{kmr{#7B0Dj!4cUJKK7GTf=R8aLSDw&IyMQywz!SvMk+c zJcgmZ!zcz2>$~O$JCLTWmXBHU0AHkj2y?AY*I)h5R_8TTL!Ak-CtTp%d_+o7N_qd! zD_k}uAJyk{Kd|D!9n?b!+O#xU<>Xzq-B-bcVX(Pk&541`j^y3|A3?Kh4+VFFjapCW z;C#J?Y-YYrk(s)B9P3A=@Dq}Xjn2bWgI{d+CwESPUTDS3#I)|%&9}qmtZZNQ{26ia zdp#VbmfCw9SF=K*N9-GgzH(ML#9cYA^4zyH3YO5z%;%}6>$KT>$lF`E`u)+0BW82J z9qTDg`859WIDw6?B<065iO|X@KJHa+Echvx4H!Ck*La>2oGl?9>kTFyNiT~iG4%RV z<%djs-BHff@~p&1b94*!S*7YzYSjI+_f#oAjxbDUSRO~|cz zP@U^#v6*J8lpU@AlnZoH`nBJCe>xQ7mg^6aH{kT$6k|;V>08ANj3bq}#g?`z;Y6>+ za-;3Sx#kp=-X2YUit(R+Pm?oDOLa-JLmCTrSaHLD=qeFAv(?ktxj1#~}RqWVSKV}VmO z{se4=71HJPor~PorWdf8v##IBVAG!0%JGEsV|evnGa$BHIEFr7%nWYW{;lj~nB9yi zO{*^y6^n-IvM}l5k@27XOm>2hDAj0RdH6K8bqGl(^U<%z-FJ?l6M7w({hep91X)^6 z0Hm}}Ga@(y_lD6W*R3Z#DdBzfd7&{hc$0<^_zF3&OQ#I-H)U_RHq+Yc<0{?;Wl`Eg zLj(FCI)2TRBFq}CgdFJ1F|Yl4vloeT*GA9msE^6|2CB2S1h}|ewV7Aoew_1(B$7wm zj6y1MlK`po%C&80l(4?zv7-aay|JMjfiiTx?LBpQYNIZY(~A@=hA_`e)vm43Sg-s^ zTbcR<`C~)W7?;s|&reN;IKz6(Ly=Z9;6CgVroyD40E7>1GNNVg@-tbR-P_f?*;2gQ zULs4SQlp&y1z~6^Sc78tyxvvR%x9e~lDxp+)(H3}Y%u&}g$%3ki}caucIeR5Clh;6 z%+9b|X8qPfm8lfd%fa)AvmsWcCUgtfoTL!sX?x`@L#a|nc2G!=D=RFhSTIQZREH6?~jq=OYt&XhplnMWf z;xR3ghtzsIoyV?EKMjwL=|67dR)b-K%G~A$m)+fkSg_K_wa~eo@4avuA>-49&o&F0HWfajm_=)sRnKHPVA@{P^L{q>h zp~md2^3oq2zsAul-yqfLFH0P>Mm5m?xXEp^tWqV9mUii1hvF`c5<&d^!2@g*b`_!x zg{D8AcD6hRHi7HJ_Jo^ySbEIr{R}zOlw#;DK#^UbN*tnO@0BPyuP|kt^E(N57PVm~ z*B>>v7^Rgnt_Ym2F8eEw=BCN;2=sYqC9ZBWygbvqvSy|Zc|um!5-<9)xtx;5D_Al% z3AAyRgjyaRlyYBw$w$+62}Gv9<$=nop1VRyAyp;i^V<#!`n$lT+%`>)sMDU?)$^= zuw{04uHz)yq@?|QZeG9X?tcKXLf@Ut*KpLGw_Kk+E?y%?V(5%Nr0lZwYgcsY@1D&c zmjaIMt`pLpuy>BZ&%iP5ik6{6=RkHCs?F2nCB({rfTEmfIgjG1V67|}!yqx(@JUxE zDvkrxLx)`H^vyYGjF6S-A(`=MAABWCQh+V*vS}f4E&w0a_W`#w`Y4&5ny`GwtMTdr zFV?H1?o~`YP-GZ#<0A+wf4`94H{qQ$%-rj3R?K@8;HRhKTg2?!aA?r+lFPJkS!u#6 zZsj@OZE_H#w^^RA929e$&pR@;X33`K@3Ba3cgN`KBhh(daY04ZV{)AQIO3<4=K-xU2dcn#;*WsyA0GEARO#G9r+>m48e z=0&5D%dAUv-cXAOgqD44-9rXXD$AO7j7LfFrZJ;vzMpWlTspt82kJSFN}F9Lp3+%P z?j!hGkXMAI#E%1}oNY&^Y`VUS+$O!|HWSfOYOj{r(Wi4g$J2BXw_@M5AqBti;p=M{ z?4U7b^%^~!D%+1BKo9~^wg1C-@O;7}a<5apn3B~%)^$y*rI4uwrzs)vx{`mzm0-tM z-3(U=lfzwV$e)#X%+%(6?+;}U8*cQpNQWck#-aonUIuq{ecI$1roLp$d4+0(&buF&-`H(_glcNoPrYVt?dQ+>(=f)arED+ zK>VweG`eN&;$*|PR7^)}r{K6J%tE4dt@@!rBG}ucMhX zQy_b0mW?Y{p}D=kI=;f+UDZa90H|&rE%3Fp(89dXv+QC13nUWjh{EwTQOaJuoi~_5 z!goA9u`c-;#Nk9*=az%< zd>C-~YdbH}%CirHS6Z8v95WwG&~9ki$EiFS|1)*^cdsCu#J zsI%d44jNtv{`>tr*`K(%uJ3JWzbKOm+VYwU=FumbLB$vEmA)pY6Dx1dFDz z`%KHt8Rz8lVKtGg6gmX^ZqGr7B6`5~5j z2LpIZp&_PT8Mc@h5u+G@-t%&5HFAu1zk$1A1^9Iq{#y}5qVROaVQ@9c__TI72FN16 zYD_1Gj?S8CAvHswZD&dh5o2K4OAi7l-BnG)SMa*A#}`^O(^NYG0d7U}RaLi++B-e2{L9QxRjpaW4l zaA-KfD_IuO4JgzmAur{d{fyThKG7l%2ZAudVwbYSpu)yOWlnK2yf3zD$z22aAsYCB?@5Z z-cWa2C?@|53ef_w@<$%JF(#d+Q96&fq;#FAbnmk}t{zSd_4Lmg47ssf^y|UOMKk83 z{)Pk!ScGY$cTT(0_fo-c7Xo32)PaHTl(6xCs!1tfiLP{g<6bV};ak*jZ*-Y~wzwA! z33&jZpJSl7f0sDFC@}WQrzC5;U9s(<4rp&AZ*kSvfbBlLd$J1y%C=PK1y6MVgUj0r zAdy|)rO;)w`<(|}H8v+f*v!mhe0xRA-D3Rc{Y6^Qxb5+N^H2dV#;Vd#lgG%GQ-jSIZ*{8v4y09$rtIS~z>N1>doy5VxD!r#H zvvc^ebYI?*>MKR63N&2wo%e_8lqw%o>iyb{)M(=^SZ}pG6E!OhYSJ^n~Q{V zrIC@ix62E*5o9H1ZuKeudKK&KCk$4nv(@tkTFipmF)w7K2U_uNOOUh zrSwe0x&}3JN79(=EX8dgxdQk^7gk;U~q=@qVJ~bG_kTUEYR^^JIVe6w^=B4cvWgYi2-Ycu$aGsxgWsm;ftw2s>Pc)N^;yS>25yWRj@ z@{>f@-#~Sx>T&VaYfL~!-}MLdX1_zkhf&uUO%}!2bV|G5$2_GR*T`#%K*Vy-`~l-% z`@kr!V(i>7>b!E}(5hN7auK#9@cQ?+Zw2c(s)0y-Uficx*}s4on~=r{ayg3*I(_OF z1K8lMty9`bqZ{xe7Y3cA4f;QH)5>Ygzwp^YPOfj$&gdfLXSmQs0~jfkr(a+fvTH!r zei}v?)6{5jUiydJrPlS+c^K2uFdbHVgRh%jiwaG>D?@-$P|xk9mq<8=_@PQiKY8D6 zCCU;aWgo@9&f<}nkVWug4ExA=zE4BQsO*sza+vZUfnW;_-$ z+`F-?Lq!tVn|4lHSt**B>{EwG5kn}BM&N**h*kMU4dXz@I~n{_!EPcKeJX?-8uvnB znWweCidfeFhVnF`HXiOIb5f4040V?i9d{PvXjJD}+RA>6J2b2O(9B34gx^x`JWvt# zyyP)U`>cWAPBWp1?||ggR)sYlqT-@tJlEPtF$=93(_xWRKXsDrX!L8C!Hd^U6i|-N z)%pRhZnHIa?p;t7Eh9M9q2~gD>&+)zI4Xp?E6qWRMkk3551$ zN;v*gu`{7r<5`38P<*6v`2?Dp|7;{pT8rlbC&tP)T7IPI78_QR7Kir}z)ZP$w}MVj z+lB|=r<0;gFZZH}&B~N(`cfEd2HJ{NRz``|6=-g^Uq;1o*Vl%@I7DeUMP-nY5Zh@8tr5I*ZqFWM}sDV_f?~EQ(Jel!o zND19-_%~a~a?sLg4)XrI{Mp{RNZEaJtVG&K$L0g9M*PBHsw3Qaz47r-*UQpS=S!Kf zXndPT*>P7#>xz?y%}R@9hFH0Svae_c5WDh`Qab3Ys?85%%<=FE2vs zI8$&7(7qt-6`=77Ee)qvbapf*&R-=B<(}G>_*~sg_7y*kYbT!tChf~BeTEo#flJp>_hRjYgIQ!utuY2mjDbIz>na0 zQxWRr-K9oKkHIG``B)xq+lt3c07sDS*jJ!Wm8EPUVRsh-k+PJk7eKfhK2Dv+5&PtnJ_I0C{utDq}4gUk=Q}fJ0 za>c^ZSAL=T#bwq`XKAtCz#GzFLEVTpX8VMDy z$DN}3m6K(frbn{z67~#K^h#F;0f?(JK>oX{T8vxY0V~tp)_Cz=4qyYkSxhprX9S5C zdSbC^W44T?%q#Em4u8cnLnbDTa^ijXVG~PN9*yqqN9XIYP(qY>9i`&$=k$>u#AfPW zg~fBbl;%sXoV2*~L3f{@_#B$rFz7-PbCcp@1*A!R8mgOZ_1??#*cAMV)6RF7Q?FK? zT6V+@nJcXtW2kaa-aTI82wAjT&UlQidoOJ^=-jzQSy42_naZne{|5jhVVET&oflTk zSJk+kjMavTiKr;H_}G%j;1wY6rEAwIDvZyKPd@YYc!LU zHBgX;2I=`oYUX`jz^AkOJ%^;0jMN@`V#E2uL}xhcRnv10@?%mK#uFBnCfzz`v1L2E zwOu#YqS<;=Q6rhK_;2M=V|@A%bXO)Fj;Z!nEXt^Y<&zbO=l2&<`wo78KEVa?I2MO< zZV9>LlZ{PfA*>W@6nf0e!Wy5o94nY;O+~ zteWgIzEV0NC<1v!|HQYe_BmGVyC3Ga$$M%{G_R*EYqE0&?r!IyaFvHuAJFtkB2t6% zZUkauL9t>3vm4MiA*9s)5&*RJd7+dT51-7I#QB4E`f^*)v;+7rlpcNny}aW4%OC@( z-}#w6Ze(0hF9)caBJ}t_3v{E*eC4QzsyiNdkJ4LLZv((xeBHx#D6t<8%PtL zrt8^UHUBQ{qF=|DrPXg3Grh7oon9dqB0HvXHiJ1h2ntQk+?BbvS&u7P2Hecn`SfGMv|)+vQhfRjb}sekS| zRlh?^W5vdLc(5Pk)HOZ*CA3_#?iM=Rmhy?M#_ozFg`xoQK+~dU1+ybAwcy$nF}X+JJfVa$ zegn@_$abLot}!{D>J_Y;Ie0Q$ftV%#IrQY8uVF~ ztK;w6RE$e|5rK5aH6!44eyeO*d^~(t@%gVTom9LcF0P1m$^J6X3k1Y6;h!#wCL2P@ zgpwz0(igw)7$T4w!PGMGPm0bqzT-X0FCC_EilzhzNa^h?O;4OJR*`^ z9>!*T)~mNAwIDUqa?SXV-JHg%N)zKH@Y(U7 z4VWO||ASAkin28_X=CXeCwFzafY619_biT}a}q!OL8m!+@ib z=*uZD3~Ic2L>2;#m;{Jt&V?hBsxj;LlnSp_$>1T7Q&EQcvs(01uvt#a;Nj6t(eN+c zbN-;w=EXV}GL(=$dNJHy%ALVmlG5=|?mN?8QOIcbnHS|HwV6q^i4atxLHe%#_o5EcSzj}Z1ArvR0MgT zR%r2Xf3A1&Rve&5*;gKp3JQajDza1L*9Tug*LkR3zE`?@i%!A+dqtWS?Kac?*w%83 ztLRvzl%vT{w=j=FKqSIBHCyX196s?qZ1Kv_ZlM!m{BacVu(OBUx>iO?UK(_y+cpi6 z^=Aaw>Y9*fG$7v)JDcnGMS4{gFAMS4I(@qiI5`pN+T$J>ypA7u>WUIYDnf=&QBk!; z9}?6{_5t)`jiYYMF3OStD5iYbV?jkZm!lOV5+a?QK6to-6fJFD+*(!IUf|;DFtaX2 zr^_F+qJX_f4}m5#2}~6Py)?Pwnk`+Pt(`lJ8P7WwF=cj9-x#-7H_Gsn`$2d#AR`6cmdRku_AJ@bx)aT8XG_fx1uK=08 zk_Jv&FCZ%}PLDub^Mv#xcr`WX+S*|cfcEs_MZUna$wMg_xx0nL&S2P^l%qJn)w$zpGlXg^ z4Q;kPUQFNL8&>myFM6kjRG1|th%d^nu3>GC=Qj(jI)U3r33Nx%PItXdLS%rke`#l2 z#5@Q&ZyWWwG`h=rj<`l;4hd!Rr+^|mB&5t`>~@am+eQJ2mD(ScI0P9eUv2wlJ>9Nm zhCX<8`Q(d;@(g#EUcx4yp|{=V&-MTT8WAdSp$%B8@uuO56nAse`V3WR#x~x%;?dA$ zCH+gN|Kl0koNIFv)=^Ui8HN7GrV;gUyt3eCos}#2%?$$ZiBJ0dD8+XQ+nI2u&yd)W z<@#6fSnYBvHYSkQt--%B(Awn7W1~W}Uos5J(TK^JpbE&lhj!T+S%fTkg%=$oX|?6t za~CE*Es3C3)*lZT8>vsnhUySJjf@%|3V3dJ4{Zwe!r%=lZpxdK8b$2~Ir zjk|q;?i?ZG0qr*|v!mjTuNZk2(t=LMgE0#Y+zd^6ZY|)U_O&&wmHHA!Dc%H;vTUp5 zH;>OZx!Dq@e8p=!0leFjO|6HTA`>EG32$Y<+4!2m$W@MK(=ILW5p3I1y-*L%wO{4Pbt3E<7-`=dS#cMcOf7vIcdv}soaGAQUfFR04s;9>6+(W zxpfYxn`{LE(+=A4xc9w~*!TrnwGV?;t6AB*qoOA8z?bhJZ=Cm|V3VyHDvyawo@kpX9Zb~T5&6;> zm>6d-UIA*N<8so1!R$$K@nBYTXZJsb*s4M^kn6;_k6PQDiZS8;NwUP`IA{%ne7*P= ziXzs>3NAb=N(i~AT|SAgzg5XMv^QCd-2M6`S~$B;T54vSbPu|JAn8qRgk?_w67f7O9FY8gZ9ziY+3m)g+_ zkHyp>*c#VWOQ2z7>~>Cl8GI9Qc${Qa6Jn@>LTy59NpR4kb1nb4$W3^}EJVAh(gWHA z#Ngwpe=6U-x;7tlds19>UEf^@MH%06LFwc*ErlGDonl`|QMebf^PdzR*h2U(yL(d? zR>J8W9VL;FXa?TRR{vzw2nVC=>zA@;JlUSN0J(v)nR3F=6*xVw>M)mbDB#m<9ZvGE z2=Iy?nK6KZpGQV#cUE}Q>8;A?Rmks57UKE6T9S2PP1RrMH6*YG9ofP|yy@E7>8w-Y zW3}WJoB@SZJzh!??ZAw-maS#1>|z2Dn>m5`J%YEiSJ8=f%8`^Oco-5FXDclYFIzbI za`;xMdCs{Pq@~#g161E@^VP6hhB$xP+kp$q-%Up^yPObwkpz_!xnLQ27p`rZPbY^t znpTyPho2JELAG6>$NA^!%(R~IF+?>h}dzV#3v$?eNOItzQCBuW-m7H4gfl|~& z;;mZ3rUjB8aWL9Bv+~fYQi}qVj!PO{BGdUzGg{n%q{~3j+)p<{80R{q_IKOo>yAj$ z>qS#c4GqtmU`vIHCfe6lQ4>AO0MT;DpJgbeZKYKY0l@q+ zVM}{@>Y!3i%W~&lDH3sRbRid45R$R{aew^r*1?$G+kNE#3A}bUjG5|xrbG~T{k)JY z$p@OQz^$h*_j#YkoH*-RcA1Umm#Oj%%+)&Bc(0JR{%%^wl-Q|-1l5uLoObY zXElkyT~pyGpQCh_+_!(S46O&>iXPm(^<}E&;luSbJnkjsak@)3+ul0ZZllUYjK*;G z$!k}&?^UHyDa|drGkhTWEFV6!I7TTl^dr-cU4M16R`TnOf#Thhs|!%mP*T2Hlb`uf zT4H*j&%JW+AvP8GKo1;V*FvmS#{Sr9TOKGuof8~uv%(3H9CkXdWMs(RlQSi`3)?q{ zEBmf@TMz(HsAkmYmvZB(NBWq)O7=I{jFM_RM~DL*_^rA=8QKo9*unon3tGVR?w z*5tuzbEaTR#GpE!c${@>x`x_Nuowyq8N>_=%lnG2mm>KL4jX zCM%%ZSuDcq@$H3l&QsJUquvDnk}@&vs<19(E$WmCw1^|fz6gTgDX!1rfp zfLXA17*JvU{zJxkDS*lqbSZeo_vGdA@$ss}?OKg8_44-{3LV?<$;+!G4ar1qX7}AU zh~ia=Qu-kE>!;=kYr4xQQ72sLetP^>>Kx*KLvQTP)|NT?Rgy{(l?T02ay{ggJ9BtN zFVRsQAhpNI zHwhJs8!C6EHb7MB3h(5-qdcx8FtS(RavxmbJtbaG*a z1rF1ki-M|<@*e|b`;NKXvY#t!4`eldpI#JIVSo1woNjB7?&_VSI3jC(kN5;v{ii;Q z&)^50Mk>!V73OA~jyggqMYtTzJhfD-t8JBp_O$X9z-ypmX_1a^6-VIB_0^iKB`hk9 zgrsM&3J$X?5sP34iEn0z%s&*f_U+mO4f5u?fMTn;|JNJbUml!CHSZ1F9hFD3=a}@C ztx5~3pMh%}+KfLbf4)f~ikzFS1k=ZFP)#Darqr$)x_By#{n3A3Bq?M(O06Dw5sSoT zr!(wqH6%tQt9z+DDY?5hGbleklok+TRaV zH#`OjR6-kRPetXl?7Ge;pS;l7XE*&WTD6H^JSuQ~`=QX6Y&+t3TO5zr-eVCe;e04j z{5oO(=zZ+t`JS;^U9{T+h2P^dvqk130t5HP`b&=1*6PL2D)Mvho!a*)<5ZsNvn`64 zb0EnU1c1AnUICCqZHO&4coDI9-PS#j&^_friThTcIprS^)ZaNb>{)yU3=+q1$V;9X zxP3tyYLiPleWIX0*40V_s^XugufAsLW#<(-rO=t?I9Oi!s6Y-S8Zaj3pFk{q)cz=S zPuoz{x?0V1smI1bLzl!ZiH*F-ZPGrg{LuZ}IfA&$&WhmhFO=0m87z&KuHD7)lethW zNw4<;WAQ8h7mN6i@Q0nb*(%%{Db|=Hr?cPcX>k}e##o;fEO#M4*NOhwy=dad6d&A^dQwn2XI*k z{k|BbX0{4v(w{_gTS!B#V-K}zCo1M*b0+0HTWlR37e4iK0}AahXW>cDHshM(wZ;uVgA$hfW#BS?IgalAf8s^GtjTis;hdgm+Ro{`7PPp zhG}z2OOpbYEZ@1f9T)d=sapF6kf^S5r#)6J71dI?}`_A~rTrBmNR$B40!+-XqIa zPIDKL`1dS17PX0szm=14l|r_y$NMeppkT%F>43dVlC7?cBy^KO>8H~5hIwW+K20ko z#^|jF9o>0!8m92PYG*y-0GlPjMt_)fxb0Xmge|J|1`31CbNTWkQ-~L#SbG$H>&@Iy z*)Y*_bv8gtqk6SHbVSN4i59o^Yu)zIME ziT#q0#Wrxg(I)Ki$AANdss-^Aw5H-;b*O-4P?6eDxze>=l^Nc>mSW>H*|~|Ao7?Yu zgGzD)%rdB3^ug`N%bs#Iu2Iy-7|=WpYk4LTHkv5W&@nkv*JotGX$~@nAiIOZ+5DEj z!{_g-MK8|#>R;AGi!_mldL4EvJ+**z1e)}}a3~H_E&0sG#%ZO2)x2{D0nJLFX6k#R z87plNzhK+H`g5guVnbY3`hp){6Y+Hf?f1$%O9+!~1Pe@Pp?3yFfxo?cd zwIiR|_JbV0J41+ilNYRd|Gb2ol<~KFN!1n3j2~q-B(i6!0#MDgfxD=9g!uwB7aaU9 znfBZUO2{8S;G>bp4o4LAi9+a z`0(5w1IR>YxHUBHFvhjG|L0f0OX1>6Lg|z!4dMn+vg+7@%N@y8P!s2}t&!&E|LS90 zx#|}9mV0F{BQ~Y+oAnyvAKzKP_H<88x*5#~;NoI|0%);=)zfU#l*xQL;5XS zlWiJ($^+K1jDQYV(K!=Q&1rD(@5S3OIUcfyqtPl9sC}FH!WA`&I=D*_({)2>gb}8P zqA|_KAPfc6rHuur=WIfj6go8tYx}`hdUCH|5JFu1GTHwz(CLmm=z}*(H|GE5lBr|a}?)w zbdOG#sEE8L)FmlswBq=3(}T&r9HpT9mJr>{aVHc|jCj z5^1wCS|<;}Hpu_D7X;-_(?5t<$^SSXpu{O;P3~@umCGg#sJgK_Kys$yz$?9ld+*Fy z>%q16Ga)ZOX~&q!V8wWlj=xT7U*H@d0TPTXoPLa|TE^lr>gR~H;lP)RdHfr6W^l~6 zB|T7H^^?2E2k||4Yy>a2vNb5JN>qt5&wF`pU*!5@(^yX>SMtk`m1`S z;vF0)kqbZ)eOh-qu|JIc7{g_IdLlgm9CP9_b4ijcHa>bGdh!)?M)NU#p0Z0M(O~gr zH{nw;H4QclfW&<76|cJ!I}Rw7Tk`ZM*{+5^nK=jAd#r_$FF?gM>8$qyTmjcMxTq5M zb?|$(^r)kn>LF(&gx=tXq~hQl<+GWf=NDu3r0VeIH?YJfx~O02*?F(R1AnJ3vwi30 z`pU|I8Pl;4y5{L}xqrHeMSary_RXt%DC9jvhOOhKOmd9l<6;!;eQ%(%C-SzV)+2oq z$5?QQiCR&KPJae3UKlVTks9?;^o_2!o41$9EE(Q1S6}X?y~SwV?4|SPyTl5Z2UG#Kh3%F-d6{WB{28M@XKidj zImVWjp2%?%z{A$u_{LxP49r)HDMx1|(PK;7UH!{|27P6Xxg;g}=+Rb5ZDE~b3OBc& z!%p*g!EEvP00j!ka~kCbU}QKZhHZmB=6EhX9t;(O2a!JCdhx))y8N9hZsk7;DMu3& zCx2H2dfjo@IjByd5LMUGDC#?|b;Q!Mk2$H0WqRrGTQLIK*ECcI(1bfx@w4J=uN;Y^ zAAXsHP4XGeg*Huk04(aIHfz{7Z|6pFhDQFYdk5~<_VF@njSoN_QNi3-{DT?O0v>%e zHo|*tv`;?h)*F=H#|CbGl8CXuD>X|J%I(jNK z;77B{DwwzGcCUEeS`4y#$SYTy*&8BZC6(a^RFU_w0lOlHY|8M#L6OtSaQn33=ju zULsnON&)RxUo?DbX0#9R17QToId%s*mjZ2Gx=<6GmYpBPkqFa1RjK1Teh&n5UkC--yvbWv*B3gV% zi4pyV_VWAET-qj5?!YK^rHKgMx!r7#j zFF5a19TJ_64*xN_y&NJMlao!}yC!)flER$tOHJHVF#deX_H zmqoo8`Y|t#2_VU%BAtAy+C+9#L!^16q-b2D6_qmlPWQQr-33}ohs=DjKmz4Sa&sQpCF~|+|-0hGvS~r$H)9{-tMmeP5 z@oBmBh2r$O@fVdG%?q0C9}-6}$M@S|eS@Hksp|RiTRL{+Nsqr#zOG!Gf}x)xi$2dr{^hTI}XLM8%=|1u4yG5*iyB zB8NPPzj*cNDpRu{xds@KB`JACD~er!okp@Le%_C(PCy??R!&&JWMPOr&g=!>H^k-a z2leKJ!p<_&N`Bb>E!$!AlXg?Prl)m8R0R{`tH31R++|VkRg&@6PG_1@=R~Y01xE%` z*LlI_4aLX|`pzGj3Z^|rDwhppYBS$%4p##*r+(h;JS)xKcsOW-(~_(8PNvg~-#@y^ z6+{cVv(9G@>POGE`5`UB?U37SJbx-|SjPF|a3C@RMX%>Y12&@_?uqFq6|-1F%Yu!ldNtLJcg9KQfc;#s z6!jc=&1#O-qRz%;Vo?9KdE$IK-!Z!4b;E08POsMXBoC?SgM)I>1(NopTaS-3lE=RqeW}t#zr3h$P`mq~`N{dlWlU+=-8A1= zD0&y!8r#hWqs0fyc)Oo+YCXIWaI8|HPtX9SR8)G$>8;tJ#(Q0y1hj~=6Q9EQUFRB! z^QS&={+%oRN%kc>eRnvRuM)1VWF|%*quX^Nk1!+b(5(LKsdMK;WON@Kyr_x|qp=3s z_bi?Iz*kw;YDA+&)Sa#`uP3$`-1Zurec*Bm>G-vpp6gRxI8HQtP2G@@!O84(Eg|`N z$KEG}_U7}8zn-K&y`I3FehmbgV!UPnyR5oCJX|BIqjeB#DMo)Ej@9N(^_UD`Zh5^u ze*ZofA3f2uO!$fGOkr?PNbcFb2kChd&EMHvK?&b=e#7{;txA-iU~9b^?Vr6*lkj0; za{;QHT&IjfsQf9%BaU@Jus}i8C1B#XK}=~?bvgjdB1D5iN{!W`x1=y zS@ANWDU~(R*D$Lxuf7*}`{X%>E|jJM^XfAkKdkN*W9ksh(flZp{0XMfdX?jx2M!N z@o-ij@B%r*viE^mEl(C+BRfQup2lyel;ysEzX`w4wz}tLu2E?2S?M6%yA`aVdfm&j zYZoD&@>fhCzucSxC6lBVDG-}|wBb1JkYe=VuZ-`1Q^$j}whvZgGJ`?kmDzg37N^?b)5!ciCRko+gnNZM9)cLnDl){G*D$Om$K6d z7?!ZP~Z3qe))2`kCw@0`_;mODs-;2@h2;IIMR! z&eZ3^#l*e(8=8IY`wI#&urfs&Z10<8Wzd*Pl^;sE2DnI>eGps>Lsy<5i6J zMRZzZ<7xI#MfS^dYfP&3P15z18NB}cKtvCAZ>={%#m05VC=!{n)f}Hgoz3!9I|Dy~ zk#o<2Lv%HmW?zd-3zI}@0luvGr+qI@3Sx(JwY$9*5(S#P)jkoF_%{3n2pS_YRNAv|1Z=5{GoI@rN z@#jh3TZ1yg9elAA;hx0y9g}pv*!-tBJj~qh4|I% z6AHa61HJO@3@g}gacZT0JqWrQ`iHyM5vLnWa1b8&*0X%K_Rh7_24)6~*k>cV*KNyN z~kwcN;2y zO0eJ0*M8Sj2$q!>367I3wbozPB@TF?ZxefX1SHWlsfR_FybY zK5#c}6s|o{l_J`lK+18Lfb&fdqOq<5e*&*>Rvd2jEj4aNHU6kDStoiI z_)9=WY78*5;L+>dTP2PcGWDM82VWR9#-RD#eRj*1tX*Nb4tlPT*9-&yFuSEy^g>RA z4Z7d8{D4KRkLtz;7gf<92vuNR_htk0Yvy~|0z2!mKW6eGd!Hw4JgCeSH)kP#?dd&M z3jTFUF0mvYXis=Pc#hflE!)q9ze z9xVX5$Yp!7ekZ(xz_7eY@1~q`>nrRam71wr46gjdKt1n3j*2`Y!Zhu8%QBl1N82g$ zC7lk;+|OUXR<1mwrmpUHSS2(x2v1Cu8wq_of(Rr^l#fSh1K0&!hPaBfxJztDi$M{U zdQ2LZZ=QM4&4HL5$)wxLdvus0`JP_Swn<%gsZC?d@#ELXlpX2mu|D&$zJFZ?3;@q& zrrDgFDXqg>$Jc*Mr{5je;{~cfrKLLTJ&olI{|2@0=$&!7y;=$P=r&J0eV%$rDu`AC zP%e>Al_7M_U83t$-rL^qc}!wJF{IBBuO;nvwHP{V4)bVH0D#OqF9W_Vj#yLR4m^q1 zKbNM32vKw2FmRmN2Yp08J?O9PmZw+D0!(hs(l7*Kqu9BVcWo8k!Kqxeb(**Kj_cvYuec+KAFV{|I?&r$?Q(`*1ly= zSelA#D=CnCXBRKSFII7Wh?(-`^4qPp!FIQ`0HyAZTLR+3me%LV%l5cmksqVDHO(9O3e><}R6~l&& z?Qt)cXVe<|vaq5@miyF|#Hh&I*3o{>w2br)xSS7UO^943QEG&!7|BVYh8rcq-#yVv z1#_EWif}^>>TwEqAb!X@y9nPlPAl6LEgg)`3bwg`g+wN9#3ZJW!)=}RUyqzb{7^;v zzL^2YlNhtk@3ToEod5!F1!p`60(lcE=w_j|tLFW1c~fF;4urH(UtRn8XZXbd(^PHg z+Ky`&z&&V!5b;~KaGWn}VmZCNkx#@FvrmXUxBe^gVI@+6ys^mI0@_lae@Uip&&OrE9=vqb$-fgg1P;##lKd8Xyj!F^ z>oZ5^|GIZmo`q*miteOMw2BZw%V;

      (lw z5vUO#kPu8=-DSeYv;@iiN6reBjQ!#L&P64T|tTgZz6Dx&+n;|FG9bJb_!<6~?H9oCvU;MH($gdX;K@ZHB z!=T0QTggl?_cTE4BY2jEh$DE_K{#oyHe;Lzh<(#-&vq4zeEtm^N{%0dsG%xMh5P3; z?dUc+?Z+}UfKRX`&vYl=;pZI(SL>?`fg62xd81>s?j48W_C6QU?uFWPTk(^0#os!O zaJx$-GjN2O6Z{U!EXjoIcR21taI6nUQ^o?{un`NL8IvE%2-h-0B>Y2o0~n{OyT4D$ zF6SY3bo#2@&0go>Q;0ayKTYIA`9Y!v6icluxRTHISwj5wK2!1S2U6oNDwCIB)C~4% zP*>X(B2{ce-u0<$*uhSh9laA>~IJh_PTWV(Q5M1g35z!M}4oG!SS~oWjqrfL9J_rT+ z3C(4^zWPGI1w|=v*$6;M3P#k>A1*rPpzvMc(xNyeMEM6p@w$opcaX&=x*s82hxX)) zPa*B41HBeiNSUJuO&T2o_bhV4Bd0-|yc@nYO5n69M1#lVP$<~I-*ds>_CcoMG3C_x*ij*9p(MU=S?T6)_;KdlceH%I>s&53Zk!w=i{&_39uILkM>M-@$f?}GlZj3eljn?&FCR}%AFN~swf ztA6@K)DiTrR&A=+utQY71j_$>D5Bv}5seC_{m%zI79Qc_EZ=`V<39^QK`ooaA=f@l7?cgf~nvC0S3O4mH+?% diff --git a/hotel_roommatik/static/img/avatar.png b/hotel_roommatik/static/img/avatar.png deleted file mode 100644 index 2f06baf5512a6218c8e1ec8d112061675e7fcbaa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44610 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

      (lw z5vUO#kPu8=-DSeYv;@iiN6reBjQ!#L&P64T|tTgZz6Dx&+n;|FG9bJb_!<6~?H9oCvU;MH($gdX;K@ZHB z!=T0QTggl?_cTE4BY2jEh$DE_K{#oyHe;Lzh<(#-&vq4zeEtm^N{%0dsG%xMh5P3; z?dUc+?Z+}UfKRX`&vYl=;pZI(SL>?`fg62xd81>s?j48W_C6QU?uFWPTk(^0#os!O zaJx$-GjN2O6Z{U!EXjoIcR21taI6nUQ^o?{un`NL8IvE%2-h-0B>Y2o0~n{OyT4D$ zF6SY3bo#2@&0go>Q;0ayKTYIA`9Y!v6icluxRTHISwj5wK2!1S2U6oNDwCIB)C~4% zP*>X(B2{ce-u0<$*uhSh9laA>~IJh_PTWV(Q5M1g35z!M}4oG!SS~oWjqrfL9J_rT+ z3C(4^zWPGI1w|=v*$6;M3P#k>A1*rPpzvMc(xNyeMEM6p@w$opcaX&=x*s82hxX)) zPa*B41HBeiNSUJuO&T2o_bhV4Bd0-|yc@nYO5n69M1#lVP$<~I-*ds>_CcoMG3C_x*ij*9p(MU=S?T6)_;KdlceH%I>s&53Zk!w=i{&_39uILkM>M-@$f?}GlZj3eljn?&FCR}%AFN~swf ztA6@K)DiTrR&A=+utQY71j_$>D5Bv}5seC_{m%zI79Qc_EZ=`V<39^QK`ooaA=f@l7?cgf~nvC0S3O4mH+?% diff --git a/kellys_daily_report/README.rst b/kellys_daily_report/README.rst deleted file mode 100644 index 7bf0177f1..000000000 --- a/kellys_daily_report/README.rst +++ /dev/null @@ -1,13 +0,0 @@ -KELLYS DAILY REPORT -============= - -Export PDF KELLYS REPORT - - -Credits -======= - -Creator ------------- - -* Jose Luis Algara Toledo diff --git a/kellys_daily_report/__init__.py b/kellys_daily_report/__init__.py deleted file mode 100644 index e73c9c829..000000000 --- a/kellys_daily_report/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018 Alexandre Díaz -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from . import models -from . import wizard diff --git a/kellys_daily_report/__manifest__.py b/kellys_daily_report/__manifest__.py deleted file mode 100644 index 1bb6cf00a..000000000 --- a/kellys_daily_report/__manifest__.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Odoo, Open Source Management Solution -# Copyright (C) 2018 Jose Luis Algara Toledo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## - -{ - 'name': 'Hotel Kellys Daily Report', - 'version': '2.1', - 'author': "Jose Luis Algara Toledo ", - 'website': 'https://www.aldahotels.com', - 'category': 'hotel report kellys', - 'summary': "Export daily report in PDF format", - 'description': "Kellys Daily Report", - 'depends': [ - 'hotel', - ], - 'data': [ - 'data/report_kellys_paperformat.xml', - 'views/kellysnames.xml', - 'wizard/kellys_daily_rooms.xml', - 'wizard/kellys_daily_pdf.xml', - 'data/menus.xml', - 'report/report_kellys.xml', - 'security/ir.model.access.csv', - ], - 'qweb': [], - 'test': [ - ], - 'css': ['static/src/css/kellys_daily_report.css'], - - 'installable': True, - 'auto_install': False, - 'application': False, - 'license': 'AGPL-3', -} diff --git a/kellys_daily_report/data/menus.xml b/kellys_daily_report/data/menus.xml deleted file mode 100644 index 0bf4c2c43..000000000 --- a/kellys_daily_report/data/menus.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/kellys_daily_report/data/report_kellys_paperformat.xml b/kellys_daily_report/data/report_kellys_paperformat.xml deleted file mode 100644 index 5f6040689..000000000 --- a/kellys_daily_report/data/report_kellys_paperformat.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - Kellys report - - custom - 360 - 60 - Portrait - 1 - 3 - 0 - 0 - - 1 - 201 - - - - diff --git a/kellys_daily_report/models/__init__.py b/kellys_daily_report/models/__init__.py deleted file mode 100644 index 9bba13048..000000000 --- a/kellys_daily_report/models/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2017 Alda Hotels -# Jose Luis Algara -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## - -from . import kellysnames diff --git a/kellys_daily_report/models/kellysnames.py b/kellys_daily_report/models/kellysnames.py deleted file mode 100644 index d2b996067..000000000 --- a/kellys_daily_report/models/kellysnames.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018 Alda Hotels -# Jose Luis Algara -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from openerp import models, fields, api - - -class KellysNames(models.Model): - _name = 'kellysnames' - - name = fields.Char('Limpiador/a') diff --git a/kellys_daily_report/report/report_kellys.xml b/kellys_daily_report/report/report_kellys.xml deleted file mode 100644 index 96ecc5af1..000000000 --- a/kellys_daily_report/report/report_kellys.xml +++ /dev/null @@ -1,205 +0,0 @@ - - - - - - - - diff --git a/kellys_daily_report/security/ir.model.access.csv b/kellys_daily_report/security/ir.model.access.csv deleted file mode 100644 index dcc559a56..000000000 --- a/kellys_daily_report/security/ir.model.access.csv +++ /dev/null @@ -1,4 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_kellys.model_category_manager,access_kellys.manager,kellys_daily_report.model_kellysnames,hotel.group_hotel_manager,1,1,1,1 -access_kellys.model_category_call,access_kellys.call,kellys_daily_report.model_kellysnames,hotel.group_hotel_call,0,0,0,0 -access_kellys.model_category_user,access_kellys.user,kellys_daily_report.model_kellysnames,hotel.group_hotel_user,1,1,1,1 diff --git a/kellys_daily_report/static/description/icon.png b/kellys_daily_report/static/description/icon.png deleted file mode 100644 index 946adacbee4c4d1725c6fa55d831bd67c46de4ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36423 zcmb@u1yqz>7buJt(ugzy($do1GaxP94Bapoh_sYQm(&a)IW$U$fPi!iosvU0 z%zcLU{l5FJ|G(?5_1|?_i+G+nXPsXsx`MGslivu&f?Ln_RU#Y8! zT0)$;%`s!ReVtu_*;rWOQogR{mX6k*3>Mb5_AU~PsK!=C274jAS}YoEBJz+Pw*K7-*a9;p64PwywACw^NR957v+1- z@b4ERu$sG-ji`>C!oO<)PZErFo}R9vJUl)=KHNV1+z@wL9$pa<5uWFKJbZjyzz8l6 zKNnAPUoIDqr~iQZtv=84{>3@Ks2|2czH@N0+{}@1!q@v_5T8P@%Wce0LgfK z&0TqTxu5emJ7e~BGup#b$NGPw@xL7H0rGRT=FzeCfOxrE0{pOf`k%=F?f&-*VFm)& zh-$jq157b@l7m=!Ia|AUD$7YQ0-tbO*;|PU2#Q#|u;e%A5)ctE=MuIQ5abf!6Ef#| z!7nJn$8RlcBP?R`A2|Q3`MkW+e1h^X_@sFS1bBJnUx>&E$w>?Iz2Fm;mlu>#Q25VW zWfu=ma~DhN|Ez5fto`p?-v8}fQCW9ub5Dpn2m*2Xj}&OxK|CQIb`VzvS=k$|GqCB~ zyI4VdJlHX8`e)a2*6#M+)>aDc5NC!PI*Z!>FK~EnX(K3P!zavTC1AnFWg{$L%_RbC znakSB%Ep}cIiH9yFCXK-^R50*>Bj>g#e)%+|A+AW=M^B4m_Prk3c$mEmC4!#&>(j} zRnYpYfc{3a{#AK zOOEE_ZJLm054EKy8PY0G=(x20x>O}Vzg_3ZyooUmNN&oE758OgZk`R=@u>HSZ$x!J zQV=M;gM}4R8K;Y}4Kn1wj~rm#5G|bl&*RG?%#ib!+$27i|5LBK0DtCFmsRB91FStO5#MXMtf*>Sp=^#_rUxrg zk&f4r;sFn>?_owUinqVb6|F719=RR!BA<|eqgBeOhLStqfUin8#@7(K7tv>7ZGwe$ zAN+~QM9x*K{q%#bS#p3Kr?&?8W#!^D4>a>Eh>H^kkes(20>M}ICXpYc_i8%%Ro7b0 z(i>JGx#SqB#MQC`?Zw6(u8)Y;nbJP&&s2x}5_7lcT`FHq7lj}uUje>NhP;EVLDwQr zla)hULQXQvJCEICttAMesje#d%kVbV^nSuy>Df5Yt(9ll8YN}6jh}Vr65(JH-?bc& zuUwcu-B1_jFDdI41wDGKY^4X+;rpxl&F%@1LSOk%RhRxe#lv!@1ZQeAA?HIZt&TL; zmTTEFd0)*~mboBeEqKG&oMwZnxj1y;ZeSf=71U=h1Cs*sTYS9Z`S;*ZPY;6d=KgFAYn7#(F5;}Rw^S*)BpCy#?)2Br<9k_@qBf{6e{{B94d`EIrUzT?=R z($9tq+oOvY#U())ZSQw8blDMhG?dE~BjkYpae&PQ5J_NR?S%i3T2SkX5_uYo=B>7u zJuwbUQ7}PY>63A+ETA6gS0dN%VYzY!>fqS2aG-m%e?(7TiPtrdn!Wyng8`eb3CeL3 z{P65bqs;Z9Aif)RxUHay*&v&GE*{(njylO&XKX9_AbVIOtZD}(Z$gG1&_=F~^!KWO1$7VA_25r$hT z>PDxXu?pd~!g0JX0;W|WH)=Eel*`ON{w-PYx#h_^XLIjkY@|za(IsR1=A7`S)af@erjV_x#9M%h zyc`VgkzQGUI8}Gcy1regKu7*?r{wU@KN!g~aiplx&E!6`W+yZ$oe(7=$|t;mr3NDO zZY;gJDA!G~z*BZupr%&8**eK+bc05)fek`8-D2PQ^SrL$2T=^Um|)bQg7yr$97URY zehTk<5}n*IaKd4qias0(NJ@?r%57-)Yj3Wp{tL$I=&*tjH_Y&o zdR!3NxjJ@NM*(CQ>>0GLIBSC}n(sYk$QBT{5YeOk=cUx76dQ*ouHMK2UJ#%Y;HWUW0$d$tC5+R0 z2@HG5k(d}Fhm-vUB&z8gV)o%acj?gD|XSP*%IG`-k)s;J-AtIUcqZNsG|Q*OQ)aBj51H^aG6sJI;DE~ z-3Y-ypPSXt(~wOg99`cvbfU?@uCEUa=+`yVP5DpV;6LODZVCT*v%v(5A3XKdHLZ&k z&Aa=vS+Yzn92O$_XAWU6bHU7C^Cmd)AqE)u{nZ>-cAG2do2M5eeg~74YCptUycdP( zPeIYSU;Yt~(#%6%dG4nv>DwAqX*yMfbQ~9+fz;y%a9UM~TNqjh??2eh@3)StwV0{! zYt^|ICstbRnRWhoYEEQkcD`9ZF_R8NeuKJgXwo zgmmx5K~!#m^NgBbjCJ$WWYQ6sQOSnh02(*wTa65OJoGK`&{h{Ac_QRRd{*#vY`w{n zG2>DPuID$t=;{Sn1(H#yePB~j;QGY25Ijy3*3rT)G8WGOQHqMf#>24ryxarnlW}P& zc*KJfsv`ezA`#&Jcb9py$>LwT9-_DMAbJVFephQWUNN2VgaU%a##*$<#KR1>8QsDfo=wu@clot~Q^GAR=!wFz@P zwSRHJJB{pqdXc#HLm@r4F#_pQ{d!`lByJHYVd*_pc6;PUpD=S|9EjisbU3fr$?n_y zl4J`!P{719y^Ze$cYy7A-xjV!I&ZRQSdWq^u6FaYu3gE{{=0>2yGe?nTByJ~a?ip; zdtqA9U~9Ct%8!5N$ycSS*$Sh6wI=>_Xdp;Yt2IB29C@RT3&_RRU6-}LW}7-fC@b}u zpT#`E+4r4P00nt1>}pFrn_;6gU=>8(?NUXXd1lGnn4$BH{T9HzneVj5qMnySE_ds<6IDJlyUH1cheIRDdhfz|K68p;QP{@!lq z&-^~zD=eL}=~>)V`YyPqOdj4tiE54hxx%9^{sLt?HZLe*+iy>lo=ZqFlQ=Rf`ee1I z(Uf*z9&3ycV+c10`<+-taUlw_Boj2+`@2VhvBTN6DrA@2~MfB zM;6?&{4L^_`1W5Kso0NCGeafMD$~mQbUB3|ms$N8{g+HyEUbDEJ{z0H?J4nwQ$+6^ zcP@PLAGyjdsnpS-Ao7z#eWo9!G1>2$?LylQQrkQID_^c}`Hd1Nh=NAGY@3SJx8r~2 z4xYDfNd0%s^!4fEnS+4X>J0*yN}*V8L+{tq$Wg&r0iiS!fW$ps^z(#`ZxSW$1#d{S z(YD+ZZ&;4)B$DX;wGNm#KzxaadnRcEg6mOkvL86jT+aS3&OMUa70A-ApBVcJa%AJd z@JYp312QSnieghWZBgZ(i3?SQ2LBZyN!oN)4@)i;N4W6I-+fucs2#4lG5hYoKl-_K z{nQJ5MGAE7^59;D18wuRzmcwm_0?W- zeRA2S|3bxnU@)nuSK>8oD0QR-vZ(sv9Zvp&|8w9~x74^^r6Dx<)UWaCJjqrGO3`;^{3#&F;G0aZVn3tc z7Twu8bGhC*Q{C2UoY6b{6vf2hGn=XaFT`BlEOz}Ud_!-Mu0ekx z$7_00IOy|?g~w>AG7E(jlT^KoH2#eib4;ub*pdhP9>ykhUOCMht8C25GM^g(!I41L zeuSdk$E6q(7JM}a)7|zzG0x8>>-C36m)Da|=xiK02peZK#BQK;to%X2@1*jDZ&0EB ze*D{*0E?!~A;haYg9cG`QcNp58A_p-Q@CQA5Zr@E6mx;l?EY-hQ%CjSvh@=7PGwN2 z{DZ%Pe8tQs0$Y!iOFSEgaz(%Usnl-#9XsXfhVAWa~gyYb1C^t1WLxI?Sq77_N zdg>x%$_GIN1511jHa-eyAVVXT@y=?>13Z67zxy0)<_g_x9Oh!t&3`LBpbX-knT_dR zm-L9xEzt9_LjlKC!~LThq?Fw0iH-9NFNDoLI?Wgtdrf%>rAkPx&dpeLHLBJ7U94U; zLHAHh9LY|o^Y(k9n|NFgZ~L>&rF+A*n6FEVUvC)z;at6CE?yDIjPn#y+O1}!MH2fe z95@Qmq7_(coKB8?yD}1b$DLBo^Os_H#e{hz-pMf^9hV{3c8VGeU$|9<&o#B~XWnBv zzh)9%d8?_GILzu;Wwl9Fr$p=h1S^gU4g%=OE|tsc?E%(Z3d!GHD}?bwTCJBuVX;Phz7bTI5+0M0_e! zvqQe0rs^Nl=Iq^=T~Df5nu&$0aZYc#ONXD{X?rb88lzw3ss{hoRqY?0$OZ2+gl~K} zCM+VM75-Z8!CH2BPg*LQXSVqbQ@&rQPom^|N0ya+R@}XDQeO9_ZPVpzU@-N z2fO!i|Cp(;Vnm~&_hDX7Y3kwG0{w1fG5vBK5fgTP7`06Q zD6Yu3rTO-h0DP)&lZPHE-FJ%^HnH-NN4v&p+Hp*v29ZpL5635TkXV>}dm-51<$mcn zxJe;#`XIa~6e%Xila_k>X1RqE8k{a>I84zDhz8rN!>#dfSR zTG=}GB$!|k4iM*%R+;?D)IKtLV4o&2ujGWwq+SmSt7sBY!0Wl6b?iZYoV_Ew7i{cl zu_D@)fgeElHA`GIH{L%05x5Y()I0ulOXTI%KZwdCbrdw)1TFf9Z`ioWo)f<0ViXxkZqERq7)+(@H+zf`w*yu_YLiXLJ3G+5A*76C6Va%@!5k8R<* z7FgDXuWulf9?80C0(2y=YPRO94=ERPs9EN)N$yRT+-Jo=Nspiyh86!D7Mog7H&=4we)s~Zopudig1798st*oqNY92*Aps={>k3Ky+JZ*HciF_Zi z`UP1+S{MWed!Hwkq91(1|K7=V*H2LgXO9)eazf;Q-npZf@Xok65neXyaI&clISu)E zIwhGSJ*ZA@=6?|$>T<%j=6^_3r%}P0!%+r>iVr;!WD+!1Cjcc zJI6}|&upP$X!h=*o#aK{6w!l2xM|99%bU*E#4hu%y!yBr>*Ha94+bPNaI!0Z99)Q02Xnoi0Zio@hsuf&H79TH{W z1}1q8bYt$sH`amP$)?S zY1A3?H9yOL!v#bo{r1mo*D8+yFY7I`^2FS(HT!f*z^)!w&0%(<*Jqi7d3!s1qvap$Wq*l3%)UM&ufwkeS9C!7+hbHrd4NLge!h(O+z3? zoix;EpH4d=xAkznLRG$RJ~5p9d$YW%|!1rms!3shd zcW=ar$fh}StmLJ3P(uVX9VZ8QaoHbKs8>R6w`juJs+M*W7u+d4I$D=px@}m6?bkV~ zWVoNC52_PG_~@x@gqj54r1}C_S5w}scnW4r@CywaN+nmNC7Ey-yXP`Hn|Ui3TYd6N z=QtGL3=_u!rMpLHqtg8dO;QrF8Q#GR7}g zQiBE1tzd$}R`Se=4rk=W=~Qc#W(GME=*+i^U_&L&XZ-EK5z0qyMB(NWn-LDEyw+!kls*}?NuF92J7WUn!w}ac z&_5d<@m+ptV)Y`3odXvg(Bq5@xJn%0I(z$I_u+K5%TbJSyj{rEL9z{<2zN(f>0MGJ z1?G%_%gja$bceJYl8voXo z+Nlg~Yc)4hHJzmg05e=JoVQf5)tj!10%RRPUYkuWwcmmO0VPRd_+5YI`Spdt!J!Ls z*3j&hlo}s(lqUs-Q9a#c4mFyfJGAT?*QHdQ5<=4wNMwodYQD)MyTvyZN!ENK1UCZW zUXZIlkD4Nc>6b|z=_@|3fmyyWb* zqMMC+f{h`IJ517}HJetxpJTb+(82{};_;crsKNLnJEw%i`M_D!7&eC3A}6$B;};%g zj1Uxj?hmeyQVN-<*-B@U16G|%k^7k(j{UbrNI-8QhmYw2wE47_JuK24`%`d{dzhTy z>tlf$F_R3N_D%ZqYsZXbo5uC1ZjIKuO3;)T5KX-d1LKUwI*OTYtu<#NTaY$x*U7D^ zf95~aqhqg#Zx&BbDNN{|v8&Gf>8T7Zj4nMA&N)uF2XF^i!>dI{2nBH&!y>&zcv=p9~raw(mMlUgbCU2}7-x0e@?fyQ(`+#(W_ zeCON%A+uzAQd)Ewdfr3UpCw%7@(%6c+?Q_3mVZnsOP%-8$Bu%X$v`Sdxt^vtCdTU& zRt7EFCgZ=z1`WM;Yruk<#{Gjtrldr>bDHM*x^})ZXLRhzf|MQ))k4Bb@8I$bd@fwG>pQ-bWnMt8&oF9Mh~<+b4|(cwgV1L{l-by&v0>l zYo}=!!IYWW=4~OzYw}yIMSYFw{hWLlQHCW}n>PHkRBZIa|4FooBBfib^VmfswZ{1C z{JvL;3c6St>dWk{M(DsFxh&GMM3Ej;dOTsr8oCd52_UCR3LE2|W*%i?%#|laa}45~ z#Fo5r{Qa8*beGoST&vb*o~U*iH9gX06eZp7NX&6K zS$A~<4yv9X6=h2j>TN}hm#4p|%APCQ8n%^xk%gBOf9!oeN>tv3Z-%PN%#d%K2bASw z_qibCRVmcR6q9xihm~vzm1*s1zS{fao$h*7U8$<5@r>qDX*N_fH?c0W|m#ySXxwc5jCH1qff#fYXf;$bZdC#V%t$D#{!>Rva9Jb`Tn%6*!-s zQn&bmXp#ui;4%Rm@5KHfg`T-aD84f&mXi|*n&XO8SjueI9CjqO*81LVbcJ?|#&?&U zd==4b-MwZVKsVR4-OJSS_ZylW-$I`ey_qFB1;xZTrGKx*q(Ha}?||qG)pKM&YGwDb zCRp{L55Mw?*=o#b3su!)ul3l}*wBkB1eh(|Z*ic9ykW`oS;3>mzEP9KA)M2$eO_?f zc9D)CebCb0x=^GS`UIcTwXDQI1Zfr=d(6G{{RIC`s=HI6_pEso63BJ}3;G5!w!`Lz zLq*Ao*N5dFRGfAlK(++)pD|%@vDGj=%S;`xYB#`ekKD-J4hHkMf>dwipql zS7Br9MSfr_XZd74tyi6OP-4V4g(GY@Iq%!?(E)f%rtbCA0vDRS2irlnWfjI&g2L{k z`W|UGTe}ZN3%LU+3a@hYM;1NT1e*?*ffL6VYXuW1=CEH>OQ@pnAC>YG4CwbuD*Z-@6$aXbdJ*OG6`eY)EQ?L1l#q&Baud^O!c{Ijv zgDrBrV7+AIe%+lcmh{AHD-rmrfk%{cMc){YB_3_YOycWFoV2=A*K@4>#RcjP0dvn# zL*#ic74P*}KRjA0dVa%1vA@UNRy7EHoc@q&_j6b>OGGmd7^d*_-rUCb)a|+7MrT{^ z_ftqm)j1=Nxm{f+%~(H|l3DcNMHgJyqIAq}LT8`X{R8C_lv)9P`AjH@##Klh1*|(E zZ@bngn6=Vl?6Ng8Q2wvquZeG5)_sqrE>EQNixGZpZ_Lou%eal3eIni*C&nDxL7X2B)f(kLy?e9lkTjM| zx`3f%rvM}?Tqv$f`-x%~9x zsqS}Tnt8>5xu5hrEg63M)x^4lFr&up(+bezkhBj#7~5$+6uFqr(s;C?rHPcf9&)Jo zSZ@p%1F8qx&@|Tc*=oz~kE~IXZx1s?wtLpIJ`c_M{BDu)vacsnx{H;IiQglh78taj zKfN3N6l8MMW3%RiA3;<566A2$A_miLMy|kG5jfc|2Q^hGgnv~Y!XAYr-chxx zmwD3mwsc@-$V|3%CyQQ)iW*u+JJ2PHv>x-yCEKSi&WuQc%ozUfuS*=HgWys;l_Ybx0JknFh6NM?98j+aNX=uRG}2dtvawbdCE z#vhwSV$oAj;^dF(dV)E~Nk+%|3^^`!@Jo$BTWY$+ll`p*%NNTtJAK1A`wOx}8|{8s zuHTD&v8~x-1)n|t)Y9zn)ZmVI*oY0uI4;aq2qoU?1_vTQ-=Opf5~dIxwksR8T#koH zdbG2-6(21k797+DZLKm5Yo62w6ynkUT{u1uk_z%hJV8J;R?kL6niU8Qj+>#;4%X;M zKr|EyAN?ivjr-KSJZmlu)ad#ek-|Y1o`a-uz3;{JnuimG`?sePJf5hF%j??VOzL1G zYvMV{g{tyNu!J7~{x-gcqR$74BeS&Wdw#ZRYqOo&_;n{+GR`?Qvflq%&@zu|Of0$S zP+W(}h8?>t`2cmn+v5B07+&q!9z zd$xO2u{y{x=E!y2!%|LA%Im}N3_oOVy%iz0!wMB+Es{?E*=1BcI(&Lo?SEX6^+D3V z%XWjmDi+wn~5la{9hFuYXrexD2TDRA7gM|q9?U)9L^bA;i=Pe z0ur$}8byoQe`)N*Z-Cy_~PRp5uUdZ@y}1t+8Jm(WOSr z?6&Xvdqt2ZQH?ntoa1-;708s5oaH!K5Kq-f%45yH#h5n-+Sx#Diia$<^&9)^V)s5@ z8SMIjy&b0Nz0&e_9GqzNDTX-w3~Voq0^2kHrw&DN2e`<%>fTWow*D61k!cwY_)a3bfp+L@8NCfO=in1kJ%lGt*Ld+XEL_RP z2Hli(q|cWwY0x<1BKYOicnD`0a1he{wahozpT@o>Md2b=>mYl)Ud32m$SqLka3REe zp5%qnm^=SrTb(O+rU$1p>2{bQKQGY%$=t-h`p4+Y zuF8w9q0c&?t4!`Uf-s$XSS0ws*}L!qOl7pehvZBx)%^f+s=RuJFIEVBx<46XH#x#1 zn)|iY>+i{^s-n1gx2?GHdc^_zQ!M*XfPx;A&-8R-CV1{SHm@r^jr`&2a^T&*;u$yK zmo=F$Qiz(bY^@Cvc(Sern|0bZ+|?Y1XQ(Yv6L{u-upkhZ*uTz443&SEtiW%-j4KyclozwksG1w@f+N{yP^J?B&#jk){J~T_U=Hx6e&(b0bhhfZ zQR1vsNP}LWk}co71p|4nH_0hfJ8dj1c^wRtKI(1IXN`njgXuE!FFK?6*C?h>PCK2u zJxiD#^H+8}r*4h%?{TRTkpi(N0omX&zL!O9(H2P7F{o?Ndz%;KMeVIlHof&+Vh1|h z9|Knd`ZD!4<^-**p*{ma6h1pS_MMvBOSGWo#W1X9&(kl&2}dxas)f_8R~USRo7XSH z-rLlze7T6cJgd(Lh>f`Z#gU>g1DG4ttIe3L7lm_0wDi#Ry1@pERSKj06;dL-Q>P*U zndk(I4EoT1%DW)%KB)vYBZd?tPIloaRjy?CaVc(MHtRB>aT!dhU(R3SPcXcX`4) zzM;FP#m!zZ#A@uoQDpk9F}H3&fU;xeb}f=tuhnj3c)(1OZS^dfom{(*`l3tDCS3&8 z(Y<=@b2=RQwA?J#piJi&NK3V$sb^}FfZWYB1*r5|Li4iT8`Wl=hz;aHxV|v?Hk+1~ zYXa#|6Vez=_DD~GDNTI4TA+#d`*#TvL8P=~mN1|NB0c#s? zMeqUQHfMQS6DB=ElJI& zyMB5}EaE@U_QOrccz#>Oz`4XYL@RVtW@B92*Dn9X8r_>~0EL-VTuR&iLYFz(k?b-V z_Ssr(2SIaOFO%#zjgpzb?Q0-ifjYBSUO;b4I>(zit-QT(Pg2PhX&Jfu`)aYbBa{4S z{K!D0H9EN9;x|!-q;*njtjd~wfA3l-83SlxBl$(cH{$jF)=sO2D^k5C@#6k$Q#>g% zUY<}}rPoT^6V!zE@eS$t_M!Csw5~y~-D;9;V`Et#UC#lf^LXl!Jh|#y*W{<;+ulak zNuEy%XlBceMtTTXlP|%TW*0%!4)GY1bi)CpS+~f2;;^69nYH=ZYW{r}VzWFCF$#Pz z!n~)?Okkt9O)6a7fgHqHJKthlY$t2>u94!lYg=bPh|IZPK~5#q?7oB+@LMdv<{yevp?c zs1|tUfSmwO#EVwQ#V;bj`zv~uBxM>HIZlI-imyX{cScLN*X4H~;OYhV&0dQ<%5ys- zU224i$S>pT9F;v4r7Z%lH;mQ$RCfN^Mgf0ex`A zVp2E$2l4aG+nVHS*`jeD?Pl4^^eKS8TbU&M`0nMF!8q=33=+0MB}0DGd-iDs&;+-` z_eEkxPs)etOUq<5NfSOr4X+VCGmy;F?h;&@>z7F8xTZY5PxzpOvNz)P%Odwqt^CB3 zOB)#evyZl~LrNasrlzX$h~c?VT~4k&3J-TUI+PW#-CHqMLY}XjWU*AwU5v&idxu|9 zo60KOg#&5q9-PVFkKo-~*!UI*!)@;Xx6JyDQld_%bmo_@pMlfv-+}n^t5D&!;{ewy zN7S;J3hB1m&!bIBEy2_z9KZ%R_y--w3=IzpeqNax)!z9d%8}p4b}Z|Pz2p$e+R<%w z=od~|St%TJzMY=gPLMpzd0qKj9@ly54=ZDd%yqSzv?7oo$3+~JflYQu%B$Ry9OkD7 zA`jHkCif45EIV4|oaZvKhwSYKhw9xg4-e0`g9Gf+NA1WKwr~Y1&oW4DZS;WD6p(XI z?=^@HdCxBDcUL>K5{_&QL#Wae?PRyc$6XF+Dfsk*^&I7{8YFphO83cl zGzN?EUn2~M32A#pybAnd`}y8*GZ`E5!MB$i)K}2bhTa>@(T21^`aqjDqdv^jZALF` zQf=v$PRD$f#vfO^x$mNaCbp~&RTKv)A{0Z%14cBH4Jp5pi}of4as6fAd5!aEH?1u3 zXhJ5HHy_Ql$hWQ!S6Psx2?d=mkX%{xPbONb>I(!qeI@o7eSP?C6auy5)Blc3iXE*; zKUcHhtt#nrUR}Hxut(k+)M&W@uWOP#; zCR3&Xk5eW_Kz6N3UW+4@U1kl}B62C3L^&6|`9&z`;x%AFQ@7y+%=gck^fN+FoXM(Fk{w^&9YpPiy zQwt-QjF%Rd2)GuOwGPukl;m7HW*qKXO9pe;#k8Ll-Q51U1gIy#AH< zVQZ7s+2Fz5WJrKwnfo(ez=nzJn2ESrjrJzs1!o3b%dDPJ-?M7<-JR%M4#GC6pZt>1 z3nvCNnNzC>bZZpeFzY@_IlVU0(0B2Oi*xm0z)7QD-0x?(LsVLckBQy4CGYKp_HIsm zqA&iw*E|7h&l*hAOB;n*tHSOh5#)#=fc6t}ec1mD}Hkf{zaVWk6m_3Slaxh!&XS60dpqe)s@Vby(nI#sSQRnO@|i7e$|8?wTVW?MZYSm859pt}KvV z?)gwUPUqr_N*HM_gZhqT;r?9X=NV9BES;o$h^oTfysmM2NBdOngL$WV_K^1iPM_~{c$ z?~-*EwD%4Y8nDwQX`mc4%Gn~-)slF*wCHY{{^P)=!_#i*usbt#uf2b~aA+fhY)=`y z-VIi%Fv07qfA)dIOxCBf4p;U%y z+G2`FX?g6+aM;l3z%IwUXotJ`xw!u;GZ}J#MPesvSzjX`cr4&NfYT>u_!rBy#y6b& zNea3rOR_OYx6&>KB*ty?EsslUD5z%4=eDv4FVS_6*#n5Cev^xwhx9pgi+c}getDvV z?FiB>8qLLv%ZKtOwEr>+0d=!W7qgF0jbDF-A=gkzi-_Q$WgP#!Hvb`6_~Bulx2msI z$0u?r@@aqdtZ6&DR=jj!pqCL6i#tjU+zA-s;fT76rB!kI74{6(R9e;?vZ@pPAT!N- zWu7Q<V~~HORtXFaqjlh1YppxKBvy@c2!r?Wa4oK| z89>Tat}6LM!|gT6+Bst#0;+7CJan%b7N<9&1;Xw8?wl*mj9SbSWsR{5Sm=qos1o!; zj_?3=sTkK z)VL&>|HlU>pr1ZT>u{aYrSdD489nNc4jnAsZHugwG0AxJYrJ&JCiSB^G?@u@dv83l!H3ikt3;X@-#;muOC3GE3h;odqucX`#}J z&p?EJwdi;+*8@%j@a+d< z+eq0Qouu>e=t&X9M_hS+OkXc3Q23%jmN6l>+ZzU+y8|2?vfQ_C!_79_zlRK|s z1%u)mpuM@~@TBOyBG%QW1pn_{vq-U&i9ya)VOC z_z-;peSB|a$`qN&{XOj!UFMqP7`)>9pVjXp3m&%PUxkMq0{>r{&i~>>efX^ zrOXj%k!yjhdFXQ9rB^0(wPq-7IzpylH*Rr$(5-Jrj`bc!QkDAqR*hrb_9AsvWyZIA z{@b)bvc+GSL(Tu4E9pS}r_WpyMc@3*x!-wI=~CC?@=gLb@HV2TlESYAzI81z=Z}M z)4=E~&6Tg8HBv^daa1W^(-Ia6Z~rmVps-u~uz!A+7~m>1=_50vm4%&lTv5mD4WP|j zFDC6}nM^)>4(l9Wz&h)T66y~F#{&SOs57g0ApxH20pojN=f5SYqdS)_Ke8;%D)Ibi z1el)qt)2+M8TK073%_pAqH@~+*twQH=Ffhp{2i6J`oDTWXEC zfQqjJpDw0m7gz&_r*It*Wdm)RCuOHrc6ad>vF43d+qnajLdpF zU8<0rxPsm~2-#ze*Um6k zY9M)cWb|B@;gXO7E0){AvFx-(hdx)09J z8TeIjAFZ6yB6<7sZy1mSCMGqz9_oOI5lo0|b9VLFtd5l8S&mX4UwzBo8n%DFOP61G z@5nQQ_Jhoz)oMWLTJWNN6~`!6gfFQKt_xr+yi)e1He7F;xN13tHEN)nO+JPre}8T@ zh0eLH{n`i91-HbAg%3#4fuv$8lUXAtn=ETdWx(x&?f1cDT~ApmN4JPPh-i$2Hw<8a z=ZXL>*eO;!snEK7-ktw*I}+N+u&nO|l!BhsdAKj>MfX?DxAe%0y`D|x%U|?wi@dMP zW^c*bE0(p9xc*q=`lq~o-cuTcw^UF=@bU}Qmdv++qFOYW2~*LQQ#6fo=R!O)z5KnJ zwYa;afYyCXT-jyKXvrLPS>qC``T8hc5Il8m0SXNfsFJW2>xZ;$q7YMBLpIA#Xj0ZMTh7kg(>ZGO@2GysoX>ZBL*^*o&iSr=>U7Mj`!CZD@GpgM@A? z9d%GbdP)(EfAG;mWT?@e{f1T`xS@LcVC5o`&iCg= z18}pavXU0@_QIezNmAX;>=c?7P4{TH;jAVMC>bR7xI1TF?wu0>fH#hJHXi;RP*~7g zHjJKeDI|3DFxEPI6c8F9>Vx*@@M0?lxZ3#rBt&D-t^=zkY^^4&!emOm)}FiV%yS13 zjPrBL2#$}Rb8lLGY|O-n4$5_MiKB80(a8#yXI4)!&KcAS)A_jm$L3G^0F+8pfuySx z-(jufER=pHwdw_1?S4&|k7n93ZP1m#{dxu6)9Q|W^X_rT)ovt^#aUP$CyV}>n4ntZ zF5A*aA};$5xXlAvkP2**6;n@BqE#sb4Ec3cAy@D&~)4}UXYX7{XnU$`AnJr7>M@W7Em9b~psVWCTR-1f1FmAwAlfwT#DYtHuFd5v1OZ(k-X8rA z4S)VFkv&LuN#FN#nIeDkxmSvXuS7X$o$edvA~S2%z72e<9%d9~u4Q z0jE#nQ}dYsQHtO%hpUmMaSO~Ds~Z%vN;CB+<9*XNtCO5e-_T8z+~ON|fKWoE1f9;q zCNmj|u-mHZwyIo4t=gtbDoXcQpm{j)q{PooypPcI$7|#yBaUNTh;#Y0PEqK*UF7wK zenrZfE>1@GykP}!=V2$R3LWviURC2{^GXwl+~11Ued8!iJIWA~Enay*gs3e;1||E@ z=MuUaxvRLo1L{7oX3voy^$pM?^Kl=u$Rc`0c4P1meBe_NfYmO2O@NR7Ls+^xOY%6q z7bMHi?17P}QT{j|{BjFe+%0H7fKH0p z4yKeg7KP*AFV`0X+EZ?><)R;^5YvB;tCcxP*VFYm#p-efE_0~+`GtMHN($b1W*t{{ zxBXoA4iLf3u+@6#yv#@)$q5t)2##8sJ6*WV7n|7>csc-lhYzBhf0wSSU0#-cw9r;$XTtfo1XSkp#9>A%@EveF_dSdesHA@o ziYg=|m#28Rdg^n!(TPI@lyT@|#r3<7QD3%zr03V26_9gY6Zu zgdiX&T}p?rG}6)n(%s$N4N3|KD6p(_*V5gobayUGqjcv2-{t*&vfO*(o;h=7o|$Kk zJpcwUq5_{J>Xm+>Z@O4pamXje#KA+ZilBGDf|w5e0;(dj(({yCLX$TP`sx`tcMm4| z>xobdwe~ClQ-4dc`s5rk@f-8ee5erq3d-zgwx+6xqa`aEqY@LvzlEKFy}s>j@JNS_$WjHyF+I1j=O z^Z+>>;GkF6mFi02{l{(pyzki}A{_l6xj6@L9~guCbs0?6_`VP#0C?qIXrH5GK=Qvas3b7Gc_X&@UjGOeST6hyz_gCL zM+kbnDx<-ISf&Zc{P$GVP)Ayz=Na$rTV1PJhXMJqESx8A?|G4s5L57z{|U$i@DWH8 zNqMAR6@LHSr}Q|o^X=&o$^GPp&F)$A-M8X4cep?{XzG6>o&4d$OLsJB0sKrVD*E3y zkd2HG6?B^5*x61;xUv2>^*r-48Lsci`qf(cFG+OtIuIHEy{zhLe}U%aDB|&oz|D~j zV2lF}uFOW?h3y}Uht3El{Q-4-QBv`^>%EA$uhkQ5o$S4^Pv(Jy0g!_7cXP+^E8x zjCpDxbm8E#H4$qvWoHGhq=k6wIUV->ebOWfdN+Zy7GH%{po3C0d#mO9?vYF zXAFpWg++J?0LBc|%8<*#LcT?T;IvZuWW}WG{QnCiR~&F}#ivKEQ+ccuAEck=wH80E zB?-`U9z^|j$?z2alZAL%wNIszn79r=vKbJ>hxdN>`s%UPukkRG^H27v$N2C3P5f4B zd5Oqdr(bkuoUYdZ^6ca#rTf>JiM=Hops$ZnO^CXlZUSF&m0Zs2_s;M{82rYoAd**CxuHQ#HP zB8c9X&2$vz&4uab+ur^O0V;o>0;K}vuHjtIA{90%b)hOM!He82wU$#4W9@}+Mi*pN zf(1D(Ep#r>UW_<9-#6BSL|lI3UXUag&e^+%5v>oZlx6yUtA9?5a9okt07|S3^>76X z)YGeIynmckuxsPwJzRU&-2VzHR>X`eXbJjI=bE0V36;ygIBUDvY1wAGxhunEjVGmP z@6V66U62G$Gp=V$uKTTIqyB~Wi#N@yZ=vt->x}%MLj^|NdOQVhLZPlq3HqjC)ywO- zb=XuRm836KjVL%M7GM4nHrUuuutJ`69sXq&85iTY6s7ttR(!>NHgV7(q3C(GoU>-a zg5mU6xf~5E=ObHL)+j^;oXW2Yb@r3Gs{GOFM}gNrH2>olT6WhYisw}gPSMm#JmmZ)oD?y_M!8>jh8F$%!H{Pw41#+Mh+_ zq{2e_2M1DOl5D2NO8K#BoG&zx?|l9e+l)9{=iW95NU^cdd8`GPRV!tJ5xwwMl@bO@ z0L?#VczY_jW&CL@=WYDz0A5Kbb2fkQx6N(-BiGxqhoMrwv}?1v5!-}wIUo?Mr{p~= zN+195Tr#657^($O&GPBv$4xII-Te&Q6bHsH!48U_$2xTkvN}t|vhb)v4M%P+49Ueb4+!!V6pUh2FG)pEdRy*Kz{{IqrdCUConTioE0MDs&RtgJIn$&3NfI;j1SH;?_aQDe&bc)FRCTK zdryK18f5^MYS^(s_7E9l>r?>{S6fD2Pby^?aN2*qOm5)7O%%^}f4ePx&9`k{q~ ze`nZdm*xf28ASEDIQY0Hei82z0G9UR#ID03HuPSXPb4(IWV0i;Jk5v!ZCid0wE{B}Z^}VDp>R;zI|&@+(!0CC-0fS@~)5b;i{3F)b|`qV!ldimIvHGEl+9 z-D->Sbf#n_Ew0{Y$B_NGIP~l*yyra(Dz6G&6uJ6ffcY(9a4LlQQ^;p&Yz&Pple6;* zP8=%8tdL%emq`1HDZ69JhZ)Bj*#OihLK6ge{d`W|2}ra8N_bFYoJX#?qIm$=T*P^T zmBPZpWQvtq;6r){WS};1a3_N3)5CY%{KCFfAFfvM#$M#N0z|l!ZmrYG=s$<%{rWBI zmC?mD_$RLKWbMLBY8?!Qf4KPfkOJVBYz0NKKIK%l0!##Eqy8~5l@ZPo2{3pyCGdbT zTEX^?2zIb|PKsvR%#3FSdwjMZG9UZ-A?wI{MTczx$WY0tk}va&)A|qkh$e_Jpu}W= zcpGa|oHTaN!{8|-qPMA205htBLbVVLg#BgyeN&v8tmFY=nQmv!As;6cdYU_Jt!^k_ zGvWqr(H+?AA?;)WA|TVOf(FJs9+~_-jtvv#$OAjhW{GmW#WiolhKK*@B>4+_Fb0Tc zJ*Ts%(e)g=F7J}~O~*YZ>l6p)zhkIr*CwLOTzdT4ZWN^6q!wQbXJbqRxa(CnXF8K9 z4o~@(Rk%?>B6X^Q)vu_tmAT^6`sZG4FuaBi@~i8VF106W7gg_KKQTU=Irgw+0uAt{ zPS}zYahbqO1YT_yIWWs5&JDpAk{`j;Z?0#)Td;(b#JglQ_Zvkg<^@+1 z{J>8Pj;NRpKZXp6?Fp=r6* ztI}1c6SEB*fzx7nR3v|~0m8ltuy9Qs3-ODn+iyrmgpCxLy09__+`a!%Z7&NIR}|aA z1T`HOwA0QCPRDrzj#u7w1kT+>j7_f?+G{$a>l71dwja}byedpQuTH+sOdy9=MAE^AU zo>5B8Tm_^#{EHd)I|}}WEQhD^J_{oUgIcQeZ4P4;b`^`}dkkt`N#CQkhF1czpTs}C zd)H>gx6ppa<|?{!W%t$Gt3GYu%FPF+l_BGGx&N4!els#U%s*h0``-+i4 zhCg?87(`3wkn{AJhw@uEg}Pe`vj{f|u5xref;3ysF=ywo9P}Q4U;oeUu&cM9XgX^g z+a4cmXh`ZR<$>CWe0A7qsE$Nux~q^jfOF2$qD@^K3>j;9?x)!;%H0^x!tw|T6K}FX zU~y_&)Z7@{hd*wQe6eO`+0aADlgTXa2W8=i&^a8`lX?mQa^|BpI`P_+$GdZmDx43h zHKVm~P#?Z#d9qHL>^TybW;Ct@PFv-aYUXgVY6B&N`1cnr?J~`%Rjhqp z!KpxesQ)*kO5;6kp4P1bMTsL93oKc|D@cC+P}Y$^Okj~=#4M#>%lh|HF46a!EGYUS z2xlb%42|oSQ-pZcAqQru23NI(8xdc%FCsA>t2pP%Kr*_3h7WOA)xZVWQ@f&cJ<)mg zk@C%F=pmVU>8WDTDHFQY$dDifgJ$Xl9UYrvc21TxYy;Gvb%*Hwt&f0}%Z)n08}`6n z*Rx2?t-*+2-n**?v!_vrkO{zC+cNV|$MPJUvDrlV$(qvGh-aYK6;`JgDr%s~=$L*| z%>X~y>;^Ao`o%b5Lz}ucqdBl3YUoFtTLgc`jO2iQ-rl2M-hkziYt-gwAWk;z=0dY2 z)g!Ctsl!S*(E_S;JZJ|g9B&F{7&bfJZO$DmlSl22fPP8&x5Y$PhB8ytB7VN;RuH2d zNEfcMt2bQq(>^~`L%-&0y^*f#=#X)niLYxhO!?`z|Hg<)9ggDrMO5mTO+gz&3RoHP zRE<_6yYISX&j*GrV~px19A5_pgjhCQ`4~U!JXgVKc&;)dIGEsUbZjMQvmv>Lh1cGH--qPfBw7q5*lz9|ad-H5 z-U8mp&=a+s+%7D16?-)!x7hVJ_?O#sSF~w+ni3vuUcvMD%4Al%K1E;a84z+^)7l6h z6AzSBbr{tpaOYV-poww1ZqC(4CA>WqekU^4Q6a1Kv(n;ftETsN-y6(l^hc5QGl;c1 ztm}1&;E*4^an#;FgCu{oXZ;d*1@%|-3Cz~Ut|$IZA33{Ho$`69;4@XTb@|_P;>AX> zAw9o>ebL=AG;6ohFvd4mL@1qbmm4Kc=84gUL{fzgEe83zI>dF}OOoX(O7dyHMRPPa zI%z?rmtr{-Dj$)gj&0G|D}=KL8MZQ&L~ah0s4v>&7S`6fR2|Ab2y@+kOniA;rCI1H zoZ+wD7}c;dAYb>5sx%$N#F>wS z=Y)C1x5f_9j)$w#OAg3ufZdd;7Oh#}2%`b^M3}0`X8>=-=R4v}>R+S2KMr#vp8SGd zO2YG#+B-v|1lUzilkiG*Cm?JyHsh`~*!xD0O}A`pqj|ZOY&4X`XIJl_QXyd{&9myb zxGWU4>hljv6QBYr0@5#{Ktar?8IpQ=1Z!2owP{Fp*))Gh{xH$PS>%{*61~H7IGe13 z-3XTJ`5tN5StlwEM)Dl{RXj9GP%_fXnO;ja94SA{PeK#2^3q|laxG?#<&D1rQUm3; zMim%Yc2vvs3yt;)b~>0Td(_Of0L9{`YK+y##fM%Qo29FPEQ!1JM_c1E3^d+V5yIRm z?idTO@Sv`tN=Ak(Fg9e2Zy3N)z}Io<@15(5A7nPN&7<`Br{Ju>n;G0bslQLiONSjQw>fX7E9~RYPNxnS~|GDKFvemM~|(LTvrZW zD6PPhyCB*9Xb`J-y5&$Kr3GXE7e&Ec5ZQvZJ{Y+;+Q4ZMuuARNCxxx9rJR{l2+*pkbWa*)g+@)1vH%8Kb@tYBrT+7=2t5k$9W8!N2RhfhOE$8~-7~BPZ$@ke? z3JnIu!ECibV1)Cn790^t9gpj{GxDk9K||yH*#32Ec4a){25U7sZES_nxnODaXIc3v z1|+RvH~ucpgs`I)S<$u%4msATp^Jk*Atd#lE59aQB_iD~h#?!QWfI(E@ACb4(7y!t zjaF)fD2KfbuD$ zH^})v?yZNuc@j6J?!g#S1>fRn>*cn=f*Jc25sT)(!X>9yT8uY`Ncdcsfn)iv?4=W6 zmbt)renNh`EmLD{We|Im+5*ox7PnojZ|gq#Wv>a0x@>5NnK@Pt2<1>kmuT7qqx$um z9*-H>1j7VLLU|jEFJ6NP|IlH;n%?Coq~KnQ00baL=4_lph4MhsC*gBLH!1n)^H#2u z7oD&4=qtMOdo+o~Q510y#s~KaeV_x@l7U*sVLj6FSLran)XY`MLM!36?NA;Y zd0zFHTQTT%M#6wpevI`Z8{=@H$?Xq=dgRgcIuBE-C+75O|E0sBzgJ4%iejqXkM^A` zD@y)E*A6Pe-ub6m%+A=9u^-4XgX9}${wa;!gt;I$h769yQ8$JNeE3HF3TpmSMH?Rh zeZ$=I&|nFmlwTLgrHYW3X*_3oON%z9#ctKQAS=0m?&qIBU;ke1PG5De#apvA9~V6a z(gP`{qR@2z`_M@t$63vMidhu0=^C5%bf)vOL-uqC5W6W?hZAcPCezP4w`dZg7NB{B zI4`q+|9f|B)vXH;IX0+zB54) zFRQZi5&z^;qKB&S$qrmPMIoWJ#U!3sUfIC3vZu!V zknN;Y(HB0!nq^XrfJg-2x-K}cF-NW_UZu#_yC~?no0MV9j5%RZ-A+eylLqBVWv^ zB#mAmlI>D0n4vEOylU`gcVbts4Lv79CXQ=2MK>N)mlrw=rbQ^Lngpm)E$}p13Vkg? zKM|V=0brlU59tICe8StL6})xd{-?c*)R-Fd>6O%vRrZSYk4i=29;WYK+YG+&csuv- z6bI6G`)JLb(IHlQ7826PM9^vEC9`bEI^bhWe71`RHUH!I+NS!8T1`+ru~0x;^^sr& z`x59&N56p2<>Uk7?kZ;L%8%n7+@}rq8Z%$+H8G=dh?!<-L$a{&v(RGoA?0VsL{c^+ z{0w8N=vvw_+vluzPkWhu*Ta753TA`MGoMKnRnY^Y{Y8zG(REH&o-xm&eG#YJ^Ci53 z^9`7`bV-f^2{#P-aa#jD8}mo;Y1(olj~*~}5{5uTTWBPU0e_X&>8n@Sn1t`7WWIYpjnJlzrMRg_88>T9;=Qu)Yr^~d@-Y=xb_ofd2Rumkbd zUJ@WZe$LN9Q{;N{O7?|@Yqm1<7JyGaV#nSGXauT8MZjzCYT^Ljtk3${F-TCA&A3Tf zxIm3TeZbmCITt1of@)hy-;Y%z`pbRmsjXi9?1rU->8=;N2@8(7?xD;i95xnjTo{m){qs*1 z^JjU)!6SjIHT=`hj`w3D{h(>(^GWVdnveII5bok%2EbI4yK+vwT1}b{2JuxF(?UQ6}3`^@4foIjY)oV4w7Y67|F?E*w;tz(NWr z-f3)tpuKU9U1gtbe249A*pm#gT#|MR-CDh85 zK>eNi77=a5@)PoLiua1L;PQ$nzYa)yUY|%Xyx!i!20p=VIf^FN*(+qjRU&+GJm?pV zq8A#RQ(n7&n)%!b^Xm+Cs;5ERjk058sJm(k7+Rm6f=qEWdS!3cc5Qhb$8RE4fQ?=k zrx7@8rh^L2LIRE~@e9+Mc}CL%MZj7A%xQu1 z!ok3k&7+ZCUbYx!S@^UV$5@!BSQeUKJ8KUuqp!LpIOl~!NTA6rqQw!yU1x>93?G=b zs`S&BGih*3a(1{EUAuXMq>-iUIt=~elAbz-p>cfKK8ST>GrP+u^kuN7GOvqA$H9}O zW(E+89_0dpqPk}J1x=I|W~6qpx3XprNU z^e&P&L{^XT{=gr2T#hX--%0QzFyLy_2A8C_e2c?rX^oP=GqC&wj>4ULX^ICEKJZTh zt}O#V^@-OhWOfVD6swo~1FfboN69bW6K=cSU-F5HAl9@rv8GrMT8C9?f_E=V`bP`j ztn_3fueeNIt?lrcc&|`Z+wQD#g(sfE_K@9ohhlj@OJ&$zJ z8Lc!W;fuw-Fc(?%0?x)lIp3>C1o&fF9jSum_EZaD2Rk7Jvhqk#;_i>tO2@g2rS_s7 zA}7_j!Bq(JKb>>5uex?t!e@4X-a!|=1&X>C)sx1MyV@P--k|4h?11t~fgqiaW&%GD zh}cES5-1VQirb)Sfn^+TZ~{TKZqYNWlTjLP%9)3awAo;?sJGucNapW5CRTx$o3vM@%`@o`2LdvJK&|n7U*MvLcTXIawMU?5KZC zRqIZD*fC6~$Hgi5wJy+M`x`8Z3Cg3mwpZLTnm-5*#hj~q;$RN?csF|2yPKSDgayII|^CO z6tD#49SW}#k$VG2s9+pfwsJA|yDs&Wh~6)nkoKMN>_>AZ16j-ZP%@?E=AA(*S-q?0 zGthM~45 z7*M<#KPZ-wx(&ZRq4-%dbrvWuN(`>Fh$CwM!fR*aIeG+?tVh7^BV|Z0_Gdlk1J1wg z^neT!d^Z0KNR_m5qLtF`O|B_@csTs9d05cq=jnsM^9@xF8zP6}L^&%wh<&AyewmMR z-28`NZ>((jSMA;x5cl@Hl05litxkLP9Lv~j%ZaaV9~UPsdCbG%PG0(5KsLwDtb-dO zp*6q>GJ#vye5B|bp^U0d?Yns3nHs}WxW?KWUtJyjk40LM5%m6~v!|Weg&a18BQVoY|DKP5!AjwGjLpi;*BNh57raMwDY3HUs2!`Q`{V%-n@$;yA2B;H!@wD zN6Ts+-ys6rq-l`X%egJ0Tiu-I?oz(ZKXjul{|*nvI{tmQ&l<{8QkULHiku=RVEd$a zRwsFp$)!}2|6cH@uU%p`Cfyu=7e{>>&}A=twg~C87=M+NaTZcAx6^RCtfr4~<|yh5 zxM+Tx%`m9y($999&edy>-S{V1OrFXEI-4&(Te^BNu{ez~Vb>=D!08Q!PKwz_7#cIN zaH@pyZ|l>`Mm36k6LwmVRu`G>g7j%I_rh%iX^p>uqJ6OTNG!Z|>LhN&{_ykK2SiC{E=8r?snKand5D*&j-361+kP0C1F zT}4%nBGmKt4pq?7pISfXcIptPhU#a`_tU8H8vyEqnyx*!&=w8g5yEPfIwGb^qUB()A z;d}L0hS*LE-yh1pce_Ob2gEJH^Comc0W{iJ>7aBayFs=xnf`Dwq2?l|tT0vEk*T%P z;BrKX30$r5{;YEXoXapkuVP@rLHfGkQ0IenA}>ueTYEwL*#H($YhQ%}?ms+b6RT9a zzgFlLn^_S3LgScVi{Hn`Wu>X-kA8OHgR@hP0CAi5{6_mtZR;L0W)vY@u=xX1SZsR~ zFdnQU3Wv$0q0=b=DYy^fHx zmrD6M3nkJDZ?*1I5x=g%H1yuaFGNAW-XEe26gYF{hA9-q7iOP{XrRB~$`6*?kbxN7 zjf0cgkA7Dag?!|^q%=Ic#v%+sP}#A{jc5bFw9YM^z&$AHHK>&MMBT3D2O=ec&_Apjr&pZpfwOS z9Tq}4vqhkpFt*pP5gq=0MA#>(9Pk}tM$B_Cz$OitZU*6~n5gkpSI6;dO8s|GINZh! zD+xQ`{gL}il+Ntc{U$nzUQ(b#HlWtoK;?;!Oe_bI|8q5F^&!TC{C^N%1yU;DKA4M$ z+Q}<&UE!77*LX8W)8%kI(EU<}!P&GtWs6kv2jR2zjNcwDp5@zSS&R<|s0;(YYC+!$ zN0H~|>H^i{QU~nh7G0{CB*P|BR;$W)coUsy#AdvP%nt~R3XM2cCGBkbZIzL^A3dc2=M zWEpw%x-tFTxZ`ny>PUe?K$p+or}!HBWWUy7oShr_1a^fJt(*_)>1h+AtIDsmPANn} z#ddJ&mg-BFieWKhq8j-`Xp#?C+IQa5+blu*0S6(audP7o@eiu$Ri9hj|4h+nA?5+r z`Yt~j+u{69fuu%_O>Qr&C49PM>Z(nv~32GgV9I74y!UEz7Hxe7{HTOI=q${m}1k*B~4nr}I zJ!N$MDjf&1j0`ynf1Uk`q58IypMPAKY5!&@Sxp}WN&vC0c)kynie;l4G6E0QlMOpJ zb$W3R$El%X%!|WGSgBr`4ay!aI}aDH3uHymd^?WaOTIS9)H`j&@<#AD>=UEKa{Oo) zwY~Dol)r1;U63ll&{*j?Q@}S)a=uZSKUlEZC=LM?uDBOliM0BxHxBH?)dqQg6U3i+ zgw9mKSe;r|y(?z#?>;py{F+Hs47^LwC{P=C%$1LblfCm(>bpC1KbOBE%5+i7xmA*JbLBPF4f@U9EurlgR4YA}R%nljlFDDhnr!9gO!!*hk-x_r? zdk?*%CBvYH*()Qp8y`nQ{XRcz9nOZUGo@LFCDoKHo2osOU){E!+SeEY6*dX&&Jl3=c(n!jS5V0ZUp*<^O10wCQGd+X z4b7URJ9gbw0W+zd;vPF%^5l@98))BjWX9uL=DOyq@5R59{HYdNBh{Cd?|LI)2dS>#2^TkH_f`qYek(8uPKkgMghg+lvsoY&lKtug@OvOptlJ zM~Wg1ec=6?3KFCovIMl1Cd=ls^{-VMX1ONxBG}Rbz&Ch8Z%cI&R8on;_!765@M>(Q z${{Q9_r&vv^C8Tz=Y1eR2yfFDW0or!8%+X0foWg*zoA1O#!Ialvc5UbH6(em`;HX7 z*k{Dh5VsBR%TzQA$C_TPPSdYwt(Y;ltnKs&+rh^gm3Q`_0gPjnRKkb``-0Q2d#!-P z2{rqu6r!WuhJ9lHsW)5PIGGsDNr|%fy>$7P_Z-7GZYc?e=|F4M-Ry05WEDNF_aC6@ z5-);YwslnL^Jv^!xw1I;%RXR?;)$(dkLZ-mrz`i*k>(m>>^AIY!h2<@-sy|9Zv>M&ZWnJOqtQ& zTaV67;Rv>aIMWWG?N-o^ed~y6K8{HKTQ(OsLyf`mr!vhEX(t2H#k}mXz+nLc!5RD6 z`JR|Wb8`#n^h%wr`tI7QO7DXyP)p;_<8W;03M1_bJ)+F}UwCDDFO42TqRA$cU8Nl(|^*Y+UqngFL3ujHllt+V~ zD_1tbZ8e`NVYVim3E7Mx9j+j z-p(BU2i4Edw>Sc`-yNW@98R{+S;CJ0y!o%)c{>0ZT6HAy+GiQ)celXeQzB05Fl5&KdfXLq6 zc`gHxTf&}X6Juea*dg>*$o?3}eyOv4_qWf9$K1GN$Y)}(d?)Oj#!o~%Ay4kiVX1lpt4IltPsUlURvS^*$$uPP*Pm-2LokCPZBWIJ$o5I5-=+otO)sL-F%k}SZy%T{)d65(^Y zuc_d_Kb`eD|0zQuPCDzgwIvQOacs~|iu3Nh;c7PI3#+rHj^G&2%BcFhhamp&#=*@; z3|T)R1z(*C6BSaY+_rr3Z^?1d^)Ql)>5;q664EDwI#)t-pCQ1WwUmpSpdF&vUW(LYNaFeP7Nu z;HEIdx%>jh31DXiJvLYoRg_I-U;IFYLy=MPKC{Yg0Y=OR3h2XpC|S#Z<cMEntIGf% z@iw4}PP+f~?+WsJvWCDrQ6R;5az72L1vhZ1YV1&JrAl64BYng?%6fAAAH+@^=51$i zeqFpz{6d4x#!bekgWbgOzD5uSLz<>dGa03D#ykgSPAI)!D6oKgE2jl+ebGpiCK~!2 zO+fLG&VIO@X=?1fkrVU}c5pA}0pK4}r1B_rdpUW_UpdC{^Vif@i9dm+V(3e~R)!pq zy>!2_MYz!-T#X`fceT_9+|)3T$13_TR4rW5&al=J!DHvTqz4kXKRsjNYph5urFWdC zR&UzweG!K`{l;=l6l8c@zvKYXI?q-7{m(L4aE7uKXNgU7t_l}cnT^pv{-sB-)$9}t-BRA08&|Be-HnR&wG zbBDvEot2iO`!@UK#GF--A@#zunJ}uM2bGZ2-2 zVBdKCkJ>UzpmQLRy`k3Tz?<$#qLQTBh|_tVf_U6>*re{ROS+= zAg3KDFb&cRv`juzYqlbf<$WXT)>=GOZ1BS`Ho6)>j^1k|4hy;OXJHY!apfreW3!N- zjOYD*cz{@an+bJWT#(`%_?XH$vqbtk_0*fx3qg{cfK>@en&q5wckO-F^Z1r{VWQx;Y)Qyq0Z z>3XD+IXZfK%Xb+HF!!%6F!Mjk7?n&k0t{i_GF5vaV*MU{9ur+ebscZX`D;(fpxgA> zC9E!6t{PQbNx(>eaaoee%jJ_~%-CxTx^+|xi=?i?cz+EIjg>8JuS>_?S5|I{Lt8Hs zdGQId|QnF0psvuCG;VlS=e#wftroh-TUmUCQ9l82%$*KcXouYdn z9m1qQQG@Y~B78}^?8Bumz2>w_fn$AC{G?P!UoXf&?I#4Z(D1$!J|wDQUAITy&OA_n zKj&zY_QCC&>zMgpjcVI8ZaY_ovL)NAUECPCy5}+o02ZjiN-871)0Ysgk$s#pjK=>E zK%9*Rw#e6<2?jj5wI)7NkG68h!ur_9nvpgyo{o}C`937@;8Ny*i%1T_O!1Z0LCT3u zp$yge_A?EpO{QAajr)*9`f>Q%o}Q&Pu8xDteeXJfaXqR$ZWd7B!Xge$xWLI`jVNe9 z`1Rg3jmOc?_~HH82cdC=qF}z|(|xlGr{)X+b*kh-GYSB<|7OtEWLYC!ZV#p9k`Zaq z4FVZG$n(Ftu*Nv zq*T#qS|?DDCuu~|*7-s&yPMxmS}_abI$`6LcEz4Yxf4T{gho9CQAHHry z{WwcqLJ?5ge>2txXASEde`FkE0pr_uYkvtVB0kqK$Ps@%jl$@ZGfin9Zi21HRBB68 z<4;475XC}!47XY5hFZ)I;Pvard4l(Cs!c~Ic5ep5CCx-~JfbhJ;0J?*i?FE~>s zf&ntGO$OH*pZtg~swo_=v`e;8I>u}kjw}ls#*c3m74EK*@;DY$7$z^JegZ8Xla{P> z!q$r%-%E##ydTL)pr8t<9QmS`9_TZ*A{@Lo2hO8|#FAg#HVac9+onPlL_TJ z^vRt9IXO!dra0)zO~wab=LK(a1N#co(Cs?GsbZMyxjGe~rar^!wc-^}C8dQb-3?2Y z(r)szceu=26p0yTA|}}dWi|5l;{-0+%=e1U{Vh}*IB@w-x^b>>8t-eo@@&HwL)3M-JQ7NS9#<&xbKsf%A@+mbn&G(peG~C%rwlP+Y;y7UCrw1@E1OrE^j{2yWhY?+x_xIz(Kcu zcW&<{k0xLxo&KPYV~;s%lJoB@kU_F{m$p%u53_t!vixw{u)xZPCNqoZd+g*uBXvJy zXL5WLh6feIpWCLx9LIzqk~ydUBq-^eV|$H$rIM)|#_;GcjMk2UUY4rKe<{_6*15=Z zv?>O2cZuN#r6LgGsoehitG(yK5wGVQZ-+V@xc-iAgCCem60Y5EzG$*cu$dEd$AEP15A zZt%2&O#Dt=o*lZf%U3TZp67J?yHRmFmbMnl%69-CI*Hps&e8O{UdsHd%s_r~-XbRI ze;B(oVe4V+mI4rA37_PI zJ6Rczq!>C$tQ2d*GZ|S-7Mk7c8aOH~dQI8wGdT^=sLU^D-e+yL@|a~LbQR8@Ppe;1 zxPJoF_|X~G#RZk|XI+E+NghL|-Xtyf)%}e5jM%dvp-|kWfoO$(VIR%EaX;!UyrjhN z4F1(t@28y`poczS=&1Bzf%4)3`E~U?&aH@{6S+477pwbOE(lccNn|U}D&L-enzGoD z4!Eb0(9Ou+^_OeL(Auq7KkwcmD3$v+NF-FjK8t=CTK;LFdP!j{@TsYG>N9tHm334j z$F&rW1pjOjpv*E%8-9NSb8H~$rQ7~P$CK*te3jM8S^SiN%eG1L|Ym@8TRcH45M8&nStnq#^aDl;~S2+!cD zg)X)w_LVM5!ey3gzPqp+ z_xj3| RyDUBX+8^jVh_!u4PvYi}|BT5~S`%@AJ(l zbgHY{iovAQD4tw;{S2Z(Ev7{1g&Dn0z^YBb% z?gB24-Z-}#s;Th3@|*~63=K|nM{~GPcYf!wFz(X4uWw|h4S?8D=$XcY+)9bfMcxx_ zafEl9k|7rVAg88ulqj^!acdbsXcar$vaK0?I_kVzk1S@d_p=pV?A=-cdLw2Qr3Ct>peeX#DqK*f4g z1f`N7F{@@7aPS5l2QSEhcLLe!l1HQ>2Zka*C(pWIDa z76h))z)xTIiD~hmtUB=mXF}Y-Ber^X{apGNuPK049xj?9$j{#)7oPAPM*`RgR-S!N z%O?DGMVv5!tdKzw(2p>j{JLQ@SfKqheK` zy}n39jmsXT-`A4B71gA4hzEN!U=2c15N$JP@?n8f32LK9^G`c&jVdwEiZCO_N%$|0 zSLXd6MD6w8QXisAa{OB5n;kX9$NFv;ZoB1{2tVA+u%5j>5r_-oWkTTwh`dQIHN}K^ zYW84q;_Rq>!XafijAe`N^uRLgU>x#i}wS}EXcsq{!-~hs?V$vP9tJR zTETC5ywPt?hek{ljPza*=HKM5#UwtXRMt1?yAvjqouGd)q|RT?=!g>zShL! z2ouc6zt_yk>$JrPAaS=>?z{zGQ2;nN)p3s>z;8J#;iE5KBJ8j!+|;87>^pN70Hsmw&vW=XH27|txY1&8iWMD(r(Y3!w<48k^vj3Q z_u!8WZ_W9h|C7aXT8M)|%=;L8} zDZnXK`E<>xr__K0DBtw@w%9NGa>wFlFW2`o*&g5mk9T)T&b{r+ANRZ2?%pb4vS1SM zX!~92pHFPO)N^LX*W0W8*Y4txmkHWl_Srfwx90WPsr6Sk>hCw7HVrtCIDP(4 zyZU4a6^4p{skTjeE;F8`*YK|+`r()`|jVj$69+Y zWErfH{F}o&Tleve$oP4a#HY$Izhd~WxW9J!ibo}uv*%Z>T(m($-R|U*XV3dU-7f|P zheeY(r>$gUXmDY55@lde(E_e5WMBZgk(GhLBLuki6{vz~Ar}LK5O6CL15g=o6);Sh zLLg|>9z!P=#2_W0VyLnSD}a}t!^{NrB0y$>m7$mkYOI6IM6w!cCa6&XavYM?5Hq1J zM6nuTCN`^K-odmQ<{eC{Vcx;88tP6gRzuy1;%kW23W31O{IG^3@Gu=jfTNiSJck8q zNUCTxAOajEB!z&-n8E`bW;HZ+Fd`A;&PBjOQ?P0RySI}IizbkJfngv3j7%)<1%|<$ dxBoc}7^F`##szzwbOA;=gQu&X%Q~loCIBh|33LDe diff --git a/kellys_daily_report/static/src/css/kellys_daily_report.css b/kellys_daily_report/static/src/css/kellys_daily_report.css deleted file mode 100644 index 1a2def628..000000000 --- a/kellys_daily_report/static/src/css/kellys_daily_report.css +++ /dev/null @@ -1 +0,0 @@ -.o_datepicker {max-width: 9.5em} diff --git a/kellys_daily_report/views/kellysnames.xml b/kellys_daily_report/views/kellysnames.xml deleted file mode 100644 index 3676787f5..000000000 --- a/kellys_daily_report/views/kellysnames.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - kellysnames - kellysnames.tree.view - - - - - - - - - - diff --git a/kellys_daily_report/wizard/__init__.py b/kellys_daily_report/wizard/__init__.py deleted file mode 100644 index a3dfd1cbe..000000000 --- a/kellys_daily_report/wizard/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2018 Alexandre Díaz -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from . import kellys_daily_pdf -from . import kellys_daily_rooms diff --git a/kellys_daily_report/wizard/kellys_daily_pdf.py b/kellys_daily_report/wizard/kellys_daily_pdf.py deleted file mode 100644 index 9a7eb33eb..000000000 --- a/kellys_daily_report/wizard/kellys_daily_pdf.py +++ /dev/null @@ -1,123 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Odoo, Open Source Management Solution -# Copyright (C) 2018-2019 Jose Luis Algara Toledo -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -############################################################################## -from datetime import datetime, date -from odoo import api, fields, models - - -class KellysWizard(models.TransientModel): - _name = 'kellysreport' - - @api.model - def _get_default_date(self): - return date.today() - - def _get_default_habitaciones(self): - return self.calculalimpiar(datetime.now()) - - date_start = fields.Date("Fecha del listado", default=_get_default_date) - habitaciones = fields.Many2many('kellysrooms', string="Limpieza:", - default=_get_default_habitaciones) - order = fields.Selection([ - ('kelly ASC', 'Calendario'), - ('kelly ASC, tipo ASC', 'Limpiar como... y orden en el Calendario'), - ('kelly ASC, tipo ASC, checkin ASC', - 'Limpiar como... y Hora de entrada'), - ], 'Orden de impresión', - default='kelly ASC, tipo ASC, checkin ASC', - required=True, - help='Establece el orden en el que se imprimira el listado') - - - def calculate_report(self): - self.habitaciones = self.calculalimpiar( - datetime.strptime(self.date_start, "%Y-%m-%d")) - return - - - def calculalimpiar(self, fechalimpieza=datetime.now()): - dates = datetime.strftime(fechalimpieza, "%Y-%m-%d") - grids = self.env['hotel.room'].search([], order='sequence ASC') - grids2 = self.env['kellysrooms'] - listid = [] - for x in grids: - rooms = self.env['hotel.reservation'].search( - ['&', '&', ('checkin', '<=', dates), - ('checkout', '>=', dates), - ('state', '<>', 'cancelled'), - ('room_id', '=', x.id) - ], order='checkin ASC') - - tipos = False - if len(rooms) != 0: - if len(rooms) == 2: - tipos = 1 - # Salida y etrada - checkinhour = rooms[1].checkin - checkouthour = rooms[1].checkout[:10] - else: - if rooms[0].checkin[:10] == dates: - checkinhour = rooms[0].checkin - checkouthour = rooms[0].checkout[:10] - tipos = 3 - # Revisar - elif rooms[0].checkout[:10] == dates: - checkinhour = 'no prevista' - checkouthour = '' - tipos = 1 - # Salida - else: - checkinhour = rooms[0].checkin[:10] - checkouthour = rooms[0].checkout[:10] - tipos = 2 - # Cliente - if rooms[0].reservation_type == 'staff': - checkinhour = rooms[0].checkin[:10] - checkouthour = rooms[0].checkout[:10] - tipos = 4 - # Staff - if rooms[0].reservation_type == 'out': - checkinhour = rooms[0].checkin[:10] - checkouthour = rooms[0].checkout[:10] - tipos = 5 - # Averiada - if tipos is not False: - listid.append(grids2.create( - {'habitacion': rooms[0].room_id.name, - 'habitacionid': rooms[0].room_id.id, - 'tipo': tipos, - 'notas': '', - 'checkin': checkinhour, - # 'checkin': rooms[0].checkin[:10], - # 'checkout': rooms[0].checkout[:10], - 'checkout': checkouthour, - # 'kelly': 5, - 'clean_date': fechalimpieza - }).id) - return self.env['kellysrooms'].search([('id', 'in', listid)]) - - - def print_rooms_report(self): - rooms = self.env['kellysrooms'].search([('id', 'in', - self.habitaciones.ids)], - order=self.order) - - return self.env.ref( - 'kellys_daily_report.report_kellysrooms').report_action(rooms) diff --git a/kellys_daily_report/wizard/kellys_daily_pdf.xml b/kellys_daily_report/wizard/kellys_daily_pdf.xml deleted file mode 100644 index 458fcc17b..000000000 --- a/kellys_daily_report/wizard/kellys_daily_pdf.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - kellys_daily_report_view - kellysreport - -

      - - - -
      ]]> - - - + + + diff --git a/pms/data/email_template_exit.xml b/pms/data/email_template_exit.xml index 349f259ea..4aeec1d78 100644 --- a/pms/data/email_template_exit.xml +++ b/pms/data/email_template_exit.xml @@ -1,15 +1,19 @@ - + Exit Reservation-Send by Email - Gracias por alojarse con nosotros en ${object.company_id.property_name} + Gracias por alojarse con nosotros en ${object.company_id.property_name} ${(object.partner_id.id or '')} - + - /*Global Styles*/ + /*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;} diff --git a/pms/data/email_template_reserv.xml b/pms/data/email_template_reserv.xml index 991b46a50..24a9ed47d 100644 --- a/pms/data/email_template_reserv.xml +++ b/pms/data/email_template_reserv.xml @@ -1,15 +1,19 @@ - + - - - - Confirm Reservation-Send by Email - Confirmación de los detalles de su reserva en ${object.company_id.property_name} - ${(object.partner_id.id or '')} - - - + + + Confirm Reservation-Send by Email + Confirmación de los detalles de su reserva en ${object.company_id.property_name} + ${(object.partner_id.id or '')} + + + /*Global Styles*/ .marco { @@ -1037,6 +1041,6 @@
      ]]> - - + + diff --git a/pms/data/menus.xml b/pms/data/menus.xml index 25fce53b3..89ce00b3c 100644 --- a/pms/data/menus.xml +++ b/pms/data/menus.xml @@ -1,27 +1,44 @@ - + - - - - - - - - - - - - + + + + + - + sequence="10" + action="action_pms_massive_change" + /> diff --git a/pms/data/pms_data.xml b/pms/data/pms_data.xml index 99e1e9499..ecae390d8 100644 --- a/pms/data/pms_data.xml +++ b/pms/data/pms_data.xml @@ -1,42 +1,35 @@ - + - - - + - - + - - - + Restriction Plan - My Property - - - + + + Rua Street Demo, s/n Commitsun city - + 15703 +34 123 456 879 commitsun@hootel.com https://www.commitsun.com - - - - - + + + + diff --git a/pms/data/pms_sequence.xml b/pms/data/pms_sequence.xml index d0cf5f98c..952b46c00 100644 --- a/pms/data/pms_sequence.xml +++ b/pms/data/pms_sequence.xml @@ -1,7 +1,6 @@ - + - PMS Folio @@ -9,6 +8,5 @@ F/ 5 - diff --git a/pms/demo/pms_demo.xml b/pms/demo/pms_demo.xml index 7c01ff3eb..97d3022a6 100644 --- a/pms/demo/pms_demo.xml +++ b/pms/demo/pms_demo.xml @@ -1,9 +1,7 @@ - + - - - + Ground Floor @@ -13,9 +11,7 @@ Second Floor - - - + Toiletries @@ -25,146 +21,141 @@ Kitchen facilities - - - + Shampoo and Soap - + High-quality Shampoo and Soap Essential Herbs - + Hair Dryer - + High speed Wired Internet access - + Wi-Fi - + Microwave oven - + Half-sized Refrigerator - + - - - + Room Conference - - - + Economic ECO 21.00 - - - + + Single SNG 20.00 - - + + Double DBL 25.00 - - + + Triple TRP 35.00 - - + + - Conference Room CFR 80.00 - - + + - - - + Economic-101 - - + + 2 Single-101 - - + + 1 Single-102 - - + + 1 Single-103 - - + + 1 Double-201 - - + + 2 1 Double-202 - - + + 2 Triple-203 - - + + 3 Open Talk Away Room - - + + 1 - - - + Breakfast Buffet 5.0 @@ -173,7 +164,6 @@ True True - Extra Bed 15.0 @@ -185,7 +175,6 @@ True True - Late Check-out 10.0 @@ -194,7 +183,6 @@ False False - Lunch 15.0 @@ -203,7 +191,6 @@ True True - Dinner 20.0 @@ -212,7 +199,6 @@ True True - Free Bar 40.0 @@ -221,116 +207,94 @@ True True - - - + BreakFast - + 'amount': 3})]" + /> fixed - Half Board - + ]" + /> fixed - FullBoard - + ]" + /> fixed - - - + - - - - + + + fixed - - - - + + + fixed - - - - - + + + + fixed - - - - - - + + + + fixed - - - - + + + fixed - - - + Maintenance - Used for closing of rooms which require a maintenance. You can specify the reason in the own reservation. + Used for closing of rooms which require a maintenance. You can specify the reason in the own reservation. - VIP Privacy - Used for closing of rooms for extra privacy. + Used for closing of rooms for extra privacy. - - - + - - + + })]" + /> - - + + })]" + /> - - + + })]" + /> - - + + })]" + /> - + out - + })]" + /> - Restriction Plan Demo - My pms Demo - - - + + + - - - + Economic ECO 21.00 - - - + + - + Single SNG 20.00 - - + + - diff --git a/pms/models/inherited_account_move.py b/pms/models/inherited_account_move.py index c50f80f61..71040d312 100644 --- a/pms/models/inherited_account_move.py +++ b/pms/models/inherited_account_move.py @@ -1,26 +1,25 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import models, fields, api, _ import json + +from odoo import _, api, fields, models from odoo.tools import float_is_zero class AccountMove(models.Model): - _inherit = 'account.move' + _inherit = "account.move" # Field Declarations folio_ids = fields.Many2many( - comodel_name='pms.folio', - compute='_computed_folio_origin') - pms_property_id = fields.Many2one( - 'pms.property') - from_folio = fields.Boolean( - compute='_computed_folio_origin') + comodel_name="pms.folio", compute="_computed_folio_origin" + ) + pms_property_id = fields.Many2one("pms.property") + from_folio = fields.Boolean(compute="_computed_folio_origin") outstanding_folios_debits_widget = fields.Text( - compute='_get_outstanding_folios_JSON') - has_folios_outstanding = fields.Boolean( - compute='_get_outstanding_folios_JSON') + compute="_get_outstanding_folios_JSON" + ) + has_folios_outstanding = fields.Boolean(compute="_get_outstanding_folios_JSON") # Compute and Search methods @@ -28,8 +27,8 @@ class AccountMove(models.Model): for inv in self: inv.from_folio = False inv.folio_ids = False - folios = inv.mapped('invoice_line_ids.reservation_ids.folio_id') - folios |= inv.mapped('invoice_line_ids.service_ids.folio_id') + folios = inv.mapped("invoice_line_ids.reservation_ids.folio_id") + folios |= inv.mapped("invoice_line_ids.service_ids.folio_id") if folios: inv.from_folio = True inv.folio_ids = [(6, 0, folios.ids)] @@ -38,21 +37,19 @@ class AccountMove(models.Model): def action_folio_payments(self): self.ensure_one() - sales = self.mapped('invoice_line_ids.sale_line_ids.order_id') - folios = self.env['pms.folio'].search([ - ('order_id.id', 'in', sales.ids) - ]) - payments_obj = self.env['account.payment'] - payments = payments_obj.search([('folio_id', 'in', folios.ids)]) - payment_ids = payments.mapped('id') - return{ - 'name': _('Payments'), - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'account.payment', - 'target': 'new', - 'type': 'ir.actions.act_window', - 'domain': [('id', 'in', payment_ids)], + sales = self.mapped("invoice_line_ids.sale_line_ids.order_id") + folios = self.env["pms.folio"].search([("order_id.id", "in", sales.ids)]) + payments_obj = self.env["account.payment"] + payments = payments_obj.search([("folio_id", "in", folios.ids)]) + payment_ids = payments.mapped("id") + return { + "name": _("Payments"), + "view_type": "form", + "view_mode": "tree,form", + "res_model": "account.payment", + "target": "new", + "type": "ir.actions.act_window", + "domain": [("id", "in", payment_ids)], } # Business methods @@ -60,62 +57,68 @@ class AccountMove(models.Model): self.ensure_one() self.outstanding_folios_debits_widget = json.dumps(False) if self.from_folio: - payment_ids = self.folio_ids.mapped('payment_ids.id') - if self.state == 'open': - account_partner = self.env['res.partner'].\ - _find_accounting_partner(self.partner_id).id + payment_ids = self.folio_ids.mapped("payment_ids.id") + if self.state == "open": + account_partner = ( + self.env["res.partner"]._find_accounting_partner(self.partner_id).id + ) domain = [ - ('account_id', '=', self.account_id.id), - ('partner_id', '!=', account_partner), - ('reconciled', '=', False), - ('payment_id', 'in', payment_ids), - '|', '&', - ('amount_residual_currency', '!=', 0.0), - ('currency_id', '!=', None), - '&', ('amount_residual_currency', '=', 0.0), - '&', ('currency_id', '=', None), - ('amount_residual', '!=', 0.0)] - if self.type in ('out_invoice', 'in_refund'): - domain.extend([('credit', '>', 0), ('debit', '=', 0)]) - type_payment = _('Outstanding credits in Folio') + ("account_id", "=", self.account_id.id), + ("partner_id", "!=", account_partner), + ("reconciled", "=", False), + ("payment_id", "in", payment_ids), + "|", + "&", + ("amount_residual_currency", "!=", 0.0), + ("currency_id", "!=", None), + "&", + ("amount_residual_currency", "=", 0.0), + "&", + ("currency_id", "=", None), + ("amount_residual", "!=", 0.0), + ] + if self.type in ("out_invoice", "in_refund"): + domain.extend([("credit", ">", 0), ("debit", "=", 0)]) + type_payment = _("Outstanding credits in Folio") else: - domain.extend([('credit', '=', 0), ('debit', '>', 0)]) - type_payment = _('Outstanding debits') - info = {'title': '', - 'outstanding': True, - 'content': [], - 'move_id': self.id} - lines = self.env['account.move.line'].search(domain) + domain.extend([("credit", "=", 0), ("debit", ">", 0)]) + type_payment = _("Outstanding debits") + info = { + "title": "", + "outstanding": True, + "content": [], + "move_id": self.id, + } + lines = self.env["account.move.line"].search(domain) currency_id = self.currency_id if len(lines) != 0: for line in lines: # get the outstanding residual value in inv. currency - if line.currency_id and line.currency_id == \ - self.currency_id: + if line.currency_id and line.currency_id == self.currency_id: amount_to_show = abs(line.amount_residual_currency) else: - amount_to_show = line.company_id.currency_id.\ - with_context(date=line.date).\ - compute(abs(line.amount_residual), - self.currency_id) + amount_to_show = line.company_id.currency_id.with_context( + date=line.date + ).compute(abs(line.amount_residual), self.currency_id) if float_is_zero( - amount_to_show, - precision_rounding=self.currency_id.rounding - ): + amount_to_show, precision_rounding=self.currency_id.rounding + ): continue if line.ref: - title = '%s : %s' % (line.move_id.name, line.ref) + title = "{} : {}".format(line.move_id.name, line.ref) else: title = line.move_id.name - info['content'].append({ - 'journal_name': line.ref or line.move_id.name, - 'title': title, - 'amount': amount_to_show, - 'currency': currency_id.symbol, - 'id': line.id, - 'position': currency_id.position, - 'digits': [69, self.currency_id.decimal_places], - }) - info['title'] = type_payment + info["content"].append( + { + "journal_name": line.ref or line.move_id.name, + "title": title, + "amount": amount_to_show, + "currency": currency_id.symbol, + "id": line.id, + "position": currency_id.position, + "digits": [69, self.currency_id.decimal_places], + } + ) + info["title"] = type_payment self.outstanding_folios_debits_widget = json.dumps(info) self.has_folio_outstanding = True diff --git a/pms/models/inherited_account_move_line.py b/pms/models/inherited_account_move_line.py index 1800b96ac..82f22403a 100644 --- a/pms/models/inherited_account_move_line.py +++ b/pms/models/inherited_account_move_line.py @@ -5,21 +5,33 @@ from odoo import fields, models class AccountMoveLine(models.Model): - _inherit = 'account.move.line' + _inherit = "account.move.line" # Fields declaration reservation_ids = fields.Many2many( - 'pms.reservation', - 'reservation_move_rel', - 'move_line_id', 'reservation_id', - string='Reservations', readonly=True, copy=False) + "pms.reservation", + "reservation_move_rel", + "move_line_id", + "reservation_id", + string="Reservations", + readonly=True, + copy=False, + ) service_ids = fields.Many2many( - 'pms.service', - 'service_line_move_rel', - 'move_line_id', 'service_id', - string='Services', readonly=True, copy=False) + "pms.service", + "service_line_move_rel", + "move_line_id", + "service_id", + string="Services", + readonly=True, + copy=False, + ) reservation_line_ids = fields.Many2many( - 'pms.reservation.line', - 'reservation_line_move_rel', - 'move_line_id', 'reservation_line_id', - string='Reservation Lines', readonly=True, copy=False) + "pms.reservation.line", + "reservation_line_move_rel", + "move_line_id", + "reservation_line_id", + string="Reservation Lines", + readonly=True, + copy=False, + ) diff --git a/pms/models/inherited_account_payment.py b/pms/models/inherited_account_payment.py index d21ad0555..ba0853f81 100644 --- a/pms/models/inherited_account_payment.py +++ b/pms/models/inherited_account_payment.py @@ -1,49 +1,51 @@ # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import _, api, fields, models from odoo.exceptions import except_orm -from odoo import models, fields, api, _ class AccountPayment(models.Model): - _inherit = 'account.payment' + _inherit = "account.payment" # Fields declaration - folio_id = fields.Many2one( - 'pms.folio', - string='Folio') + folio_id = fields.Many2one("pms.folio", string="Folio") amount_total_folio = fields.Float( - compute="_compute_folio_amount", store=True, - string="Total amount in folio", + compute="_compute_folio_amount", store=True, string="Total amount in folio", ) - save_amount = fields.Monetary(string='onchange_amount') + save_amount = fields.Monetary(string="onchange_amount") save_date = fields.Date() save_journal_id = fields.Integer() # Compute and Search methods - - @api.depends('state') + + @api.depends("state") def _compute_folio_amount(self): # FIXME: Finalize method res = [] fol = () for payment in self: if payment.folio_id: - fol = payment.env['pms.folio'].search([ - ('id', '=', payment.folio_id.id) - ]) + fol = payment.env["pms.folio"].search( + [("id", "=", payment.folio_id.id)] + ) else: return if not any(fol): return if len(fol) > 1: - raise except_orm(_('Warning'), _('This pay is related with \ - more than one Reservation.')) + raise except_orm( + _("Warning"), + _( + "This pay is related with \ + more than one Reservation." + ), + ) else: fol.compute_amount() return res # Constraints and onchanges - @api.onchange('amount', 'payment_date', 'journal_id') + @api.onchange("amount", "payment_date", "journal_id") def onchange_amount(self): if self._origin: self.save_amount = self._origin.amount @@ -52,100 +54,107 @@ class AccountPayment(models.Model): # Action methods """WIP""" - + def return_payment_folio(self): journal = self.journal_id partner = self.partner_id amount = self.amount reference = self.communication - account_move_lines = self.move_line_ids.filtered(lambda x: ( - x.account_id.internal_type == 'receivable')) + account_move_lines = self.move_line_ids.filtered( + lambda x: (x.account_id.internal_type == "receivable") + ) return_line_vals = { - 'move_line_ids': [(6, False, [x.id for x in account_move_lines])], - 'partner_id': partner.id, - 'amount': amount, - 'reference': reference, - } + "move_line_ids": [(6, False, [x.id for x in account_move_lines])], + "partner_id": partner.id, + "amount": amount, + "reference": reference, + } return_vals = { - 'journal_id': journal.id, - 'line_ids': [(0, 0, return_line_vals)], - } - return_pay = self.env['payment.return'].create(return_vals) + "journal_id": journal.id, + "line_ids": [(0, 0, return_line_vals)], + } + return_pay = self.env["payment.return"].create(return_vals) if self.save_amount: self.amount = self.save_amount if self.save_date: self.payment_date = self.save_date if self.save_journal_id: - self.journal_id = self.env['account.journal'].browse( - self.save_journal_id) + self.journal_id = self.env["account.journal"].browse(self.save_journal_id) return_pay.action_confirm() # Business methods - + def modify(self): self.cancel() vals = { - 'journal_id': self.journal_id, - 'partner_id': self.partner_id, - 'amount': self.amount, - 'payment_date': self.payment_date, - 'communication': self.communication, - 'state': 'draft'} + "journal_id": self.journal_id, + "partner_id": self.partner_id, + "amount": self.amount, + "payment_date": self.payment_date, + "communication": self.communication, + "state": "draft", + } self.update(vals) - self.with_context({'ignore_notification_post': True}).post() + self.with_context({"ignore_notification_post": True}).post() self._compute_folio_amount() if self.folio_id: msg = _("Payment %s modified: \n") % (self.communication) if self.save_amount and self.save_amount != self.amount: msg += _("Amount from %s to %s %s \n") % ( - self.save_amount, self.amount, self.currency_id.symbol) + self.save_amount, + self.amount, + self.currency_id.symbol, + ) if self.save_date and self.save_date != self.payment_date: - msg += _("Date from %s to %s \n") % ( - self.save_date, self.payment_date) - if self.save_journal_id and \ - self.save_journal_id != self.journal_id.id: + msg += _("Date from %s to %s \n") % (self.save_date, self.payment_date) + if self.save_journal_id and self.save_journal_id != self.journal_id.id: msg += _("Journal from %s to %s") % ( - self.env['account.journal'].browse( - self.save_journal_id).name, self.journal_id.name) - self.folio_id.message_post(subject=_('Payment'), body=msg) + self.env["account.journal"].browse(self.save_journal_id).name, + self.journal_id.name, + ) + self.folio_id.message_post(subject=_("Payment"), body=msg) - def delete(self): msg = False if self.folio_id: - msg = _("Deleted payment: %s %s ") % ( - self.amount, self.currency_id.symbol) + msg = _("Deleted payment: %s %s ") % (self.amount, self.currency_id.symbol) self.cancel() - self.move_name = '' + self.move_name = "" self.unlink() if msg: - self.folio_id.message_post(subject=_('Payment Deleted'), body=msg) + self.folio_id.message_post(subject=_("Payment Deleted"), body=msg) - def post(self): rec = super(AccountPayment, self).post() if rec and not self._context.get("ignore_notification_post", False): for pay in self: if pay.folio_id: - msg = _("Payment of %s %s registered from %s \ - using %s payment method") % \ - (pay.amount, pay.currency_id.symbol, - pay.communication, pay.journal_id.name) - pay.folio_id.message_post(subject=_('Payment'), body=msg) + msg = ( + _( + "Payment of %s %s registered from %s \ + using %s payment method" + ) + % ( + pay.amount, + pay.currency_id.symbol, + pay.communication, + pay.journal_id.name, + ) + ) + pay.folio_id.message_post(subject=_("Payment"), body=msg) - def modify_payment(self): self.ensure_one() - view_form_id = self.env.ref('pms.account_payment_view_form_folio').id + view_form_id = self.env.ref("pms.account_payment_view_form_folio").id # moves = self.mapped('move_ids.id') - return{ - 'name': _('Payment'), - 'view_type': 'form', - 'views': [(view_form_id, 'form')], - 'view_mode': 'tree,form', - 'res_model': 'account.payment', - 'target': 'new', - 'init_mode': 'edit', - 'type': 'ir.actions.act_window', - 'res_id': self.id, + return { + "name": _("Payment"), + "view_type": "form", + "views": [(view_form_id, "form")], + "view_mode": "tree,form", + "res_model": "account.payment", + "target": "new", + "init_mode": "edit", + "type": "ir.actions.act_window", + "res_id": self.id, } diff --git a/pms/models/inherited_ir_http.py b/pms/models/inherited_ir_http.py index 77caa06aa..5fdf1d9f0 100644 --- a/pms/models/inherited_ir_http.py +++ b/pms/models/inherited_ir_http.py @@ -2,13 +2,13 @@ # Copyright 2019 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, _ -from odoo.http import request +from odoo import _, models from odoo.exceptions import MissingError +from odoo.http import request class IrHttp(models.AbstractModel): - _inherit = 'ir.http' + _inherit = "ir.http" def session_info(self): res = super().session_info() @@ -16,21 +16,33 @@ class IrHttp(models.AbstractModel): display_switch_pms_menu = len(user.pms_property_ids) > 1 # TODO: limit properties to the current company? # or switch company automatically - res['pms_property_id'] = request.env.user.pms_property_id.id if \ - request.session.uid else None - res['user_properties'] = { - 'current_property': (user.pms_property_id.id, user.pms_property_id.name), - 'allowed_properties': [ - (property.id, property.name) for property in user.pms_property_ids - ] - } if display_switch_pms_menu else False + res["pms_property_id"] = ( + request.env.user.pms_property_id.id if request.session.uid else None + ) + res["user_properties"] = ( + { + "current_property": ( + user.pms_property_id.id, + user.pms_property_id.name, + ), + "allowed_properties": [ + (property.id, property.name) for property in user.pms_property_ids + ], + } + if display_switch_pms_menu + else False + ) if user.pms_property_id.company_id in user.company_ids: user.company_id = user.pms_property_id.company_id - res['company_id'] = user.pms_property_id.company_id.id + res["company_id"] = user.pms_property_id.company_id.id else: - return res #TODO Review method + return res # TODO Review method raise MissingError( - _("Wrong property and company access settings for this user. " - "Please review property and company for user %s") % user.name) + _( + "Wrong property and company access settings for this user. " + "Please review property and company for user %s" + ) + % user.name + ) return res diff --git a/pms/models/inherited_mail_compose_message.py b/pms/models/inherited_mail_compose_message.py index 054019c63..a617d61ca 100644 --- a/pms/models/inherited_mail_compose_message.py +++ b/pms/models/inherited_mail_compose_message.py @@ -5,20 +5,19 @@ from odoo import api, models class MailComposeMessage(models.TransientModel): - _inherit = 'mail.compose.message' + _inherit = "mail.compose.message" - def send_mail(self, auto_commit=False): - if self._context.get('default_model') == 'pms.folio' and \ - self._context.get('default_res_id') and \ - self._context.get('mark_so_as_sent'): - folio = self.env['pms.folio'].browse([ - self._context['default_res_id'] - ]) + if ( + self._context.get("default_model") == "pms.folio" + and self._context.get("default_res_id") + and self._context.get("mark_so_as_sent") + ): + folio = self.env["pms.folio"].browse([self._context["default_res_id"]]) if folio: - cmds = [(1, lid, {'to_send': False}) for lid in - folio.reservation_ids.ids] + cmds = [ + (1, lid, {"to_send": False}) for lid in folio.reservation_ids.ids + ] if any(cmds): folio.reservation_ids = cmds - return super(MailComposeMessage, self).send_mail( - auto_commit=auto_commit) + return super(MailComposeMessage, self).send_mail(auto_commit=auto_commit) diff --git a/pms/models/inherited_payment_return.py b/pms/models/inherited_payment_return.py index 0c1210504..3fdc2a2ba 100644 --- a/pms/models/inherited_payment_return.py +++ b/pms/models/inherited_payment_return.py @@ -1,38 +1,35 @@ # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import models, fields, api, _ +from openerp import _, api, fields, models class PaymentReturn(models.Model): - _inherit = 'payment.return' + _inherit = "payment.return" # Fields declaration - folio_id = fields.Many2one( - 'pms.folio', - string='Folio') + folio_id = fields.Many2one("pms.folio", string="Folio") pms_property_id = fields.Many2one( - 'pms.property', - store=True, - readonly=True, - related='folio_id.pms_property_id') + "pms.property", store=True, readonly=True, related="folio_id.pms_property_id" + ) # Business methods - + def action_confirm(self): pay = super(PaymentReturn, self).action_confirm() if pay: folio_ids = [] - folios = self.env['pms.folio'].browse(folio_ids) + folios = self.env["pms.folio"].browse(folio_ids) for line in self.line_ids: - payments = self.env['account.payment'].search([ - ('move_line_ids', 'in', line.move_line_ids.ids) - ]) - folios_line = self.env['pms.folio'].browse( - payments.mapped('folio_id.id')) + payments = self.env["account.payment"].search( + [("move_line_ids", "in", line.move_line_ids.ids)] + ) + folios_line = self.env["pms.folio"].browse( + payments.mapped("folio_id.id") + ) for folio in folios_line: if self.id not in folio.return_ids.ids: - folio.update({'return_ids': [(4, self.id)]}) + folio.update({"return_ids": [(4, self.id)]}) msg = _("Return of %s registered") % (line.amount) - folio.message_post(subject=_('Payment Return'), body=msg) + folio.message_post(subject=_("Payment Return"), body=msg) folios += folios_line folios.compute_amount() diff --git a/pms/models/inherited_product_pricelist.py b/pms/models/inherited_product_pricelist.py index b33d66d25..6bbfe054d 100644 --- a/pms/models/inherited_product_pricelist.py +++ b/pms/models/inherited_product_pricelist.py @@ -1,6 +1,6 @@ # Copyright 2017 Alexandre Díaz, Pablo Quesada, Darío Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -9,39 +9,47 @@ class ProductPricelist(models.Model): A pricelist marked as daily is used as a daily rate plan for room types and therefore is related only with one property. """ - _inherit = 'product.pricelist' + + _inherit = "product.pricelist" # Fields declaration pms_property_ids = fields.Many2many( - 'pms.property', - string='Properties', - required=False, - ondelete='restrict') + "pms.property", string="Properties", required=False, ondelete="restrict" + ) cancelation_rule_id = fields.Many2one( - 'pms.cancelation.rule', - string="Cancelation Policy") - pricelist_type = fields.Selection([ - ('daily', 'Daily Plan')], - string='Pricelist Type', - default='daily') - is_staff = fields.Boolean('Is Staff') + "pms.cancelation.rule", string="Cancelation Policy" + ) + pricelist_type = fields.Selection( + [("daily", "Daily Plan")], string="Pricelist Type", default="daily" + ) + is_staff = fields.Boolean("Is Staff") # Constraints and onchanges - @api.constrains('pricelist_type', 'pms_property_ids') + @api.constrains("pricelist_type", "pms_property_ids") def _check_pricelist_type_property_ids(self): for record in self: - if record.pricelist_type == 'daily' and len(record.pms_property_ids) != 1: + if record.pricelist_type == "daily" and len(record.pms_property_ids) != 1: raise ValidationError( - _("A daily pricelist is used as a daily Rate Plan " - "for room types and therefore must be related with " - "one and only one property.")) + _( + "A daily pricelist is used as a daily Rate Plan " + "for room types and therefore must be related with " + "one and only one property." + ) + ) - if record.pricelist_type == 'daily' and len(record.pms_property_ids) == 1: - pms_property_id = self.env['pms.property'].search([ - ('default_pricelist_id', '=', record.id) - ]) or None + if record.pricelist_type == "daily" and len(record.pms_property_ids) == 1: + pms_property_id = ( + self.env["pms.property"].search( + [("default_pricelist_id", "=", record.id)] + ) + or None + ) if pms_property_id and pms_property_id != record.pms_property_ids: raise ValidationError( - _("Relationship mismatch.") + " " + - _("This pricelist is used as default in a " - "different property.")) + _("Relationship mismatch.") + + " " + + _( + "This pricelist is used as default in a " + "different property." + ) + ) diff --git a/pms/models/inherited_product_template.py b/pms/models/inherited_product_template.py index 7f47a4798..8f788d13d 100644 --- a/pms/models/inherited_product_template.py +++ b/pms/models/inherited_product_template.py @@ -1,25 +1,26 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models class ProductTemplate(models.Model): _inherit = "product.template" pms_property_ids = fields.Many2many( - 'pms.property', - string='Properties', - required=False, - ondelete='restrict') - per_day = fields.Boolean('Unit increment per day') - per_person = fields.Boolean('Unit increment per person') - consumed_on = fields.Selection([ - ('before', 'Before night'), - ('after', 'After night')], 'Consumed', default='before') - daily_limit = fields.Integer('Daily limit') - is_extra_bed = fields.Boolean('Is extra bed', default=False) + "pms.property", string="Properties", required=False, ondelete="restrict" + ) + per_day = fields.Boolean("Unit increment per day") + per_person = fields.Boolean("Unit increment per person") + consumed_on = fields.Selection( + [("before", "Before night"), ("after", "After night")], + "Consumed", + default="before", + ) + daily_limit = fields.Integer("Daily limit") + is_extra_bed = fields.Boolean("Is extra bed", default=False) show_in_calendar = fields.Boolean( - 'Show in Calendar', + "Show in Calendar", default=False, - help='Specifies if the product is shown in the calendar information.') + help="Specifies if the product is shown in the calendar information.", + ) diff --git a/pms/models/inherited_res_company.py b/pms/models/inherited_res_company.py index 5673e78d2..393dc3ede 100644 --- a/pms/models/inherited_res_company.py +++ b/pms/models/inherited_res_company.py @@ -1,14 +1,14 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models class ResCompany(models.Model): - _inherit = 'res.company' + _inherit = "res.company" # Fields declaration - pms_property_ids = fields.One2many('pms.property', 'company_id', 'Properties') + pms_property_ids = fields.One2many("pms.property", "company_id", "Properties") # TODO: need extra explanation or remove otherwise # additional_hours = fields.Integer('Additional Hours', # help="Provide the min hours value for \ diff --git a/pms/models/inherited_res_partner.py b/pms/models/inherited_res_partner.py index 2d12144fb..9f12eb3f3 100644 --- a/pms/models/inherited_res_partner.py +++ b/pms/models/inherited_res_partner.py @@ -1,48 +1,54 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models import logging + +from odoo import api, fields, models + _logger = logging.getLogger(__name__) class ResPartner(models.Model): - _inherit = 'res.partner' + _inherit = "res.partner" # Fields declaration main_partner_id = fields.Many2one( - 'res.partner', - string='Destination Partner fusion') + "res.partner", string="Destination Partner fusion" + ) reservations_count = fields.Integer( - 'Reservations', compute='_compute_reservations_count') - folios_count = fields.Integer( - 'Folios', compute='_compute_folios_count') - unconfirmed = fields.Boolean('Unconfirmed', default=True) - is_tour_operator = fields.Boolean('Is Tour Operator') + "Reservations", compute="_compute_reservations_count" + ) + folios_count = fields.Integer("Folios", compute="_compute_folios_count") + unconfirmed = fields.Boolean("Unconfirmed", default=True) + is_tour_operator = fields.Boolean("Is Tour Operator") # Compute and Search methods def _compute_reservations_count(self): - pms_reservation_obj = self.env['pms.reservation'] + pms_reservation_obj = self.env["pms.reservation"] for record in self: - record.reservations_count = pms_reservation_obj.search_count([ - ('partner_id.id', '=', record.id) - ]) + record.reservations_count = pms_reservation_obj.search_count( + [("partner_id.id", "=", record.id)] + ) def _compute_folios_count(self): - pms_folio_obj = self.env['pms.folio'] + pms_folio_obj = self.env["pms.folio"] for record in self: - record.folios_count = pms_folio_obj.search_count([ - ('partner_id.id', '=', record.id) - ]) + record.folios_count = pms_folio_obj.search_count( + [("partner_id.id", "=", record.id)] + ) # ORM Overrides @api.model - def name_search(self, name, args=None, operator='ilike', limit=100): + def name_search(self, name, args=None, operator="ilike", limit=100): if not args: args = [] - domain = ['|', '|', ('phone', operator, name), - ('mobile', operator, name), ('email', operator, name), - ] + domain = [ + "|", + "|", + ("phone", operator, name), + ("mobile", operator, name), + ("email", operator, name), + ] partners = self.search(domain + args, limit=limit,) res = partners.name_get() if limit: @@ -50,7 +56,8 @@ class ResPartner(models.Model): else: limit_rest = limit if limit_rest or not limit: - args += [('id', 'not in', partners.ids)] + args += [("id", "not in", partners.ids)] res += super(ResPartner, self).name_search( - name, args=args, operator=operator, limit=limit_rest) + name, args=args, operator=operator, limit=limit_rest + ) return res diff --git a/pms/models/inherited_res_users.py b/pms/models/inherited_res_users.py index e2533ba5f..e43fb3856 100644 --- a/pms/models/inherited_res_users.py +++ b/pms/models/inherited_res_users.py @@ -1,10 +1,10 @@ # Copyright 2019 Pablo Quesada # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, api, fields +from odoo import api, fields, models class ResUsers(models.Model): - _inherit = 'res.users' + _inherit = "res.users" # Default Methods ang Gets @api.model @@ -13,15 +13,17 @@ class ResUsers(models.Model): # Fields declaration pms_property_id = fields.Many2one( - 'pms.property', - string='Property', + "pms.property", + string="Property", default=_get_default_pms_property, - help='The property this user is currently working for.', - context={'user_preference': True}) + help="The property this user is currently working for.", + context={"user_preference": True}, + ) pms_property_ids = fields.Many2many( - 'pms.property', - 'pms_property_users_rel', - 'user_id', - 'pms_property_id', - string='Properties', - default=_get_default_pms_property) + "pms.property", + "pms_property_users_rel", + "user_id", + "pms_property_id", + string="Properties", + default=_get_default_pms_property, + ) diff --git a/pms/models/pms_amenity.py b/pms/models/pms_amenity.py index ba4127903..3546434d8 100644 --- a/pms/models/pms_amenity.py +++ b/pms/models/pms_amenity.py @@ -1,24 +1,20 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models class PmsRoomAmenity(models.Model): - _name = 'pms.amenity' - _description = 'Room amenities' + _name = "pms.amenity" + _description = "Room amenities" # Fields declaration - name = fields.Char('Amenity Name', translate=True, required=True) + name = fields.Char("Amenity Name", translate=True, required=True) pms_property_ids = fields.Many2many( - 'pms.property', - string='Properties', - required=False, - ondelete='restrict') - room_amenity_type_id = fields.Many2one( - 'pms.amenity.type', - 'Amenity Category') - default_code = fields.Char('Internal Reference') - active = fields.Boolean('Active', default=True) + "pms.property", string="Properties", required=False, ondelete="restrict" + ) + room_amenity_type_id = fields.Many2one("pms.amenity.type", "Amenity Category") + default_code = fields.Char("Internal Reference") + active = fields.Boolean("Active", default=True) # TODO: Constrain coherence pms_property_ids with amenity types pms_property_ids diff --git a/pms/models/pms_amenity_type.py b/pms/models/pms_amenity_type.py index a94ac3080..0db9e652f 100644 --- a/pms/models/pms_amenity_type.py +++ b/pms/models/pms_amenity_type.py @@ -1,20 +1,21 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models class PmsRoomAmenityType(models.Model): - _name = 'pms.amenity.type' - _description = 'Amenities Type' + _name = "pms.amenity.type" + _description = "Amenities Type" # Fields declaration - name = fields.Char('Amenity Type Name', translate=True, required=True) + name = fields.Char("Amenity Type Name", translate=True, required=True) pms_property_ids = fields.Many2many( - 'pms.property', string='Properties', required=False, ondelete='restrict') - room_amenity_ids = fields.One2many('pms.amenity', - 'room_amenity_type_id', - 'Amenities in this category') - active = fields.Boolean('Active', default=True) + "pms.property", string="Properties", required=False, ondelete="restrict" + ) + room_amenity_ids = fields.One2many( + "pms.amenity", "room_amenity_type_id", "Amenities in this category" + ) + active = fields.Boolean("Active", default=True) # TODO: Constrain coherence pms_property_ids with amenities pms_property_ids diff --git a/pms/models/pms_board_service.py b/pms/models/pms_board_service.py index a3174e3a4..ed4e63fae 100644 --- a/pms/models/pms_board_service.py +++ b/pms/models/pms_board_service.py @@ -1,6 +1,6 @@ # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, models, fields +from odoo import api, fields, models class PmsBoardService(models.Model): diff --git a/pms/models/pms_board_service_room_type.py b/pms/models/pms_board_service_room_type.py index 191cf3de2..24cbeb3c1 100644 --- a/pms/models/pms_board_service_room_type.py +++ b/pms/models/pms_board_service_room_type.py @@ -1,15 +1,15 @@ # Copyright 2017 Dario # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models, _ +from odoo import _, api, fields, models from odoo.exceptions import UserError class PmsBoardServiceRoomType(models.Model): - _name = 'pms.board.service.room.type' - _table = 'pms_board_service_room_type_rel' - _rec_name = 'pms_board_service_id' + _name = "pms.board.service.room.type" + _table = "pms_board_service_room_type_rel" + _rec_name = "pms_board_service_id" _log_access = False - _description = 'Board Service included in Room' + _description = "Board Service included in Room" # Default Methods ang Gets @@ -17,124 +17,128 @@ class PmsBoardServiceRoomType(models.Model): result = [] for res in self: if res.pricelist_id: - name = u'%s (%s)' % ( + name = u"{} ({})".format( res.pms_board_service_id.name, - res.pricelist_id.name) + res.pricelist_id.name, + ) else: - name = u'%s (%s)' % (res.pms_board_service_id.name, - _('Generic')) + name = u"{} ({})".format(res.pms_board_service_id.name, _("Generic")) result.append((res.id, name)) return result # Fields declaration pms_board_service_id = fields.Many2one( - 'pms.board.service', - string='Board Service', + "pms.board.service", + string="Board Service", index=True, - ondelete='cascade', - required=True) + ondelete="cascade", + required=True, + ) pms_room_type_id = fields.Many2one( - 'pms.room.type', - string='Room Type', + "pms.room.type", + string="Room Type", index=True, - ondelete='cascade', - required=True) + ondelete="cascade", + required=True, + ) pricelist_id = fields.Many2one( - 'product.pricelist', - string='Pricelist', - required=False) + "product.pricelist", string="Pricelist", required=False + ) board_service_line_ids = fields.One2many( - 'pms.board.service.room.type.line', - 'pms_board_service_room_type_id') + "pms.board.service.room.type.line", "pms_board_service_room_type_id" + ) pms_property_id = fields.Many2one( - 'pms.property', - related='pms_room_type_id.pms_property_id') - price_type = fields.Selection([ - ('fixed', 'Fixed'), - ('percent', 'Percent')], - string='Type', - default='fixed', - required=True) + "pms.property", related="pms_room_type_id.pms_property_id" + ) + price_type = fields.Selection( + [("fixed", "Fixed"), ("percent", "Percent")], + string="Type", + default="fixed", + required=True, + ) amount = fields.Float( - 'Amount', - digits=('Product Price'), - compute='_compute_board_amount', - store=True) + "Amount", digits=("Product Price"), compute="_compute_board_amount", store=True + ) # Compute and Search methods - @api.depends('board_service_line_ids.amount') + @api.depends("board_service_line_ids.amount") def _compute_board_amount(self): for record in self: total = 0 for service in record.board_service_line_ids: total += service.amount - record.update({'amount': total}) + record.update({"amount": total}) # Constraints and onchanges - @api.constrains('pricelist_id') + @api.constrains("pricelist_id") def constrains_pricelist_id(self): for record in self: if self.pricelist_id: - board_pricelist = self.env['pms.board.service.room.type'].search([ - ('pricelist_id', '=', record.pricelist_id.id), - ('pms_room_type_id', '=', record.pms_room_type_id.id), - ('pms_board_service_id', '=', - record.pms_board_service_id.id), - ('id', '!=', record.id) - ]) + board_pricelist = self.env["pms.board.service.room.type"].search( + [ + ("pricelist_id", "=", record.pricelist_id.id), + ("pms_room_type_id", "=", record.pms_room_type_id.id), + ("pms_board_service_id", "=", record.pms_board_service_id.id), + ("id", "!=", record.id), + ] + ) if board_pricelist: raise UserError( - _("This Board Service in this Room can't repeat pricelist")) + _("This Board Service in this Room can't repeat pricelist") + ) else: - board_pricelist = self.env['pms.board.service.room.type'].search([ - ('pricelist_id', '=', False), - ('pms_room_type_id', '=', record.pms_room_type_id.id), - ('pms_board_service_id', '=', - record.pms_board_service_id.id), - ('id', '!=', record.id) - ]) + board_pricelist = self.env["pms.board.service.room.type"].search( + [ + ("pricelist_id", "=", False), + ("pms_room_type_id", "=", record.pms_room_type_id.id), + ("pms_board_service_id", "=", record.pms_board_service_id.id), + ("id", "!=", record.id), + ] + ) if board_pricelist: raise UserError( - _("This Board Service in this Room \ - can't repeat without pricelist")) + _( + "This Board Service in this Room \ + can't repeat without pricelist" + ) + ) # Action methods def open_board_lines_form(self): - action = self.env.ref( - 'pms.action_pms_board_service_room_type_view').read()[0] - action['views'] = [(self.env.ref( - 'pms.pms_board_service_room_type_form').id, 'form')] - action['res_id'] = self.id - action['target'] = 'new' + action = self.env.ref("pms.action_pms_board_service_room_type_view").read()[0] + action["views"] = [ + (self.env.ref("pms.pms_board_service_room_type_form").id, "form") + ] + action["res_id"] = self.id + action["target"] = "new" return action # ORM Overrides def init(self): self._cr.execute( - 'SELECT indexname FROM pg_indexes WHERE indexname = %s', - ('pms_board_service_id_pms_room_type_id_pricelist_id',)) + "SELECT indexname FROM pg_indexes WHERE indexname = %s", + ("pms_board_service_id_pms_room_type_id_pricelist_id",), + ) if not self._cr.fetchone(): self._cr.execute( - 'CREATE INDEX pms_board_service_id_pms_room_type_id_pricelist_id \ + "CREATE INDEX pms_board_service_id_pms_room_type_id_pricelist_id \ ON pms_board_service_room_type_rel \ - (pms_board_service_id, pms_room_type_id, pricelist_id)') + (pms_board_service_id, pms_room_type_id, pricelist_id)" + ) @api.model def create(self, vals): - if 'pms_board_service_id' in vals: + if "pms_board_service_id" in vals: vals.update( - self.prepare_board_service_reservation_ids( - vals['pms_board_service_id']) + self.prepare_board_service_reservation_ids(vals["pms_board_service_id"]) ) return super(PmsBoardServiceRoomType, self).create(vals) - def write(self, vals): - if 'pms_board_service_id' in vals: + if "pms_board_service_id" in vals: vals.update( - self.prepare_board_service_reservation_ids( - vals['pms_board_service_id']) + self.prepare_board_service_reservation_ids(vals["pms_board_service_id"]) ) return super(PmsBoardServiceRoomType, self).write(vals) @@ -145,11 +149,9 @@ class PmsBoardServiceRoomType(models.Model): Prepare line to price products config """ cmds = [(5, 0, 0)] - board_service = self.env['pms.board.service'].browse( - board_service_id) + board_service = self.env["pms.board.service"].browse(board_service_id) for line in board_service.board_service_line_ids: - cmds.append((0, False, { - 'product_id': line.product_id.id, - 'amount': line.amount - })) - return {'board_service_line_ids': cmds} + cmds.append( + (0, False, {"product_id": line.product_id.id, "amount": line.amount}) + ) + return {"board_service_line_ids": cmds} diff --git a/pms/models/pms_board_service_room_type_line.py b/pms/models/pms_board_service_room_type_line.py index ec6df2781..8e89e1551 100644 --- a/pms/models/pms_board_service_room_type_line.py +++ b/pms/models/pms_board_service_room_type_line.py @@ -2,20 +2,20 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import fields, models + class PmsBoardServiceRoomTypeLine(models.Model): - _name = 'pms.board.service.room.type.line' - _description = 'Services on Board Service included in Room' + _name = "pms.board.service.room.type.line" + _description = "Services on Board Service included in Room" # Fields declaration pms_board_service_room_type_id = fields.Many2one( - 'pms.board.service.room.type', - 'Board Service Room', - ondelete='cascade', - required=True) - product_id = fields.Many2one( - 'product.product', - 'Product', + "pms.board.service.room.type", + "Board Service Room", + ondelete="cascade", required=True, - readonly=True) + ) + product_id = fields.Many2one( + "product.product", "Product", required=True, readonly=True + ) # TODO def default_amount "amount of service" - amount = fields.Float('Amount', digits=('Product Price'), default=0.0) + amount = fields.Float("Amount", digits=("Product Price"), default=0.0) diff --git a/pms/models/pms_cancelation_rule.py b/pms/models/pms_cancelation_rule.py index ae98f98de..22d7dfe17 100644 --- a/pms/models/pms_cancelation_rule.py +++ b/pms/models/pms_cancelation_rule.py @@ -1,40 +1,39 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models # TODO: refactoring to cancellation.rule class PmsCancelationRule(models.Model): - _name = 'pms.cancelation.rule' - _description = 'Cancelation Rules' + _name = "pms.cancelation.rule" + _description = "Cancelation Rules" # Fields declaration - name = fields.Char('Amenity Name', translate=True, required=True) + name = fields.Char("Amenity Name", translate=True, required=True) pricelist_ids = fields.One2many( - 'product.pricelist', - 'cancelation_rule_id', - 'Pricelist that use this rule') + "product.pricelist", "cancelation_rule_id", "Pricelist that use this rule" + ) pms_property_ids = fields.Many2many( - 'pms.property', - string='Properties', - required=False, - ondelete='restrict') - active = fields.Boolean('Active', default=True) + "pms.property", string="Properties", required=False, ondelete="restrict" + ) + active = fields.Boolean("Active", default=True) days_intime = fields.Integer( - 'Days Late', - help='Maximum number of days for free cancellation before Checkin') - penalty_late = fields.Integer('% Penalty Late', defaul="100") - apply_on_late = fields.Selection([ - ('first', 'First Day'), - ('all', 'All Days'), - ('days', 'Specify days')], 'Late apply on', default='first') - days_late = fields.Integer('Late first days', default="2") - penalty_noshow = fields.Integer('% Penalty No Show', default="100") - apply_on_noshow = fields.Selection([ - ('first', 'First Day'), - ('all', 'All Days'), - ('days', 'Specify days')], 'No Show apply on', default='all') - days_noshow = fields.Integer('NoShow first days', default="2") + "Days Late", help="Maximum number of days for free cancellation before Checkin" + ) + penalty_late = fields.Integer("% Penalty Late", defaul="100") + apply_on_late = fields.Selection( + [("first", "First Day"), ("all", "All Days"), ("days", "Specify days")], + "Late apply on", + default="first", + ) + days_late = fields.Integer("Late first days", default="2") + penalty_noshow = fields.Integer("% Penalty No Show", default="100") + apply_on_noshow = fields.Selection( + [("first", "First Day"), ("all", "All Days"), ("days", "Specify days")], + "No Show apply on", + default="all", + ) + days_noshow = fields.Integer("NoShow first days", default="2") # TODO: Constrain coherence pms_property_ids pricelist and cancelation_rules diff --git a/pms/models/pms_checkin_partner.py b/pms/models/pms_checkin_partner.py index 4f2ed3670..354bc47e1 100644 --- a/pms/models/pms_checkin_partner.py +++ b/pms/models/pms_checkin_partner.py @@ -2,72 +2,72 @@ # Copyright 2018 Alexandre Diaz # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import datetime -from odoo import models, fields, api, _ + +from odoo import _, api, fields, models from odoo.exceptions import ValidationError -from odoo.tools import ( - DEFAULT_SERVER_DATETIME_FORMAT, - DEFAULT_SERVER_DATE_FORMAT) +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT class PmsCheckinPartner(models.Model): - _name = 'pms.checkin.partner' - _description = 'Partner Checkins' + _name = "pms.checkin.partner" + _description = "Partner Checkins" # Default Methods ang Gets def _default_reservation_id(self): - if 'reservation_id' in self.env.context: - reservation = self.env['pms.reservation'].browse([ - self.env.context['reservation_id'] - ]) + if "reservation_id" in self.env.context: + reservation = self.env["pms.reservation"].browse( + [self.env.context["reservation_id"]] + ) return reservation return False def _default_partner_id(self): - if 'reservation_id' in self.env.context: - reservation = self.env['pms.reservation'].browse([ - self.env.context['reservation_id'] - ]) + if "reservation_id" in self.env.context: + reservation = self.env["pms.reservation"].browse( + [self.env.context["reservation_id"]] + ) partner_ids = [] if reservation.folio_id: for room in reservation.folio_id.reservation_ids: - partner_ids.append(room.mapped( - 'checkin_partner_ids.partner_id.id')) - if 'checkin_partner_ids' in self.env.context: - for checkin in self.env.context['checkin_partner_ids']: + partner_ids.append(room.mapped("checkin_partner_ids.partner_id.id")) + if "checkin_partner_ids" in self.env.context: + for checkin in self.env.context["checkin_partner_ids"]: if checkin[0] == 0: - partner_ids.append(checkin[2].get('partner_id')) - if self._context.get('include_customer') and \ - reservation.partner_id.id not in partner_ids and \ - not reservation.partner_id.is_company: + partner_ids.append(checkin[2].get("partner_id")) + if ( + self._context.get("include_customer") + and reservation.partner_id.id not in partner_ids + and not reservation.partner_id.is_company + ): return reservation.partner_id return False def _default_folio_id(self): - if 'folio_id' in self.env.context: - folio = self.env['pms.folio'].browse([ - self.env.context['folio_id'] - ]) + if "folio_id" in self.env.context: + folio = self.env["pms.folio"].browse([self.env.context["folio_id"]]) return folio - if 'reservation_id' in self.env.context: - folio = self.env['pms.reservation'].browse([ - self.env.context['reservation_id'] - ]).folio_id + if "reservation_id" in self.env.context: + folio = ( + self.env["pms.reservation"] + .browse([self.env.context["reservation_id"]]) + .folio_id + ) return folio return False def _default_enter_date(self): - if 'reservation_id' in self.env.context: - reservation = self.env['pms.reservation'].browse([ - self.env.context['reservation_id'] - ]) + if "reservation_id" in self.env.context: + reservation = self.env["pms.reservation"].browse( + [self.env.context["reservation_id"]] + ) return reservation.checkin return False def _default_exit_date(self): - if 'reservation_id' in self.env.context: - reservation = self.env['pms.reservation'].browse([ - self.env.context['reservation_id'] - ]) + if "reservation_id" in self.env.context: + reservation = self.env["pms.reservation"].browse( + [self.env.context["reservation_id"]] + ) return reservation.checkout return False @@ -77,123 +77,124 @@ class PmsCheckinPartner(models.Model): # Fields declaration partner_id = fields.Many2one( - 'res.partner', - default=_default_partner_id, - required=True) - reservation_id = fields.Many2one( - 'pms.reservation', - default=_default_reservation_id) + "res.partner", default=_default_partner_id, required=True + ) + reservation_id = fields.Many2one("pms.reservation", default=_default_reservation_id) folio_id = fields.Many2one( - 'pms.folio', - default=_default_folio_id, - readonly=True, - required=True) + "pms.folio", default=_default_folio_id, readonly=True, required=True + ) pms_property_id = fields.Many2one( - 'pms.property', - default=_get_default_pms_property, - required=True) - email = fields.Char('E-mail', related='partner_id.email') - mobile = fields.Char('Mobile', related='partner_id.mobile') + "pms.property", default=_get_default_pms_property, required=True + ) + email = fields.Char("E-mail", related="partner_id.email") + mobile = fields.Char("Mobile", related="partner_id.mobile") enter_date = fields.Date(default=_default_enter_date, required=True) exit_date = fields.Date(default=_default_exit_date, required=True) - arrival_hour = fields.Char('Arrival Hour', - help="Default Arrival Hour (HH:MM)") - departure_hour = fields.Char('Departure Hour', - help="Default Departure Hour (HH:MM)") - auto_booking = fields.Boolean('Get in Now', default=False) - state = fields.Selection([ - ('draft', 'Pending Entry'), - ('booking', 'On Board'), - ('done', 'Out'), - ('cancelled', 'Cancelled')], - string='State', + arrival_hour = fields.Char("Arrival Hour", help="Default Arrival Hour (HH:MM)") + departure_hour = fields.Char( + "Departure Hour", help="Default Departure Hour (HH:MM)" + ) + auto_booking = fields.Boolean("Get in Now", default=False) + state = fields.Selection( + [ + ("draft", "Pending Entry"), + ("booking", "On Board"), + ("done", "Out"), + ("cancelled", "Cancelled"), + ], + string="State", readonly=True, - default=lambda *a: 'draft', - track_visibility='onchange') + default=lambda *a: "draft", + track_visibility="onchange", + ) # Constraints and onchanges - @api.constrains('exit_date', 'enter_date') + @api.constrains("exit_date", "enter_date") def _check_exit_date(self): for record in self: date_in = fields.Date.from_string(record.enter_date) date_out = fields.Date.from_string(record.exit_date) if date_out < date_in: raise models.ValidationError( - _('Departure date (%s) is prior to arrival on %s') % - (date_out, date_in)) + _("Departure date (%s) is prior to arrival on %s") + % (date_out, date_in) + ) - @api.onchange('enter_date', 'exit_date') + @api.onchange("enter_date", "exit_date") def _onchange_enter_date(self): date_in = fields.Date.from_string(self.enter_date) date_out = fields.Date.from_string(self.exit_date) if date_out <= date_in: date_out = date_in + datetime.timedelta(days=1) - self.update({'exit_date': date_out}) + self.update({"exit_date": date_out}) raise ValidationError( - _('Departure date, is prior to arrival. Check it now. %s') % - date_out) + _("Departure date, is prior to arrival. Check it now. %s") % date_out + ) - - @api.onchange('partner_id') + @api.onchange("partner_id") def _check_partner_id(self): for record in self: if record.partner_id: if record.partner_id.is_company: raise models.ValidationError( - _('A Checkin Guest is configured like a company, \ - modify it in contact form if its a mistake')) - indoor_partner_ids = record.reservation_id.\ - checkin_partner_ids.filtered( - lambda r: r.id != record.id - ).mapped('partner_id.id') + _( + "A Checkin Guest is configured like a company, \ + modify it in contact form if its a mistake" + ) + ) + indoor_partner_ids = record.reservation_id.checkin_partner_ids.filtered( + lambda r: r.id != record.id + ).mapped("partner_id.id") if indoor_partner_ids.count(record.partner_id.id) > 1: record.partner_id = None raise models.ValidationError( - _('This guest is already registered in the room')) + _("This guest is already registered in the room") + ) # Action methods def action_on_board(self): for record in self: if record.reservation_id.checkin > fields.Date.today(): - raise models.ValidationError( - _('It is not yet checkin day!')) + raise models.ValidationError(_("It is not yet checkin day!")) hour = record._get_arrival_hour() vals = { - 'state': 'booking', - 'arrival_hour': hour, + "state": "booking", + "arrival_hour": hour, } record.update(vals) - if record.reservation_id.state == 'confirm': - record.reservation_id.state = 'booking' + 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') - ]) + 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'}) + splitted_reservs.update({"state": "booking"}) return { "type": "ir.actions.do_nothing", } - def action_done(self): for record in self: - if record.state == 'booking': + if record.state == "booking": hour = record._get_departure_hour() vals = { - 'state': 'done', - 'departure_hour': hour, + "state": "done", + "departure_hour": hour, } record.update(vals) return True @@ -202,7 +203,7 @@ class PmsCheckinPartner(models.Model): @api.model def create(self, vals): record = super(PmsCheckinPartner, self).create(vals) - if vals.get('auto_booking', False): + if vals.get("auto_booking", False): record.action_on_board() return record @@ -212,16 +213,17 @@ class PmsCheckinPartner(models.Model): tz_property = self.env.user.pms_property_id.tz today = fields.Datetime.context_timestamp( self.with_context(tz=tz_property), - datetime.datetime.strptime(fields.Date.today(), - DEFAULT_SERVER_DATE_FORMAT)) + datetime.datetime.strptime(fields.Date.today(), DEFAULT_SERVER_DATE_FORMAT), + ) default_arrival_hour = self.env.user.pms_property_id.default_arrival_hour - if self.reservation_id.checkin < today.strftime( - DEFAULT_SERVER_DATE_FORMAT): + if self.reservation_id.checkin < today.strftime(DEFAULT_SERVER_DATE_FORMAT): return default_arrival_hour now = fields.Datetime.context_timestamp( self.with_context(tz=tz_property), - datetime.datetime.strptime(fields.Datetime.now(), - DEFAULT_SERVER_DATETIME_FORMAT)) + datetime.datetime.strptime( + fields.Datetime.now(), DEFAULT_SERVER_DATETIME_FORMAT + ), + ) arrival_hour = now.strftime("%H:%M") return arrival_hour @@ -230,15 +232,16 @@ class PmsCheckinPartner(models.Model): tz_property = self.env.user.pms_property_id.tz today = fields.Datetime.context_timestamp( self.with_context(tz=tz_property), - datetime.datetime.strptime(fields.Date.today(), - DEFAULT_SERVER_DATE_FORMAT)) + datetime.datetime.strptime(fields.Date.today(), DEFAULT_SERVER_DATE_FORMAT), + ) default_departure_hour = self.env.user.pms_property_id.default_departure_hour - if self.reservation_id.checkout < today.strftime( - DEFAULT_SERVER_DATE_FORMAT): + if self.reservation_id.checkout < today.strftime(DEFAULT_SERVER_DATE_FORMAT): return default_departure_hour now = fields.Datetime.context_timestamp( self.with_context(tz=tz_property), - datetime.datetime.strptime(fields.Datetime.now(), - DEFAULT_SERVER_DATETIME_FORMAT)) + datetime.datetime.strptime( + fields.Datetime.now(), DEFAULT_SERVER_DATETIME_FORMAT + ), + ) departure_hour = now.strftime("%H:%M") return departure_hour diff --git a/pms/models/pms_floor.py b/pms/models/pms_floor.py index 3322aaf56..b59ead9f8 100644 --- a/pms/models/pms_floor.py +++ b/pms/models/pms_floor.py @@ -1,6 +1,6 @@ # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models class PmsFloor(models.Model): @@ -8,14 +8,10 @@ class PmsFloor(models.Model): _description = "Ubication" # Fields declaration - name = fields.Char('Ubication Name', - translate=True, - size=64, - required=True, - index=True) + name = fields.Char( + "Ubication Name", translate=True, size=64, required=True, index=True + ) pms_property_ids = fields.Many2many( - 'pms.property', - string='Properties', - required=False, - ondelete='restrict') - sequence = fields.Integer('Sequence') + "pms.property", string="Properties", required=False, ondelete="restrict" + ) + sequence = fields.Integer("Sequence") diff --git a/pms/models/pms_folio.py b/pms/models/pms_folio.py index 64bf67c4f..a5a080a29 100644 --- a/pms/models/pms_folio.py +++ b/pms/models/pms_folio.py @@ -2,14 +2,14 @@ # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models class PmsFolio(models.Model): - _name = 'pms.folio' - _description = 'PMS Folio' - _inherit = ['mail.thread', 'mail.activity.mixin', 'portal.mixin'] - _order = 'id' + _name = "pms.folio" + _description = "PMS Folio" + _inherit = ["mail.thread", "mail.activity.mixin", "portal.mixin"] + _order = "id" # Default Methods ang Gets @api.model @@ -18,17 +18,15 @@ class PmsFolio(models.Model): If the guest has an invoicing address set, this method return diff_invoicing = True, else, return False """ - if 'folio_id' in self.env.context: - folio = self.env['pms.folio'].browse([ - self.env.context['folio_id'] - ]) + if "folio_id" in self.env.context: + folio = self.env["pms.folio"].browse([self.env.context["folio_id"]]) if folio.partner_id.id == folio.partner_invoice_id.id: return False return True @api.model def _get_default_team(self): - return self.env['crm.team']._get_default_team_id() + return self.env["crm.team"]._get_default_team_id() @api.model def _get_default_pms_property(self): @@ -36,230 +34,259 @@ class PmsFolio(models.Model): # Fields declaration name = fields.Char( - String='Folio Number', - readonly=True, - index=True, - default=lambda self: _('New')) + String="Folio Number", readonly=True, index=True, default=lambda self: _("New") + ) pms_property_id = fields.Many2one( - 'pms.property', - default=_get_default_pms_property, - required=True) + "pms.property", default=_get_default_pms_property, required=True + ) partner_id = fields.Many2one( - 'res.partner', - track_visibility='onchange', - ondelete='restrict') + "res.partner", track_visibility="onchange", ondelete="restrict" + ) reservation_ids = fields.One2many( - 'pms.reservation', - 'folio_id', + "pms.reservation", + "folio_id", readonly=False, - states={'done': [('readonly', True)]}, - help="Room reservation detail.",) + states={"done": [("readonly", True)]}, + help="Room reservation detail.", + ) service_ids = fields.One2many( - 'pms.service', - 'folio_id', + "pms.service", + "folio_id", readonly=False, - states={'done': [('readonly', True)]}, + states={"done": [("readonly", True)]}, help="Services detail provide to customer and it will " - "include in main Invoice.") + "include in main Invoice.", + ) company_id = fields.Many2one( - 'res.company', - 'Company', - default=lambda self: self.env.company) + "res.company", "Company", default=lambda self: self.env.company + ) analytic_account_id = fields.Many2one( - 'account.analytic.account', - 'Analytic Account', + "account.analytic.account", + "Analytic Account", readonly=True, - states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, + states={"draft": [("readonly", False)], "sent": [("readonly", False)]}, help="The analytic account related to a folio.", - copy=False) - currency_id = fields.Many2one( - 'res.currency', - related='pricelist_id.currency_id', - string='Currency', - readonly=True, - required=True, - ondelete='restrict',) - pricelist_id = fields.Many2one( - 'product.pricelist', - string='Pricelist', - required=True, - ondelete='restrict', - states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, - help="Pricelist for current folio.") - user_id = fields.Many2one( - 'res.users', - string='Salesperson', - index=True, - ondelete='restrict', - track_visibility='onchange', - default=lambda self: self.env.user) - tour_operator_id = fields.Many2one( - 'res.partner', - 'Tour Operator', - ondelete='restrict', - domain=[('is_tour_operator', '=', True)]) - payment_ids = fields.One2many( - 'account.payment', - 'folio_id', - readonly=True) - return_ids = fields.One2many( - 'payment.return', - 'folio_id', - readonly=True) - payment_term_id = fields.Many2one( - 'account.payment.term', - string='Payment Terms') - checkin_partner_ids = fields.One2many( - 'pms.checkin.partner', - 'folio_id') - move_ids = fields.Many2many( - 'account.move', - string='Invoices', - compute='_get_invoiced', - readonly=True, - copy=False) - partner_invoice_id = fields.Many2one( - 'res.partner', - string='Invoice Address', - required=True, - states={'done': [('readonly', True)]}, - help="Invoice address for current sales order.") - 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") - fiscal_position_id = fields.Many2one( - 'account.fiscal.position', - string='Fiscal Position') - closure_reason_id = fields.Many2one( - 'room.closure.reason') - segmentation_ids = fields.Many2many( - 'res.partner.category', - string='Segmentation', - ondelete='restrict') - team_id = fields.Many2one( - 'crm.team', - string='Sales Team', - ondelete='restrict', - change_default=True, - default=_get_default_team) - client_order_ref = fields.Char(string='Customer Reference', copy=False) - reservation_type = fields.Selection([ - ('normal', 'Normal'), - ('staff', 'Staff'), - ('out', 'Out of Service')], - string='Type', - default=lambda *a: 'normal') - channel_type = fields.Selection([ - ('door', 'Door'), - ('mail', 'Mail'), - ('phone', 'Phone'), - ('call', 'Call Center'), - ('web', 'Web'), - ('agency', 'Agencia'), - ('operator', 'Tour operador'), - ('virtualdoor', 'Virtual Door'), ], 'Sales Channel', default='door') - date_order = fields.Datetime( - string='Order Date', - required=True, - readonly=True, - index=True, - states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, copy=False, - default=fields.Datetime.now) + ) + currency_id = fields.Many2one( + "res.currency", + related="pricelist_id.currency_id", + string="Currency", + readonly=True, + required=True, + ondelete="restrict", + ) + pricelist_id = fields.Many2one( + "product.pricelist", + string="Pricelist", + required=True, + ondelete="restrict", + states={"draft": [("readonly", False)], "sent": [("readonly", False)]}, + help="Pricelist for current folio.", + ) + user_id = fields.Many2one( + "res.users", + string="Salesperson", + index=True, + ondelete="restrict", + track_visibility="onchange", + default=lambda self: self.env.user, + ) + tour_operator_id = fields.Many2one( + "res.partner", + "Tour Operator", + ondelete="restrict", + domain=[("is_tour_operator", "=", True)], + ) + payment_ids = fields.One2many("account.payment", "folio_id", readonly=True) + return_ids = fields.One2many("payment.return", "folio_id", readonly=True) + payment_term_id = fields.Many2one("account.payment.term", string="Payment Terms") + checkin_partner_ids = fields.One2many("pms.checkin.partner", "folio_id") + move_ids = fields.Many2many( + "account.move", + string="Invoices", + compute="_get_invoiced", + readonly=True, + copy=False, + ) + partner_invoice_id = fields.Many2one( + "res.partner", + string="Invoice Address", + required=True, + states={"done": [("readonly", True)]}, + help="Invoice address for current sales order.", + ) + 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" + ) + fiscal_position_id = fields.Many2one( + "account.fiscal.position", string="Fiscal Position" + ) + closure_reason_id = fields.Many2one("room.closure.reason") + segmentation_ids = fields.Many2many( + "res.partner.category", string="Segmentation", ondelete="restrict" + ) + team_id = fields.Many2one( + "crm.team", + string="Sales Team", + ondelete="restrict", + change_default=True, + default=_get_default_team, + ) + client_order_ref = fields.Char(string="Customer Reference", copy=False) + reservation_type = fields.Selection( + [("normal", "Normal"), ("staff", "Staff"), ("out", "Out of Service")], + string="Type", + default=lambda *a: "normal", + ) + channel_type = fields.Selection( + [ + ("door", "Door"), + ("mail", "Mail"), + ("phone", "Phone"), + ("call", "Call Center"), + ("web", "Web"), + ("agency", "Agencia"), + ("operator", "Tour operador"), + ("virtualdoor", "Virtual Door"), + ], + "Sales Channel", + default="door", + ) + date_order = fields.Datetime( + string="Order Date", + required=True, + readonly=True, + index=True, + states={"draft": [("readonly", False)], "sent": [("readonly", False)]}, + copy=False, + default=fields.Datetime.now, + ) confirmation_date = fields.Datetime( - string='Confirmation Date', + string="Confirmation Date", readonly=True, index=True, help="Date on which the folio is confirmed.", - copy=False) - state = fields.Selection([ - ('draft', 'Quotation'), - ('sent', 'Quotation Sent'), - ('confirm', 'Confirmed'), - ('done', 'Locked'), - ('cancel', 'Cancelled'), ], - string='Status', + copy=False, + ) + state = fields.Selection( + [ + ("draft", "Quotation"), + ("sent", "Quotation Sent"), + ("confirm", "Confirmed"), + ("done", "Locked"), + ("cancel", "Cancelled"), + ], + string="Status", readonly=True, copy=False, index=True, - track_visibility='onchange', - default='draft') + track_visibility="onchange", + default="draft", + ) # Partner fields for being used directly in the Folio views--------- - email = fields.Char('E-mail', related='partner_id.email') - mobile = fields.Char('Mobile', related='partner_id.mobile') - phone = fields.Char('Phone', related='partner_id.phone') - partner_internal_comment = fields.Text(string='Internal Partner Notes', - related='partner_id.comment') + email = fields.Char("E-mail", related="partner_id.email") + mobile = fields.Char("Mobile", related="partner_id.mobile") + phone = fields.Char("Phone", related="partner_id.phone") + partner_internal_comment = fields.Text( + string="Internal Partner Notes", related="partner_id.comment" + ) # Payment Fields----------------------------------------------------- - credit_card_details = fields.Text('Credit Card Details') + credit_card_details = fields.Text("Credit Card Details") # Amount Fields------------------------------------------------------ - pending_amount = fields.Monetary(compute='compute_amount', - store=True, - string="Pending in Folio") - refund_amount = fields.Monetary(compute='compute_amount', - store=True, - string="Payment Returns") - invoices_paid = fields.Monetary(compute='compute_amount', - store=True, track_visibility='onchange', - string="Payments") - amount_untaxed = fields.Monetary(string='Untaxed Amount', store=True, - readonly=True, compute='_amount_all', - track_visibility='onchange') - amount_tax = fields.Monetary(string='Taxes', store=True, - readonly=True, compute='_amount_all') - amount_total = fields.Monetary(string='Total', store=True, - readonly=True, compute='_amount_all', - track_visibility='always') - # Checkin Fields----------------------------------------------------- - booking_pending = fields.Integer( - 'Booking pending', compute='_compute_checkin_partner_count') - checkin_partner_count = fields.Integer( - 'Checkin counter', compute='_compute_checkin_partner_count') - checkin_partner_pending_count = fields.Integer( - 'Checkin Pending', compute='_compute_checkin_partner_count') - # Invoice Fields----------------------------------------------------- - invoice_count = fields.Integer(compute='_get_invoiced') - invoice_status = fields.Selection([ - ('invoiced', 'Fully Invoiced'), - ('to invoice', 'To Invoice'), - ('no', 'Nothing to Invoice')], - string='Invoice Status', - compute='_get_invoiced', + pending_amount = fields.Monetary( + compute="compute_amount", store=True, string="Pending in Folio" + ) + refund_amount = fields.Monetary( + compute="compute_amount", store=True, string="Payment Returns" + ) + invoices_paid = fields.Monetary( + compute="compute_amount", + store=True, + track_visibility="onchange", + string="Payments", + ) + amount_untaxed = fields.Monetary( + string="Untaxed Amount", store=True, readonly=True, - default='no') + compute="_amount_all", + track_visibility="onchange", + ) + amount_tax = fields.Monetary( + string="Taxes", store=True, readonly=True, compute="_amount_all" + ) + amount_total = fields.Monetary( + string="Total", + store=True, + readonly=True, + compute="_amount_all", + track_visibility="always", + ) + # Checkin Fields----------------------------------------------------- + booking_pending = fields.Integer( + "Booking pending", compute="_compute_checkin_partner_count" + ) + checkin_partner_count = fields.Integer( + "Checkin counter", compute="_compute_checkin_partner_count" + ) + checkin_partner_pending_count = fields.Integer( + "Checkin Pending", compute="_compute_checkin_partner_count" + ) + # Invoice Fields----------------------------------------------------- + invoice_count = fields.Integer(compute="_get_invoiced") + invoice_status = fields.Selection( + [ + ("invoiced", "Fully Invoiced"), + ("to invoice", "To Invoice"), + ("no", "Nothing to Invoice"), + ], + string="Invoice Status", + compute="_get_invoiced", + store=True, + 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_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') + 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') + 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') + internal_comment = fields.Text(string="Internal Folio Notes") + cancelled_reason = fields.Text("Cause of cancelled") prepaid_warning_days = fields.Integer( - 'Prepaid Warning Days', - help='Margin in days to create a notice if a payment \ - advance has not been recorded') - note = fields.Text('Terms and conditions') - sequence = fields.Integer(string='Sequence', default=10) + "Prepaid Warning Days", + help="Margin in days to create a notice if a payment \ + advance has not been recorded", + ) + note = fields.Text("Terms and conditions") + sequence = fields.Integer(string="Sequence", default=10) # Compute and Search methods - @api.depends('state', 'reservation_ids.invoice_status', - 'service_ids.invoice_status') + @api.depends( + "state", "reservation_ids.invoice_status", "service_ids.invoice_status" + ) def _get_invoiced(self): """ Compute the invoice status of a Folio. Possible statuses: @@ -277,186 +304,217 @@ class PmsFolio(models.Model): refund is not directly linked to the Folio. """ for folio in self: - move_ids = folio.reservation_ids.mapped('move_line_ids').\ - mapped('move_id').filtered(lambda r: r.type in [ - 'out_invoice', 'out_refund']) - invoice_ids = folio.service_ids.mapped('move_line_ids').mapped( - 'move_id').filtered(lambda r: r.type in [ - 'out_invoice', 'out_refund']) + move_ids = ( + folio.reservation_ids.mapped("move_line_ids") + .mapped("move_id") + .filtered(lambda r: r.type in ["out_invoice", "out_refund"]) + ) + invoice_ids = ( + folio.service_ids.mapped("move_line_ids") + .mapped("move_id") + .filtered(lambda r: r.type in ["out_invoice", "out_refund"]) + ) # TODO: Search for invoices which have been 'cancelled' # (filter_refund = 'modify' in 'account.move.refund') # use like as origin may contains multiple references # (e.g. 'SO01, SO02') - refunds = invoice_ids.search([ - ('invoice_origin', 'like', folio.name), - ('company_id', '=', folio.company_id.id)]).filtered( - lambda r: r.type in ['out_invoice', 'out_refund']) - invoice_ids |= refunds.filtered( - lambda r: folio.id in r.folio_ids.ids) + refunds = invoice_ids.search( + [ + ("invoice_origin", "like", folio.name), + ("company_id", "=", folio.company_id.id), + ] + ).filtered(lambda r: r.type in ["out_invoice", "out_refund"]) + invoice_ids |= refunds.filtered(lambda r: folio.id in r.folio_ids.ids) # Search for refunds as well - refund_ids = self.env['account.move'].browse() + refund_ids = self.env["account.move"].browse() if invoice_ids: for inv in invoice_ids: - refund_ids += refund_ids.search([ - ('type', '=', 'out_refund'), - ('invoice_origin', '=', inv.number), - ('invoice_origin', '!=', False), - ('journal_id', '=', inv.journal_id.id)]) + refund_ids += refund_ids.search( + [ + ("type", "=", "out_refund"), + ("invoice_origin", "=", inv.number), + ("invoice_origin", "!=", False), + ("journal_id", "=", inv.journal_id.id), + ] + ) # Ignore the status of the deposit product - deposit_product_id = self.env['sale.advance.payment.inv'].\ - _default_product_id() + deposit_product_id = self.env[ + "sale.advance.payment.inv" + ]._default_product_id() service_invoice_status = [ - service.invoice_status for service in folio.service_ids - if service.product_id != deposit_product_id] + service.invoice_status + for service in folio.service_ids + if service.product_id != deposit_product_id + ] reservation_invoice_status = [ - reservation.invoice_status for reservation in - folio.reservation_ids] + reservation.invoice_status for reservation in folio.reservation_ids + ] - if folio.state not in ('confirm', 'done'): - invoice_status = 'no' - elif any(invoice_status == 'to invoice' for - invoice_status in service_invoice_status) or \ - any(invoice_status == 'to invoice' for invoice_status - in reservation_invoice_status): - invoice_status = 'to invoice' - elif all(invoice_status == 'invoiced' for invoice_status in - service_invoice_status) or \ - any(invoice_status == 'invoiced' for invoice_status in - reservation_invoice_status): - invoice_status = 'invoiced' + if folio.state not in ("confirm", "done"): + invoice_status = "no" + elif any( + invoice_status == "to invoice" + for invoice_status in service_invoice_status + ) or any( + invoice_status == "to invoice" + for invoice_status in reservation_invoice_status + ): + invoice_status = "to invoice" + elif all( + invoice_status == "invoiced" + for invoice_status in service_invoice_status + ) or any( + invoice_status == "invoiced" + for invoice_status in reservation_invoice_status + ): + invoice_status = "invoiced" else: - invoice_status = 'no' + invoice_status = "no" - folio.update({ - 'invoice_count': len(set(move_ids.ids + refund_ids.ids)), - 'move_ids': move_ids.ids + refund_ids.ids, - 'invoice_status': invoice_status - }) + folio.update( + { + "invoice_count": len(set(move_ids.ids + refund_ids.ids)), + "move_ids": move_ids.ids + refund_ids.ids, + "invoice_status": invoice_status, + } + ) - @api.depends('reservation_ids.price_total', 'service_ids.price_total') + @api.depends("reservation_ids.price_total", "service_ids.price_total") def _amount_all(self): """ Compute the total amounts of the SO. """ for record in self: amount_untaxed = amount_tax = 0.0 - amount_untaxed = \ - sum(record.reservation_ids.mapped('price_subtotal')) + \ - sum(record.service_ids.mapped('price_subtotal')) - amount_tax = sum(record.reservation_ids.mapped('price_tax')) + \ - sum(record.service_ids.mapped('price_tax')) - record.update({ - 'amount_untaxed': record.pricelist_id.currency_id.round( - amount_untaxed), - 'amount_tax': record.pricelist_id.currency_id.round( - amount_tax), - 'amount_total': amount_untaxed + amount_tax, - }) - - @api.depends('amount_total', 'payment_ids', 'return_ids', - 'reservation_type', 'state') + amount_untaxed = sum(record.reservation_ids.mapped("price_subtotal")) + sum( + record.service_ids.mapped("price_subtotal") + ) + amount_tax = sum(record.reservation_ids.mapped("price_tax")) + sum( + record.service_ids.mapped("price_tax") + ) + record.update( + { + "amount_untaxed": record.pricelist_id.currency_id.round( + amount_untaxed + ), + "amount_tax": record.pricelist_id.currency_id.round(amount_tax), + "amount_total": amount_untaxed + amount_tax, + } + ) + @api.depends( + "amount_total", "payment_ids", "return_ids", "reservation_type", "state" + ) def compute_amount(self): - acc_pay_obj = self.env['account.payment'] + acc_pay_obj = self.env["account.payment"] for record in self: - if record.reservation_type in ('staff', 'out'): + if record.reservation_type in ("staff", "out"): vals = { - 'pending_amount': 0, - 'invoices_paid': 0, - 'refund_amount': 0, + "pending_amount": 0, + "invoices_paid": 0, + "refund_amount": 0, } record.update(vals) else: total_inv_refund = 0 - payments = acc_pay_obj.search([ - ('folio_id', '=', record.id) - ]) + payments = acc_pay_obj.search([("folio_id", "=", record.id)]) total_paid = sum(pay.amount for pay in payments) - return_lines = self.env['payment.return.line'].search([ - ('move_line_ids', 'in', payments.mapped( - 'move_line_ids.id')), - ('return_id.state', '=', 'done') - ]) - total_inv_refund = sum( - pay_return.amount for pay_return in return_lines) + return_lines = self.env["payment.return.line"].search( + [ + ("move_line_ids", "in", payments.mapped("move_line_ids.id")), + ("return_id.state", "=", "done"), + ] + ) + total_inv_refund = sum(pay_return.amount for pay_return in return_lines) total = record.amount_total # REVIEW: Must We ignored services in cancelled folios # pending amount? - if record.state == 'cancelled': - total = total - \ - sum(record.service_ids.mapped('price_total')) + if record.state == "cancelled": + total = total - sum(record.service_ids.mapped("price_total")) vals = { - 'pending_amount': total - total_paid + total_inv_refund, - 'invoices_paid': total_paid, - 'refund_amount': total_inv_refund, + "pending_amount": total - total_paid + total_inv_refund, + "invoices_paid": total_paid, + "refund_amount": total_inv_refund, } record.update(vals) - @api.depends('reservation_ids') + @api.depends("reservation_ids") def _compute_has_confirmed_reservations_to_send(self): has_to_send = False - if self.reservation_type != 'out': + 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 = ( + 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') + @api.depends("reservation_ids") def _compute_has_cancelled_reservations_to_send(self): has_to_send = False - if self.reservation_type != 'out': + 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 = ( + 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') + @api.depends("reservation_ids") def _compute_has_checkout_to_send(self): has_to_send = True - if self.reservation_type != 'out': + 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), - ]) + 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': + elif not rline.to_send or rline.state != "done": has_to_send = False break self.has_checkout_to_send = has_to_send @@ -465,7 +523,7 @@ class PmsFolio(models.Model): # Constraints and onchanges - @api.onchange('partner_id') + @api.onchange("partner_id") def onchange_partner_id(self): """ Update the following fields when the partner is changed: @@ -475,43 +533,49 @@ class PmsFolio(models.Model): - Delivery address """ if not self.partner_id: - self.update({ - 'partner_invoice_id': False, - 'payment_term_id': False, - 'fiscal_position_id': False, - }) + self.update( + { + "partner_invoice_id": False, + "payment_term_id": False, + "fiscal_position_id": False, + } + ) return - addr = self.partner_id.address_get(['invoice']) - 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 + addr = self.partner_id.address_get(["invoice"]) + 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 + ) values = { - 'pricelist_id': pricelist, - 'payment_term_id': self.partner_id.property_payment_term_id and - self.partner_id.property_payment_term_id.id or False, - 'partner_invoice_id': addr['invoice'], - 'user_id': self.partner_id.user_id.id or self.env.uid, + "pricelist_id": pricelist, + "payment_term_id": self.partner_id.property_payment_term_id + and self.partner_id.property_payment_term_id.id + or False, + "partner_invoice_id": addr["invoice"], + "user_id": self.partner_id.user_id.id or self.env.uid, } - if self.env['ir.config_parameter'].sudo().\ - get_param('sale.use_sale_note') and \ - self.env.user.company_id.sale_note: - values['note'] = self.with_context( - lang=self.partner_id.lang).env.user.company_id.sale_note + if ( + self.env["ir.config_parameter"].sudo().get_param("sale.use_sale_note") + and self.env.user.company_id.sale_note + ): + values["note"] = self.with_context( + lang=self.partner_id.lang + ).env.user.company_id.sale_note if self.partner_id.team_id: - values['team_id'] = self.partner_id.team_id.id + values["team_id"] = self.partner_id.team_id.id self.update(values) - - @api.onchange('pricelist_id') + @api.onchange("pricelist_id") def onchange_pricelist_id(self): - values = {'reservation_type': self.env['pms.folio']. - calcule_reservation_type( - self.pricelist_id.is_staff, - self.reservation_type - )} + values = { + "reservation_type": self.env["pms.folio"].calcule_reservation_type( + self.pricelist_id.is_staff, self.reservation_type + ) + } self.update(values) # Action methods @@ -520,226 +584,224 @@ class PmsFolio(models.Model): self.ensure_one() partner = self.partner_id.id amount = self.pending_amount - view_id = self.env.ref('pms.account_payment_view_form_folio').id - return{ - 'name': _('Register Payment'), - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'account.payment', - 'type': 'ir.actions.act_window', - 'view_id': view_id, - 'context': { - 'default_folio_id': self.id, - 'default_amount': amount, - 'default_payment_type': 'inbound', - 'default_partner_type': 'customer', - 'default_partner_id': partner, - 'default_communication': self.name, + view_id = self.env.ref("pms.account_payment_view_form_folio").id + return { + "name": _("Register Payment"), + "view_type": "form", + "view_mode": "form", + "res_model": "account.payment", + "type": "ir.actions.act_window", + "view_id": view_id, + "context": { + "default_folio_id": self.id, + "default_amount": amount, + "default_payment_type": "inbound", + "default_partner_type": "customer", + "default_partner_id": partner, + "default_communication": self.name, }, - 'target': 'new', + "target": "new", } - def open_moves_folio(self): - invoices = self.mapped('move_ids') - action = self.env.ref('account.action_move_out_invoice_type').read()[0] + invoices = self.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)] + 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] + action["views"] = [(self.env.ref("account.view_move_form").id, "form")] + action["res_id"] = invoices.ids[0] else: - action = {'type': 'ir.actions.act_window_close'} + action = {"type": "ir.actions.act_window_close"} return action - def action_return_payments(self): self.ensure_one() return_move_ids = [] - acc_pay_obj = self.env['account.payment'] - payments = acc_pay_obj.search([ - '|', - ('move_ids', 'in', self.move_ids.ids), - ('folio_id', '=', self.id) - ]) + acc_pay_obj = self.env["account.payment"] + payments = acc_pay_obj.search( + ["|", ("move_ids", "in", self.move_ids.ids), ("folio_id", "=", self.id)] + ) return_move_ids += self.move_ids.filtered( - lambda invoice: invoice.type == 'out_refund').mapped( - 'payment_move_line_ids.move_id.id') - return_lines = self.env['payment.return.line'].search([ - ('move_line_ids', 'in', payments.mapped('move_line_ids.id')), - ]) - return_move_ids += return_lines.mapped('return_id.move_id.id') + lambda invoice: invoice.type == "out_refund" + ).mapped("payment_move_line_ids.move_id.id") + return_lines = self.env["payment.return.line"].search( + [("move_line_ids", "in", payments.mapped("move_line_ids.id")),] + ) + return_move_ids += return_lines.mapped("return_id.move_id.id") - return{ - 'name': _('Returns'), - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'account.move', - 'type': 'ir.actions.act_window', - 'domain': [('id', 'in', return_move_ids)], + return { + "name": _("Returns"), + "view_type": "form", + "view_mode": "tree,form", + "res_model": "account.move", + "type": "ir.actions.act_window", + "domain": [("id", "in", return_move_ids)], } - def action_checks(self): self.ensure_one() - rooms = self.mapped('reservation_ids.id') + rooms = self.mapped("reservation_ids.id") return { - 'name': _('Checkins'), - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'pms.checkin.partner', - 'type': 'ir.actions.act_window', - 'domain': [('reservation_id', 'in', rooms)], - 'target': 'new', + "name": _("Checkins"), + "view_type": "form", + "view_mode": "tree,form", + "res_model": "pms.checkin.partner", + "type": "ir.actions.act_window", + "domain": [("reservation_id", "in", rooms)], + "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'] + ir_model_data = self.env["ir.model.data"] try: template_id = ir_model_data.get_object_reference( - 'pms', - 'email_template_reservation')[1] + "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] + "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 - }) + 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 + "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'] + ir_model_data = self.env["ir.model.data"] try: template_id = ir_model_data.get_object_reference( - 'pms', - 'mail_template_pms_exit')[1] + "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] + "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 - }) + 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 + "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'] + ir_model_data = self.env["ir.model.data"] try: template_id = ir_model_data.get_object_reference( - 'pms', - 'mail_template_pms_cancel')[1] + "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] + "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 - }) + 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 + "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): - if vals.get('name', _('New')) == _('New') or 'name' not in vals: - if 'company_id' in vals: - vals['name'] = self.env['ir.sequence'].with_context( - force_company=vals['company_id'] - ).next_by_code('pms.folio') or _('New') + if vals.get("name", _("New")) == _("New") or "name" not in vals: + if "company_id" in vals: + vals["name"] = self.env["ir.sequence"].with_context( + force_company=vals["company_id"] + ).next_by_code("pms.folio") or _("New") else: - vals['name'] = self.env['ir.sequence'].next_by_code( - 'pms.folio') or _('New') + vals["name"] = self.env["ir.sequence"].next_by_code("pms.folio") or _( + "New" + ) vals.update(self._prepare_add_missing_fields(vals)) result = super(PmsFolio, self).create(vals) return result @@ -749,55 +811,48 @@ class PmsFolio(models.Model): def _prepare_add_missing_fields(self, values): """ Deduce missing required fields from the onchange """ res = {} - onchange_fields = ['partner_invoice_id', - 'pricelist_id', - 'payment_term_id'] - if values.get('partner_id'): + onchange_fields = ["partner_invoice_id", "pricelist_id", "payment_term_id"] + if values.get("partner_id"): line = self.new(values) if any(f not in values for f in onchange_fields): line.onchange_partner_id() for field in onchange_fields: if field not in values: - res[field] = line._fields[field].convert_to_write( - line[field], line) + res[field] = line._fields[field].convert_to_write(line[field], line) return res @api.model def calcule_reservation_type(self, is_staff, current_type): - if current_type == 'out': - return 'out' + if current_type == "out": + return "out" elif is_staff: - return 'staff' + return "staff" else: - return 'normal' - + return "normal" def action_done(self): - reservation_ids = self.mapped('reservation_ids') + reservation_ids = self.mapped("reservation_ids") for line in reservation_ids: if line.state == "booking": line.action_reservation_checkout() - def action_cancel(self): for folio in self: for reservation in folio.reservation_ids.filtered( - lambda res: res.state != 'cancelled'): + lambda res: res.state != "cancelled" + ): reservation.action_cancel() - self.write({ - 'state': 'cancel', - }) + self.write( + {"state": "cancel",} + ) return True - def action_confirm(self): - for folio in self.filtered(lambda folio: folio.partner_id not in - folio.message_partner_ids): + for folio in self.filtered( + lambda folio: folio.partner_id not in folio.message_partner_ids + ): folio.message_subscribe([folio.partner_id.id]) - self.write({ - 'state': 'confirm', - 'confirmation_date': fields.Datetime.now() - }) + self.write({"state": "confirm", "confirmation_date": fields.Datetime.now()}) # if self.env.context.get('send_email'): # self.force_quotation_send() @@ -812,65 +867,68 @@ class PmsFolio(models.Model): CHECKIN/OUT PROCESS """ - def _compute_checkin_partner_count(self): for record in self: - if record.reservation_type == 'normal' and record.reservation_ids: + 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" and not x.parent_reservation + ) mapped_checkin_partner = filtered_reservs.mapped( - 'checkin_partner_ids.id') + "checkin_partner_ids.id" + ) record.checkin_partner_count = len(mapped_checkin_partner) mapped_checkin_partner_count = filtered_reservs.mapped( - lambda x: (x.adults + x.children) - - len(x.checkin_partner_ids)) - record.checkin_partner_pending_count = sum( - mapped_checkin_partner_count) - + lambda x: (x.adults + x.children) - len(x.checkin_partner_ids) + ) + 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: + if ( + (import_all or rline.to_send) + and not rline.parent_reservation + and rline.state == state + ): dates = (rline.real_checkin, rline.real_checkout) vals = { - 'num': len( + "num": len( self.reservation_ids.filtered( - lambda r: r.real_checkin == dates[0] and - r.real_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) + lambda r: r.real_checkin == dates[0] + and r.real_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, + "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, + "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']: + 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']) - + 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() @@ -878,30 +936,28 @@ class PmsFolio(models.Model): for line in self.reservation_ids: price_reduce = line.price_total product = line.room_type_id.product_id - taxes = line.tax_ids.compute_all( - price_reduce, quantity=1, product=product)['taxes'] + taxes = line.tax_ids.compute_all(price_reduce, quantity=1, product=product)[ + "taxes" + ] for tax in line.tax_ids: group = tax.tax_group_id - res.setdefault(group, {'amount': 0.0, 'base': 0.0}) + res.setdefault(group, {"amount": 0.0, "base": 0.0}) for t in taxes: - if t['id'] == tax.id or t['id'] in \ - tax.children_tax_ids.ids: - res[group]['amount'] += t['amount'] - res[group]['base'] += t['base'] + if t["id"] == tax.id or t["id"] in tax.children_tax_ids.ids: + res[group]["amount"] += t["amount"] + res[group]["base"] += t["base"] for line in self.service_ids: price_reduce = line.price_unit * (1.0 - line.discount / 100.0) taxes = line.tax_ids.compute_all( - price_reduce, quantity=line.product_qty, - product=line.product_id)['taxes'] + price_reduce, quantity=line.product_qty, product=line.product_id + )["taxes"] for tax in line.tax_ids: group = tax.tax_group_id - res.setdefault(group, {'amount': 0.0, 'base': 0.0}) + res.setdefault(group, {"amount": 0.0, "base": 0.0}) for t in taxes: - if t['id'] == tax.id or t['id'] in \ - tax.children_tax_ids.ids: - res[group]['amount'] += t['amount'] - res[group]['base'] += t['base'] + if t["id"] == tax.id or t["id"] in tax.children_tax_ids.ids: + res[group]["amount"] += t["amount"] + res[group]["base"] += t["base"] res = sorted(res.items(), key=lambda l: l[0].sequence) - res = [(l[0].name, l[1]['amount'], l[1]['base'], len(res)) - for l in res] + res = [(l[0].name, l[1]["amount"], l[1]["base"], len(res)) for l in res] return res diff --git a/pms/models/pms_property.py b/pms/models/pms_property.py index cdcf49fa4..5a1994aea 100644 --- a/pms/models/pms_property.py +++ b/pms/models/pms_property.py @@ -3,61 +3,59 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import re -from odoo import models, fields, api, _ + +from odoo import _, api, fields, models from odoo.exceptions import ValidationError class PmsProperty(models.Model): - _name = 'pms.property' - _description = 'Property' - _inherits = {'res.partner': 'partner_id'} + _name = "pms.property" + _description = "Property" + _inherits = {"res.partner": "partner_id"} # Fields declaration partner_id = fields.Many2one( - 'res.partner', - 'Property', - required=True, - delegate=True, - ondelete='cascade') + "res.partner", "Property", required=True, delegate=True, ondelete="cascade" + ) company_id = fields.Many2one( - 'res.company', + "res.company", required=True, - help='The company that owns or operates this property.') + help="The company that owns or operates this property.", + ) user_ids = fields.Many2many( - 'res.users', - 'pms_property_users_rel', - 'pms_property_id', - 'user_id', - string='Accepted Users') - room_type_ids = fields.One2many( - 'pms.room.type', - 'pms_property_id', - 'Room Types') - room_ids = fields.One2many( - 'pms.room', - 'pms_property_id', - 'Rooms') + "res.users", + "pms_property_users_rel", + "pms_property_id", + "user_id", + string="Accepted Users", + ) + room_type_ids = fields.One2many("pms.room.type", "pms_property_id", "Room Types") + room_ids = fields.One2many("pms.room", "pms_property_id", "Rooms") default_pricelist_id = fields.Many2one( - 'product.pricelist', - string='Product Pricelist', + "product.pricelist", + string="Product Pricelist", required=True, - help='The default pricelist used in this property.') + help="The default pricelist used in this property.", + ) default_restriction_id = fields.Many2one( - 'pms.room.type.restriction', - 'Restriction Plan', + "pms.room.type.restriction", + "Restriction Plan", required=True, - help='The default restriction plan used in this property.') - default_arrival_hour = fields.Char('Arrival Hour (GMT)', - help="HH:mm Format", default="14:00") - default_departure_hour = fields.Char('Departure Hour (GMT)', - help="HH:mm Format", default="12:00") - default_cancel_policy_days = fields.Integer('Cancellation Days') - default_cancel_policy_percent = fields.Float('Percent to pay') + help="The default restriction plan used in this property.", + ) + default_arrival_hour = fields.Char( + "Arrival Hour (GMT)", help="HH:mm Format", default="14:00" + ) + default_departure_hour = fields.Char( + "Departure Hour (GMT)", help="HH:mm Format", default="12:00" + ) + default_cancel_policy_days = fields.Integer("Cancellation Days") + default_cancel_policy_percent = fields.Float("Percent to pay") # Constraints and onchanges - @api.constrains('default_arrival_hour', 'default_departure_hour') + @api.constrains("default_arrival_hour", "default_departure_hour") def _check_hours(self): - r = re.compile('[0-2][0-9]:[0-5][0-9]') + r = re.compile("[0-2][0-9]:[0-5][0-9]") if not r.match(self.default_arrival_hour): raise ValidationError(_("Invalid arrival hour (Format: HH:mm)")) if not r.match(self.default_departure_hour): diff --git a/pms/models/pms_reservation.py b/pms/models/pms_reservation.py index 14a8c5f3f..5de7e7f91 100644 --- a/pms/models/pms_reservation.py +++ b/pms/models/pms_reservation.py @@ -1,62 +1,65 @@ # Copyright 2017-2018 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import logging +import re import time from datetime import timedelta -import re + +from odoo import _, api, fields, models from odoo.exceptions import UserError, ValidationError from odoo.tools import ( - float_is_zero, - float_compare, DEFAULT_SERVER_DATE_FORMAT, - DEFAULT_SERVER_DATETIME_FORMAT) -from odoo import models, fields, api, _ -import logging + DEFAULT_SERVER_DATETIME_FORMAT, + float_compare, + float_is_zero, +) + _logger = logging.getLogger(__name__) class PmsReservation(models.Model): - _name = 'pms.reservation' - _description = 'Reservation' - _inherit = ['mail.thread', 'mail.activity.mixin', 'portal.mixin'] + _name = "pms.reservation" + _description = "Reservation" + _inherit = ["mail.thread", "mail.activity.mixin", "portal.mixin"] _order = "last_updated_res desc, name" # Default Methods ang Gets 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_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 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.from_string(today).strftime(DEFAULT_SERVER_DATE_FORMAT) 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_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 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.from_string(today) + timedelta(days=1)).strftime( + DEFAULT_SERVER_DATE_FORMAT + ) def _get_default_arrival_hour(self): folio = False default_arrival_hour = self.env.user.pms_property_id.default_arrival_hour - if 'folio_id' in self._context: - folio = self.env['pms.folio'].search([ - ('id', '=', self._context['folio_id']) - ]) + 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].arrival_hour else: @@ -65,10 +68,10 @@ class PmsReservation(models.Model): def _get_default_departure_hour(self): folio = False default_departure_hour = self.env.user.pms_property_id.default_departure_hour - if 'folio_id' in self._context: - folio = self.env['pms.folio'].search([ - ('id', '=', self._context['folio_id']) - ]) + 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].departure_hour else: @@ -80,151 +83,141 @@ class PmsReservation(models.Model): If the guest has an invoicing address set, this method return diff_invoicing = True, else, return False """ - if 'reservation_id' in self.env.context: - reservation = self.env['pms.reservation'].browse([ - self.env.context['reservation_id'] - ]) + if "reservation_id" in self.env.context: + reservation = self.env["pms.reservation"].browse( + [self.env.context["reservation_id"]] + ) if reservation.partner_id.id == reservation.partner_invoice_id.id: return False return True # Fields declaration - name = fields.Text('Reservation Description', required=True) + name = fields.Text("Reservation Description", required=True) room_id = fields.Many2one( - 'pms.room', - string='Room', - track_visibility='onchange', - ondelete='restrict') + "pms.room", string="Room", track_visibility="onchange", ondelete="restrict" + ) folio_id = fields.Many2one( - 'pms.folio', - string='Folio', - track_visibility='onchange', - ondelete='cascade') + "pms.folio", string="Folio", track_visibility="onchange", ondelete="cascade" + ) board_service_room_id = fields.Many2one( - 'pms.board.service.room.type', - string='Board Service') + "pms.board.service.room.type", string="Board Service" + ) room_type_id = fields.Many2one( - 'pms.room.type', - string='Room Type', - required=True, - track_visibility='onchange') - partner_id = fields.Many2one( - related='folio_id.partner_id') - tour_operator_id = fields.Many2one( - related='folio_id.tour_operator_id') - partner_invoice_id = fields.Many2one( - related='folio_id.partner_invoice_id') - partner_invoice_state_id = fields.Many2one( - related="partner_invoice_id.state_id") + "pms.room.type", string="Room Type", required=True, track_visibility="onchange" + ) + partner_id = fields.Many2one(related="folio_id.partner_id") + tour_operator_id = fields.Many2one(related="folio_id.tour_operator_id") + partner_invoice_id = fields.Many2one(related="folio_id.partner_invoice_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") - partner_parent_id = fields.Many2one( - related="partner_id.parent_id") - closure_reason_id = fields.Many2one( - related='folio_id.closure_reason_id') + 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) + related="folio_id.company_id", string="Company", store=True, readonly=True + ) pms_property_id = fields.Many2one( - 'pms.property', - store=True, - readonly=True, - related='folio_id.pms_property_id') + "pms.property", store=True, readonly=True, related="folio_id.pms_property_id" + ) reservation_line_ids = fields.One2many( - 'pms.reservation.line', - 'reservation_id', - required=True) - service_ids = fields.One2many( - 'pms.service', - 'reservation_id') - pricelist_id = fields.Many2one( - 'product.pricelist', - related='folio_id.pricelist_id') + "pms.reservation.line", "reservation_id", required=True + ) + service_ids = fields.One2many("pms.service", "reservation_id") + pricelist_id = fields.Many2one("product.pricelist", related="folio_id.pricelist_id") # 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') + 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") currency_id = fields.Many2one( - 'res.currency', - related='pricelist_id.currency_id', - string='Currency', + "res.currency", + related="pricelist_id.currency_id", + string="Currency", readonly=True, - required=True) + required=True, + ) tax_ids = fields.Many2many( - 'account.tax', - string='Taxes', - ondelete='restrict', - domain=['|', ('active', '=', False), ('active', '=', True)]) + "account.tax", + string="Taxes", + ondelete="restrict", + domain=["|", ("active", "=", False), ("active", "=", True)], + ) move_line_ids = fields.Many2many( - 'account.move.line', - 'reservation_move_rel', - 'reservation_id', - 'move_line_id', - string='Invoice Lines', - copy=False) - analytic_tag_ids = fields.Many2many( - 'account.analytic.tag', - string='Analytic Tags') + "account.move.line", + "reservation_move_rel", + "reservation_id", + "move_line_id", + string="Invoice Lines", + copy=False, + ) + analytic_tag_ids = fields.Many2many("account.analytic.tag", string="Analytic Tags") localizator = fields.Char( - string='Localizator', - compute='_compute_localizator', - store=True) - sequence = fields.Integer(string='Sequence', default=10) - reservation_no = fields.Char('Reservation No', size=64, readonly=True) - adults = fields.Integer('Adults', size=64, readonly=False, - track_visibility='onchange', - help='List of adults there in guest list. ') - children = fields.Integer('Children', size=64, readonly=False, - track_visibility='onchange', - help='Number of children there in guest list.') - to_assign = fields.Boolean('To Assign', track_visibility='onchange') - state = fields.Selection([ - ('draft', 'Pre-reservation'), - ('confirm', 'Pending Entry'), - ('booking', 'On Board'), - ('done', 'Out'), - ('cancelled', 'Cancelled')], - string='Status', - readonly=True, - default=lambda *a: 'draft', - copy=False, - track_visibility='onchange') - reservation_type = fields.Selection(related='folio_id.reservation_type', - default=lambda *a: 'normal') - invoice_count = fields.Integer(related='folio_id.invoice_count') - credit_card_details = fields.Text(related='folio_id.credit_card_details') - cancelled_reason = fields.Selection([ - ('late', 'Late'), - ('intime', 'In time'), - ('noshow', 'No Show')], - string='Cause of cancelled', - track_visibility='onchange') - out_service_description = fields.Text('Cause of out of service') - checkin = fields.Date('Check In', required=True, - default=_get_default_checkin) - checkout = fields.Date('Check Out', required=True, - default=_get_default_checkout) - real_checkin = fields.Date('Arrival', required=True, - track_visibility='onchange') - real_checkout = fields.Date('Departure', required=True, - track_visibility='onchange') - arrival_hour = fields.Char('Arrival Hour', - default=_get_default_arrival_hour, - help="Default Arrival Hour (HH:MM)") - departure_hour = fields.Char('Departure Hour', - default=_get_default_departure_hour, - help="Default Departure Hour (HH:MM)") + string="Localizator", compute="_compute_localizator", store=True + ) + sequence = fields.Integer(string="Sequence", default=10) + reservation_no = fields.Char("Reservation No", size=64, readonly=True) + adults = fields.Integer( + "Adults", + size=64, + readonly=False, + track_visibility="onchange", + help="List of adults there in guest list. ", + ) + children = fields.Integer( + "Children", + size=64, + readonly=False, + track_visibility="onchange", + help="Number of children there in guest list.", + ) + to_assign = fields.Boolean("To Assign", track_visibility="onchange") + state = fields.Selection( + [ + ("draft", "Pre-reservation"), + ("confirm", "Pending Entry"), + ("booking", "On Board"), + ("done", "Out"), + ("cancelled", "Cancelled"), + ], + string="Status", + readonly=True, + default=lambda *a: "draft", + copy=False, + track_visibility="onchange", + ) + reservation_type = fields.Selection( + related="folio_id.reservation_type", default=lambda *a: "normal" + ) + invoice_count = fields.Integer(related="folio_id.invoice_count") + credit_card_details = fields.Text(related="folio_id.credit_card_details") + cancelled_reason = fields.Selection( + [("late", "Late"), ("intime", "In time"), ("noshow", "No Show")], + string="Cause of cancelled", + track_visibility="onchange", + ) + out_service_description = fields.Text("Cause of out of service") + checkin = fields.Date("Check In", required=True, default=_get_default_checkin) + checkout = fields.Date("Check Out", required=True, default=_get_default_checkout) + real_checkin = fields.Date("Arrival", required=True, track_visibility="onchange") + real_checkout = fields.Date("Departure", required=True, track_visibility="onchange") + arrival_hour = fields.Char( + "Arrival Hour", + default=_get_default_arrival_hour, + help="Default Arrival Hour (HH:MM)", + ) + departure_hour = fields.Char( + "Departure Hour", + 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_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") @@ -235,115 +228,132 @@ class PmsReservation(models.Model): # searching on a computed field can also be enabled by setting the # search parameter. The value is a method name returning a Domains checkin_partner_count = fields.Integer( - 'Checkin counter', - compute='_compute_checkin_partner_count') + "Checkin counter", compute="_compute_checkin_partner_count" + ) checkin_partner_pending_count = fields.Integer( - 'Checkin Pending Num', - compute='_compute_checkin_partner_count', - search='_search_checkin_partner_pending') + "Checkin Pending Num", + compute="_compute_checkin_partner_count", + search="_search_checkin_partner_pending", + ) customer_sleep_here = fields.Boolean( default=True, string="Include customer", - help="Indicates if the customer sleeps in this room") + help="Indicates if the customer sleeps in this room", + ) # check_rooms = fields.Boolean('Check Rooms') - splitted = fields.Boolean('Splitted', default=False) - overbooking = fields.Boolean('Is Overbooking', default=False) - reselling = fields.Boolean('Is Reselling', default=False) - nights = fields.Integer('Nights', compute='_computed_nights', store=True) - channel_type = fields.Selection([ - ('door', 'Door'), - ('mail', 'Mail'), - ('phone', 'Phone'), - ('call', 'Call Center'), - ('web', 'Web'), - ('agency', 'Agencia'), - ('operator', 'Tour operador'), - ('virtualdoor', 'Virtual Door'), ], - string='Sales Channel', - default='door') - last_updated_res = fields.Datetime('Last Updated') - folio_pending_amount = fields.Monetary(related='folio_id.pending_amount') - shared_folio = fields.Boolean(compute='_computed_shared') + splitted = fields.Boolean("Splitted", default=False) + overbooking = fields.Boolean("Is Overbooking", default=False) + reselling = fields.Boolean("Is Reselling", default=False) + nights = fields.Integer("Nights", compute="_computed_nights", store=True) + channel_type = fields.Selection( + [ + ("door", "Door"), + ("mail", "Mail"), + ("phone", "Phone"), + ("call", "Call Center"), + ("web", "Web"), + ("agency", "Agencia"), + ("operator", "Tour operador"), + ("virtualdoor", "Virtual Door"), + ], + string="Sales Channel", + default="door", + ) + last_updated_res = fields.Datetime("Last Updated") + folio_pending_amount = fields.Monetary(related="folio_id.pending_amount") + shared_folio = fields.Boolean(compute="_computed_shared") # Used to notify is the reservation folio has other reservations/services - email = fields.Char('E-mail', related='partner_id.email') - mobile = fields.Char('Mobile', related='partner_id.mobile') - phone = fields.Char('Phone', related='partner_id.phone') - partner_internal_comment = fields.Text(string='Internal Partner Notes', - related='partner_id.comment') - folio_internal_comment = fields.Text(string='Internal Folio Notes', - related='folio_id.internal_comment') - preconfirm = fields.Boolean('Auto confirm to Save', default=True) - to_send = fields.Boolean('To Send', default=True) - call_center = fields.Boolean(default='set_call_center_user') + email = fields.Char("E-mail", related="partner_id.email") + mobile = fields.Char("Mobile", related="partner_id.mobile") + phone = fields.Char("Phone", related="partner_id.phone") + partner_internal_comment = fields.Text( + string="Internal Partner Notes", related="partner_id.comment" + ) + folio_internal_comment = fields.Text( + string="Internal Folio Notes", related="folio_id.internal_comment" + ) + preconfirm = fields.Boolean("Auto confirm to Save", default=True) + to_send = fields.Boolean("To Send", default=True) + call_center = fields.Boolean(default="set_call_center_user") has_confirmed_reservations_to_send = fields.Boolean( - related='folio_id.has_confirmed_reservations_to_send', - readonly=True) + 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) + 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'), - ('to invoice', 'To Invoice'), - ('no', 'Nothing to Invoice')], - string='Invoice Status', - compute='_compute_invoice_status', - store=True, - readonly=True, - default='no') + 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"), + ("to invoice", "To Invoice"), + ("no", "Nothing to Invoice"), + ], + string="Invoice Status", + compute="_compute_invoice_status", + store=True, + readonly=True, + default="no", + ) qty_to_invoice = fields.Float( - compute='_get_to_invoice_qty', - string='To Invoice', + compute="_get_to_invoice_qty", + string="To Invoice", store=True, readonly=True, - digits=('Product Unit of Measure')) + digits=("Product Unit of Measure"), + ) qty_invoiced = fields.Float( - compute='_get_invoice_qty', - string='Invoiced', + compute="_get_invoice_qty", + string="Invoiced", store=True, readonly=True, - digits=('Product Unit of Measure')) + digits=("Product Unit of Measure"), + ) price_subtotal = fields.Monetary( - string='Subtotal', + string="Subtotal", readonly=True, store=True, - digits=('Product Price'), - compute='_compute_amount_reservation') + digits=("Product Price"), + compute="_compute_amount_reservation", + ) price_total = fields.Monetary( - string='Total', + string="Total", readonly=True, store=True, - digits=('Product Price'), - compute='_compute_amount_reservation') + digits=("Product Price"), + compute="_compute_amount_reservation", + ) price_tax = fields.Float( - string='Taxes Amount', + string="Taxes Amount", readonly=True, store=True, - compute='_compute_amount_reservation') + compute="_compute_amount_reservation", + ) price_services = fields.Monetary( - string='Services Total', + string="Services Total", readonly=True, store=True, - digits=('Product Price'), - compute='_compute_amount_room_services') + digits=("Product Price"), + compute="_compute_amount_room_services", + ) price_room_services_set = fields.Monetary( - string='Room Services Total', + string="Room Services Total", readonly=True, store=True, - digits=('Product Price'), - compute='_compute_amount_set') + digits=("Product Price"), + compute="_compute_amount_set", + ) discount = fields.Float( - string='Discount (€)', - digits=('Discount'), - compute='_compute_discount', - store=True) + string="Discount (€)", + digits=("Discount"), + compute="_compute_discount", + store=True, + ) # Compute and Search methods - @api.depends('state', 'qty_to_invoice', 'qty_invoiced') + @api.depends("state", "qty_to_invoice", "qty_invoiced") def _compute_invoice_status(self): """ Compute the invoice status of a Reservation. Possible statuses: @@ -356,22 +366,27 @@ class PmsReservation(models.Model): - invoiced: the quantity invoiced is larger or equal to the quantity ordered. """ - precision = self.env['decimal.precision'].precision_get( - 'Product Unit of Measure') + precision = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) for line in self: - if line.state in ('draft'): - line.invoice_status = 'no' - elif not float_is_zero(line.qty_to_invoice, - precision_digits=precision): - line.invoice_status = 'to invoice' - elif float_compare(line.qty_invoiced, - len(line.reservation_line_ids), - precision_digits=precision) >= 0: - line.invoice_status = 'invoiced' + if line.state in ("draft"): + line.invoice_status = "no" + elif not float_is_zero(line.qty_to_invoice, precision_digits=precision): + line.invoice_status = "to invoice" + elif ( + float_compare( + line.qty_invoiced, + len(line.reservation_line_ids), + precision_digits=precision, + ) + >= 0 + ): + line.invoice_status = "invoiced" else: - line.invoice_status = 'no' + line.invoice_status = "no" - @api.depends('qty_invoiced', 'nights', 'folio_id.state') + @api.depends("qty_invoiced", "nights", "folio_id.state") def _get_to_invoice_qty(self): """ Compute the quantity to invoice. If the invoice policy is order, @@ -379,14 +394,12 @@ class PmsReservation(models.Model): Otherwise, the quantity delivered is used. """ for line in self: - if line.folio_id.state not in ['draft']: - line.qty_to_invoice = len( - line.reservation_line_ids) - line.qty_invoiced + if line.folio_id.state not in ["draft"]: + line.qty_to_invoice = len(line.reservation_line_ids) - line.qty_invoiced else: line.qty_to_invoice = 0 - @api.depends('move_line_ids.move_id.state', - 'move_line_ids.quantity') + @api.depends("move_line_ids.move_id.state", "move_line_ids.quantity") def _get_invoice_qty(self): """ Compute the quantity invoiced. If case of a refund, the quantity @@ -397,204 +410,221 @@ class PmsReservation(models.Model): qty_invoiced = 0.0 for day in line.reservation_line_ids: invoice_lines = day.move_line_ids.filtered( - lambda r: r.move_id.state != 'cancel') - qty_invoiced += len(invoice_lines.filtered( - lambda r: r.move_id.type == 'out_invoice') - ) - len(invoice_lines.filtered( - lambda r: r.move_id.type == - 'out_refund')) + lambda r: r.move_id.state != "cancel" + ) + qty_invoiced += len( + invoice_lines.filtered(lambda r: r.move_id.type == "out_invoice") + ) - len( + invoice_lines.filtered(lambda r: r.move_id.type == "out_refund") + ) line.qty_invoiced = qty_invoiced - @api.depends('checkin', 'checkout') + @api.depends("checkin", "checkout") def _computed_nights(self): for res in self: if res.checkin and res.checkout: res.nights = ( - fields.Date.from_string( - res.checkout) - fields.Date.from_string(res.checkin) + fields.Date.from_string(res.checkout) + - fields.Date.from_string(res.checkin) ).days - @api.depends('folio_id', 'checkin', 'checkout') + @api.depends("folio_id", "checkin", "checkout") def _compute_localizator(self): for record in self: if record.folio_id: - #TODO: Review Datetime type no char v13 + # TODO: Review Datetime type no char v13 localizator = re.sub("[^0-9]", "", record.folio_id.name) # checkout = int(re.sub("[^0-9]", "", record.checkout)) # checkin = int(re.sub("[^0-9]", "", record.checkin)) # localizator += str((checkin + checkout) % 99) record.localizator = localizator - @api.depends('service_ids.price_total') + @api.depends("service_ids.price_total") def _compute_amount_room_services(self): for record in self: - record.price_services = sum( - record.mapped('service_ids.price_total')) + record.price_services = sum(record.mapped("service_ids.price_total")) - @api.depends('price_services', 'price_total') + @api.depends("price_services", "price_total") def _compute_amount_set(self): for record in self: - record.price_room_services_set = record.price_services + \ - record.price_total + record.price_room_services_set = record.price_services + record.price_total - @api.depends('reservation_line_ids.discount', - 'reservation_line_ids.cancel_discount') + @api.depends( + "reservation_line_ids.discount", "reservation_line_ids.cancel_discount" + ) def _compute_discount(self): for record in self: discount = 0 for line in record.reservation_line_ids: first_discount = line.price * ((line.discount or 0.0) * 0.01) price = line.price - first_discount - cancel_discount = price * \ - ((line.cancel_discount or 0.0) * 0.01) + cancel_discount = price * ((line.cancel_discount or 0.0) * 0.01) discount += first_discount + cancel_discount record.discount = discount - @api.depends('reservation_line_ids.price', 'discount', 'tax_ids') + @api.depends("reservation_line_ids.price", "discount", "tax_ids") def _compute_amount_reservation(self): """ Compute the amounts of the reservation. """ for record in self: - amount_room = sum(record.reservation_line_ids.mapped('price')) + amount_room = sum(record.reservation_line_ids.mapped("price")) if amount_room > 0: product = record.room_type_id.product_id price = amount_room - record.discount taxes = record.tax_ids.compute_all( - price, record.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'], - }) + price, record.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"], + } + ) # Constraints and onchanges - @api.constrains('adults') + @api.constrains("adults") def _check_adults(self): for record in self: extra_bed = record.service_ids.filtered( - lambda r: r.product_id.is_extra_bed is True) + lambda r: r.product_id.is_extra_bed is True + ) if record.adults > record.room_id.get_capacity(len(extra_bed)): - raise ValidationError( - _("Persons can't be higher than room capacity")) + raise ValidationError(_("Persons can't be higher than room capacity")) if record.adults == 0: raise ValidationError(_("Reservation has no adults")) # TODO: Use default values on checkin /checkout is empty - @api.constrains('checkin', 'checkout', 'state', - 'room_id', 'overbooking', 'reselling') + @api.constrains( + "checkin", "checkout", "state", "room_id", "overbooking", "reselling" + ) def check_dates(self): """ 1.-When date_order is less then checkin date or Checkout date should be greater than the checkin date. 3.-Check the reservation dates are not occuped """ - _logger.info('check_dates') + _logger.info("check_dates") if fields.Date.from_string(self.checkin) >= fields.Date.from_string( - self.checkout): - raise ValidationError(_('Room line Check In Date Should be \ - less than the Check Out Date!')) - if not self.overbooking \ - and self.state not in ('cancelled') \ - and not self._context.get("ignore_avail_restrictions", False): - occupied = self.env['pms.reservation'].get_reservations( + self.checkout + ): + raise ValidationError( + _( + "Room line Check In Date Should be \ + less than the Check Out Date!" + ) + ) + if ( + not self.overbooking + and self.state not in ("cancelled") + and not self._context.get("ignore_avail_restrictions", False) + ): + occupied = self.env["pms.reservation"].get_reservations( self.checkin, - (fields.Date.from_string(self.checkout) - timedelta(days=1)). - strftime(DEFAULT_SERVER_DATE_FORMAT)) + (fields.Date.from_string(self.checkout) - timedelta(days=1)).strftime( + DEFAULT_SERVER_DATE_FORMAT + ), + ) occupied = occupied.filtered( - lambda r: r.room_id.id == self.room_id.id and - r.id != self.id) - occupied_name = ', '.join(str(x.folio_id.name) for x in occupied) + lambda r: r.room_id.id == self.room_id.id and r.id != self.id + ) + occupied_name = ", ".join(str(x.folio_id.name) for x in occupied) if occupied: - warning_msg = _('You tried to change/confirm \ + warning_msg = ( + _( + "You tried to change/confirm \ reservation with room those already reserved in this \ - reservation period: %s ') % occupied_name + reservation period: %s " + ) + % occupied_name + ) raise ValidationError(warning_msg) - - @api.constrains('checkin_partner_ids') + @api.constrains("checkin_partner_ids") def _max_checkin_partner_ids(self): for record in self: - if len(record.checkin_partner_ids) > record.adults + \ - record.children: - raise models.ValidationError( - _('The room already is completed')) + if len(record.checkin_partner_ids) > record.adults + record.children: + raise models.ValidationError(_("The room already is completed")) - @api.onchange('adults', 'room_id') + @api.onchange("adults", "room_id") def onchange_room_id(self): if self.room_id: write_vals = {} extra_bed = self.service_ids.filtered( - lambda r: r.product_id.is_extra_bed is True) + lambda r: r.product_id.is_extra_bed is True + ) if self.room_id.get_capacity(len(extra_bed)) < self.adults: raise UserError( - _('%s people do not fit in this room! ;)') % (self.adults)) + _("%s people do not fit in this room! ;)") % (self.adults) + ) if self.adults == 0: - write_vals.update({'adults': self.room_id.capacity}) + write_vals.update({"adults": self.room_id.capacity}) if not self.room_type_id: - write_vals.update( - {'room_type_id': self.room_id.room_type_id.id}) + write_vals.update({"room_type_id": self.room_id.room_type_id.id}) self.update(write_vals) - @api.onchange('cancelled_reason') + @api.onchange("cancelled_reason") def onchange_cancelled_reason(self): for record in self: record._compute_cancelled_discount() - @api.onchange('partner_id') + @api.onchange("partner_id") def onchange_partner_id(self): - addr = self.partner_id.address_get(['invoice']) - 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 + addr = self.partner_id.address_get(["invoice"]) + 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 + ) values = { - 'pricelist_id': pricelist, - 'partner_invoice_id': addr['invoice'], + "pricelist_id": pricelist, + "partner_invoice_id": addr["invoice"], } self.update(values) - - @api.onchange('pricelist_id') + @api.onchange("pricelist_id") def onchange_pricelist_id(self): - values = {'reservation_type': self.env['pms.folio']. - calcule_reservation_type( - self.pricelist_id.is_staff, - self.reservation_type)} + values = { + "reservation_type": self.env["pms.folio"].calcule_reservation_type( + self.pricelist_id.is_staff, self.reservation_type + ) + } self.update(values) - @api.onchange('reservation_type') + @api.onchange("reservation_type") def assign_partner_company_on_out_service(self): - if self.reservation_type == 'out': - self.update({'partner_id': - self.env.user.company_id.partner_id.id}) + if self.reservation_type == "out": + self.update({"partner_id": self.env.user.company_id.partner_id.id}) - - @api.onchange('checkin_partner_ids') + @api.onchange("checkin_partner_ids") def onchange_checkin_partner_ids(self): for record in self: - if len(record.checkin_partner_ids) > \ - record.adults + record.children: - raise models.ValidationError( - _('The room already is completed')) + if len(record.checkin_partner_ids) > record.adults + record.children: + raise models.ValidationError(_("The room already is completed")) - @api.onchange('room_type_id', 'pricelist_id', 'reservation_type') + @api.onchange("room_type_id", "pricelist_id", "reservation_type") def onchange_overwrite_price_by_day(self): """ We need to overwrite the prices even if they were already established """ if self.room_type_id and self.checkin and self.checkout: days_diff = ( - fields.Date.from_string(self.checkout) - - fields.Date.from_string(self.checkin) + fields.Date.from_string(self.checkout) + - fields.Date.from_string(self.checkin) ).days - self.update(self.prepare_reservation_lines( - self.checkin, - days_diff, - self.pricelist_id.id, - update_old_prices=True)) + self.update( + self.prepare_reservation_lines( + self.checkin, + days_diff, + self.pricelist_id.id, + update_old_prices=True, + ) + ) - @api.onchange('checkin', 'checkout') + @api.onchange("checkin", "checkout") def onchange_dates(self): """ We need to update prices respecting those that were already established @@ -606,21 +636,24 @@ class PmsReservation(models.Model): checkin_dt = fields.Date.from_string(self.checkin) checkout_dt = fields.Date.from_string(self.checkout) if checkin_dt >= checkout_dt: - self.checkout = (fields.Date.from_string(self.checkin) + - timedelta(days=1)).strftime( - DEFAULT_SERVER_DATE_FORMAT) + self.checkout = ( + fields.Date.from_string(self.checkin) + timedelta(days=1) + ).strftime(DEFAULT_SERVER_DATE_FORMAT) if self.room_type_id: days_diff = ( - fields.Date.from_string(self.checkout) - - fields.Date.from_string(self.checkin) + fields.Date.from_string(self.checkout) + - fields.Date.from_string(self.checkin) ).days - self.update(self.prepare_reservation_lines( - self.checkin, - days_diff, - self.pricelist_id.id, - update_old_prices=False)) + self.update( + self.prepare_reservation_lines( + self.checkin, + days_diff, + self.pricelist_id.id, + update_old_prices=False, + ) + ) - @api.onchange('checkin', 'checkout', 'room_type_id') + @api.onchange("checkin", "checkout", "room_type_id") def onchange_room_type_id(self): """ When change de room_type_id, we calc the line description and tax_ids @@ -628,48 +661,52 @@ class PmsReservation(models.Model): if self.room_type_id and self.checkin and self.checkout: checkin_dt = fields.Date.from_string(self.checkin) checkout_dt = fields.Date.from_string(self.checkout) - checkin_str = checkin_dt.strftime('%d/%m/%Y') - checkout_str = checkout_dt.strftime('%d/%m/%Y') - self.name = self.room_type_id.name + ': ' + checkin_str + ' - '\ - + checkout_str + checkin_str = checkin_dt.strftime("%d/%m/%Y") + checkout_str = checkout_dt.strftime("%d/%m/%Y") + self.name = ( + self.room_type_id.name + ": " + checkin_str + " - " + checkout_str + ) self._compute_tax_ids() - @api.onchange('checkin', 'checkout') + @api.onchange("checkin", "checkout") def onchange_update_service_per_day(self): services = self.service_ids.filtered(lambda r: r.per_day is True) for service in services: service.onchange_product_id() - - @api.onchange('checkin', 'checkout', 'room_id') + @api.onchange("checkin", "checkout", "room_id") def onchange_room_availabiltiy_domain(self): self.ensure_one() if self.checkin and self.checkout: - if self.overbooking or self.reselling or \ - self.state in ('cancelled'): + if self.overbooking or self.reselling or self.state in ("cancelled"): return - occupied = self.env['pms.reservation'].get_reservations( + occupied = self.env["pms.reservation"].get_reservations( self.checkin, - (fields.Date.from_string(self.checkout) - timedelta(days=1)). - strftime(DEFAULT_SERVER_DATE_FORMAT)) - rooms_occupied = occupied.mapped('room_id.id') + (fields.Date.from_string(self.checkout) - timedelta(days=1)).strftime( + DEFAULT_SERVER_DATE_FORMAT + ), + ) + rooms_occupied = occupied.mapped("room_id.id") if self.room_id: occupied = occupied.filtered( - lambda r: r.room_id.id == self.room_id.id and - r.id != self._origin.id) + lambda r: r.room_id.id == self.room_id.id + and r.id != self._origin.id + ) if occupied: - occupied_name = ', '.join( - str(x.folio_id.name) for x in occupied) - warning_msg = _('You tried to change/confirm \ + occupied_name = ", ".join(str(x.folio_id.name) for x in occupied) + warning_msg = ( + _( + "You tried to change/confirm \ reservation with room those already reserved in this \ - reservation period: %s ') % occupied_name + reservation period: %s " + ) + % occupied_name + ) raise ValidationError(warning_msg) - domain_rooms = [ - ('id', 'not in', rooms_occupied) - ] - return {'domain': {'room_id': domain_rooms}} + domain_rooms = [("id", "not in", rooms_occupied)] + return {"domain": {"room_id": domain_rooms}} - @api.onchange('board_service_room_id') + @api.onchange("board_service_room_id") def onchange_board_service(self): if self.board_service_room_id: board_services = [(5, 0, 0)] @@ -677,262 +714,264 @@ class PmsReservation(models.Model): product = line.product_id if product.per_day: res = { - 'product_id': product.id, - 'is_board_service': True, - 'folio_id': self.folio_id.id, - 'reservation_id': self.id, + "product_id": product.id, + "is_board_service": True, + "folio_id": self.folio_id.id, + "reservation_id": self.id, } - line = self.env['pms.service'].new(res) + line = self.env["pms.service"].new(res) + res.update(self.env["pms.service"]._prepare_add_missing_fields(res)) res.update( - self.env['pms.service']._prepare_add_missing_fields( - res)) - res.update(self.env['pms.service'].prepare_service_ids( - dfrom=self.checkin, - days=self.nights, - per_person=product.per_person, - persons=self.adults, - old_line_days=False, - consumed_on=product.consumed_on,)) + self.env["pms.service"].prepare_service_ids( + dfrom=self.checkin, + days=self.nights, + per_person=product.per_person, + persons=self.adults, + old_line_days=False, + consumed_on=product.consumed_on, + ) + ) board_services.append((0, False, res)) - other_services = self.service_ids.filtered( - lambda r: not r.is_board_service) - self.update({'service_ids': board_services}) + other_services = self.service_ids.filtered(lambda r: not r.is_board_service) + self.update({"service_ids": board_services}) self.service_ids |= other_services - for service in self.service_ids.filtered( - lambda r: r.is_board_service): + for service in self.service_ids.filtered(lambda r: r.is_board_service): service._compute_tax_ids() service.price_unit = service._compute_price_unit() # 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] + 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)] + 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] + 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} + 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} + 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] + action = self.env.ref("pms.open_pms_folio1_form_tree_all").read()[0] if self.folio_id: - action['views'] = [ - (self.env.ref('pms.pms_folio_view_form').id, 'form')] - action['res_id'] = self.folio_id.id + action["views"] = [(self.env.ref("pms.pms_folio_view_form").id, "form")] + action["res_id"] = self.folio_id.id else: - action = {'type': 'ir.actions.act_window_close'} + action = {"type": "ir.actions.act_window_close"} return action - def open_reservation_form(self): - action = self.env.ref( - 'pms.open_pms_reservation_form_tree_all').read()[0] - action['views'] = [ - (self.env.ref('pms.pms_reservation_view_form').id, 'form')] - action['res_id'] = self.id + action = self.env.ref("pms.open_pms_reservation_form_tree_all").read()[0] + action["views"] = [(self.env.ref("pms.pms_reservation_view_form").id, "form")] + action["res_id"] = self.id return action - def action_pay_folio(self): self.ensure_one() return self.folio_id.action_pay() - def action_pay_reservation(self): self.ensure_one() partner = self.partner_id.id amount = min(self.price_room_services_set, self.folio_pending_amount) - note = self.folio_id.name + ' (' + self.name + ')' - view_id = self.env.ref('pms.account_payment_view_form_folio').id - return{ - 'name': _('Register Payment'), - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'account.payment', - 'type': 'ir.actions.act_window', - 'view_id': view_id, - 'context': { - 'default_folio_id': self.folio_id.id, - 'default_room_id': self.id, - 'default_amount': amount, - 'default_payment_type': 'inbound', - 'default_partner_type': 'customer', - 'default_partner_id': partner, - 'default_communication': note, + note = self.folio_id.name + " (" + self.name + ")" + view_id = self.env.ref("pms.account_payment_view_form_folio").id + return { + "name": _("Register Payment"), + "view_type": "form", + "view_mode": "form", + "res_model": "account.payment", + "type": "ir.actions.act_window", + "view_id": view_id, + "context": { + "default_folio_id": self.folio_id.id, + "default_room_id": self.id, + "default_amount": amount, + "default_payment_type": "inbound", + "default_partner_type": "customer", + "default_partner_id": partner, + "default_communication": note, }, - 'target': 'new', + "target": "new", } # ORM Overrides @api.model - def name_search(self, name='', args=None, operator='ilike', limit=100): + def name_search(self, name="", args=None, operator="ilike", limit=100): if args is None: args = [] - if not(name == '' and operator == 'ilike'): + if not (name == "" and operator == "ilike"): args += [ - '|', - ('folio_id.name', operator, name), - ('room_id.name', operator, name) + "|", + ("folio_id.name", operator, name), + ("room_id.name", operator, name), ] return super(PmsReservation, self).name_search( - name='', args=args, operator='ilike', limit=limit) - + name="", args=args, operator="ilike", limit=limit + ) def name_get(self): result = [] for res in self: - name = u'%s (%s)' % (res.folio_id.name, res.room_id.name) + name = u"{} ({})".format(res.folio_id.name, res.room_id.name) result.append((res.id, name)) return result @api.model def create(self, vals): - if 'room_id' not in vals: + if "room_id" not in vals: vals.update(self._autoassign(vals)) vals.update(self._prepare_add_missing_fields(vals)) - if 'folio_id' in vals and 'channel_type' not in vals: - folio = self.env["pms.folio"].browse(vals['folio_id']) - vals.update({'channel_type': folio.channel_type}) - elif 'partner_id' in vals: - folio_vals = {'partner_id': int(vals.get('partner_id')), - 'channel_type': vals.get('channel_type')} + if "folio_id" in vals and "channel_type" not in vals: + folio = self.env["pms.folio"].browse(vals["folio_id"]) + vals.update({"channel_type": folio.channel_type}) + elif "partner_id" in vals: + folio_vals = { + "partner_id": int(vals.get("partner_id")), + "channel_type": vals.get("channel_type"), + } # Create the folio in case of need # (To allow to create reservations direct) folio = self.env["pms.folio"].create(folio_vals) - vals.update({'folio_id': folio.id, - 'reservation_type': vals.get('reservation_type'), - 'channel_type': vals.get('channel_type')}) - if vals.get('service_ids'): - for service in vals['service_ids']: + vals.update( + { + "folio_id": folio.id, + "reservation_type": vals.get("reservation_type"), + "channel_type": vals.get("channel_type"), + } + ) + if vals.get("service_ids"): + for service in vals["service_ids"]: if service[2]: - service[2]['folio_id'] = folio.id - user = self.env['res.users'].browse(self.env.uid) - if user.has_group('pms.group_pms_call'): - vals.update({ - 'to_assign': True, - 'channel_type': 'call' - }) - vals.update({ - 'last_updated_res': fields.Datetime.now(), - }) + service[2]["folio_id"] = folio.id + user = self.env["res.users"].browse(self.env.uid) + if user.has_group("pms.group_pms_call"): + vals.update({"to_assign": True, "channel_type": "call"}) + vals.update( + {"last_updated_res": fields.Datetime.now(),} + ) if self.compute_price_out_vals(vals): days_diff = ( - fields.Date.from_string( - vals['checkout']) - fields.Date.from_string( - vals['checkin']) + fields.Date.from_string(vals["checkout"]) + - fields.Date.from_string(vals["checkin"]) ).days - vals.update(self.prepare_reservation_lines( - vals['checkin'], - days_diff, - vals['pricelist_id'], - vals=vals)) # REVISAR el unlink - if 'checkin' in vals and 'checkout' in vals \ - and 'real_checkin' not in vals and 'real_checkout' not in vals: - vals['real_checkin'] = vals['checkin'] - vals['real_checkout'] = vals['checkout'] + vals.update( + self.prepare_reservation_lines( + vals["checkin"], days_diff, vals["pricelist_id"], vals=vals + ) + ) # REVISAR el unlink + if ( + "checkin" in vals + and "checkout" in vals + and "real_checkin" not in vals + and "real_checkout" not in vals + ): + vals["real_checkin"] = vals["checkin"] + vals["real_checkout"] = vals["checkout"] record = super(PmsReservation, self).create(vals) if record.preconfirm: record.confirm() return record - def write(self, vals): if self.notify_update(vals): - vals.update({ - 'last_updated_res': fields.Datetime.now() - }) + vals.update({"last_updated_res": fields.Datetime.now()}) for record in self: - checkin = vals['checkin'] if 'checkin' in vals \ - else record.checkin - checkout = vals['checkout'] if 'checkout' in vals \ - else record.checkout - if not record.splitted and not vals.get('splitted', False): - if 'checkin' in vals: - vals['real_checkin'] = vals['checkin'] - if 'checkout' in vals: - vals['real_checkout'] = vals['checkout'] + checkin = vals["checkin"] if "checkin" in vals else record.checkin + checkout = vals["checkout"] if "checkout" in vals else record.checkout + if not record.splitted and not vals.get("splitted", False): + if "checkin" in vals: + vals["real_checkin"] = vals["checkin"] + if "checkout" in vals: + vals["real_checkout"] = vals["checkout"] - real_checkin = vals['real_checkin'] if 'real_checkin' in vals \ - else record.real_checkin - real_checkout = vals['real_checkout'] if 'real_checkout' in vals \ + real_checkin = ( + vals["real_checkin"] if "real_checkin" in vals else record.real_checkin + ) + real_checkout = ( + vals["real_checkout"] + if "real_checkout" in vals else record.real_checkout + ) days_diff = ( - fields.Date.from_string(checkout) - - fields.Date.from_string(checkin) + fields.Date.from_string(checkout) - fields.Date.from_string(checkin) ).days if self.compute_board_services(vals): record.service_ids.filtered( - lambda r: r.is_board_service is True).unlink() + lambda r: r.is_board_service is True + ).unlink() board_services = [] - board = self.env['pms.board.service.room.type'].browse( - vals['board_service_room_id']) + board = self.env["pms.board.service.room.type"].browse( + vals["board_service_room_id"] + ) for line in board.board_service_line_ids: res = { - 'product_id': line.product_id.id, - 'is_board_service': True, - 'folio_id': vals.get('folio_id'), - 'reservation_id': self.id, + "product_id": line.product_id.id, + "is_board_service": True, + "folio_id": vals.get("folio_id"), + "reservation_id": self.id, } - res.update( - self.env['pms.service']._prepare_add_missing_fields( - res)) + res.update(self.env["pms.service"]._prepare_add_missing_fields(res)) board_services.append((0, False, res)) # REVIEW: Why I need add manually the old IDs if # board service is (0,0,(-)) ¿?¿?¿ record.update( - {'service_ids': [(6, 0, record.service_ids.ids)] + - board_services}) + {"service_ids": [(6, 0, record.service_ids.ids)] + board_services} + ) if record.compute_price_out_vals(vals): - pricelist_id = vals['pricelist_id'] if 'pricelist_id' in \ - vals else record.pricelist_id.id - record.update(record.prepare_reservation_lines( - checkin, - days_diff, - pricelist_id, - vals=vals)) # REVIEW unlink + pricelist_id = ( + vals["pricelist_id"] + if "pricelist_id" in vals + else record.pricelist_id.id + ) + record.update( + record.prepare_reservation_lines( + checkin, days_diff, pricelist_id, vals=vals + ) + ) # REVIEW unlink if record.compute_qty_service_day(vals): service_days_diff = ( - fields.Date.from_string(real_checkout) - - fields.Date.from_string(real_checkin) + fields.Date.from_string(real_checkout) + - fields.Date.from_string(real_checkin) ).days for service in record.service_ids: if service.product_id.per_day: - service.update(service.prepare_service_ids( - dfrom=real_checkin, - days=service_days_diff, - per_person=service.product_id.per_person, - persons=service.reservation_id.adults, - old_line_days=service.service_line_ids, - consumed_on=service.product_id.consumed_on, - )) - if ('checkin' in vals and record.checkin != vals['checkin']) or\ - ('checkout' in vals and record.checkout != vals['checkout']) or\ - ('state' in vals and record.state != vals['state']): - record.update({'to_send': True}) - user = self.env['res.users'].browse(self.env.uid) - if user.has_group('pms.group_pms_call'): - vals.update({ - 'to_assign': True, - }) + service.update( + service.prepare_service_ids( + dfrom=real_checkin, + days=service_days_diff, + per_person=service.product_id.per_person, + persons=service.reservation_id.adults, + old_line_days=service.service_line_ids, + consumed_on=service.product_id.consumed_on, + ) + ) + if ( + ("checkin" in vals and record.checkin != vals["checkin"]) + or ("checkout" in vals and record.checkout != vals["checkout"]) + or ("state" in vals and record.state != vals["state"]) + ): + record.update({"to_send": True}) + user = self.env["res.users"].browse(self.env.uid) + if user.has_group("pms.group_pms_call"): + vals.update( + {"to_assign": True,} + ) record = super(PmsReservation, self).write(vals) return record @@ -943,27 +982,28 @@ class PmsReservation(models.Model): # Yes?, then, this is share folio ;) for record in self: if record.folio_id: - record.shared_folio = \ - len(record.folio_id.reservation_ids) > 1 \ - or any(record.folio_id.service_ids.filtered( - lambda x: x.reservation_id.id != record.id)) - + record.shared_folio = len(record.folio_id.reservation_ids) > 1 or any( + record.folio_id.service_ids.filtered( + lambda x: x.reservation_id.id != record.id + ) + ) def compute_board_services(self, vals): """ We must compute service_ids when we have a board_service_id without service_ids associated to reservation """ - if 'board_service_room_id' in vals: - if 'service_ids' in vals: - for service in vals['service_ids']: - if 'is_board_service' in service[2] and \ - service[2]['is_board_service'] is True: + if "board_service_room_id" in vals: + if "service_ids" in vals: + for service in vals["service_ids"]: + if ( + "is_board_service" in service[2] + and service[2]["is_board_service"] is True + ): return False return True return False - def compute_qty_service_day(self, vals): """ Compute if it is necesary calc price in write/create @@ -971,11 +1011,13 @@ class PmsReservation(models.Model): self.ensure_one() if not vals: vals = {} - if 'service_ids' in vals: + if "service_ids" in vals: return False - if ('checkin' in vals and self.checkin != vals['checkin']) or \ - ('checkout' in vals and self.checkout != vals['checkout']) or \ - ('adults' in vals and self.checkout != vals['adults']): + if ( + ("checkin" in vals and self.checkin != vals["checkin"]) + or ("checkout" in vals and self.checkout != vals["checkout"]) + or ("adults" in vals and self.checkout != vals["adults"]) + ): return True return False @@ -983,225 +1025,216 @@ class PmsReservation(models.Model): def _prepare_add_missing_fields(self, values): """ Deduce missing required fields from the onchange """ res = {} - onchange_fields = ['room_id', 'tax_ids', - 'currency_id', 'name', 'service_ids'] - if values.get('room_type_id'): - if not values.get('reservation_type'): - values['reservation_type'] = 'normal' + onchange_fields = ["room_id", "tax_ids", "currency_id", "name", "service_ids"] + if values.get("room_type_id"): + if not values.get("reservation_type"): + values["reservation_type"] = "normal" line = self.new(values) if any(f not in values for f in onchange_fields): line.onchange_room_id() line.onchange_room_type_id() - if 'pricelist_id' not in values: + if "pricelist_id" not in values: line.onchange_partner_id() - onchange_fields.append('pricelist_id') + onchange_fields.append("pricelist_id") for field in onchange_fields: - if field == 'service_ids': + if field == "service_ids": if self.compute_board_services(values): line.onchange_board_service() res[field] = line._fields[field].convert_to_write( - line[field], line) + line[field], line + ) if field not in values: - res[field] = line._fields[field].convert_to_write( - line[field], line) + res[field] = line._fields[field].convert_to_write(line[field], line) return res @api.model def _autoassign(self, values): res = {} - checkin = values.get('checkin') - checkout = values.get('checkout') - room_type_id = values.get('room_type_id') + checkin = values.get("checkin") + checkout = values.get("checkout") + room_type_id = values.get("room_type_id") if checkin and checkout and room_type_id: - if 'overbooking' not in values: - room_chosen = self.env['pms.room.type'].\ - check_availability_room_type( - checkin, - (fields.Date.from_string(checkout) - - timedelta(days=1)).strftime( - DEFAULT_SERVER_DATE_FORMAT - ), - room_type_id)[0] + if "overbooking" not in values: + room_chosen = self.env["pms.room.type"].check_availability_room_type( + checkin, + (fields.Date.from_string(checkout) - timedelta(days=1)).strftime( + DEFAULT_SERVER_DATE_FORMAT + ), + room_type_id, + )[0] # Check room_chosen exist else: - room_chosen = self.env['pms.room.type'].browse( - room_type_id).room_ids[0] - res.update({ - 'room_id': room_chosen.id - }) + room_chosen = self.env["pms.room.type"].browse(room_type_id).room_ids[0] + res.update({"room_id": room_chosen.id}) return res @api.model def autocheckout(self): - reservations = self.env['pms.reservation'].search([ - ('state', 'not in', ('done', 'cancelled')), - ('checkout', '<', fields.Date.today()) - ]) + reservations = self.env["pms.reservation"].search( + [ + ("state", "not in", ("done", "cancelled")), + ("checkout", "<", fields.Date.today()), + ] + ) for res in reservations: res.action_reservation_checkout() - res_without_checkin = reservations.filtered( - lambda r: r.state != 'booking') + res_without_checkin = reservations.filtered(lambda r: r.state != "booking") for res in res_without_checkin: msg = _("No checkin was made for this reservation") - res.message_post(subject=_('No Checkins!'), - subtype='mt_comment', body=msg) + res.message_post(subject=_("No Checkins!"), subtype="mt_comment", body=msg) return True - def notify_update(self, vals): - if 'checkin' in vals or \ - 'checkout' in vals or \ - 'discount' in vals or \ - 'state' in vals or \ - 'room_type_id' in vals or \ - 'to_assign' in vals: + if ( + "checkin" in vals + or "checkout" in vals + or "discount" in vals + or "state" in vals + or "room_type_id" in vals + or "to_assign" in vals + ): return True return False - def overbooking_button(self): self.ensure_one() self.overbooking = not self.overbooking - def generate_copy_values(self, checkin=False, checkout=False): self.ensure_one() return { - 'name': self.name, - 'adults': self.adults, - 'children': self.children, - '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, - 'price_total': self.price_total, - 'price_tax': self.price_tax, - 'price_subtotal': self.price_subtotal, - 'splitted': self.splitted, - 'room_type_id': self.room_type_id.id, - 'room_id': self.room_id.id, - 'real_checkin': self.real_checkin, - 'real_checkout': self.real_checkout, + "name": self.name, + "adults": self.adults, + "children": self.children, + "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, + "price_total": self.price_total, + "price_tax": self.price_tax, + "price_subtotal": self.price_subtotal, + "splitted": self.splitted, + "room_type_id": self.room_type_id.id, + "room_id": self.room_id.id, + "real_checkin": self.real_checkin, + "real_checkout": self.real_checkout, } """ STATE WORKFLOW ----------------------------------------------------- """ - def confirm(self): - ''' + """ @param self: object pointer - ''' - _logger.info('confirm') - pms_reserv_obj = self.env['pms.reservation'] - user = self.env['res.users'].browse(self.env.uid) + """ + _logger.info("confirm") + pms_reserv_obj = self.env["pms.reservation"] + user = self.env["res.users"].browse(self.env.uid) for record in self: vals = {} - if user.has_group('pms.group_pms_call'): - vals.update({'channel_type': 'call'}) + if user.has_group("pms.group_pms_call"): + vals.update({"channel_type": "call"}) if record.checkin_partner_ids: - vals.update({'state': 'booking'}) + vals.update({"state": "booking"}) else: - vals.update({'state': 'confirm'}) + vals.update({"state": "confirm"}) record.write(vals) - record.reservation_line_ids.update({ - 'cancel_discount': 0 - }) - if record.folio_id.state != 'confirm': + 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')) - ]) + 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'}) + record.update({"state": "booking"}) splitted_reservs.confirm() return True - def button_done(self): - ''' + """ @param self: object pointer - ''' + """ for record in self: record.action_reservation_checkout() return True - def action_cancel(self): for record in self: - cancel_reason = 'intime' if self._context.get( - "no_penalty", False) \ + cancel_reason = ( + "intime" + if self._context.get("no_penalty", False) else record.compute_cancelation_reason() + ) if self._context.get("no_penalty", False): _logger.info("Modified Reservation - No Penalty") - record.write({ - 'state': 'cancelled', - 'cancelled_reason': cancel_reason - }) + 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 = 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): self.ensure_one() pricelist = self.pricelist_id if pricelist and pricelist.cancelation_rule_id: tz_property = self.env.user.pms_property_id.tz - today = fields.Date.context_today(self.with_context( - tz=tz_property)) - days_diff = (fields.Date.from_string(self.real_checkin) - - fields.Date.from_string(today)).days + today = fields.Date.context_today(self.with_context(tz=tz_property)) + days_diff = ( + fields.Date.from_string(self.real_checkin) + - fields.Date.from_string(today) + ).days if days_diff < 0: - return 'noshow' + return "noshow" elif days_diff < pricelist.cancelation_rule_id.days_intime: - return 'late' + return "late" else: - return 'intime' + return "intime" return False - def draft(self): for record in self: - record.state = 'draft' - record.reservation_line_ids.update({ - 'cancel_discount': 0 - }) + 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 = 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() """ @@ -1214,87 +1247,87 @@ class PmsReservation(models.Model): """ if not vals: vals = {} - if ('reservation_line_ids' not in vals and - ('checkout' in vals or 'checkin' in vals or - 'room_type_id' in vals or 'pricelist_id' in vals)): + if "reservation_line_ids" not in vals and ( + "checkout" in vals + or "checkin" in vals + or "room_type_id" in vals + or "pricelist_id" in vals + ): return True return False - def _compute_cancelled_discount(self): self.ensure_one() pricelist = self.pricelist_id - if self.state == 'cancelled': + if self.state == "cancelled": # REVIEW: Set 0 qty on cancel room services # (view constrain service_line_days) for service in self.service_ids: - service.service_line_ids.write({'day_qty': 0}) + service.service_line_ids.write({"day_qty": 0}) service._compute_days_qty() - if self.cancelled_reason and pricelist and \ - pricelist.cancelation_rule_id: + if self.cancelled_reason and pricelist and pricelist.cancelation_rule_id: date_start_dt = fields.Date.from_string( - self.real_checkin or self.checkin) + self.real_checkin or self.checkin + ) date_end_dt = fields.Date.from_string( - self.real_checkout or self.checkout) + self.real_checkout or self.checkout + ) days = abs((date_end_dt - date_start_dt).days) rule = pricelist.cancelation_rule_id - if self.cancelled_reason == 'late': + if self.cancelled_reason == "late": discount = 100 - rule.penalty_late - if rule.apply_on_late == 'first': + if rule.apply_on_late == "first": days = 1 - elif rule.apply_on_late == 'days': + elif rule.apply_on_late == "days": days = rule.days_late - elif self.cancelled_reason == 'noshow': + elif self.cancelled_reason == "noshow": discount = 100 - rule.penalty_noshow - if rule.apply_on_noshow == 'first': + if rule.apply_on_noshow == "first": days = 1 - elif rule.apply_on_noshow == 'days': + elif rule.apply_on_noshow == "days": days = rule.days_late - 1 - elif self.cancelled_reason == 'intime': + elif self.cancelled_reason == "intime": discount = 100 checkin = self.real_checkin or self.checkin dates = [] for i in range(0, days): - dates.append((fields.Date.from_string(checkin) + - timedelta(days=i)).strftime( - DEFAULT_SERVER_DATE_FORMAT)) + dates.append( + (fields.Date.from_string(checkin) + timedelta(days=i)).strftime( + DEFAULT_SERVER_DATE_FORMAT + ) + ) + self.reservation_line_ids.filtered(lambda r: r.date in dates).update( + {"cancel_discount": discount} + ) self.reservation_line_ids.filtered( - lambda r: r.date in dates).update({ - 'cancel_discount': discount - }) - self.reservation_line_ids.filtered( - lambda r: r.date not in dates).update({ - 'cancel_discount': 100 - }) + lambda r: r.date not in dates + ).update({"cancel_discount": 100}) else: - self.reservation_line_ids.update({ - 'cancel_discount': 0 - }) + self.reservation_line_ids.update({"cancel_discount": 0}) else: - self.reservation_line_ids.update({ - 'cancel_discount': 0 - }) + self.reservation_line_ids.update({"cancel_discount": 0}) @api.model - def prepare_reservation_lines(self, dfrom, days, - pricelist_id, vals=False, - update_old_prices=False): + def prepare_reservation_lines( + self, dfrom, days, pricelist_id, vals=False, update_old_prices=False + ): discount = 0 cmds = [(5, 0, 0)] if not vals: vals = {} - room_type_id = vals.get('room_type_id') or self.room_type_id.id - product = self.env['pms.room.type'].browse(room_type_id).product_id - partner = self.env['res.partner'].browse( - vals.get('partner_id') or self.partner_id.id) - if 'discount' in vals and vals.get('discount') > 0: - discount = vals.get('discount') + room_type_id = vals.get("room_type_id") or self.room_type_id.id + product = self.env["pms.room.type"].browse(room_type_id).product_id + partner = self.env["res.partner"].browse( + vals.get("partner_id") or self.partner_id.id + ) + if "discount" in vals and vals.get("discount") > 0: + discount = vals.get("discount") for i in range(0, days): - idate = (fields.Date.from_string(dfrom) + - timedelta(days=i)).strftime(DEFAULT_SERVER_DATE_FORMAT) - old_line = self.reservation_line_ids.filtered( - lambda r: r.date == idate) + idate = (fields.Date.from_string(dfrom) + timedelta(days=i)).strftime( + DEFAULT_SERVER_DATE_FORMAT + ) + old_line = self.reservation_line_ids.filtered(lambda r: r.date == idate) if update_old_prices or not old_line: product = product.with_context( lang=partner.lang, @@ -1302,25 +1335,27 @@ class PmsReservation(models.Model): quantity=1, date=idate, pricelist=pricelist_id, - uom=product.uom_id.id) + uom=product.uom_id.id, + ) # REVIEW this forces to have configured the taxes # included in the price line_price = product.price if old_line and old_line.id: - cmds.append((1, old_line.id, { - 'price': line_price, - 'discount': discount - })) + cmds.append( + (1, old_line.id, {"price": line_price, "discount": discount}) + ) else: - cmds.append((0, False, { - 'date': idate, - 'price': line_price, - 'discount': discount - })) + cmds.append( + ( + 0, + False, + {"date": idate, "price": line_price, "discount": discount}, + ) + ) else: line_price = old_line.price cmds.append((4, old_line.id)) - return {'reservation_line_ids': cmds} + return {"reservation_line_ids": cmds} """ AVAILABILTY PROCESS ------------------------------------------------ @@ -1336,15 +1371,17 @@ class PmsReservation(models.Model): """ domain = self._get_domain_reservations_occupation(dfrom, dto) # _logger.info(domain) - return self.env['pms.reservation'].search(domain) + return self.env["pms.reservation"].search(domain) @api.model def _get_domain_reservations_occupation(self, dfrom, dto): - domain = [('reservation_line_ids.date', '>=', dfrom), - ('reservation_line_ids.date', '<=', dto), - ('state', '!=', 'cancelled'), - ('overbooking', '=', False), - ('reselling', '=', False)] + domain = [ + ("reservation_line_ids.date", ">=", dfrom), + ("reservation_line_ids.date", "<=", dto), + ("state", "!=", "cancelled"), + ("overbooking", "=", False), + ("reselling", "=", False), + ] return domain # INFO: This function is not in use and should include `dto` in the search @@ -1363,30 +1400,29 @@ class PmsReservation(models.Model): pms.reservation(36,)], } """ - domain = [('date', '>=', dfrom), - ('date', '<', dto)] - lines = self.env['pms.reservation.line'].search(domain) + domain = [("date", ">=", dfrom), ("date", "<", dto)] + lines = self.env["pms.reservation.line"].search(domain) reservations_dates = {} for record in lines: # kumari.net/index.php/programming/programmingcat/22-python-making-a-dictionary-of-lists-a-hash-of-arrays # reservations_dates.setdefault(record.date,[]).append(record.reservation_id.room_type_id) reservations_dates.setdefault(record.date, []).append( - [record.reservation_id, record.reservation_id.room_type_id]) + [record.reservation_id, record.reservation_id.room_type_id] + ) return reservations_dates """ CHECKIN/OUT PROCESS ------------------------------------------------ """ - def _compute_checkin_partner_count(self): - _logger.info('_compute_checkin_partner_count') + _logger.info("_compute_checkin_partner_count") for record in self: - if record.reservation_type != 'out': + if record.reservation_type != "out": record.checkin_partner_count = len(record.checkin_partner_ids) - record.checkin_partner_pending_count = \ - (record.adults + record.children) - \ - len(record.checkin_partner_ids) + record.checkin_partner_pending_count = ( + record.adults + record.children + ) - len(record.checkin_partner_ids) else: record.checkin_partner_count = 0 record.checkin_partner_pending_count = 0 @@ -1395,59 +1431,61 @@ class PmsReservation(models.Model): def _search_checkin_partner_pending(self, operator, value): self.ensure_one() - recs = self.search([]).filtered( - lambda x: x.checkin_partner_pending_count > 0) - return [('id', 'in', [x.id for x in recs])] if recs else [] - + recs = self.search([]).filtered(lambda x: x.checkin_partner_pending_count > 0) + return [("id", "in", [x.id for x in recs])] if recs else [] def action_reservation_checkout(self): for record in self: - record.state = 'done' + record.state = "done" if record.checkin_partner_ids: record.checkin_partner_ids.filtered( - lambda check: check.state == 'booking').action_done() + 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')) - ]) + 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'}) + splitted_reservs.update({"state": "done"}) return True - def action_checks(self): self.ensure_one() - action = self.env.ref( - 'pms.open_pms_reservation_form_tree_all').read()[0] - action['views'] = [ - (self.env.ref('pms.pms_reservation_checkin_view_form').id, - 'form')] - action['res_id'] = self.id - action['target'] = 'new' + action = self.env.ref("pms.open_pms_reservation_form_tree_all").read()[0] + action["views"] = [ + (self.env.ref("pms.pms_reservation_checkin_view_form").id, "form") + ] + action["res_id"] = self.id + action["target"] = "new" return action """ RESERVATION SPLITTED ----------------------------------------------- """ - def split(self, nights): for record in self: date_start_dt = fields.Date.from_string(record.checkin) date_end_dt = fields.Date.from_string(record.checkout) date_diff = abs((date_end_dt - date_start_dt).days) - new_start_date_dt = date_start_dt + \ - timedelta(days=date_diff - nights) + new_start_date_dt = date_start_dt + timedelta(days=date_diff - nights) if nights >= date_diff or nights < 1: - raise ValidationError(_("Invalid Nights! Max is \ - '%d'") % (date_diff - 1)) + raise ValidationError( + _( + "Invalid Nights! Max is \ + '%d'" + ) + % (date_diff - 1) + ) vals = record.generate_copy_values( new_start_date_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT), @@ -1458,39 +1496,56 @@ class PmsReservation(models.Model): for rline in record.reservation_line_ids: rline_dt = fields.Date.from_string(rline.date) if rline_dt >= new_start_date_dt: - reservation_lines[1].append((0, False, { - 'date': rline.date, - 'price': rline.price, - 'cancel_discount': rline.cancel_discount, - 'discount': rline.discount, - 'move_line_ids': rline.move_line_ids, - 'state': rline.state, - })) + reservation_lines[1].append( + ( + 0, + False, + { + "date": rline.date, + "price": rline.price, + "cancel_discount": rline.cancel_discount, + "discount": rline.discount, + "move_line_ids": rline.move_line_ids, + "state": rline.state, + }, + ) + ) reservation_lines[0].append((2, rline.id, False)) parent_res = record.parent_reservation or record - vals.update({ - 'splitted': True, - 'parent_reservation': parent_res.id, - 'room_type_id': parent_res.room_type_id.id, - 'state': parent_res.state, - 'reservation_line_ids': reservation_lines[1], - 'preconfirm': False, - }) - reservation_copy = self.env['pms.reservation'].with_context({ - 'ignore_avail_restrictions': True}).create(vals) + vals.update( + { + "splitted": True, + "parent_reservation": parent_res.id, + "room_type_id": parent_res.room_type_id.id, + "state": parent_res.state, + "reservation_line_ids": reservation_lines[1], + "preconfirm": False, + } + ) + reservation_copy = ( + self.env["pms.reservation"] + .with_context({"ignore_avail_restrictions": True}) + .create(vals) + ) if not reservation_copy: - raise ValidationError(_("Unexpected error copying record. \ - Can't split reservation!")) - record.write({ - 'checkout': new_start_date_dt.strftime( - DEFAULT_SERVER_DATETIME_FORMAT), - 'splitted': True, - 'reservation_line_ids': reservation_lines[0], - }) + raise ValidationError( + _( + "Unexpected error copying record. \ + Can't split reservation!" + ) + ) + record.write( + { + "checkout": new_start_date_dt.strftime( + DEFAULT_SERVER_DATETIME_FORMAT + ), + "splitted": True, + "reservation_line_ids": reservation_lines[0], + } + ) return True - def unify(self): self.ensure_one() if not self.splitted: @@ -1498,18 +1553,20 @@ class PmsReservation(models.Model): master_reservation = self.parent_reservation or self - splitted_reservs = self.env['pms.reservation'].search([ - ('splitted', '=', True), - ('folio_id', '=', self.folio_id.id), - '|', - ('parent_reservation', '=', master_reservation.id), - ('id', '=', master_reservation.id) - ]) + splitted_reservs = self.env["pms.reservation"].search( + [ + ("splitted", "=", True), + ("folio_id", "=", self.folio_id.id), + "|", + ("parent_reservation", "=", master_reservation.id), + ("id", "=", master_reservation.id), + ] + ) self.unify_books(splitted_reservs) - self_is_master = (master_reservation == self) + self_is_master = master_reservation == self if not self_is_master: - return {'type': 'ir.actions.act_window_close'} + return {"type": "ir.actions.act_window_close"} @api.model def unify_ids(self, reserv_ids): @@ -1518,14 +1575,20 @@ class PmsReservation(models.Model): @api.model def unify_books(self, splitted_reservs): - parent_reservation = splitted_reservs[0].parent_reservation or \ - splitted_reservs[0] - room_type_ids = splitted_reservs.mapped('room_type_id.id') - if len(room_type_ids) > 1 or \ - (len(room_type_ids) == 1 and - parent_reservation.room_type_id.id != room_type_ids[0]): - raise ValidationError(_("This reservation can't be unified: They \ - all need to be in the same room")) + parent_reservation = ( + splitted_reservs[0].parent_reservation or splitted_reservs[0] + ) + room_type_ids = splitted_reservs.mapped("room_type_id.id") + if len(room_type_ids) > 1 or ( + len(room_type_ids) == 1 + and parent_reservation.room_type_id.id != room_type_ids[0] + ): + raise ValidationError( + _( + "This reservation can't be unified: They \ + all need to be in the same room" + ) + ) # Search checkout last_checkout = splitted_reservs[0].checkout @@ -1539,18 +1602,24 @@ class PmsReservation(models.Model): master_reservation = reserv # Agrupate reservation lines - reservation_line_ids = splitted_reservs.mapped('reservation_line_ids') + reservation_line_ids = splitted_reservs.mapped("reservation_line_ids") reservation_line_ids.sorted(key=lambda r: r.date) rlines = [(5, False, False)] for rline in reservation_line_ids: - rlines.append((0, False, { - 'date': rline.date, - 'price': rline.price, - 'cancel_discount': rline.cancel_discount, - 'discount': rline.discount, - 'move_line_ids': rline.move_line_ids, - 'state': rline.state, - })) + rlines.append( + ( + 0, + False, + { + "date": rline.date, + "price": rline.price, + "cancel_discount": rline.cancel_discount, + "discount": rline.discount, + "move_line_ids": rline.move_line_ids, + "state": rline.state, + }, + ) + ) # Unify osplitted_reservs = splitted_reservs - master_reservation @@ -1562,39 +1631,35 @@ class PmsReservation(models.Model): _logger.info(master_reservation.real_checkout) _logger.info(last_checkout) - master_reservation.write({ - 'checkout': last_checkout, - 'splitted': master_reservation.real_checkin != first_checkin or - master_reservation.real_checkout != last_checkout, - 'reservation_line_ids': rlines, - }) + master_reservation.write( + { + "checkout": last_checkout, + "splitted": master_reservation.real_checkin != first_checkin + or master_reservation.real_checkout != last_checkout, + "reservation_line_ids": rlines, + } + ) return True - def open_master(self): self.ensure_one() if not self.parent_reservation: raise ValidationError(_("This is the parent reservation")) - action = self.env.ref( - 'pms.open_pms_reservation_form_tree_all').read()[0] - action['views'] = [ - (self.env.ref('pms.pms_reservation_view_form').id, 'form')] - action['res_id'] = self.parent_reservation.id + action = self.env.ref("pms.open_pms_reservation_form_tree_all").read()[0] + action["views"] = [(self.env.ref("pms.pms_reservation_view_form").id, "form")] + action["res_id"] = self.parent_reservation.id return action """ MAILING PROCESS """ - def send_reservation_mail(self): return self.folio_id.send_reservation_mail() - def send_exit_mail(self): return self.folio_id.send_exit_mail() - def send_cancel_mail(self): return self.folio_id.send_cancel_mail() @@ -1602,18 +1667,17 @@ class PmsReservation(models.Model): INVOICING PROCESS """ - def _compute_tax_ids(self): for record in self: # If company_id is set, always filter taxes by the company - folio = record.folio_id or self.env.context.get('default_folio_id') - product = self.env['product.product'].browse( - record.room_type_id.product_id.id) + folio = record.folio_id or self.env.context.get("default_folio_id") + product = self.env["product.product"].browse( + record.room_type_id.product_id.id + ) record.tax_ids = product.taxes_id.filtered( - lambda r: not record.company_id or - r.company_id == folio.company_id) - + lambda r: not record.company_id or r.company_id == folio.company_id + ) def set_call_center_user(self): - user = self.env['res.users'].browse(self.env.uid) - return user.has_group('pms.group_pms_call') + user = self.env["res.users"].browse(self.env.uid) + return user.has_group("pms.group_pms_call") diff --git a/pms/models/pms_reservation_line.py b/pms/models/pms_reservation_line.py index 6b0e1f0d5..75a8e7152 100644 --- a/pms/models/pms_reservation_line.py +++ b/pms/models/pms_reservation_line.py @@ -1,7 +1,7 @@ # Copyright 2017-2018 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -16,57 +16,55 @@ class PmsReservationLine(models.Model): result = [] for res in self: date = fields.Date.from_string(res.date) - name = u'%s/%s' % (date.day, date.month) + name = u"{}/{}".format(date.day, date.month) result.append((res.id, name)) return result # Fields declaration reservation_id = fields.Many2one( - 'pms.reservation', - string='Reservation', - ondelete='cascade', + "pms.reservation", + string="Reservation", + ondelete="cascade", required=True, - copy=False) + copy=False, + ) move_line_ids = fields.Many2many( - 'account.move.line', - 'reservation_line_move_rel', - 'reservation_line_id', - 'move_line_id', - string='Invoice Lines', + "account.move.line", + "reservation_line_move_rel", + "reservation_line_id", + "move_line_id", + string="Invoice Lines", readonly=True, - copy=False) + copy=False, + ) pms_property_id = fields.Many2one( - 'pms.property', + "pms.property", store=True, readonly=True, - related='reservation_id.pms_property_id') - date = fields.Date('Date') - state = fields.Selection(related='reservation_id.state') - price = fields.Float( - string='Price', - digits=('Product Price')) + related="reservation_id.pms_property_id", + ) + date = fields.Date("Date") + state = fields.Selection(related="reservation_id.state") + price = fields.Float(string="Price", digits=("Product Price")) cancel_discount = fields.Float( - string='Cancel Discount (%)', - digits=('Discount'), default=0.0) - discount = fields.Float( - string='Discount (%)', - digits=('Discount'), default=0.0) + string="Cancel Discount (%)", digits=("Discount"), default=0.0 + ) + discount = fields.Float(string="Discount (%)", digits=("Discount"), default=0.0) # Constraints and onchanges - @api.constrains('date') + @api.constrains("date") def constrains_duplicated_date(self): for record in self: duplicated = record.reservation_id.reservation_line_ids.filtered( - lambda r: r.date == record.date and - r.id != record.id + lambda r: r.date == record.date and r.id != record.id ) if duplicated: - raise ValidationError(_('Duplicated reservation line date')) + raise ValidationError(_("Duplicated reservation line date")) - @api.constrains('state') + @api.constrains("state") def constrains_service_cancel(self): for record in self: - if record.state == 'cancelled': + if record.state == "cancelled": room_services = record.reservation_id.service_ids for service in room_services: cancel_lines = service.service_line_ids.filtered( diff --git a/pms/models/pms_room.py b/pms/models/pms_room.py index 603274a31..b908e97c3 100644 --- a/pms/models/pms_room.py +++ b/pms/models/pms_room.py @@ -2,7 +2,7 @@ # Copyright 2017 Dario Lodeiros # Copyright 2018 Pablo Quesada # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -11,82 +11,95 @@ class PmsRoom(models.Model): and also for speeches (conference rooms), parking, relax with cafe con leche, spa... """ - _name = 'pms.room' - _description = 'Property Room' + + _name = "pms.room" + _description = "Property Room" _order = "sequence, room_type_id, name" # Fields declaration - name = fields.Char('Room Name', required=True) + name = fields.Char("Room Name", required=True) pms_property_id = fields.Many2one( - 'pms.property', + "pms.property", store=True, readonly=True, - related='room_type_id.pms_property_id') + related="room_type_id.pms_property_id", + ) room_type_id = fields.Many2one( - 'pms.room.type', - 'Property Room Type', - required=True, - ondelete='restrict') - shared_room_id = fields.Many2one( - 'pms.shared.room', - 'Shared Room', - default=False) + "pms.room.type", "Property Room Type", required=True, ondelete="restrict" + ) + shared_room_id = fields.Many2one("pms.shared.room", "Shared Room", default=False) floor_id = fields.Many2one( - 'pms.floor', - 'Ubication', - help='At which floor the room is located.') - capacity = fields.Integer('Capacity') - to_be_cleaned = fields.Boolean('To be Cleaned', default=False) - extra_beds_allowed = fields.Integer('Extra beds allowed', - default='0', - required=True) + "pms.floor", "Ubication", help="At which floor the room is located." + ) + capacity = fields.Integer("Capacity") + to_be_cleaned = fields.Boolean("To be Cleaned", default=False) + extra_beds_allowed = fields.Integer( + "Extra beds allowed", default="0", required=True + ) description_sale = fields.Text( - 'Sale Description', translate=True, + "Sale Description", + translate=True, help="A description of the Product that you want to communicate to " - " your customers. This description will be copied to every Sales " - " Order, Delivery Order and Customer Invoice/Credit Note") - active = fields.Boolean('Active', default=True) - sequence = fields.Integer('Sequence', default=0) + " your customers. This description will be copied to every Sales " + " Order, Delivery Order and Customer Invoice/Credit Note", + ) + active = fields.Boolean("Active", default=True) + sequence = fields.Integer("Sequence", default=0) # Constraints and onchanges - @api.constrains('capacity') + @api.constrains("capacity") def _check_capacity(self): for record in self: if record.capacity < 1: - raise ValidationError(_("The capacity of the \ - room must be greater than 0.")) + raise ValidationError( + _( + "The capacity of the \ + room must be greater than 0." + ) + ) # CRUD methods @api.model def create(self, vals): - if vals.get('pms_property_id', self.env.user.pms_property_id.id) != \ - self.env['pms.room.type'].browse( - vals['room_type_id']).pms_property_id.id: - raise ValidationError( - _("A room cannot be created in a room type \ - of another property.")) + if ( + vals.get("pms_property_id", self.env.user.pms_property_id.id) + != self.env["pms.room.type"].browse(vals["room_type_id"]).pms_property_id.id + ): + raise ValidationError( + _( + "A room cannot be created in a room type \ + of another property." + ) + ) return super().create(vals) - def write(self, vals): for record in self: - if vals.get('pms_property_id', record.pms_property_id.id) != record.pms_property_id.id: + if ( + vals.get("pms_property_id", record.pms_property_id.id) + != record.pms_property_id.id + ): raise ValidationError( - _("A room cannot be changed to another property.") + " " + - _("%s does not belong to %s.") - % (record, record.pms_property_id)) - room_type_ids = self.env['pms.room.type'].search([ - ('pms_property_id', '=', record.pms_property_id.id) - ]).ids - if vals.get('room_type_id', record.room_type_id.id) \ - not in room_type_ids: + _("A room cannot be changed to another property.") + + " " + + _("%s does not belong to %s.") % (record, record.pms_property_id) + ) + room_type_ids = ( + self.env["pms.room.type"] + .search([("pms_property_id", "=", record.pms_property_id.id)]) + .ids + ) + if vals.get("room_type_id", record.room_type_id.id) not in room_type_ids: raise ValidationError( - _("A room cannot be changed to a room type of \ - another property or unlinked from a room type.")) + _( + "A room cannot be changed to a room type of \ + another property or unlinked from a room type." + ) + ) return super().write(vals) # Business methods - + def get_capacity(self, extra_bed=0): if not self.shared_room_id: return self.capacity + extra_bed diff --git a/pms/models/pms_room_closure_reason.py b/pms/models/pms_room_closure_reason.py index d579b551e..48ad08e4a 100644 --- a/pms/models/pms_room_closure_reason.py +++ b/pms/models/pms_room_closure_reason.py @@ -1,6 +1,6 @@ # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models class RoomClosureReason(models.Model): @@ -8,10 +8,8 @@ class RoomClosureReason(models.Model): _description = "Cause of out of service" # Fields declaration - name = fields.Char('Name', translate=True, required=True) + name = fields.Char("Name", translate=True, required=True) pms_property_ids = fields.Many2many( - 'pms.property', - string='Properties', - required=False, - ondelete='restrict') - description = fields.Text('Description', translate=True) + "pms.property", string="Properties", required=False, ondelete="restrict" + ) + description = fields.Text("Description", translate=True) diff --git a/pms/models/pms_room_type.py b/pms/models/pms_room_type.py index 4ca354ab7..de5d263f8 100644 --- a/pms/models/pms_room_type.py +++ b/pms/models/pms_room_type.py @@ -1,7 +1,7 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -10,9 +10,10 @@ class PmsRoomType(models.Model): With the term 'room type' is meant a sales type of residential accommodation: for example, a Double Room, a Economic Room, an Apartment, a Tent, a Caravan... """ + _name = "pms.room.type" _description = "Room Type" - _inherits = {'product.product': 'product_id'} + _inherits = {"product.product": "product_id"} _order = "sequence, code_type, name" # Default methods @@ -22,50 +23,50 @@ class PmsRoomType(models.Model): # Fields declaration product_id = fields.Many2one( - 'product.product', - 'Product Room Type', + "product.product", + "Product Room Type", required=True, delegate=True, - ondelete='cascade') + ondelete="cascade", + ) pms_property_id = fields.Many2one( - 'pms.property', - 'Property', + "pms.property", + "Property", required=True, - ondelete='restrict', - default=_get_default_pms_property,) - room_ids = fields.One2many( - 'pms.room', - 'room_type_id', - 'Rooms') - class_id = fields.Many2one( - 'pms.room.type.class', - 'Property Type Class') + ondelete="restrict", + default=_get_default_pms_property, + ) + room_ids = fields.One2many("pms.room", "room_type_id", "Rooms") + class_id = fields.Many2one("pms.room.type.class", "Property Type Class") board_service_room_type_ids = fields.One2many( - 'pms.board.service.room.type', - 'pms_room_type_id', - string='Board Services') + "pms.board.service.room.type", "pms_room_type_id", string="Board Services" + ) room_amenity_ids = fields.Many2many( - 'pms.amenity', - 'pms_room_type_aminity_rel', - 'room_type_ids', - 'amenity_ids', - string='Room Type Amenities', - help='List of Amenities.') - code_type = fields.Char('Code', required=True, ) - shared_room = fields.Boolean('Shared Room', default=False, - help="This room type is reservation by beds") - total_rooms_count = fields.Integer( - compute='_compute_total_rooms', store=True) - active = fields.Boolean('Active', default=True) - sequence = fields.Integer('Sequence', default=0) + "pms.amenity", + "pms_room_type_aminity_rel", + "room_type_ids", + "amenity_ids", + string="Room Type Amenities", + help="List of Amenities.", + ) + code_type = fields.Char("Code", required=True,) + shared_room = fields.Boolean( + "Shared Room", default=False, help="This room type is reservation by beds" + ) + total_rooms_count = fields.Integer(compute="_compute_total_rooms", store=True) + active = fields.Boolean("Active", default=True) + sequence = fields.Integer("Sequence", default=0) _sql_constraints = [ - ('code_type_pms_unique', 'unique(code_type, pms_property_id)', - 'Room Type Code must be unique by Property!'), + ( + "code_type_pms_unique", + "unique(code_type, pms_property_id)", + "Room Type Code must be unique by Property!", + ), ] # Constraints and onchanges - @api.depends('room_ids', 'room_ids.active') + @api.depends("room_ids", "room_ids.active") def _compute_total_rooms(self): for record in self: record.total_rooms_count = len(record.room_ids) @@ -74,43 +75,38 @@ class PmsRoomType(models.Model): @api.model def create(self, vals): """ Add room types as not purchase services. """ - vals.update({ - 'purchase_ok': False, - 'type': 'service', - }) + vals.update( + {"purchase_ok": False, "type": "service",} + ) return super().create(vals) - def unlink(self): for record in self: record.product_id.unlink() return super().unlink() # Business methods - + def get_capacity(self): self.ensure_one() - capacities = self.room_ids.mapped('capacity') + capacities = self.room_ids.mapped("capacity") return min(capacities) if any(capacities) else 0 @api.model - def check_availability_room_type(self, dfrom, dto, - room_type_id=False, notthis=[]): + def check_availability_room_type(self, dfrom, dto, room_type_id=False, notthis=[]): """ Check the max availability for an specific type of room in a range of dates """ - reservations = self.env['pms.reservation'].get_reservations(dfrom, - dto) - reservations_rooms = reservations.mapped('room_id.id') - free_rooms = self.env['pms.room'].search([ - ('id', 'not in', reservations_rooms), - ('id', 'not in', notthis) - ]) + reservations = self.env["pms.reservation"].get_reservations(dfrom, dto) + reservations_rooms = reservations.mapped("room_id.id") + free_rooms = self.env["pms.room"].search( + [("id", "not in", reservations_rooms), ("id", "not in", notthis)] + ) if room_type_id: - rooms_linked = self.env['pms.room.type'].search([ - ('id', '=', room_type_id) - ]).room_ids + rooms_linked = ( + self.env["pms.room.type"].search([("id", "=", room_type_id)]).room_ids + ) free_rooms = free_rooms & rooms_linked return free_rooms.sorted(key=lambda r: r.sequence) @@ -126,38 +122,43 @@ class PmsRoomType(models.Model): Return Dict Code Room Types: subdict with day, discount, price """ vals = {} - room_type_ids = kwargs.get('room_type_ids', False) - room_types = self.env['pms.room.type'].browse(room_type_ids) if \ - room_type_ids else self.env['pms.room.type'].search([]) - date_from = kwargs.get('date_from', False) - days = kwargs.get('days', False) - discount = kwargs.get('discount', False) + room_type_ids = kwargs.get("room_type_ids", False) + room_types = ( + self.env["pms.room.type"].browse(room_type_ids) + if room_type_ids + else self.env["pms.room.type"].search([]) + ) + date_from = kwargs.get("date_from", False) + days = kwargs.get("days", False) + discount = kwargs.get("discount", False) if not date_from or not days: - raise ValidationError(_('Date From and days are mandatory')) - partner_id = kwargs.get('partner_id', False) - partner = self.env['res.partner'].browse(partner_id) + raise ValidationError(_("Date From and days are mandatory")) + partner_id = kwargs.get("partner_id", False) + partner = self.env["res.partner"].browse(partner_id) pricelist_id = kwargs.get( - 'pricelist_id', - partner.property_product_pricelist.id and - partner.property_product_pricelist.id or - self.env.user.pms_property_id.default_pricelist_id.id) - vals.update({ - 'partner_id': partner_id if partner_id else False, - 'discount': discount, - }) + "pricelist_id", + partner.property_product_pricelist.id + and partner.property_product_pricelist.id + or self.env.user.pms_property_id.default_pricelist_id.id, + ) + vals.update( + {"partner_id": partner_id if partner_id else False, "discount": discount,} + ) rate_vals = {} for room_type in room_types: - vals.update({'room_type_id': room_type.id}) - room_vals = self.env['pms.reservation'].\ - prepare_reservation_lines( - date_from, - days, - pricelist_id=pricelist_id, - vals=vals, - update_old_prices=False) - rate_vals.update({ - room_type.id: [item[2] for item in - room_vals['reservation_line_ids'] if - item[2]] - }) + vals.update({"room_type_id": room_type.id}) + room_vals = self.env["pms.reservation"].prepare_reservation_lines( + date_from, + days, + pricelist_id=pricelist_id, + vals=vals, + update_old_prices=False, + ) + rate_vals.update( + { + room_type.id: [ + item[2] for item in room_vals["reservation_line_ids"] if item[2] + ] + } + ) return rate_vals diff --git a/pms/models/pms_room_type_class.py b/pms/models/pms_room_type_class.py index f2769b744..3535b8a70 100644 --- a/pms/models/pms_room_type_class.py +++ b/pms/models/pms_room_type_class.py @@ -1,7 +1,7 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models class PmsRoomTypeClass(models.Model): @@ -10,25 +10,22 @@ class PmsRoomTypeClass(models.Model): residential accommodation: for example, a Room, a Bed, an Apartment, a Tent, a Caravan... """ + _name = "pms.room.type.class" _description = "Room Type Class" _order = "sequence, name, code_class" # Fields declaration - name = fields.Char('Class Name', required=True, translate=True) + name = fields.Char("Class Name", required=True, translate=True) # Relationship between models pms_property_ids = fields.Many2many( - 'pms.property', - string='Properties', - required=False, - ondelete='restrict') - room_type_ids = fields.One2many( - 'pms.room.type', - 'class_id', - 'Types') - code_class = fields.Char('Code') - active = fields.Boolean('Active', default=True) - sequence = fields.Integer('Sequence', default=0) + "pms.property", string="Properties", required=False, ondelete="restrict" + ) + room_type_ids = fields.One2many("pms.room.type", "class_id", "Types") + code_class = fields.Char("Code") + active = fields.Boolean("Active", default=True) + sequence = fields.Integer("Sequence", default=0) - _sql_constraints = [('code_class_unique', 'unique(code_class)', - 'Room Class Code must be unique!')] + _sql_constraints = [ + ("code_class_unique", "unique(code_class)", "Room Class Code must be unique!") + ] diff --git a/pms/models/pms_room_type_restriction.py b/pms/models/pms_room_type_restriction.py index 7591be257..42ba993b5 100644 --- a/pms/models/pms_room_type_restriction.py +++ b/pms/models/pms_room_type_restriction.py @@ -1,13 +1,14 @@ # Copyright 2017 Alexandre Díaz # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api +from odoo import api, fields, models class PmsRoomTypeRestriction(models.Model): """ The room type restriction is used as a daily restriction plan for room types and therefore is related only with one property. """ - _name = 'pms.room.type.restriction' - _description = 'Reservation restriction plan' + + _name = "pms.room.type.restriction" + _description = "Reservation restriction plan" # Default methods @api.model @@ -15,19 +16,22 @@ class PmsRoomTypeRestriction(models.Model): return self.env.user.pms_property_id or None # Fields declaration - name = fields.Char('Restriction Plan Name', required=True) + name = fields.Char("Restriction Plan Name", required=True) pms_property_id = fields.Many2one( - 'pms.property', - 'Property', - ondelete='restrict', - default=_get_default_pms_property) + "pms.property", + "Property", + ondelete="restrict", + default=_get_default_pms_property, + ) item_ids = fields.One2many( - 'pms.room.type.restriction.item', - 'restriction_id', - string='Restriction Items', - copy=True) + "pms.room.type.restriction.item", + "restriction_id", + string="Restriction Items", + copy=True, + ) active = fields.Boolean( - 'Active', + "Active", default=True, - help='If unchecked, it will allow you to hide the ' - 'restriction plan without removing it.') + help="If unchecked, it will allow you to hide the " + "restriction plan without removing it.", + ) diff --git a/pms/models/pms_room_type_restriction_item.py b/pms/models/pms_room_type_restriction_item.py index b3029d19b..e1cacc06a 100644 --- a/pms/models/pms_room_type_restriction_item.py +++ b/pms/models/pms_room_type_restriction_item.py @@ -1,47 +1,49 @@ # Copyright 2017 Alexandre Díaz # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError class PmsRoomTypeRestrictionItem(models.Model): - _name = 'pms.room.type.restriction.item' - _description = 'Reservation restriction by day' + _name = "pms.room.type.restriction.item" + _description = "Reservation restriction by day" # Field Declarations - restriction_id = fields.Many2one('pms.room.type.restriction', - 'Restriction Plan', ondelete='cascade', - index=True) - room_type_id = fields.Many2one('pms.room.type', 'Room Type', - required=True, ondelete='cascade') - date = fields.Date('Date') + restriction_id = fields.Many2one( + "pms.room.type.restriction", "Restriction Plan", ondelete="cascade", index=True + ) + room_type_id = fields.Many2one( + "pms.room.type", "Room Type", required=True, ondelete="cascade" + ) + date = fields.Date("Date") min_stay = fields.Integer("Min. Stay") min_stay_arrival = fields.Integer("Min. Stay Arrival") max_stay = fields.Integer("Max. Stay") max_stay_arrival = fields.Integer("Max. Stay Arrival") - closed = fields.Boolean('Closed') - closed_departure = fields.Boolean('Closed Departure') - closed_arrival = fields.Boolean('Closed Arrival') + closed = fields.Boolean("Closed") + closed_departure = fields.Boolean("Closed Departure") + closed_arrival = fields.Boolean("Closed Arrival") - _sql_constraints = [('room_type_registry_unique', - 'unique(restriction_id, room_type_id, date)', - 'Only can exists one restriction in the same \ - day for the same room type!')] + _sql_constraints = [ + ( + "room_type_registry_unique", + "unique(restriction_id, room_type_id, date)", + "Only can exists one restriction in the same \ + day for the same room type!", + ) + ] # Constraints and onchanges - @api.constrains('min_stay', 'min_stay_arrival', 'max_stay', - 'max_stay_arrival') + @api.constrains("min_stay", "min_stay_arrival", "max_stay", "max_stay_arrival") def _check_min_stay(self): for record in self: if record.min_stay < 0: raise ValidationError(_("Min. Stay can't be less than zero")) elif record.min_stay_arrival < 0: - raise ValidationError( - _("Min. Stay Arrival can't be less than zero")) + raise ValidationError(_("Min. Stay Arrival can't be less than zero")) elif record.max_stay < 0: raise ValidationError(_("Max. Stay can't be less than zero")) elif record.max_stay_arrival < 0: - raise ValidationError( - _("Max. Stay Arrival can't be less than zero")) + raise ValidationError(_("Max. Stay Arrival can't be less than zero")) diff --git a/pms/models/pms_service.py b/pms/models/pms_service.py index 50b3a3499..55553db8b 100644 --- a/pms/models/pms_service.py +++ b/pms/models/pms_service.py @@ -1,19 +1,18 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ -from odoo.tools import ( - float_is_zero, - float_compare, - DEFAULT_SERVER_DATE_FORMAT) -from datetime import timedelta import logging +from datetime import timedelta + +from odoo import _, api, fields, models +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, float_compare, float_is_zero + _logger = logging.getLogger(__name__) class PmsService(models.Model): - _name = 'pms.service' - _description = 'Services and its charges' + _name = "pms.service" + _description = "Services and its charges" # Default methods @@ -21,141 +20,129 @@ class PmsService(models.Model): result = [] for rec in self: name = [] - name.append('%(name)s' % {'name': rec.name}) + name.append("{name}".format(name=rec.name)) if rec.reservation_id.name: - name.append('%(name)s' % {'name': rec.reservation_id.name}) + name.append("{name}".format(name=rec.reservation_id.name)) result.append((rec.id, ", ".join(name))) return result @api.model def _default_reservation_id(self): - if self.env.context.get('reservation_ids'): - ids = [item[1] for item in self.env.context['reservation_ids']] - return self.env['pms.reservation'].browse([ - (ids)], limit=1) - elif self.env.context.get('default_reservation_id'): - return self.env.context.get('default_reservation_id') + if self.env.context.get("reservation_ids"): + ids = [item[1] for item in self.env.context["reservation_ids"]] + return self.env["pms.reservation"].browse([(ids)], limit=1) + elif self.env.context.get("default_reservation_id"): + return self.env.context.get("default_reservation_id") return False @api.model def _default_folio_id(self): - if 'folio_id' in self._context: - return self._context['folio_id'] + if "folio_id" in self._context: + return self._context["folio_id"] return False # Fields declaration - name = fields.Char('Service description', required=True) + name = fields.Char("Service description", required=True) product_id = fields.Many2one( - 'product.product', - 'Service', - ondelete='restrict', - required=True) + "product.product", "Service", ondelete="restrict", required=True + ) folio_id = fields.Many2one( - 'pms.folio', - 'Folio', - ondelete='cascade', - default=_default_folio_id) + "pms.folio", "Folio", ondelete="cascade", default=_default_folio_id + ) reservation_id = fields.Many2one( - 'pms.reservation', - 'Room', - default=_default_reservation_id) - service_line_ids = fields.One2many( - 'pms.service.line', - 'service_id') + "pms.reservation", "Room", default=_default_reservation_id + ) + service_line_ids = fields.One2many("pms.service.line", "service_id") company_id = fields.Many2one( - related='folio_id.company_id', - string='Company', - store=True, - readonly=True) + related="folio_id.company_id", string="Company", store=True, readonly=True + ) pms_property_id = fields.Many2one( - 'pms.property', - store=True, - readonly=True, - related='folio_id.pms_property_id') + "pms.property", store=True, readonly=True, related="folio_id.pms_property_id" + ) tax_ids = fields.Many2many( - 'account.tax', - string='Taxes', - domain=['|', ('active', '=', False), ('active', '=', True)]) + "account.tax", + string="Taxes", + domain=["|", ("active", "=", False), ("active", "=", True)], + ) move_line_ids = fields.Many2many( - 'account.move.line', - 'service_line_move_rel', - 'service_id', - 'move_line_id', - string='move Lines', - copy=False) - analytic_tag_ids = fields.Many2many( - 'account.analytic.tag', - string='Analytic Tags') + "account.move.line", + "service_line_move_rel", + "service_id", + "move_line_id", + string="move Lines", + copy=False, + ) + analytic_tag_ids = fields.Many2many("account.analytic.tag", string="Analytic Tags") currency_id = fields.Many2one( - related='folio_id.currency_id', - store=True, - string='Currency', - readonly=True) - sequence = fields.Integer(string='Sequence', default=10) - state = fields.Selection(related='folio_id.state') - per_day = fields.Boolean(related='product_id.per_day', related_sudo=True) - product_qty = fields.Integer('Quantity', default=1) + related="folio_id.currency_id", store=True, string="Currency", readonly=True + ) + sequence = fields.Integer(string="Sequence", default=10) + state = fields.Selection(related="folio_id.state") + per_day = fields.Boolean(related="product_id.per_day", related_sudo=True) + product_qty = fields.Integer("Quantity", default=1) days_qty = fields.Integer(compute="_compute_days_qty", store=True) is_board_service = fields.Boolean() - to_print = fields.Boolean('Print', help='Print in Folio Report') + 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( - 'Product Image', related="product_id.image_1024", - store=False, related_sudo=True) - invoice_status = fields.Selection([ - ('invoiced', 'Fully Invoiced'), - ('to invoice', 'To Invoice'), - ('no', 'Nothing to Invoice')], - string='Invoice Status', - compute='_compute_invoice_status', - store=True, - readonly=True, - default='no') - channel_type = fields.Selection([ - ('door', 'Door'), - ('mail', 'Mail'), - ('phone', 'Phone'), - ('call', 'Call Center'), - ('web', 'Web')], - string='Sales Channel') + "Product Image", related="product_id.image_1024", store=False, related_sudo=True + ) + invoice_status = fields.Selection( + [ + ("invoiced", "Fully Invoiced"), + ("to invoice", "To Invoice"), + ("no", "Nothing to Invoice"), + ], + string="Invoice Status", + compute="_compute_invoice_status", + store=True, + readonly=True, + default="no", + ) + channel_type = fields.Selection( + [ + ("door", "Door"), + ("mail", "Mail"), + ("phone", "Phone"), + ("call", "Call Center"), + ("web", "Web"), + ], + string="Sales Channel", + ) price_unit = fields.Float( - 'Unit Price', - required=True, - digits=('Product Price'), default=0.0) - discount = fields.Float( - string='Discount (%)', - digits=('Discount'), default=0.0) + "Unit Price", required=True, digits=("Product Price"), default=0.0 + ) + discount = fields.Float(string="Discount (%)", digits=("Discount"), default=0.0) qty_to_invoice = fields.Float( - compute='_get_to_invoice_qty', - string='To Invoice', + compute="_get_to_invoice_qty", + string="To Invoice", store=True, readonly=True, - digits=('Product Unit of Measure')) + digits=("Product Unit of Measure"), + ) qty_invoiced = fields.Float( - compute='_get_invoice_qty', - string='Invoiced', + compute="_get_invoice_qty", + string="Invoiced", store=True, readonly=True, - digits=('Product Unit of Measure')) + digits=("Product Unit of Measure"), + ) price_subtotal = fields.Monetary( - string='Subtotal', - readonly=True, - store=True, - compute='_compute_amount_service') + string="Subtotal", readonly=True, store=True, compute="_compute_amount_service" + ) price_total = fields.Monetary( - string='Total', - readonly=True, - store=True, - compute='_compute_amount_service') + string="Total", readonly=True, store=True, compute="_compute_amount_service" + ) price_tax = fields.Float( - string='Taxes Amount', + string="Taxes Amount", readonly=True, store=True, - compute='_compute_amount_service') + compute="_compute_amount_service", + ) # Compute and Search methods - @api.depends('qty_invoiced', 'product_qty', 'folio_id.state') + @api.depends("qty_invoiced", "product_qty", "folio_id.state") def _get_to_invoice_qty(self): """ Compute the quantity to invoice. If the invoice policy is order, @@ -163,13 +150,12 @@ class PmsService(models.Model): Otherwise, the quantity delivered is used. """ for line in self: - if line.folio_id.state not in ['draft']: + if line.folio_id.state not in ["draft"]: line.qty_to_invoice = line.product_qty - line.qty_invoiced else: line.qty_to_invoice = 0 - @api.depends('move_line_ids.move_id.state', - 'move_line_ids.quantity') + @api.depends("move_line_ids.move_id.state", "move_line_ids.quantity") def _get_invoice_qty(self): """ Compute the quantity invoiced. If case of a refund, @@ -183,16 +169,18 @@ class PmsService(models.Model): for line in self: qty_invoiced = 0.0 for invoice_line in line.move_line_ids: - if invoice_line.move_id.state != 'cancel': - if invoice_line.move_id.type == 'out_invoice': + if invoice_line.move_id.state != "cancel": + if invoice_line.move_id.type == "out_invoice": qty_invoiced += invoice_line.uom_id._compute_quantity( - invoice_line.quantity, line.product_id.uom_id) - elif invoice_line.move_id.type == 'out_refund': + invoice_line.quantity, line.product_id.uom_id + ) + elif invoice_line.move_id.type == "out_refund": qty_invoiced -= move_line.uom_id._compute_quantity( - invoice_line.quantity, line.product_id.uom_id) + invoice_line.quantity, line.product_id.uom_id + ) line.qty_invoiced = qty_invoiced - @api.depends('product_qty', 'qty_to_invoice', 'qty_invoiced') + @api.depends("product_qty", "qty_to_invoice", "qty_invoiced") def _compute_invoice_status(self): """ Compute the invoice status of a SO line. Possible statuses: @@ -212,58 +200,65 @@ class PmsService(models.Model): - invoiced: the quantity invoiced is larger or equal to the quantity ordered. """ - precision = self.env['decimal.precision'].precision_get( - 'Product Unit of Measure') + precision = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) for line in self: - if line.folio_id.state in ('draft'): - line.invoice_status = 'no' - elif not float_is_zero(line.qty_to_invoice, - precision_digits=precision): - line.invoice_status = 'to invoice' - elif float_compare(line.qty_invoiced, line.product_qty, - precision_digits=precision) >= 0: - line.invoice_status = 'invoiced' + if line.folio_id.state in ("draft"): + line.invoice_status = "no" + elif not float_is_zero(line.qty_to_invoice, precision_digits=precision): + line.invoice_status = "to invoice" + elif ( + float_compare( + line.qty_invoiced, line.product_qty, precision_digits=precision + ) + >= 0 + ): + line.invoice_status = "invoiced" else: - line.invoice_status = 'no' + line.invoice_status = "no" - @api.depends('product_qty', 'discount', 'price_unit', 'tax_ids') + @api.depends("product_qty", "discount", "price_unit", "tax_ids") def _compute_amount_service(self): """ Compute the amounts of the service line. """ for record in self: - folio = record.folio_id or self.env['pms.folio'].browse( - self.env.context.get('default_folio_id')) + folio = record.folio_id or self.env["pms.folio"].browse( + self.env.context.get("default_folio_id") + ) reservation = record.reservation_id or self.env.context.get( - 'reservation_id') + "reservation_id" + ) currency = folio.currency_id if folio else reservation.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_qty, product=product) + price, currency, record.product_qty, 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'], - }) + 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"], + } + ) - @api.depends('service_line_ids.day_qty') + @api.depends("service_line_ids.day_qty") def _compute_days_qty(self): for record in self: if record.per_day: - qty = sum(record.service_line_ids.mapped('day_qty')) - vals = { - 'days_qty': qty, - 'product_qty': qty - } + qty = sum(record.service_line_ids.mapped("day_qty")) + vals = {"days_qty": qty, "product_qty": qty} else: - vals = {'days_qty': 0} + vals = {"days_qty": 0} record.update(vals) # Constraints and onchanges - @api.onchange('product_id') + @api.onchange("product_id") def onchange_product_id(self): """ Compute the default quantity according to the @@ -274,13 +269,13 @@ class PmsService(models.Model): if not self.product_id: return vals = {} - vals['product_qty'] = 1.0 + vals["product_qty"] = 1.0 for record in self: if record.per_day and record.reservation_id: product = record.product_id - if self.env.context.get('default_reservation_id'): - reservation = self.env['pms.reservation'].browse( - self.env.context.get('default_reservation_id') + if self.env.context.get("default_reservation_id"): + reservation = self.env["pms.reservation"].browse( + self.env.context.get("default_reservation_id") ) else: reservation = record.reservation_id @@ -293,14 +288,16 @@ class PmsService(models.Model): checkin_dt = fields.Date.from_string(checkin) checkout_dt = fields.Date.from_string(checkout) nights = abs((checkout_dt - checkin_dt).days) - vals.update(record.prepare_service_ids( - dfrom=checkin, - days=nights, - per_person=product.per_person, - persons=reservation.adults, - old_line_days=record.service_line_ids, - consumed_on=product.consumed_on, - )) + vals.update( + record.prepare_service_ids( + dfrom=checkin, + days=nights, + per_person=product.per_person, + persons=reservation.adults, + old_line_days=record.service_line_ids, + consumed_on=product.consumed_on, + ) + ) if record.product_id.daily_limit > 0: for day in record.service_line_ids: day.no_free_resources() @@ -308,64 +305,62 @@ class PmsService(models.Model): Description and warnings """ product = self.product_id.with_context( - lang=self.folio_id.partner_id.lang, - partner=self.folio_id.partner_id.id + lang=self.folio_id.partner_id.lang, partner=self.folio_id.partner_id.id ) title = False message = False warning = {} - if product.sale_line_warn != 'no-message': + if product.sale_line_warn != "no-message": title = _("Warning for %s") % product.name message = product.sale_line_warn_msg - warning['title'] = title - warning['message'] = message - result = {'warning': warning} - if product.sale_line_warn == 'block': + warning["title"] = title + warning["message"] = message + result = {"warning": warning} + if product.sale_line_warn == "block": self.product_id = False return result name = product.name_get()[0][1] if product.description_sale: - name += '\n' + product.description_sale - vals['name'] = name + name += "\n" + product.description_sale + vals["name"] = name """ Compute tax and price unit """ self._compute_tax_ids() - vals['price_unit'] = self._compute_price_unit() + vals["price_unit"] = self._compute_price_unit() record.update(vals) # Action methods def open_service_ids(self): - action = self.env.ref('pms.action_pms_services_form').read()[0] - action['views'] = [ - (self.env.ref('pms.pms_service_view_form').id, 'form')] - action['res_id'] = self.id - action['target'] = 'new' + action = self.env.ref("pms.action_pms_services_form").read()[0] + action["views"] = [(self.env.ref("pms.pms_service_view_form").id, "form")] + action["res_id"] = self.id + action["target"] = "new" return action # ORM Overrides @api.model - def name_search(self, name='', args=None, operator='ilike', limit=100): + def name_search(self, name="", args=None, operator="ilike", limit=100): if args is None: args = [] - if not(name == '' and operator == 'ilike'): + if not (name == "" and operator == "ilike"): args += [ - '|', - ('reservation_id.name', operator, name), - ('name', operator, name) + "|", + ("reservation_id.name", operator, name), + ("name", operator, name), ] return super(PmsService, self).name_search( - name='', args=args, operator='ilike', limit=limit) + name="", args=args, operator="ilike", limit=limit + ) @api.model def create(self, vals): vals.update(self._prepare_add_missing_fields(vals)) if self.compute_lines_out_vals(vals): - reservation = self.env['pms.reservation'].browse( - vals['reservation_id']) - product = self.env['product.product'].browse(vals['product_id']) + reservation = self.env["pms.reservation"].browse(vals["reservation_id"]) + product = self.env["product.product"].browse(vals["product_id"]) if reservation.splitted: checkin = reservation.real_checkin checkout = reservation.real_checkout @@ -375,33 +370,34 @@ class PmsService(models.Model): checkin_dt = fields.Date.from_string(checkin) checkout_dt = fields.Date.from_string(checkout) nights = abs((checkout_dt - checkin_dt).days) - vals.update(self.prepare_service_ids( - dfrom=checkin, - days=nights, - per_person=product.per_person, - persons=reservation.adults, - old_day_lines=False, - consumed_on=product.consumed_on, - )) + vals.update( + self.prepare_service_ids( + dfrom=checkin, + days=nights, + per_person=product.per_person, + persons=reservation.adults, + old_day_lines=False, + consumed_on=product.consumed_on, + ) + ) record = super(PmsService, self).create(vals) return record - def write(self, vals): # If you write product, We must check if its necesary create or delete # service lines - if vals.get('product_id'): - product = self.env['product.product'].browse( - vals.get('product_id')) + if vals.get("product_id"): + product = self.env["product.product"].browse(vals.get("product_id")) if not product.per_day: - vals.update({ - 'service_line_ids': [(5, 0, 0)] - }) + vals.update({"service_line_ids": [(5, 0, 0)]}) else: for record in self: - reservations = self.env['pms.reservation'] - reservation = reservations.browse(vals['reservation_id']) \ - if 'reservation_id' in vals else record.reservation_id + reservations = self.env["pms.reservation"] + reservation = ( + reservations.browse(vals["reservation_id"]) + if "reservation_id" in vals + else record.reservation_id + ) if reservation.splitted: checkin = reservation.real_checkin checkout = reservation.real_checkout @@ -411,14 +407,16 @@ class PmsService(models.Model): checkin_dt = fields.Date.from_string(checkin) checkout_dt = fields.Date.from_string(checkout) nights = abs((checkout_dt - checkin_dt).days) - record.update(record.prepare_service_ids( - dfrom=checkin, - days=nights, - per_person=product.per_person, - persons=reservation.adults, - old_line_days=self.service_line_ids, - consumed_on=product.consumed_on, - )) + record.update( + record.prepare_service_ids( + dfrom=checkin, + days=nights, + per_person=product.per_person, + persons=reservation.adults, + old_line_days=self.service_line_ids, + consumed_on=product.consumed_on, + ) + ) res = super(PmsService, self).write(vals) return res @@ -427,103 +425,123 @@ class PmsService(models.Model): def _prepare_add_missing_fields(self, values): """ Deduce missing required fields from the onchange """ res = {} - onchange_fields = ['price_unit', 'tax_ids', 'name'] - if values.get('product_id'): + onchange_fields = ["price_unit", "tax_ids", "name"] + if values.get("product_id"): line = self.new(values) if any(f not in values for f in onchange_fields): line.onchange_product_id() for field in onchange_fields: if field not in values: - res[field] = line._fields[field].convert_to_write( - line[field], line) + res[field] = line._fields[field].convert_to_write(line[field], line) return res - def compute_lines_out_vals(self, vals): """ Compute if It is necesary service days in write/create """ if not vals: vals = {} - if 'product_id' in vals: - product = self.env['product.product'].browse(vals['product_id']) \ - if 'product_id' in vals else self.product_id - if (product.per_day and 'service_line_ids' not in vals): + if "product_id" in vals: + product = ( + self.env["product.product"].browse(vals["product_id"]) + if "product_id" in vals + else self.product_id + ) + if product.per_day and "service_line_ids" not in vals: return True return False - def _compute_tax_ids(self): for record in self: # If company_id is set, always filter taxes by the company - folio = record.folio_id or self.env['pms.folio'].browse( - self.env.context.get('default_folio_id')) + folio = record.folio_id or self.env["pms.folio"].browse( + self.env.context.get("default_folio_id") + ) reservation = record.reservation_id or self.env.context.get( - 'reservation_id') + "reservation_id" + ) origin = folio if folio else reservation record.tax_ids = record.product_id.taxes_id.filtered( - lambda r: not record.company_id or - r.company_id == origin.company_id) - + lambda r: not record.company_id or r.company_id == origin.company_id + ) def _get_display_price(self, product): - folio = self.folio_id or self.env.context.get('default_folio_id') - reservation = self.reservation_id or self.env.context.get( - 'reservation_id') + folio = self.folio_id or self.env.context.get("default_folio_id") + reservation = self.reservation_id or self.env.context.get("reservation_id") origin = folio if folio else reservation - if origin.pricelist_id.discount_policy == 'with_discount': + if origin.pricelist_id.discount_policy == "with_discount": return product.with_context(pricelist=origin.pricelist_id.id).price product_context = dict( self.env.context, partner_id=origin.partner_id.id, date=folio.date_order if folio else fields.Date.today(), - uom=self.product_id.uom_id.id) + uom=self.product_id.uom_id.id, + ) final_price, rule_id = origin.pricelist_id.with_context( - product_context).get_product_price_rule( - self.product_id, - self.product_qty or 1.0, - origin.partner_id) + product_context + ).get_product_price_rule( + self.product_id, self.product_qty or 1.0, origin.partner_id + ) base_price, currency_id = self.with_context( - product_context)._get_real_price_currency( - product, - rule_id, - self.product_qty, - self.product_id.uom_id, - origin.pricelist_id.id) + product_context + )._get_real_price_currency( + product, + rule_id, + self.product_qty, + self.product_id.uom_id, + origin.pricelist_id.id, + ) if currency_id != origin.pricelist_id.currency_id.id: - base_price = self.env['res.currency'].browse( - currency_id).with_context(product_context).compute( - base_price, - origin.pricelist_id.currency_id) + base_price = ( + self.env["res.currency"] + .browse(currency_id) + .with_context(product_context) + .compute(base_price, origin.pricelist_id.currency_id) + ) # negative discounts (= surcharge) are included in the display price return max(base_price, final_price) - def _compute_price_unit(self): self.ensure_one() - folio = self.folio_id or self.env.context.get('default_folio_id') - reservation = self.reservation_id or self.env.context.get( - 'reservation_id') + folio = self.folio_id or self.env.context.get("default_folio_id") + reservation = self.reservation_id or self.env.context.get("reservation_id") origin = reservation if reservation else folio if origin: partner = origin.partner_id pricelist = origin.pricelist_id if reservation and self.is_board_service: board_room_type = reservation.board_service_room_id - if board_room_type.price_type == 'fixed': - return self.env['pms.board.service.room.type.line'].\ - search([ - ('pms_board_service_room_type_id', - '=', board_room_type.id), - ('product_id', '=', self.product_id.id)]).amount + if board_room_type.price_type == "fixed": + return ( + self.env["pms.board.service.room.type.line"] + .search( + [ + ( + "pms_board_service_room_type_id", + "=", + board_room_type.id, + ), + ("product_id", "=", self.product_id.id), + ] + ) + .amount + ) else: - return (reservation.price_total * - self.env['pms.board.service.room.type.line']. - search([ - ('pms_board_service_room_type_id', - '=', board_room_type.id), - ('product_id', '=', self.product_id.id)]) - .amount) / 100 + return ( + reservation.price_total + * self.env["pms.board.service.room.type.line"] + .search( + [ + ( + "pms_board_service_room_type_id", + "=", + board_room_type.id, + ), + ("product_id", "=", self.product_id.id), + ] + ) + .amount + ) / 100 else: product = self.product_id.with_context( lang=partner.lang, @@ -532,12 +550,14 @@ class PmsService(models.Model): date=folio.date_order if folio else fields.Date.today(), pricelist=pricelist.id, uom=self.product_id.uom_id.id, - fiscal_position=False + fiscal_position=False, ) - return self.env['account.tax']._fix_tax_included_price_company( + return self.env["account.tax"]._fix_tax_included_price_company( self._get_display_price(product), - product.taxes_id, self.tax_ids, - origin.company_id) + product.taxes_id, + self.tax_ids, + origin.company_id, + ) @api.model def prepare_service_ids(self, **kwargs): @@ -545,28 +565,26 @@ class PmsService(models.Model): Prepare line and respect the old manual changes on lines """ cmds = [(5, 0, 0)] - old_line_days = kwargs.get('old_line_days') - consumed_on = kwargs.get('consumed_on') if kwargs.get( - 'consumed_on') else 'before' + old_line_days = kwargs.get("old_line_days") + consumed_on = ( + kwargs.get("consumed_on") if kwargs.get("consumed_on") else "before" + ) total_qty = 0 day_qty = 1 # WARNING: Change adults in reservation NOT update qty service!! - if kwargs.get('per_person'): - day_qty = kwargs.get('persons') - for i in range(0, kwargs.get('days')): - if consumed_on == 'after': + if kwargs.get("per_person"): + day_qty = kwargs.get("persons") + for i in range(0, kwargs.get("days")): + if consumed_on == "after": i += 1 - idate = (fields.Date.from_string(kwargs.get('dfrom')) + - timedelta(days=i)).strftime( - DEFAULT_SERVER_DATE_FORMAT) - if not old_line_days or idate not in old_line_days.mapped('date'): - cmds.append((0, False, { - 'date': idate, - 'day_qty': day_qty - })) + idate = ( + fields.Date.from_string(kwargs.get("dfrom")) + timedelta(days=i) + ).strftime(DEFAULT_SERVER_DATE_FORMAT) + if not old_line_days or idate not in old_line_days.mapped("date"): + cmds.append((0, False, {"date": idate, "day_qty": day_qty})) total_qty = total_qty + day_qty else: old_line = old_line_days.filtered(lambda r: r.date == idate) cmds.append((4, old_line.id)) total_qty = total_qty + old_line.day_qty - return {'service_line_ids': cmds, 'product_qty': total_qty} + return {"service_line_ids": cmds, "product_qty": total_qty} diff --git a/pms/models/pms_service_line.py b/pms/models/pms_service_line.py index 06fa2d394..18247e536 100644 --- a/pms/models/pms_service_line.py +++ b/pms/models/pms_service_line.py @@ -1,7 +1,7 @@ # Copyright 2017-2018 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -12,50 +12,39 @@ class PmsServiceLine(models.Model): # Fields declaration service_id = fields.Many2one( - 'pms.service', - string='Service Room', - ondelete='cascade', + "pms.service", + string="Service Room", + ondelete="cascade", required=True, - copy=False) - product_id = fields.Many2one( - related='service_id.product_id', - store=True) + copy=False, + ) + product_id = fields.Many2one(related="service_id.product_id", store=True) tax_ids = fields.Many2many( - 'account.tax', - string='Taxes', - related="service_id.tax_ids", - readonly="True") + "account.tax", string="Taxes", related="service_id.tax_ids", readonly="True" + ) pms_property_id = fields.Many2one( - 'pms.property', - store=True, - readonly=True, - related='service_id.pms_property_id') - date = fields.Date('Date') - day_qty = fields.Integer('Units') + "pms.property", store=True, readonly=True, related="service_id.pms_property_id" + ) + date = fields.Date("Date") + day_qty = fields.Integer("Units") price_total = fields.Float( - 'Price Total', - compute='_compute_price_total', - store=True) + "Price Total", compute="_compute_price_total", store=True + ) price_unit = fields.Float( - 'Unit Price', - related="service_id.price_unit", - readonly=True, - store=True) + "Unit Price", related="service_id.price_unit", readonly=True, store=True + ) room_id = fields.Many2one( - string='Room', - related="service_id.reservation_id", - readonly=True, - store=True) + string="Room", related="service_id.reservation_id", readonly=True, store=True + ) discount = fields.Float( - 'Discount', - related="service_id.discount", - readonly=True, - store=True) + "Discount", related="service_id.discount", readonly=True, store=True + ) cancel_discount = fields.Float( - 'Discount cancel', compute='_compute_cancel_discount') + "Discount cancel", compute="_compute_cancel_discount" + ) # Compute and Search methods - @api.depends('day_qty', 'service_id.price_total') + @api.depends("day_qty", "service_id.price_total") def _compute_price_total(self): """ Used to reports @@ -63,26 +52,33 @@ class PmsServiceLine(models.Model): for record in self: if record.service_id.product_qty != 0: record.price_total = ( - record.service_id.price_total * record.day_qty) \ - / record.service_id.product_qty + record.service_id.price_total * record.day_qty + ) / record.service_id.product_qty else: record.price_total = 0 # Constraints and onchanges - @api.constrains('day_qty') + @api.constrains("day_qty") def no_free_resources(self): for record in self: limit = record.product_id.daily_limit if limit > 0: - out_qty = sum(self.env['pms.service.line'].search([ - ('product_id', '=', record.product_id.id), - ('date', '=', record.date), - ('service_id', '!=', record.service_id.id) - ]).mapped('day_qty')) + out_qty = sum( + self.env["pms.service.line"] + .search( + [ + ("product_id", "=", record.product_id.id), + ("date", "=", record.date), + ("service_id", "!=", record.service_id.id), + ] + ) + .mapped("day_qty") + ) if limit < out_qty + record.day_qty: raise ValidationError( - _("%s limit exceeded for %s") % - (record.service_id.product_id.name, record.date)) + _("%s limit exceeded for %s") + % (record.service_id.product_id.name, record.date) + ) # Business methods def _cancel_discount(self): diff --git a/pms/models/pms_shared_room.py b/pms/models/pms_shared_room.py index 24010fa0b..f5f6854f5 100644 --- a/pms/models/pms_shared_room.py +++ b/pms/models/pms_shared_room.py @@ -2,111 +2,112 @@ # Copyright 2017 Dario Lodeiros # Copyright 2018 Pablo Quesada # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError class PmsSharedRoom(models.Model): - _name = 'pms.shared.room' - _description = 'Shared Room' + _name = "pms.shared.room" + _description = "Shared Room" _order = "room_type_id, name" # Fields declaration - name = fields.Char('Room Name', required=True) + name = fields.Char("Room Name", required=True) room_type_id = fields.Many2one( - 'pms.room.type', - 'Room Type', + "pms.room.type", + "Room Type", required=True, - ondelete='restrict', - domain=[('shared_room', '=', True)] - ) + ondelete="restrict", + domain=[("shared_room", "=", True)], + ) pms_property_id = fields.Many2one( - 'pms.property', + "pms.property", store=True, readonly=True, - related='room_type_id.pms_property_id') + related="room_type_id.pms_property_id", + ) floor_id = fields.Many2one( - 'pms.floor', - 'Ubication', - ondelete='restrict', - help='At which floor the room is located.') + "pms.floor", + "Ubication", + ondelete="restrict", + help="At which floor the room is located.", + ) bed_ids = fields.One2many( - 'pms.room', - 'shared_room_id', - readonly=True, - ondelete='restrict',) - active = fields.Boolean('Active', default=True) - sequence = fields.Integer('Sequence', required=True) - beds = fields.Integer('Beds') + "pms.room", "shared_room_id", readonly=True, ondelete="restrict", + ) + active = fields.Boolean("Active", default=True) + sequence = fields.Integer("Sequence", required=True) + beds = fields.Integer("Beds") description_sale = fields.Text( - 'Sale Description', + "Sale Description", translate=True, help="A description of the Product that you want to communicate to " - " your customers. This description will be copied to every Sales " - " Order, Delivery Order and Customer Invoice/Credit Note") + " your customers. This description will be copied to every Sales " + " Order, Delivery Order and Customer Invoice/Credit Note", + ) # Constraints and onchanges - @api.constrains('beds') + @api.constrains("beds") def _constrain_beds(self): self.ensure_one() if self.beds < 1: raise ValidationError(_("Room beds can't be less than one")) if len(self.bed_ids) > self.beds: - raise ValidationError(_( - "If you want to eliminate beds in the \ - room you must deactivate the beds from your form")) + raise ValidationError( + _( + "If you want to eliminate beds in the \ + room you must deactivate the beds from your form" + ) + ) beds = [] - inactive_beds = self.env['pms.room'].search([ - ('active', '=', False), - ('shared_room_id', '=', self.id) - ]) + inactive_beds = self.env["pms.room"].search( + [("active", "=", False), ("shared_room_id", "=", self.id)] + ) for i in range(len(self.bed_ids), self.beds): if inactive_beds: bed = inactive_beds[0] - bed.update({'active': True}) + bed.update({"active": True}) inactive_beds -= bed continue - name = u'%s (%s)' % (self.name, i + 1) + name = u"{} ({})".format(self.name, i + 1) bed_vals = { - 'name': name, - 'capacity': 1, - 'room_type_id': self.room_type_id.id, - 'sequence': self.sequence, - 'floor_id': self.floor_id.id if self.floor_id else False, - 'shared_room_id': self.id, + "name": name, + "capacity": 1, + "room_type_id": self.room_type_id.id, + "sequence": self.sequence, + "floor_id": self.floor_id.id if self.floor_id else False, + "shared_room_id": self.id, } beds.append((0, False, bed_vals)) if beds: - self.update({ - 'bed_ids': beds - }) + self.update({"bed_ids": beds}) - @api.constrains('active') + @api.constrains("active") def _constrain_active(self): - self.bed_ids.write({ - 'active': self.active, - }) + self.bed_ids.write( + {"active": self.active,} + ) - @api.constrains('room_type_id') + @api.constrains("room_type_id") def _constrain_room_type_id(self): - self.bed_ids.write({ - 'room_type_id': self.room_type_id.id, - }) + self.bed_ids.write( + {"room_type_id": self.room_type_id.id,} + ) - @api.constrains('floor_id') + @api.constrains("floor_id") def _constrain_floor_id(self): - self.bed_ids.write({ - 'floor_id': self.floor_id.id, - }) + self.bed_ids.write( + {"floor_id": self.floor_id.id,} + ) - @api.constrains('sequence') + @api.constrains("sequence") def _constrain_sequence(self): - self.bed_ids.write({ - 'sequence': self.sequence, - }) + self.bed_ids.write( + {"sequence": self.sequence,} + ) - @api.constrains('descrition_sale') + @api.constrains("descrition_sale") def _constrain_descrition_sale(self): - self.bed_ids.write({ - 'description_sale': self.descrition_sale, - }) + self.bed_ids.write( + {"description_sale": self.descrition_sale,} + ) diff --git a/pms/readme/CONFIGURE.rst b/pms/readme/CONFIGURE.rst index be4b60011..c26b4de76 100644 --- a/pms/readme/CONFIGURE.rst +++ b/pms/readme/CONFIGURE.rst @@ -3,4 +3,4 @@ the module before using it; it is aimed at advanced users. ] You will find the hotel settings in `Settings > Users & Companies > Hotels > Your Hotel. -This module required additional configuration for company, accounting, invoicing and user privileges. \ No newline at end of file +This module required additional configuration for company, accounting, invoicing and user privileges. diff --git a/pms/readme/CREDITS.rst b/pms/readme/CREDITS.rst index 61d2f78cc..cbd69e8ad 100644 --- a/pms/readme/CREDITS.rst +++ b/pms/readme/CREDITS.rst @@ -1,2 +1,2 @@ .. [ This file is optional and contains additional credits, other than -authors, contributors, and maintainers. ] \ No newline at end of file +authors, contributors, and maintainers. ] diff --git a/pms/readme/DESCRIPTION.rst b/pms/readme/DESCRIPTION.rst index 2e7db9fc7..d2b3d8f57 100644 --- a/pms/readme/DESCRIPTION.rst +++ b/pms/readme/DESCRIPTION.rst @@ -4,4 +4,4 @@ This module is an all-in-one property management system (PMS) focused on medium- for managing every aspect of your property's daily operations. You can manage hotel properties with multi-hotel and multi-company support, including your rooms inventory, -reservations, check-in, daily reports, board services, rate and restriction plans among other hotel functionalities. \ No newline at end of file +reservations, check-in, daily reports, board services, rate and restriction plans among other hotel functionalities. diff --git a/pms/readme/INSTALL.rst b/pms/readme/INSTALL.rst index f6504d125..2e88cc45a 100644 --- a/pms/readme/INSTALL.rst +++ b/pms/readme/INSTALL.rst @@ -2,4 +2,4 @@ installation instructions, such as installing non-python dependencies. The audience is systems administrators. ] This module depends on modules ``base``, ``sale_stock``, ``account_payment_return``, ``partner_firstname``, -and ``account_cancel``. Ensure yourself to have all them in your addons list. \ No newline at end of file +and ``account_cancel``. Ensure yourself to have all them in your addons list. diff --git a/pms/readme/ROADMAP.rst b/pms/readme/ROADMAP.rst index d49912e0a..e39c36eaa 100644 --- a/pms/readme/ROADMAP.rst +++ b/pms/readme/ROADMAP.rst @@ -1,4 +1,4 @@ .. [ Enumerate known caveats and future potential improvements. It is mostly intended for end-users, and can also help potential new contributors discovering new features to implement. ] -- [ ] \ No newline at end of file +- [ ] diff --git a/pms/readme/USAGE.rst b/pms/readme/USAGE.rst index 7fac9ecd5..e59fca743 100644 --- a/pms/readme/USAGE.rst +++ b/pms/readme/USAGE.rst @@ -3,4 +3,4 @@ for end-users. As all other rst files included in the README, it MUST NOT contai only body text (paragraphs, lists, tables, etc). Should you need a more elaborate structure to explain the addon, please create a Sphinx documentation (which may include this file as a "quick start" section). ] -To use this module, please, read the complete user guide at https://roomdoo.com. \ No newline at end of file +To use this module, please, read the complete user guide at https://roomdoo.com. diff --git a/pms/report/pms_folio.xml b/pms/report/pms_folio.xml index b9bc673d0..42ac8b644 100644 --- a/pms/report/pms_folio.xml +++ b/pms/report/pms_folio.xml @@ -1,4 +1,4 @@ - + + - + diff --git a/pms/security/pms_security.xml b/pms/security/pms_security.xml index ce3f78ca8..93734e609 100644 --- a/pms/security/pms_security.xml +++ b/pms/security/pms_security.xml @@ -1,22 +1,18 @@ - + - Property Management / User - Property Management/ Manager - + - Property Management / CallCenter - diff --git a/pms/static/src/js/views/list/list_controller.js b/pms/static/src/js/views/list/list_controller.js index c783b9984..7c75d376e 100644 --- a/pms/static/src/js/views/list/list_controller.js +++ b/pms/static/src/js/views/list/list_controller.js @@ -1,28 +1,32 @@ -odoo.define('pms.ListController', function(require) { -'use strict'; -/* - * Pms - * GNU Public License - * Alexandre Díaz - */ +odoo.define("pms.ListController", function(require) { + "use strict"; + /* + * Pms + * GNU Public License + * Alexandre Díaz + */ -var ListController = require('web.ListController'); -var Core = require('web.core'); + var ListController = require("web.ListController"); + var Core = require("web.core"); -var _t = Core._t; - -ListController.include({ - - renderButtons: function () { - this._super.apply(this, arguments); // Sets this.$buttons - var self = this; - if (this.modelName === 'pms.reservation') { - this.$buttons.append(""); - this.$buttons.find('.oe_open_reservation_wizard').on('click', function(){ - self.do_action('pms.open_wizard_reservations'); - }); - } - } -}); + var _t = Core._t; + ListController.include({ + renderButtons: function() { + this._super.apply(this, arguments); // Sets this.$buttons + var self = this; + if (this.modelName === "pms.reservation") { + this.$buttons.append( + "" + ); + this.$buttons + .find(".oe_open_reservation_wizard") + .on("click", function() { + self.do_action("pms.open_wizard_reservations"); + }); + } + }, + }); }); diff --git a/pms/static/src/js/widgets/switch_hotel_menu.js b/pms/static/src/js/widgets/switch_hotel_menu.js index bb985f3c7..515440f2e 100644 --- a/pms/static/src/js/widgets/switch_hotel_menu.js +++ b/pms/static/src/js/widgets/switch_hotel_menu.js @@ -1,61 +1,77 @@ -odoo.define('pms.SwitchPmsMenu', function(require) { -"use strict"; +odoo.define("pms.SwitchPmsMenu", function(require) { + "use strict"; -var config = require('web.config'); -var core = require('web.core'); -var session = require('web.session'); -var SystrayMenu = require('web.SystrayMenu'); -var Widget = require('web.Widget'); + var config = require("web.config"); + var core = require("web.core"); + var session = require("web.session"); + var SystrayMenu = require("web.SystrayMenu"); + var Widget = require("web.Widget"); -var _t = core._t; + var _t = core._t; -var SwitchPmsMenu = Widget.extend({ - template: 'pms.SwitchPmsMenu', - willStart: function() { - this.isMobile = config.device.isMobile; - if (!session.user_pms) { - return $.Deferred().reject(); - } - return this._super(); - }, - start: function() { - var self = this; - this.$el.on('click', '.dropdown-menu li a[data-menu]', _.debounce(function(ev) { - ev.preventDefault(); - var pms_property_id = $(ev.currentTarget).data('property-id'); - self._rpc({ - model: 'res.users', - method: 'write', - args: [[session.uid], {'pms_property_id': pms_property_id}], - }) - .then(function() { - location.reload(); - }); - }, 1500, true)); - - var properties_list = ''; - if (this.isMobile) { - propertiess_list = '
    • ' + _t('Tap on the list to change property') + '
    • '; - } - else { - self.$('.oe_topbar_name').text(session.user_properties.current_property[1]); - } - _.each(session.user_properties.allowed_propierties, function(property) { - var a = ''; - if (property[0] === session.user_properties.current_property[0]) { - a = ''; - } else { - a = ''; + var SwitchPmsMenu = Widget.extend({ + template: "pms.SwitchPmsMenu", + willStart: function() { + this.isMobile = config.device.isMobile; + if (!session.user_pms) { + return $.Deferred().reject(); } - properties_list += '
    • ' + a + property[1] + '
    • '; - }); - self.$('.dropdown-menu').html(properties_list); - return this._super(); - }, -}); - -SystrayMenu.Items.push(SwitchPmsMenu); - -return SwitchPmsMenu; - + return this._super(); + }, + start: function() { + var self = this; + this.$el.on( + "click", + ".dropdown-menu li a[data-menu]", + _.debounce( + function(ev) { + ev.preventDefault(); + var pms_property_id = $(ev.currentTarget).data("property-id"); + self._rpc({ + model: "res.users", + method: "write", + args: [[session.uid], {pms_property_id: pms_property_id}], + }).then(function() { + location.reload(); + }); + }, + 1500, + true + ) + ); + + var properties_list = ""; + if (this.isMobile) { + propertiess_list = + '
    • ' + + _t("Tap on the list to change property") + + "
    • "; + } else { + self.$(".oe_topbar_name").text( + session.user_properties.current_property[1] + ); + } + _.each(session.user_properties.allowed_propierties, function(property) { + var a = ""; + if (property[0] === session.user_properties.current_property[0]) { + a = ''; + } else { + a = ''; + } + properties_list += + '
    • ' + + a + + property[1] + + "
    • "; + }); + self.$(".dropdown-menu").html(properties_list); + return this._super(); + }, + }); + + SystrayMenu.Items.push(SwitchPmsMenu); + + return SwitchPmsMenu; }); diff --git a/pms/static/src/xml/pms_base_templates.xml b/pms/static/src/xml/pms_base_templates.xml index 60a185ef3..7c3dee2ae 100644 --- a/pms/static/src/xml/pms_base_templates.xml +++ b/pms/static/src/xml/pms_base_templates.xml @@ -1,12 +1,18 @@ diff --git a/pms/templates/pms_email_template.xml b/pms/templates/pms_email_template.xml index 47efc3ed1..d94e4050e 100644 --- a/pms/templates/pms_email_template.xml +++ b/pms/templates/pms_email_template.xml @@ -1,117 +1,139 @@ - + - - + + + Property: Reservation Confirmed - - ${('%s <%s>' % (object.pms_property_id.partner_id.name, object.pms_property_id.partner_id.email) or '')|safe} + + ${('%s <%s>' % (object.pms_property_id.partner_id.name, object.pms_property_id.partner_id.email) or '')|safe} ${(object.email or '')|safe} ${(object.partner_id.id or '')} ${object.partner_id.lang} - Your reservation ${object.name} has been confirmed by the property staff - + Your reservation ${object.name} has been confirmed by the property staff + qweb - - - + + diff --git a/pms/tests/__init__.py b/pms/tests/__init__.py index 2f32bd6cf..dc537dc29 100644 --- a/pms/tests/__init__.py +++ b/pms/tests/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution diff --git a/pms/tests/common.py b/pms/tests/common.py index be7844f90..dc9aa610d 100644 --- a/pms/tests/common.py +++ b/pms/tests/common.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution @@ -20,46 +19,50 @@ # along with this program. If not, see . # ############################################################################## +import logging from datetime import timedelta + from odoo import api, fields from odoo.tests import common -from odoo.tools import ( - DEFAULT_SERVER_DATE_FORMAT, - DEFAULT_SERVER_DATETIME_FORMAT) -import logging +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT + _logger = logging.getLogger(__name__) class TestHotel(common.SavepointCase): - @classmethod def _init_mock_hotel(cls): return True def create_folio(self, creator, partner): # Create Folio - folio = self.env['hotel.folio'].sudo(creator).create({ - 'partner_id': partner.id, - }) + folio = ( + self.env["hotel.folio"].sudo(creator).create({"partner_id": partner.id,}) + ) self.assertTrue(folio, "Can't create folio") return folio - def create_reservation(self, creator, folio, checkin, checkout, room, - resname, adults=1, children=0): + def create_reservation( + self, creator, folio, checkin, checkout, room, resname, adults=1, children=0 + ): # Create Reservation (Special Room) - reservation = self.env['hotel.reservation'].sudo(creator).create({ - 'name': resname, - 'adults': adults, - 'children': children, - 'checkin': checkin.strftime(DEFAULT_SERVER_DATETIME_FORMAT), - 'checkout': checkout.strftime(DEFAULT_SERVER_DATETIME_FORMAT), - 'folio_id': folio.id, - 'room_type_id': room.price_room_type.id, - 'product_id': room.product_id.id, - }) - self.assertTrue( - reservation, - "Hotel Calendar can't create a new reservation!") + reservation = ( + self.env["hotel.reservation"] + .sudo(creator) + .create( + { + "name": resname, + "adults": adults, + "children": children, + "checkin": checkin.strftime(DEFAULT_SERVER_DATETIME_FORMAT), + "checkout": checkout.strftime(DEFAULT_SERVER_DATETIME_FORMAT), + "folio_id": folio.id, + "room_type_id": room.price_room_type.id, + "product_id": room.product_id.id, + } + ) + ) + self.assertTrue(reservation, "Hotel Calendar can't create a new reservation!") # Create Reservation Lines + Update Reservation Price # days_diff = date_utils.date_diff(checkin, checkout, hours=False) @@ -79,27 +82,26 @@ class TestHotel(common.SavepointCase): cls._init_mock_hotel() # Create Tests Records - cls.main_hotel_property = cls.env.ref('hotel.main_hotel_property') - cls.demo_hotel_property = cls.env.ref('hotel.demo_hotel_property') + cls.main_hotel_property = cls.env.ref("hotel.main_hotel_property") + cls.demo_hotel_property = cls.env.ref("hotel.demo_hotel_property") - cls.room_type_0 = cls.env.ref('hotel.hotel_room_type_0') - cls.room_type_1 = cls.env.ref('hotel.hotel_room_type_1') - cls.room_type_2 = cls.env.ref('hotel.hotel_room_type_2') - cls.room_type_3 = cls.env.ref('hotel.hotel_room_type_3') + cls.room_type_0 = cls.env.ref("hotel.hotel_room_type_0") + cls.room_type_1 = cls.env.ref("hotel.hotel_room_type_1") + cls.room_type_2 = cls.env.ref("hotel.hotel_room_type_2") + cls.room_type_3 = cls.env.ref("hotel.hotel_room_type_3") - cls.demo_room_type_0 = cls.env.ref('hotel.demo_hotel_room_type_0') - cls.demo_room_type_1 = cls.env.ref('hotel.demo_hotel_room_type_1') + cls.demo_room_type_0 = cls.env.ref("hotel.demo_hotel_room_type_0") + cls.demo_room_type_1 = cls.env.ref("hotel.demo_hotel_room_type_1") - cls.room_0 = cls.env.ref('hotel.hotel_room_0') - cls.room_1 = cls.env.ref('hotel.hotel_room_1') - cls.room_2 = cls.env.ref('hotel.hotel_room_2') - cls.room_3 = cls.env.ref('hotel.hotel_room_3') - cls.room_4 = cls.env.ref('hotel.hotel_room_4') - cls.room_5 = cls.env.ref('hotel.hotel_room_5') - cls.room_6 = cls.env.ref('hotel.hotel_room_6') + cls.room_0 = cls.env.ref("hotel.hotel_room_0") + cls.room_1 = cls.env.ref("hotel.hotel_room_1") + cls.room_2 = cls.env.ref("hotel.hotel_room_2") + cls.room_3 = cls.env.ref("hotel.hotel_room_3") + cls.room_4 = cls.env.ref("hotel.hotel_room_4") + cls.room_5 = cls.env.ref("hotel.hotel_room_5") + cls.room_6 = cls.env.ref("hotel.hotel_room_6") - cls.list0 = cls.env.ref('product.list0') - cls.list1 = cls.env['product.pricelist'].create({ - 'name': 'Test Pricelist', - 'pricelist_type': '' - }) + cls.list0 = cls.env.ref("product.list0") + cls.list1 = cls.env["product.pricelist"].create( + {"name": "Test Pricelist", "pricelist_type": ""} + ) diff --git a/pms/tests/test_folio.py b/pms/tests/test_folio.py index 4c53e7912..6f5e26f5b 100644 --- a/pms/tests/test_folio.py +++ b/pms/tests/test_folio.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution @@ -21,12 +20,13 @@ # ############################################################################## from datetime import timedelta -from .common import TestHotel + from odoo.addons.hotel import date_utils +from .common import TestHotel + class TestHotelReservations(TestHotel): - def test_cancel_folio(self): now_utc_dt = date_utils.now() @@ -39,17 +39,18 @@ class TestHotelReservations(TestHotel): org_reserv_start_utc_dt, org_reserv_end_utc_dt, self.hotel_room_double_200, - "Reservation Test #1") + "Reservation Test #1", + ) reservation_b = self.create_reservation( self.user_hotel_manager, folio, org_reserv_start_utc_dt, org_reserv_end_utc_dt, self.hotel_room_simple_100, - "Reservation Test #2") - self.assertEqual(len(folio.reservation_ids), 2, 'Invalid room lines count') + "Reservation Test #2", + ) + self.assertEqual(len(folio.reservation_ids), 2, "Invalid room lines count") folio.action_cancel() - self.assertEqual(folio.state, 'cancel', 'Invalid folio state') + self.assertEqual(folio.state, "cancel", "Invalid folio state") for rline in folio.reservation_ids: - self.assertEqual(rline.state, 'cancelled', - 'Invalid reservation state') + self.assertEqual(rline.state, "cancelled", "Invalid reservation state") diff --git a/pms/tests/test_hotel_property.py b/pms/tests/test_hotel_property.py index 2e4efbca4..5f01cb038 100644 --- a/pms/tests/test_hotel_property.py +++ b/pms/tests/test_hotel_property.py @@ -1,7 +1,8 @@ -from .common import TestHotel from odoo import fields from odoo.exceptions import ValidationError +from .common import TestHotel + class TestHotelProperty(TestHotel): diff --git a/pms/tests/test_hotel_room.py b/pms/tests/test_hotel_room.py index d047b00e0..c4aa1e140 100644 --- a/pms/tests/test_hotel_room.py +++ b/pms/tests/test_hotel_room.py @@ -19,36 +19,36 @@ # along with this program. If not, see . # ############################################################################## -from .common import TestHotel from odoo.exceptions import ValidationError +from .common import TestHotel + class TestHotelRoom(TestHotel): - def test_rooms_by_hotel(self): # A room cannot be created in a room type of another hotel with self.assertRaises(ValidationError): - record = self.env['hotel.room'].sudo().create({ - 'name': 'Test Room', - 'hotel_id': self.demo_hotel_property.id, - 'room_type_id': self.room_type_0.id, - }) + record = ( + self.env["hotel.room"] + .sudo() + .create( + { + "name": "Test Room", + "hotel_id": self.demo_hotel_property.id, + "room_type_id": self.room_type_0.id, + } + ) + ) # A room cannot be changed to another hotel with self.assertRaises(ValidationError): - self.room_0.sudo().write({ - 'hotel_id': self.demo_room_type_0.hotel_id.id - }) + self.room_0.sudo().write({"hotel_id": self.demo_room_type_0.hotel_id.id}) def test_rooms_by_room_type(self): # A room cannot be changed to a room type of another hotel with self.assertRaises(ValidationError): - self.room_0.sudo().write({ - 'room_type_id': self.demo_room_type_1.id - }) + self.room_0.sudo().write({"room_type_id": self.demo_room_type_1.id}) def test_check_capacity(self): # The capacity of the room must be greater than 0 with self.assertRaises(ValidationError): - self.room_0.sudo().write({ - 'capacity': 0 - }) + self.room_0.sudo().write({"capacity": 0}) diff --git a/pms/tests/test_hotel_room_type.py b/pms/tests/test_hotel_room_type.py index a1bf6ec82..bff93c081 100644 --- a/pms/tests/test_hotel_room_type.py +++ b/pms/tests/test_hotel_room_type.py @@ -19,10 +19,12 @@ # along with this program. If not, see . # ############################################################################## -from .common import TestHotel from psycopg2 import IntegrityError + from odoo.tools import mute_logger +from .common import TestHotel + class TestHotelRoomType(TestHotel): @@ -30,15 +32,12 @@ class TestHotelRoomType(TestHotel): # code type must be unique by hotel def test_code_type_unique_by_hotel(self): - with self.assertRaises(IntegrityError), mute_logger('odoo.sql_db'): - self.room_type_0.sudo().write({ - 'code_type': self.room_type_1.code_type - }) + with self.assertRaises(IntegrityError), mute_logger("odoo.sql_db"): + self.room_type_0.sudo().write({"code_type": self.room_type_1.code_type}) # code type can be used in other hotel def test_code_type_shared_by_hotel(self): - test_result = self.demo_room_type_0.sudo().write({ - 'code_type': self.room_type_0.code_type - }) + test_result = self.demo_room_type_0.sudo().write( + {"code_type": self.room_type_0.code_type} + ) self.assertEqual(test_result, True) - diff --git a/pms/tests/test_inherited_ir_http.py b/pms/tests/test_inherited_ir_http.py index 63518fcf1..c3c92f698 100644 --- a/pms/tests/test_inherited_ir_http.py +++ b/pms/tests/test_inherited_ir_http.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- from .common import TestHotel class TestInheritedIrHttp(TestHotel): - def test_user_hotel_company(self): - admin_user = self.env.ref('base.user_root') - self.assertTrue(admin_user.hotel_id.company_id in admin_user.company_ids, - "Wrong hotel and company access settings for %s" % admin_user.name) - + admin_user = self.env.ref("base.user_root") + self.assertTrue( + admin_user.hotel_id.company_id in admin_user.company_ids, + "Wrong hotel and company access settings for %s" % admin_user.name, + ) diff --git a/pms/tests/test_inherited_product_pricelist.py b/pms/tests/test_inherited_product_pricelist.py index be7f56507..abfe2e408 100644 --- a/pms/tests/test_inherited_product_pricelist.py +++ b/pms/tests/test_inherited_product_pricelist.py @@ -1,7 +1,8 @@ -from .common import TestHotel from odoo import fields from odoo.exceptions import ValidationError +from .common import TestHotel + class TestInheritedProductPricelist(TestHotel): @@ -17,11 +18,13 @@ class TestInheritedProductPricelist(TestHotel): self.list0.hotel_ids = False # create a valid record using a daily pricelist - test_result = self.env['product.pricelist'].create({ - 'name': 'Test Daily Pricelist', - 'hotel_ids': [(4, self.demo_hotel_property.id)] - }) - self.assertEqual(test_result.pricelist_type, 'daily') + test_result = self.env["product.pricelist"].create( + { + "name": "Test Daily Pricelist", + "hotel_ids": [(4, self.demo_hotel_property.id)], + } + ) + self.assertEqual(test_result.pricelist_type, "daily") self.assertEqual(test_result.hotel_ids, self.demo_hotel_property) def test_pricelist_by_hotel(self): diff --git a/pms/tests/test_massive_changes.py b/pms/tests/test_massive_changes.py index 83b0a3ee9..1da2d6b8f 100644 --- a/pms/tests/test_massive_changes.py +++ b/pms/tests/test_massive_changes.py @@ -1,7 +1,8 @@ -from .common import TestHotel from odoo import fields from odoo.exceptions import ValidationError +from .common import TestHotel + class TestMassiveChanges(TestHotel): @@ -10,59 +11,51 @@ class TestMassiveChanges(TestHotel): # base massive change record def base_massive_change_vals(self, hotel_id=None): return { - 'hotel_id': hotel_id and hotel_id.id or self.main_hotel_property.id, - 'date_start': fields.Date.today(), - 'date_end': fields.Date.today(), + "hotel_id": hotel_id and hotel_id.id or self.main_hotel_property.id, + "date_start": fields.Date.today(), + "date_end": fields.Date.today(), } def pricelist_massive_change_vals(self, pricelist_id=None): return { - 'pricelist_id': pricelist_id and pricelist_id.id or self.list0.id, - 'price': 50.0, + "pricelist_id": pricelist_id and pricelist_id.id or self.list0.id, + "price": 50.0, } def test_daily_pricelist(self): # Only daily pricelist can be manage by a massive change - self.list0.pricelist_type = '' + self.list0.pricelist_type = "" with self.assertRaises(ValidationError): vals = self.base_massive_change_vals() - vals.update( - self.pricelist_massive_change_vals() - ) - self.env['hotel.wizard.massive.changes'].create(vals) + vals.update(self.pricelist_massive_change_vals()) + self.env["hotel.wizard.massive.changes"].create(vals) # create a valid record using a daily pricelist - self.list0.pricelist_type = 'daily' - test_result = self.env['hotel.wizard.massive.changes'].create(vals) + self.list0.pricelist_type = "daily" + test_result = self.env["hotel.wizard.massive.changes"].create(vals) self.assertEqual(test_result.pricelist_id, self.list0) def test_pricelist_by_hotel(self): # Ensure the pricelist plan belongs to the current hotel #1 with self.assertRaises(ValidationError): vals = self.base_massive_change_vals(self.demo_hotel_property) - vals.update( - self.pricelist_massive_change_vals() - ) - self.env['hotel.wizard.massive.changes'].create(vals) + vals.update(self.pricelist_massive_change_vals()) + self.env["hotel.wizard.massive.changes"].create(vals) # Ensure the pricelist plan belongs to the current hotel #2 with self.assertRaises(ValidationError): vals = self.base_massive_change_vals() - vals.update( - self.pricelist_massive_change_vals(self.list1) - ) + vals.update(self.pricelist_massive_change_vals(self.list1)) self.list1.hotel_ids = self.demo_hotel_property - self.list1.pricelist_type = 'daily' - self.env['hotel.wizard.massive.changes'].create(vals) + self.list1.pricelist_type = "daily" + self.env["hotel.wizard.massive.changes"].create(vals) # create a valid record using the current hotel vals = self.base_massive_change_vals() - vals.update( - self.pricelist_massive_change_vals(self.list1) - ) + vals.update(self.pricelist_massive_change_vals(self.list1)) self.list1.hotel_ids = self.main_hotel_property - self.list1.pricelist_type = 'daily' - test_result = self.env['hotel.wizard.massive.changes'].create(vals) + self.list1.pricelist_type = "daily" + test_result = self.env["hotel.wizard.massive.changes"].create(vals) self.assertEqual(test_result.pricelist_id.hotel_ids, self.main_hotel_property) def test_do_massive_change(self): diff --git a/pms/tests/test_reservation.py b/pms/tests/test_reservation.py index dc02750e0..acb7b7af9 100644 --- a/pms/tests/test_reservation.py +++ b/pms/tests/test_reservation.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution @@ -21,19 +20,23 @@ # ############################################################################## import datetime -from datetime import timedelta -from odoo import fields -from openerp.tools import DEFAULT_SERVER_DATE_FORMAT -from openerp.exceptions import ValidationError -from .common import TestHotel -from odoo.addons.hotel import date_utils -import pytz import logging +from datetime import timedelta + +import pytz +from openerp.exceptions import ValidationError +from openerp.tools import DEFAULT_SERVER_DATE_FORMAT + +from odoo import fields + +from odoo.addons.hotel import date_utils + +from .common import TestHotel + _logger = logging.getLogger(__name__) class TestHotelReservations(TestHotel): - def test_create_reservation(self): now_utc_dt = date_utils.now() reserv_start_utc_dt = now_utc_dt + timedelta(days=3) @@ -45,25 +48,32 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Reservation Test #1") + "Reservation Test #1", + ) - reserv_start_dt = date_utils.dt_as_timezone(reserv_start_utc_dt, - self.tz_hotel) - reserv_end_dt = date_utils.dt_as_timezone(reserv_end_utc_dt - - timedelta(days=1), - self.tz_hotel) - self.assertEqual(reservation.reservation_lines[0].date, - reserv_start_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - "Reservation lines don't start in the correct date") - self.assertEqual(reservation.reservation_lines[-1].date, - reserv_end_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - "Reservation lines don't end in the correct date") + reserv_start_dt = date_utils.dt_as_timezone(reserv_start_utc_dt, self.tz_hotel) + reserv_end_dt = date_utils.dt_as_timezone( + reserv_end_utc_dt - timedelta(days=1), self.tz_hotel + ) + self.assertEqual( + reservation.reservation_lines[0].date, + reserv_start_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), + "Reservation lines don't start in the correct date", + ) + self.assertEqual( + reservation.reservation_lines[-1].date, + reserv_end_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), + "Reservation lines don't end in the correct date", + ) total_price = 0.0 for rline in reservation.reservation_lines: total_price += rline.price - self.assertEqual(folio.amount_untaxed, total_price, - "Folio amount doesn't match with reservation lines") + self.assertEqual( + folio.amount_untaxed, + total_price, + "Folio amount doesn't match with reservation lines", + ) def test_create_reservations(self): now_utc_dt = date_utils.now() @@ -76,7 +86,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Reservation Test #1") + "Reservation Test #1", + ) reserv_start_utc_dt = reserv_end_utc_dt reserv_end_utc_dt = reserv_start_utc_dt + timedelta(days=3) @@ -87,7 +98,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Reservation Test #2") + "Reservation Test #2", + ) reserv_end_utc_dt = now_utc_dt + timedelta(days=3) reserv_start_utc_dt = reserv_end_utc_dt - timedelta(days=1) @@ -98,7 +110,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Reservation Test #3") + "Reservation Test #3", + ) reserv_start_utc_dt = now_utc_dt + timedelta(days=3) reserv_end_utc_dt = reserv_start_utc_dt + timedelta(days=3) @@ -109,7 +122,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_simple_100, - "Reservation Test #4") + "Reservation Test #4", + ) def test_create_invalid_reservations(self): now_utc_dt = date_utils.now() @@ -123,7 +137,8 @@ class TestHotelReservations(TestHotel): org_reserv_start_utc_dt, org_reserv_end_utc_dt, self.hotel_room_double_200, - "Original Reservation Test #1") + "Original Reservation Test #1", + ) # Same Dates reserv_start_utc_dt = now_utc_dt + timedelta(days=3) @@ -136,7 +151,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Invalid Reservation Test #1") + "Invalid Reservation Test #1", + ) # Inside Org Reservation (Start Same Date) reserv_start_utc_dt = now_utc_dt + timedelta(days=3) @@ -149,7 +165,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Invalid Reservation Test #2") + "Invalid Reservation Test #2", + ) # Inside Org Reservation (Start after) reserv_start_utc_dt = now_utc_dt + timedelta(days=4) @@ -162,7 +179,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Invalid Reservation Test #3") + "Invalid Reservation Test #3", + ) # Intersect Org Reservation (Start before) reserv_start_utc_dt = now_utc_dt + timedelta(days=2) @@ -175,7 +193,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Invalid Reservation Test #4") + "Invalid Reservation Test #4", + ) # Intersect Org Reservation (End Same) reserv_start_utc_dt = org_reserv_end_utc_dt - timedelta(days=2) @@ -188,7 +207,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Invalid Reservation Test #5") + "Invalid Reservation Test #5", + ) # Intersect Org Reservation (End after) reserv_start_utc_dt = org_reserv_end_utc_dt - timedelta(days=2) @@ -201,7 +221,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Invalid Reservation Test #6") + "Invalid Reservation Test #6", + ) # Overlays Org Reservation reserv_start_utc_dt = org_reserv_start_utc_dt - timedelta(days=2) @@ -214,7 +235,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Invalid Reservation Test #7") + "Invalid Reservation Test #7", + ) # Checkin > Checkout with self.assertRaises(ValidationError): @@ -225,14 +247,17 @@ class TestHotelReservations(TestHotel): org_reserv_end_utc_dt, org_reserv_start_utc_dt, self.hotel_room_simple_100, - "Invalid Reservation Test #8") + "Invalid Reservation Test #8", + ) def test_modify_reservation(self): now_utc_dt = date_utils.now() # 5.0, 15.0, 15.0, 35.0, 35.0, 10.0, 10.0 - room_type_prices = self.prices_tmp[self.hotel_room_double_200.price_room_type.id] + room_type_prices = self.prices_tmp[ + self.hotel_room_double_200.price_room_type.id + ] org_reserv_start_utc_dt = now_utc_dt + timedelta(days=1) org_reserv_end_utc_dt = org_reserv_start_utc_dt + timedelta(days=2) folio = self.create_folio(self.user_hotel_manager, self.partner_2) @@ -242,19 +267,28 @@ class TestHotelReservations(TestHotel): org_reserv_start_utc_dt, org_reserv_end_utc_dt, self.hotel_room_double_200, - "Original Reservation Test #1") + "Original Reservation Test #1", + ) ndate = org_reserv_start_utc_dt for r_k, r_v in enumerate(reservation.reservation_lines): self.assertEqual(r_v.date, ndate.strftime(DEFAULT_SERVER_DATE_FORMAT)) - self.assertEqual(r_v.price, room_type_prices[r_k+1]) + self.assertEqual(r_v.price, room_type_prices[r_k + 1]) ndate = ndate + timedelta(days=1) self.assertEqual(reservation.amount_room, 30.0) ndate = org_reserv_start_utc_dt + timedelta(days=1) - line = reservation.reservation_lines.filtered(lambda r: r.date == ndate.strftime(DEFAULT_SERVER_DATE_FORMAT)) - reservation.reservation_lines = [(1, line.id, {'price': 100.0})] + line = reservation.reservation_lines.filtered( + lambda r: r.date == ndate.strftime(DEFAULT_SERVER_DATE_FORMAT) + ) + reservation.reservation_lines = [(1, line.id, {"price": 100.0})] self.assertEqual(reservation.amount_room, 115.0) - reservation.sudo(self.user_hotel_manager).write({ - 'checkin': (org_reserv_start_utc_dt + timedelta(days=1)).strftime(DEFAULT_SERVER_DATE_FORMAT), - 'checkout': (org_reserv_end_utc_dt + timedelta(days=1)).strftime(DEFAULT_SERVER_DATE_FORMAT), - }) + reservation.sudo(self.user_hotel_manager).write( + { + "checkin": (org_reserv_start_utc_dt + timedelta(days=1)).strftime( + DEFAULT_SERVER_DATE_FORMAT + ), + "checkout": (org_reserv_end_utc_dt + timedelta(days=1)).strftime( + DEFAULT_SERVER_DATE_FORMAT + ), + } + ) self.assertEqual(reservation.amount_room, 135.0) diff --git a/pms/views/general.xml b/pms/views/general.xml index ac7898093..7d5672a99 100644 --- a/pms/views/general.xml +++ b/pms/views/general.xml @@ -1,11 +1,12 @@ - + -