Merge PR #1031 into 14.0

Signed-off-by renda-dev
This commit is contained in:
OCA-git-bot
2024-01-25 14:16:13 +00:00
5 changed files with 169 additions and 22 deletions

View File

@@ -277,6 +277,7 @@ class SaleSubscription(models.Model):
return values return values
def create_invoice(self): def create_invoice(self):
self.ensure_one()
if not self.env["account.move"].check_access_rights("create", False): if not self.env["account.move"].check_access_rights("create", False):
try: try:
self.check_access_rights("write") self.check_access_rights("write")
@@ -284,8 +285,10 @@ class SaleSubscription(models.Model):
except AccessError: except AccessError:
return self.env["account.move"] return self.env["account.move"]
line_ids = [] line_ids = []
start_date = self.recurring_next_date or self.date_start
end_date = self._get_next_period_date_end(start_date, self.date)
for line in self.sale_subscription_line_ids: for line in self.sale_subscription_line_ids:
line_values = line._prepare_account_move_line() line_values = line._prepare_account_move_line(start_date, end_date)
line_ids.append((0, 0, line_values)) line_ids.append((0, 0, line_values))
invoice_values = self._prepare_account_move(line_ids) invoice_values = self._prepare_account_move(line_ids)
invoice_id = ( invoice_id = (
@@ -305,8 +308,10 @@ class SaleSubscription(models.Model):
except AccessError: except AccessError:
return self.env["sale.order"] return self.env["sale.order"]
line_ids = [] line_ids = []
start_date = self.recurring_next_date or self.date_start
end_date = self._get_next_period_date_end(start_date, self.date)
for line in self.sale_subscription_line_ids: for line in self.sale_subscription_line_ids:
line_values = line._prepare_sale_order_line() line_values = line._prepare_sale_order_line(start_date, end_date)
line_ids.append((0, 0, line_values)) line_ids.append((0, 0, line_values))
values = self._prepare_sale_order(line_ids) values = self._prepare_sale_order(line_ids)
order_id = self.env["sale.order"].sudo().create(values) order_id = self.env["sale.order"].sudo().create(values)
@@ -431,6 +436,27 @@ class SaleSubscription(models.Model):
return True return True
return False return False
def _get_next_period_date_end(
self,
next_period_date_start,
max_date_end,
):
self.ensure_one()
if not next_period_date_start:
return False
if max_date_end and next_period_date_start > max_date_end:
return False
next_period_date_end = (
next_period_date_start
+ self.template_id.get_relative_delta()
- relativedelta(days=1)
)
if not max_date_end:
return next_period_date_end
if next_period_date_end > max_date_end:
return max_date_end
return next_period_date_end
def write(self, values): def write(self, values):
res = super().write(values) res = super().write(values)
if "stage_id" in values: if "stage_id" in values:

View File

@@ -1,6 +1,6 @@
# Copyright 2023 Domatix - Carlos Martínez # Copyright 2023 Domatix - Carlos Martínez
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, fields, models from odoo import _, api, fields, models
from odoo.tools.misc import get_lang from odoo.tools.misc import get_lang
@@ -290,11 +290,11 @@ class SaleSubscriptionLine(models.Model):
) )
return max(base_price, final_price) return max(base_price, final_price)
def _prepare_sale_order_line(self): def _prepare_sale_order_line(self, start_date, end_date):
self.ensure_one() self.ensure_one()
return { return {
"product_id": self.product_id.id, "product_id": self.product_id.id,
"name": self.name, "name": self._generate_name(start_date, end_date),
"product_uom_qty": self.product_uom_qty, "product_uom_qty": self.product_uom_qty,
"price_unit": self.price_unit, "price_unit": self.price_unit,
"discount": self.discount, "discount": self.discount,
@@ -303,7 +303,7 @@ class SaleSubscriptionLine(models.Model):
"product_uom": self.product_id.uom_id.id, "product_uom": self.product_id.uom_id.id,
} }
def _prepare_account_move_line(self): def _prepare_account_move_line(self, start_date, end_date):
self.ensure_one() self.ensure_one()
account = ( account = (
self.product_id.property_account_income_id self.product_id.property_account_income_id
@@ -311,7 +311,7 @@ class SaleSubscriptionLine(models.Model):
) )
return { return {
"product_id": self.product_id.id, "product_id": self.product_id.id,
"name": self.name, "name": self._generate_name(start_date, end_date),
"quantity": self.product_uom_qty, "quantity": self.product_uom_qty,
"price_unit": self.price_unit, "price_unit": self.price_unit,
"discount": self.discount, "discount": self.discount,
@@ -320,3 +320,43 @@ class SaleSubscriptionLine(models.Model):
"product_uom_id": self.product_id.uom_id.id, "product_uom_id": self.product_id.uom_id.id,
"account_id": account.id, "account_id": account.id,
} }
def _generate_name(self, start_date, end_date):
return self._insert_markers(start_date, end_date)
def _insert_markers(self, start_date, end_date):
self.ensure_one()
lang_obj = self.env["res.lang"]
lang = lang_obj.search(
[("code", "=", self.sale_subscription_id.partner_id.lang)]
)
date_format = lang.date_format or "%m/%d/%Y"
name = self.name
name = name.replace("#START#", start_date.strftime(date_format))
name = name.replace("#END#", end_date.strftime(date_format)) if end_date else ""
name = name.replace("#INVOICEMONTHNUMBER#", start_date.strftime("%m"))
name = name.replace("#INVOICEYEAR#", start_date.strftime("%Y"))
name = name.replace(
"#INVOICEMONTHNAME#",
self.with_context(lang=lang.code)._translate_marker_month_name(
start_date.strftime("%m")
),
)
return name
def _translate_marker_month_name(self, month_name):
months = {
"01": _("January"),
"02": _("February"),
"03": _("March"),
"04": _("April"),
"05": _("May"),
"06": _("June"),
"07": _("July"),
"08": _("August"),
"09": _("September"),
"10": _("October"),
"11": _("November"),
"12": _("December"),
}
return months[month_name]

