mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[IMP]pms: Improvemente relation amount pending folio and payments with invoices linked
This commit is contained in:
@@ -9,7 +9,6 @@ class AccountPayment(models.Model):
|
|||||||
folio_ids = fields.Many2many(
|
folio_ids = fields.Many2many(
|
||||||
string="Folios",
|
string="Folios",
|
||||||
comodel_name="pms.folio",
|
comodel_name="pms.folio",
|
||||||
ondelete="cascade",
|
|
||||||
compute="_compute_folio_ids",
|
compute="_compute_folio_ids",
|
||||||
store=True,
|
store=True,
|
||||||
readonly=False,
|
readonly=False,
|
||||||
@@ -68,15 +67,15 @@ class AccountPayment(models.Model):
|
|||||||
"line_ids.folio_line_ids.folio_id.id"
|
"line_ids.folio_line_ids.folio_id.id"
|
||||||
)
|
)
|
||||||
folio_ids = list(set(inv_folio_ids + bill_folio_ids))
|
folio_ids = list(set(inv_folio_ids + bill_folio_ids))
|
||||||
if folio_ids:
|
# If the payment was already assigned to a specific page of the invoice,
|
||||||
|
# we do not want it to be associated with others
|
||||||
|
if folio_ids and len(set(rec.folio_ids.ids) & set(folio_ids)) == 0:
|
||||||
|
folios = self.env["pms.folio"].browse(folio_ids)
|
||||||
|
# If the payment is in a new invoice, we want it to be associated with all
|
||||||
|
# folios of the invoice that don't are paid yet
|
||||||
|
folio_ids = folios.filtered(lambda f: f.pending_amount > 0).ids
|
||||||
rec.write({"folio_ids": [(6, 0, folio_ids)]})
|
rec.write({"folio_ids": [(6, 0, folio_ids)]})
|
||||||
elif (
|
elif not rec.folio_ids:
|
||||||
not rec.reconciled_invoice_ids
|
|
||||||
and not rec.reconciled_bill_ids
|
|
||||||
and rec.folio_ids
|
|
||||||
):
|
|
||||||
rec.folio_ids = rec.folio_ids
|
|
||||||
else:
|
|
||||||
rec.folio_ids = False
|
rec.folio_ids = False
|
||||||
|
|
||||||
def _prepare_move_line_default_vals(self, write_off_line_vals=None):
|
def _prepare_move_line_default_vals(self, write_off_line_vals=None):
|
||||||
|
|||||||
@@ -265,6 +265,8 @@ class PmsFolio(models.Model):
|
|||||||
("not_paid", "Not Paid"),
|
("not_paid", "Not Paid"),
|
||||||
("paid", "Paid"),
|
("paid", "Paid"),
|
||||||
("partial", "Partially Paid"),
|
("partial", "Partially Paid"),
|
||||||
|
("overpayment", "Overpayment"),
|
||||||
|
("nothing_to_pay", "Nothing to pay"),
|
||||||
],
|
],
|
||||||
compute="_compute_amount",
|
compute="_compute_amount",
|
||||||
tracking=True,
|
tracking=True,
|
||||||
@@ -400,6 +402,13 @@ class PmsFolio(models.Model):
|
|||||||
compute="_compute_amount",
|
compute="_compute_amount",
|
||||||
tracking=True,
|
tracking=True,
|
||||||
)
|
)
|
||||||
|
payment_multi = fields.Boolean(
|
||||||
|
string="Folio paid with payments assigned to other folios",
|
||||||
|
help="Technical field for manage payments with multiple folios assigned",
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_amount",
|
||||||
|
)
|
||||||
amount_untaxed = fields.Monetary(
|
amount_untaxed = fields.Monetary(
|
||||||
string="Untaxed Amount",
|
string="Untaxed Amount",
|
||||||
help="The price without taxes on a folio",
|
help="The price without taxes on a folio",
|
||||||
@@ -1103,72 +1112,126 @@ class PmsFolio(models.Model):
|
|||||||
"payment_ids.move_id.line_ids.credit",
|
"payment_ids.move_id.line_ids.credit",
|
||||||
"payment_ids.move_id.line_ids.currency_id",
|
"payment_ids.move_id.line_ids.currency_id",
|
||||||
"payment_ids.move_id.line_ids.amount_currency",
|
"payment_ids.move_id.line_ids.amount_currency",
|
||||||
|
"move_ids.amount_residual",
|
||||||
)
|
)
|
||||||
def _compute_amount(self):
|
def _compute_amount(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
if record.reservation_type in ("staff", "out"):
|
if record.reservation_type in ("staff", "out"):
|
||||||
record.amount_total = 0
|
record.amount_total = 0
|
||||||
vals = {
|
vals = {
|
||||||
"payment_state": False,
|
"payment_state": "nothing_to_pay",
|
||||||
"pending_amount": 0,
|
"pending_amount": 0,
|
||||||
"invoices_paid": 0,
|
"invoices_paid": 0,
|
||||||
}
|
}
|
||||||
record.update(vals)
|
record.update(vals)
|
||||||
else:
|
else:
|
||||||
mls = record.payment_ids.mapped("move_id.line_ids").filtered(
|
# first attempt compute amount search payments refs with only one folio
|
||||||
|
mls_one_folio = (
|
||||||
|
record.payment_ids.filtered(lambda pay: len(pay.folio_ids) == 1)
|
||||||
|
.mapped("move_id.line_ids")
|
||||||
|
.filtered(
|
||||||
|
lambda x: x.account_id.internal_type == "receivable"
|
||||||
|
and x.parent_state == "posted"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
advance_amount = record._get_advance_amount(mls_one_folio)
|
||||||
|
# Compute 'payment_state'.
|
||||||
|
vals = record._get_amount_vals(mls_one_folio, advance_amount)
|
||||||
|
# If folio its not paid, search payments refs with more than one folio
|
||||||
|
folio_ids = record.payment_ids.mapped("folio_ids.id")
|
||||||
|
if vals["pending_amount"] > 0 and len(folio_ids) > 1:
|
||||||
|
folios = self.env["pms.folio"].browse(folio_ids)
|
||||||
|
mls_multi_folio = folios.payment_ids.mapped(
|
||||||
|
"move_id.line_ids"
|
||||||
|
).filtered(
|
||||||
|
lambda x: x.account_id.internal_type == "receivable"
|
||||||
|
and x.parent_state == "posted"
|
||||||
|
)
|
||||||
|
if mls_multi_folio:
|
||||||
|
advance_amount = record._get_advance_amount(mls_multi_folio)
|
||||||
|
vals = record._get_amount_vals(
|
||||||
|
mls_multi_folio, advance_amount, folio_ids
|
||||||
|
)
|
||||||
|
|
||||||
|
record.update(vals)
|
||||||
|
|
||||||
|
def _get_advance_amount(self, mls):
|
||||||
|
self.ensure_one()
|
||||||
|
advance_amount = 0.0
|
||||||
|
for line in mls:
|
||||||
|
line_currency = line.currency_id or line.company_id.currency_id
|
||||||
|
line_amount = line.amount_currency if line.currency_id else line.balance
|
||||||
|
line_amount *= -1
|
||||||
|
if line_currency != self.currency_id:
|
||||||
|
advance_amount += line.currency_id._convert(
|
||||||
|
line_amount,
|
||||||
|
self.currency_id,
|
||||||
|
self.company_id,
|
||||||
|
line.date or fields.Date.today(),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
advance_amount += line_amount
|
||||||
|
return advance_amount
|
||||||
|
|
||||||
|
def _get_amount_vals(self, mls, advance_amount, folio_ids=False):
|
||||||
|
self.ensure_one()
|
||||||
|
folios = self
|
||||||
|
if folio_ids:
|
||||||
|
folios = self.env["pms.folio"].browse(folio_ids)
|
||||||
|
mls_one_folio = (
|
||||||
|
self.payment_ids.filtered(lambda pay: len(pay.folio_ids) == 1)
|
||||||
|
.mapped("move_id.line_ids")
|
||||||
|
.filtered(
|
||||||
lambda x: x.account_id.internal_type == "receivable"
|
lambda x: x.account_id.internal_type == "receivable"
|
||||||
and x.parent_state == "posted"
|
and x.parent_state == "posted"
|
||||||
)
|
)
|
||||||
advance_amount = 0.0
|
)
|
||||||
for line in mls:
|
amount_folio_residual = self.amount_total - self._get_advance_amount(
|
||||||
line_currency = line.currency_id or line.company_id.currency_id
|
mls_one_folio
|
||||||
line_amount = (
|
)
|
||||||
line.amount_currency if line.currency_id else line.balance
|
amount_total_residual = sum(folios.mapped("amount_total")) - advance_amount
|
||||||
)
|
else:
|
||||||
line_amount *= -1
|
amount_folio_residual = amount_total_residual = (
|
||||||
if line_currency != record.currency_id:
|
sum(folios.mapped("amount_total")) - advance_amount
|
||||||
advance_amount += line.currency_id._convert(
|
)
|
||||||
line_amount,
|
total = sum(folios.mapped("amount_total"))
|
||||||
record.currency_id,
|
|
||||||
record.company_id,
|
|
||||||
line.date or fields.Date.today(),
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
advance_amount += line_amount
|
|
||||||
amount_residual = record.amount_total - advance_amount
|
|
||||||
|
|
||||||
total = record.amount_total
|
# REVIEW: Must We ignored services in cancelled folios
|
||||||
# REVIEW: Must We ignored services in cancelled folios
|
# pending amount?
|
||||||
# pending amount?
|
for folio in folios:
|
||||||
if record.state == "cancel":
|
if folio.state == "cancel":
|
||||||
total = total - sum(record.service_ids.mapped("price_total"))
|
total = total - sum(folio.service_ids.mapped("price_total"))
|
||||||
# Compute 'payment_state'.
|
payment_state = "not_paid"
|
||||||
payment_state = "not_paid"
|
if (
|
||||||
if (
|
mls
|
||||||
mls
|
and float_compare(
|
||||||
and float_compare(
|
amount_total_residual,
|
||||||
amount_residual,
|
total,
|
||||||
total,
|
precision_rounding=self.currency_id.rounding,
|
||||||
precision_rounding=record.currency_id.rounding,
|
)
|
||||||
)
|
!= 0
|
||||||
!= 0
|
):
|
||||||
):
|
has_due_amount = float_compare(
|
||||||
has_due_amount = float_compare(
|
amount_total_residual,
|
||||||
amount_residual,
|
0.0,
|
||||||
0.0,
|
precision_rounding=self.currency_id.rounding,
|
||||||
precision_rounding=record.currency_id.rounding,
|
)
|
||||||
)
|
if has_due_amount == 0:
|
||||||
if has_due_amount <= 0:
|
payment_state = "paid"
|
||||||
payment_state = "paid"
|
elif has_due_amount > 0:
|
||||||
elif has_due_amount > 0:
|
payment_state = "partial"
|
||||||
payment_state = "partial"
|
elif has_due_amount < 0:
|
||||||
|
payment_state = "overpayment"
|
||||||
|
elif total == 0:
|
||||||
|
payment_state = "nothing_to_pay"
|
||||||
|
|
||||||
vals = {
|
vals = {
|
||||||
"pending_amount": amount_residual,
|
"payment_multi": len(folios) > 1,
|
||||||
"invoices_paid": advance_amount,
|
"pending_amount": min(amount_total_residual, amount_folio_residual),
|
||||||
"payment_state": payment_state,
|
"invoices_paid": advance_amount,
|
||||||
}
|
"payment_state": payment_state,
|
||||||
record.update(vals)
|
}
|
||||||
|
return vals
|
||||||
|
|
||||||
@api.depends("reservation_ids", "reservation_ids.priority")
|
@api.depends("reservation_ids", "reservation_ids.priority")
|
||||||
def _compute_max_reservation_priority(self):
|
def _compute_max_reservation_priority(self):
|
||||||
@@ -1236,12 +1299,11 @@ class PmsFolio(models.Model):
|
|||||||
if operator == "in" and value:
|
if operator == "in" and value:
|
||||||
self.env.cr.execute(
|
self.env.cr.execute(
|
||||||
"""
|
"""
|
||||||
SELECT array_agg(so.id)
|
SELECT array_agg(fo.id)
|
||||||
FROM pms_folio so
|
FROM pms_folio fo
|
||||||
JOIN folio_sale_line sol ON sol.folio_id = so.id
|
JOIN folio_sale_line fol ON fol.folio_id = fo.id
|
||||||
JOIN folio_sale_line_invoice_rel soli_rel ON \
|
JOIN folio_sale_line_invoice_rel foli_rel ON foli_rel.sale_line_id = fol.id
|
||||||
soli_rel.sale_line_ids = sol.id
|
JOIN account_move_line aml ON aml.id = foli_rel.invoice_line_id
|
||||||
JOIN account_move_line aml ON aml.id = soli_rel.invoice_line_id
|
|
||||||
JOIN account_move am ON am.id = aml.move_id
|
JOIN account_move am ON am.id = aml.move_id
|
||||||
WHERE
|
WHERE
|
||||||
am.move_type in ('out_invoice', 'out_refund', 'in_receipt') AND
|
am.move_type in ('out_invoice', 'out_refund', 'in_receipt') AND
|
||||||
@@ -1811,7 +1873,6 @@ class PmsFolio(models.Model):
|
|||||||
"invoice_origin": self.name,
|
"invoice_origin": self.name,
|
||||||
"invoice_payment_term_id": self.payment_term_id.id,
|
"invoice_payment_term_id": self.payment_term_id.id,
|
||||||
"transaction_ids": [(6, 0, self.transaction_ids.ids)],
|
"transaction_ids": [(6, 0, self.transaction_ids.ids)],
|
||||||
"folio_ids": [(6, 0, [self.id])],
|
|
||||||
"invoice_line_ids": [],
|
"invoice_line_ids": [],
|
||||||
"company_id": self.company_id.id,
|
"company_id": self.company_id.id,
|
||||||
"payment_reference": self.external_reference or self.reference,
|
"payment_reference": self.external_reference or self.reference,
|
||||||
|
|||||||
@@ -157,13 +157,13 @@
|
|||||||
<i class="fa fa-fw fa-arrow-circle-right" /> Pay Now
|
<i class="fa fa-fw fa-arrow-circle-right" /> Pay Now
|
||||||
</a>
|
</a>
|
||||||
<div
|
<div
|
||||||
t-if="tx_ids and not pending_manual_txs and folio.payment_state not in ('paid', 'in_payment')"
|
t-if="tx_ids and not pending_manual_txs and folio.payment_state in ('not_paid', 'partial')"
|
||||||
class="alert alert-info py-1 mb-2"
|
class="alert alert-info py-1 mb-2"
|
||||||
>
|
>
|
||||||
<i class="fa fa-fw fa-check-circle" /> Pending
|
<i class="fa fa-fw fa-check-circle" /> Pending
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
t-if="folio.payment_state in ('paid', 'in_payment')"
|
t-if="folio.payment_state in ('paid', 'overpayment')"
|
||||||
class="alert alert-success py-1 mb-2"
|
class="alert alert-success py-1 mb-2"
|
||||||
>
|
>
|
||||||
<i class="fa fa-fw fa-check-circle" /> Paid
|
<i class="fa fa-fw fa-check-circle" /> Paid
|
||||||
|
|||||||
@@ -55,6 +55,17 @@
|
|||||||
/>
|
/>
|
||||||
</bold>
|
</bold>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="alert alert-warning"
|
||||||
|
role="alert"
|
||||||
|
style="margin-bottom:0px;"
|
||||||
|
attrs="{'invisible': [('payment_multi','=',False)]}"
|
||||||
|
>
|
||||||
|
This folio has payments assigned to multiple folios (through an invoice or directly).
|
||||||
|
The Folio will only be considered paid if all the folios with the same associated
|
||||||
|
payment are paid, or if a specific payment is assigned to this folio.
|
||||||
|
<field name="payment_multi" invisible="1" />
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
class="alert alert-warning"
|
class="alert alert-warning"
|
||||||
role="alert"
|
role="alert"
|
||||||
@@ -209,6 +220,12 @@
|
|||||||
bg_color="bg-warning"
|
bg_color="bg-warning"
|
||||||
attrs="{'invisible': [('payment_state', '!=', 'partial')]}"
|
attrs="{'invisible': [('payment_state', '!=', 'partial')]}"
|
||||||
/>
|
/>
|
||||||
|
<widget
|
||||||
|
name="web_ribbon"
|
||||||
|
title="Overpayment!"
|
||||||
|
bg_color="bg-info"
|
||||||
|
attrs="{'invisible': [('payment_state', '!=', 'overpayment')]}"
|
||||||
|
/>
|
||||||
<widget
|
<widget
|
||||||
name="web_ribbon"
|
name="web_ribbon"
|
||||||
title="Staff"
|
title="Staff"
|
||||||
@@ -549,7 +566,13 @@
|
|||||||
<field name="payment_type" />
|
<field name="payment_type" />
|
||||||
<field name="amount" />
|
<field name="amount" />
|
||||||
<field name="partner_id" invisible="1" />
|
<field name="partner_id" invisible="1" />
|
||||||
<field name="state" invisible="1" />
|
<field
|
||||||
|
name="state"
|
||||||
|
decoration-success="state == 'posted'"
|
||||||
|
decoration-danger="state == 'cancel'"
|
||||||
|
decoration-warning="state == 'draft'"
|
||||||
|
widget="badge"
|
||||||
|
/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
<!-- REVIEW: transations has a payment related -->
|
<!-- REVIEW: transations has a payment related -->
|
||||||
@@ -659,9 +682,9 @@
|
|||||||
/>
|
/>
|
||||||
<field
|
<field
|
||||||
name="payment_state"
|
name="payment_state"
|
||||||
decoration-success="payment_state == 'paid'"
|
decoration-success="payment_state in ['paid', 'nothing_to_pay']"
|
||||||
decoration-danger="payment_state == 'not_paid' and state in ['cancel','onboard','done','arrival_delayed','departure_delayed']"
|
decoration-danger="payment_state == 'not_paid'"
|
||||||
decoration-info="payment_state == 'not_paid' and state in ['draft','confirm',]"
|
decoration-info="payment_state == 'overpayment'"
|
||||||
decoration-warning="payment_state == 'partial'"
|
decoration-warning="payment_state == 'partial'"
|
||||||
widget="badge"
|
widget="badge"
|
||||||
optional="show"
|
optional="show"
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ class FolioAdvancePaymentInv(models.TransientModel):
|
|||||||
if self.partner_invoice_id
|
if self.partner_invoice_id
|
||||||
else order.partner_invoice_id.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)],
|
||||||
"payment_reference": order.reference,
|
"payment_reference": order.reference,
|
||||||
"invoice_payment_term_id": order.payment_term_id.id,
|
"invoice_payment_term_id": order.payment_term_id.id,
|
||||||
"partner_bank_id": order.company_id.partner_id.bank_ids[:1].id,
|
"partner_bank_id": order.company_id.partner_id.bank_ids[:1].id,
|
||||||
|
|||||||
Reference in New Issue
Block a user