Skip to content

Commit

Permalink
[IMP] subscription_oca: Various improvements
Browse files Browse the repository at this point in the history
[IMP] subscription_oca: Various improvements

oca_subscription: Open subscription button in invoice view

[IMP]subscription_oca: syntax as requested

[IMP]subscription_oca: non used field on stage

[IMP]subscription_oca: track amount changes

[REF]subscription_oca: more consistent stage and status management

[IMP]subscription_oca: add views
* Invoices menu
* Filter entries in search views

[IMP]subscription_oca: Contributors

[IMP]subscription_oca: ACL issue on contact

[IMP]subscription_oca: use code as invoice reference

[IMP]subscription_oca: unit tests

[LINT]subscription_oca

[LINT]subscription_oca

[LINT]subscription_oca

[FIX]subscription_oca: authoring

[IMP] subscription_oca: Various improvements
  • Loading branch information
tarteo authored and flotho committed Jul 11, 2024
1 parent df881dd commit 63ab1cf
Show file tree
Hide file tree
Showing 15 changed files with 232 additions and 64 deletions.
6 changes: 5 additions & 1 deletion subscription_oca/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Subscription management
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:7f85e1d860d2d5332db014c46c57b9bece8f76a849049bda632499d422ba6dd8
!! source digest: sha256:34d756071fe20a703cbc859e7a7cb18a85f6f4c46423c76523860a3c28100788
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
Expand Down Expand Up @@ -87,6 +87,10 @@ Contributors

* Ilyas <[email protected]>

* `Mind And Go <https://mind-and-go.com>`__:

* Florent THOMAs <[email protected]>

Maintainers
~~~~~~~~~~~

Expand Down
3 changes: 2 additions & 1 deletion subscription_oca/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
"website": "https://github.com/OCA/contract",
"license": "AGPL-3",
"author": "Domatix, Odoo Community Association (OCA)",
"depends": ["sale_management", "account"],
"depends": ["sale_management", "sales_team", "account"],
"data": [
"views/product_template_views.xml",
"views/account_move_views.xml",
"views/sale_subscription_views.xml",
"views/sale_subscription_stage_views.xml",
"views/sale_subscription_tag_views.xml",
Expand Down
18 changes: 14 additions & 4 deletions subscription_oca/data/sale_subscription_data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,29 @@
<data noupdate="1">
<record id="subscription_stage_draft" model="sale.subscription.stage">
<!-- <field eval="True" name="active"/> -->
<field name="name">Ready to start</field>
<field name="name">Draft</field>
<field name="sequence">0</field>
<field name="type">draft</field>
<field name="description">
Draft, still working on the specifics.
</field>
<field eval="False" name="fold" />
</record>
<record id="subscription_stage_ready" model="sale.subscription.stage">
<!-- <field eval="True" name="active"/> -->
<field name="name">Ready to start</field>
<field name="sequence">1</field>
<field name="type">pre</field>
<field name="description">
Draft equivalent, a subscription is ready to start when is not marked as in progress but it can be at any moment. If there's no 'Closed'-type stage defined, when a subscription comes to an end by automatic means, it will be marked with this stage.
A subscription is ready to start when is not marked as in progress but it can be at any moment. If there's no 'Closed'-type stage defined, when a subscription comes to an end by automatic means, it will be marked with this stage.
</field>
<field eval="False" name="fold" />
</record>

<record id="subscription_stage_in_progress" model="sale.subscription.stage">
<!-- <field eval="True" name="active"/> -->
<field name="name">In progress</field>
<field name="sequence">1</field>
<field name="sequence">2</field>
<field name="type">in_progress</field>
<field eval="False" name="fold" />
<field name="description">
Expand All @@ -36,7 +46,7 @@
<record id="subscription_stage_closed" model="sale.subscription.stage">
<!-- <field eval="True" name="active"/> -->
<field name="name">Closed</field>
<field name="sequence">2</field>
<field name="sequence">3</field>
<field name="type">post</field>
<field eval="False" name="fold" />
<field name="description">
Expand Down
8 changes: 8 additions & 0 deletions subscription_oca/models/account_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,11 @@ class AccountMove(models.Model):
subscription_id = fields.Many2one(
comodel_name="sale.subscription", string="Subscription"
)

def action_open_subscription(self):
self.ensure_one()
action = self.env["ir.actions.act_window"]._for_xml_id(

Check warning on line 16 in subscription_oca/models/account_move.py

View check run for this annotation

Codecov / codecov/patch

subscription_oca/models/account_move.py#L15-L16

Added lines #L15 - L16 were not covered by tests
"subscription_oca.sale_subscription_action"
)
action["domain"] = [("id", "=", self.subscription_id.id)]
return action

Check warning on line 20 in subscription_oca/models/account_move.py

View check run for this annotation

Codecov / codecov/patch

subscription_oca/models/account_move.py#L19-L20

Added lines #L19 - L20 were not covered by tests
3 changes: 1 addition & 2 deletions subscription_oca/models/res_partner.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ class Partner(models.Model):
string="Subscriptions",
)
subscription_count = fields.Integer(
required=False,
compute="_compute_subscription_count",
required=False, compute="_compute_subscription_count", compute_sudo=True
)

