From 76b5332fa96102e79f05504854db61532227d6c4 Mon Sep 17 00:00:00 2001 From: PicchiSeba Date: Tue, 9 Jan 2024 17:43:38 +0100 Subject: [PATCH] [IMP] subscription_oca: enable markers to print invoice names (like in _contract_) --- subscription_oca/models/sale_subscription.py | 30 ++++++++- .../models/sale_subscription_line.py | 50 +++++++++++++-- .../models/sale_subscription_template.py | 13 ++++ .../tests/test_subscription_oca.py | 64 ++++++++++++++----- .../views/sale_subscription_views.xml | 34 ++++++++++ 5 files changed, 169 insertions(+), 22 deletions(-) diff --git a/subscription_oca/models/sale_subscription.py b/subscription_oca/models/sale_subscription.py index 250f97a00..4e9e6f33a 100644 --- a/subscription_oca/models/sale_subscription.py +++ b/subscription_oca/models/sale_subscription.py @@ -277,6 +277,7 @@ class SaleSubscription(models.Model): return values def create_invoice(self): + self.ensure_one() if not self.env["account.move"].check_access_rights("create", False): try: self.check_access_rights("write") @@ -284,8 +285,10 @@ class SaleSubscription(models.Model): except AccessError: return self.env["account.move"] 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: - 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)) invoice_values = self._prepare_account_move(line_ids) invoice_id = ( @@ -305,8 +308,10 @@ class SaleSubscription(models.Model): except AccessError: return self.env["sale.order"] 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: - 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)) values = self._prepare_sale_order(line_ids) order_id = self.env["sale.order"].sudo().create(values) @@ -431,6 +436,27 @@ class SaleSubscription(models.Model): return True 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): res = super().write(values) if "stage_id" in values: diff --git a/subscription_oca/models/sale_subscription_line.py b/subscription_oca/models/sale_subscription_line.py index b90792d1b..d535041e2 100644 --- a/subscription_oca/models/sale_subscription_line.py +++ b/subscription_oca/models/sale_subscription_line.py @@ -1,6 +1,6 @@ # Copyright 2023 Domatix - Carlos Martínez # 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 @@ -290,11 +290,11 @@ class SaleSubscriptionLine(models.Model): ) 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() return { "product_id": self.product_id.id, - "name": self.name, + "name": self._generate_name(start_date, end_date), "product_uom_qty": self.product_uom_qty, "price_unit": self.price_unit, "discount": self.discount, @@ -303,7 +303,7 @@ class SaleSubscriptionLine(models.Model): "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() account = ( self.product_id.property_account_income_id @@ -311,7 +311,7 @@ class SaleSubscriptionLine(models.Model): ) return { "product_id": self.product_id.id, - "name": self.name, + "name": self._generate_name(start_date, end_date), "quantity": self.product_uom_qty, "price_unit": self.price_unit, "discount": self.discount, @@ -320,3 +320,43 @@ class SaleSubscriptionLine(models.Model): "product_uom_id": self.product_id.uom_id.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] diff --git a/subscription_oca/models/sale_subscription_template.py b/subscription_oca/models/sale_subscription_template.py index df89f401b..9b987ca14 100644 --- a/subscription_oca/models/sale_subscription_template.py +++ b/subscription_oca/models/sale_subscription_template.py @@ -100,3 +100,16 @@ class SaleSubscriptionTemplate(models.Model): "type": "ir.actions.act_window", "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) diff --git a/subscription_oca/tests/test_subscription_oca.py b/subscription_oca/tests/test_subscription_oca.py index d16708e3b..f6316fc28 100644 --- a/subscription_oca/tests/test_subscription_oca.py +++ b/subscription_oca/tests/test_subscription_oca.py @@ -15,6 +15,13 @@ class TestSubscriptionOCA(SavepointCase): super().setUpClass() cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) 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( [ ("type", "=", "cash"), @@ -181,15 +188,15 @@ class TestSubscriptionOCA(SavepointCase): "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_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_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_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_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.close_reason = cls.env["sale.subscription.close.reason"].create( @@ -263,14 +270,15 @@ class TestSubscriptionOCA(SavepointCase): return rec @classmethod - def create_sub_line(cls, sub, prod=None): - ssl = cls.env["sale.subscription.line"].create( - { - "company_id": 1, - "sale_subscription_id": sub.id, - "product_id": prod or cls.product_1.id, - } - ) + def create_sub_line(cls, sub, prod=None, name=None): + create_dict = { + "company_id": 1, + "sale_subscription_id": sub.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 @classmethod @@ -320,9 +328,11 @@ class TestSubscriptionOCA(SavepointCase): self.assertEqual(self.sub_line.discount, 0) res = self.sub_line._get_display_price(self.product_2) 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) - 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) def test_subscription_oca_sub_cron(self): @@ -593,13 +603,37 @@ class TestSubscriptionOCA(SavepointCase): res = self.sub_line.read(["discount"]) 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): test_res = [] sale_order = subscription.create_sale_order() test_res.append(sale_order) move_id = subscription.create_invoice() 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() + 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"]) inv_ids = self.env["account.move"].search( [("subscription_id", "=", subscription.id)] diff --git a/subscription_oca/views/sale_subscription_views.xml b/subscription_oca/views/sale_subscription_views.xml index 427914348..cf9153e47 100644 --- a/subscription_oca/views/sale_subscription_views.xml +++ b/subscription_oca/views/sale_subscription_views.xml @@ -192,6 +192,40 @@ + +

+ #START# + : Start + date + of the + invoiced period +

+

+ #END# + : End date + of + the + invoiced period +

+

+ #INVOICEMONTHNAME# + : Invoice month name + of + the + invoiced period +

+

+ #INVOICEMONTHNUMBER#: Invoice month number of the period +

+

+ #INVOICEYEAR#: Invoice year of the period +

+