mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[WIP]pms: advance autoinvoicinf configuration
This commit is contained in:
@@ -101,5 +101,21 @@
|
||||
<field name="nextcall" eval="DateTime.now()" />
|
||||
<field name="code">model.send_cancelation_mail()</field>
|
||||
</record>
|
||||
<record model="ir.cron" id="autoinvoicing_folios">
|
||||
<field name="name">Auto Invoicing Folios</field>
|
||||
<field name="interval_number">5</field>
|
||||
<field name="user_id" ref="base.user_root" />
|
||||
<field name="active" eval="False" />
|
||||
<field name="interval_type">days</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False" />
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="model_pms_property" />
|
||||
<field
|
||||
name="nextcall"
|
||||
eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d 04:30:00')"
|
||||
/>
|
||||
<field name="code">model.autoinvoicing()</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -34,7 +34,7 @@ class AccountBankStatement(models.Model):
|
||||
)
|
||||
super(AccountBankStatement, self).button_post()
|
||||
for line in lines_of_moves_to_post:
|
||||
payment_move_line = line._get_payment_move_lines_to_reconcile(line)
|
||||
payment_move_line = line._get_payment_move_lines_to_reconcile()
|
||||
statement_move_line = line.move_id.line_ids.filtered(
|
||||
lambda line: line.account_id.reconcile
|
||||
)
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import datetime
|
||||
from dateutil import relativedelta
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from itertools import groupby
|
||||
|
||||
@@ -505,6 +507,11 @@ class PmsFolio(models.Model):
|
||||
store=True,
|
||||
compute="_compute_last_checkout",
|
||||
)
|
||||
autoinvoice_date = fields.Date(
|
||||
string="Autoinvoice Date",
|
||||
compute="_compute_autoinvoice_date",
|
||||
store=True,
|
||||
)
|
||||
|
||||
def name_get(self):
|
||||
result = []
|
||||
@@ -539,68 +546,114 @@ class PmsFolio(models.Model):
|
||||
)
|
||||
invoice_vals_list = []
|
||||
invoice_item_sequence = 0
|
||||
for order in self:
|
||||
order = order.with_company(order.company_id)
|
||||
current_section_vals = None
|
||||
down_payments = order.env["folio.sale.line"]
|
||||
|
||||
# Invoice values.
|
||||
invoice_vals = order._prepare_invoice(partner_invoice_id=partner_invoice_id)
|
||||
|
||||
# Invoice line values (keep only necessary sections).
|
||||
invoice_lines_vals = []
|
||||
for line in order.sale_line_ids.filtered(
|
||||
for folio in self:
|
||||
folio_lines_to_invoice = folio.sale_line_ids.filtered(
|
||||
lambda l: l.id in list(lines_to_invoice.keys())
|
||||
):
|
||||
if line.display_type == "line_section":
|
||||
current_section_vals = line._prepare_invoice_line(
|
||||
sequence=invoice_item_sequence + 1
|
||||
)
|
||||
continue
|
||||
if line.display_type != "line_note" and float_is_zero(
|
||||
line.qty_to_invoice, precision_digits=precision
|
||||
):
|
||||
continue
|
||||
if (
|
||||
line.qty_to_invoice > 0
|
||||
or (line.qty_to_invoice < 0 and final)
|
||||
or line.display_type == "line_note"
|
||||
):
|
||||
if line.is_downpayment:
|
||||
down_payments += line
|
||||
continue
|
||||
if current_section_vals:
|
||||
invoice_item_sequence += 1
|
||||
invoice_lines_vals.append(current_section_vals)
|
||||
current_section_vals = None
|
||||
invoice_item_sequence += 1
|
||||
prepared_line = line._prepare_invoice_line(
|
||||
sequence=invoice_item_sequence, qty=lines_to_invoice[line.id]
|
||||
)
|
||||
invoice_lines_vals.append(prepared_line)
|
||||
)
|
||||
|
||||
# If down payments are present in SO, group them under common section
|
||||
if down_payments:
|
||||
invoice_item_sequence += 1
|
||||
down_payments_section = order._prepare_down_payment_section_line(
|
||||
sequence=invoice_item_sequence
|
||||
folio_partner_invoice_id = partner_invoice_id
|
||||
if not folio_partner_invoice_id:
|
||||
if folio.partner_id and folio.partner_id.document_number_to_invoice:
|
||||
folio_partner_invoice_id = folio.partner_id.id
|
||||
else:
|
||||
folio_partner_invoice_id = (
|
||||
self.partner_invoice_ids[0].id if self.partner_invoice_ids else False
|
||||
)
|
||||
|
||||
target_lines = folio_lines_to_invoice
|
||||
if self._context.get("lines_auto_add") and folio_partner_invoice_id:
|
||||
if folio_partner_invoice_id.default_invoice_lines == 'overnights':
|
||||
target_lines = target_lines.filtered(
|
||||
lambda r: r.is_board_service or r.reservation_id.overnight_room
|
||||
)
|
||||
elif folio_partner_invoice_id.default_invoice_lines == 'reservations':
|
||||
target_lines = target_lines.filtered(
|
||||
lambda r: r.is_board_service or r.reservation_id
|
||||
)
|
||||
elif folio_partner_invoice_id.default_invoice_lines == 'services':
|
||||
target_lines = target_lines.filtered(
|
||||
lambda r: not r.is_board_service or r.service_id
|
||||
)
|
||||
groups_invoice_lines = [
|
||||
{
|
||||
"partner_id": folio_partner_invoice_id,
|
||||
"lines": target_lines,
|
||||
}
|
||||
]
|
||||
if (
|
||||
folio.autoinvoice_date
|
||||
and folio.autoinvoice_date <= fields.Date.today()
|
||||
and len(target_lines) < len(folio_lines_to_invoice)
|
||||
):
|
||||
second_partner_to_invoice = folio.partner_invoice_ids.filtered(
|
||||
lambda p: p.id != folio_partner_invoice_id
|
||||
)
|
||||
invoice_lines_vals.append(down_payments_section)
|
||||
for down_payment in down_payments:
|
||||
groups_invoice_lines.append(
|
||||
{
|
||||
"partner_id": second_partner_to_invoice and second_partner_to_invoice.id,
|
||||
"lines": folio_lines_to_invoice - target_lines
|
||||
}
|
||||
)
|
||||
for group in groups_invoice_lines:
|
||||
folio = folio.with_company(folio.company_id)
|
||||
down_payments = folio.env["folio.sale.line"]
|
||||
|
||||
# Invoice values.
|
||||
invoice_vals = folio._prepare_invoice(partner_invoice_id=group["partner_id"])
|
||||
|
||||
# Invoice line values (keep only necessary sections).
|
||||
current_section_vals = None
|
||||
invoice_lines_vals = []
|
||||
for line in group["lines"]:
|
||||
if line.display_type == "line_section":
|
||||
current_section_vals = line._prepare_invoice_line(
|
||||
sequence=invoice_item_sequence + 1
|
||||
)
|
||||
continue
|
||||
if line.display_type != "line_note" and float_is_zero(
|
||||
line.qty_to_invoice, precision_digits=precision
|
||||
):
|
||||
continue
|
||||
if (
|
||||
line.qty_to_invoice > 0
|
||||
or (line.qty_to_invoice < 0 and final)
|
||||
or line.display_type == "line_note"
|
||||
):
|
||||
if line.is_downpayment:
|
||||
down_payments += line
|
||||
continue
|
||||
if current_section_vals:
|
||||
invoice_item_sequence += 1
|
||||
invoice_lines_vals.append(current_section_vals)
|
||||
current_section_vals = None
|
||||
invoice_item_sequence += 1
|
||||
prepared_line = line._prepare_invoice_line(
|
||||
sequence=invoice_item_sequence, qty=lines_to_invoice[line.id]
|
||||
)
|
||||
invoice_lines_vals.append(prepared_line)
|
||||
|
||||
# If down payments are present in SO, group them under common section
|
||||
if down_payments:
|
||||
invoice_item_sequence += 1
|
||||
invoice_down_payment_vals = down_payment._prepare_invoice_line(
|
||||
down_payments_section = folio._prepare_down_payment_section_line(
|
||||
sequence=invoice_item_sequence
|
||||
)
|
||||
invoice_lines_vals.append(invoice_down_payment_vals)
|
||||
invoice_lines_vals.append(down_payments_section)
|
||||
for down_payment in down_payments:
|
||||
invoice_item_sequence += 1
|
||||
invoice_down_payment_vals = down_payment._prepare_invoice_line(
|
||||
sequence=invoice_item_sequence
|
||||
)
|
||||
invoice_lines_vals.append(invoice_down_payment_vals)
|
||||
|
||||
if not any(
|
||||
new_line["display_type"] is False for new_line in invoice_lines_vals
|
||||
):
|
||||
raise self._nothing_to_invoice_error()
|
||||
if not any(
|
||||
new_line["display_type"] is False for new_line in invoice_lines_vals
|
||||
):
|
||||
raise self._nothing_to_invoice_error()
|
||||
|
||||
invoice_vals["invoice_line_ids"] = [
|
||||
(0, 0, invoice_line_id) for invoice_line_id in invoice_lines_vals
|
||||
]
|
||||
invoice_vals["invoice_line_ids"] = [
|
||||
(0, 0, invoice_line_id) for invoice_line_id in invoice_lines_vals
|
||||
]
|
||||
|
||||
invoice_vals_list.append(invoice_vals)
|
||||
return invoice_vals_list
|
||||
@@ -627,6 +680,28 @@ class PmsFolio(models.Model):
|
||||
]
|
||||
return res
|
||||
|
||||
@api.depends("partner_id", "invoice_status", "last_checkout")
|
||||
def _compute_autoinvoice_date(self):
|
||||
self.autoinvoice_date = False
|
||||
for record in self.filtered(lambda r: r.invoice_status == "to_invoice"):
|
||||
record.autoinvoice_date = record._get_to_invoice_date()
|
||||
|
||||
def _get_to_invoice_date(self):
|
||||
self.ensure_one()
|
||||
partner = self.partner_id
|
||||
invoicing_policy = self.pms_property_id.default_invoicing_policy if not partner or partner.invoicing_policy == "property" else partner.invoicing_policy
|
||||
if invoicing_policy == "manual":
|
||||
return False
|
||||
if invoicing_policy == "checkout":
|
||||
margin_days = self.pms_property_id.margin_days_autoinvoice if not partner or partner.invoicing_policy == "property" else partner.margin_days_autoinvoice
|
||||
return self.checkout + timedelta(days=margin_days)
|
||||
if invoicing_policy == "month_day":
|
||||
month_day = self.pms_property_id.invoicing_month_day if not partner or partner.invoicing_policy == "property" else partner.invoicing_month_day
|
||||
if self.checkout.day <= month_day:
|
||||
self.autoinvoice_date = self.checkout.replace(day=month_day)
|
||||
else:
|
||||
self.autoinvoice_date = (self.checkout + relativedelta.relativedelta(months=1)).replace(day=month_day)
|
||||
|
||||
@api.depends("reservation_ids", "reservation_ids.state")
|
||||
def _compute_number_of_rooms(self):
|
||||
for folio in self:
|
||||
@@ -1508,6 +1583,7 @@ class PmsFolio(models.Model):
|
||||
return self.env["account.move"]
|
||||
# 1) Create invoices.
|
||||
if not lines_to_invoice:
|
||||
self = self.with_context(lines_auto_add=True)
|
||||
lines_to_invoice = dict()
|
||||
for line in self.sale_line_ids:
|
||||
lines_to_invoice[line.id] = (
|
||||
@@ -1630,29 +1706,31 @@ class PmsFolio(models.Model):
|
||||
(making sure to call super() to establish a clean extension chain).
|
||||
"""
|
||||
self.ensure_one()
|
||||
journal = (
|
||||
self.env["account.move"]
|
||||
.with_context(
|
||||
default_move_type="out_invoice",
|
||||
default_company_id=self.company_id.id,
|
||||
default_pms_property_id=self.pms_property_id.id,
|
||||
if not partner_invoice_id:
|
||||
partner_invoice_id = (
|
||||
self.partner_invoice_ids[0].id if self.partner_invoice_ids else False
|
||||
)
|
||||
|
||||
journal = self._get_folio_default_journal(partner_invoice_id)
|
||||
if not journal:
|
||||
journal = (
|
||||
self.env["account.move"]
|
||||
.with_context(
|
||||
default_move_type="out_invoice",
|
||||
default_company_id=self.company_id.id,
|
||||
default_pms_property_id=self.pms_property_id.id,
|
||||
)
|
||||
._get_default_journal()
|
||||
)
|
||||
._get_default_journal()
|
||||
)
|
||||
if not journal:
|
||||
raise UserError(
|
||||
_("Please define an accounting sales journal for the company %s (%s).")
|
||||
% (self.company_id.name, self.company_id.id)
|
||||
)
|
||||
|
||||
if not partner_invoice_id:
|
||||
partner_invoice_id = (
|
||||
self.partner_invoice_ids[0].id if self.partner_invoice_ids else False
|
||||
)
|
||||
|
||||
invoice_vals = {
|
||||
"ref": self.client_order_ref or "",
|
||||
"move_type": "out_invoice",
|
||||
"move_type": self._get_default_move_type(partner_invoice_id),
|
||||
"narration": self.note,
|
||||
"currency_id": self.pricelist_id.currency_id.id,
|
||||
# 'campaign_id': self.campaign_id.id,
|
||||
@@ -1672,6 +1750,21 @@ class PmsFolio(models.Model):
|
||||
}
|
||||
return invoice_vals
|
||||
|
||||
def _get_folio_default_journal(self, partner_invoice_id):
|
||||
self.ensure_one()
|
||||
pms_property = self.pms_property_id
|
||||
partner = self.env["res.partner"].browse(partner_invoice_id)
|
||||
if partner.document_number_to_invoice:
|
||||
return pms_property.journal_normal_invoice_id
|
||||
return pms_property.journal_simplified_invoice_id
|
||||
|
||||
def _get_default_move_type(self, partner_invoice_id):
|
||||
self.ensure_one()
|
||||
partner = self.env["res.partner"].browse(partner_invoice_id)
|
||||
if partner.document_number_to_invoice:
|
||||
return "out_invoice"
|
||||
return "entry"
|
||||
|
||||
def do_payment(
|
||||
self,
|
||||
journal,
|
||||
|
||||
@@ -147,10 +147,21 @@ class PmsProperty(models.Model):
|
||||
selection=[
|
||||
("manual", "Manual"),
|
||||
("checkout", "Checkout"),
|
||||
("month_day", "Month Day Invoice"),
|
||||
],
|
||||
default="manual",
|
||||
)
|
||||
|
||||
margin_days_autoinvoice = fields.Integer(
|
||||
string="Margin Days",
|
||||
help="Days from Checkout to generate the invoice",
|
||||
)
|
||||
|
||||
invoicing_month_day = fields.Integer(
|
||||
string="Invoicing Month Day",
|
||||
help="The day of the month to invoice",
|
||||
)
|
||||
|
||||
journal_simplified_invoice_id = fields.Many2one(
|
||||
string="Simplified Invoice Journal",
|
||||
comodel_name="account.journal",
|
||||
@@ -580,5 +591,20 @@ class PmsProperty(models.Model):
|
||||
"closed": True,
|
||||
}
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def autoinvoicing(self):
|
||||
"""
|
||||
This method is used to autoinvoicing the folios
|
||||
"""
|
||||
folios = self.env["pms.folio"].search([
|
||||
("autoinvoice_date", "=" , fields.date.today()),
|
||||
])
|
||||
if folios:
|
||||
invoices = folios.with_context(autoinvoice=True)._create_invoices(
|
||||
grouped=True,
|
||||
)
|
||||
if invoices:
|
||||
invoices.action_post()
|
||||
return True
|
||||
|
||||
@@ -42,7 +42,7 @@ class ResPartner(models.Model):
|
||||
)
|
||||
pms_property_ids = fields.Many2many(
|
||||
string="Properties",
|
||||
help="Properties with access to the element;"
|
||||
help="Properties with access to the element"
|
||||
" if not set, all properties can access",
|
||||
required=False,
|
||||
comodel_name="pms.property",
|
||||
@@ -133,6 +133,60 @@ class ResPartner(models.Model):
|
||||
string="Possible Customer In Checkin Partner",
|
||||
comodel_name="pms.checkin.partner",
|
||||
)
|
||||
invoicing_policy = fields.Selection(
|
||||
string="Invoicing Policy",
|
||||
help="The invoicing policy of the partner, set Property to user the policy configured in the Property",
|
||||
selection=[
|
||||
("property", "Property Policy Invoice"),
|
||||
("manual", "Manual"),
|
||||
("checkout", "From Checkout"),
|
||||
("month_day", "Month Day Invoice"),
|
||||
],
|
||||
default="property",
|
||||
)
|
||||
invoicing_month_day = fields.Integer(
|
||||
string="Invoicing Month Day",
|
||||
help="The day of the month to invoice",
|
||||
)
|
||||
margin_days_autoinvoice = fields.Integer(
|
||||
string="Days from Checkout",
|
||||
help="Days from Checkout to generate the invoice",
|
||||
)
|
||||
default_invoice_lines = fields.Selection(
|
||||
string="Invoice...",
|
||||
help="""Use to preconfigure the sale lines to autoinvoice
|
||||
for this partner. All (invoice reservations and services),
|
||||
Only overnights to invoice only the reservations
|
||||
with overnight and board services(exclude parkings, salon, etc...),
|
||||
All reservations to include all reservations,
|
||||
and Services only include services not boards""",
|
||||
selection=[
|
||||
("all", "All"),
|
||||
("overnights", "Only Overnights"),
|
||||
("reservations", "All reservations"),
|
||||
("services", "Services"),
|
||||
],
|
||||
default="all",
|
||||
)
|
||||
document_number_to_invoice = fields.Char(
|
||||
string="Document Number to invoices",
|
||||
help="""Technical field to compute the partner reference to invoice,
|
||||
it can be the VAT, if its set, or the document number, if its set,
|
||||
else it will be False""",
|
||||
compute="_compute_document_number_to_invoice",
|
||||
readonly=False,
|
||||
store=True,
|
||||
)
|
||||
|
||||
@api.depends("vat", "id_numbers", "id_numbers.name")
|
||||
def _compute_document_number_to_invoice(self):
|
||||
for partner in self:
|
||||
if partner.vat:
|
||||
partner.document_number_to_invoice = partner.vat
|
||||
elif partner.id_numbers:
|
||||
partner.document_number_to_invoice = partner.id_numbers[0].name
|
||||
else:
|
||||
partner.document_number_to_invoice = False
|
||||
|
||||
@api.depends("pms_checkin_partner_ids", "pms_checkin_partner_ids.gender")
|
||||
def _compute_gender(self):
|
||||
|
||||
@@ -88,6 +88,10 @@
|
||||
<page string="Invoicing" name="property_invoicing">
|
||||
<group>
|
||||
<field name="default_invoicing_policy" />
|
||||
<field
|
||||
name="margin_days_autoinvoice"
|
||||
attrs="{'invisible': [('default_invoicing_policy', '=', 'manual')]}"
|
||||
/>
|
||||
<field
|
||||
name="journal_simplified_invoice_id"
|
||||
attrs="{'required': [('default_invoicing_policy', '!=', 'manual')]}"
|
||||
|
||||
@@ -93,6 +93,29 @@
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_partner_property_form" model="ir.ui.view">
|
||||
<field name="name">view.partner.property.form</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="account.view_partner_property_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr='//group[@name="accounting_entries"]' position='after'>
|
||||
<group string="PMS Invoice policy" name="pms_invoice_policy">
|
||||
<field name="invoicing_policy" />
|
||||
<field
|
||||
name="invoicing_month_day"
|
||||
attrs="{'invisible': [('invoicing_policy', '!=', 'month_day')]}"
|
||||
/>
|
||||
<field
|
||||
name="margin_days_autoinvoice"
|
||||
attrs="{'invisible': [('invoicing_policy', '!=', 'checkout')]}"
|
||||
/>
|
||||
<field name="default_invoice_lines" />
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_partner_data_form" model="ir.ui.view">
|
||||
<field name="name">res.partner.form.data</field>
|
||||
<field name="model">res.partner</field>
|
||||
|
||||
Reference in New Issue
Block a user