def _compute_subscription_count(self):
Expand Down
107 changes: 72 additions & 35 deletions subscription_oca/models/sale_subscription.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class SaleSubscription(models.Model):
_name = "sale.subscription"
_description = "Subscription"
_inherit = ["mail.thread", "mail.activity.mixin"]
_order = "id desc"
_order = "recurring_next_date ASC, commercial_partner_id, id DESC"

color = fields.Integer("Color Index")
name = fields.Char(
Expand All @@ -33,6 +33,14 @@ class SaleSubscription(models.Model):
partner_id = fields.Many2one(
comodel_name="res.partner", required=True, string="Partner", index=True
)
commercial_partner_id = fields.Many2one(
comodel_name="res.partner",
string="Commercial Entity",
compute_sudo=True,
related="partner_id.commercial_partner_id",
index=True,
store=True,
)
fiscal_position_id = fields.Many2one(
"account.fiscal.position",
string="Fiscal Position",
Expand All @@ -49,7 +57,14 @@ class SaleSubscription(models.Model):
string="Reference",
default=lambda self: self.env["ir.sequence"].next_by_code("sale.subscription"),
)
in_progress = fields.Boolean(string="In progress", default=False)
in_progress = fields.Boolean(
string="In progress",
compute="_compute_status",
readonly=False,
index=True,
store=True,
copy=False,
)
recurring_rule_boundary = fields.Boolean(
string="Boundary", compute="_compute_rule_boundary", store=True
)
Expand Down Expand Up @@ -90,10 +105,18 @@ class SaleSubscription(models.Model):
string="Orders",
)
recurring_total = fields.Monetary(
compute="_compute_total", string="Recurring price", store=True
compute="_compute_total",
string="Recurring price",
store=True,
)
amount_tax = fields.Monetary(
compute="_compute_total",
store=True,
)
amount_total = fields.Monetary(
compute="_compute_total",
store=True,
)
amount_tax = fields.Monetary(compute="_compute_total", store=True)
amount_total = fields.Monetary(compute="_compute_total", store=True)
tag_ids = fields.Many2many(comodel_name="sale.subscription.tag", string="Tags")
image = fields.Binary("Image", related="user_id.image_512", store=True)
journal_id = fields.Many2one(comodel_name="account.journal", string="Journal")
Expand All @@ -109,12 +132,22 @@ def _read_group_stage_ids(self, stages, domain, order):
stage_ids = stages.search([], order=order)
return stage_ids

def _get_default_stage_id(self):
"""Gives default stage_id"""
first_stage = self.env["sale.subscription.stage"].search(
[("type", "=", "draft")], order="sequence"
)
return first_stage[:1]

