diff --git a/pms/README.rst b/pms/README.rst index 115e2e8f5a..bb7d85e4ba 100644 --- a/pms/README.rst +++ b/pms/README.rst @@ -1,7 +1,3 @@ -.. image:: https://odoo-community.org/readme-banner-image - :target: https://odoo-community.org/get-involved?utm_source=readme - :alt: Odoo Community Association - ================================ PMS (Property Management System) ================================ @@ -17,7 +13,7 @@ PMS (Property Management System) .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpms-lightgray.png?logo=github diff --git a/pms/__manifest__.py b/pms/__manifest__.py index 419d1e04fc..6b9afd0c22 100644 --- a/pms/__manifest__.py +++ b/pms/__manifest__.py @@ -4,7 +4,7 @@ { "name": "PMS (Property Management System)", "summary": "A property management system", - "version": "16.0.0.14.0", + "version": "16.0.0.16.0", "development_status": "Beta", "category": "Generic Modules/Property Management System", "website": "https://github.com/OCA/pms", @@ -26,7 +26,6 @@ "partner_contact_birthdate", "partner_contact_nationality", # "partner_identification_unique_by_category", - "queue_job", "web_timeline", "partner_identification", "analytic", @@ -50,8 +49,6 @@ "report/invoice.xml", # "templates/pms_email_template.xml", "data/menus.xml", - "data/queue_data.xml", - "data/queue_job_function_data.xml", "wizards/wizard_payment_folio.xml", "wizards/folio_make_invoice_advance_views.xml", "wizards/pms_booking_engine_views.xml", diff --git a/pms/data/cron_jobs.xml b/pms/data/cron_jobs.xml index f5f86f0da0..a5cec1d906 100644 --- a/pms/data/cron_jobs.xml +++ b/pms/data/cron_jobs.xml @@ -70,36 +70,5 @@ model.send_cancelation_mail() --> - - Auto Invoicing Folios - 1 - - - days - -1 - - code - - - model.autoinvoicing() - - - Auto Invoicing DownPayments - 1 - - - days - -1 - - code - - - model.auto_invoice_downpayments(offset=1) - + diff --git a/pms/data/queue_data.xml b/pms/data/queue_data.xml deleted file mode 100644 index 8881324d02..0000000000 --- a/pms/data/queue_data.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - autoinvoicing folios - - - diff --git a/pms/data/queue_job_function_data.xml b/pms/data/queue_job_function_data.xml deleted file mode 100644 index f1fba42330..0000000000 --- a/pms/data/queue_job_function_data.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - autoinvoice_folio - - - - - - autovalidate_folio_invoice - - - - diff --git a/pms/migrations/16.0.0.16.0/pre-migration.py b/pms/migrations/16.0.0.16.0/pre-migration.py new file mode 100644 index 0000000000..17f9861f6e --- /dev/null +++ b/pms/migrations/16.0.0.16.0/pre-migration.py @@ -0,0 +1,14 @@ +from openupgradelib import openupgrade + + +@openupgrade.migrate() +def migrate(env, version): + _deleted_xml_records = [ + "pms.view_partner_property_form", + "pms.autoinvoicing_folios", + "pms.autoinvoicing_downpayments", + "pms.autoinvoice_folio_job_function", + "pms.autovalidate_invoice_folio_job_function", + "pms.channel_autoinvoicing_folios", + ] + openupgrade.delete_records_safely_by_xml_id(env, _deleted_xml_records) diff --git a/pms/models/account_journal.py b/pms/models/account_journal.py index b3db56936a..35930e6fab 100644 --- a/pms/models/account_journal.py +++ b/pms/models/account_journal.py @@ -20,10 +20,6 @@ class AccountJournal(models.Model): string="For manual payments", help="Use to pay for reservations", ) - avoid_autoinvoice_downpayment = fields.Boolean( - help="Avoid autoinvoice downpayment", - default=False, - ) is_simplified_invoice = fields.Boolean( string="Simplified invoice", help="Use to simplified invoice", diff --git a/pms/models/account_payment.py b/pms/models/account_payment.py index fef5fee16d..238f8b9b4b 100644 --- a/pms/models/account_payment.py +++ b/pms/models/account_payment.py @@ -1,9 +1,5 @@ # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from collections import defaultdict - -from dateutil.relativedelta import relativedelta - from odoo import _, api, fields, models @@ -125,51 +121,6 @@ def action_draft(self): downpayment_invoices.unlink() return super().action_draft() - @api.model - def auto_invoice_downpayments(self, offset=0): - """ - This method is called by a cron job to invoice the downpayments - based on the company settings. - """ - date_reference = fields.Date.today() - relativedelta(days=offset) - payments = self._get_downpayments_to_invoice(date_reference) - for payment in payments: - partner_id = ( - payment.partner_id.id or self.env.ref("pms.various_pms_partner").id - ) - self._create_downpayment_invoice( - payment=payment, - partner_id=partner_id, - ) - return True - - @api.model - def _get_downpayments_to_invoice(self, date_reference): - companys = self.env["res.company"].search([]) - payments = self.env["account.payment"] - for company in companys: - if company.pms_invoice_downpayment_policy == "all": - date_ref = fields.Date.today() - elif company.pms_invoice_downpayment_policy == "checkout_past_month": - date_ref = fields.Date.today().replace( - day=1, month=fields.Date.today().month + 1 - ) - else: - continue - payments += self.search( - [ - ("state", "=", "posted"), - ("partner_type", "=", "customer"), - ("company_id", "=", company.id), - ("journal_id.avoid_autoinvoice_downpayment", "=", False), - ("folio_ids", "!=", False), - ("folio_ids.last_checkout", ">=", date_ref), - ("date", "<=", date_reference), - ] - ) - payments = payments.filtered(lambda p: not p.reconciled_invoice_ids) - return payments - @api.model def _check_has_downpayment_invoice(self, payment): if ( @@ -181,39 +132,3 @@ def _check_has_downpayment_invoice(self, payment): ): return True return False - - @api.model - def _create_downpayment_invoice(self, payment, partner_id): - invoice_wizard = self.env["folio.advance.payment.inv"].create( - { - "partner_invoice_id": partner_id, - "advance_payment_method": "fixed", - "fixed_amount": payment.amount, - } - ) - move = invoice_wizard.with_context( - active_ids=payment.folio_ids.ids, - return_invoices=True, - ).create_invoices() - if payment.payment_type == "outbound": - move.action_switch_invoice_into_refund_credit_note() - move.action_post() - for invoice, payment_move in zip(move, payment.move_id, strict=True): - group = defaultdict(list) - for line in (invoice.line_ids + payment_move.line_ids).filtered( - lambda r: not r.reconciled - ): - group[(line.account_id, line.currency_id)].append(line.id) - for (account, _dummy), line_ids in group.items(): - if ( - account.reconcile or account.account_type == "liquidity" - ): # TODO: liquidity not in account.account_type - self.env["account.move.line"].browse(line_ids).reconcile() - # Set folio sale lines default_invoice_to to partner downpayment invoice - for folio in payment.folio_ids: - for sale_line in folio.sale_line_ids.filtered( - lambda r: not r.default_invoice_to - ): - sale_line.default_invoice_to = move.partner_id.id - - return move diff --git a/pms/models/folio_sale_line.py b/pms/models/folio_sale_line.py index c82b771bb7..f6d3af0dfd 100644 --- a/pms/models/folio_sale_line.py +++ b/pms/models/folio_sale_line.py @@ -1,11 +1,9 @@ # Copyright 2020 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from datetime import timedelta from math import ceil import babel.dates -from dateutil import relativedelta from odoo import _, api, fields, models from odoo.exceptions import UserError @@ -330,10 +328,6 @@ class FolioSaleLine(models.Model): ondelete="restrict", index=True, ) - autoinvoice_date = fields.Date( - compute="_compute_autoinvoice_date", - store=True, - ) auto_reservation_note = fields.Boolean( help="Indicates if the folio sale line is an auto reservation note", default=False, @@ -402,55 +396,6 @@ def _compute_date_order(self): else: record.date_order = 0 - @api.depends( - "default_invoice_to", - "invoice_status", - "folio_id.last_checkout", - "reservation_id.checkout", - "service_id.reservation_id.checkout", - ) - def _compute_autoinvoice_date(self): - for record in self: - record.autoinvoice_date = record._get_to_invoice_date() - - def _get_to_invoice_date(self): - self.ensure_one() - partner = self.default_invoice_to - if self.reservation_id: - last_checkout = self.reservation_id.checkout - elif self.service_id and self.service_id.reservation_id: - last_checkout = self.service_id.reservation_id.checkout - else: - last_checkout = self.folio_id.last_checkout - if not last_checkout: - return False - invoicing_policy = ( - self.folio_id.pms_property_id.default_invoicing_policy - if not partner or partner.invoicing_policy == "property" - else partner.invoicing_policy - ) - if invoicing_policy == "manual": - return False - if invoicing_policy == "checkout": - margin_days = ( - self.folio_id.pms_property_id.margin_days_autoinvoice - if not partner or partner.invoicing_policy == "property" - else partner.margin_days_autoinvoice - ) - return last_checkout + timedelta(days=margin_days) - if invoicing_policy == "month_day": - month_day = ( - self.folio_id.pms_property_id.invoicing_month_day - if not partner or partner.invoicing_policy == "property" - else partner.invoicing_month_day - ) - if last_checkout.day <= month_day: - return last_checkout.replace(day=month_day) - else: - return (last_checkout + relativedelta.relativedelta(months=1)).replace( - day=month_day - ) - @api.depends("date_order") def _compute_reservation_order(self): for record in self: diff --git a/pms/models/pms_folio.py b/pms/models/pms_folio.py index d98193ae38..75950cd8e0 100644 --- a/pms/models/pms_folio.py +++ b/pms/models/pms_folio.py @@ -1841,6 +1841,26 @@ def preview_folio(self): "url": self.get_portal_url(), } + def _get_lines_to_invoice(self, final=False): + self = self.with_context(lines_auto_add=True) + lines_to_invoice = dict() + for line in self.sale_line_ids.filtered( + lambda r: r.qty_to_invoice > 0 + or (r.qty_to_invoice < 0 and final) + or r.display_type == "line_note" + ): + lines_to_invoice[line.id] = 0 if line.display_type else line.qty_to_invoice + return lines_to_invoice + + def _get_invoice_date(self, partner_invoice_id, lines_to_invoice, date=None): + """ + Return the invoice date to use for the created invoices. + """ + invoice_date = False + if date: + invoice_date = date + return invoice_date + # ruff: noqa: C901 def _create_invoices( self, @@ -1868,24 +1888,7 @@ def _create_invoices( return self.env["account.move"] # 1) Create invoices. if not lines_to_invoice: - self = self.with_context(lines_auto_add=True) - lines_to_invoice = dict() - for line in self.sale_line_ids.filtered( - lambda r: r.qty_to_invoice > 0 - or (r.qty_to_invoice < 0 and final) - or r.display_type == "line_note" - ): - if not self._context.get("autoinvoice"): - lines_to_invoice[line.id] = ( - 0 if line.display_type else line.qty_to_invoice - ) - elif ( - line.autoinvoice_date - and line.autoinvoice_date <= fields.Date.today() - ): - lines_to_invoice[line.id] = ( - 0 if line.display_type else line.qty_to_invoice - ) + lines_to_invoice = self._get_lines_to_invoice(final=final) invoice_vals_list = self.get_invoice_vals_list( final=final, lines_to_invoice=lines_to_invoice, @@ -1898,41 +1901,9 @@ def _create_invoices( if not grouped: invoice_vals_list = self._get_group_vals_list(invoice_vals_list) - partner_invoice = self.env["res.partner"].browse(partner_invoice_id) - partner_invoice_policy = self.pms_property_id.default_invoicing_policy - if partner_invoice and partner_invoice.invoicing_policy != "property": - partner_invoice_policy = partner_invoice.invoicing_policy - invoice_date = False - if date: - invoice_date = date - if partner_invoice_policy == "checkout": - margin_days_autoinvoice = ( - self.pms_property_id.margin_days_autoinvoice - if partner_invoice.margin_days_autoinvoice == 0 - else partner_invoice.margin_days_autoinvoice - ) - invoice_date = max( - self.env["pms.reservation"] - .search([("sale_line_ids", "in", lines_to_invoice.keys())]) - .mapped("checkout") - ) + datetime.timedelta(days=margin_days_autoinvoice) - if partner_invoice_policy == "month_day": - month_day = ( - self.pms_property_id.invoicing_month_day - if partner_invoice.invoicing_month_day == 0 - else partner_invoice.invoicing_month_day - ) - invoice_date = datetime.date( - datetime.date.today().year, - datetime.date.today().month, - month_day, - ) - if invoice_date < datetime.date.today(): - invoice_date = datetime.date( - datetime.date.today().year, - datetime.date.today().month + 1, - month_day, - ) + invoice_date = self._get_invoice_date( + partner_invoice_id, lines_to_invoice, date + ) if invoice_date: if ( self.company_id.period_lock_date @@ -1945,10 +1916,6 @@ def _create_invoices( "Please contact your administrator to unlock it." ) ) - if invoice_date < datetime.date.today() and not self._context.get( - "autoinvoice" - ): - invoice_date = datetime.date.today() key_field = ( "invoice_date" if invoice_date <= fields.Date.today() diff --git a/pms/models/pms_property.py b/pms/models/pms_property.py index f79a6e1efe..93c13adaac 100644 --- a/pms/models/pms_property.py +++ b/pms/models/pms_property.py @@ -7,7 +7,6 @@ import time import pytz -from dateutil.relativedelta import relativedelta from odoo import _, api, fields, models, modules from odoo.exceptions import ValidationError @@ -156,21 +155,7 @@ class PmsProperty(models.Model): is_modified_auto_mail = fields.Boolean(string="Auto Send Modification Mail") is_exit_auto_mail = fields.Boolean(string="Auto Send Exit Mail") is_canceled_auto_mail = fields.Boolean(string="Auto Send Cancellation Mail") - default_invoicing_policy = fields.Selection( - selection=[ - ("manual", "Manual"), - ("checkout", "Checkout"), - ("month_day", "Month Day Invoice"), - ], - default="manual", - ) - margin_days_autoinvoice = fields.Integer( - string="Margin Days", - help="Days from Checkout to generate the invoice", - ) - invoicing_month_day = fields.Integer( - help="The day of the month to invoice", - ) + journal_simplified_invoice_id = fields.Many2one( string="Simplified Invoice Journal", comodel_name="account.journal", @@ -763,201 +748,6 @@ def daily_closing( ) return True - @api.model - def autoinvoicing(self, offset=0, with_delay=False, autocommit=False): - """ - This method is used to invoicing automatically the folios - and validate the draft invoices created by the folios - """ - date_reference = fields.Date.today() - relativedelta(days=offset) - # REVIEW: We clean the autoinvoice_date of the past draft invoices - # to avoid blocking the autoinvoicing - self.clean_date_on_past_draft_invoices(date_reference) - # 1- Invoicing the folios - folios = self.env["pms.folio"].search( - [ - ("sale_line_ids.autoinvoice_date", "=", date_reference), - ("invoice_status", "=", "to_invoice"), - ("amount_total", ">", 0), - ] - ) - paid_folios = folios.filtered(lambda f: f.pending_amount <= 0) - unpaid_folios = folios.filtered(lambda f: f.pending_amount > 0) - folios_to_invoice = paid_folios - # If the folio is unpaid we will auto invoice only the - # not cancelled lines - for folio in unpaid_folios: - if any([res.state != "cancel" for res in folio.reservation_ids]): - folios_to_invoice += folio - else: - folio.sudo().message_post( - body=_( - "Not invoiced due to pending amounts and cancelled reservations" - ) - ) - for folio in folios_to_invoice: - if with_delay: - self.with_delay().autoinvoice_folio(folio) - else: - self.autoinvoice_folio(folio) - # 2- Validate the draft invoices created by the folios - draft_invoices_to_post = self.env["account.move"].search( - [ - ("state", "=", "draft"), - ("invoice_date_due", "=", date_reference), - ("folio_ids", "!=", False), - ("amount_total", ">", 0), - ] - ) - for invoice in draft_invoices_to_post: - if with_delay: - self.with_delay().autovalidate_folio_invoice(invoice) - else: - self.autovalidate_folio_invoice(invoice) - - # 3- Reverse the downpayment invoices that not was included in final invoice - downpayments_invoices_to_reverse = self.env["account.move.line"].search( - [ - ("move_id.state", "=", "posted"), - ("folio_line_ids.is_downpayment", "=", True), - ("folio_line_ids.qty_invoiced", ">", 0), - ("folio_ids", "in", folios.ids), - ] - ) - downpayment_invoices = downpayments_invoices_to_reverse.mapped("move_id") - if downpayment_invoices: - for downpayment_invoice in downpayment_invoices: - default_values_list = [ - { - "ref": _(f'Reversal of: {move.name + " - " + move.ref}'), - } - for move in downpayment_invoice - ] - downpayment_invoice.with_context(sii_refund_type="I")._reverse_moves( - default_values_list, cancel=True - ) - downpayment_invoice.message_post( - body=_( - """ - The downpayment invoice has been reversed - because it was not included in the final invoice - """ - ) - ) - - return True - - @api.model - def clean_date_on_past_draft_invoices(self, date_reference): - """ - This method is used to clean the date on past draft invoices - """ - journal_ids = ( - self.env["account.journal"] - .search( - [ - ("type", "=", "sale"), - ("pms_property_ids", "!=", False), - ] - ) - .ids - ) - draft_invoices = self.env["account.move"].search( - [ - ("state", "=", "draft"), - ("invoice_date", "<", date_reference), - ("journal_id", "in", journal_ids), - ] - ) - if draft_invoices: - draft_invoices.write({"invoice_date": date_reference}) - return True - - def autovalidate_folio_invoice(self, invoice): - try: - with self.env.cr.savepoint(): - invoice.action_post() - except Exception as e: - raise ValidationError( - _("Error in autovalidate invoice: %s") % str(e) - ) from e - - def autoinvoice_folio(self, folio): - try: - with self.env.cr.savepoint(): - # REVIEW: folio sale line "_compute_auotinvoice_date" sometimes - # dont work in services (probably cache issue¿?), - # we ensure that the date is set or recompute this - for line in folio.sale_line_ids.filtered( - lambda r: not r.autoinvoice_date - ): - line._compute_autoinvoice_date() - invoices = folio.with_context(autoinvoice=True)._create_invoices( - grouped=True, - final=False, - ) - downpayments = folio.sale_line_ids.filtered( - lambda r: r.is_downpayment and r.qty_invoiced > 0 - ) - for invoice in invoices: - if ( - invoice.amount_total - > invoice.pms_property_id.max_amount_simplified_invoice - and invoice.journal_id.is_simplified_invoice - ): - hosts_to_invoice = ( - invoice.folio_ids.partner_invoice_ids.filtered( - lambda p: p._check_enought_invoice_data() - ).mapped("id") - ) - if hosts_to_invoice: - invoice.partner_id = hosts_to_invoice[0] - invoice.journal_id = ( - invoice.pms_property_id.journal_normal_invoice_id - ) - else: - mens = _( - "The total amount of the simplified invoice is " - "higher than the maximum amount allowed for " - "simplified invoices, and dont have enought data" - " in hosts to create a normal invoice." - ) - folio.sudo().message_post(body=mens) - raise ValidationError(mens) - for downpayment in downpayments.filtered( - lambda d, i=invoice: d.default_invoice_to == i.partner_id - ): - # If the downpayment invoice partner is the same that the - # folio partner, we include the downpayment in the - # normal invoice - invoice_down_payment_vals = downpayment._prepare_invoice_line( - sequence=max(invoice.invoice_line_ids.mapped("sequence")) - + 1, - ) - invoice.write( - {"invoice_line_ids": [(0, 0, invoice_down_payment_vals)]} - ) - invoice.action_post() - # The downpayment invoices that not was included in final - # invoice, are reversed - downpayment_invoices = ( - downpayments.filtered( - lambda d: d.qty_invoiced > 0 - ).invoice_lines.mapped("move_id") - ).filtered(lambda i: i.is_simplified_invoice) - if downpayment_invoices: - default_values_list = [ - { - "ref": _(f'Reversal of: {move.name + " - " + move.ref}'), - } - for move in downpayment_invoices - ] - downpayment_invoices.with_context( - sii_refund_type="I" - )._reverse_moves(default_values_list, cancel=True) - except Exception as e: - raise ValidationError(_("Error in autoinvoicing folio: %s") % str(e)) from e - @api.constrains("journal_normal_invoice_id") def _check_journal_normal_invoice(self): for pms_property in self.filtered("journal_normal_invoice_id"): @@ -973,74 +763,42 @@ def _check_journal_simplified_invoice(self): if not pms_property.journal_simplified_invoice_id.is_simplified_invoice: pms_property.journal_simplified_invoice_id.is_simplified_invoice = True + def _get_journal(self, is_simplified_invoice, room_ids=False): + self.ensure_one() + if is_simplified_invoice: + if self.journal_simplified_invoice_id: + return self.journal_simplified_invoice_id + else: + if self.journal_normal_invoice_id: + return self.journal_normal_invoice_id + journals = self.env["account.journal"].search( + [ + ("type", "=", "sale"), + ("is_simplified_invoice", "=", is_simplified_invoice), + ("company_id", "=", self.company_id.id), + "|", + ("pms_property_ids", "in", self.id), + ("pms_property_ids", "=", False), + ] + ) + if journals: + if room_ids: + journals = journals.filtered( + lambda j: not j.room_filter_ids + or any([room_id in j.room_filter_ids.ids for room_id in room_ids]) + ) + return journals[0] + return False + @api.model def _get_folio_default_journal(self, partner_invoice_id, room_ids=False): self.ensure_one() partner = self.env["res.partner"].browse(partner_invoice_id) # For simplified invoices - if ( - self.company_id.partner_id.id == partner.id - and self.company_id.self_billed_journal_id - ): - return self.company_id.self_billed_journal_id - if ( - not partner - or partner.id == self.env.ref("pms.various_pms_partner").id - or ( - not partner._check_enought_invoice_data() - and self._context.get("autoinvoice") - ) - ): - if self.journal_simplified_invoice_id: - return self.journal_simplified_invoice_id - else: - journals = self.env["account.journal"].search( - [ - ("type", "=", "sale"), - ("is_simplified_invoice", "=", True), - ("company_id", "=", self.company_id.id), - "|", - ("pms_property_ids", "in", self.id), - ("pms_property_ids", "=", False), - ] - ) - if journals: - if room_ids: - journals = journals.filtered( - lambda j: not j.room_filter_ids - or any( - [ - room_id in j.room_filter_ids.ids - for room_id in room_ids - ] - ) - ) - return journals[0] - return False + if not partner or partner.id == self.env.ref("pms.various_pms_partner").id: + return self._get_journal(is_simplified_invoice=True, room_ids=room_ids) # For normal invoices - if self.journal_normal_invoice_id: - return self.journal_normal_invoice_id - else: - journals = self.env["account.journal"].search( - [ - ("type", "=", "sale"), - ("is_simplified_invoice", "=", False), - ("company_id", "=", self.company_id.id), - "|", - ("pms_property_ids", "in", self.id), - ("pms_property_ids", "=", False), - ] - ) - if journals: - if room_ids: - journals = journals.filtered( - lambda j: not j.room_filter_ids - or any( - [room_id in j.room_filter_ids.ids for room_id in room_ids] - ) - ) - return journals[0] - return False + return self._get_journal(is_simplified_invoice=False, room_ids=room_ids) def _get_adr(self, start_date, end_date, domain=False): """ diff --git a/pms/models/res_company.py b/pms/models/res_company.py index b140bff0b2..491e06b52f 100644 --- a/pms/models/res_company.py +++ b/pms/models/res_company.py @@ -27,22 +27,6 @@ class ResCompany(models.Model): default=False, ) - pms_invoice_downpayment_policy = fields.Selection( - selection=[ - ("no", "Manual"), - ("all", "All"), - ("checkout_past_month", "Checkout past month"), - ], - string="Downpayment policy invoce", - help=""" - - Manual: Downpayment invoice will be created manually - - All: Downpayment invoice will be created automatically - - Current Month: Downpayment invoice will be created automatically - only for reservations with checkout date past of current month - """, - default="no", - ) - document_partner_required = fields.Boolean( help="""If true, the partner document is required to create a new contact""", @@ -56,20 +40,3 @@ class ResCompany(models.Model): index=True, ondelete="restrict", ) - - self_billed_journal_id = fields.Many2one( - string="Self billed journal", - help="Journal used to create self billing", - comodel_name="account.journal", - index=True, - ondelete="restrict", - ) - self_billed_tax_ids = fields.Many2many( - string="Self billed taxes", - help="Taxes used to create self billing", - comodel_name="account.tax", - relation="company_autoinvoicing_tax_rel", - column1="company_id", - column2="tax_id", - domain="[('company_id', '=', id)]", - ) diff --git a/pms/models/res_partner.py b/pms/models/res_partner.py index 70501e8015..0b9b4bf3e7 100644 --- a/pms/models/res_partner.py +++ b/pms/models/res_partner.py @@ -179,24 +179,6 @@ class ResPartner(models.Model): string="Possible Customer In Checkin Partner", comodel_name="pms.checkin.partner", ) - invoicing_policy = fields.Selection( - help="""The invoicing policy of the partner, - set Property to user the policy configured in the Property""", - selection=[ - ("property", "Property Policy Invoice"), - ("manual", "Manual"), - ("checkout", "From Checkout"), - ("month_day", "Month Day Invoice"), - ], - default="property", - ) - invoicing_month_day = fields.Integer( - help="The day of the month to invoice", - ) - margin_days_autoinvoice = fields.Integer( - string="Days from Checkout", - help="Days from Checkout to generate the invoice", - ) residence_street = fields.Char( string="Street of residence", help="Street of the guest's residence", diff --git a/pms/static/description/index.html b/pms/static/description/index.html index 3131a40299..edc8df0bbf 100644 --- a/pms/static/description/index.html +++ b/pms/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +PMS (Property Management System) -
+
+

