From ab5f0562802cf2e4a450dfad579f79544926f4d4 Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 20 Sep 2023 21:49:04 +0200 Subject: [PATCH 01/35] [15.0][ADD] subscription_oca --- subscription_oca/README.rst | 99 ++ subscription_oca/__init__.py | 2 + subscription_oca/__manifest__.py | 28 + subscription_oca/data/ir_cron.xml | 15 + .../data/sale_subscription_data.xml | 76 ++ subscription_oca/i18n/subscription_oca.pot | 985 ++++++++++++++++++ subscription_oca/models/__init__.py | 11 + subscription_oca/models/account_move.py | 12 + subscription_oca/models/product_template.py | 12 + subscription_oca/models/res_partner.py | 33 + subscription_oca/models/sale_order.py | 83 ++ subscription_oca/models/sale_order_line.py | 17 + subscription_oca/models/sale_subscription.py | 470 +++++++++ .../models/sale_subscription_close_reason.py | 10 + .../models/sale_subscription_line.py | 322 ++++++ .../models/sale_subscription_stage.py | 29 + .../models/sale_subscription_tag.py | 10 + .../models/sale_subscription_template.py | 102 ++ subscription_oca/readme/CONTRIBUTORS.rst | 1 + subscription_oca/readme/DESCRIPTION.rst | 1 + subscription_oca/readme/ROADMAP.rst | 2 + subscription_oca/readme/USAGE.rst | 13 + subscription_oca/security/ir.model.access.csv | 8 + subscription_oca/static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/index.html | 439 ++++++++ subscription_oca/static/img/icon.png | Bin 0 -> 16096 bytes .../views/product_template_views.xml | 20 + subscription_oca/views/res_partner_views.xml | 28 + subscription_oca/views/sale_order_views.xml | 29 + .../views/sale_subscription_stage_views.xml | 73 ++ .../views/sale_subscription_tag_views.xml | 27 + .../sale_subscription_template_views.xml | 135 +++ .../views/sale_subscription_views.xml | 473 +++++++++ subscription_oca/wizard/__init__.py | 1 + .../wizard/close_subscription_wizard.py | 25 + .../wizard/close_subscription_wizard.xml | 30 + 36 files changed, 3621 insertions(+) create mode 100644 subscription_oca/README.rst create mode 100644 subscription_oca/__init__.py create mode 100644 subscription_oca/__manifest__.py create mode 100644 subscription_oca/data/ir_cron.xml create mode 100644 subscription_oca/data/sale_subscription_data.xml create mode 100644 subscription_oca/i18n/subscription_oca.pot create mode 100644 subscription_oca/models/__init__.py create mode 100644 subscription_oca/models/account_move.py create mode 100644 subscription_oca/models/product_template.py create mode 100644 subscription_oca/models/res_partner.py create mode 100644 subscription_oca/models/sale_order.py create mode 100644 subscription_oca/models/sale_order_line.py create mode 100644 subscription_oca/models/sale_subscription.py create mode 100644 subscription_oca/models/sale_subscription_close_reason.py create mode 100644 subscription_oca/models/sale_subscription_line.py create mode 100644 subscription_oca/models/sale_subscription_stage.py create mode 100644 subscription_oca/models/sale_subscription_tag.py create mode 100644 subscription_oca/models/sale_subscription_template.py create mode 100644 subscription_oca/readme/CONTRIBUTORS.rst create mode 100644 subscription_oca/readme/DESCRIPTION.rst create mode 100644 subscription_oca/readme/ROADMAP.rst create mode 100644 subscription_oca/readme/USAGE.rst create mode 100644 subscription_oca/security/ir.model.access.csv create mode 100644 subscription_oca/static/description/icon.png create mode 100644 subscription_oca/static/description/index.html create mode 100644 subscription_oca/static/img/icon.png create mode 100644 subscription_oca/views/product_template_views.xml create mode 100644 subscription_oca/views/res_partner_views.xml create mode 100644 subscription_oca/views/sale_order_views.xml create mode 100644 subscription_oca/views/sale_subscription_stage_views.xml create mode 100644 subscription_oca/views/sale_subscription_tag_views.xml create mode 100644 subscription_oca/views/sale_subscription_template_views.xml create mode 100644 subscription_oca/views/sale_subscription_views.xml create mode 100644 subscription_oca/wizard/__init__.py create mode 100644 subscription_oca/wizard/close_subscription_wizard.py create mode 100644 subscription_oca/wizard/close_subscription_wizard.xml diff --git a/subscription_oca/README.rst b/subscription_oca/README.rst new file mode 100644 index 0000000000..0b03b4bf12 --- /dev/null +++ b/subscription_oca/README.rst @@ -0,0 +1,99 @@ +======================= +Subscription management +======================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:3772d65a58c07d0348bd13d3c882810c94bfb87389c62fec6d16fe8ef130252c + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fcontract-lightgray.png?logo=github + :target: https://github.com/OCA/contract/tree/15.0/subscription_oca + :alt: OCA/contract +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/contract-15-0/contract-15-0-subscription_oca + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/contract&target_branch=15.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows creating subscriptions that generate recurring invoices or orders. It also enables the sale of products that generate subscriptions. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To make a subscription: + +#. Go to *Subscriptions > Configuration > Subscription templates*. +#. Create the templates you consider, choosing the billing frequency: daily, monthly... and the method of creating the invoice and/or order. +#. Go to *Subscription > Subscriptions*. +#. Create a subscription and indicate the start date. When the *Subscriptions Management* cron job is executed, the subscription will begin and the first invoice will be created if the execution date matches the start date. The invoice will also be created when the execution date matches the next invoice date. Additionally, you can manually change the subscription status and create an invoice. +#. The cron job will also end the subscription if its end date has been reached. + +To create subscriptions with the sale of a product: + +#. Go to *Subscriptions > Subscriptions > Products*. +#. Create the product and in the sales tab, complete the fields *Subscribable product* and *Subscription template* +#. Create a sales order with the product and confirm it. + +Known issues / Roadmap +====================== + +* Refactor all the onchanges that have business logic to computed write-able fields when possible. Keep onchanges only for UI purposes. +* Add tests. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Domatix + +Contributors +~~~~~~~~~~~~ + +* Carlos Martínez + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/contract `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/subscription_oca/__init__.py b/subscription_oca/__init__.py new file mode 100644 index 0000000000..9b4296142f --- /dev/null +++ b/subscription_oca/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizard diff --git a/subscription_oca/__manifest__.py b/subscription_oca/__manifest__.py new file mode 100644 index 0000000000..8e9cb33b82 --- /dev/null +++ b/subscription_oca/__manifest__.py @@ -0,0 +1,28 @@ +# Copyright 2023 Domatix - Carlos Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Subscription management", + "summary": "Generate recurring invoices.", + "version": "15.0.1.0.0", + "development_status": "Beta", + "category": "Subscription Management", + "website": "https://github.com/OCA/contract", + "license": "AGPL-3", + "author": "Domatix, Odoo Community Association (OCA)", + "depends": ["sale_management", "account"], + "data": [ + "views/product_template_views.xml", + "views/sale_subscription_views.xml", + "views/sale_subscription_stage_views.xml", + "views/sale_subscription_tag_views.xml", + "views/sale_subscription_template_views.xml", + "views/sale_order_views.xml", + "views/res_partner_views.xml", + "data/ir_cron.xml", + "data/sale_subscription_data.xml", + "wizard/close_subscription_wizard.xml", + "security/ir.model.access.csv", + ], + "installable": True, + "application": True, +} diff --git a/subscription_oca/data/ir_cron.xml b/subscription_oca/data/ir_cron.xml new file mode 100644 index 0000000000..fbc7f7c888 --- /dev/null +++ b/subscription_oca/data/ir_cron.xml @@ -0,0 +1,15 @@ + + + + Subscriptions management + + + 24 + hours + -1 + + + code + model.cron_subscription_management() + + diff --git a/subscription_oca/data/sale_subscription_data.xml b/subscription_oca/data/sale_subscription_data.xml new file mode 100644 index 0000000000..6ee56a620e --- /dev/null +++ b/subscription_oca/data/sale_subscription_data.xml @@ -0,0 +1,76 @@ + + + + + + sale_subscription_sequencer + sale.subscription + SUB + 5 + + + + + + + Ready to start + 0 + pre + + Draft equivalent, a subscription is ready to start when is not marked as in progress but it can be at any moment. If there's no 'Closed'-type stage defined, when a subscription comes to an end by automatic means, it will be marked with this stage. + + + + + + + In progress + 1 + in_progress + + + As an 'In progress'-type of stage, it will trigger the recurring invoicing process if applicable. If this stage is the first - sequence order - of the available 'In progress' types and there's a stage change from any other non-'In progress' types to this one, an invoice will be created automatically if the start date is the present day. + + + + + + Closed + 2 + post + + + The final stage of a subscription. There are two ways to mark a subscription as closed. The easiest one is using the kanban card-moving capabilities, pressing the 'Close subscription' button (only available if a subscription is in progress). + + + + + + + + + The subscription is too expensive + + + + + Subscription does not meet my requirements + + + + + The subscription ended + + + + + I don't really use it + + + + + Other + + + + diff --git a/subscription_oca/i18n/subscription_oca.pot b/subscription_oca/i18n/subscription_oca.pot new file mode 100644 index 0000000000..60a0262041 --- /dev/null +++ b/subscription_oca/i18n/subscription_oca.pot @@ -0,0 +1,985 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * subscription_oca +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: subscription_oca +#: model:sale.subscription.stage,description:subscription_oca.subscription_stage_in_progress +msgid "" +"\n" +" As an 'In progress'-type of stage, it will trigger the recurring invoicing process if applicable. If this stage is the first - sequence order - of the available 'In progress' types and there's a stage change from any other non-'In progress' types to this one, an invoice will be created automatically if the start date is the present day.\n" +" " +msgstr "" + +#. module: subscription_oca +#: model:sale.subscription.stage,description:subscription_oca.subscription_stage_draft +msgid "" +"\n" +" Draft equivalent, a subscription is ready to start when is not marked as in progress but it can be at any moment. If there's no 'Closed'-type stage defined, when a subscription comes to an end by automatic means, it will be marked with this stage.\n" +" " +msgstr "" + +#. module: subscription_oca +#: model:sale.subscription.stage,description:subscription_oca.subscription_stage_closed +msgid "" +"\n" +" The final stage of a subscription. There are two ways to mark a subscription as closed. The easiest one is using the kanban card-moving capabilities, pressing the 'Close subscription' button (only available if a subscription is in progress).\n" +" " +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_template_form +msgid "Every" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_template_form +msgid "For" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.res_partner_view_form +msgid "Subscriptions" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_form +msgid "" +"A given subscription can be marked as closed when, for example, renewal is " +"not desired." +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__active +msgid "Active" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__activity_ids +msgid "Activities" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__activity_state +msgid "Activity State" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__activity_type_icon +msgid "Activity Type Icon" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.view_subscription_stage_form +msgid "Add new description..." +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__amount_tax +msgid "Amount Tax" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__amount_total +msgid "Amount Total" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__recurring_rule_boundary +msgid "Boundary" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.close_reason_wizard_view +msgid "Cancel" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.actions.act_window,help:subscription_oca.subscription_stage_action +msgid "Click to create a new subscription stage." +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__close_reason_id +msgid "Close Reason" +msgstr "" + +#. module: subscription_oca +#: model:ir.actions.act_window,name:subscription_oca.close_reason_wizard_act_window +#: model_terms:ir.ui.view,arch_db:subscription_oca.close_reason_wizard_view +msgid "Close reason" +msgstr "" + +#. module: subscription_oca +#: model:ir.model,name:subscription_oca.model_sale_subscription_close_reason +msgid "Close reason model" +msgstr "" + +#. module: subscription_oca +#: model:ir.model,name:subscription_oca.model_close_reason_wizard +msgid "Close reason wizard" +msgstr "" + +#. module: subscription_oca +#: model:ir.actions.act_window,name:subscription_oca.subscription_close_reason_action +#: model:ir.ui.menu,name:subscription_oca.subscription_close_reason_menu +msgid "Close reasons" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_form +msgid "Close subscription" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields.selection,name:subscription_oca.selection__sale_subscription_stage__type__post +#: model:sale.subscription.stage,name:subscription_oca.subscription_stage_closed +msgid "Closed" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__code +msgid "Code" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__color +msgid "Color Index" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__user_id +msgid "Commercial agent" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__company_id +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__company_id +msgid "Company" +msgstr "" + +#. module: subscription_oca +#: model:ir.ui.menu,name:subscription_oca.sale_subscription_configuration_menu +msgid "Configuration" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.close_reason_wizard_view +msgid "Confirm" +msgstr "" + +#. module: subscription_oca +#: model:ir.model,name:subscription_oca.model_res_partner +msgid "Contact" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_form +msgid "Create Invoice" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_close_reason_wizard__create_uid +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__create_uid +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_close_reason__create_uid +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__create_uid +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_stage__create_uid +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_tag__create_uid +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__create_uid +msgid "Created by" +msgstr "" + +#. module: subscription_oca +#: code:addons/subscription_oca/models/sale_subscription.py:0 +#, python-format +msgid "Created invoice with reference" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_close_reason_wizard__create_date +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__create_date +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_close_reason__create_date +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__create_date +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_stage__create_date +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_tag__create_date +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__create_date +msgid "Created on" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__currency_id +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__currency_id +msgid "Currency" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields.selection,name:subscription_oca.selection__sale_subscription_template__recurring_rule_type__days +msgid "Day(s)" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_kanban +msgid "Delete" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__description +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__name +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_stage__description +msgid "Description" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__discount +msgid "Discount (%)" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_close_reason_wizard__display_name +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__display_name +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_close_reason__display_name +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__display_name +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_tag__display_name +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__display_name +msgid "Display Name" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_stage__display_name +msgid "Display name" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields.selection,name:subscription_oca.selection__sale_subscription_template__invoicing_mode__draft +msgid "Draft" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__recurring_rule_boundary +msgid "Duration" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_kanban +msgid "Edit" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__stage_str +msgid "Etapa" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__date +msgid "Finish date" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__fiscal_position_id +msgid "Fiscal Position" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields.selection,name:subscription_oca.selection__sale_subscription_template__recurring_rule_boundary__limited +msgid "Fixed" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,help:subscription_oca.field_sale_subscription__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields.selection,name:subscription_oca.selection__sale_subscription_template__recurring_rule_boundary__unlimited +msgid "Forever" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__has_message +msgid "Has Message" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_close_reason_wizard__id +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__id +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_close_reason__id +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__id +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_stage__id +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_tag__id +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__id +msgid "ID" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__activity_exception_icon +msgid "Icon" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,help:subscription_oca.field_sale_subscription__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,help:subscription_oca.field_sale_subscription__message_needaction +#: model:ir.model.fields,help:subscription_oca.field_sale_subscription__message_unread +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,help:subscription_oca.field_sale_subscription__message_has_error +#: model:ir.model.fields,help:subscription_oca.field_sale_subscription__message_has_sms_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__image +msgid "Image" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__in_progress +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_stage__in_progress +#: model:ir.model.fields.selection,name:subscription_oca.selection__sale_subscription_stage__type__in_progress +#: model:sale.subscription.stage,name:subscription_oca.subscription_stage_in_progress +msgid "In progress" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields.selection,name:subscription_oca.selection__sale_subscription_template__invoicing_mode__invoice +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_template_form +msgid "Invoice" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields.selection,name:subscription_oca.selection__sale_subscription_template__invoicing_mode__invoice_send +msgid "Invoice & send" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__account_invoice_ids_count +msgid "Invoice Count" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__invoice_mail_template_id +msgid "Invoice Email" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__invoice_ids +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_form +msgid "Invoices" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_template_form +msgid "Invoicing" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__invoicing_mode +msgid "Invoicing mode" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__journal_id +msgid "Journal" +msgstr "" + +#. module: subscription_oca +#: model:ir.model,name:subscription_oca.model_account_move +msgid "Journal Entry" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_stage__fold +msgid "Kanban folded" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_close_reason_wizard____last_update +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription____last_update +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_close_reason____last_update +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line____last_update +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_stage____last_update +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_tag____last_update +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template____last_update +msgid "Last Modified on" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_close_reason_wizard__write_uid +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__write_uid +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_close_reason__write_uid +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__write_uid +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_stage__write_uid +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_tag__write_uid +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_close_reason_wizard__write_date +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__write_date +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_close_reason__write_date +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__write_date +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_stage__write_date +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_tag__write_date +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__write_date +msgid "Last Updated on" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__message_main_attachment_id +msgid "Main Attachment" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_kanban +msgid "Menú desplegable" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__message_ids +msgid "Messages" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_form +msgid "Misc" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields.selection,name:subscription_oca.selection__sale_subscription_template__recurring_rule_type__months +msgid "Month(s)" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__name +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_close_reason__name +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_stage__name +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__name +msgid "Name" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_form +msgid "New subscription" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__activity_summary +msgid "Next Activity Summary" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__activity_type_id +msgid "Next Activity Type" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__recurring_next_date +msgid "Next invoice date" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,help:subscription_oca.field_sale_subscription__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,help:subscription_oca.field_sale_subscription__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,help:subscription_oca.field_sale_subscription__message_unread_counter +msgid "Number of unread messages" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__sale_order_ids +msgid "Orders" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__sale_order_id +msgid "Origin sale order" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_form +msgid "Other info" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__partner_id +msgid "Partner" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.view_sale_order_pending_filter +msgid "Pending subscriptions" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__pricelist_id +msgid "Pricelist" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__product_id +msgid "Product" +msgstr "" + +#. module: subscription_oca +#: model:ir.model,name:subscription_oca.model_product_template +msgid "Product Template" +msgstr "" + +#. module: subscription_oca +#: model:ir.actions.act_window,name:subscription_oca.subscription_product_template_action +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__product_ids +#: model:ir.ui.menu,name:subscription_oca.product_subscription_menu +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_template_form +msgid "Products" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__product_uom_qty +msgid "Quantity" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields.selection,name:subscription_oca.selection__sale_subscription_stage__type__pre +#: model:sale.subscription.stage,name:subscription_oca.subscription_stage_draft +msgid "Ready to start" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_close_reason_wizard__close_reason_id +msgid "Reason" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__recurring_rule_type +msgid "Recurrence" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__recurring_total +msgid "Recurring price" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__code +msgid "Reference" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__recurring_interval +msgid "Repeat every" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__activity_user_id +msgid "Responsible User" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__recurring_rule_count +msgid "Rule count" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__message_has_sms_error +msgid "SMS Delivery error" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__sale_subscription_line_ids +msgid "Sale Subscription Line" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields.selection,name:subscription_oca.selection__sale_subscription_template__invoicing_mode__sale_and_invoice +msgid "Sale order & Invoice" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__sale_order_ids_count +msgid "Sale orders" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__crm_team_id +msgid "Sale team" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_form +msgid "Sales" +msgstr "" + +#. module: subscription_oca +#: model:ir.model,name:subscription_oca.model_sale_order +msgid "Sales Order" +msgstr "" + +#. module: subscription_oca +#: model:ir.model,name:subscription_oca.model_sale_order_line +msgid "Sales Order Line" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__sequence +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_stage__sequence +msgid "Sequence" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__stage_id +msgid "Stage" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.view_subscription_stage_form +msgid "" +"Stages define the life-cycle of a given subscription; this is," +" a subscription can be a 'Ready to start', 'In progress' or " +"'Closed' type of stage. Bear in mind that there can only be " +"one 'Closed'-type stage." +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__date_start +msgid "Start date" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,help:subscription_oca.field_sale_subscription__activity_state +msgid "" +"Status based on activities\n" +"Overdue: Due date is already passed\n" +"Today: Activity date is today\n" +"Planned: Future activities." +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_product_product__subscribable +#: model:ir.model.fields,field_description:subscription_oca.field_product_template__subscribable +msgid "Subscribable product" +msgstr "" + +#. module: subscription_oca +#: model:ir.model,name:subscription_oca.model_sale_subscription +#: model:ir.model.fields,field_description:subscription_oca.field_account_bank_statement_line__subscription_id +#: model:ir.model.fields,field_description:subscription_oca.field_account_move__subscription_id +#: model:ir.model.fields,field_description:subscription_oca.field_account_payment__subscription_id +#: model:ir.model.fields,field_description:subscription_oca.field_sale_order__order_subscription_id +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__sale_subscription_id +msgid "Subscription" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_res_partner__subscription_count +#: model:ir.model.fields,field_description:subscription_oca.field_res_users__subscription_count +msgid "Subscription Count" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_form +msgid "Subscription lines" +msgstr "" + +#. module: subscription_oca +#: model:ir.model,name:subscription_oca.model_sale_subscription_line +msgid "Subscription lines added to a given subscription" +msgstr "" + +#. module: subscription_oca +#: model:ir.model,name:subscription_oca.model_sale_subscription_stage +#: model_terms:ir.ui.view,arch_db:subscription_oca.view_subscription_stage_form +msgid "Subscription stage" +msgstr "" + +#. module: subscription_oca +#: model:ir.ui.menu,name:subscription_oca.subscription_stage_menu +msgid "Subscription stages" +msgstr "" + +#. module: subscription_oca +#: model:ir.ui.menu,name:subscription_oca.subscription_tag_menu +msgid "Subscription tags" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_product_product__subscription_template_id +#: model:ir.model.fields,field_description:subscription_oca.field_product_template__subscription_template_id +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__template_id +msgid "Subscription template" +msgstr "" + +#. module: subscription_oca +#: model:ir.actions.act_window,name:subscription_oca.sale_subscription_template_act_window +#: model:ir.model,name:subscription_oca.model_sale_subscription_template +#: model:ir.ui.menu,name:subscription_oca.sale_template_subscription_menu +msgid "Subscription templates" +msgstr "" + +#. module: subscription_oca +#: model:ir.actions.act_window,name:subscription_oca.sale_subscription_action +#: model:ir.model.fields,field_description:subscription_oca.field_res_partner__subscription_ids +#: model:ir.model.fields,field_description:subscription_oca.field_res_users__subscription_ids +#: model:ir.model.fields,field_description:subscription_oca.field_sale_order__subscription_ids +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__subscription_ids +#: model:ir.ui.menu,name:subscription_oca.sale_subscription_menu +#: model:ir.ui.menu,name:subscription_oca.sale_subscription_root +#: model:ir.ui.menu,name:subscription_oca.subscription_menu +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_template_form +#: model_terms:ir.ui.view,arch_db:subscription_oca.view_sale_order_form +msgid "Subscriptions" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_order__subscriptions_count +msgid "Subscriptions Count" +msgstr "" + +#. module: subscription_oca +#: model:ir.actions.server,name:subscription_oca.ir_cron_subscription_management_ir_actions_server +#: model:ir.cron,cron_name:subscription_oca.ir_cron_subscription_management +#: model:ir.cron,name:subscription_oca.ir_cron_subscription_management +msgid "Subscriptions management" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__price_subtotal +msgid "Subtotal" +msgstr "" + +#. module: subscription_oca +#: model:ir.actions.act_window,name:subscription_oca.subscription_stage_action +msgid "Susbcription stages" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.view_product_suscribable_filter +msgid "Suscribable products" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_tag__name +msgid "Tag name" +msgstr "" + +#. module: subscription_oca +#: model:ir.actions.act_window,name:subscription_oca.subscription_tag_action +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__tag_ids +msgid "Tags" +msgstr "" + +#. module: subscription_oca +#: model:ir.model,name:subscription_oca.model_sale_subscription_tag +msgid "Tags for sale subscription" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__tax_ids +msgid "Taxes" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__amount_tax_line_amount +msgid "Taxes Amount" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_template_form +msgid "Terms and Conditions" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__terms +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__description +msgid "Terms and conditions" +msgstr "" + +#. module: subscription_oca +#: code:addons/subscription_oca/models/sale_subscription_stage.py:0 +#, python-format +msgid "There is already a Closed-type stage declared" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_kanban +msgid "" +"This bar allows to filter the opportunities based on scheduled activities." +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.view_subscription_stage_form +msgid "" +"This field dictates the stages' visual order on the Kanban and form view. " +"Although is purely visual, mind that if the order isn't " +"consequent with your needs, you could have a 'Closed'-type stage before a " +"'Ready to start' one." +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__to_renew +msgid "To renew" +msgstr "" + +#. module: subscription_oca +#: code:addons/subscription_oca/models/sale_subscription.py:0 +#, python-format +msgid "To validate" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__price_total +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_tree +msgid "Total" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_tree +msgid "Total Tax" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_tree +msgid "Total subtotal" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_stage__type +msgid "Type" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,help:subscription_oca.field_sale_subscription__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_line__price_unit +msgid "Unit price" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__message_unread +msgid "Unread Messages" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__message_unread_counter +msgid "Unread Messages Counter" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,help:subscription_oca.field_sale_subscription__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields.selection,name:subscription_oca.selection__sale_subscription_template__recurring_rule_type__weeks +msgid "Week(s)" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields.selection,name:subscription_oca.selection__sale_subscription_template__recurring_rule_type__years +msgid "Year(s)" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_template_form +msgid "e.g. Monthly Subscription" +msgstr "" + +#. module: subscription_oca +#: model_terms:ir.ui.view,arch_db:subscription_oca.sale_subscription_template_form +msgid "month(s)" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__product_ids_count +msgid "product_ids" +msgstr "" + +#. module: subscription_oca +#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription_template__subscription_count +msgid "subscription_ids" +msgstr "" diff --git a/subscription_oca/models/__init__.py b/subscription_oca/models/__init__.py new file mode 100644 index 0000000000..6fa4481885 --- /dev/null +++ b/subscription_oca/models/__init__.py @@ -0,0 +1,11 @@ +from . import account_move +from . import product_template +from . import res_partner +from . import sale_order +from . import sale_order_line +from . import sale_subscription +from . import sale_subscription_close_reason +from . import sale_subscription_line +from . import sale_subscription_stage +from . import sale_subscription_tag +from . import sale_subscription_template diff --git a/subscription_oca/models/account_move.py b/subscription_oca/models/account_move.py new file mode 100644 index 0000000000..570a02f29a --- /dev/null +++ b/subscription_oca/models/account_move.py @@ -0,0 +1,12 @@ +# Copyright 2023 Domatix - Carlos Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class AccountMove(models.Model): + _inherit = "account.move" + + subscription_id = fields.Many2one( + comodel_name="sale.subscription", string="Subscription" + ) diff --git a/subscription_oca/models/product_template.py b/subscription_oca/models/product_template.py new file mode 100644 index 0000000000..c866cfa3ed --- /dev/null +++ b/subscription_oca/models/product_template.py @@ -0,0 +1,12 @@ +# Copyright 2023 Domatix - Carlos Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class Product(models.Model): + _inherit = "product.template" + + subscribable = fields.Boolean(string="Subscribable product") + subscription_template_id = fields.Many2one( + comodel_name="sale.subscription.template", string="Subscription template" + ) diff --git a/subscription_oca/models/res_partner.py b/subscription_oca/models/res_partner.py new file mode 100644 index 0000000000..e4107108e3 --- /dev/null +++ b/subscription_oca/models/res_partner.py @@ -0,0 +1,33 @@ +# Copyright 2023 Domatix - Carlos Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class Partner(models.Model): + _inherit = "res.partner" + + subscription_ids = fields.One2many( + comodel_name="sale.subscription", + inverse_name="partner_id", + string="Subscriptions", + ) + subscription_count = fields.Integer( + required=False, + compute="_compute_subscription_count", + ) + + def _compute_subscription_count(self): + for record in self: + record.subscription_count = len(record.subscription_ids) + + def action_view_subscription_ids(self): + return { + "type": "ir.actions.act_window", + "res_model": "sale.subscription", + "domain": [("id", "in", self.subscription_ids.ids)], + "name": self.name, + "view_mode": "tree,form", + "context": { + "default_partner_id": self.id, + }, + } diff --git a/subscription_oca/models/sale_order.py b/subscription_oca/models/sale_order.py new file mode 100644 index 0000000000..0aabac0cb8 --- /dev/null +++ b/subscription_oca/models/sale_order.py @@ -0,0 +1,83 @@ +# Copyright 2023 Domatix - Carlos Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from collections import defaultdict +from datetime import date + +from dateutil.relativedelta import relativedelta + +from odoo import api, fields, models + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + subscription_ids = fields.One2many( + comodel_name="sale.subscription", + inverse_name="sale_order_id", + string="Subscriptions", + ) + subscriptions_count = fields.Integer(compute="_compute_subscriptions_count") + order_subscription_id = fields.Many2one( + comodel_name="sale.subscription", string="Subscription" + ) + + @api.depends("subscription_ids") + def _compute_subscriptions_count(self): + for record in self: + record.subscriptions_count = len(record.subscription_ids) + + def action_view_subscriptions(self): + return { + "type": "ir.actions.act_window", + "res_model": "sale.subscription", + "domain": [("id", "in", self.subscription_ids.ids)], + "name": self.name, + "view_mode": "tree,form", + } + + def get_next_interval(self, type_interval, interval): + date_start = date.today() + date_start += relativedelta(**{type_interval: interval}) + return date_start + + def create_subscription(self, lines, subscription_tmpl): + subscription_lines = [] + for line in lines: + subscription_lines.append((0, 0, line.get_subscription_line_values())) + + if subscription_tmpl: + rec = self.env["sale.subscription"].create( + { + "partner_id": self.partner_id.id, + "user_id": self._context["uid"], + "template_id": subscription_tmpl.id, + "pricelist_id": self.partner_id.property_product_pricelist.id, + "date_start": date.today(), + "sale_order_id": self.id, + "sale_subscription_line_ids": subscription_lines, + } + ) + rec.action_start_subscription() + self.subscription_ids = [(4, rec.id)] + rec.recurring_next_date = self.get_next_interval( + subscription_tmpl.recurring_rule_type, + subscription_tmpl.recurring_interval, + ) + + def group_subscription_lines(self): + grouped = defaultdict(list) + for order_line in self.order_line.filtered( + lambda line: line.product_id.subscribable + ): + grouped[ + order_line.product_id.product_tmpl_id.subscription_template_id + ].append(order_line) + return grouped + + def action_confirm(self): + res = super(SaleOrder, self).action_confirm() + for record in self: + grouped = self.group_subscription_lines() + for tmpl, lines in grouped.items(): + record.create_subscription(lines, tmpl) + return res diff --git a/subscription_oca/models/sale_order_line.py b/subscription_oca/models/sale_order_line.py new file mode 100644 index 0000000000..b843e48f7f --- /dev/null +++ b/subscription_oca/models/sale_order_line.py @@ -0,0 +1,17 @@ +# Copyright 2023 Domatix - Carlos Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import models + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + def get_subscription_line_values(self): + return { + "product_id": self.product_id.id, + "name": self.product_id.name, + "product_uom_qty": self.product_uom_qty, + "price_unit": self.price_unit, + "discount": self.discount, + "price_subtotal": self.price_subtotal, + } diff --git a/subscription_oca/models/sale_subscription.py b/subscription_oca/models/sale_subscription.py new file mode 100644 index 0000000000..4cc562ee49 --- /dev/null +++ b/subscription_oca/models/sale_subscription.py @@ -0,0 +1,470 @@ +# Copyright 2023 Domatix - Carlos Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import logging +from datetime import date, datetime + +from dateutil.relativedelta import relativedelta + +from odoo import _, api, fields, models +from odoo.exceptions import AccessError + +logger = logging.getLogger(__name__) + + +class SaleSubscription(models.Model): + _name = "sale.subscription" + _description = "Subscription" + _inherit = ["mail.thread", "mail.activity.mixin"] + _order = "id desc" + + color = fields.Integer("Color Index") + name = fields.Char( + compute="_compute_name", + store=True, + ) + sequence = fields.Integer() + company_id = fields.Many2one( + "res.company", + "Company", + required=True, + index=True, + default=lambda self: self.env.company, + ) + partner_id = fields.Many2one( + comodel_name="res.partner", required=True, string="Partner", index=True + ) + fiscal_position_id = fields.Many2one( + "account.fiscal.position", + string="Fiscal Position", + domain="[('company_id', '=', company_id)]", + check_company=True, + ) + active = fields.Boolean(default=True) + template_id = fields.Many2one( + comodel_name="sale.subscription.template", + required=True, + string="Subscription template", + ) + code = fields.Char( + string="Reference", + default=lambda self: self.env["ir.sequence"].next_by_code("sale.subscription"), + ) + in_progress = fields.Boolean(string="In progress", default=False) + recurring_rule_boundary = fields.Boolean( + string="Boundary", compute="_compute_rule_boundary", store=True + ) + pricelist_id = fields.Many2one( + comodel_name="product.pricelist", required=True, string="Pricelist" + ) + recurring_next_date = fields.Date(string="Next invoice date", default=date.today()) + user_id = fields.Many2one( + comodel_name="res.users", + string="Commercial agent", + default=lambda self: self.env.user.id, + ) + date_start = fields.Date(string="Start date", default=date.today()) + date = fields.Date( + string="Finish date", + compute="_compute_rule_boundary", + store=True, + readonly=False, + ) + description = fields.Text() + sale_order_id = fields.Many2one( + comodel_name="sale.order", string="Origin sale order" + ) + terms = fields.Text( + string="Terms and conditions", + compute="_compute_terms", + store=True, + readonly=False, + ) + invoice_ids = fields.One2many( + comodel_name="account.move", + inverse_name="subscription_id", + string="Invoices", + ) + sale_order_ids = fields.One2many( + comodel_name="sale.order", + inverse_name="order_subscription_id", + string="Orders", + ) + recurring_total = fields.Monetary( + compute="_compute_total", string="Recurring price", store=True + ) + amount_tax = fields.Monetary(compute="_compute_total", store=True) + amount_total = fields.Monetary(compute="_compute_total", store=True) + tag_ids = fields.Many2many(comodel_name="sale.subscription.tag", string="Tags") + image = fields.Binary("Image", related="user_id.image_512", store=True) + journal_id = fields.Many2one(comodel_name="account.journal", string="Journal") + currency_id = fields.Many2one( + related="pricelist_id.currency_id", + depends=["pricelist_id"], + store=True, + ondelete="restrict", + ) + + @api.model + def _read_group_stage_ids(self, stages, domain, order): + stage_ids = stages.search([], order=order) + return stage_ids + + stage_id = fields.Many2one( + comodel_name="sale.subscription.stage", + string="Stage", + tracking=True, + group_expand="_read_group_stage_ids", + store="true", + ) + stage_str = fields.Char( + related="stage_id.name", + string="Etapa", + store=True, + ) + sale_subscription_line_ids = fields.One2many( + comodel_name="sale.subscription.line", + inverse_name="sale_subscription_id", + ) + sale_order_ids_count = fields.Integer( + compute="_compute_sale_order_ids_count", string="Sale orders" + ) + account_invoice_ids_count = fields.Integer( + compute="_compute_account_invoice_ids_count", string="Invoice Count" + ) + close_reason_id = fields.Many2one( + comodel_name="sale.subscription.close.reason", string="Close Reason" + ) + crm_team_id = fields.Many2one(comodel_name="crm.team", string="Sale team") + to_renew = fields.Boolean(default=False, string="To renew") + + def cron_subscription_management(self): + today = date.today() + for subscription in self.search([]): + if subscription.in_progress: + if ( + subscription.recurring_next_date == today + and subscription.sale_subscription_line_ids + ): + try: + subscription.generate_invoice() + except Exception: + logger.exception("Error on subscription invoice generate") + if not subscription.recurring_rule_boundary: + if subscription.date == today: + subscription.action_close_subscription() + + else: + if subscription.date_start == today: + subscription.action_start_subscription() + subscription.generate_invoice() + + @api.depends("sale_subscription_line_ids") + def _compute_total(self): + for record in self: + recurring_total = amount_tax = 0.0 + for order_line in record.sale_subscription_line_ids: + recurring_total += order_line.price_subtotal + amount_tax += order_line.amount_tax_line_amount + record.update( + { + "recurring_total": recurring_total, + "amount_tax": amount_tax, + "amount_total": recurring_total + amount_tax, + } + ) + + @api.depends("template_id", "code") + def _compute_name(self): + for record in self: + template_code = record.template_id.code if record.template_id.code else "" + code = record.code if record.code else "" + slash = "/" if template_code and code else "" + record.name = "{}{}{}".format(template_code, slash, code) + + @api.depends("template_id", "date_start") + def _compute_rule_boundary(self): + for record in self: + if record.template_id.recurring_rule_boundary == "unlimited": + record.date = False + record.recurring_rule_boundary = True + else: + record.date = ( + relativedelta(months=+record.template_id.recurring_rule_count) + + record.date_start + ) + record.recurring_rule_boundary = False + + @api.depends("template_id") + def _compute_terms(self): + for record in self: + record.terms = record.template_id.description + + @api.onchange("template_id", "date_start") + def _onchange_template_id(self): + today = date.today() + if self.date_start: + today = self.date_start + if self.template_id and self.account_invoice_ids_count > 0: + self.calculate_recurring_next_date(self.recurring_next_date) + else: + self.calculate_recurring_next_date(today) + + def calculate_recurring_next_date(self, start_date): + if self.account_invoice_ids_count == 0: + self.recurring_next_date = date.today() + else: + type_interval = self.template_id.recurring_rule_type + interval = int(self.template_id.recurring_interval) + self.recurring_next_date = start_date + relativedelta( + **{type_interval: interval} + ) + + @api.onchange("partner_id") + def onchange_partner_id(self): + self.pricelist_id = self.partner_id.property_product_pricelist + + @api.onchange("partner_id", "company_id") + def onchange_partner_id_fpos(self): + self.fiscal_position_id = ( + self.env["account.fiscal.position"] + .with_company(self.company_id) + .get_fiscal_position(self.partner_id.id) + ) + + def action_start_subscription(self): + self.close_reason_id = False + in_progress_stage = self.env["sale.subscription.stage"].search( + [("type", "=", "in_progress")], limit=1 + ) + self.stage_id = in_progress_stage + + def action_close_subscription(self): + self.recurring_next_date = False + return { + "view_type": "form", + "view_mode": "form", + "res_model": "close.reason.wizard", + "type": "ir.actions.act_window", + "target": "new", + "res_id": False, + } + + def _prepare_sale_order(self, line_ids=False): + self.ensure_one() + return { + "partner_id": self.partner_id.id, + "fiscal_position_id": self.fiscal_position_id.id, + "date_order": datetime.now(), + "payment_term_id": self.partner_id.property_payment_term_id.id, + "user_id": self.user_id.id, + "origin": self.name, + "order_line": line_ids, + } + + def _prepare_account_move(self, line_ids): + self.ensure_one() + values = { + "partner_id": self.partner_id.id, + "invoice_date": self.recurring_next_date, + "invoice_payment_term_id": self.partner_id.property_payment_term_id.id, + "invoice_origin": self.name, + "invoice_user_id": self.user_id.id, + "partner_bank_id": self.company_id.partner_id.bank_ids[:1].id, + "invoice_line_ids": line_ids, + } + if self.journal_id: + values["journal_id"] = self.journal_id.id + return values + + def create_invoice(self): + if not self.env["account.move"].check_access_rights("create", False): + try: + self.check_access_rights("write") + self.check_access_rule("write") + except AccessError: + return self.env["account.move"] + line_ids = [] + for line in self.sale_subscription_line_ids: + line_values = line._prepare_account_move_line() + line_ids.append((0, 0, line_values)) + invoice_values = self._prepare_account_move(line_ids) + invoice_id = ( + self.env["account.move"] + .sudo() + .with_context(default_move_type="out_invoice", journal_type="sale") + .create(invoice_values) + ) + self.write({"invoice_ids": [(4, invoice_id.id)]}) + return invoice_id + + def create_sale_order(self): + if not self.env["sale.order"].check_access_rights("create", False): + try: + self.check_access_rights("write") + self.check_access_rule("write") + except AccessError: + return self.env["sale.order"] + line_ids = [] + for line in self.sale_subscription_line_ids: + line_values = line._prepare_sale_order_line() + line_ids.append((0, 0, line_values)) + values = self._prepare_sale_order(line_ids) + order_id = self.env["sale.order"].sudo().create(values) + self.write({"sale_order_ids": [(4, order_id.id)]}) + return order_id + + def generate_invoice(self): + invoice_number = "" + msg_static = _("Created invoice with reference") + if self.template_id.invoicing_mode in ["draft", "invoice", "invoice_send"]: + invoice = self.create_invoice() + if self.template_id.invoicing_mode != "draft": + invoice.action_post() + if self.template_id.invoicing_mode == "invoice_send": + mail_template = self.template_id.invoice_mail_template_id + invoice.with_context(force_send=True).message_post_with_template( + mail_template.id, + composition_mode="comment", + email_layout_xmlid="mail.mail_notification_paynow", + ) + invoice_number = invoice.name + message_body = ( + "%s %s" + % (msg_static, invoice.id, invoice_number) + ) + + if self.template_id.invoicing_mode == "sale_and_invoice": + order_id = self.create_sale_order() + order_id.action_done() + new_invoice = order_id._create_invoices() + new_invoice.action_post() + new_invoice.invoice_origin = order_id.name + ", " + self.name + invoice_number = new_invoice.name + message_body = ( + "%s %s" + % (msg_static, new_invoice.id, invoice_number) + ) + if not invoice_number: + invoice_number = _("To validate") + message_body = "%s %s" % (msg_static, invoice_number) + self.calculate_recurring_next_date(self.recurring_next_date) + self.message_post(body=message_body) + + def manual_invoice(self): + invoice_id = self.create_invoice() + self.calculate_recurring_next_date(self.recurring_next_date) + context = dict(self.env.context) + context["form_view_initial_mode"] = "edit" + return { + "name": self.name, + "views": [ + (self.env.ref("account.view_move_form").id, "form"), + (self.env.ref("account.view_move_tree").id, "tree"), + ], + "view_type": "form", + "view_mode": "form", + "res_model": "account.move", + "res_id": invoice_id.id, + "type": "ir.actions.act_window", + "context": context, + } + + @api.depends("invoice_ids", "sale_order_ids.invoice_ids") + def _compute_account_invoice_ids_count(self): + for record in self: + record.account_invoice_ids_count = len(self.invoice_ids) + len( + self.sale_order_ids.invoice_ids + ) + + def action_view_account_invoice_ids(self): + return { + "name": self.name, + "views": [ + (self.env.ref("account.view_move_tree").id, "tree"), + (self.env.ref("account.view_move_form").id, "form"), + ], + "view_type": "form", + "view_mode": "tree,form", + "res_model": "account.move", + "type": "ir.actions.act_window", + "domain": [ + ("id", "in", self.invoice_ids.ids + self.sale_order_ids.invoice_ids.ids) + ], + "context": self.env.context, + } + + def _compute_sale_order_ids_count(self): + data = self.env["sale.order"].read_group( + domain=[("order_subscription_id", "in", self.ids)], + fields=["order_subscription_id"], + groupby=["order_subscription_id"], + ) + count_dict = { + item["order_subscription_id"][0]: item["order_subscription_id_count"] + for item in data + } + for record in self: + record.sale_order_ids_count = count_dict.get(record.id, 0) + + def action_view_sale_order_ids(self): + active_ids = self.sale_order_ids.ids + return { + "name": self.name, + "view_type": "form", + "view_mode": "tree,form", + "res_model": "sale.order", + "type": "ir.actions.act_window", + "domain": [("id", "in", active_ids)], + "context": self.env.context, + } + + def _check_dates(self, start, next_invoice): + if start and next_invoice: + date_start = start + date_next_invoice = next_invoice + if not isinstance(date_start, date) and not isinstance( + date_next_invoice, date + ): + date_start = fields.Date.to_date(start) + date_next_invoice = fields.Date.to_date(next_invoice) + if date_start > date_next_invoice: + return True + return False + + def write(self, values): + res = super().write(values) + if "stage_id" in values: + for record in self: + if record.stage_id: + if record.stage_id.type == "in_progress": + record.in_progress = True + record.date_start = date.today() + elif record.stage_id.type == "post": + record.close_reason_id = False + record.in_progress = False + else: + record.in_progress = False + + return res + + @api.model + def create(self, values): + if "recurring_rule_boundary" in values: + if not values["recurring_rule_boundary"]: + template_id = self.env["sale.subscription.template"].search( + [("id", "=", values["template_id"])] + ) + date_start = values["date_start"] + if not isinstance(values["date_start"], date): + date_start = fields.Date.to_date(values["date_start"]) + values["date"] = template_id._get_date(date_start) + if "date_start" in values and "recurring_next_date" in values: + res = self._check_dates(values["date_start"], values["recurring_next_date"]) + if res: + values["date_start"] = values["recurring_next_date"] + values["stage_id"] = ( + self.env["sale.subscription.stage"] + .search([("type", "=", "pre")], order="sequence desc")[-1] + .id + ) + return super(SaleSubscription, self).create(values) diff --git a/subscription_oca/models/sale_subscription_close_reason.py b/subscription_oca/models/sale_subscription_close_reason.py new file mode 100644 index 0000000000..36107a0296 --- /dev/null +++ b/subscription_oca/models/sale_subscription_close_reason.py @@ -0,0 +1,10 @@ +# Copyright 2023 Domatix - Carlos Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class SaleSubscriptionCloseReason(models.Model): + _name = "sale.subscription.close.reason" + _description = "Close reason model" + + name = fields.Char(required=True) diff --git a/subscription_oca/models/sale_subscription_line.py b/subscription_oca/models/sale_subscription_line.py new file mode 100644 index 0000000000..b90792d1b5 --- /dev/null +++ b/subscription_oca/models/sale_subscription_line.py @@ -0,0 +1,322 @@ +# 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.tools.misc import get_lang + + +class SaleSubscriptionLine(models.Model): + _name = "sale.subscription.line" + _description = "Subscription lines added to a given subscription" + + product_id = fields.Many2one( + comodel_name="product.product", + domain=[("sale_ok", "=", True)], + string="Product", + ) + currency_id = fields.Many2one( + "res.currency", + related="sale_subscription_id.currency_id", + store=True, + readonly=True, + ) + name = fields.Char( + string="Description", compute="_compute_name", store=True, readonly=False + ) + product_uom_qty = fields.Float(default=1.0, string="Quantity") + price_unit = fields.Float( + string="Unit price", compute="_compute_price_unit", store=True, readonly=False + ) + discount = fields.Float( + string="Discount (%)", compute="_compute_discount", store=True, readonly=False + ) + tax_ids = fields.Many2many( + comodel_name="account.tax", + relation="subscription_line_tax", + column1="subscription_line_id", + column2="tax_id", + string="Taxes", + compute="_compute_tax_ids", + store=True, + readonly=False, + ) + + @api.depends("product_id", "price_unit", "product_uom_qty", "discount", "tax_ids") + def _compute_subtotal(self): + for record in self: + price = record.price_unit * (1 - (record.discount or 0.0) / 100.0) + taxes = record.tax_ids.compute_all( + price, + record.currency_id, + record.product_uom_qty, + product=record.product_id, + partner=record.sale_subscription_id.partner_id, + ) + record.update( + { + "amount_tax_line_amount": sum( + t.get("amount", 0.0) for t in taxes.get("taxes", []) + ), + "price_total": taxes["total_included"], + "price_subtotal": taxes["total_excluded"], + } + ) + + price_subtotal = fields.Monetary( + string="Subtotal", readonly="True", compute=_compute_subtotal, store=True + ) + price_total = fields.Monetary( + string="Total", readonly="True", compute=_compute_subtotal, store=True + ) + amount_tax_line_amount = fields.Float( + string="Taxes Amount", compute="_compute_subtotal", store=True + ) + sale_subscription_id = fields.Many2one( + comodel_name="sale.subscription", string="Subscription" + ) + company_id = fields.Many2one( + related="sale_subscription_id.company_id", + string="Company", + store=True, + index=True, + ) + + @api.depends("product_id") + def _compute_name(self): + for record in self: + if not record.product_id: + record.name = False + lang = get_lang(self.env, record.sale_subscription_id.partner_id.lang).code + product = record.product_id.with_context(lang=lang) + record.name = product.with_context( + lang=lang + ).get_product_multiline_description_sale() + + @api.depends("product_id", "sale_subscription_id.fiscal_position_id") + def _compute_tax_ids(self): + for line in self: + fpos = ( + line.sale_subscription_id.fiscal_position_id + or line.sale_subscription_id.fiscal_position_id.get_fiscal_position( + line.sale_subscription_id.partner_id.id + ) + ) + # If company_id is set, always filter taxes by the company + taxes = line.product_id.taxes_id.filtered( + lambda t: t.company_id == line.env.company + ) + line.tax_ids = fpos.map_tax(taxes) + + @api.depends( + "product_id", + "sale_subscription_id.partner_id", + "sale_subscription_id.pricelist_id", + ) + def _compute_price_unit(self): + for record in self: + if not record.product_id: + continue + if ( + record.sale_subscription_id.pricelist_id + and record.sale_subscription_id.partner_id + ): + product = record.product_id.with_context( + partner=record.sale_subscription_id.partner_id, + quantity=record.product_uom_qty, + date=fields.datetime.now(), + pricelist=record.sale_subscription_id.pricelist_id.id, + uom=record.product_id.uom_id.id, + ) + record.price_unit = product._get_tax_included_unit_price( + record.company_id, + record.sale_subscription_id.currency_id, + fields.datetime.now(), + "sale", + fiscal_position=record.sale_subscription_id.fiscal_position_id, + product_price_unit=record._get_display_price(product), + product_currency=record.sale_subscription_id.currency_id, + ) + + @api.depends( + "product_id", + "price_unit", + "product_uom_qty", + "tax_ids", + "sale_subscription_id.partner_id", + "sale_subscription_id.pricelist_id", + ) + def _compute_discount(self): + for record in self: + if not ( + record.product_id + and record.product_id.uom_id + and record.sale_subscription_id.partner_id + and record.sale_subscription_id.pricelist_id + and record.sale_subscription_id.pricelist_id.discount_policy + == "without_discount" + and self.env.user.has_group("product.group_discount_per_so_line") + ): + record.discount = 0.0 + continue + + record.discount = 0.0 + product = record.product_id.with_context( + lang=record.sale_subscription_id.partner_id.lang, + partner=record.sale_subscription_id.partner_id, + quantity=record.product_uom_qty, + date=fields.Datetime.now(), + pricelist=record.sale_subscription_id.pricelist_id.id, + uom=record.product_id.uom_id.id, + fiscal_position=record.sale_subscription_id.fiscal_position_id + or self.env.context.get("fiscal_position"), + ) + + price, rule_id = record.sale_subscription_id.pricelist_id.with_context( + partner_id=record.sale_subscription_id.partner_id.id, + date=fields.Datetime.now(), + uom=record.product_id.uom_id.id, + ).get_product_price_rule( + record.product_id, + record.product_uom_qty or 1.0, + record.sale_subscription_id.partner_id, + ) + new_list_price, currency = record.with_context( + partner_id=record.sale_subscription_id.partner_id.id, + date=fields.Datetime.now(), + uom=record.product_id.uom_id.id, + )._get_real_price_currency( + product, rule_id, record.product_uom_qty, record.product_id.uom_id + ) + + if new_list_price != 0: + if record.sale_subscription_id.pricelist_id.currency_id != currency: + new_list_price = currency._convert( + new_list_price, + record.sale_subscription_id.pricelist_id.currency_id, + record.sale_subscription_id.company_id or self.env.company, + fields.Date.today(), + ) + discount = (new_list_price - price) / new_list_price * 100 + if (discount > 0 and new_list_price > 0) or ( + discount < 0 and new_list_price < 0 + ): + record.discount = discount + + def _get_real_price_currency(self, product, rule_id, qty, uom): + PricelistItem = self.env["product.pricelist.item"] + field_name = "lst_price" + currency_id = None + product_currency = product.currency_id + if rule_id: + pricelist_item = PricelistItem.browse(rule_id) + if pricelist_item.pricelist_id.discount_policy == "without_discount": + while ( + pricelist_item.base == "pricelist" + and pricelist_item.base_pricelist_id + and pricelist_item.base_pricelist_id.discount_policy + == "without_discount" + ): + _price, rule_id = pricelist_item.base_pricelist_id.with_context( + uom=uom.id + ).get_product_price_rule( + product, qty, self.sale_subscription_id.partner_id + ) + pricelist_item = PricelistItem.browse(rule_id) + + if pricelist_item.base == "standard_price": + field_name = "standard_price" + product_currency = product.cost_currency_id + elif ( + pricelist_item.base == "pricelist" and pricelist_item.base_pricelist_id + ): + field_name = "price" + product = product.with_context( + pricelist=pricelist_item.base_pricelist_id.id + ) + product_currency = pricelist_item.base_pricelist_id.currency_id + currency_id = pricelist_item.pricelist_id.currency_id + + if not currency_id: + currency_id = product_currency + cur_factor = 1.0 + else: + if currency_id.id == product_currency.id: + cur_factor = 1.0 + else: + cur_factor = currency_id._get_conversion_rate( + product_currency, + currency_id, + self.company_id or self.env.company, + fields.Date.today(), + ) + + product_uom = self.env.context.get("uom") or product.uom_id.id + if uom and uom.id != product_uom: + # the unit price is in a different uom + uom_factor = uom._compute_price(1.0, product.uom_id) + else: + uom_factor = 1.0 + + return product[field_name] * uom_factor * cur_factor, currency_id + + def _get_display_price(self, product): + if self.sale_subscription_id.pricelist_id.discount_policy == "with_discount": + return product.with_context( + pricelist=self.sale_subscription_id.pricelist_id.id, + uom=self.product_id.uom_id.id, + ).price + + final_price, rule_id = self.sale_subscription_id.pricelist_id.with_context( + partner_id=self.sale_subscription_id.partner_id.id, + date=fields.Datetime.now(), + uom=self.product_id.uom_id.id, + ).get_product_price_rule( + product or self.product_id, + self.product_uom_qty or 1.0, + self.sale_subscription_id.partner_id, + ) + base_price, currency = self.with_context( + partner_id=self.sale_subscription_id.partner_id.id, + date=fields.Datetime.now(), + uom=self.product_id.uom_id.id, + )._get_real_price_currency( + product, rule_id, self.product_uom_qty, self.product_id.uom_id + ) + if currency != self.sale_subscription_id.pricelist_id.currency_id: + base_price = currency._convert( + base_price, + self.sale_subscription_id.pricelist_id.currency_id, + self.sale_subscription_id.company_id or self.env.company, + fields.Date.today(), + ) + return max(base_price, final_price) + + def _prepare_sale_order_line(self): + self.ensure_one() + return { + "product_id": self.product_id.id, + "name": self.name, + "product_uom_qty": self.product_uom_qty, + "price_unit": self.price_unit, + "discount": self.discount, + "price_subtotal": self.price_subtotal, + "tax_id": self.tax_ids, + "product_uom": self.product_id.uom_id.id, + } + + def _prepare_account_move_line(self): + self.ensure_one() + account = ( + self.product_id.property_account_income_id + or self.product_id.categ_id.property_account_income_categ_id + ) + return { + "product_id": self.product_id.id, + "name": self.name, + "quantity": self.product_uom_qty, + "price_unit": self.price_unit, + "discount": self.discount, + "price_subtotal": self.price_subtotal, + "tax_ids": [(6, 0, self.tax_ids.ids)], + "product_uom_id": self.product_id.uom_id.id, + "account_id": account.id, + } diff --git a/subscription_oca/models/sale_subscription_stage.py b/subscription_oca/models/sale_subscription_stage.py new file mode 100644 index 0000000000..ee6b6bb460 --- /dev/null +++ b/subscription_oca/models/sale_subscription_stage.py @@ -0,0 +1,29 @@ +# 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.exceptions import ValidationError + + +class SaleSubscriptionStage(models.Model): + _name = "sale.subscription.stage" + _description = "Subscription stage" + _order = "sequence, name, id" + + name = fields.Char(required=True, translate=True) + sequence = fields.Integer() + display_name = fields.Char(string="Display name") + in_progress = fields.Boolean(string="In progress", default=False) + fold = fields.Boolean(string="Kanban folded") + description = fields.Text(translate=True) + type = fields.Selection( + [("pre", "Ready to start"), ("in_progress", "In progress"), ("post", "Closed")], + default="pre", + ) + + @api.constrains("type") + def _check_lot_product(self): + post_stages = self.env["sale.subscription.stage"].search( + [("type", "=", "post")] + ) + if len(post_stages) > 1: + raise ValidationError(_("There is already a Closed-type stage declared")) diff --git a/subscription_oca/models/sale_subscription_tag.py b/subscription_oca/models/sale_subscription_tag.py new file mode 100644 index 0000000000..19101a160f --- /dev/null +++ b/subscription_oca/models/sale_subscription_tag.py @@ -0,0 +1,10 @@ +# Copyright 2023 Domatix - Carlos Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class SaleSubscriptionTag(models.Model): + _name = "sale.subscription.tag" + _description = "Tags for sale subscription" + + name = fields.Char("Tag name", required=True) diff --git a/subscription_oca/models/sale_subscription_template.py b/subscription_oca/models/sale_subscription_template.py new file mode 100644 index 0000000000..df89f401b0 --- /dev/null +++ b/subscription_oca/models/sale_subscription_template.py @@ -0,0 +1,102 @@ +# Copyright 2023 Domatix - Carlos Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from dateutil.relativedelta import relativedelta + +from odoo import api, fields, models + + +class SaleSubscriptionTemplate(models.Model): + _name = "sale.subscription.template" + _description = "Subscription templates" + + name = fields.Char(required=True) + description = fields.Text(string="Terms and conditions") + recurring_interval = fields.Integer(string="Repeat every", default=1) + recurring_rule_type = fields.Selection( + [ + ("days", "Day(s)"), + ("weeks", "Week(s)"), + ("months", "Month(s)"), + ("years", "Year(s)"), + ], + string="Recurrence", + default="months", + ) + recurring_rule_boundary = fields.Selection( + [("unlimited", "Forever"), ("limited", "Fixed")], + string="Duration", + default="unlimited", + ) + invoicing_mode = fields.Selection( + default="draft", + string="Invoicing mode", + selection=[ + ("draft", "Draft"), + ("invoice", "Invoice"), + ("invoice_send", "Invoice & send"), + ("sale_and_invoice", "Sale order & Invoice"), + ], + ) + code = fields.Char() + recurring_rule_count = fields.Integer(default=1, string="Rule count") + invoice_mail_template_id = fields.Many2one( + comodel_name="mail.template", + string="Invoice Email", + domain="[('model', '=', 'account.move')]", + ) + product_ids = fields.One2many( + comodel_name="product.template", + inverse_name="subscription_template_id", + string="Products", + ) + product_ids_count = fields.Integer( + compute="_compute_product_ids_count", string="product_ids" + ) + subscription_ids = fields.One2many( + comodel_name="sale.subscription", + inverse_name="template_id", + string="Subscriptions", + ) + subscription_count = fields.Integer( + compute="_compute_subscription_count", string="subscription_ids" + ) + + def _compute_subscription_count(self): + data = self.env["sale.subscription"].read_group( + domain=[("template_id", "in", self.ids)], + fields=["template_id"], + groupby=["template_id"], + ) + count_dict = { + item["template_id"][0]: item["template_id_count"] for item in data + } + for record in self: + record.subscription_count = count_dict.get(record.id, 0) + + def action_view_subscription_ids(self): + return { + "name": self.name, + "view_mode": "tree,form", + "res_model": "sale.subscription", + "type": "ir.actions.act_window", + "domain": [("id", "in", self.subscription_ids.ids)], + } + + def _get_date(self, date_start): + self.ensure_one() + return relativedelta(months=+self.recurring_rule_count) + date_start + + @api.depends("product_ids") + def _compute_product_ids_count(self): + for record in self: + record.product_ids_count = len(self.product_ids) + + def action_view_product_ids(self): + return { + "name": self.name, + "view_type": "form", + "view_mode": "tree,form", + "res_model": "product.template", + "type": "ir.actions.act_window", + "domain": [("id", "in", self.product_ids.ids)], + } diff --git a/subscription_oca/readme/CONTRIBUTORS.rst b/subscription_oca/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..e4139ac7d2 --- /dev/null +++ b/subscription_oca/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Carlos Martínez diff --git a/subscription_oca/readme/DESCRIPTION.rst b/subscription_oca/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..648d69835f --- /dev/null +++ b/subscription_oca/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module allows creating subscriptions that generate recurring invoices or orders. It also enables the sale of products that generate subscriptions. diff --git a/subscription_oca/readme/ROADMAP.rst b/subscription_oca/readme/ROADMAP.rst new file mode 100644 index 0000000000..c6355d44b0 --- /dev/null +++ b/subscription_oca/readme/ROADMAP.rst @@ -0,0 +1,2 @@ +* Refactor all the onchanges that have business logic to computed write-able fields when possible. Keep onchanges only for UI purposes. +* Add tests. diff --git a/subscription_oca/readme/USAGE.rst b/subscription_oca/readme/USAGE.rst new file mode 100644 index 0000000000..4c5a5ed85a --- /dev/null +++ b/subscription_oca/readme/USAGE.rst @@ -0,0 +1,13 @@ +To make a subscription: + +#. Go to *Subscriptions > Configuration > Subscription templates*. +#. Create the templates you consider, choosing the billing frequency: daily, monthly... and the method of creating the invoice and/or order. +#. Go to *Subscription > Subscriptions*. +#. Create a subscription and indicate the start date. When the *Subscriptions Management* cron job is executed, the subscription will begin and the first invoice will be created if the execution date matches the start date. The invoice will also be created when the execution date matches the next invoice date. Additionally, you can manually change the subscription status and create an invoice. +#. The cron job will also end the subscription if its end date has been reached. + +To create subscriptions with the sale of a product: + +#. Go to *Subscriptions > Subscriptions > Products*. +#. Create the product and in the sales tab, complete the fields *Subscribable product* and *Subscription template* +#. Create a sales order with the product and confirm it. diff --git a/subscription_oca/security/ir.model.access.csv b/subscription_oca/security/ir.model.access.csv new file mode 100644 index 0000000000..cd0f7dba90 --- /dev/null +++ b/subscription_oca/security/ir.model.access.csv @@ -0,0 +1,8 @@ +id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink +access_custom_sale_subscription_template,sale.subscription.template,model_sale_subscription_template,sales_team.group_sale_salesman,1,1,1,1 +access_custom_sale_subscription,sale.subscription,model_sale_subscription,sales_team.group_sale_salesman,1,1,1,1 +access_custom_sale_subscription_close_reason,sale.subscription.close.reason,model_sale_subscription_close_reason,sales_team.group_sale_salesman,1,1,1,1 +access_custom_sale_subscription_stage,sale.subscription.stage,model_sale_subscription_stage,sales_team.group_sale_salesman,1,1,1,1 +access_custom_sale_subscription_line,sale.subscription.line,model_sale_subscription_line,sales_team.group_sale_salesman,1,1,1,1 +access_custom_sale_subscription_tag,sale.subscription.tag,model_sale_subscription_tag,sales_team.group_sale_salesman,1,1,1,1 +access_close_subscription,Close subscription access,model_close_reason_wizard,sales_team.group_sale_salesman,1,1,1,1 diff --git a/subscription_oca/static/description/icon.png b/subscription_oca/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/subscription_oca/static/description/index.html b/subscription_oca/static/description/index.html new file mode 100644 index 0000000000..5e4490677c --- /dev/null +++ b/subscription_oca/static/description/index.html @@ -0,0 +1,439 @@ + + + + + + +Subscription management + + + +
+

