Skip to content

Commit

Permalink
[WIP] Subscriptions integration Curq (#14)
Browse files Browse the repository at this point in the history
* Update website_sale_recurring_payment modules

* [WIP] Intergrate subscription_oca

* [WIP] Intergrate subscription_oca

* [WIP] Intergrate subscription_oca: Demo products

* argocd_sale: documentation about subscriptions

* argocd_sale: subscription integration

* argocd_sale: grace period

* website_sale_recurring_payment: Remove website_sale dependency
Renamed website_sale_recurring_payment to sale_recurring_payment

* sale_recurring_payment: Remove branding

* argocd_website: Create subscription

* argocd_website: Create subscription

* argocd_website: Create subscription

* argocd_website: Create subscription

* argocd_website: Create subscription

* website_sale_recurring_payment: Move compatible provider filter to sale_recurring_payment

and remove website_sale_recurring_payment

* sale_recurring_payment: filtered providers on invoice payments linked to subs

* sale_recurring_payment: subs directly from invoice

* sale_recurring_payment: subs directly from invoice

* argocd_sale: Fixup

* subscription portal

* argocd_sale: res config settings

* argocd_sale: res config settings

* subscription_portal: Cancellation of subscriptions by customers (WIP)

* subscription_portal: Cancellation of subscriptions by customers (WIP)

* subscription_portal: Cancellation of subscriptions by customers (WIP)

* subscription_portal: Cancel subscription from app page

* subscription_portal: Grace period

* subscription_portal: Fixup

* argocd_sale: Test tag action

* argocd_sale: Test destroy app

* argocd_sale: Test destroy app

* sale_recurring_payment: comment line

---------

Co-authored-by: Anjeel <[email protected]>
  • Loading branch information
tarteo and ByteMeAsap authored Mar 21, 2024
1 parent 6d71704 commit 29a7388
Show file tree
Hide file tree
Showing 68 changed files with 1,259 additions and 577 deletions.
35 changes: 35 additions & 0 deletions argocd_sale/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# ArgoCD Sales Management


Deploy application when an invoice is paid.

How oca_subscription works:
<pre>
sale order -> (payment link + payment) -> first invoice
-> subscription -> recurring invoice
</pre>

Because the first invoice is not linked ot the subscription we skip the sale order and directly use
sale.subscription

## Product configuration

1. Go to Sales > Products > Products
2. Create a product
3. Select an application template for the product
4. Check "Subscribable product" subscription template is not necessary.

## Deployment notifications

1. If you want to a send email to customer when a deployment is queued, please take a look at mail template: **ArgoCD: Deployment Notification (for partner)**
2. Also take a look at the field **"Automatically send deployment notification"** on application templates (ArgoCD > Templates)

## Grace periods

1. Set system parameter `argocd_sale.grace_period` to the amount of **days** you allow
customers to not pay until the subscription is closed and application is deleted.
2. You can also find these settings in res.config.parameters (tab Sales)

## Roadmap

* Move generic functionality (grace period, invoice paid hook) to subscription_oca
18 changes: 0 additions & 18 deletions argocd_sale/README.rst

This file was deleted.

10 changes: 9 additions & 1 deletion argocd_sale/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,22 @@
"license": "AGPL-3",
"category": "Sales",
"version": "16.0.1.0.0",
"depends": ["sale", "argocd_deployer"],
"depends": [
"sale",
"subscription_oca",
"argocd_deployer",
"sale_recurring_payment",
],
"demo": [
"demo/sale_subscription_template_demo.xml",
"demo/product_template_demo.xml",
],
"data": [
"data/mail_template_data.xml",
"data/ir_config_parameter_data.xml",
"views/product_template.xml",
"views/application_view.xml",
"views/application_template_view.xml",
"views/res_config_settings_view.xml",
],
}
15 changes: 15 additions & 0 deletions argocd_sale/data/ir_config_parameter_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="grace_period" model="ir.config_parameter">
<field name="key">argocd_sale.grace_period</field>
<field name="value">0</field>
</record>
<record id="grace_period_action" model="ir.config_parameter">
<field name="key">argocd_sale.grace_period_action</field>
<field name="value">add_tag</field>
</record>
<record id="grace_period_tag_id" model="ir.config_parameter">
<field name="key">argocd_sale.grace_period_tag_id</field>
<field name="value"></field>
</record>
</odoo>
6 changes: 6 additions & 0 deletions argocd_sale/demo/product_template_demo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@
<record id="demo_curq_basis_product_template" model="product.template">
<field name="name">Curq Basis</field>
<field name="application_template_id" ref="argocd_deployer.demo_curq_basis_application_template" />
<field name="subscribable" eval="True" />
<field name="subscription_template_id" ref="argocd_sale.demo_subscription_template" />
</record>
<record id="demo_pos_product_template" model="product.template">
<field name="name">Point of Sales</field>
<field name="application_tag_ids" eval="[Command.set([ref('argocd_deployer.demo_pos_application_tag')])]" />
<field name="subscribable" eval="True" />
<field name="subscription_template_id" ref="argocd_sale.demo_subscription_template" />
</record>
<record id="demo_matomo_server_product_template" model="product.template">
<field name="name">Matomo Server</field>
<field name="application_tag_ids" eval="[Command.set([ref('argocd_deployer.demo_matomo_server_application_tag')])]" />
<field name="subscribable" eval="True" />
<field name="subscription_template_id" ref="argocd_sale.demo_subscription_template" />
</record>
</odoo>
7 changes: 7 additions & 0 deletions argocd_sale/demo/sale_subscription_template_demo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="demo_subscription_template" model="sale.subscription.template">
<field name="name">Curq (monthly)</field>
<field name="invoicing_mode">invoice_send</field>
</record>
</odoo>
2 changes: 2 additions & 0 deletions argocd_sale/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
from . import application
from . import application_template
from . import res_partner
from . import subscription
from . import res_config_settings
53 changes: 4 additions & 49 deletions argocd_sale/models/account_move.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,11 @@
from odoo import _, api, models
from odoo.exceptions import ValidationError
from odoo import models


