diff --git a/subscription_package/README.md b/subscription_package/README.md index 815553060..cf7595359 100644 --- a/subscription_package/README.md +++ b/subscription_package/README.md @@ -19,8 +19,7 @@ Contacts Credits -------- * Developer: Amal Prasad @ Cybrosys, - Alakananda @ Cybrosys - V16 Archana V @ Cybrosys + Archana V @ Cybrosys Bug Tracker ----------- @@ -34,4 +33,4 @@ For support and more information, please visit https://www.cybrosys.com Further information =================== -HTML Description: ``__ \ No newline at end of file +HTML Description: ``__ diff --git a/subscription_package/__manifest__.py b/subscription_package/__manifest__.py index 4829d99ea..4696d986a 100644 --- a/subscription_package/__manifest__.py +++ b/subscription_package/__manifest__.py @@ -48,6 +48,7 @@ 'views/mail_activity_views.xml', 'views/res_partner.xml', 'views/recurrence_views.xml', + 'views/sale_order_inherit.xml', 'report/subscription_report_view.xml', ], 'images': ['static/description/banner.png'], diff --git a/subscription_package/data/cron.xml b/subscription_package/data/cron.xml index d5459a16b..45a3da3a0 100644 --- a/subscription_package/data/cron.xml +++ b/subscription_package/data/cron.xml @@ -1,6 +1,6 @@ - + Check Close Limit diff --git a/subscription_package/data/mail_template.xml b/subscription_package/data/mail_template.xml index 61a4ff74f..80692b188 100644 --- a/subscription_package/data/mail_template.xml +++ b/subscription_package/data/mail_template.xml @@ -1,6 +1,6 @@ - + Subscription: Email Renew Alert diff --git a/subscription_package/doc/RELEASE_NOTES.md b/subscription_package/doc/RELEASE_NOTES.md deleted file mode 100644 index 0555b1f0c..000000000 --- a/subscription_package/doc/RELEASE_NOTES.md +++ /dev/null @@ -1,6 +0,0 @@ -## Module - -#### 01.10.2022 -#### Version 16.0.1.0.0 -#### ADD -- Initial commit for subscription_packages odoo \ No newline at end of file diff --git a/subscription_package/models/__init__.py b/subscription_package/models/__init__.py index c7fea59e9..6e40351b4 100644 --- a/subscription_package/models/__init__.py +++ b/subscription_package/models/__init__.py @@ -28,3 +28,4 @@ from . import subscription_close from . import subscription_renew from . import res_partner from.import recurrence_period +from . import sale_order diff --git a/subscription_package/models/recurrence_period.py b/subscription_package/models/recurrence_period.py index b2424543e..c6288fff6 100644 --- a/subscription_package/models/recurrence_period.py +++ b/subscription_package/models/recurrence_period.py @@ -24,6 +24,7 @@ from odoo import models, fields class RecurrencePeriod(models.Model): + """This class is used to create new model recurrence period""" _name = "recurrence.period" _description = "Recurrence Period " @@ -36,6 +37,7 @@ class RecurrencePeriod(models.Model): class SubPackages(models.Model): + """ This function is used to inherit subscription packages""" _inherit = 'subscription.package' recurrence_period_id = fields.Many2one("recurrence.period" , string= "Recurrence Period") diff --git a/subscription_package/models/res_partner.py b/subscription_package/models/res_partner.py index 171c7f58d..c1ad87f99 100644 --- a/subscription_package/models/res_partner.py +++ b/subscription_package/models/res_partner.py @@ -24,11 +24,11 @@ from odoo import fields, models class Partner(models.Model): + """Inherited res partner model""" _inherit = 'res.partner' active_subscription = fields.Boolean(string="Active Subscription", default=False) - subscription_product_line_ids = fields.One2many('subscription.package.product.line', - 'res_partner_id', - ondelete='restrict', - string='Products Line') + subscription_product_line_ids = fields.One2many( + 'subscription.package.product.line', 'res_partner_id', + ondelete='restrict', string='Products Line') diff --git a/subscription_package/models/sale_order.py b/subscription_package/models/sale_order.py new file mode 100644 index 000000000..ad6b854e2 --- /dev/null +++ b/subscription_package/models/sale_order.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from odoo import fields, models, api + + +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') + + @api.depends('subscription_count') + def _compute_subscription_count(self): + subscription_count = self.env['subscription.package'].search_count( + [('sale_order', '=', self.id)]) + if subscription_count > 0: + self.subscription_count = subscription_count + else: + self.subscription_count = 0 + + def button_subscription(self): + return { + 'name': 'Subscription', + 'sale_order': False, + 'domain': [('sale_order', '=', self.id)], + 'view_type': 'form', + 'res_model': 'subscription.package', + 'view_mode': 'tree,form', + 'type': 'ir.actions.act_window', + 'context': { + "create": False + } + } + + def _action_confirm(self): + 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': 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() + + +class SubscriptionInherit(models.Model): + """ This class is used to inherit subscription packages""" + _inherit = 'subscription.package' + + sale_order_count = fields.Integer() diff --git a/subscription_package/models/subscription_package.py b/subscription_package/models/subscription_package.py index 9491ebcf1..81520763f 100644 --- a/subscription_package/models/subscription_package.py +++ b/subscription_package/models/subscription_package.py @@ -20,13 +20,14 @@ # ############################################################################# -from odoo import _, api, models, fields, SUPERUSER_ID import datetime -from odoo.exceptions import UserError from dateutil.relativedelta import relativedelta +from odoo import _, api, models, fields, SUPERUSER_ID +from odoo.exceptions import UserError class SubscriptionPackageProductLine(models.Model): + """Subscription Package Product Line Model""" _name = 'subscription.package.product.line' _description = 'Subscription Product Lines' @@ -69,13 +70,12 @@ class SubscriptionPackageProductLine(models.Model): 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, categories, domain, order): """ Read all the stages and display it in the kanban view, @@ -111,7 +111,7 @@ class SubscriptionPackage(models.Model): default=lambda self: self.env.company, required=True) user_id = fields.Many2one('res.users', string='Sales Person', - default=lambda self: self.env.uid) + default=lambda self: self.env.user) sale_order = fields.Many2one('sale.order', string="Sale Order") to_renew = fields.Boolean(string='To Renew', default=False) tag_ids = fields.Many2many('account.account.tag', string='Tags') @@ -129,9 +129,8 @@ class SubscriptionPackage(models.Model): 'subscription_id', ondelete='restrict', string='Products Line') currency_id = fields.Many2one('res.currency', string='Currency', - readonly=True, - default=lambda - self: self.env.company.currency_id) + readonly=True, default=lambda + self: self.env.company.currency_id) current_stage = fields.Char(string='Current Stage', default='Draft', store=True, compute='_compute_current_stage') reference_code = fields.Char(string='Reference', store=True) @@ -146,9 +145,15 @@ class SubscriptionPackage(models.Model): compute='_compute_total_recurring_price', store=True) + """ Calculate Invoice count based on subscription package """ + @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)]) + 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: @@ -160,7 +165,7 @@ class SubscriptionPackage(models.Model): def _compute_sale_count(self): """ Calculate sale order count based on subscription package """ self.so_count = self.env['sale.order'].search_count( - [('subscription_id', '=', self.id)]) + [('id', '=', self.sale_order.id)]) @api.depends('stage_id') def _compute_current_stage(self): @@ -196,7 +201,7 @@ class SubscriptionPackage(models.Model): """ It displays sale order based on subscription package """ return { 'name': 'Products', - 'domain': [('subscription_id', '=', self.id)], + 'domain': [('id', '=', self.sale_order.id)], 'view_type': 'form', 'res_model': 'sale.order', 'view_mode': 'tree,form', @@ -224,8 +229,10 @@ class SubscriptionPackage(models.Model): rec_list = [0, 0, {'product_id': rec.product_id.id, 'quantity': rec.product_qty}] this_products_line.append(rec_list) - invoices = self.env['account.move'].search([('subscription_id', '=', self.id), ('state', '=', 'draft')]) - orders = self.env['sale.order'].search([('subscription_id', '=', self.id), ('invoice_status', '=', 'no')]) + invoices = self.env['account.move'].search( + [('subscription_id', '=', self.id), ('state', '=', 'draft')]) + orders = self.env['sale.order'].search( + [('subscription_id', '=', self.id), ('invoice_status', '=', 'no')]) if invoices: for invoice in invoices: invoice.action_post() @@ -257,39 +264,38 @@ class SubscriptionPackage(models.Model): def button_start_date(self): """Button to start subscription package""" - print("kkkkkkkkkkkkkkkkkkkkkkkkkkkk") - if not self.start_date: - self.start_date = datetime.date.today() for rec in self: - print((rec.env['subscription.package.stage'].search([ - ('category', '=', 'draft')]).id)) - if len(rec.env['subscription.package.stage'].search([('category', '=', 'draft')])) > 1: + 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: - print( (rec.env['subscription.package.stage'].search([ - ('category', '=', 'draft')]).id) ) rec.write( - {'stage_id': (rec.env['subscription.package.stage'].search([ - ('category', '=', 'draft')]).id) + 1}) + {'stage_id': (rec.env[ + 'subscription.package.stage'].search( + [ + ('category', '=', 'draft')]).id) + 1}) 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}] this_products_line.append(rec_list) - # for order in self.sale_order: - orders = self.env['sale.order'].search([('subscription_id', '=', self.id), ('invoice_status', '=', 'no')]) + 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, @@ -315,17 +321,18 @@ class SubscriptionPackage(models.Model): 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(SubscriptionPackage, self).create(vals) + 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 """ - plan_id = self.env['subscription.package.plan'].search( - [('id', '=', self.plan_id.id)]) - if plan_id.short_code and self.reference_code: - self.name = plan_id.short_code + '/' + self.reference_code + '-' + self.partner_id.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 """ @@ -344,35 +351,37 @@ class SubscriptionPackage(models.Model): pending_subscription = False close_subscription = False for pending_subscription in pending_subscriptions: - pending_subscription.close_date = pending_subscription.start_date + relativedelta( - days=pending_subscription.plan_id.days_to_end) - difference = ( - pending_subscription.close_date - pending_subscription.start_date).days / 10 - renew_date = pending_subscription.close_date - relativedelta( - days=difference) + if pending_subscription.start_date: + pending_subscription.close_date = pending_subscription.start_date + relativedelta( + days=pending_subscription.plan_id.days_to_end) + difference = (pending_subscription.close_date - + pending_subscription.start_date).days / 10 + renew_date = pending_subscription.close_date - relativedelta(days=difference) if today_date == renew_date: + pending_subscription.to_renew = True self.env.ref( 'subscription_package.mail_template_subscription_renew').send_mail( pending_subscription.id, force_send=True) - pending_subscription.write({'to_renew': True}) + 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}] this_products_line.append(rec_list) - self.env['account.move'].create( - { - 'move_type': 'out_invoice', - 'date': fields.Date.today(), - 'invoice_date': fields.Date.today(), - 'state': 'draft', - '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({'to_renew': False, - 'start_date': datetime.datetime.today()}) + b = self.env['account.move'].create( + { + 'move_type': 'out_invoice', + 'subscription_id': pending_subscription.id, + 'date': fields.Date.today(), + 'invoice_date': fields.Date.today(), + 'state': 'draft', + '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({'to_renew': False, + 'start_date': datetime.datetime.today()}) close_subscriptions = self.env['subscription.package'].search( [('stage_category', '=', 'progress'), ('to_renew', '=', True)]) for close_subscription in close_subscriptions: diff --git a/subscription_package/models/subscription_plan.py b/subscription_package/models/subscription_plan.py index db553fbf3..7f73d04b0 100644 --- a/subscription_package/models/subscription_plan.py +++ b/subscription_package/models/subscription_plan.py @@ -103,11 +103,14 @@ class SubscriptionPlan(models.Model): """ It displays products based on subscription plan """ return { 'name': 'Products', + 'res_model': 'product.product', 'domain': [('subscription_plan_id', '=', self.id)], 'view_type': 'form', - 'res_model': 'product.product', 'view_mode': 'tree,form', 'type': 'ir.actions.act_window', + 'context': { + 'default_is_subscription': True, + }, } def button_sub_count(self): diff --git a/subscription_package/models/subscription_products.py b/subscription_package/models/subscription_products.py index 1d3df8832..de2ccbb2d 100644 --- a/subscription_package/models/subscription_products.py +++ b/subscription_package/models/subscription_products.py @@ -23,7 +23,8 @@ from odoo import api, models, fields -class SubscriptionInvoice(models.Model): +class AccountMove(models.Model): + """Inherited sale order model""" _inherit = "account.move" is_subscription = fields.Boolean(string='Is Subscription', default=False) @@ -40,10 +41,11 @@ class SubscriptionInvoice(models.Model): new_vals_list = [{'is_subscription': True, 'subscription_id': so_id.subscription_id}] vals_list[0].update(new_vals_list[0]) - return super(SubscriptionInvoice, self).create(vals_list) + return super().create(vals_list) -class SubscriptionProduct(models.Model): +class Product(models.Model): + """Inherited product template model""" _inherit = "product.template" is_subscription = fields.Boolean(string='Is Subscription', default=False) diff --git a/subscription_package/models/subscription_renew.py b/subscription_package/models/subscription_renew.py index 899e21277..494679015 100644 --- a/subscription_package/models/subscription_renew.py +++ b/subscription_package/models/subscription_renew.py @@ -20,11 +20,12 @@ # ############################################################################# -from odoo import api, models, fields import datetime +from odoo import api, models, fields -class SubscriptionSaleOrder(models.Model): +class SaleOrder(models.Model): + """Inherited sale order model""" _inherit = "sale.order" is_subscription = fields.Boolean(string='Is Subscription', default=False) @@ -36,15 +37,12 @@ class SubscriptionSaleOrder(models.Model): @api.model def create(self, vals): """ It displays subscription in sale order """ - if vals.get('is_subscription') is True: - new_vals = [{ + if vals.get('is_subscription'): + vals.update({ 'is_subscription': True, 'subscription_id': vals.get('subscription_id'), - }] - vals.update(new_vals[0]) - return super(SubscriptionSaleOrder, self).create(vals) - else: - return super(SubscriptionSaleOrder, self).create(vals) + }) + return super().create(vals) @api.depends('subscription_id') def _compute_reference_code(self): @@ -56,7 +54,7 @@ class SubscriptionSaleOrder(models.Model): """ It Changed the stage, to renew, start date for subscription package based on sale order confirm """ - res = super(SubscriptionSaleOrder, self).action_confirm() + res = super().action_confirm() sale_order = self.subscription_id.sale_order so_state = self.search([('id', '=', sale_order.id)]).state if so_state in ['sale', 'done']: diff --git a/subscription_package/static/description/index.html b/subscription_package/static/description/index.html index 88743dbb0..6e3a8bf1c 100644 --- a/subscription_package/static/description/index.html +++ b/subscription_package/static/description/index.html @@ -23,10 +23,7 @@