Subscription management

+ + +

Beta License: AGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runboat

+

This module allows creating subscriptions that generate recurring invoices or orders. It also enables the sale of products that generate subscriptions.

+

Table of contents

+ +
+

Usage

+

To make a subscription:

+
    +
  1. Go to Subscriptions > Configuration > Subscription templates.
  2. +
  3. Create the templates you consider, choosing the billing frequency: daily, monthly… and the method of creating the invoice and/or order.
  4. +
  5. Go to Subscription > Subscriptions.
  6. +
  7. Create a subscription and indicate the start date. When the Subscriptions Management cron job is executed, the subscription will begin and the first invoice will be created if the execution date matches the start date. The invoice will also be created when the execution date matches the next invoice date. Additionally, you can manually change the subscription status and create an invoice.
  8. +
  9. The cron job will also end the subscription if its end date has been reached.
  10. +
+

To create subscriptions with the sale of a product:

+
    +
  1. Go to Subscriptions > Subscriptions > Products.
  2. +
  3. Create the product and in the sales tab, complete the fields Subscribable product and Subscription template
  4. +
  5. Create a sales order with the product and confirm it.
  6. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Domatix
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/sale-workflow project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/subscription_oca/static/img/icon.png b/subscription_oca/static/img/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bab981dd92b92c32d5d27f100be3ff90bfa3b561 GIT binary patch literal 16096 zcmcIr1ydYNw8hmWR}cAIb8`#!clxHO2MIK+u13;gvaNHg%!QWgFOMv|kXrG;x=TT`G6`X&e> zjB>1QmQAi?s~oMW|9#iVJ%viO^UHv{jr+H^7)95`j-R>YuHx zv}vR(5h!wU^1oZblYQLCfBxS-vG%;6gvTY~@$#{8wk%KDZNOB}aVki)sqd9JXcypp z${u69@5)G`esFK%Z~^}sUVt&&mIE5lW@r0&2_?F*9BjD_gz3?*Fw$k=fHMw5f;~1Ta;k4gEPnc@*()pzF2Ot3kVD@GxM>kswSp_ssKx} z69_eu5(|llNP!CiEoR$Dj&|c#P+g5{KsIr3W-Y;SP*UXAq8Ri`jg@)q2jldZ)nNjI z2z~H0CfVUonU>7&kN8D;EV}yAt5LW#OlWBCGc&x9H)u>#TKE9*u%Aa|scK?`n)9>& zp4Xr!GfS2hKr8p}5p@MbWT&6`=UX6F&bW`)Zi#0M;&L z;Yi;XXN>ROIT|#6=d^Wi!`|XynQxef*O^uku6jlcRCRHQ{iAWr0TX}}p}@@PGYD2p z6D0hh{_PX<-kah_o~78oxSp2S~@Zsgx%WmmFmc1o%CLh zhq&im_?ZKPVl7+C2)!;+#mGXoymI19Y6n@UFan1~*Olons}6EyBju^1GEb|Zvgu|v zD}Rms2VrsxEuUd?)44?=l`m%gvcss^81zBY@L*1Y>KFKa9P{3fv_yTge$R9<&SA26z?F`*z7;^KOM6DN1NtI zp;m^aUIJ1zvf1sp8XO72YAv%QFP?0nGjFrxiRgOYgU+-RO#Q_wfu++~NJ=+wRA<

Ui*J$|ByCo)v~>D%$XS<*DoC_r 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 MPD)9#3TzDdAGpB)Gynhq literal 0 HcmV?d00001 diff --git a/subscription_oca/views/product_template_views.xml b/subscription_oca/views/product_template_views.xml new file mode 100644 index 0000000000..0ef00a86bc --- /dev/null +++ b/subscription_oca/views/product_template_views.xml @@ -0,0 +1,20 @@ + + + + product.template.sub.form + product.template + + + + + + + + + + + + diff --git a/subscription_oca/views/res_partner_views.xml b/subscription_oca/views/res_partner_views.xml new file mode 100644 index 0000000000..3be7a6690a --- /dev/null +++ b/subscription_oca/views/res_partner_views.xml @@ -0,0 +1,28 @@ + + + + + res.partner.form + res.partner + + + + + + + diff --git a/subscription_oca/views/sale_order_views.xml b/subscription_oca/views/sale_order_views.xml new file mode 100644 index 0000000000..674776bf1c --- /dev/null +++ b/subscription_oca/views/sale_order_views.xml @@ -0,0 +1,29 @@ + + + + view.sale_order.form + sale.order + + + + +