class AccountMove(models.Model):
_inherit = "account.move"

@api.constrains("invoice_line_ids")
def _check_multiple_application_products(self):
app_lines = self.line_ids.filtered(
lambda l: l.product_id.application_template_id
)
if len(app_lines) > 1:
raise ValidationError(
_("Invoice can only have one application, please remove one")
)

def _customer_name_to_application_name(self):
self.ensure_one()
replacements = {" ": "-", ".": "", "&": "-"}
partner = self.partner_id.commercial_partner_id
name = partner.display_name
name = name.strip().lower()
for replace in replacements:
name = name.replace(replace, replacements[replace])
return "".join(c for c in name if c.isalnum() or c == "-")

def _invoice_paid_hook(self):
application_sudo = self.env["argocd.application"].sudo()
for invoice in self:
lines = invoice.line_ids.filtered(
lambda l: l.product_id.application_template_id
)
for line in lines:
name = application_sudo.find_next_available_name(
self._customer_name_to_application_name()
)
tags = invoice.line_ids.filtered(
lambda l: l.product_id.application_tag_ids
and not l.product_id.application_filter_ids # All lines with modules linked to them
or line.product_id.application_template_id # If there's no filter
in l.product_id.application_filter_ids # If there's a filter
).mapped("product_id.application_tag_ids")

application = application_sudo.create(
{
"name": name,
"invoice_id": invoice.id,
"tag_ids": tags.ids,
"template_id": line.product_id.application_template_id.id,
}
)
application.render_config()
application.deploy()

self.filtered(lambda i: i.subscription_id).mapped(
"subscription_id"
)._invoice_paid_hook()
return super(AccountMove, self)._invoice_paid_hook()
8 changes: 4 additions & 4 deletions argocd_sale/models/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ def is_created_by_reseller(self):
self.partner_id.parent_id and self.partner_id.parent_id.is_reseller
)

invoice_id = fields.Many2one(comodel_name="account.move")
subscription_id = fields.Many2one(comodel_name="sale.subscription")

@api.depends("invoice_id", "invoice_id.partner_id")
@api.depends("subscription_id", "subscription_id.partner_id")
def _compute_partner_id(self):
for app in self.filtered(lambda a: a.invoice_id):
app.partner_id = app.invoice_id.partner_id
for app in self.filtered(lambda a: a.subscription_id):
app.partner_id = app.subscription_id.partner_id

