mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
@@ -9,7 +9,6 @@ class AccountPayment(models.Model):
|
||||
folio_ids = fields.Many2many(
|
||||
string="Folios",
|
||||
comodel_name="pms.folio",
|
||||
ondelete="cascade",
|
||||
compute="_compute_folio_ids",
|
||||
store=True,
|
||||
readonly=False,
|
||||
@@ -68,15 +67,15 @@ class AccountPayment(models.Model):
|
||||
"line_ids.folio_line_ids.folio_id.id"
|
||||
)
|
||||
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)]})
|
||||
elif (
|
||||
not rec.reconciled_invoice_ids
|
||||
and not rec.reconciled_bill_ids
|
||||
and rec.folio_ids
|
||||
):
|
||||
rec.folio_ids = rec.folio_ids
|
||||
else:
|
||||
elif not rec.folio_ids:
|
||||
rec.folio_ids = False
|
||||
|
||||
def _prepare_move_line_default_vals(self, write_off_line_vals=None):
|
||||
|
||||
@@ -265,6 +265,8 @@ class PmsFolio(models.Model):
|
||||
("not_paid", "Not Paid"),
|
||||
("paid", "Paid"),
|
||||
("partial", "Partially Paid"),
|
||||
("overpayment", "Overpayment"),
|
||||
("nothing_to_pay", "Nothing to pay"),
|
||||
],
|
||||
compute="_compute_amount",
|
||||
tracking=True,
|
||||
@@ -400,6 +402,13 @@ class PmsFolio(models.Model):
|
||||
compute="_compute_amount",
|
||||
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(
|
||||
string="Untaxed Amount",
|
||||
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.currency_id",
|
||||
"payment_ids.move_id.line_ids.amount_currency",
|
||||
"move_ids.amount_residual",
|
||||
)
|
||||
def _compute_amount(self):
|
||||
for record in self:
|
||||
if record.reservation_type in ("staff", "out"):
|
||||
record.amount_total = 0
|
||||
vals = {
|
||||
"payment_state": False,
|
||||
"payment_state": "nothing_to_pay",
|
||||
"pending_amount": 0,
|
||||
"invoices_paid": 0,
|
||||
}
|
||||
record.update(vals)
|
||||
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"
|
||||
and x.parent_state == "posted"
|
||||
)
|
||||
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 != record.currency_id:
|
||||
advance_amount += line.currency_id._convert(
|
||||
line_amount,
|
||||
record.currency_id,
|
||||
record.company_id,
|
||||
line.date or fields.Date.today(),
|
||||
)
|
||||
else:
|
||||
advance_amount += line_amount
|
||||
amount_residual = record.amount_total - advance_amount
|
||||
)
|
||||
amount_folio_residual = self.amount_total - self._get_advance_amount(
|
||||
mls_one_folio
|
||||
)
|
||||
amount_total_residual = sum(folios.mapped("amount_total")) - advance_amount
|
||||
else:
|
||||
amount_folio_residual = amount_total_residual = (
|
||||
sum(folios.mapped("amount_total")) - advance_amount
|
||||
)
|
||||
total = sum(folios.mapped("amount_total"))
|
||||
|
||||
total = record.amount_total
|
||||
# REVIEW: Must We ignored services in cancelled folios
|
||||
# pending amount?
|
||||
if record.state == "cancel":
|
||||
total = total - sum(record.service_ids.mapped("price_total"))
|
||||
# Compute 'payment_state'.
|
||||
payment_state = "not_paid"
|
||||
if (
|
||||
mls
|
||||
and float_compare(
|
||||
amount_residual,
|
||||
total,
|
||||
precision_rounding=record.currency_id.rounding,
|
||||
)
|
||||
!= 0
|
||||
):
|
||||
has_due_amount = float_compare(
|
||||
amount_residual,
|
||||
0.0,
|
||||
precision_rounding=record.currency_id.rounding,
|
||||
)
|
||||
if has_due_amount <= 0:
|
||||
payment_state = "paid"
|
||||
elif has_due_amount > 0:
|
||||
payment_state = "partial"
|
||||
# REVIEW: Must We ignored services in cancelled folios
|
||||
# pending amount?
|
||||
for folio in folios:
|
||||
if folio.state == "cancel":
|
||||
total = total - sum(folio.service_ids.mapped("price_total"))
|
||||
payment_state = "not_paid"
|
||||
if (
|
||||
mls
|
||||
and float_compare(
|
||||
amount_total_residual,
|
||||
total,
|
||||
precision_rounding=self.currency_id.rounding,
|
||||
)
|
||||
!= 0
|
||||
):
|
||||
has_due_amount = float_compare(
|
||||
amount_total_residual,
|
||||
0.0,
|
||||
precision_rounding=self.currency_id.rounding,
|
||||
)
|
||||
if has_due_amount == 0:
|
||||
payment_state = "paid"
|
||||
elif has_due_amount > 0:
|
||||
payment_state = "partial"
|
||||
elif has_due_amount < 0:
|
||||
payment_state = "overpayment"
|
||||
elif total == 0:
|
||||
payment_state = "nothing_to_pay"
|
||||
|
||||
vals = {
|
||||
"pending_amount": amount_residual,
|
||||
"invoices_paid": advance_amount,
|
||||
"payment_state": payment_state,
|
||||
}
|
||||
record.update(vals)
|
||||
vals = {
|
||||
"payment_multi": len(folios) > 1,
|
||||
"pending_amount": min(amount_total_residual, amount_folio_residual),
|
||||
"invoices_paid": advance_amount,
|
||||
"payment_state": payment_state,
|
||||
}
|
||||
return vals
|
||||
|
||||
@api.depends("reservation_ids", "reservation_ids.priority")
|
||||
def _compute_max_reservation_priority(self):
|
||||
@@ -1236,12 +1299,11 @@ class PmsFolio(models.Model):
|
||||
if operator == "in" and value:
|
||||
self.env.cr.execute(
|
||||
"""
|
||||
SELECT array_agg(so.id)
|
||||
FROM pms_folio so
|
||||
JOIN folio_sale_line sol ON sol.folio_id = so.id
|
||||
JOIN folio_sale_line_invoice_rel soli_rel ON \
|
||||
soli_rel.sale_line_ids = sol.id
|
||||
JOIN account_move_line aml ON aml.id = soli_rel.invoice_line_id
|
||||
SELECT array_agg(fo.id)
|
||||
FROM pms_folio fo
|
||||
JOIN folio_sale_line fol ON fol.folio_id = fo.id
|
||||
JOIN folio_sale_line_invoice_rel foli_rel ON foli_rel.sale_line_id = fol.id
|
||||
JOIN account_move_line aml ON aml.id = foli_rel.invoice_line_id
|
||||
JOIN account_move am ON am.id = aml.move_id
|
||||
WHERE
|
||||
am.move_type in ('out_invoice', 'out_refund', 'in_receipt') AND
|
||||
@@ -1811,7 +1873,6 @@ class PmsFolio(models.Model):
|
||||
"invoice_origin": self.name,
|
||||
"invoice_payment_term_id": self.payment_term_id.id,
|
||||
"transaction_ids": [(6, 0, self.transaction_ids.ids)],
|
||||
"folio_ids": [(6, 0, [self.id])],
|
||||
"invoice_line_ids": [],
|
||||
"company_id": self.company_id.id,
|
||||
"payment_reference": self.external_reference or self.reference,
|
||||
|
||||
@@ -157,13 +157,13 @@
|
||||
<i class="fa fa-fw fa-arrow-circle-right" /> Pay Now
|
||||
</a>
|
||||
<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"
|
||||
>
|
||||
<i class="fa fa-fw fa-check-circle" /> Pending
|
||||
</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"
|
||||
>
|
||||
<i class="fa fa-fw fa-check-circle" /> Paid
|
||||
|
||||
@@ -55,6 +55,17 @@
|
||||
/>
|
||||
</bold>
|
||||
</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
|
||||
class="alert alert-warning"
|
||||
role="alert"
|
||||
@@ -209,6 +220,12 @@
|
||||
bg_color="bg-warning"
|
||||
attrs="{'invisible': [('payment_state', '!=', 'partial')]}"
|
||||
/>
|
||||
<widget
|
||||
name="web_ribbon"
|
||||
title="Overpayment!"
|
||||
bg_color="bg-info"
|
||||
attrs="{'invisible': [('payment_state', '!=', 'overpayment')]}"
|
||||
/>
|
||||
<widget
|
||||
name="web_ribbon"
|
||||
title="Staff"
|
||||
@@ -549,7 +566,13 @@
|
||||
<field name="payment_type" />
|
||||
<field name="amount" />
|
||||
<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>
|
||||
</field>
|
||||
<!-- REVIEW: transations has a payment related -->
|
||||
@@ -659,9 +682,9 @@
|
||||
/>
|
||||
<field
|
||||
name="payment_state"
|
||||
decoration-success="payment_state == 'paid'"
|
||||
decoration-danger="payment_state == 'not_paid' and state in ['cancel','onboard','done','arrival_delayed','departure_delayed']"
|
||||
decoration-info="payment_state == 'not_paid' and state in ['draft','confirm',]"
|
||||
decoration-success="payment_state in ['paid', 'nothing_to_pay']"
|
||||
decoration-danger="payment_state == 'not_paid'"
|
||||
decoration-info="payment_state == 'overpayment'"
|
||||
decoration-warning="payment_state == 'partial'"
|
||||
widget="badge"
|
||||
optional="show"
|
||||
|
||||
@@ -162,6 +162,7 @@ class FolioAdvancePaymentInv(models.TransientModel):
|
||||
if self.partner_invoice_id
|
||||
else order.partner_invoice_id.id,
|
||||
"currency_id": order.pricelist_id.currency_id.id,
|
||||
"folio_ids": [(6, 0, order.ids)],
|
||||
"payment_reference": order.reference,
|
||||
"invoice_payment_term_id": order.payment_term_id.id,
|
||||
"partner_bank_id": order.company_id.partner_id.bank_ids[:1].id,
|
||||
|
||||
Reference in New Issue
Block a user