PMS (Property Management System)

- - -Odoo Community Association - -
-

PMS (Property Management System)

-

Beta License: AGPL-3 OCA/pms Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/pms Translate me on Weblate Try me on Runboat

This module is an all-in-one property management system (PMS) focused on medium-sized properties for managing every aspect of your property’s daily operations.

You can manage properties with multi-property and multi-company support, including your rooms inventory, @@ -395,21 +390,21 @@

PMS (Property Management System)

-

Installation

+

Installation

This module depends on modules base, mail, sale and multi_pms_properties. Ensure yourself to have all them in your addons list.

-

Configuration

+

Configuration

You will find the hotel settings in PMS Management > Configuration > Properties > Your Property.

This module required additional configuration for company, accounting, invoicing and user privileges.

-

Usage

+

Usage

To use this module, please, read the complete user guide at roomdoo.com.

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -417,15 +412,15 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Commit [Sun]
-

Contributors

+

Contributors

  • Alexandre Díaz
  • Pablo Quesada
  • @@ -442,7 +437,7 @@

    Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -455,6 +450,5 @@

Maintainers

-
diff --git a/pms/tests/test_pms_folio_invoice.py b/pms/tests/test_pms_folio_invoice.py index 558dd18fc9..8f50fa547b 100644 --- a/pms/tests/test_pms_folio_invoice.py +++ b/pms/tests/test_pms_folio_invoice.py @@ -1,6 +1,5 @@ import datetime -from odoo import fields from odoo.tests import tagged from odoo.addons.account.tests.common import AccountTestInvoicingCommon @@ -651,272 +650,6 @@ def test_qty_to_invoice_board_service(self): "The quantity of board services to be invoice is wrong", ) - def test_autoinvoice_folio_checkout_property_policy(self): - """ - Test create and invoice the cron by property preconfig automation - -------------------------------------- - Set property default_invoicing_policy to checkout with 0 days with - margin, and check that the folio autoinvoice date is set to last checkout - folio date - """ - # ARRANGE - self.property.default_invoicing_policy = "checkout" - self.property.margin_days_autoinvoice = 0 - - # ACT - self.reservation1 = self.env["pms.reservation"].create( - { - "pms_property_id": self.property.id, - "checkin": datetime.date.today(), - "checkout": datetime.date.today() + datetime.timedelta(days=3), - "adults": 2, - "room_type_id": self.room_type_double.id, - "partner_id": self.partner_id.id, - "sale_channel_origin_id": self.sale_channel_direct1.id, - } - ) - - # ASSERT - self.assertIn( - datetime.date.today() + datetime.timedelta(days=3), - self.reservation1.folio_id.mapped("sale_line_ids.autoinvoice_date"), - "The autoinvoice date in folio with property checkout policy is wrong", - ) - - def test_autoinvoice_folio_checkout_partner_policy(self): - """ - Test create and invoice the cron by partner preconfig automation - -------------------------------------- - Set partner invoicing_policy to checkout with 2 days with - margin, and check that the folio autoinvoice date is set to last checkout - folio date + 2 days - """ - # ARRANGE - self.partner_id.invoicing_policy = "checkout" - self.partner_id.margin_days_autoinvoice = 2 - - # ACT - self.reservation1 = self.env["pms.reservation"].create( - { - "pms_property_id": self.property.id, - "checkin": datetime.date.today(), - "checkout": datetime.date.today() + datetime.timedelta(days=3), - "adults": 2, - "room_type_id": self.room_type_double.id, - "partner_id": self.partner_id.id, - "sale_channel_origin_id": self.sale_channel_direct1.id, - } - ) - self.reservation1.reservation_line_ids.default_invoice_to = self.partner_id - - # ASSERT - self.assertEqual( - datetime.date.today() + datetime.timedelta(days=5), - self.reservation1.folio_id.sale_line_ids.filtered( - lambda r: r.invoice_status == "to_invoice" - )[0].autoinvoice_date, - "The autoinvoice date in folio with property checkout policy is wrong", - ) - - def test_autoinvoice_paid_folio_overnights_partner_policy(self): - """ - Test create and invoice the cron by partner preconfig automation - with partner setted as default invoiced to in reservation lines - -------------------------------------- - Set partner invoicing_policy to checkout, create a reservation - with room, board service and normal service, run autoinvoicing - method and check that only room and board service was invoiced - in partner1, the folio must be paid - - """ - # ARRANGE - self.create_configuration_accounting_scenario() - self.partner_id2 = self.env["res.partner"].create( - { - "name": "Sara", - "vat": "54235544A", - "country_id": self.env.ref("base.es").id, - "city": "Madrid", - "zip": "28013", - "street": "Street 321", - } - ) - self.partner_id.invoicing_policy = "checkout" - self.partner_id.margin_days_autoinvoice = 0 - self.product1 = self.env["product.product"].create( - { - "name": "Test Product 1", - } - ) - - self.product2 = self.env["product.product"].create( - { - "name": "Test Product 2", - "lst_price": 100, - } - ) - - self.board_service1 = self.env["pms.board.service"].create( - { - "name": "Test Board Service 1", - "default_code": "CB1", - "amount": 10, - } - ) - - self.board_service_line1 = self.env["pms.board.service.line"].create( - { - "product_id": self.product1.id, - "pms_board_service_id": self.board_service1.id, - "amount": 10, - "adults": True, - } - ) - - self.board_service_room_type1 = self.env["pms.board.service.room.type"].create( - { - "pms_room_type_id": self.demo_room_type_double.id, - "pms_board_service_id": self.board_service1.id, - "pms_property_id": self.pms_property_demo.id, - } - ) - # ACT - self.reservation1 = self.env["pms.reservation"].create( - { - "pms_property_id": self.pms_property_demo.id, - "checkin": datetime.date.today() - datetime.timedelta(days=3), - "checkout": datetime.date.today(), - "adults": 2, - "room_type_id": self.demo_room_type_double.id, - "partner_id": self.partner_id2.id, - "board_service_room_id": self.board_service_room_type1.id, - "sale_channel_origin_id": self.sale_channel_direct1.id, - } - ) - self.service = self.env["pms.service"].create( - { - "is_board_service": False, - "product_id": self.product2.id, - "reservation_id": self.reservation1.id, - } - ) - folio = self.reservation1.folio_id - reservation1 = self.reservation1 - reservation1.reservation_line_ids.default_invoice_to = self.partner_id - reservation1.service_ids.filtered( - "is_board_service" - ).default_invoice_to = self.partner_id - - folio.do_payment( - journal=self.env["account.journal"].browse( - reservation1.folio_id.pms_property_id._get_payment_methods().ids[0] - ), - receivable_account=self.env["account.journal"] - .browse(reservation1.folio_id.pms_property_id._get_payment_methods().ids[0]) - .suspense_account_id, - user=self.env.user, - amount=reservation1.folio_id.pending_amount, - folio=folio, - partner=reservation1.partner_id, - date=fields.date.today(), - ) - self.pms_property_demo.autoinvoicing() - - # ASSERT - overnight_sale_lines = self.reservation1.folio_id.sale_line_ids.filtered( - lambda line: line.reservation_line_ids or line.is_board_service - ) - partner_invoice = self.reservation1.folio_id.move_ids.filtered( - lambda inv: inv.partner_id == self.partner_id - ) - self.assertEqual( - partner_invoice.mapped("line_ids.folio_line_ids.id"), - overnight_sale_lines.ids, - "Billed services and overnights invoicing wrong compute", - ) - - def test_not_autoinvoice_unpaid_cancel_folio_partner_policy(self): - """ - Test create and invoice the cron by partner preconfig automation - -------------------------------------- - Set partner invoicing_policy to checkout, create a reservation - with room, board service and normal service, run autoinvoicing - method and check that not invoice was created becouse - the folio is cancel and not paid - """ - # ARRANGE - self.partner_id.invoicing_policy = "checkout" - self.partner_id.margin_days_autoinvoice = 0 - self.product1 = self.env["product.product"].create( - { - "name": "Test Product 1", - } - ) - - self.product2 = self.env["product.product"].create( - { - "name": "Test Product 2", - "lst_price": 100, - } - ) - - self.board_service1 = self.env["pms.board.service"].create( - { - "name": "Test Board Service 1", - "default_code": "CB1", - "amount": 10, - } - ) - - self.board_service_line1 = self.env["pms.board.service.line"].create( - { - "product_id": self.product1.id, - "pms_board_service_id": self.board_service1.id, - "amount": 10, - "adults": True, - } - ) - - self.board_service_room_type1 = self.env["pms.board.service.room.type"].create( - { - "pms_room_type_id": self.room_type_double.id, - "pms_board_service_id": self.board_service1.id, - "pms_property_id": self.property.id, - } - ) - # ACT - self.reservation1 = self.env["pms.reservation"].create( - { - "pms_property_id": self.property.id, - "checkin": datetime.date.today() - datetime.timedelta(days=3), - "checkout": datetime.date.today(), - "adults": 2, - "room_type_id": self.room_type_double.id, - "partner_id": self.partner_id.id, - "board_service_room_id": self.board_service_room_type1.id, - "sale_channel_origin_id": self.sale_channel_direct1.id, - } - ) - self.service = self.env["pms.service"].create( - { - "is_board_service": False, - "product_id": self.product2.id, - "reservation_id": self.reservation1.id, - } - ) - self.reservation1.action_cancel() - self.property.autoinvoicing() - - # ASSERT - partner_invoice = self.reservation1.folio_id.move_ids.filtered( - lambda inv: inv.partner_id == self.partner_id - ) - self.assertEqual( - partner_invoice.mapped("line_ids.folio_line_ids.id"), - [], - "Billed services and overnights invoicing wrong compute", - ) - def _test_invoice_line_group_by_room_type_sections(self): """Test create and invoice from the Folio, and check qty invoice/to invoice, and the grouped invoice lines by room type, by one diff --git a/pms/views/account_journal_views.xml b/pms/views/account_journal_views.xml index de104fd4d1..ffb84891b2 100644 --- a/pms/views/account_journal_views.xml +++ b/pms/views/account_journal_views.xml @@ -16,10 +16,6 @@ name="allowed_pms_payments" attrs="{'invisible':[('type','not in',('bank', 'cash'))]}" /> - diff --git a/pms/views/pms_property_views.xml b/pms/views/pms_property_views.xml index a572ea1c7f..dd448883f3 100644 --- a/pms/views/pms_property_views.xml +++ b/pms/views/pms_property_views.xml @@ -100,14 +100,8 @@ - - - diff --git a/pms/views/res_partner_views.xml b/pms/views/res_partner_views.xml index b7d4bd5135..f68017b004 100644 --- a/pms/views/res_partner_views.xml +++ b/pms/views/res_partner_views.xml @@ -136,27 +136,6 @@ - - view.partner.property.form - res.partner - - - - - - - - - - - - res.partner.form.data res.partner