def _get_deployment_notification_mail_template(self):
self.ensure_one()
Expand Down
22 changes: 22 additions & 0 deletions argocd_sale/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from odoo import fields, models


class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"

subscription_grace_period = fields.Integer(
config_parameter="argocd_sale.grace_period", string="Grace Period (in days)"
)
subscription_grace_period_action = fields.Selection(
selection=[
("destroy_app", "Destroy application"),
("add_tag", "Add tag"),
],
default="add_tag",
config_parameter="argocd_sale.grace_period_action",
)
subscription_grace_period_tag_id = fields.Many2one(
comodel_name="argocd.application.tag",
config_parameter="argocd_sale.grace_period_tag_id",
string="Tag",
)
113 changes: 113 additions & 0 deletions argocd_sale/models/subscription.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
from datetime import timedelta

from odoo import Command, _, api, fields, models
from odoo.exceptions import ValidationError


class Subscription(models.Model):
_inherit = "sale.subscription"

application_ids = fields.One2many(
comodel_name="argocd.application", inverse_name="subscription_id"
)

def _get_grace_period(self):
return int(
self.env["ir.config_parameter"]
.sudo()
.get_param("argocd_sale.grace_period", "0")
)

@api.constrains("sale_subscription_line_ids")
def _check_multiple_application_products(self):
app_lines = self.sale_subscription_line_ids.filtered(
lambda l: l.product_id.application_template_id
)
if len(app_lines) > 1:
raise ValidationError(
_("Subscription can only have one application, please remove one")
)

def _customer_name_to_application_name(self):
self.ensure_one()
replacements = {" ": "-", ".": "", "&": "-"}
partner = self.partner_id.commercial_partner_id
name = partner.display_name
name = name.strip().lower()
for replace in replacements:
name = name.replace(replace, replacements[replace])
return "".join(c for c in name if c.isalnum() or c == "-")

def _invoice_paid_hook(self):
application_sudo = self.env["argocd.application"].sudo()
for subscription in self.filtered(
lambda i: len(i.invoice_ids) == 1
): # Create the application after the first invoice has been paid
subscription.action_start_subscription()
lines = subscription.sale_subscription_line_ids.filtered(
lambda l: l.product_id.application_template_id
)
for line in lines:
name = application_sudo.find_next_available_name(
self._customer_name_to_application_name()
)
tags = subscription.sale_subscription_line_ids.filtered(
lambda l: l.product_id.application_tag_ids
and not l.product_id.application_filter_ids # All lines with modules linked to them
or line.product_id.application_template_id # If there's no filter
in l.product_id.application_filter_ids # If there's a filter
).mapped("product_id.application_tag_ids")

application = application_sudo.create(
{
"name": name,
"subscription_id": subscription.id,
"tag_ids": tags.ids,
"template_id": line.product_id.application_template_id.id,
}
)
application.render_config()
application.deploy()

def _do_grace_period_action(self):
"""
Executes the grace period action on self.
@return: False if nothing has been done, True if the action has been done
"""
grace_period_action = self.env["ir.config_parameter"].get_param(
"argocd_sale.grace_period_action"
)
if not grace_period_action:
return False # Do nothing
if grace_period_action == "add_tag":
grace_period_tag_id = int(
self.env["ir.config_parameter"].get_param(
"argocd_sale.grace_period_tag_id", "0"
)
)
if not grace_period_tag_id:
return False
tag = self.env["argocd.application.tag"].browse(grace_period_tag_id)
if not tag:
return False
self.mapped("application_ids").write({"tag_ids": [Command.link(tag.id)]})
elif grace_period_action == "destroy_app":
self.mapped("application_ids").destroy()
return True

def cron_update_payment_provider_subscriptions(self):
# Process last payments first in here last_date_invoiced can be updated
res = super().cron_update_payment_provider_subscriptions()
period = self._get_grace_period()
if not period:
return res
today = fields.Date.today()
late_date = today - timedelta(days=period)
late_subs = self.search(
[("last_date_invoiced", "<", late_date), ("in_progress", "=", True)]
)
if late_subs:
late_subs.close_subscription()
late_subs._do_grace_period_action()
return res
1 change: 1 addition & 0 deletions argocd_sale/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import test_reseller
from . import test_grace_period
Loading

0 comments on commit 29a7388

Please sign in to comment.