diff --git a/subscription_package/README.rst b/subscription_package/README.rst new file mode 100755 index 000000000..871f606bd --- /dev/null +++ b/subscription_package/README.rst @@ -0,0 +1,46 @@ +.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg + :target: https://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +Subscription Management +======================= +* Subscription Package for Odoo 18 community edition + +License +------- +General Public License, Version 3 (AGPL v3). +(https://www.gnu.org/licenses/agpl-3.0-standalone.html) + +Company +------- +* `Cybrosys Techno Solutions `__ + +Credits +------- +Developers: (V15) Amal Prasad + (V16) Archana V + (V17) Janish Babu EK + (V18) Sreerag PM +Contact: odoo@cybrosys.com + +Contacts +-------- +* Mail Contact : odoo@cybrosys.com +* Website : https://cybrosys.com + +Bug Tracker +----------- +Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. + +Maintainer +========== +.. image:: https://cybrosys.com/images/logo.png + :target: https://cybrosys.com + +This module is maintained by Cybrosys Technologies. + +For support and more information, please visit `Our Website `__ + +Further information +=================== +HTML Description: ``__ diff --git a/subscription_package/__init__.py b/subscription_package/__init__.py new file mode 100644 index 000000000..70ab2c453 --- /dev/null +++ b/subscription_package/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from . import models +from . import report +from . import wizard diff --git a/subscription_package/__manifest__.py b/subscription_package/__manifest__.py new file mode 100644 index 000000000..fd3a7c46a --- /dev/null +++ b/subscription_package/__manifest__.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +{ + 'name': 'Subscription Management', + 'version': '18.0.1.0.0', + 'category': 'Sales', + 'summary': 'Subscription Package Management Module For Odoo18 Community', + 'description': 'Subscription Package Management Module specifically ' + 'designed for Odoo 18 Community edition. ' + 'This module aims to enhance the subscription ' + 'management capabilities within the Odoo platform, ' + 'providing users with advanced features and ' + 'functionalities for efficiently handling subscription ' + 'packages in the community version of Odoo 18.', + 'author': 'Cybrosys Techno solutions', + 'company': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'website': "https://www.cybrosys.com", + 'depends': ['base', 'sale_management'], + 'data': [ + 'security/subscription_package_groups.xml', + 'security/ir.model.access.csv', + 'data/uom_demo_data.xml', + 'data/subscription_package_stop_data.xml', + 'data/subscription_stage_data.xml', + 'data/mail_subscription_renew_data.xml', + 'data/ir_cron_data.xml', + 'data/ir_sequence.xml', + 'views/subscription_package_views.xml', + 'views/product_template_views.xml', + 'views/subscription_package_plan_views.xml', + 'views/subscription_stage_views.xml', + 'views/subscription_package_stop_views.xml', + 'views/mail_activity_views.xml', + 'views/res_partner_views.xml', + 'views/recurrence_period_views.xml', + 'views/sale_order_views.xml', + 'views/product_product_views.xml', + 'report/subscription_report_view.xml', + 'wizard/subscription_close_views.xml', + ], + 'images': ['static/description/banner.jpg'], + 'license': 'AGPL-3', + 'installable': True, + 'auto_install': False, + 'application': True, +} diff --git a/subscription_package/data/ir_cron_data.xml b/subscription_package/data/ir_cron_data.xml new file mode 100644 index 000000000..32d25002a --- /dev/null +++ b/subscription_package/data/ir_cron_data.xml @@ -0,0 +1,12 @@ + + + + + Check Close Limit + + code + model.close_limit_cron() + 1 + days + + diff --git a/subscription_package/data/ir_sequence.xml b/subscription_package/data/ir_sequence.xml new file mode 100644 index 000000000..cd077e248 --- /dev/null +++ b/subscription_package/data/ir_sequence.xml @@ -0,0 +1,12 @@ + + + + + + Reference Code + sequence.reference.code + SUB + 4 + + + diff --git a/subscription_package/data/mail_subscription_renew_data.xml b/subscription_package/data/mail_subscription_renew_data.xml new file mode 100644 index 000000000..75048cfaa --- /dev/null +++ b/subscription_package/data/mail_subscription_renew_data.xml @@ -0,0 +1,55 @@ + + + + + Subscription: Email Renew Alert + + {{ object.company_id.name }}: Please check the subscription {{ object.name }} + {{ object.company_id.email }} + {{ object.partner_id.email }} + + {{ object.partner_id.lang }} + +
+ + + + + + + +
+ + + + + Subscription Renew Alert +
+ + + + + + + + +
+

Dear, +

+

Your subscription Plan + + is going to Expired on + . +

+

If you have any concerns about it, please contact your representative at + + or reply to this email. +

+

Kind regards.