+ +
+ + + + + diff --git a/subscription_oca/views/sale_subscription_stage_views.xml b/subscription_oca/views/sale_subscription_stage_views.xml new file mode 100644 index 0000000000..8e6b0bd724 --- /dev/null +++ b/subscription_oca/views/sale_subscription_stage_views.xml @@ -0,0 +1,73 @@ + + + + + view.subscription.stage.form + sale.subscription.stage + +
+ + + + + + + + + + + + + + + +
+
+
+ + + view.subscription.stage.tree + sale.subscription.stage + + + + + + + + + + Susbcription stages + sale.subscription.stage + tree,form + +

+ Click to create a new subscription stage. +

+
+
+ + + +
diff --git a/subscription_oca/views/sale_subscription_tag_views.xml b/subscription_oca/views/sale_subscription_tag_views.xml new file mode 100644 index 0000000000..d5997c98c1 --- /dev/null +++ b/subscription_oca/views/sale_subscription_tag_views.xml @@ -0,0 +1,27 @@ + + + + view.sale.subscription.tag.tree + sale.subscription.tag + + + + + + + + + Tags + sale.subscription.tag + tree + + + + + diff --git a/subscription_oca/views/sale_subscription_template_views.xml b/subscription_oca/views/sale_subscription_template_views.xml new file mode 100644 index 0000000000..d683c55b7b --- /dev/null +++ b/subscription_oca/views/sale_subscription_template_views.xml @@ -0,0 +1,135 @@ + + + + + sale.subscription.template.form + sale.subscription.template + + +
+ + +
+ + +
+
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + sale.subscription.template.tree + sale.subscription.template + + + + + + + + + + + Subscription templates + sale.subscription.template + tree,form + + + +
diff --git a/subscription_oca/views/sale_subscription_views.xml b/subscription_oca/views/sale_subscription_views.xml new file mode 100644 index 0000000000..4279143483 --- /dev/null +++ b/subscription_oca/views/sale_subscription_views.xml @@ -0,0 +1,473 @@ + + + + + sale.subscription.form + sale.subscription + +
+
+
+ + +
+ + + + +
+ +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+
+
+
+ + + sale.subscription.tree + sale.subscription + + + + + + + + + + + + + + + + + + + sale.subscription.kanban + sale.subscription + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + +
+ +
+
+
+ +
+ +
+ +
+ + + , + + + + +
+ +
+
+ +
+
+ + + + + +
+
+ +
+
+
+
+
+
+
+ + + sale.order.pending.filter + sale.subscription + + + + + + + + + + product.suscribable.filter + product.template + + + + + + + + + + view.subscription.close.reason.tree + sale.subscription.close.reason + + + + + + + + + Close reasons + sale.subscription.close.reason + tree + + + + Subscriptions + sale.subscription + tree,kanban,form + + + + Products + product.template + tree,form + + {'search_default_subsproducts': True, "default_type": "service", "default_subscribable": True} + + + + + + + + + + + + + + + +
diff --git a/subscription_oca/wizard/__init__.py b/subscription_oca/wizard/__init__.py new file mode 100644 index 0000000000..a1aca59def --- /dev/null +++ b/subscription_oca/wizard/__init__.py @@ -0,0 +1 @@ +from . import close_subscription_wizard diff --git a/subscription_oca/wizard/close_subscription_wizard.py b/subscription_oca/wizard/close_subscription_wizard.py new file mode 100644 index 0000000000..1f38879e40 --- /dev/null +++ b/subscription_oca/wizard/close_subscription_wizard.py @@ -0,0 +1,25 @@ +# Copyright 2023 Domatix - Carlos Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class CloseSubscriptionWizard(models.TransientModel): + _name = "close.reason.wizard" + _description = "Close reason wizard" + + close_reason_id = fields.Many2one( + comodel_name="sale.subscription.close.reason", string="Reason" + ) + + def button_confirm(self): + sale_subscription = self.env["sale.subscription"].browse( + self.env.context["active_id"] + ) + sale_subscription.close_reason_id = self.close_reason_id.id + stage = sale_subscription.stage_id + closed_stage = self.env["sale.subscription.stage"].search( + [("type", "=", "post")], limit=1 + ) + if stage != closed_stage: + sale_subscription.stage_id = closed_stage + sale_subscription.active = False diff --git a/subscription_oca/wizard/close_subscription_wizard.xml b/subscription_oca/wizard/close_subscription_wizard.xml new file mode 100644 index 0000000000..132a897227 --- /dev/null +++ b/subscription_oca/wizard/close_subscription_wizard.xml @@ -0,0 +1,30 @@ + + + + close.reason.wizard.view + close.reason.wizard + +
+ + + +
+
+
+
+
+ + + Close reason + close.reason.wizard + form + new + +
From 7f3870222f2016eca2cdf3932508048ae55d882c Mon Sep 17 00:00:00 2001 From: Ilyas Date: Tue, 26 Sep 2023 11:58:12 +0200 Subject: [PATCH 02/35] [IMP] subscription_oca: tests --- subscription_oca/readme/CONTRIBUTORS.rst | 5 + subscription_oca/tests/__init__.py | 3 + .../tests/test_subscription_oca.py | 182 ++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 subscription_oca/tests/__init__.py create mode 100644 subscription_oca/tests/test_subscription_oca.py diff --git a/subscription_oca/readme/CONTRIBUTORS.rst b/subscription_oca/readme/CONTRIBUTORS.rst index e4139ac7d2..3324fc4745 100644 --- a/subscription_oca/readme/CONTRIBUTORS.rst +++ b/subscription_oca/readme/CONTRIBUTORS.rst @@ -1 +1,6 @@ * Carlos Martínez + + +* `Ooops404 `__: + + * Ilyas diff --git a/subscription_oca/tests/__init__.py b/subscription_oca/tests/__init__.py new file mode 100644 index 0000000000..f445239d7f --- /dev/null +++ b/subscription_oca/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_subscription_oca diff --git a/subscription_oca/tests/test_subscription_oca.py b/subscription_oca/tests/test_subscription_oca.py new file mode 100644 index 0000000000..65f3b75d3e --- /dev/null +++ b/subscription_oca/tests/test_subscription_oca.py @@ -0,0 +1,182 @@ +# Copyright 2023 ooops404 +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from dateutil.relativedelta import relativedelta + +from odoo import fields +from odoo.tests import TransactionCase + + +class TestSubscriptionOCA(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.pricelist = cls.env["product.pricelist"].create( + {"name": "pricelist for contract test"} + ) + cls.partner = cls.env["res.partner"].create( + { + "name": "partner test subscription_oca", + "property_product_pricelist": cls.pricelist.id, + "email": "demo1@demo.com", + } + ) + cls.partner_2 = cls.env["res.partner"].create( + { + "name": "partner test subscription_oca 2", + "property_product_pricelist": cls.pricelist.id, + "email": "demo2@demo.com", + } + ) + cls.tax_10pc_incl = cls.env["account.tax"].create( + { + "name": "10% Tax incl", + "amount_type": "percent", + "amount": 10, + "price_include": True, + } + ) + cls.product_1 = cls.env.ref("product.product_product_1") + cls.product_1.taxes_id = [(6, 0, cls.tax_10pc_incl.ids)] + cls.product_2 = cls.env.ref("product.product_product_2") + cls.country = cls.env["res.country"].search([], limit=1) + cls.fiscal = cls.env["account.fiscal.position"].create( + { + "name": "Regime National", + "auto_apply": True, + "country_id": cls.country.id, + "vat_required": True, + "sequence": 10, + } + ) + cls.tmpl = cls.env["sale.subscription.template"].create( + { + "name": "Test Template", + "code": "OMG", + "description": "Some sort of subscription terms", + "product_ids": [(6, 0, [cls.product_1.id, cls.product_2.id])], + } + ) + cls.stage = cls.env["sale.subscription.stage"].create( + { + "name": "Test Sub Stage", + } + ) + cls.tag = cls.env["sale.subscription.tag"].create( + { + "name": "Test Tag", + } + ) + cls.sub = cls.env["sale.subscription"].create( + { + "company_id": 1, + "partner_id": cls.partner.id, + "template_id": cls.tmpl.id, + "tag_ids": [(6, 0, [cls.tag.id])], + "stage_id": cls.stage.id, + "pricelist_id": cls.pricelist.id, + "fiscal_position_id": cls.fiscal.id, + } + ) + cls.sub_line = cls.env["sale.subscription.line"].create( + { + "company_id": 1, + "sale_subscription_id": cls.sub.id, + "product_id": cls.product_1.id, + } + ) + cls.close_reason = cls.env["sale.subscription.close.reason"].create( + { + "name": "Test Close Reason", + } + ) + + def test_subscription_oca_sale_order(self): + # SO standard flow + so = self.env["sale.order"].create( + { + "partner_id": self.partner.id, + "partner_invoice_id": self.partner.id, + "partner_shipping_id": self.partner.id, + "order_line": [ + ( + 0, + 0, + { + "name": self.product_1.name, + "product_id": self.product_1.id, + "product_uom_qty": 2, + "product_uom": self.product_1.uom_id.id, + "price_unit": self.product_1.list_price, + }, + ) + ], + } + ) + so._compute_subscriptions_count() + self.assertEqual(so.subscriptions_count, 0) + action = so.action_view_subscriptions() + self.assertIsInstance(action, dict) + so.action_confirm() # without subs. + + def test_subscription_oca_sub_line(self): + # sale.subscription.line + self.assertEqual(self.sub_line.name, self.sub_line.product_id.name) + self.assertIsNotNone(self.sub_line.tax_ids) + self.assertEqual(self.sub_line.price_unit, 30.75) + 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() + self.assertIsInstance(sol_res, dict) + move_res = self.sub_line._prepare_account_move_line() + self.assertIsInstance(move_res, dict) + + def test_subscription_oca_sub_cron(self): + # sale.subscription + self.sub.cron_subscription_management() + # invoice should be created by cron + inv_id = self.env["account.move"].search( + [("subscription_id", "=", self.sub.id)] + ) + self.assertEqual(len(inv_id), 1) + self.assertEqual(self.sub.recurring_total, 27.95) + self.assertEqual(self.sub.amount_total, 30.75) + + def test_subscription_oca_sub_workflow(self): + sale_order = self.sub.create_sale_order() + self.assertTrue(sale_order) + move_id = self.sub.create_invoice() + self.assertTrue(move_id) + res = self.sub.manual_invoice() + self.assertEqual(res["type"], "ir.actions.act_window") + inv_ids = self.env["account.move"].search( + [("subscription_id", "=", self.sub.id)] + ) + self.assertEqual(len(inv_ids), 2) + self.assertEqual(sum(inv_ids.mapped("amount_total")), 2 * 30.75) + self.assertEqual(self.sub.account_invoice_ids_count, 2) + res = self.sub.action_view_account_invoice_ids() + self.assertEqual(res["type"], "ir.actions.act_window") + self.assertEqual(self.sub.sale_order_ids_count, 1) + res = self.sub.action_view_sale_order_ids() + self.assertIn(str(self.sub.sale_order_ids.id), str(res["domain"])) + self.sub.calculate_recurring_next_date(fields.Datetime.now()) + self.assertEqual( + self.sub.recurring_next_date, + fields.Date.today() + relativedelta(months=1), + ) + self.sub.partner_id = self.partner_2 + self.sub.onchange_partner_id() + self.assertEqual( + self.sub.pricelist_id.id, self.partner_2.property_product_pricelist.id + ) + self.sub.onchange_partner_id_fpos() + self.assertFalse(self.sub.fiscal_position_id) + res = self.sub.action_close_subscription() + self.assertEqual(res["type"], "ir.actions.act_window") + + def test_subscription_oca_sub_stage(self): + # sale.subscription.stage + self.stage._check_lot_product() # should not raise From 9aeb865e3f6363d2aa1394325b3b470a50cc3cb3 Mon Sep 17 00:00:00 2001 From: Ilyas Date: Tue, 26 Sep 2023 15:10:04 +0200 Subject: [PATCH 03/35] [IMP] subscription_oca: refactoring --- subscription_oca/README.rst | 7 ++- subscription_oca/__manifest__.py | 2 +- subscription_oca/i18n/subscription_oca.pot | 6 --- subscription_oca/models/sale_subscription.py | 6 +-- .../static/description/index.html | 54 +++++++++++-------- 5 files changed, 43 insertions(+), 32 deletions(-) diff --git a/subscription_oca/README.rst b/subscription_oca/README.rst index 0b03b4bf12..f8a70688ff 100644 --- a/subscription_oca/README.rst +++ b/subscription_oca/README.rst @@ -7,7 +7,7 @@ Subscription management !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:3772d65a58c07d0348bd13d3c882810c94bfb87389c62fec6d16fe8ef130252c + !! source digest: sha256:42fc353409c68ca6defc36ba2273b97ae36edb6f629c0c041db1ef7f1e01ba00 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png @@ -81,6 +81,11 @@ Contributors * Carlos Martínez + +* `Ooops404 `__: + + * Ilyas + Maintainers ~~~~~~~~~~~ diff --git a/subscription_oca/__manifest__.py b/subscription_oca/__manifest__.py index 8e9cb33b82..fe221adda4 100644 --- a/subscription_oca/__manifest__.py +++ b/subscription_oca/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Subscription management", "summary": "Generate recurring invoices.", - "version": "15.0.1.0.0", + "version": "15.0.1.0.1", "development_status": "Beta", "category": "Subscription Management", "website": "https://github.com/OCA/contract", diff --git a/subscription_oca/i18n/subscription_oca.pot b/subscription_oca/i18n/subscription_oca.pot index 60a0262041..0502817f3f 100644 --- a/subscription_oca/i18n/subscription_oca.pot +++ b/subscription_oca/i18n/subscription_oca.pot @@ -363,7 +363,6 @@ msgstr "" #. module: subscription_oca #: model:ir.model.fields,help:subscription_oca.field_sale_subscription__message_has_error -#: model:ir.model.fields,help:subscription_oca.field_sale_subscription__message_has_sms_error msgid "If checked, some messages have a delivery error." msgstr "" @@ -657,11 +656,6 @@ msgstr "" msgid "Rule count" msgstr "" -#. module: subscription_oca -#: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__message_has_sms_error -msgid "SMS Delivery error" -msgstr "" - #. module: subscription_oca #: model:ir.model.fields,field_description:subscription_oca.field_sale_subscription__sale_subscription_line_ids msgid "Sale Subscription Line" diff --git a/subscription_oca/models/sale_subscription.py b/subscription_oca/models/sale_subscription.py index 4cc562ee49..250f97a006 100644 --- a/subscription_oca/models/sale_subscription.py +++ b/subscription_oca/models/sale_subscription.py @@ -451,8 +451,8 @@ def write(self, values): def create(self, values): if "recurring_rule_boundary" in values: if not values["recurring_rule_boundary"]: - template_id = self.env["sale.subscription.template"].search( - [("id", "=", values["template_id"])] + template_id = self.env["sale.subscription.template"].browse( + values["template_id"] ) date_start = values["date_start"] if not isinstance(values["date_start"], date): @@ -464,7 +464,7 @@ def create(self, values): values["date_start"] = values["recurring_next_date"] values["stage_id"] = ( self.env["sale.subscription.stage"] - .search([("type", "=", "pre")], order="sequence desc")[-1] + .search([("type", "=", "pre")], order="sequence desc", limit=1) .id ) return super(SaleSubscription, self).create(values) diff --git a/subscription_oca/static/description/index.html b/subscription_oca/static/description/index.html index 5e4490677c..da81d249fb 100644 --- a/subscription_oca/static/description/index.html +++ b/subscription_oca/static/description/index.html @@ -1,20 +1,20 @@ - + - + Subscription management -
-

