mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
341 lines
13 KiB
Python
341 lines
13 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import time
|
|
|
|
from odoo import _, api, fields, models
|
|
from odoo.exceptions import UserError
|
|
|
|
|
|
class FolioAdvancePaymentInv(models.TransientModel):
|
|
_name = "folio.advance.payment.inv"
|
|
_description = "Folio Advance Payment Invoice"
|
|
|
|
partner_invoice_id = fields.Many2one(
|
|
string="Billing contact",
|
|
help="Invoice address for current partner",
|
|
default=lambda self: self._default_partner_invoice_id(),
|
|
comodel_name="res.partner",
|
|
)
|
|
advance_payment_method = fields.Selection(
|
|
string="Create Invoice",
|
|
help="A standard invoice is issued with all the order \
|
|
lines ready for invoicing, \
|
|
according to their invoicing policy \
|
|
(based on ordered or delivered quantity).",
|
|
required=True,
|
|
default="delivered",
|
|
selection=[
|
|
("delivered", "Regular invoice"),
|
|
("percentage", "Down payment (percentage)"),
|
|
("fixed", "Down payment (fixed amount)"),
|
|
],
|
|
)
|
|
bill_services = fields.Boolean(
|
|
string="Bill Services", help="Bill Services", default=True
|
|
)
|
|
bill_rooms = fields.Boolean(string="Bill Rooms", help="Bill Rooms", default=True)
|
|
deduct_down_payments = fields.Boolean(
|
|
string="Deduct down payments", help="Deduct down payments", default=True
|
|
)
|
|
has_down_payments = fields.Boolean(
|
|
string="Has down payments",
|
|
help="Has down payments",
|
|
readonly=True,
|
|
default=lambda self: self._default_has_down_payment(),
|
|
)
|
|
product_id = fields.Many2one(
|
|
string="Down Payment Product",
|
|
default=lambda self: self._default_product_id(),
|
|
comodel_name="product.product",
|
|
domain=[("type", "=", "service")],
|
|
)
|
|
count = fields.Integer(
|
|
string="Order Count",
|
|
default=lambda self: self._count(),
|
|
)
|
|
amount = fields.Float(
|
|
string="Down Payment Amount",
|
|
help="The percentage of amount to be invoiced in advance, taxes excluded.",
|
|
digits="Account",
|
|
)
|
|
currency_id = fields.Many2one(
|
|
string="Currency",
|
|
help="Currency used in invoices",
|
|
comodel_name="res.currency",
|
|
default=lambda self: self._default_currency_id(),
|
|
)
|
|
fixed_amount = fields.Monetary(
|
|
string="Down Payment Amount (Fixed)",
|
|
help="The fixed amount to be invoiced in advance, taxes excluded.",
|
|
)
|
|
deposit_account_id = fields.Many2one(
|
|
string="Income Account",
|
|
help="Account used for deposits",
|
|
default=lambda self: self._default_deposit_account_id(),
|
|
comodel_name="account.account",
|
|
domain=[("deprecated", "=", False)],
|
|
)
|
|
deposit_taxes_id = fields.Many2many(
|
|
string="Customer Taxes",
|
|
help="Taxes used for deposits",
|
|
default=lambda self: self._default_deposit_taxes_id(),
|
|
comodel_name="account.tax",
|
|
)
|
|
|
|
@api.model
|
|
def _count(self):
|
|
return len(self._context.get("active_ids", []))
|
|
|
|
@api.model
|
|
def _default_product_id(self):
|
|
product_id = (
|
|
self.env["ir.config_parameter"]
|
|
.sudo()
|
|
.get_param("sale.default_deposit_product_id")
|
|
)
|
|
return self.env["product.product"].browse(int(product_id)).exists()
|
|
|
|
@api.model
|
|
def _default_deposit_account_id(self):
|
|
return self._default_product_id()._get_product_accounts()["income"]
|
|
|
|
@api.model
|
|
def _default_deposit_taxes_id(self):
|
|
return self._default_product_id().taxes_id
|
|
|
|
@api.model
|
|
def _default_has_down_payment(self):
|
|
if self._context.get("active_model") == "pms.folio" and self._context.get(
|
|
"active_id", False
|
|
):
|
|
folio = self.env["pms.folio"].browse(self._context.get("active_id"))
|
|
return folio.sale_line_ids.filtered(lambda line: line.is_downpayment)
|
|
|
|
return False
|
|
|
|
@api.model
|
|
def _default_currency_id(self):
|
|
if self._context.get("active_model") == "pms.folio" and self._context.get(
|
|
"active_id", False
|
|
):
|
|
sale_order = self.env["pms.folio"].browse(self._context.get("active_id"))
|
|
return sale_order.currency_id
|
|
|
|
@api.model
|
|
def _default_partner_invoice_id(self):
|
|
if self._context.get("active_model") == "pms.folio" and self._context.get(
|
|
"active_id", False
|
|
):
|
|
folio = self.env["pms.folio"].browse(self._context.get("active_id", []))
|
|
if folio.partner_invoice_ids:
|
|
return folio.partner_invoice_ids[0]
|
|
else:
|
|
return False
|
|
|
|
def _get_advance_details(self, order):
|
|
context = {"lang": order.partner_id.lang}
|
|
if self.advance_payment_method == "percentage":
|
|
amount = order.amount_untaxed * self.amount / 100
|
|
name = _("Down payment of %s%%") % (self.amount)
|
|
else:
|
|
amount = self.fixed_amount
|
|
name = _("Down Payment")
|
|
del context
|
|
|
|
return amount, name
|
|
|
|
@api.onchange("advance_payment_method")
|
|
def onchange_advance_payment_method(self):
|
|
if self.advance_payment_method == "percentage":
|
|
amount = self.default_get(["amount"]).get("amount")
|
|
return {"value": {"amount": amount}}
|
|
return {}
|
|
|
|
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 = {
|
|
"ref": order.name,
|
|
"move_type": "out_invoice",
|
|
"journal_id": order.pms_property_id._get_folio_default_journal(
|
|
partner_id
|
|
).id,
|
|
"invoice_origin": order.name,
|
|
"invoice_user_id": order.user_id.id,
|
|
"narration": order.note,
|
|
"partner_id": partner_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,
|
|
# 'campaign_id': order.campaign_id.id,
|
|
# 'medium_id': order.medium_id.id,
|
|
# 'source_id': order.source_id.id,
|
|
"invoice_line_ids": [
|
|
(
|
|
0,
|
|
0,
|
|
{
|
|
"name": name,
|
|
"price_unit": amount,
|
|
"quantity": 1.0,
|
|
"product_id": self.product_id.id,
|
|
"product_uom_id": line.product_uom.id,
|
|
"tax_ids": [(6, 0, line.tax_ids.ids)],
|
|
"folio_line_ids": [(6, 0, [line.id])],
|
|
"analytic_tag_ids": [(6, 0, line.analytic_tag_ids.ids)],
|
|
"analytic_account_id": order.analytic_account_id.id or False,
|
|
},
|
|
)
|
|
],
|
|
}
|
|
|
|
return invoice_vals
|
|
|
|
def _create_invoice(self, order, line, amount):
|
|
if (self.advance_payment_method == "percentage" and self.amount <= 0.00) or (
|
|
self.advance_payment_method == "fixed" and self.fixed_amount <= 0.00
|
|
):
|
|
raise UserError(_("The value of the down payment amount must be positive."))
|
|
|
|
amount, name = self._get_advance_details(order)
|
|
|
|
invoice_vals = self._prepare_invoice_values(order, name, amount, line)
|
|
if order.fiscal_position_id:
|
|
invoice_vals["fiscal_position_id"] = order.fiscal_position_id.id
|
|
invoice = (
|
|
self.env["account.move"].sudo().create(invoice_vals).with_user(self.env.uid)
|
|
)
|
|
invoice.message_post_with_view(
|
|
"mail.message_origin_link",
|
|
values={"self": invoice, "origin": order},
|
|
subtype_id=self.env.ref("mail.mt_note").id,
|
|
)
|
|
return invoice
|
|
|
|
def _prepare_line(self, order, analytic_tag_ids, tax_ids, amount):
|
|
context = {"lang": order.partner_id.lang}
|
|
so_values = {
|
|
"name": _("Down Payment: %s") % (time.strftime("%m %Y"),),
|
|
"price_unit": amount,
|
|
"product_uom_qty": 0.0,
|
|
"folio_id": order.id,
|
|
"discount": 0.0,
|
|
"product_uom": self.product_id.uom_id.id,
|
|
"product_id": self.product_id.id,
|
|
"analytic_tag_ids": analytic_tag_ids,
|
|
"tax_ids": [(6, 0, tax_ids)],
|
|
"is_downpayment": True,
|
|
"sequence": order.sale_line_ids
|
|
and order.sale_line_ids[-1].sequence + 1
|
|
or 10,
|
|
}
|
|
del context
|
|
return so_values
|
|
|
|
def create_invoices(self):
|
|
folios = self.env["pms.folio"].browse(self._context.get("active_ids", []))
|
|
|
|
if self.advance_payment_method == "delivered":
|
|
lines_to_invoice = self._get_lines_to_invoice(
|
|
folios=folios,
|
|
bill_services=self.bill_services,
|
|
bill_rooms=self.bill_rooms,
|
|
)
|
|
folios._create_invoices(
|
|
final=self.deduct_down_payments,
|
|
lines_to_invoice=lines_to_invoice,
|
|
partner_invoice_id=self.partner_invoice_id.id
|
|
if self.partner_invoice_id
|
|
else False,
|
|
)
|
|
else:
|
|
# Create deposit product if necessary
|
|
if not self.product_id:
|
|
vals = self._prepare_deposit_product()
|
|
self.product_id = self.env["product.product"].create(vals)
|
|
self.env["ir.config_parameter"].sudo().set_param(
|
|
"sale.default_deposit_product_id", self.product_id.id
|
|
)
|
|
|
|
sale_line_obj = self.env["folio.sale.line"]
|
|
invoices = self.env["account.move"]
|
|
for order in folios:
|
|
amount, name = self._get_advance_details(order)
|
|
|
|
if self.product_id.invoice_policy != "order":
|
|
raise UserError(
|
|
_(
|
|
"""The product used to invoice a down payment should
|
|
have an invoice policy set to "Ordered quantities".
|
|
Please update your deposit product to be able
|
|
to create a deposit invoice."""
|
|
)
|
|
)
|
|
if self.product_id.type != "service":
|
|
raise UserError(
|
|
_(
|
|
"""The product used to invoice a down payment should
|
|
be of type 'Service'.
|
|
Please use another product or update this product."""
|
|
)
|
|
)
|
|
taxes = self.product_id.taxes_id.filtered(
|
|
lambda r: not order.company_id or r.company_id == order.company_id
|
|
)
|
|
tax_ids = order.fiscal_position_id.map_tax(taxes, self.product_id).ids
|
|
analytic_tag_ids = []
|
|
for line in order.sale_line_ids:
|
|
analytic_tag_ids = [
|
|
(4, analytic_tag.id, None)
|
|
for analytic_tag in line.analytic_tag_ids
|
|
]
|
|
|
|
line_values = self._prepare_line(
|
|
order, analytic_tag_ids, tax_ids, amount
|
|
)
|
|
line = sale_line_obj.sudo().create(line_values)
|
|
invoices += self._create_invoice(order, line, amount)
|
|
if self._context.get("open_invoices", False):
|
|
return folios.action_view_invoice()
|
|
if self._context.get("return_invoices", False):
|
|
return invoices
|
|
return {"type": "ir.actions.act_window_close"}
|
|
|
|
def _prepare_deposit_product(self):
|
|
return {
|
|
"name": "Down payment",
|
|
"type": "service",
|
|
"invoice_policy": "order",
|
|
"property_account_income_id": self.deposit_account_id.id,
|
|
"taxes_id": [(6, 0, self.deposit_taxes_id.ids)],
|
|
"company_id": False,
|
|
}
|
|
|
|
@api.model
|
|
def _get_lines_to_invoice(self, folios, bill_services=True, bill_rooms=True):
|
|
lines_to_invoice = folios.sale_line_ids
|
|
if not bill_services:
|
|
lines_to_invoice = lines_to_invoice - lines_to_invoice.filtered(
|
|
lambda l: l.service_id and not l.service_id.is_board_service
|
|
)
|
|
if not bill_rooms:
|
|
lines_to_invoice = lines_to_invoice.filtered(
|
|
lambda l: l.reservation_id and l.reservation_line_ids
|
|
)
|
|
if not lines_to_invoice:
|
|
raise UserError(_("Nothing to invoice"))
|
|
|
|
lines_dict = dict()
|
|
for line in lines_to_invoice:
|
|
if not line.display_type:
|
|
lines_dict[line.id] = line.qty_to_invoice
|
|
else:
|
|
lines_dict[line.id] = 0
|
|
return lines_dict
|
|
# return lines_to_invoice
|