+
+
+
+
+
diff --git a/subscription_package/data/subscription_package_stop_data.xml b/subscription_package/data/subscription_package_stop_data.xml new file mode 100644 index 000000000..291763946 --- /dev/null +++ b/subscription_package/data/subscription_package_stop_data.xml @@ -0,0 +1,9 @@ + + + + + + Renewal Limit Exceeded + + + diff --git a/subscription_package/data/subscription_stage_data.xml b/subscription_package/data/subscription_stage_data.xml new file mode 100644 index 000000000..768032fd0 --- /dev/null +++ b/subscription_package/data/subscription_stage_data.xml @@ -0,0 +1,22 @@ + + + + + + Draft + 5 + draft + + + In Progress + 10 + progress + + + Closed + 15 + + closed + + + diff --git a/subscription_package/data/uom_demo_data.xml b/subscription_package/data/uom_demo_data.xml new file mode 100644 index 000000000..0e0876062 --- /dev/null +++ b/subscription_package/data/uom_demo_data.xml @@ -0,0 +1,22 @@ + + + + + + Time + + + + Months + + reference + + + + Years + + bigger + + + + diff --git a/subscription_package/doc/RELEASE_NOTES.md b/subscription_package/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..4a6b23f9e --- /dev/null +++ b/subscription_package/doc/RELEASE_NOTES.md @@ -0,0 +1,6 @@ +## Module +#### 19.02.2025 +#### Version 18.0.1.0.0 +#### ADD +- Initial commit for Subscription Management + diff --git a/subscription_package/models/__init__.py b/subscription_package/models/__init__.py new file mode 100644 index 000000000..d32d34548 --- /dev/null +++ b/subscription_package/models/__init__.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from . import account_move +from . import product_template +from . import recurrence_period +from . import res_partner +from . import sale_order +from . import subscription_package +from . import subscription_package_plan +from . import subscription_package_product_line +from . import subscription_package_stage +from . import subscription_package_stop +from . import sale_order_line diff --git a/subscription_package/models/account_move.py b/subscription_package/models/account_move.py new file mode 100644 index 000000000..c818a6e58 --- /dev/null +++ b/subscription_package/models/account_move.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import api, fields, models + + +class AccountMove(models.Model): + """Inherited sale order model""" + _inherit = "account.move" + + is_subscription = fields.Boolean(string='Is Subscription', default=False, + help='Is subscription') + subscription_id = fields.Many2one('subscription.package', + string='Subscription', + help='Choose subscription package') + + @api.model_create_multi + def create(self, vals_list): + """ It displays subscription in account move """ + for rec in vals_list: + so_id = self.env['sale.order'].search( + [('name', '=', rec.get('invoice_origin'))]) + if so_id.is_subscription is True: + so_id.subscription_id.start_date = so_id.subscription_id.next_invoice_date + new_vals_list = [{'is_subscription': True, + 'subscription_id': so_id.subscription_id.id}] + vals_list[0].update(new_vals_list[0]) + return super().create(vals_list) diff --git a/subscription_package/models/product_template.py b/subscription_package/models/product_template.py new file mode 100644 index 000000000..e59abcfed --- /dev/null +++ b/subscription_package/models/product_template.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import fields, models + + +class ProductTemplate(models.Model): + """Inherited product template model""" + _inherit = "product.template" + + is_subscription = fields.Boolean(string='Is Subscription', default=False, + help='Indicates whether the product is ' + 'associated with a subscription ' + 'or not.') + subscription_plan_id = fields.Many2one('subscription.package.plan', + string='Subscription Plan', + help='Select the subscription plan ' + 'associated with this record.') diff --git a/subscription_package/models/recurrence_period.py b/subscription_package/models/recurrence_period.py new file mode 100644 index 000000000..62bd2ce05 --- /dev/null +++ b/subscription_package/models/recurrence_period.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import fields, models + + +class RecurrencePeriod(models.Model): + """This class is used to create new model recurrence period""" + _name = "recurrence.period" + _description = "Recurrence Period " + + name = fields.Char(string="Name", + help='The name of the recurrence period. Enter a ' + 'descriptive name for the period.') + duration = fields.Float(string="Duration", + help='The duration associated with this record. ' + 'Enter the duration value.') + unit = fields.Selection([('hours', 'hours'), + ('days', 'Days'), ('weeks', 'Weeks'), + ('months', 'Months'), ('years', 'Years')], + string='Unit', + help='Select the unit of time associated with this ' + 'record. Choose from the available options.') diff --git a/subscription_package/models/res_partner.py b/subscription_package/models/res_partner.py new file mode 100644 index 000000000..a039c9b7d --- /dev/null +++ b/subscription_package/models/res_partner.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import fields, models + + +class ResPartner(models.Model): + """Inherited res partner model""" + _inherit = 'res.partner' + + is_active_subscription = fields.Boolean(string="Active Subscription", + default=False, + help='Is Subscription is active') + subscription_product_line_ids = fields.One2many( + 'subscription.package.product.line', 'res_partner_id', + ondelete='restrict', string='Products Line', + help='Subscription product') + + def _valid_field_parameter(self, field, name): + """ + Validate field parameters, allowing custom handling for 'ondelete' + """ + if name == 'ondelete': + return True + return super(ResPartner, + self)._valid_field_parameter(field, name) diff --git a/subscription_package/models/sale_order.py b/subscription_package/models/sale_order.py new file mode 100644 index 000000000..bd0df10c4 --- /dev/null +++ b/subscription_package/models/sale_order.py @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import api, fields, models +from odoo.tools.safe_eval import datetime + + +class SaleOrder(models.Model): + """ This class is used to inherit sale order""" + _inherit = 'sale.order' + + subscription_count = fields.Integer(string='Subscriptions', + compute='_compute_subscription_count', + help='Subscriptions count') + is_subscription = fields.Boolean(string='Is Subscription', default=False, + help='Is subscription') + subscription_id = fields.Many2one('subscription.package', + string='Subscription', + help='Choose the subscription') + sub_reference = fields.Char(string="Sub Reference Code", store=True, + compute="_compute_reference_code", + help='Subscription Reference Code') + + @api.model_create_multi + def create(self, vals_list): + """ It displays subscription in sale order """ + for vals in vals_list: + if vals.get('is_subscription'): + vals.update({ + 'is_subscription': True, + 'subscription_id': vals.get('subscription_id'), + }) + return super().create(vals) + + @api.depends('subscription_id') + def _compute_reference_code(self): + """ It displays subscription reference code """ + self.sub_reference = self.env['subscription.package'].search( + [('id', '=', int(self.subscription_id.id))]).reference_code + + def action_confirm(self): + """ It Changed the stage, to renew, start date for subscription + package based on sale order confirm """ + + res = super().action_confirm() + sale_order = self.subscription_id.sale_order_id + so_state = self.search([('id', '=', sale_order.id)]).state + if so_state in ['sale', 'done']: + stage = self.env['subscription.package.stage'].search( + [('category', '=', 'progress')], limit=1).id + values = {'stage_id': stage, 'is_to_renew': False, + 'start_date': datetime.datetime.today()} + self.subscription_id.write(values) + return res + + @api.depends('subscription_count') + def _compute_subscription_count(self): + """the compute function the count of + subscriptions associated with the sale order.""" + subscription_count = self.env[ + 'subscription.package'].sudo().search_count( + [('sale_order_id', '=', self.id)]) + if subscription_count > 0: + self.subscription_count = subscription_count + else: + self.subscription_count = 0 + + def button_subscription(self): + """Open the subscription packages associated with the sale order.""" + return { + 'name': 'Subscription', + 'sale_order_id': False, + 'domain': [('sale_order_id', '=', self.id)], + 'view_type': 'form', + 'res_model': 'subscription.package', + 'view_mode': 'list,form', + 'type': 'ir.actions.act_window', + 'context': { + "create": False + } + } + + def _action_confirm(self): + """the function used to Confrim the sale order and + create subscriptions for subscription products""" + if self.subscription_count != 1: + if self.order_line: + for line in self.order_line: + if line.product_id.is_subscription: + this_products_line = [] + rec_list = [0, 0, {'product_id': line.product_id.id, + 'product_qty': line.product_uom_qty, + 'unit_price': line.price_unit}] + this_products_line.append(rec_list) + self.env['subscription.package'].create( + { + 'sale_order_id': self.id, + 'reference_code': self.env[ + 'ir.sequence'].next_by_code( + 'sequence.reference.code'), + 'start_date': fields.Date.today(), + 'stage_id': self.env.ref( + 'subscription_package.draft_stage').id, + 'partner_id': self.partner_id.id, + 'plan_id': line.product_id.subscription_plan_id.id, + 'product_line_ids': this_products_line + }) + return super()._action_confirm() diff --git a/subscription_package/models/sale_order_line.py b/subscription_package/models/sale_order_line.py new file mode 100644 index 000000000..10e969872 --- /dev/null +++ b/subscription_package/models/sale_order_line.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from odoo import api, models + + +class SaleOrderLine(models.Model): + _inherit = 'sale.order.line' + + @api.depends('qty_invoiced', 'qty_delivered', 'product_uom_qty', 'state') + def _compute_qty_to_invoice(self): + """Over-write the _compute_qty_to_invoice function + to look weather order is subscriptions""" + for line in self: + if (line.order_id.subscription_id and not + line.order_id.subscription_id.is_closed + and line.order_id.is_subscription): + if line.product_template_id.is_subscription: + line.qty_to_invoice = line.product_uom_qty + else: + if line.state == 'sale' and not line.display_type: + if line.product_id.invoice_policy == 'order': + line.qty_to_invoice = ( + line.product_uom_qty - line.qty_invoiced) + else: + line.qty_to_invoice = ( + line.qty_delivered - line.qty_invoiced) + else: + line.qty_to_invoice = 0 diff --git a/subscription_package/models/subscription_package.py b/subscription_package/models/subscription_package.py new file mode 100644 index 000000000..4f2ff7503 --- /dev/null +++ b/subscription_package/models/subscription_package.py @@ -0,0 +1,471 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from dateutil.relativedelta import relativedelta +from odoo import api, fields, models, SUPERUSER_ID, _ +from odoo.exceptions import UserError + + +class SubscriptionPackage(models.Model): + """Subscription Package Model""" + _name = 'subscription.package' + _description = 'Subscription Package' + _rec_name = 'name' + _inherit = ['mail.thread', 'mail.activity.mixin'] + + @api.model + def _read_group_stage_ids(self, stages, domain): + """ Read all the stages and display it in the kanban view, + even if it is empty.""" + stages_ids = stages.sudo()._search([], order=stages._order) + return stages.browse(stages_ids) + + def _default_stage_id(self): + """Setting default stage""" + rec = self.env['subscription.package.stage'].search([], limit=1, + order='sequence ASC') + return rec.id if rec else None + + name = fields.Char(string='Name', default="New", compute='_compute_name', + store=True, required=True, + help='Choose the name for the subscription package.') + partner_id = fields.Many2one('res.partner', string='Customer', + help='Select the customer associated with ' + 'this record.') + partner_invoice_id = fields.Many2one('res.partner', + help='Select the invoice address ' + 'associated with this record.', + string='Invoice Address', + related='partner_id') + partner_shipping_id = fields.Many2one('res.partner', + help="Add shipping/service address", + string='Shipping/Service Address', + related='partner_id') + plan_id = fields.Many2one('subscription.package.plan', + string='Subscription Plan', + help="Choose the subscription package plan") + start_date = fields.Date(string='Period Start Date', + help='Add the period start date', + ondelete='restrict') + date_started = fields.Date(string='Subsciption Start date', + help='Add the Subscription package start date', + ondelete='restrict', readonly=True) + next_invoice_date = fields.Date(string='Next Invoice Date', + store=True, help='Add next invoice date', + compute="_compute_next_invoice_date", + inverse="_inverse_next_invoice_date") + company_id = fields.Many2one('res.company', string='Company', + help='Select the company', + default=lambda self: self.env.company, + required=True) + user_id = fields.Many2one('res.users', string='Sales Person', + help='Add the Sales person', + default=lambda self: self.env.user) + sale_order_id = fields.Many2one('sale.order', string="Sale Order", + help='Select the sale order', copy=False) + is_to_renew = fields.Boolean(string='To Renew', copy=True, + help='Is subscription package is renew') + tag_ids = fields.Many2many('account.account.tag', string='Tags', + help='Add the tags') + stage_id = fields.Many2one('subscription.package.stage', string='Stage', + default=lambda self: self._default_stage_id(), + index=True, + group_expand='_read_group_stage_ids', + help='Subscription Package stage', copy=False) + invoice_count = fields.Integer(string='Invoices', + help='Subscription package invoice count', + compute='_compute_invoice_count') + so_count = fields.Integer(string='Sales', + help='subscription package sales count', + compute='_compute_sale_count') + description = fields.Text(string='Description', + help='Subscription package description') + analytic_account_id = fields.Many2one('account.analytic.account', + help='Choose the analytic account', + string='Analytic Account') + product_line_ids = fields.One2many('subscription.package.product.line', + 'subscription_id', ondelete='restrict', + string='Products Line', + help='Subscription package product line') + currency_id = fields.Many2one('res.currency', string='Currency', + readonly=True, default=lambda + self: self.env.company.currency_id, help='Add Currency') + current_stage = fields.Char(string='Current Stage', default='Draft', + help='Current stage of the ' + 'subscription package. ' + 'This field is computed based on ' + 'the associated stage_id.', + store=True, compute='_compute_current_stage') + reference_code = fields.Char(string='Reference', + help='This field represents the ' + 'reference code associated ' + 'with the record.') + is_closed = fields.Boolean(string="Closed", default=False, + help='Is Closed') + close_reason_id = fields.Many2one('subscription.package.stop', + help='The reason for c' + 'losing the subscription package.', + string='Close Reason') + closed_by = fields.Many2one('res.users', string='Closed By', + help="The user responsible " + "for closing the record") + close_date = fields.Date(string='Closed on', + help="The date on which the record was closed") + stage_category = fields.Selection(related='stage_id.category', + help="The category associated with " + "the current stage of the record. ", + store=True) + invoice_mode = fields.Selection(related="plan_id.invoice_mode", + help="The invoice mode " + "associated with the plan.") + total_recurring_price = fields.Float(string='Untaxed Amount', + help="The total recurring " + "price excluding taxes.", + compute='_compute_total_recurring_price', + store=True) + tax_total = fields.Float("Taxes", readonly=True, + help="The total amount of " + "taxes associated with the record") + total_with_tax = fields.Monetary("Total Recurring Price", readonly=True, + help="The total recurring " + "price including taxes") + recurrence_period_id = fields.Many2one("recurrence.period", + string="Recurrence Period") + sale_order_count = fields.Integer(string='Sale Order Count', + help="The count of associated " + "sale orders for this record.") + + def _valid_field_parameter(self, field, name): + """Check the validity of a field parameter for a specific field.""" + if name == 'ondelete': + return True + return super(SubscriptionPackage, + self)._valid_field_parameter(field, name) + + @api.depends('invoice_count') + def _compute_invoice_count(self): + """ Calculate Invoice count based on subscription package """ + sale_id = self.env['sale.order'].search( + [('id', '=', self.sale_order_id.id)]) + invoices = sale_id.order_line.invoice_lines.move_id.filtered( + lambda r: r.move_type in ('out_invoice', 'out_refund')) + invoices.write({'subscription_id': self.id}) + invoice_count = self.env['account.move'].search_count( + [('subscription_id', '=', self.id)]) + if invoice_count > 0: + self.invoice_count = invoice_count + else: + self.invoice_count = 0 + + @api.depends('so_count') + def _compute_sale_count(self): + """ Calculate sale order count based on subscription package """ + self.so_count = self.env['sale.order'].search_count( + [('id', '=', self.sale_order_id.id)]) + + @api.depends('stage_id') + def _compute_current_stage(self): + """ It displays current stage for subscription package """ + for rec in self: + rec.current_stage = rec.env['subscription.package.stage'].search( + [('id', '=', rec.stage_id.id)]).category + + @api.depends('start_date') + def _compute_next_invoice_date(self): + """The compute function is the next invoice date for subscription + packages based on the start date and renewal time.""" + for sub in self.env['subscription.package'].search([]): + if sub.start_date: + sub.next_invoice_date = sub.start_date + relativedelta( + days=sub.plan_id.renewal_time) + + def _inverse_next_invoice_date(self): + """Inverse function for next invoice date""" + for sub in self.env['subscription.package'].search([]): + if sub.start_date: + return + + def button_invoice_count(self): + """ It displays invoice based on subscription package """ + return { + 'name': 'Invoices', + 'domain': [('subscription_id', '=', self.id)], + 'view_type': 'form', + 'res_model': 'account.move', + 'view_mode': 'list,form', + 'type': 'ir.actions.act_window', + 'context': { + "create": False + } + } + + def button_sale_count(self): + """ It displays sale order based on subscription package """ + return { + 'name': 'Products', + 'domain': [('id', '=', self.sale_order_id.id)], + 'view_type': 'form', + 'res_model': 'sale.order', + 'view_mode': 'list,form', + 'type': 'ir.actions.act_window', + 'context': { + "create": False + } + } + + def button_close(self): + """ Button for subscription close wizard """ + return { + 'name': "Subscription Close Reason", + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'subscription.close', + 'target': 'new' + } + + def button_start_date(self): + """Button to start subscription package""" + stage_id = (self.env['subscription.package.stage'].search([ + ('category', '=', 'progress')], limit=1).id) + for rec in self: + if len(rec.env['subscription.package.stage'].search( + [('category', '=', 'draft')])) > 1: + raise UserError( + _('More than one stage is having category "Draft". ' + 'Please change category of stage to "In Progress", ' + 'only one stage is allowed to have category "Draft"')) + else: + if not rec.product_line_ids: + raise UserError("Empty order lines !! Please add the " + "subscription product.") + else: + if rec.sale_order_id: + rec.sale_order_id.write({'subscription_id': rec.id, + 'is_subscription': True}) + for line in rec.sale_order_id.order_line.filtered( + lambda x: x.product_template_id.is_subscription == True): + line.qty_to_invoice = line.product_uom_qty + rec.write( + {'stage_id': stage_id, + 'date_started': fields.Date.today(), + 'start_date': fields.Date.today()}) + + def button_sale_order(self): + """Button to create sale order""" + this_products_line = [] + for rec in self.product_line_ids: + rec_list = [0, 0, {'product_id': rec.product_id.id, + 'product_uom_qty': rec.product_qty, + 'discount': rec.discount}] + this_products_line.append(rec_list) + orders = self.env['sale.order'].search( + [('id', '=', self.sale_order_count), + ('invoice_status', '=', 'no')]) + if orders: + for order in orders: + order.action_confirm() + so_id = self.env['sale.order'].create({ + 'id': self.sale_order_count, + 'partner_id': self.partner_id.id, + 'partner_invoice_id': self.partner_id.id, + 'partner_shipping_id': self.partner_id.id, + 'is_subscription': True, + 'subscription_id': self.id, + 'order_line': this_products_line + }) + self.sale_order_id = so_id + return { + 'name': _('Sales Orders'), + 'type': 'ir.actions.act_window', + 'res_model': 'sale.order', + 'domain': [('id', '=', so_id.id)], + 'view_mode': 'list,form', + 'context': { + "create": False + } + } + + @api.model_create_multi + def create(self, vals_list): + """It displays subscription product in partner and generate sequence""" + for vals in vals_list: + partner = self.env['res.partner'].search( + [('id', '=', vals.get('partner_id'))]) + partner.is_active_subscription = True + if vals.get('reference_code', 'New') is False: + vals['reference_code'] = self.env['ir.sequence'].next_by_code( + 'sequence.reference.code') or 'New' + create_id = super().create(vals) + return create_id + + @api.depends('reference_code') + def _compute_name(self): + """It displays record name as combination of short code, reference + code and partner name """ + for rec in self: + plan_id = self.env['subscription.package.plan'].search( + [('id', '=', rec.plan_id.id)]) + if plan_id.short_code and rec.reference_code: + rec.name = plan_id.short_code + '/' + rec.reference_code + '-' + rec.partner_id.name + + def set_close(self): + """ Button to close subscription package """ + stage = self.env['subscription.package.stage'].search( + [('category', '=', 'closed')], limit=1).id + for sub in self: + values = {'stage_id': stage, 'is_to_renew': False} + sub.write(values) + return True + + def send_renew_alert_mail(self, today, renew_date, sub_id): + """The function is used to send a renewal alert email and mark the + subscription for renewal if today is the renewal date.""" + if today == renew_date: + self.env.ref( + 'subscription_package' + '.mail_template_subscription_renew').send_mail( + sub_id, force_send=True) + subscription = self.env['subscription.package'].browse(sub_id) + subscription.write({'is_to_renew': True}) + return True + else: + return False + + def find_renew_date(self, next_invoice, date_started, end): + """The function is used to calculate the renewal date, end date, + and close date based on subscription details.""" + if end == 0: + end_date = next_invoice + difference = (next_invoice - date_started).days / 10 + renew_date = next_invoice - relativedelta( + days=difference) + close_date = next_invoice + else: + end_date = fields.Date.add(date_started, + days=end) + close = date_started + relativedelta(days=end) + difference = (close - date_started).days / 10 + renew_date = close - relativedelta( + days=difference) + close_date = close + + data = {'renew_date': renew_date, + 'end_date': end_date, + 'close_date': close_date} + return data + + def close_limit_cron(self): + """ It Checks renew date, close date. It will send mail when renew + date and also generates invoices based on the plan. It wil close the + subscription automatically if renewal limit is exceeded""" + pending_subscriptions = self.env['subscription.package'].search( + [('stage_category', '=', 'progress')]) + today_date = fields.Date.today() + pending_subscription = False + for pending_subscription in pending_subscriptions: + get_dates = self.find_renew_date( + pending_subscription.next_invoice_date, + pending_subscription.date_started, + pending_subscription.plan_id.days_to_end) + renew_date = get_dates['renew_date'] + end_date = get_dates['end_date'] + pending_subscription.close_date = get_dates['close_date'] + if today_date == pending_subscription.next_invoice_date: + if pending_subscription.plan_id.invoice_mode == 'draft_invoice': + this_products_line = [] + for rec in pending_subscription.product_line_ids: + rec_list = [0, 0, {'product_id': rec.product_id.id, + 'quantity': rec.product_qty, + 'price_unit': rec.unit_price, + 'discount': rec.discount, + 'tax_ids': rec.tax_ids + }] + this_products_line.append(rec_list) + self.env['account.move'].create( + { + 'move_type': 'out_invoice', + 'invoice_date_due': today_date, + 'invoice_payment_term_id': False, + 'invoice_date': today_date, + 'state': 'draft', + 'subscription_id': pending_subscription.id, + 'partner_id': pending_subscription.partner_invoice_id.id, + 'currency_id': pending_subscription.partner_invoice_id.currency_id.id, + 'invoice_line_ids': this_products_line + }) + pending_subscription.write({ + 'is_to_renew': False, + 'start_date': pending_subscription.next_invoice_date}) + new_date = self.find_renew_date( + pending_subscription.next_invoice_date, + pending_subscription.date_started, + pending_subscription.plan_id.days_to_end) + pending_subscription.write( + {'close_date': new_date['close_date']}) + self.send_renew_alert_mail(today_date, + new_date['renew_date'], + pending_subscription.id) + + if (today_date == end_date) and ( + pending_subscription.plan_id.limit_choice != 'manual'): + display_msg = ("
The renewal limit has been exceeded " + "today for this subscription based on the " + "current subscription plan.
") + pending_subscription.message_post(body=display_msg) + pending_subscription.is_closed = True + reason = (self.env['subscription.package.stop'].search([ + ('name', '=', 'Renewal Limit Exceeded')]).id) + pending_subscription.close_reason_id = reason + pending_subscription.closed_by = self.user_id + pending_subscription.close_date = fields.Date.today() + stage = (self.env['subscription.package.stage'].search([ + ('category', '=', 'closed')]).id) + values = {'stage_id': stage, 'is_to_renew': False, + 'next_invoice_date': False} + pending_subscription.write(values) + + self.send_renew_alert_mail(today_date, renew_date, + pending_subscription.id) + + return dict(pending=pending_subscription) + + @api.depends('product_line_ids.total_amount', + 'product_line_ids.price_total', 'product_line_ids.tax_ids') + def _compute_total_recurring_price(self): + """ The compute function used to calculate recurring price """ + for record in self: + total_recurring = 0 + total_tax = 0.0 + for line in record.product_line_ids: + if line.total_amount != line.price_total: + line_tax = line.price_total - line.total_amount + total_tax += line_tax + total_recurring += line.total_amount + record['total_recurring_price'] = total_recurring + record['tax_total'] = total_tax + total_with_tax = total_recurring + total_tax + record['total_with_tax'] = total_with_tax + + def action_renew(self): + """ The function is used to perform the renewal + action for the subscription package.""" + return self.button_sale_order() diff --git a/subscription_package/models/subscription_package_plan.py b/subscription_package/models/subscription_package_plan.py new file mode 100644 index 000000000..8fb739785 --- /dev/null +++ b/subscription_package/models/subscription_package_plan.py @@ -0,0 +1,156 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import api, fields, models + + +class SubscriptionPackagePlan(models.Model): + _name = 'subscription.package.plan' + _description = 'Subscription Package Plan' + + name = fields.Char(string='Plan Name', required=True, + help='The name of the subscription plan.') + renewal_value = fields.Char(string='Renewal', + help='A descriptive value indicating the ' + 'renewal status or details for the ' + 'subscription plan.') + renewal_period = fields.Selection([('days', 'Day(s)'), + ('weeks', 'Week(s)'), + ('months', 'Month(s)'), + ('years', 'Year(s)')], + default='months', + help='Select the unit of time for the ' + 'renewal period of the ' + 'subscription plan.') + renewal_time = fields.Integer(string='Renewal Time Interval', + compute='_compute_renewal_time', + store=True, + help='The computed renewal time interval ' + 'for the subscription plan, based on ' + 'the selected renewal period.') + limit_choice = fields.Selection([('ones', 'Ones'), + ('manual', 'Until Closed Manually'), + ('custom', 'Custom')], + default='ones', + help='Select the limit choice for the ' + 'subscription plan, specifying how ' + 'long it will be active.') + limit_count = fields.Integer(string='Custom Renewal Limit', + help='Specify the custom renewal limit for ' + 'the subscription plan. This field is ' + 'relevant when the "Limit Choice" is ' + 'set to "Custom".') + days_to_end = fields.Integer(string='Days End', readonly=True, + compute='_compute_days_to_end', store=True, + help="Subscription ending date") + invoice_mode = fields.Selection([('manual', 'Manually'), + ('draft_invoice', 'Draft')], + default='draft_invoice', + help='Select the invoice mode for the ' + 'subscription plan, specifying ' + 'whether invoices are generated ' + 'manually or in draft state.') + journal_id = fields.Many2one('account.journal', string='Journal', + domain="[('type', '=', 'sale')]") + company_id = fields.Many2one('res.company', string='Company', store=True, + default=lambda self: self.env.company) + short_code = fields.Char(string='Short Code') + terms_and_conditions = fields.Text(string='Terms and Conditions') + product_count = fields.Integer(string='Products', + compute='_compute_product_count') + subscription_count = fields.Integer(string='Subscriptions', + compute='_compute_subscription_count') + + @api.depends('product_count') + def _compute_product_count(self): + """ Calculate product count based on subscription plan """ + self.product_count = self.env['product.product'].search_count( + [('subscription_plan_id', '=', self.id)]) + + @api.depends('subscription_count') + def _compute_subscription_count(self): + """ Calculate subscription count based on subscription plan """ + self.subscription_count = self.env[ + 'subscription.package'].search_count([('plan_id', '=', self.id)]) + + @api.depends('renewal_value', 'renewal_period') + def _compute_renewal_time(self): + """ This method calculate renewal time based on renewal value """ + for rec in self: + if int(rec.renewal_value) == 0 or int(rec.renewal_value) < 0: + rec.renewal_value = 1 + if rec.renewal_period == 'days': + rec.renewal_time = int(rec.renewal_value) + elif rec.renewal_period == 'weeks': + rec.renewal_time = int(rec.renewal_value) * 7 + elif rec.renewal_period == 'months': + rec.renewal_time = int(rec.renewal_value) * 28 + elif rec.renewal_period == 'years': + rec.renewal_time = int(rec.renewal_value) * 364 + if rec.name: + rec.short_code = str(rec.name[0:3]).upper() + + @api.depends('renewal_time', 'limit_count') + def _compute_days_to_end(self): + """ This method calculate days to end for subscription plan based on + limit count """ + for rec in self: + if rec.limit_count == 0 or rec.limit_count < 0: + rec.limit_count = 1 + if rec.limit_choice == 'ones': + rec.days_to_end = rec.renewal_time + if rec.limit_choice == 'manual': + rec.days_to_end = False + if rec.limit_choice == 'custom': + rec.days_to_end = rec.renewal_time * rec.limit_count + + def button_product_count(self): + """ It displays products based on subscription plan """ + return { + 'name': 'Products', + 'res_model': 'product.product', + 'domain': [('subscription_plan_id', '=', self.id)], + 'view_type': 'form', + 'view_mode': 'list,form', + 'type': 'ir.actions.act_window', + 'context': { + 'default_is_subscription': True, + }, + } + + def button_sub_count(self): + """ It displays subscriptions based on subscription plan """ + return { + 'name': 'Subscriptions', + 'domain': [('plan_id', '=', self.id)], + 'view_type': 'form', + 'res_model': 'subscription.package', + 'view_mode': 'list,form', + 'type': 'ir.actions.act_window', + } + + def name_get(self): + """ It displays record name as combination of short code and + plan name """ + res = [] + for rec in self: + res.append((rec.id, '%s - %s' % (rec.short_code, rec.name))) + return res diff --git a/subscription_package/models/subscription_package_product_line.py b/subscription_package/models/subscription_package_product_line.py new file mode 100644 index 000000000..fc6c504b2 --- /dev/null +++ b/subscription_package/models/subscription_package_product_line.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import api, models, fields + + +class SubscriptionPackageProductLine(models.Model): + """Subscription Package Product Line Model""" + _name = 'subscription.package.product.line' + _description = 'Subscription Package Product Lines' + + subscription_id = fields.Many2one('subscription.package', store=True, + string='Subscription', + help='Choose Subscription Package') + company_id = fields.Many2one('res.company', string='Company', store=True, + related='subscription_id.company_id') + create_date = fields.Datetime(string='Create date', store=True, + default=fields.Datetime.now, + help='Add Create Date') + user_id = fields.Many2one('res.users', string='Salesperson', store=True, + related='subscription_id.user_id', + help='Add Salesperson') + product_id = fields.Many2one('product.product', string='Product', + store=True, ondelete='restrict', + domain=[('is_subscription', '=', True)], + help='Choose Product') + product_qty = fields.Float(string='Quantity', store=True, default=1.0, + help='Add Product Quantity') + product_uom_id = fields.Many2one('uom.uom', string='UoM', store=True, + related='product_id.uom_id', + ondelete='restrict', + help='Add Product UOM') + uom_catg_id = fields.Many2one('uom.category', string='UoM Category', + store=True, + related='product_id.uom_id.category_id', + help='Choose Product Uom quantity') + unit_price = fields.Float(string='Unit Price', store=True, readonly=False, + related='product_id.list_price', + help='Add Product Unit Price') + discount = fields.Float(string="Discount (%)", help='Add Discount') + tax_ids = fields.Many2many('account.tax', string="Taxes", + ondelete='restrict', + related='product_id.taxes_id', readonly=False, + help='Add Taxes') + price_total = fields.Monetary(store=True, readonly=True, + help='Add Product Price Total') + price_tax = fields.Monetary(store=True, readonly=True, string='Price Tax', + help='Add Price Tax') + currency_id = fields.Many2one('res.currency', string='Currency', + store=True, help='Add Subscription Currency', + related='subscription_id.currency_id') + total_amount = fields.Monetary(string='Subtotal', store=True, + help='Add Total Amount', + compute='_compute_total_amount') + sequence = fields.Integer('Sequence', help="Determine the display order", + index=True) + res_partner_id = fields.Many2one('res.partner', string='Partner', + store=True, help='Choose the Partner', + related='subscription_id.partner_id') + + @api.depends('product_qty', 'unit_price', 'discount', 'tax_ids', + 'currency_id') + def _compute_total_amount(self): + """ Calculate subtotal amount of product line """ + for line in self: + price = line.unit_price * (1 - (line.discount or 0.0) / 100.0) + taxes = line.tax_ids._origin.compute_all(price, + line.subscription_id._origin.currency_id, + line.product_qty, + product=line.product_id, + partner=line.subscription_id._origin.partner_id) + line.write({ + 'price_tax': sum( + t.get('amount', 0.0) for t in taxes.get('taxes', [])), + 'price_total': taxes['total_included'], + 'total_amount': taxes['total_excluded'], + }) + + def _valid_field_parameter(self, field, name): + if name == 'ondelete': + return True + return super(SubscriptionPackageProductLine, + self)._valid_field_parameter(field, name) diff --git a/subscription_package/models/subscription_package_stage.py b/subscription_package/models/subscription_package_stage.py new file mode 100644 index 000000000..79009ce78 --- /dev/null +++ b/subscription_package/models/subscription_package_stage.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import models, fields + + +class SubscriptionPackageStage(models.Model): + _name = "subscription.package.stage" + _description = "Subscription Package Stages" + _rec_name = 'name' + + name = fields.Char(string='Stage Name', required=True, + help=' Enter a descriptive name for the stage') + sequence = fields.Integer('Sequence', help="Determine the display order", + index=True) + condition = fields.Text(string='Conditions', + help='Use this field to provide detailed ' + 'information about the conditions') + is_fold = fields.Boolean(string='Folded in Kanban', + help="This stage is folded in the kanban view " + "when there are no records in that stage " + "to display.") + category = fields.Selection([('draft', 'Draft'), + ('progress', 'In Progress'), + ('closed', 'Closed')], + readonly=False, default='draft', + help='Choose the appropriate category from' + ' the available options.') diff --git a/subscription_package/models/subscription_package_stop.py b/subscription_package/models/subscription_package_stop.py new file mode 100644 index 000000000..5a1560db3 --- /dev/null +++ b/subscription_package/models/subscription_package_stop.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import models, fields + + +class SubscriptionPackageStop(models.Model): + _name = "subscription.package.stop" + _description = "Subscription Package Stop Reason" + _order = 'sequence' + + sequence = fields.Integer(help="Determine the display order", index=True, + string='Sequence') + name = fields.Char(string='Reason', required=True, + help='Enter the reason for stopping the ' + 'subscription package.') diff --git a/subscription_package/report/__init__.py b/subscription_package/report/__init__.py new file mode 100644 index 000000000..7fd86733d --- /dev/null +++ b/subscription_package/report/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from . import subscription_report diff --git a/subscription_package/report/subscription_report.py b/subscription_package/report/subscription_report.py new file mode 100644 index 000000000..acc2b0f20 --- /dev/null +++ b/subscription_package/report/subscription_report.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import models, fields +from odoo import tools + + +class SubscriptionReport(models.Model): + _name = "subscription.report" + _description = "Subscription Analysis" + _auto = False + + total_recurring_price = fields.Float('Recurring Price', readonly=True, + help='Total recurring price ' + 'associated with this ' + 'subscription analysis.') + quantity = fields.Float('Quantity', readonly=True, + help='The quantity associated with this ' + 'subscription analysis.') + user_id = fields.Many2one('res.users', 'Salesperson', readonly=True, + help='The salesperson associated with' + ' this record.') + plan_id = fields.Many2one('subscription.package.plan', + 'Subscription Template', readonly=True, + help='The subscription template ' + 'associated with this record.') + + def _query(self): + select_ = """ + SELECT min(sl.id) as id, + sl.product_qty as quantity, + sub.total_recurring_price as total_recurring_price, + sub.user_id as user_id, + sub.plan_id as plan_id, + sub.name as name + """ + from_ = """ + subscription_package_product_line sl + join subscription_package sub on (sl.subscription_id = sub.id) + """ + groupby_ = """ + GROUP BY sl.product_qty, + sub.total_recurring_price, + sub.user_id, + sub.plan_id, + sub.name + """ + return '%s FROM ( %s ) %s' % (select_, from_, groupby_) + + def init(self): + tools.drop_view_if_exists(self.env.cr, self._table) + self.env.cr.execute("""CREATE or REPLACE VIEW %s as (%s)""" % ( + self._table, self._query())) diff --git a/subscription_package/report/subscription_report_view.xml b/subscription_package/report/subscription_report_view.xml new file mode 100644 index 000000000..7cbbc52eb --- /dev/null +++ b/subscription_package/report/subscription_report_view.xml @@ -0,0 +1,36 @@ + + + + + Subscription Report Pivot + subscription.report + + + + + + + + + + Subscription Report Graph + subscription.report + + + + + + + + + Subscriptions Report + subscription.report + pivot,graph + + + + diff --git a/subscription_package/security/ir.model.access.csv b/subscription_package/security/ir.model.access.csv new file mode 100644 index 000000000..702e98d88 --- /dev/null +++ b/subscription_package/security/ir.model.access.csv @@ -0,0 +1,9 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_subscription_package_plan,subscription.package_plan,model_subscription_package_plan,base.group_user,1,1,1,1 +access_subscription_package_stage,subscription.package.stage,model_subscription_package_stage,base.group_user,1,1,1,1 +access_subscription_package,subscription.package,model_subscription_package,base.group_user,1,1,1,1 +access_subscription_report,subscription.report,model_subscription_report,base.group_user,1,1,1,1 +access_subscription_package_product_line,subscription.package.product.line,model_subscription_package_product_line,base.group_user,1,1,1,1 +access_subscription_package_stop,subscription.package.stop,model_subscription_package_stop,base.group_user,1,1,1,1 +access_subscription_close,access.subscription.close,model_subscription_close,base.group_user,1,1,1,1 +access_recurrence_period,access.recurrence.period,model_recurrence_period,base.group_user,1,1,1,1 diff --git a/subscription_package/security/subscription_package_groups.xml b/subscription_package/security/subscription_package_groups.xml new file mode 100644 index 000000000..3e264aee9 --- /dev/null +++ b/subscription_package/security/subscription_package_groups.xml @@ -0,0 +1,23 @@ + + + + + + Subscription + Helps you handle your subscription security. + 9 + + + + User + + + + + + Subscription Administrator + + + + + diff --git a/subscription_package/static/description/assets/cybro-icon.png b/subscription_package/static/description/assets/cybro-icon.png new file mode 100644 index 000000000..06e73e11d Binary files /dev/null and b/subscription_package/static/description/assets/cybro-icon.png differ diff --git a/subscription_package/static/description/assets/cybro-odoo.png b/subscription_package/static/description/assets/cybro-odoo.png new file mode 100644 index 000000000..ed02e07a4 Binary files /dev/null and b/subscription_package/static/description/assets/cybro-odoo.png differ diff --git a/subscription_package/static/description/assets/h2.png b/subscription_package/static/description/assets/h2.png new file mode 100644 index 000000000..0bfc4707d Binary files /dev/null and b/subscription_package/static/description/assets/h2.png differ diff --git a/subscription_package/static/description/assets/icons/arrows-repeat.svg b/subscription_package/static/description/assets/icons/arrows-repeat.svg new file mode 100644 index 000000000..1d7efabc5 --- /dev/null +++ b/subscription_package/static/description/assets/icons/arrows-repeat.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/banner-1.png b/subscription_package/static/description/assets/icons/banner-1.png new file mode 100644 index 000000000..c180db172 Binary files /dev/null and b/subscription_package/static/description/assets/icons/banner-1.png differ diff --git a/subscription_package/static/description/assets/icons/banner-2.svg b/subscription_package/static/description/assets/icons/banner-2.svg new file mode 100644 index 000000000..e606d97d9 --- /dev/null +++ b/subscription_package/static/description/assets/icons/banner-2.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/banner-bg.png b/subscription_package/static/description/assets/icons/banner-bg.png new file mode 100644 index 000000000..a8238d3c0 Binary files /dev/null and b/subscription_package/static/description/assets/icons/banner-bg.png differ diff --git a/subscription_package/static/description/assets/icons/banner-bg.svg b/subscription_package/static/description/assets/icons/banner-bg.svg new file mode 100644 index 000000000..b1378103e --- /dev/null +++ b/subscription_package/static/description/assets/icons/banner-bg.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/banner-call.svg b/subscription_package/static/description/assets/icons/banner-call.svg new file mode 100644 index 000000000..96c687e81 --- /dev/null +++ b/subscription_package/static/description/assets/icons/banner-call.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/subscription_package/static/description/assets/icons/banner-mail.svg b/subscription_package/static/description/assets/icons/banner-mail.svg new file mode 100644 index 000000000..cbf0d158d --- /dev/null +++ b/subscription_package/static/description/assets/icons/banner-mail.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/subscription_package/static/description/assets/icons/banner-pattern.svg b/subscription_package/static/description/assets/icons/banner-pattern.svg new file mode 100644 index 000000000..9c1c7e101 --- /dev/null +++ b/subscription_package/static/description/assets/icons/banner-pattern.svg @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/banner-promo.svg b/subscription_package/static/description/assets/icons/banner-promo.svg new file mode 100644 index 000000000..d52791b11 --- /dev/null +++ b/subscription_package/static/description/assets/icons/banner-promo.svg @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/brand-pair.svg b/subscription_package/static/description/assets/icons/brand-pair.svg new file mode 100644 index 000000000..d8db7fc1e --- /dev/null +++ b/subscription_package/static/description/assets/icons/brand-pair.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/check.png b/subscription_package/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/subscription_package/static/description/assets/icons/check.png differ diff --git a/subscription_package/static/description/assets/icons/chevron.png b/subscription_package/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/subscription_package/static/description/assets/icons/chevron.png differ diff --git a/subscription_package/static/description/assets/icons/close-icon.svg b/subscription_package/static/description/assets/icons/close-icon.svg new file mode 100644 index 000000000..df8cce37a --- /dev/null +++ b/subscription_package/static/description/assets/icons/close-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/subscription_package/static/description/assets/icons/cogs.png b/subscription_package/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/subscription_package/static/description/assets/icons/cogs.png differ diff --git a/subscription_package/static/description/assets/icons/collabarate-icon.svg b/subscription_package/static/description/assets/icons/collabarate-icon.svg new file mode 100644 index 000000000..dd4e10518 --- /dev/null +++ b/subscription_package/static/description/assets/icons/collabarate-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/subscription_package/static/description/assets/icons/consultation.png b/subscription_package/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/subscription_package/static/description/assets/icons/consultation.png differ diff --git a/subscription_package/static/description/assets/icons/cybro-logo.png b/subscription_package/static/description/assets/icons/cybro-logo.png new file mode 100644 index 000000000..ff4b78220 Binary files /dev/null and b/subscription_package/static/description/assets/icons/cybro-logo.png differ diff --git a/subscription_package/static/description/assets/icons/down.svg b/subscription_package/static/description/assets/icons/down.svg new file mode 100644 index 000000000..f21c36271 --- /dev/null +++ b/subscription_package/static/description/assets/icons/down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/subscription_package/static/description/assets/icons/ecom-black.png b/subscription_package/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/subscription_package/static/description/assets/icons/ecom-black.png differ diff --git a/subscription_package/static/description/assets/icons/education-black.png b/subscription_package/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/subscription_package/static/description/assets/icons/education-black.png differ diff --git a/subscription_package/static/description/assets/icons/faq.png b/subscription_package/static/description/assets/icons/faq.png new file mode 100644 index 000000000..4250b5b81 Binary files /dev/null and b/subscription_package/static/description/assets/icons/faq.png differ diff --git a/subscription_package/static/description/assets/icons/feature-icon.svg b/subscription_package/static/description/assets/icons/feature-icon.svg new file mode 100644 index 000000000..fa0ea6850 --- /dev/null +++ b/subscription_package/static/description/assets/icons/feature-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/feature.png b/subscription_package/static/description/assets/icons/feature.png new file mode 100644 index 000000000..ac7a785c0 Binary files /dev/null and b/subscription_package/static/description/assets/icons/feature.png differ diff --git a/subscription_package/static/description/assets/icons/gear.svg b/subscription_package/static/description/assets/icons/gear.svg new file mode 100644 index 000000000..0cc66b6ea --- /dev/null +++ b/subscription_package/static/description/assets/icons/gear.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/hero.gif b/subscription_package/static/description/assets/icons/hero.gif new file mode 100644 index 000000000..380654dfe Binary files /dev/null and b/subscription_package/static/description/assets/icons/hero.gif differ diff --git a/subscription_package/static/description/assets/icons/hire-odoo.svg b/subscription_package/static/description/assets/icons/hire-odoo.svg new file mode 100644 index 000000000..e1ac089b0 --- /dev/null +++ b/subscription_package/static/description/assets/icons/hire-odoo.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/hotel-black.png b/subscription_package/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/subscription_package/static/description/assets/icons/hotel-black.png differ diff --git a/subscription_package/static/description/assets/icons/license.png b/subscription_package/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/subscription_package/static/description/assets/icons/license.png differ diff --git a/subscription_package/static/description/assets/icons/life-ring-icon.svg b/subscription_package/static/description/assets/icons/life-ring-icon.svg new file mode 100644 index 000000000..3ae6e1d89 --- /dev/null +++ b/subscription_package/static/description/assets/icons/life-ring-icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/lifebuoy.png b/subscription_package/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/subscription_package/static/description/assets/icons/lifebuoy.png differ diff --git a/subscription_package/static/description/assets/icons/mail.svg b/subscription_package/static/description/assets/icons/mail.svg new file mode 100644 index 000000000..1eedde695 --- /dev/null +++ b/subscription_package/static/description/assets/icons/mail.svg @@ -0,0 +1,3 @@ + + + diff --git a/subscription_package/static/description/assets/icons/manufacturing-black.png b/subscription_package/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/subscription_package/static/description/assets/icons/manufacturing-black.png differ diff --git a/subscription_package/static/description/assets/icons/notes.png b/subscription_package/static/description/assets/icons/notes.png new file mode 100644 index 000000000..ee5e95404 Binary files /dev/null and b/subscription_package/static/description/assets/icons/notes.png differ diff --git a/subscription_package/static/description/assets/icons/notification icon.svg b/subscription_package/static/description/assets/icons/notification icon.svg new file mode 100644 index 000000000..053189973 --- /dev/null +++ b/subscription_package/static/description/assets/icons/notification icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/odoo-consultancy.svg b/subscription_package/static/description/assets/icons/odoo-consultancy.svg new file mode 100644 index 000000000..e05f65bde --- /dev/null +++ b/subscription_package/static/description/assets/icons/odoo-consultancy.svg @@ -0,0 +1,4 @@ + + + + diff --git a/subscription_package/static/description/assets/icons/odoo-licencing.svg b/subscription_package/static/description/assets/icons/odoo-licencing.svg new file mode 100644 index 000000000..2606c88b0 --- /dev/null +++ b/subscription_package/static/description/assets/icons/odoo-licencing.svg @@ -0,0 +1,3 @@ + + + diff --git a/subscription_package/static/description/assets/icons/odoo-logo.png b/subscription_package/static/description/assets/icons/odoo-logo.png new file mode 100644 index 000000000..0e4d0eb5a Binary files /dev/null and b/subscription_package/static/description/assets/icons/odoo-logo.png differ diff --git a/subscription_package/static/description/assets/icons/patter.svg b/subscription_package/static/description/assets/icons/patter.svg new file mode 100644 index 000000000..25c9c0a8f --- /dev/null +++ b/subscription_package/static/description/assets/icons/patter.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/pattern1.png b/subscription_package/static/description/assets/icons/pattern1.png new file mode 100644 index 000000000..09ab0fb2d Binary files /dev/null and b/subscription_package/static/description/assets/icons/pattern1.png differ diff --git a/subscription_package/static/description/assets/icons/pos-black.png b/subscription_package/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/subscription_package/static/description/assets/icons/pos-black.png differ diff --git a/subscription_package/static/description/assets/icons/puzzle-piece-icon.svg b/subscription_package/static/description/assets/icons/puzzle-piece-icon.svg new file mode 100644 index 000000000..3e9ad9373 --- /dev/null +++ b/subscription_package/static/description/assets/icons/puzzle-piece-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/puzzle.png b/subscription_package/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/subscription_package/static/description/assets/icons/puzzle.png differ diff --git a/subscription_package/static/description/assets/icons/replace-icon.svg b/subscription_package/static/description/assets/icons/replace-icon.svg new file mode 100644 index 000000000..d0e3a7af1 --- /dev/null +++ b/subscription_package/static/description/assets/icons/replace-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/restaurant-black.png b/subscription_package/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/subscription_package/static/description/assets/icons/restaurant-black.png differ diff --git a/subscription_package/static/description/assets/icons/screenshot-main.png b/subscription_package/static/description/assets/icons/screenshot-main.png new file mode 100644 index 000000000..575f8e676 Binary files /dev/null and b/subscription_package/static/description/assets/icons/screenshot-main.png differ diff --git a/subscription_package/static/description/assets/icons/screenshot.png b/subscription_package/static/description/assets/icons/screenshot.png new file mode 100644 index 000000000..cef272529 Binary files /dev/null and b/subscription_package/static/description/assets/icons/screenshot.png differ diff --git a/subscription_package/static/description/assets/icons/service-black.png b/subscription_package/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/subscription_package/static/description/assets/icons/service-black.png differ diff --git a/subscription_package/static/description/assets/icons/skype-fill.svg b/subscription_package/static/description/assets/icons/skype-fill.svg new file mode 100644 index 000000000..c17423639 --- /dev/null +++ b/subscription_package/static/description/assets/icons/skype-fill.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/skype.png b/subscription_package/static/description/assets/icons/skype.png new file mode 100644 index 000000000..51b409fb3 Binary files /dev/null and b/subscription_package/static/description/assets/icons/skype.png differ diff --git a/subscription_package/static/description/assets/icons/skype.svg b/subscription_package/static/description/assets/icons/skype.svg new file mode 100644 index 000000000..df3dad39b --- /dev/null +++ b/subscription_package/static/description/assets/icons/skype.svg @@ -0,0 +1,3 @@ + + + diff --git a/subscription_package/static/description/assets/icons/star-1.svg b/subscription_package/static/description/assets/icons/star-1.svg new file mode 100644 index 000000000..7e55ab162 --- /dev/null +++ b/subscription_package/static/description/assets/icons/star-1.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/star-2.svg b/subscription_package/static/description/assets/icons/star-2.svg new file mode 100644 index 000000000..5ae9f507a --- /dev/null +++ b/subscription_package/static/description/assets/icons/star-2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/support.png b/subscription_package/static/description/assets/icons/support.png new file mode 100644 index 000000000..4f18b8b82 Binary files /dev/null and b/subscription_package/static/description/assets/icons/support.png differ diff --git a/subscription_package/static/description/assets/icons/test-1 - Copy.png b/subscription_package/static/description/assets/icons/test-1 - Copy.png new file mode 100644 index 000000000..f6a902663 Binary files /dev/null and b/subscription_package/static/description/assets/icons/test-1 - Copy.png differ diff --git a/subscription_package/static/description/assets/icons/test-1.png b/subscription_package/static/description/assets/icons/test-1.png new file mode 100644 index 000000000..0908add2b Binary files /dev/null and b/subscription_package/static/description/assets/icons/test-1.png differ diff --git a/subscription_package/static/description/assets/icons/test-2.png b/subscription_package/static/description/assets/icons/test-2.png new file mode 100644 index 000000000..4671fe91e Binary files /dev/null and b/subscription_package/static/description/assets/icons/test-2.png differ diff --git a/subscription_package/static/description/assets/icons/trading-black.png b/subscription_package/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/subscription_package/static/description/assets/icons/trading-black.png differ diff --git a/subscription_package/static/description/assets/icons/training.png b/subscription_package/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/subscription_package/static/description/assets/icons/training.png differ diff --git a/subscription_package/static/description/assets/icons/translate.svg b/subscription_package/static/description/assets/icons/translate.svg new file mode 100644 index 000000000..af9c8a1aa --- /dev/null +++ b/subscription_package/static/description/assets/icons/translate.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/update.png b/subscription_package/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/subscription_package/static/description/assets/icons/update.png differ diff --git a/subscription_package/static/description/assets/icons/user.png b/subscription_package/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/subscription_package/static/description/assets/icons/user.png differ diff --git a/subscription_package/static/description/assets/icons/video.png b/subscription_package/static/description/assets/icons/video.png new file mode 100644 index 000000000..576705b17 Binary files /dev/null and b/subscription_package/static/description/assets/icons/video.png differ diff --git a/subscription_package/static/description/assets/icons/whatsapp.png b/subscription_package/static/description/assets/icons/whatsapp.png new file mode 100644 index 000000000..d513a5356 Binary files /dev/null and b/subscription_package/static/description/assets/icons/whatsapp.png differ diff --git a/subscription_package/static/description/assets/icons/wrench-icon.svg b/subscription_package/static/description/assets/icons/wrench-icon.svg new file mode 100644 index 000000000..174b5a465 --- /dev/null +++ b/subscription_package/static/description/assets/icons/wrench-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/subscription_package/static/description/assets/icons/wrench.png b/subscription_package/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/subscription_package/static/description/assets/icons/wrench.png differ diff --git a/subscription_package/static/description/assets/modules/1.gif b/subscription_package/static/description/assets/modules/1.gif new file mode 100644 index 000000000..ae3a880a2 Binary files /dev/null and b/subscription_package/static/description/assets/modules/1.gif differ diff --git a/subscription_package/static/description/assets/modules/2.gif b/subscription_package/static/description/assets/modules/2.gif new file mode 100644 index 000000000..d19e2b352 Binary files /dev/null and b/subscription_package/static/description/assets/modules/2.gif differ diff --git a/subscription_package/static/description/assets/modules/3.png b/subscription_package/static/description/assets/modules/3.png new file mode 100644 index 000000000..8513873ea Binary files /dev/null and b/subscription_package/static/description/assets/modules/3.png differ diff --git a/subscription_package/static/description/assets/modules/4.png b/subscription_package/static/description/assets/modules/4.png new file mode 100644 index 000000000..3bedf7981 Binary files /dev/null and b/subscription_package/static/description/assets/modules/4.png differ diff --git a/subscription_package/static/description/assets/modules/5.png b/subscription_package/static/description/assets/modules/5.png new file mode 100644 index 000000000..0e311ca87 Binary files /dev/null and b/subscription_package/static/description/assets/modules/5.png differ diff --git a/subscription_package/static/description/assets/modules/6.jpg b/subscription_package/static/description/assets/modules/6.jpg new file mode 100644 index 000000000..67c7f7062 Binary files /dev/null and b/subscription_package/static/description/assets/modules/6.jpg differ diff --git a/subscription_package/static/description/assets/screenshots/1.png b/subscription_package/static/description/assets/screenshots/1.png new file mode 100644 index 000000000..8112f4fc1 Binary files /dev/null and b/subscription_package/static/description/assets/screenshots/1.png differ diff --git a/subscription_package/static/description/assets/screenshots/2.png b/subscription_package/static/description/assets/screenshots/2.png new file mode 100644 index 000000000..e3c6ad433 Binary files /dev/null and b/subscription_package/static/description/assets/screenshots/2.png differ diff --git a/subscription_package/static/description/assets/screenshots/3.png b/subscription_package/static/description/assets/screenshots/3.png new file mode 100644 index 000000000..a59941d6a Binary files /dev/null and b/subscription_package/static/description/assets/screenshots/3.png differ diff --git a/subscription_package/static/description/assets/screenshots/4.png b/subscription_package/static/description/assets/screenshots/4.png new file mode 100644 index 000000000..f14babcbd Binary files /dev/null and b/subscription_package/static/description/assets/screenshots/4.png differ diff --git a/subscription_package/static/description/assets/screenshots/5.png b/subscription_package/static/description/assets/screenshots/5.png new file mode 100644 index 000000000..1d8a9ddfa Binary files /dev/null and b/subscription_package/static/description/assets/screenshots/5.png differ diff --git a/subscription_package/static/description/assets/screenshots/6.png b/subscription_package/static/description/assets/screenshots/6.png new file mode 100644 index 000000000..d9d5b8063 Binary files /dev/null and b/subscription_package/static/description/assets/screenshots/6.png differ diff --git a/subscription_package/static/description/assets/screenshots/7.png b/subscription_package/static/description/assets/screenshots/7.png new file mode 100644 index 000000000..864bf8721 Binary files /dev/null and b/subscription_package/static/description/assets/screenshots/7.png differ diff --git a/subscription_package/static/description/assets/screenshots/8.png b/subscription_package/static/description/assets/screenshots/8.png new file mode 100644 index 000000000..eb4a07abd Binary files /dev/null and b/subscription_package/static/description/assets/screenshots/8.png differ diff --git a/subscription_package/static/description/assets/screenshots/9.png b/subscription_package/static/description/assets/screenshots/9.png new file mode 100644 index 000000000..169e503f3 Binary files /dev/null and b/subscription_package/static/description/assets/screenshots/9.png differ diff --git a/subscription_package/static/description/assets/screenshots/hero.gif b/subscription_package/static/description/assets/screenshots/hero.gif new file mode 100644 index 000000000..5b11841b2 Binary files /dev/null and b/subscription_package/static/description/assets/screenshots/hero.gif differ diff --git a/subscription_package/static/description/assets/y18.jpg b/subscription_package/static/description/assets/y18.jpg new file mode 100644 index 000000000..eea1714f2 Binary files /dev/null and b/subscription_package/static/description/assets/y18.jpg differ diff --git a/subscription_package/static/description/banner.jpg b/subscription_package/static/description/banner.jpg new file mode 100644 index 000000000..d93a48f3a Binary files /dev/null and b/subscription_package/static/description/banner.jpg differ diff --git a/subscription_package/static/description/icon.png b/subscription_package/static/description/icon.png new file mode 100644 index 000000000..8341e04d3 Binary files /dev/null and b/subscription_package/static/description/icon.png differ diff --git a/subscription_package/static/description/index.html b/subscription_package/static/description/index.html new file mode 100644 index 000000000..97ab5dfe9 --- /dev/null +++ b/subscription_package/static/description/index.html @@ -0,0 +1,1282 @@ + + + + + + Subscription Management + + + + + + + + + + +
+
+ + + +
+
+ Community +
+ + + + + + + + +
+
+ +
+
+
+
+

+ For Efficiently Handling Subscription Packages in The Community Version of Odoo 18. +

+

Subscription Management +

+
+
+ +
+ +
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+

Key + Highlights

+
+
+
+
+ +
+
+ Community compatible. +
+

+

+
+
+
+
+
+ +
+
+ Automatic Subscription Closed +
+

+

+
+
+
+
+
+ +
+
+ Recurring Invoices Daily/Weekly/Monthly/Yearly. +
+

+

+
+
+
+
+
+ +
+
+ Subscription Package Plan. +
+

+

+
+
+
+
+
+ +
+
+ Create Subscription Plans. +
+

+

+
+
+
+
+
+ +
+
+ Subscription Renewable. +
+

+

+
+
+
+
+ +
+
+
+ Subscription Management +

+ Are you ready to make your business more + organized? +
Improve now! +

+ +
+
+ +
+
+
+ + + + +
+
+ +
+
+
+
+ acc_bg +
+ +
+
+
+
+

+ Create Subscription Plan. + + +

+
+
+

+

+
+
+
+ +
+
+
+
+
+
+
+
+
+

+ Create Subscription Product. + + +

+
+
+

+

+
+
+
+ +
+
+
+
+
+
+
+
+
+

+ Create Subscription Customer. + +

+
+
+

+

+
+
+
+ +
+
+
+
+
+
+
+
+
+

+ Activity Types. + + +

+
+
+

+

+
+
+
+ +
+
+
+
+
+
+
+
+
+

+ Subscription Close Reasons. + + + +

+
+
+

+

+
+
+
+ +
+
+
+
+
+
+
+
+
+

+ Subscription Stages. + + +

+
+
+

+

+
+
+
+ +
+
+
+
+
+
+
+
+
+

+ Create Recurrence Period. + +

+
+
+

+

+
+
+
+ +
+
+
+
+
+
+
+
+
+

+ Subscription Report. + +

+
+
+

+

+
+
+
+ +
+
+
+
+
+
+
+
+
+

+ Create Subscription Package with Sale Order. + +

+
+
+

+

+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+ +
+

+ Community compatible.

+
+
+
+
+
+
+
+ +
+

+ Automatic Subscription Closed.

+
+
+
+
+
+
+
+ +
+

+ Recurring Invoices Daily/Weekly/Monthly/Yearly.

+
+
+

+

+
+ +
+
+
+
+
+
+ +
+

+ Subscription Package Plan.

+
+
+

+

+
+ +
+
+
+
+
+
+ +
+

+ Create Subscription Plans.

+
+
+
+
+
+
+
+ +
+

+ Subscription Renewable.

+
+
+
+
+
+
+
+
+
+
+ +
+ +
+ +
+

+ While configuring a + backup, selecting + the Zip option will + include the + filestore in the + backup, while + choosing the Dump + option will create a + backup without the + filestore. +

+
+
+ +
+ +
+

+ Enable the "Remove + Old Backups" option + in the backup + creation view to + automatically delete + previous backups + based on the number + of days specified. +

+
+
+ +
+ +
+

+ Enable the "Notify + User" option and + specify a contact to + receive an email + containing a + detailed report with + the failure reason + and backup details. + This option will + also send an email + upon successful + backup. +

+
+
+ +
+ +
+

+ Select the backup + destination as local + storage and specify + a backup path to a + location on the + system to create + backups on your own + system. +

+
+
+
+ +
+
+
+
+
+
+ +
+
+

+ Latest Release 18.0.1.0.0 +

+ + 30th January, 2025 + +
+
+
+
+
+ Add +
+
+
+
    +
  • + Initial Commit +
  • + +
+
+
+
+
+
+
+
+
+
+ + + +
+

+ Related Products +

+ +
+ + +
+

+ Our Services

+ +
+ +
+
+ .... +
+
+ +
+ + +
+
+ + + + + + diff --git a/subscription_package/views/mail_activity_views.xml b/subscription_package/views/mail_activity_views.xml new file mode 100644 index 000000000..0e5a3fdad --- /dev/null +++ b/subscription_package/views/mail_activity_views.xml @@ -0,0 +1,17 @@ + + + + + Activity Types + mail.activity.type + list,form + ['|', ('res_model', '=', False), ('res_model', '=', 'subscription.package')] + {'default_res_model': 'subscription.package'} + + + + diff --git a/subscription_package/views/product_product_views.xml b/subscription_package/views/product_product_views.xml new file mode 100644 index 000000000..18dac70ab --- /dev/null +++ b/subscription_package/views/product_product_views.xml @@ -0,0 +1,26 @@ + + + + + + product.product.view.form.inherit.subscription.package + + product.product + + 25 + + + + + + + + + + + + + + diff --git a/subscription_package/views/product_template_views.xml b/subscription_package/views/product_template_views.xml new file mode 100644 index 000000000..e897b4fbf --- /dev/null +++ b/subscription_package/views/product_template_views.xml @@ -0,0 +1,64 @@ + + + + + product.template.view.search.inherit.subscription.package + product.template + + 25 + + + + + + + + + + product.template.view.form.inherit.subscription.package + product.template + + 25 + + + + + + + + + + + + + + + + Products + ir.actions.act_window + product.template + kanban,list,form,activity + + + { + 'search_default_filter_is_subscription': True, + 'search_default_filter_to_sell':1, + 'default_detailed_type':'service', + 'default_is_subscription':True} + + +

+ Create a new product +

+
+
+ + +
diff --git a/subscription_package/views/recurrence_period_views.xml b/subscription_package/views/recurrence_period_views.xml new file mode 100644 index 000000000..c4aba26f7 --- /dev/null +++ b/subscription_package/views/recurrence_period_views.xml @@ -0,0 +1,43 @@ + + + + + recurrence.period.view.tree + recurrence.period + + + + + + + + + + + recurrence.period.view.form + recurrence.period + +
+ + + + + + + +
+
+
+ + + Recurrence Period + recurrence.period + list,form + + + +
diff --git a/subscription_package/views/res_partner_views.xml b/subscription_package/views/res_partner_views.xml new file mode 100644 index 000000000..8664a56e7 --- /dev/null +++ b/subscription_package/views/res_partner_views.xml @@ -0,0 +1,24 @@ + + + + + res.partner.view.form.inherit..subscription.package + res.partner + + + + + + + + + + + + + + + + + + diff --git a/subscription_package/views/sale_order_views.xml b/subscription_package/views/sale_order_views.xml new file mode 100644 index 000000000..88f0038c0 --- /dev/null +++ b/subscription_package/views/sale_order_views.xml @@ -0,0 +1,19 @@ + + + + + sale.order.view.form.inherit.subscription.package + + sale.order + + +
+ +
+
+
+
diff --git a/subscription_package/views/subscription_package_plan_views.xml b/subscription_package/views/subscription_package_plan_views.xml new file mode 100644 index 000000000..e98bc022e --- /dev/null +++ b/subscription_package/views/subscription_package_plan_views.xml @@ -0,0 +1,116 @@ + + + + + Subscription Plan + ir.actions.act_window + subscription.package.plan + list,form + +

+ Create a new subscription plan ! +

+
+
+ + + subscription.package.plan.view.tree + subscription.package.plan + + + + + + + + + + + + subscription.package.plan.view.form + subscription.package.plan + +
+ +
+ + +
+
+

+ +

+
+ + + + + + + + + + + + + + +
+
+
+
+ + + + + +
diff --git a/subscription_package/views/subscription_package_stop_views.xml b/subscription_package/views/subscription_package_stop_views.xml new file mode 100644 index 000000000..a04c98aca --- /dev/null +++ b/subscription_package/views/subscription_package_stop_views.xml @@ -0,0 +1,28 @@ + + + + + Subscription Stop Reason + subscription.package.stop + list + + + + subscription.package.stop.view.tree + subscription.package.stop + + + + + + + + + + + diff --git a/subscription_package/views/subscription_package_views.xml b/subscription_package/views/subscription_package_views.xml new file mode 100644 index 000000000..8d9fbcf6f --- /dev/null +++ b/subscription_package/views/subscription_package_views.xml @@ -0,0 +1,287 @@ + + + + + Subscriptions + ir.actions.act_window + subscription.package + kanban,list,form,pivot + +

+ Create a new subscription package ! +

+
+
+ + Subscriptions Customers + ir.actions.act_window + res.partner + kanban,list,form + [('is_active_subscription', '=', True)] + +

+ Create a new subscription package ! +

+
+
+ + Subscriptions to Renew + ir.actions.act_window + subscription.package + [('is_to_renew', '=', True)] + kanban,list,form,pivot + +

+ Create a new subscription package ! +

+
+
+ + + subscription.package.view.tree + subscription.package + + + + + + + + + + + + + subscription.package.view.form + subscription.package + + +
+
+ + +
+ +
+ + +
+
+ +
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ + + subscription.package.view.kanban + subscription.package + + + + + + + + + + +
+
+
+ +
+
+ +
+
+ To Renew +
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + + + + + subscription.package.view.pivot + subscription.package + + + + + + + + + + + + + + + diff --git a/subscription_package/views/subscription_stage_views.xml b/subscription_package/views/subscription_stage_views.xml new file mode 100644 index 000000000..2ae9e0b55 --- /dev/null +++ b/subscription_package/views/subscription_stage_views.xml @@ -0,0 +1,50 @@ + + + + + Stages + subscription.package.stage + list,form + + + + subscription.package.stage.view.tree + subscription.package.stage + + + + + + + + + + subscription.package.stage.view.form + subscription.package.stage + +
+ + + + + + + + + + + + + + +
+
+
+ + +
diff --git a/subscription_package/wizard/__init__.py b/subscription_package/wizard/__init__.py new file mode 100644 index 000000000..ebf357793 --- /dev/null +++ b/subscription_package/wizard/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from . import subscription_close diff --git a/subscription_package/wizard/subscription_close.py b/subscription_package/wizard/subscription_close.py new file mode 100644 index 000000000..b13df0a7f --- /dev/null +++ b/subscription_package/wizard/subscription_close.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: SREERAG PM () +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from odoo import models, fields + + +class SubscriptionClose(models.TransientModel): + _name = 'subscription.close' + _description = 'Subscription Close Wizard' + + close_reason_id = fields.Many2one('subscription.package.stop', + string='Close Reason', + help='Add Subscription Package ' + 'Close Reason') + closed_by = fields.Many2one('res.users', string='Closed By', + default=lambda self: self.env.user, + help='Choose Subscription Package ' + 'Closed Person') + close_date = fields.Date(string='Closed On', + default=lambda self: fields.Date.today(), + help='Add Subscription Package Closed Date') + + def button_submit(self): + self.ensure_one() + this_sub_id = self.env.context.get('active_id') + sub = self.env['subscription.package'].search( + [('id', '=', this_sub_id)]) + sub.is_closed = True + sub.close_reason_id = self.close_reason_id + sub.closed_by = self.closed_by + sub.close_date = self.close_date + stage = (self.env['subscription.package.stage'].search([ + ('category', '=', 'closed')]).id) + values = {'stage_id': stage, 'is_to_renew': False} + sub.write(values) + for lines in sub.sale_order_id.order_line.filtered( + lambda x: x.product_template_id.is_subscription == True): + lines.qty_invoiced = lines.product_uom_qty + lines.qty_to_invoice = 0 diff --git a/subscription_package/wizard/subscription_close_views.xml b/subscription_package/wizard/subscription_close_views.xml new file mode 100644 index 000000000..9a8b6fc56 --- /dev/null +++ b/subscription_package/wizard/subscription_close_views.xml @@ -0,0 +1,22 @@ + + + + + subscription.close.view.form + subscription.close + +
+ + + + + + +
+
+
+