View File

@@ -100,3 +100,16 @@ class SaleSubscriptionTemplate(models.Model):
"type": "ir.actions.act_window", "type": "ir.actions.act_window",
"domain": [("id", "in", self.product_ids.ids)], "domain": [("id", "in", self.product_ids.ids)],
} }
def get_relative_delta(self):
self.ensure_one()
rule_type = self.recurring_rule_type
interval = self.recurring_interval
if rule_type == "days":
return relativedelta(days=interval)
elif rule_type == "weeks":
return relativedelta(weeks=interval)
elif rule_type == "months":
return relativedelta(months=interval)
else:
return relativedelta(years=interval)

View File

@@ -15,6 +15,13 @@ class TestSubscriptionOCA(SavepointCase):
super().setUpClass() super().setUpClass()
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
cls.portal_user = cls.env.ref("base.demo_user0") cls.portal_user = cls.env.ref("base.demo_user0")
cls.line_placeholders = [
"#START#",
"#END#",
"#INVOICEMONTHNAME#",
"#INVOICEMONTHNUMBER#",
"#INVOICEYEAR#",
]
cls.cash_journal = cls.env["account.journal"].search( cls.cash_journal = cls.env["account.journal"].search(
[ [
("type", "=", "cash"), ("type", "=", "cash"),
@@ -181,15 +188,15 @@ class TestSubscriptionOCA(SavepointCase):
"sale_subscription_id": cls.sub1.id, "sale_subscription_id": cls.sub1.id,
} }
) )
cls.sub_line21 = cls.create_sub_line(cls.sub2) cls.sub_line21 = cls.create_sub_line(cls.sub2, name="#START#")
cls.sub_line22 = cls.create_sub_line(cls.sub2, cls.product_2.id) cls.sub_line22 = cls.create_sub_line(cls.sub2, cls.product_2.id)
cls.sub_line31 = cls.create_sub_line(cls.sub3) cls.sub_line31 = cls.create_sub_line(cls.sub3, name="#END#")
cls.sub_line32 = cls.create_sub_line(cls.sub3, cls.product_2.id) cls.sub_line32 = cls.create_sub_line(cls.sub3, cls.product_2.id)
cls.sub_line41 = cls.create_sub_line(cls.sub4) cls.sub_line41 = cls.create_sub_line(cls.sub4, name="#INVOICEMONTHNAME#")
cls.sub_line42 = cls.create_sub_line(cls.sub4, cls.product_2.id) cls.sub_line42 = cls.create_sub_line(cls.sub4, cls.product_2.id)
cls.sub_line51 = cls.create_sub_line(cls.sub5) cls.sub_line51 = cls.create_sub_line(cls.sub5, name="#INVOICEMONTHNUMBER#")
cls.sub_line52 = cls.create_sub_line(cls.sub5, cls.product_2.id) cls.sub_line52 = cls.create_sub_line(cls.sub5, cls.product_2.id)
cls.sub_line71 = cls.create_sub_line(cls.sub7) cls.sub_line71 = cls.create_sub_line(cls.sub7, name="#INVOICEYEAR#")
cls.sub_line72 = cls.create_sub_line(cls.sub7, cls.product_2.id) cls.sub_line72 = cls.create_sub_line(cls.sub7, cls.product_2.id)
cls.close_reason = cls.env["sale.subscription.close.reason"].create( cls.close_reason = cls.env["sale.subscription.close.reason"].create(
@@ -263,14 +270,15 @@ class TestSubscriptionOCA(SavepointCase):
return rec return rec
@classmethod @classmethod
def create_sub_line(cls, sub, prod=None): def create_sub_line(cls, sub, prod=None, name=None):
ssl = cls.env["sale.subscription.line"].create( create_dict = {
{
"company_id": 1, "company_id": 1,
"sale_subscription_id": sub.id, "sale_subscription_id": sub.id,
"product_id": prod or cls.product_1.id, "product_id": prod or cls.product_1.id,
} }
) if name:
create_dict["name"] = name
ssl = cls.env["sale.subscription.line"].create(create_dict)
return ssl return ssl
@classmethod @classmethod
@@ -320,9 +328,11 @@ class TestSubscriptionOCA(SavepointCase):
self.assertEqual(self.sub_line.discount, 0) self.assertEqual(self.sub_line.discount, 0)
res = self.sub_line._get_display_price(self.product_2) res = self.sub_line._get_display_price(self.product_2)
self.assertEqual(res, 38.25) self.assertEqual(res, 38.25)
sol_res = self.sub_line._prepare_sale_order_line() date_start = self.sub_line.sale_subscription_id.date_start
date_end = self.sub_line.sale_subscription_id.date
sol_res = self.sub_line._prepare_sale_order_line(date_start, date_end)
self.assertIsInstance(sol_res, dict) self.assertIsInstance(sol_res, dict)
move_res = self.sub_line._prepare_account_move_line() move_res = self.sub_line._prepare_account_move_line(date_start, date_end)
self.assertIsInstance(move_res, dict) self.assertIsInstance(move_res, dict)
def test_subscription_oca_sub_cron(self): def test_subscription_oca_sub_cron(self):
@@ -593,13 +603,37 @@ class TestSubscriptionOCA(SavepointCase):
res = self.sub_line.read(["discount"]) res = self.sub_line.read(["discount"])
self.assertEqual(res[0]["discount"], 0) self.assertEqual(res[0]["discount"], 0)
def get_converted_placeholders(self, placeholder_lines, date_from, date_to):
return [line._insert_markers(date_from, date_to) for line in placeholder_lines]
def _collect_all_sub_test_results(self, subscription): def _collect_all_sub_test_results(self, subscription):
test_res = [] test_res = []
sale_order = subscription.create_sale_order() sale_order = subscription.create_sale_order()
test_res.append(sale_order) test_res.append(sale_order)
move_id = subscription.create_invoice() move_id = subscription.create_invoice()
test_res.append(move_id) test_res.append(move_id)
lines_w_placeholder = subscription.sale_subscription_line_ids.filtered(
lambda line: any(p == line.name for p in self.line_placeholders)
)
start_date = subscription.recurring_next_date or subscription.date_start
end_date = subscription._get_next_period_date_end(start_date, subscription.date)
# remove the lines with #INVOICEYEAR# if the end_date is not set
if not end_date and "#INVOICEYEAR#" in lines_w_placeholder.mapped("name"):
target = lines_w_placeholder.filtered(
lambda line: line.name == "#INVOICEYEAR#"
)
lines_w_placeholder -= target
res = subscription.manual_invoice() res = subscription.manual_invoice()
if lines_w_placeholder:
markers = self.get_converted_placeholders(
lines_w_placeholder, start_date, end_date
)
# all markers must be among the line names
account_move_line_names = (
self.env["account.move"].browse(res["res_id"]).line_ids.mapped("name")
)
result = all(m in account_move_line_names for m in markers)
self.assertTrue(result)
test_res.append(res["type"]) test_res.append(res["type"])
inv_ids = self.env["account.move"].search( inv_ids = self.env["account.move"].search(
[("subscription_id", "=", subscription.id)] [("subscription_id", "=", subscription.id)]

View File

@@ -192,6 +192,40 @@
<field name="user_id" /> <field name="user_id" />
</group> </group>
</group> </group>
<group
string="Legend (for the markers inside invoice lines description)"
name="group_legend"
>
<p colspan="2">
<strong>#START#</strong>
: Start
date
of the
invoiced period
</p>
<p colspan="2">
<strong>#END#</strong>
: End date
of
the
invoiced period
</p>
<p colspan="2">
<strong>#INVOICEMONTHNAME#</strong>
: Invoice month name
of
the
invoiced period
</p>
<p colspan="2">
<strong
>#INVOICEMONTHNUMBER#</strong>: Invoice month number of the period
</p>
<p colspan="2">
<strong
>#INVOICEYEAR#</strong>: Invoice year of the period
</p>
</group>
</page> </page>
</notebook> </notebook>