Subscription Package

-

Print Subscription - Package -

@@ -123,12 +120,12 @@ Enterprise and Community compatible. + +
- Available in Odoo 15.0 Community and Enterprise. + Available in Odoo 16.0 Community and Enterprise.
- -
@@ -138,7 +135,6 @@
-
Subscription Renewable @@ -269,64 +265,64 @@
-
-

Recurrence Period -

- - -
-

Create Recurrence Period -

+

Recurrence Period +

- -
-
-

CSubscription Report -

-

-

    -
  • It shows report for subscription plans. -
  • -
-

- -
-
-

- Subscriptions to Renew -

+ +
+
+

Create Recurrence Period +

- -
+ + +
+

Subscription Report +

+

+

    +
  • It shows report for subscription plans. +
  • +
+

+ +
+
+

+ Subscriptions to Renew +

-
-

Create Subscription - Package with Sale Order -

+ +
- -
-
-

-

-

-

    -
  • To create a Subscription with source 'Create Sale Order', Create a Quotation of Sale Order with at - least - one - subscription type product in the order line. -
  • -
  • When we click on the 'Confirm' button on the 'Sale Quotation' then, automatically a subscription is - created - in 'In-progress' state. -
  • -
-

- -
+
+

Create Subscription + Package with Sale Order +

