mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
219 lines
8.4 KiB
Python
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
|