stage_id = fields.Many2one(
comodel_name="sale.subscription.stage",
string="Stage",
tracking=True,
group_expand="_read_group_stage_ids",
required=True,
store=True,
default=_get_default_stage_id,
copy=False,
)
stage_str = fields.Char(
related="stage_id.name",
Expand All @@ -135,28 +168,37 @@ def _read_group_stage_ids(self, stages, domain, order):
comodel_name="sale.subscription.close.reason", string="Close Reason"
)
crm_team_id = fields.Many2one(comodel_name="crm.team", string="Sale team")
to_renew = fields.Boolean(default=False, string="To renew")
to_renew = fields.Boolean(default=False, string="To renew") # TODO: Seems not used

def cron_subscription_management(self):
today = date.today()
for subscription in self.search([]):
if subscription.in_progress:
if (
subscription.recurring_next_date == today
subscription.recurring_next_date <= today
and subscription.sale_subscription_line_ids
):
try:
subscription.generate_invoice()
except Exception:
logger.exception("Error on subscription invoice generate")
if not subscription.recurring_rule_boundary:
if subscription.date == today:
if subscription.date <= today:
subscription.action_close_subscription()

else:
if subscription.date_start == today:
subscription.action_start_subscription()
subscription.generate_invoice()
elif (
subscription.date_start <= today and subscription.stage_id.type == "pre"
):
subscription.action_start_subscription()
subscription.generate_invoice()

Check warning on line 193 in subscription_oca/models/sale_subscription.py

View check run for this annotation

Codecov / codecov/patch

subscription_oca/models/sale_subscription.py#L192-L193

Added lines #L192 - L193 were not covered by tests

@api.depends("stage_id")
def _compute_status(self):
for record in self:
in_progress = False
if record.stage_id and record.stage_id.type == "in_progress":
in_progress = True
record.in_progress = in_progress

@api.depends("sale_subscription_line_ids")
def _compute_total(self):
Expand All @@ -181,7 +223,7 @@ def _compute_name(self):
slash = "/" if template_code and code else ""
record.name = "{}{}{}".format(template_code, slash, code)

@api.depends("template_id", "date_start")
@api.depends("template_id", "date_start", "stage_id")
def _compute_rule_boundary(self):
for record in self:
if record.template_id.recurring_rule_boundary == "unlimited":
Expand Down Expand Up @@ -234,12 +276,11 @@ def onchange_partner_id_fpos(self):
def action_start_subscription(self):
self.close_reason_id = False
in_progress_stage = self.env["sale.subscription.stage"].search(
[("type", "=", "in_progress")], limit=1
[("type", "=", "in_progress")], order="sequence", limit=1
)
self.stage_id = in_progress_stage

def action_close_subscription(self):
self.recurring_next_date = False
return {
"view_type": "form",
"view_mode": "form",
Expand All @@ -249,6 +290,17 @@ def action_close_subscription(self):
"res_id": False,
}

def close_subscription(self, close_reason_id=False):
self.ensure_one()
self.recurring_next_date = False
closed_stage_ids = self.env["sale.subscription.stage"].search(
[("type", "=", "post")],
)
closed_stage = closed_stage_ids[:1]
self.close_reason_id = close_reason_id
if self.stage_id != closed_stage and closed_stage:
self.stage_id = closed_stage

def _prepare_sale_order(self, line_ids=False):
self.ensure_one()
return {
Expand All @@ -268,9 +320,11 @@ def _prepare_account_move(self, line_ids):
"invoice_date": self.recurring_next_date,
"invoice_payment_term_id": self.partner_id.property_payment_term_id.id,
"invoice_origin": self.name,
"ref": self.code,
"invoice_user_id": self.user_id.id,
"partner_bank_id": self.company_id.partner_id.bank_ids[:1].id,
"invoice_line_ids": line_ids,
"subscription_id": self.id,
}
if self.journal_id:
values["journal_id"] = self.journal_id.id
Expand All @@ -294,7 +348,6 @@ def create_invoice(self):
.with_context(default_move_type="out_invoice", journal_type="sale")
.create(invoice_values)
)
self.write({"invoice_ids": [(4, invoice_id.id)]})
return invoice_id