+ +
+
+

+

+

+

    +
  • To create a Subscription with source 'Create Sale Order', Create a Quotation of Sale Order with at + least + one + subscription type product in the order line. +
  • +
  • When we click on the 'Confirm' button on the 'Sale Quotation' then, automatically a subscription is + created + in 'In-progress' state. +
  • +
+

+
+ + diff --git a/subscription_package/views/res_partner.xml b/subscription_package/views/res_partner.xml index 8ee8b780b..ff32ef13a 100644 --- a/subscription_package/views/res_partner.xml +++ b/subscription_package/views/res_partner.xml @@ -8,15 +8,15 @@ - - - - - - - - - + + + + + + + + + diff --git a/subscription_package/views/sale_order_inherit.xml b/subscription_package/views/sale_order_inherit.xml new file mode 100644 index 000000000..86fbfd5e4 --- /dev/null +++ b/subscription_package/views/sale_order_inherit.xml @@ -0,0 +1,18 @@ + + + + + subscriptions.view.buttons + sale.order + + +
+ +
+
+
+
+
\ No newline at end of file diff --git a/subscription_package/views/subscription_package.xml b/subscription_package/views/subscription_package.xml index 5114d70d2..99e7cfea9 100644 --- a/subscription_package/views/subscription_package.xml +++ b/subscription_package/views/subscription_package.xml @@ -72,22 +72,23 @@