diff --git a/pms/models/account_journal.py b/pms/models/account_journal.py
index deea22ab4..82b51e21a 100644
--- a/pms/models/account_journal.py
+++ b/pms/models/account_journal.py
@@ -20,6 +20,11 @@ class AccountJournal(models.Model):
string="For manual payments",
help="Use to pay for reservations",
)
+ avoid_autoinvoice_downpayment = fields.Boolean(
+ string="Avoid autoinvoice downpayment",
+ 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 416ea3f37..872c95e09 100644
--- a/pms/models/account_payment.py
+++ b/pms/models/account_payment.py
@@ -1,6 +1,8 @@
# Copyright 2017 Dario Lodeiros
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-from odoo import api, fields, models
+
+
+from odoo import _, api, fields, models
class AccountPayment(models.Model):
@@ -143,21 +145,118 @@ class AccountPayment(models.Model):
# if 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)
+ def action_post(self):
+ res = super(AccountPayment, self).action_post()
+ for payment in self:
+ if (
+ self.folio_ids
+ and self.company_id.pms_invoice_downpayment_policy != "no"
+ and not self.journal_id.avoid_autoinvoice_downpayment
+ ):
+ checkout_ref = max(self.folio_ids.mapped("last_checkout"))
+ if (
+ self.company_id.pms_invoice_downpayment_policy == "all"
+ and payment.date < checkout_ref
+ ) or (
+ self.company_id.pms_invoice_downpayment_policy
+ == "checkout_past_month"
+ and checkout_ref.month > payment.date.month
+ and checkout_ref.year >= payment.date.year
+ ):
+ partner_id = (
+ payment.partner_id.id
+ or self.env.ref("pms.various_pms_partner").id
+ )
+ if payment.payment_type == "inbound":
+ 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=self.folio_ids.ids,
+ return_invoices=True,
+ ).create_invoices()
+ move.action_post()
+ move_lines = move.line_ids.filtered(
+ lambda line: line.account_id.user_type_id.type
+ in ("receivable", "payable")
+ )
+ payment_lines = payment.move_id.line_ids.filtered(
+ lambda line: line.account_id == move_lines.account_id
+ )
+ if not move_lines.reconciled:
+ (payment_lines + move_lines).reconcile()
+ else:
+ # We try to revert the advance payment invoice
+ advance_invoices = self.env["account.move"].search(
+ [
+ ("folio_ids", "in", self.folio_ids.ids),
+ ("state", "=", "posted"),
+ ("payment_state", "=", "paid"),
+ ("move_type", "=", "out_invoice"),
+ ]
+ )
+ advance_invoices = advance_invoices.filtered(
+ lambda inv: any(
+ [
+ line.folio_line_ids.is_downpayment
+ for line in inv.invoice_line_ids
+ ]
+ )
+ )
+ if advance_invoices:
+ match_inv = advance_invoices.filtered(
+ lambda inv: round(inv.amount_total, 2)
+ == round(payment.amount, 2)
+ )
+ move_reversal = (
+ self.env["account.move.reversal"]
+ .with_context(
+ active_model="account.move",
+ active_ids=match_inv.ids
+ if match_inv
+ else advance_invoices.ids,
+ )
+ .create(
+ {
+ "date": fields.Date.today(),
+ "reason": _("Deposit return"),
+ "refund_method": "refund",
+ }
+ )
+ )
+ move_reversal.reverse_moves()
+ move = move_reversal.new_move_ids
+ invoice_line = move.invoice_line_ids.filtered(
+ lambda l: not l.display_type
+ )
+ move.write(
+ {
+ "invoice_line_ids": [
+ (
+ 1,
+ invoice_line.id,
+ {
+ "price_unit": payment.amount,
+ },
+ )
+ ],
+ }
+ )
+ move_lines = move.line_ids.filtered(
+ lambda line: line.account_id.user_type_id.type
+ in ("receivable", "payable")
+ )
+ payment_lines = payment.move_id.line_ids.filtered(
+ lambda line: line.account_id == move_lines.account_id
+ )
+ move.sudo().action_post()
+ if not move_lines.reconciled:
+ (payment_lines + move_lines).reconcile()
+ return res
# def modify_payment(self):
# self.ensure_one()
diff --git a/pms/models/folio_sale_line.py b/pms/models/folio_sale_line.py
index 9e3869b6c..20a8e53b8 100644
--- a/pms/models/folio_sale_line.py
+++ b/pms/models/folio_sale_line.py
@@ -719,7 +719,7 @@ class FolioSaleLine(models.Model):
Otherwise, the quantity delivered is used.
"""
for line in self:
- if line.folio_id.state not in ["draft"] and line.price_total > 0.0:
+ if line.folio_id.state not in ["draft"]:
line.qty_to_invoice = line.product_uom_qty - line.qty_invoiced
else:
line.qty_to_invoice = 0
@@ -996,13 +996,6 @@ class FolioSaleLine(models.Model):
should be added to the returned invoice line
"""
self.ensure_one()
- if (qty > self.qty_to_invoice or qty <= 0) and not self.display_type:
- raise ValueError(
- _(
- "The qty (%s) is wrong." % qty
- + " The quantity pending to invoice is %s" % self.qty_to_invoice
- )
- )
res = {
"display_type": self.display_type,
"sequence": self.sequence,
diff --git a/pms/models/pms_folio.py b/pms/models/pms_folio.py
index 7a2166319..cf3894a62 100644
--- a/pms/models/pms_folio.py
+++ b/pms/models/pms_folio.py
@@ -2032,7 +2032,7 @@ class PmsFolio(models.Model):
(making sure to call super() to establish a clean extension chain).
"""
self.ensure_one()
- journal = self._get_folio_default_journal(partner_invoice_id)
+ journal = self.pms_property_id._get_folio_default_journal(partner_invoice_id)
if not journal:
journal = (
self.env["account.move"]
@@ -2074,17 +2074,6 @@ class PmsFolio(models.Model):
}
return invoice_vals
- def _get_folio_default_journal(self, partner_invoice_id):
- self.ensure_one()
- pms_property = self.pms_property_id
- partner = self.env["res.partner"].browse(partner_invoice_id)
- if not partner or (
- not partner._check_enought_invoice_data()
- and self._context.get("autoinvoice")
- ):
- return pms_property.journal_simplified_invoice_id
- return pms_property.journal_normal_invoice_id
-
def do_payment(
self,
journal,
diff --git a/pms/models/pms_property.py b/pms/models/pms_property.py
index 024377011..61d94fa43 100644
--- a/pms/models/pms_property.py
+++ b/pms/models/pms_property.py
@@ -701,6 +701,21 @@ class PmsProperty(models.Model):
if not pms_property.journal_simplified_invoice_id.is_simplified_invoice:
pms_property.journal_simplified_invoice_id.is_simplified_invoice = True
+ @api.model
+ def _get_folio_default_journal(self, partner_invoice_id):
+ self.ensure_one()
+ partner = self.env["res.partner"].browse(partner_invoice_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")
+ )
+ ):
+ return self.journal_simplified_invoice_id
+ return self.journal_normal_invoice_id
+
def _get_adr(self, start_date, end_date, domain=False):
"""
Calculate monthly ADR for a property
diff --git a/pms/models/res_company.py b/pms/models/res_company.py
index f44d9effa..d047ff05e 100644
--- a/pms/models/res_company.py
+++ b/pms/models/res_company.py
@@ -27,3 +27,19 @@ class ResCompany(models.Model):
- VAT, name, street, city, country""",
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",
+ )
diff --git a/pms/tests/test_pms_folio_invoice.py b/pms/tests/test_pms_folio_invoice.py
index bed69c713..a46a8fcb4 100644
--- a/pms/tests/test_pms_folio_invoice.py
+++ b/pms/tests/test_pms_folio_invoice.py
@@ -253,42 +253,6 @@ class TestPmsFolioInvoice(TestPms):
"The status after an invoicing is not correct",
)
- def test_invoice_partial_folio_wrong_qtys(self):
- """
- Check that an invoice of a folio with wrong amounts cannot be created.
- ------------
- A reservation is created. Then the create_invoices method of the folio
- is launched with the lines to be invoiced with wrong amounts and through
- subtest it is verified that the invoices cannot be created.
- """
- # ARRANGE
- r1 = self.env["pms.reservation"].create(
- {
- "pms_property_id": self.property.id,
- "checkin": datetime.datetime.now(),
- "checkout": datetime.datetime.now() + datetime.timedelta(days=2),
- "adults": 2,
- "room_type_id": self.room_type_double.id,
- "partner_id": self.env.ref("base.res_partner_12").id,
- "sale_channel_origin_id": self.sale_channel_direct1.id,
- }
- )
- tcs = [-1, 0, 3]
-
- for tc in tcs:
- with self.subTest(k=tc):
- with self.assertRaises(ValueError):
- # ARRANGE
- dict_lines = dict()
- dict_lines[
- r1.folio_id.sale_line_ids.filtered(
- lambda l: not l.display_type
- )[0].id
- ] = tc
- r1.folio_id._create_invoices(lines_to_invoice=dict_lines)
- # test does not work without invalidating cache
- self.env["account.move"].invalidate_cache()
-
def test_amount_invoice_folio(self):
"""
Test create and invoice from the Folio, and check amount of the reservation.
diff --git a/pms/views/account_journal_views.xml b/pms/views/account_journal_views.xml
index d63420eff..5bc5fa053 100644
--- a/pms/views/account_journal_views.xml
+++ b/pms/views/account_journal_views.xml
@@ -6,7 +6,14 @@
-
+
+
diff --git a/pms/views/res_company_views.xml b/pms/views/res_company_views.xml
index c69bc150e..5d5d688ad 100644
--- a/pms/views/res_company_views.xml
+++ b/pms/views/res_company_views.xml
@@ -16,6 +16,7 @@
+
diff --git a/pms/wizards/folio_make_invoice_advance.py b/pms/wizards/folio_make_invoice_advance.py
index 23ecb4b24..b04f8be1f 100644
--- a/pms/wizards/folio_make_invoice_advance.py
+++ b/pms/wizards/folio_make_invoice_advance.py
@@ -152,15 +152,21 @@ class FolioAdvancePaymentInv(models.TransientModel):
return {}
def _prepare_invoice_values(self, order, name, amount, line):
+ partner_id = (
+ self.partner_invoice_id.id
+ if self.partner_invoice_id
+ else order.partner_invoice_id.id
+ )
invoice_vals = {
"ref": order.name,
"move_type": "out_invoice",
+ "journal_id": order.pms_property_id._get_folio_default_journal(
+ partner_id
+ ).id,
"invoice_origin": order.name,
"invoice_user_id": order.user_id.id,
"narration": order.note,
- "partner_id": self.partner_invoice_id
- if self.partner_invoice_id
- else order.partner_invoice_id.id,
+ "partner_id": partner_id,
"currency_id": order.pricelist_id.currency_id.id,
"folio_ids": [(6, 0, order.ids)],
"payment_reference": order.reference,
@@ -199,7 +205,6 @@ class FolioAdvancePaymentInv(models.TransientModel):
amount, name = self._get_advance_details(order)
invoice_vals = self._prepare_invoice_values(order, name, amount, line)
-
if order.fiscal_position_id:
invoice_vals["fiscal_position_id"] = order.fiscal_position_id.id
invoice = (
@@ -258,6 +263,7 @@ class FolioAdvancePaymentInv(models.TransientModel):
)
sale_line_obj = self.env["folio.sale.line"]
+ invoices = self.env["account.move"]
for order in folios:
amount, name = self._get_advance_details(order)
@@ -293,9 +299,11 @@ class FolioAdvancePaymentInv(models.TransientModel):
order, analytic_tag_ids, tax_ids, amount
)
line = sale_line_obj.sudo().create(line_values)
- self._create_invoice(order, line, amount)
+ invoices += self._create_invoice(order, line, amount)
if self._context.get("open_invoices", False):
return folios.action_view_invoice()
+ if self._context.get("return_invoices", False):
+ return invoices
return {"type": "ir.actions.act_window_close"}
def _prepare_deposit_product(self):