def create_sale_order(self):
Expand Down Expand Up @@ -372,8 +425,8 @@ def manual_invoice(self):
@api.depends("invoice_ids", "sale_order_ids.invoice_ids")
def _compute_account_invoice_ids_count(self):
for record in self:
record.account_invoice_ids_count = len(self.invoice_ids) + len(
self.sale_order_ids.invoice_ids
record.account_invoice_ids_count = len(record.invoice_ids) + len(
record.sale_order_ids.invoice_ids
)

def action_view_account_invoice_ids(self):
Expand Down Expand Up @@ -431,22 +484,6 @@ def _check_dates(self, start, next_invoice):
return True
return False

def write(self, values):
res = super().write(values)
if "stage_id" in values:
for record in self:
if record.stage_id:
if record.stage_id.type == "in_progress":
record.in_progress = True
record.date_start = date.today()
elif record.stage_id.type == "post":
record.close_reason_id = False
record.in_progress = False
else:
record.in_progress = False

return res

@api.model
def create(self, values):
if "recurring_rule_boundary" in values:
Expand All @@ -464,7 +501,7 @@ def create(self, values):
values["date_start"] = values["recurring_next_date"]
values["stage_id"] = (
self.env["sale.subscription.stage"]
.search([("type", "=", "pre")], order="sequence desc", limit=1)
.search([("type", "=", "draft")], order="sequence desc", limit=1)
.id
)
return super(SaleSubscription, self).create(values)
13 changes: 9 additions & 4 deletions subscription_oca/models/sale_subscription_stage.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,21 @@ class SaleSubscriptionStage(models.Model):
name = fields.Char(required=True, translate=True)
sequence = fields.Integer()
display_name = fields.Char(string="Display name", compute="_compute_display_name")
in_progress = fields.Boolean(string="In progress", default=False)
fold = fields.Boolean(string="Kanban folded")
description = fields.Text(translate=True)
type = fields.Selection(
[("pre", "Ready to start"), ("in_progress", "In progress"), ("post", "Closed")],
default="pre",
[
("draft", "Draft"),
("pre", "Ready to start"),
("in_progress", "In progress"),
("post", "Closed"),
],
required=True,
default="draft",
)

@api.constrains("type")
def _check_lot_product(self):
def _check_subscription_stages(self):
post_stages = self.env["sale.subscription.stage"].search(
[("type", "=", "post")]
)
Expand Down
4 changes: 4 additions & 0 deletions subscription_oca/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@
* `Ooops404 <https://www.ooops404.com>`__:

* Ilyas <[email protected]>

* `Mind And Go <https://mind-and-go.com>`__:

* Florent Thomas <[email protected]>
7 changes: 6 additions & 1 deletion subscription_oca/static/description/index.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
Expand Down Expand Up @@ -366,7 +367,7 @@ <h1 class="title">Subscription management</h1>
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:7f85e1d860d2d5332db014c46c57b9bece8f76a849049bda632499d422ba6dd8
!! source digest: sha256:34d756071fe20a703cbc859e7a7cb18a85f6f4c46423c76523860a3c28100788
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/contract/tree/16.0/subscription_oca"><img alt="OCA/contract" src="https://img.shields.io/badge/github-OCA%2Fcontract-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/contract-16-0/contract-16-0-subscription_oca"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/contract&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module allows creating subscriptions that generate recurring invoices or orders. It also enables the sale of products that generate subscriptions.</p>
Expand Down Expand Up @@ -433,6 +434,10 @@ <h2><a class="toc-backref" href="#toc-entry-6">Contributors</a></h2>
<li>Ilyas &lt;<a class="reference external" href="mailto:irazor147&#64;gmail.com">irazor147&#64;gmail.com</a>&gt;</li>
</ul>
</li>
<li><a class="reference external" href="https://mind-and-go.com">Mind And Go</a>:<ul>
<li>Florent THOMAs &lt;<a class="reference external" href="mailto:florent.thomas&#64;mind-and-go.com">florent.thomas&#64;mind-and-go.com</a>&gt;</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
Expand Down
Loading

0 comments on commit 63ab1cf

Please sign in to comment.