mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[ADD]pms_api_rest: autoinvoice downpayments policy
This commit is contained in:
@@ -20,6 +20,11 @@ class AccountJournal(models.Model):
|
|||||||
string="For manual payments",
|
string="For manual payments",
|
||||||
help="Use to pay for reservations",
|
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(
|
is_simplified_invoice = fields.Boolean(
|
||||||
string="Simplified invoice",
|
string="Simplified invoice",
|
||||||
help="Use to simplified invoice",
|
help="Use to simplified invoice",
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
# Copyright 2017 Dario Lodeiros
|
# Copyright 2017 Dario Lodeiros
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# 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):
|
class AccountPayment(models.Model):
|
||||||
@@ -143,21 +145,118 @@ class AccountPayment(models.Model):
|
|||||||
# if msg:
|
# 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):
|
def action_post(self):
|
||||||
# rec = super(AccountPayment, self).post()
|
res = super(AccountPayment, self).action_post()
|
||||||
# if rec and not self._context.get("ignore_notification_post", False):
|
for payment in self:
|
||||||
# for pay in self:
|
if (
|
||||||
# if pay.folio_id:
|
self.folio_ids
|
||||||
# msg = _(
|
and self.company_id.pms_invoice_downpayment_policy != "no"
|
||||||
# "Payment of %s %s registered from %s \
|
and not self.journal_id.avoid_autoinvoice_downpayment
|
||||||
# using %s payment method"
|
):
|
||||||
# ) % (
|
checkout_ref = max(self.folio_ids.mapped("last_checkout"))
|
||||||
# pay.amount,
|
if (
|
||||||
# pay.currency_id.symbol,
|
self.company_id.pms_invoice_downpayment_policy == "all"
|
||||||
# pay.communication,
|
and payment.date < checkout_ref
|
||||||
# pay.journal_id.name,
|
) or (
|
||||||
# )
|
self.company_id.pms_invoice_downpayment_policy
|
||||||
# pay.folio_id.message_post(subject=_("Payment"), body=msg)
|
== "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):
|
# def modify_payment(self):
|
||||||
# self.ensure_one()
|
# self.ensure_one()
|
||||||
|
|||||||
@@ -719,7 +719,7 @@ class FolioSaleLine(models.Model):
|
|||||||
Otherwise, the quantity delivered is used.
|
Otherwise, the quantity delivered is used.
|
||||||
"""
|
"""
|
||||||
for line in self:
|
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
|
line.qty_to_invoice = line.product_uom_qty - line.qty_invoiced
|
||||||
else:
|
else:
|
||||||
line.qty_to_invoice = 0
|
line.qty_to_invoice = 0
|
||||||
@@ -996,13 +996,6 @@ class FolioSaleLine(models.Model):
|
|||||||
should be added to the returned invoice line
|
should be added to the returned invoice line
|
||||||
"""
|
"""
|
||||||
self.ensure_one()
|
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 = {
|
res = {
|
||||||
"display_type": self.display_type,
|
"display_type": self.display_type,
|
||||||
"sequence": self.sequence,
|
"sequence": self.sequence,
|
||||||
|
|||||||
@@ -2032,7 +2032,7 @@ class PmsFolio(models.Model):
|
|||||||
(making sure to call super() to establish a clean extension chain).
|
(making sure to call super() to establish a clean extension chain).
|
||||||
"""
|
"""
|
||||||
self.ensure_one()
|
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:
|
if not journal:
|
||||||
journal = (
|
journal = (
|
||||||
self.env["account.move"]
|
self.env["account.move"]
|
||||||
@@ -2074,17 +2074,6 @@ class PmsFolio(models.Model):
|
|||||||
}
|
}
|
||||||
return invoice_vals
|
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(
|
def do_payment(
|
||||||
self,
|
self,
|
||||||
journal,
|
journal,
|
||||||
|
|||||||
@@ -701,6 +701,21 @@ class PmsProperty(models.Model):
|
|||||||
if not pms_property.journal_simplified_invoice_id.is_simplified_invoice:
|
if not pms_property.journal_simplified_invoice_id.is_simplified_invoice:
|
||||||
pms_property.journal_simplified_invoice_id.is_simplified_invoice = True
|
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):
|
def _get_adr(self, start_date, end_date, domain=False):
|
||||||
"""
|
"""
|
||||||
Calculate monthly ADR for a property
|
Calculate monthly ADR for a property
|
||||||
|
|||||||
@@ -27,3 +27,19 @@ class ResCompany(models.Model):
|
|||||||
- VAT, name, street, city, country""",
|
- VAT, name, street, city, country""",
|
||||||
default=False,
|
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",
|
||||||
|
)
|
||||||
|
|||||||
@@ -253,42 +253,6 @@ class TestPmsFolioInvoice(TestPms):
|
|||||||
"The status after an invoicing is not correct",
|
"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):
|
def test_amount_invoice_folio(self):
|
||||||
"""
|
"""
|
||||||
Test create and invoice from the Folio, and check amount of the reservation.
|
Test create and invoice from the Folio, and check amount of the reservation.
|
||||||
|
|||||||
@@ -6,7 +6,14 @@
|
|||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//field[@name='company_id']" position="after">
|
<xpath expr="//field[@name='company_id']" position="after">
|
||||||
<field name="pms_property_ids" widget="many2many_tags" />
|
<field name="pms_property_ids" widget="many2many_tags" />
|
||||||
<field name="allowed_pms_payments" />
|
<field
|
||||||
|
name="allowed_pms_payments"
|
||||||
|
attrs="{'invisible':[('type','not in',('bank', 'cash'))]}"
|
||||||
|
/>
|
||||||
|
<field
|
||||||
|
name="avoid_autoinvoice_downpayment"
|
||||||
|
attrs="{'invisible':[('type','not in',('bank', 'cash'))]}"
|
||||||
|
/>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='vat']" position="after">
|
<xpath expr="//field[@name='vat']" position="after">
|
||||||
<field name="check_min_partner_data_invoice" />
|
<field name="check_min_partner_data_invoice" />
|
||||||
|
<field name="pms_invoice_downpayment_policy" />
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
@@ -152,15 +152,21 @@ class FolioAdvancePaymentInv(models.TransientModel):
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
def _prepare_invoice_values(self, order, name, amount, line):
|
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 = {
|
invoice_vals = {
|
||||||
"ref": order.name,
|
"ref": order.name,
|
||||||
"move_type": "out_invoice",
|
"move_type": "out_invoice",
|
||||||
|
"journal_id": order.pms_property_id._get_folio_default_journal(
|
||||||
|
partner_id
|
||||||
|
).id,
|
||||||
"invoice_origin": order.name,
|
"invoice_origin": order.name,
|
||||||
"invoice_user_id": order.user_id.id,
|
"invoice_user_id": order.user_id.id,
|
||||||
"narration": order.note,
|
"narration": order.note,
|
||||||
"partner_id": self.partner_invoice_id
|
"partner_id": partner_id,
|
||||||
if self.partner_invoice_id
|
|
||||||
else order.partner_invoice_id.id,
|
|
||||||
"currency_id": order.pricelist_id.currency_id.id,
|
"currency_id": order.pricelist_id.currency_id.id,
|
||||||
"folio_ids": [(6, 0, order.ids)],
|
"folio_ids": [(6, 0, order.ids)],
|
||||||
"payment_reference": order.reference,
|
"payment_reference": order.reference,
|
||||||
@@ -199,7 +205,6 @@ class FolioAdvancePaymentInv(models.TransientModel):
|
|||||||
amount, name = self._get_advance_details(order)
|
amount, name = self._get_advance_details(order)
|
||||||
|
|
||||||
invoice_vals = self._prepare_invoice_values(order, name, amount, line)
|
invoice_vals = self._prepare_invoice_values(order, name, amount, line)
|
||||||
|
|
||||||
if order.fiscal_position_id:
|
if order.fiscal_position_id:
|
||||||
invoice_vals["fiscal_position_id"] = order.fiscal_position_id.id
|
invoice_vals["fiscal_position_id"] = order.fiscal_position_id.id
|
||||||
invoice = (
|
invoice = (
|
||||||
@@ -258,6 +263,7 @@ class FolioAdvancePaymentInv(models.TransientModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
sale_line_obj = self.env["folio.sale.line"]
|
sale_line_obj = self.env["folio.sale.line"]
|
||||||
|
invoices = self.env["account.move"]
|
||||||
for order in folios:
|
for order in folios:
|
||||||
amount, name = self._get_advance_details(order)
|
amount, name = self._get_advance_details(order)
|
||||||
|
|
||||||
@@ -293,9 +299,11 @@ class FolioAdvancePaymentInv(models.TransientModel):
|
|||||||
order, analytic_tag_ids, tax_ids, amount
|
order, analytic_tag_ids, tax_ids, amount
|
||||||
)
|
)
|
||||||
line = sale_line_obj.sudo().create(line_values)
|
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):
|
if self._context.get("open_invoices", False):
|
||||||
return folios.action_view_invoice()
|
return folios.action_view_invoice()
|
||||||
|
if self._context.get("return_invoices", False):
|
||||||
|
return invoices
|
||||||
return {"type": "ir.actions.act_window_close"}
|
return {"type": "ir.actions.act_window_close"}
|
||||||
|
|
||||||
def _prepare_deposit_product(self):
|
def _prepare_deposit_product(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user