Subscription management

+
+ + +Odoo Community Association + +
+

Subscription management

-

Beta License: AGPL-3 OCA/contract Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/contract Translate me on Weblate Try me on Runboat

This module allows creating subscriptions that generate recurring invoices or orders. It also enables the sale of products that generate subscriptions.

@@ -388,7 +393,7 @@

Subscription management

-

Usage

+

Usage

To make a subscription:

  1. Go to Subscriptions > Configuration > Subscription templates.
  2. @@ -418,7 +423,7 @@

    Usage

-

Known issues / Roadmap

+

Known issues / Roadmap

  • Refactor all the onchanges that have business logic to computed write-able fields when possible. Keep onchanges only for UI purposes.
  • @@ -426,23 +431,24 @@

    Known issues / Roadmap

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Domatix
  • +
  • Onestein
-

Contributors

+

Contributors

+
  • Dennis Sluijk <d.sluijk@onestein.nl>
  • +
  • IKU Solutions: +
  • -

    Maintainers

    +

    Maintainers

    This module is maintained by the OCA.

    Odoo Community Association @@ -467,10 +478,11 @@

    Maintainers

    OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

    -

    This module is part of the OCA/contract project on GitHub.

    +

    This module is part of the OCA/contract project on GitHub.

    You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

    +
    diff --git a/subscription_oca/static/img/icon.png b/subscription_oca/static/img/icon.png index bab981dd92b92c32d5d27f100be3ff90bfa3b561..5348996fe371953cef708affec2d9588c41a74fb 100644 GIT binary patch literal 119900 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 AiU0rr literal 16096 zcmcIr1ydYNw8hmWR}cAIb8`#!clxHO2MIK+u13;gvaNHg%!QWgFOMv|kXrG;x=TT`G6`X&e> zjB>1QmQAi?s~oMW|9#iVJ%viO^UHv{jr+H^7)95`j-R>YuHx zv}vR(5h!wU^1oZblYQLCfBxS-vG%;6gvTY~@$#{8wk%KDZNOB}aVki)sqd9JXcypp z${u69@5)G`esFK%Z~^}sUVt&&mIE5lW@r0&2_?F*9BjD_gz3?*Fw$k=fHMw5f;~1Ta;k4gEPnc@*()pzF2Ot3kVD@GxM>kswSp_ssKx} z69_eu5(|llNP!CiEoR$Dj&|c#P+g5{KsIr3W-Y;SP*UXAq8Ri`jg@)q2jldZ)nNjI z2z~H0CfVUonU>7&kN8D;EV}yAt5LW#OlWBCGc&x9H)u>#TKE9*u%Aa|scK?`n)9>& zp4Xr!GfS2hKr8p}5p@MbWT&6`=UX6F&bW`)Zi#0M;&L z;Yi;XXN>ROIT|#6=d^Wi!`|XynQxef*O^uku6jlcRCRHQ{iAWr0TX}}p}@@PGYD2p z6D0hh{_PX<-kah_o~78oxSp2S~@Zsgx%WmmFmc1o%CLh zhq&im_?ZKPVl7+C2)!;+#mGXoymI19Y6n@UFan1~*Olons}6EyBju^1GEb|Zvgu|v zD}Rms2VrsxEuUd?)44?=l`m%gvcss^81zBY@L*1Y>KFKa9P{3fv_yTge$R9<&SA26z?F`*z7;^KOM6DN1NtI zp;m^aUIJ1zvf1sp8XO72YAv%QFP?0nGjFrxiRgOYgU+-RO#Q_wfu++~NJ=+wRA<

    Ui*J$|ByCo)v~>D%$XS<*DoC_r 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 MPD)9#3TzDdAGpB)Gynhq diff --git a/subscription_oca/tests/test_subscription_oca.py b/subscription_oca/tests/test_subscription_oca.py index 66d6dca017..880745900b 100644 --- a/subscription_oca/tests/test_subscription_oca.py +++ b/subscription_oca/tests/test_subscription_oca.py @@ -2,31 +2,40 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import uuid +from unittest.mock import patch from dateutil.relativedelta import relativedelta -from odoo import exceptions, fields -from odoo.tests import TransactionCase +from odoo import Command, exceptions, fields +from odoo.tools import mute_logger +from odoo.addons.base.tests.common import BaseCommon +from odoo.addons.product.tests.common import ProductCommon -class TestSubscriptionOCA(TransactionCase): + +class TestSubscriptionOCA(ProductCommon, BaseCommon): @classmethod def setUpClass(cls): super().setUpClass() 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._create_new_portal_user() + cls.internal_user = cls._create_new_internal_user( + login="subscription_oca_internal_user" + ) cls.cash_journal = cls.env["account.journal"].search( [ ("type", "=", "cash"), ("company_id", "=", cls.env.ref("base.main_company").id), - ] - )[0] + ], + limit=1, + ) cls.sale_journal = cls.env["account.journal"].search( [ ("type", "=", "sale"), ("company_id", "=", cls.env.ref("base.main_company").id), - ] - )[0] + ], + limit=1, + ) cls.pricelist1 = cls.env["product.pricelist"].create( { "name": "pricelist for contract test", @@ -35,7 +44,6 @@ def setUpClass(cls): cls.pricelist2 = cls.env["product.pricelist"].create( { "name": "pricelist for contract test 2", - "discount_policy": "with_discount", } ) cls.partner = cls.env["res.partner"].create( @@ -57,7 +65,7 @@ def setUpClass(cls): "name": "10% Tax incl", "amount_type": "percent", "amount": 10, - "price_include": True, + "price_include_override": "tax_included", } ) cls.tax_0pc = cls.env["account.tax"].create( @@ -67,12 +75,20 @@ def setUpClass(cls): "amount": 0, } ) - cls.product_1 = cls.env.ref("product.product_product_1") - cls.product_1.subscribable = True - cls.product_1.taxes_id = [(6, 0, cls.tax_10pc_incl.ids)] - cls.product_2 = cls.env.ref("product.product_product_2") - cls.product_2.taxes_id = [(6, 0, cls.tax_0pc.ids)] - cls.product_2.subscribable = True + cls.product_1 = cls._create_product( + name="product_1", + lst_price=30.75, + subscribable=True, + uom_id=cls.uom_unit.id, + taxes_id=[Command.set(cls.tax_10pc_incl.ids)], + ) + cls.product_2 = cls._create_product( + name="product_2", + lst_price=38.25, + subscribable=True, + uom_id=cls.uom_unit.id, + taxes_id=[Command.set(cls.tax_0pc.ids)], + ) cls.country = cls.env["res.country"].search([], limit=1) cls.fiscal = cls.env["account.fiscal.position"].create( @@ -181,6 +197,14 @@ def setUpClass(cls): "journal_id": cls.cash_journal.id, } ) + cls.sub9 = cls.create_sub( + { + "template_id": cls.tmpl3.id, + "date_start": fields.Date.today() - relativedelta(days=100), + "in_progress": True, + "recurring_rule_boundary": True, + } + ) cls.sub_line = cls.create_sub_line(cls.sub1) cls.sub_line2 = cls.env["sale.subscription.line"].create( @@ -248,7 +272,14 @@ def create_sub_template(cls, vals): "name": "Test Template " + code, "code": code, "description": "Some sort of subscription terms", - "product_ids": [(6, 0, [cls.product_1.id, cls.product_2.id])], + "product_ids": [ + Command.set( + [ + cls.product_1.product_tmpl_id.id, + cls.product_2.product_tmpl_id.id, + ] + ) + ], } default_vals.update(vals) rec = cls.env["sale.subscription.template"].create(default_vals) @@ -260,7 +291,7 @@ def create_sub(cls, vals): "company_id": 1, "partner_id": cls.partner.id, "template_id": cls.tmpl1.id, - "tag_ids": [(6, 0, [cls.tag.id])], + "tag_ids": [Command.set([cls.tag.id])], "stage_id": cls.stage.id, "pricelist_id": cls.pricelist1.id, "fiscal_position_id": cls.fiscal.id, @@ -287,10 +318,24 @@ def _create_price_list(cls, name): "name": name, "active": True, "currency_id": cls.env.ref("base.USD").id, - "company_id": cls.env.user.company_id.id, + "company_id": cls.env.company.id, } ) + def _ensure_subscription_pricelist(self, subscription): + if subscription.pricelist_id: + return + pricelist = ( + subscription.sale_order_id.pricelist_id + or subscription.sale_order_ids[:1].pricelist_id + or subscription.partner_id.with_company( + subscription.company_id + ).property_product_pricelist + or subscription.partner_id.property_product_pricelist + ) + if pricelist: + subscription.pricelist_id = pricelist + def test_subscription_oca_sale_order(self): # SO standard flow so = self.env["sale.order"].create( @@ -299,14 +344,12 @@ def test_subscription_oca_sale_order(self): "partner_invoice_id": self.partner.id, "partner_shipping_id": self.partner.id, "order_line": [ - ( - 0, - 0, + Command.create( { "name": self.product_1.name, "product_id": self.product_1.id, "product_uom_qty": 2, - "product_uom": self.product_1.uom_id.id, + "product_uom_id": self.product_1.uom_id.id, "price_unit": self.product_1.list_price, }, ) @@ -321,17 +364,29 @@ def test_subscription_oca_sale_order(self): def test_subscription_oca_sub_lines(self): # sale.subscription.line - self.assertEqual(self.sub_line.name, self.sub_line.product_id.name) + self.assertEqual(self.sub_line.name, self.sub_line.product_id.display_name) self.assertIsNotNone(self.sub_line.tax_ids) - self.assertEqual(self.sub_line.price_unit, 30.75) + self.assertAlmostEqual(self.sub_line.price_unit, 27.95, 2) self.assertEqual(self.sub_line.discount, 0) res = self.sub_line._get_display_price(self.product_2) - self.assertEqual(res, 38.25) + self.assertAlmostEqual(res, 38.25, 2) sol_res = self.sub_line._prepare_sale_order_line() self.assertIsInstance(sol_res, dict) move_res = self.sub_line._prepare_account_move_line() self.assertIsInstance(move_res, dict) + @patch( + "odoo.addons.subscription_oca.models.sale_subscription." + "SaleSubscription.generate_invoice" + ) + def test_subscription_oca_sub_cron_error(self, generate_invoice_patch): + # Simulate something failing in generating an invoice, + # we expect something being logged + generate_invoice_patch.side_effect = exceptions.UserError("Error") + with mute_logger("odoo.addons.subscription_oca.models.sale_subscription"): + with self.assertRaises(exceptions.UserError): + self.sub1.cron_subscription_management() + def test_subscription_oca_sub_cron(self): # sale.subscription self.sub1.cron_subscription_management() @@ -340,17 +395,17 @@ def test_subscription_oca_sub_cron(self): [("subscription_id", "=", self.sub1.id)] ) self.assertEqual(len(inv_id), 1) - self.assertEqual(self.sub1.recurring_total, 27.95) - self.assertEqual(self.sub1.amount_total, 30.75) - self.assertEqual(self.sub2.recurring_total, 66.2) - self.assertEqual(self.sub2.amount_total, 69) + self.assertAlmostEqual(self.sub1.recurring_total, 27.95, 2) + self.assertAlmostEqual(self.sub1.amount_total, 27.95, 2) + self.assertAlmostEqual(self.sub2.recurring_total, 66.2, 2) + self.assertAlmostEqual(self.sub2.amount_total, 66.2, 2) def test_subscription_oca_sub1_workflow(self): res = self._collect_all_sub_test_results(self.sub1) self.assertTrue(res[0]) self.assertTrue(res[1]) self.assertEqual(res[3], 2) - self.assertEqual(res[4], 2 * 30.75) + self.assertAlmostEqual(res[4], 2 * 27.95, 2) self.assertEqual(res[5], 2) self.assertEqual(res[7], 1) self.assertEqual( @@ -366,7 +421,7 @@ def test_subscription_oca_sub2_workflow(self): self.assertTrue(res[0]) self.assertTrue(res[1]) self.assertEqual(res[3], 2) - self.assertEqual(res[4], 138) + self.assertAlmostEqual(res[4], 132.4, 2) self.assertEqual(res[5], 2) self.assertEqual(res[7], 1) self.assertEqual( @@ -381,7 +436,7 @@ def test_subscription_oca_sub3_workflow(self): self.assertTrue(res[0]) self.assertTrue(res[1]) self.assertEqual(res[3], 2) - self.assertEqual(res[4], 138) + self.assertAlmostEqual(res[4], 132.4, 2) self.assertEqual(res[5], 2) self.assertEqual(res[6], "ir.actions.act_window") self.assertEqual(res[7], 1) @@ -398,7 +453,7 @@ def test_subscription_oca_sub4_workflow(self): self.assertTrue(res[1]) self.assertEqual(res[2], "ir.actions.act_window") self.assertEqual(res[3], 2) - self.assertEqual(res[4], 138) + self.assertAlmostEqual(res[4], 132.4, 2) self.assertEqual(res[5], 2) self.assertEqual(res[7], 1) self.assertEqual( @@ -413,7 +468,7 @@ def test_subscription_oca_sub5_workflow(self): self.assertTrue(res[0]) self.assertTrue(res[1]) self.assertEqual(res[3], 2) - self.assertEqual(res[4], 138) + self.assertAlmostEqual(res[4], 132.4, 2) self.assertEqual(res[5], 2) self.assertEqual(res[7], 1) self.assertEqual( @@ -424,8 +479,11 @@ def test_subscription_oca_sub5_workflow(self): self.assertFalse(res[11]) self.sub5.recurring_next_date = fields.Date.today() self.sub5.template_id = self.tmpl5 + self._ensure_subscription_pricelist(self.sub5) self.sub5._onchange_template_id() + self._ensure_subscription_pricelist(self.sub5) self.sub5.invoice_ids.unlink() + self._ensure_subscription_pricelist(self.sub5) self.sub5._onchange_template_id() def test_subscription_oca_sub7_workflow(self): @@ -433,7 +491,7 @@ def test_subscription_oca_sub7_workflow(self): self.assertTrue(res[0]) self.assertTrue(res[1]) self.assertEqual(res[3], 2) - self.assertEqual(res[4], 138) + self.assertAlmostEqual(res[4], 132.4, 2) self.assertEqual(res[5], 2) self.assertEqual(res[7], 1) self.assertEqual( @@ -446,8 +504,7 @@ def test_subscription_oca_sub7_workflow(self): def test_subscription_oca_sub8_workflow(self): subscription = self.sub8 subscription.create_sale_order() - with self.assertRaises(exceptions.UserError): - subscription.create_invoice() + subscription.create_invoice() self.sub8.journal_id = self.sale_journal subscription.create_invoice() self.sub8.template_id.invoicing_mode = "invoice" @@ -516,7 +573,7 @@ def test_subscription_oca_sub_stage(self): def test_x_subscription_oca_pricelist_related(self): res = self.partner.read(["subscription_count", "subscription_ids"]) - self.assertEqual(res[0]["subscription_count"], 8) + self.assertEqual(res[0]["subscription_count"], 9) res = self.partner.action_view_subscription_ids() self.assertIsInstance(res, dict) sale_order = self.sub1.create_sale_order() @@ -526,19 +583,27 @@ def test_x_subscription_oca_pricelist_related(self): sale_order.get_next_interval( self.tmpl1.recurring_rule_type, self.tmpl1.recurring_interval ) - self.sub_line.sale_subscription_id.pricelist_id.discount_policy = ( - "without_discount" - ) self.sub_line.product_uom_qty = 100 - self.env.user.group_ids = [ - (4, self.env.ref("product.group_discount_per_so_line").id) - ] + self.internal_user.write( + { + "group_ids": [ + Command.link(self.env.ref("sale.group_discount_per_so_line").id) + ] + } + ) + self.env.user.write( + { + "group_ids": [ + Command.link(self.env.ref("sale.group_discount_per_so_line").id) + ] + } + ) disc = self.sub_line.read(["discount"]) self.assertEqual(disc[0]["discount"], 0) wiz = self.env["close.reason.wizard"].create({}) wiz.with_context(active_id=self.sub1.id).button_confirm() self.assertEqual(self.sub1.stage_id.name, "Closed") - self.assertFalse(self.sub1.active) + self.assertTrue(self.sub1.active) self.tmpl1.action_view_subscription_ids() self.tmpl1.action_view_product_ids() self.tmpl1.read(["product_ids_count", "subscription_count"]) @@ -591,34 +656,27 @@ def test_x_subscription_oca_pricelist_related(self): self.assertEqual(res[0]["discount"], 100) def test_x_subscription_oca_pricelist_related_2(self): - self.pricelist_l3.discount_policy = "without_discount" self.pricelist_l3.currency_id = self.env.ref("base.THB") self.sub_line.sale_subscription_id.pricelist_id = self.pricelist_l3 res = self.sub_line._get_display_price(self.product_1) + conversion_rate = self.env.company.currency_id._get_conversion_rate( + self.env.company.currency_id, + self.pricelist_l3.currency_id, + self.env.company, + fields.Date.today(), + ) self.assertAlmostEqual( int(res), - round( - self.product_1.standard_price - * self.pricelist_l3.currency_id.rate_ids[:1].company_rate - ), + round(self.product_1.standard_price * conversion_rate), ) self.sub_line.product_uom_qty = 300 res = self.sub_line.read(["discount"]) self.assertEqual(res[0]["discount"], 0) - def test_compute_display_name(self): - stage = self.env["sale.subscription.stage"].create( - { - "name": "Test Stage", - "type": "pre", - } - ) - self.assertEqual(stage.display_name, "Test Stage", "display_name not computed") - stage.name = "Updated Test Stage" - stage._compute_display_name() - self.assertEqual( - stage.display_name, "Updated Test Stage", "display_name not computed" - ) + def test_open_subscription(self): + invoice = self.sub1.create_invoice() + action = invoice.action_open_subscription() + self.assertEqual(action["res_id"], self.sub1.id) def _collect_all_sub_test_results(self, subscription): """Creates the invoice of a subscription and returns its data @@ -649,40 +707,26 @@ def _collect_all_sub_test_results(self, subscription): inv_ids = self.env["account.move"].search( [("subscription_id", "=", subscription.id)] ) - # self.assertEqual(len(inv_ids), 2) - # self.assertEqual(sum(inv_ids.mapped("amount_total")), 2 * 30.75) - # self.assertEqual(subscription.account_invoice_ids_count, 2) test_res.append(len(inv_ids)) test_res.append(sum(inv_ids.mapped("amount_total"))) test_res.append(subscription.account_invoice_ids_count) res = subscription.action_view_account_invoice_ids() - # self.assertEqual(res["type"], "ir.actions.act_window") - # self.assertEqual(subscription.sale_order_ids_count, 1) test_res.append(res["type"]) test_res.append(subscription.sale_order_ids_count) subscription.action_view_sale_order_ids() - # self.assertIn(str(subscription.sale_order_ids.id), str(res["domain"])) test_res.append(subscription.sale_order_ids.id) subscription.calculate_recurring_next_date(fields.Datetime.now()) - # self.assertEqual( - # subscription.recurring_next_date, - # fields.Date.today() + relativedelta(months=1), - # ) test_res.append(subscription.recurring_next_date) subscription.partner_id = self.partner_2 subscription.onchange_partner_id() - # self.assertEqual( - # subscription.pricelist_id.id, self.partner_2.property_product_pricelist.id - # ) test_res.append(subscription.pricelist_id.id) subscription.onchange_partner_id_fpos() - # self.assertFalse(subscription.fiscal_position_id) test_res.append(subscription.fiscal_position_id) res = subscription.action_close_subscription() self.assertEqual(res["type"], "ir.actions.act_window") test_res.append(res["type"]) group_stage_ids = subscription._read_group_stage_ids( - stages=self.env["sale.subscription.stage"].search([]), domain=[], order="id" + stages=self.env["sale.subscription.stage"].search([]), domain=[] ) test_res.append(group_stage_ids) return test_res diff --git a/subscription_oca/views/account_move_views.xml b/subscription_oca/views/account_move_views.xml new file mode 100644 index 0000000000..1486342f66 --- /dev/null +++ b/subscription_oca/views/account_move_views.xml @@ -0,0 +1,20 @@ + + + + account.move + + + + +