Files
pms/pms/models/account_payment.py

219 lines
8.4 KiB
Python

# 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
class AccountPayment(models.Model):
_inherit = "account.payment"
folio_ids = fields.Many2many(
string="Folios",
comodel_name="pms.folio",
compute="_compute_folio_ids",
store=True,
readonly=False,
relation="account_payment_folio_rel",
column1="payment_id",
column2="folio_id",
)
origin_agency_id = fields.Many2one(
string="Origin Agency",
help="The agency where the folio account move originates",
comodel_name="res.partner",
domain="[('is_agency', '=', True)]",
compute="_compute_origin_agency_id",
store=True,
index=True,
readonly=True,
)
origin_reference = fields.Char(
string="Origin Reference",
help="The reference of the payment origin",
)
@api.depends("reconciled_invoice_ids", "reconciled_bill_ids")
def _compute_origin_agency_id(self):
"""
Compute the origin agency of the sale line,
if the line has multiple agencies in origin,
(p.e. nights with different agencies in origin),
the first one is returned (REVIEW: is this correct?)
"""
for rec in self:
inv_agency_ids = rec.reconciled_invoice_ids.mapped(
"line_ids.folio_line_ids.origin_agency_id.id"
)
bill_agency_ids = rec.reconciled_bill_ids.mapped(
"line_ids.folio_line_ids.origin_agency_id.id"
)
agency_ids = list(set(inv_agency_ids + bill_agency_ids))
if agency_ids:
rec.write({"origin_agency_id": agency_ids[0]})
elif (
not rec.reconciled_invoice_ids
and not rec.reconciled_bill_ids
and rec.folio_ids
):
rec.origin_agency_id = rec.origin_agency_id
else:
rec.origin_agency_id = False
@api.depends("reconciled_invoice_ids", "reconciled_bill_ids")
def _compute_folio_ids(self):
for rec in self:
inv_folio_ids = rec.reconciled_invoice_ids.mapped(
"line_ids.folio_line_ids.folio_id.id"
)
bill_folio_ids = rec.reconciled_bill_ids.mapped(
"line_ids.folio_line_ids.folio_id.id"
)
folio_ids = list(set(inv_folio_ids + bill_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.folio_ids:
rec.folio_ids = False
def _prepare_move_line_default_vals(self, write_off_line_vals=None):
line_vals_list = super(AccountPayment, self)._prepare_move_line_default_vals(
write_off_line_vals
)
if self.folio_ids:
for line in line_vals_list:
line.update(
{
"folio_ids": [(6, 0, self.folio_ids.ids)],
}
)
return line_vals_list
def _synchronize_to_moves(self, changed_fields):
super(AccountPayment, self)._synchronize_to_moves(changed_fields)
if "folio_ids" in changed_fields:
for pay in self.with_context(skip_account_move_synchronization=True):
pay.move_id.write(
{
"folio_ids": [(6, 0, pay.folio_ids.ids)],
}
)
def action_draft(self):
for payment in self:
if payment._check_has_downpayment_invoice(payment):
downpayment_invoices = payment.reconciled_invoice_ids.filtered(
lambda inv: inv._is_downpayment()
)
if downpayment_invoices.state == "posted":
default_values_list = [
{
"ref": _(f'Reversal of: {move.name + " - " + move.ref}'),
}
for move in downpayment_invoices
]
downpayment_invoices._reverse_moves(
default_values_list, cancel=True
)
else:
downpayment_invoices.unlink()
return super(AccountPayment, self).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 (
payment.folio_ids
and payment.partner_type == "customer"
and payment.reconciled_invoice_ids.filtered(
lambda inv: inv._is_downpayment()
)
):
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):
group = defaultdict(list)
for line in (invoice.line_ids + payment_move.line_ids).filtered(
lambda l: not l.reconciled
):
group[(line.account_id, line.currency_id)].append(line.id)
for (account, _dummy), line_ids in group.items():
if account.reconcile or account.internal_type == "liquidity":
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 l: not l.default_invoice_to
):
sale_line.default_invoice_to = move.partner_id.id
return move