Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
96b4abc
Update website_sale_recurring_payment modules
ByteMeAsap Mar 12, 2024
d14bdd0
[WIP] Intergrate subscription_oca
tarteo Mar 12, 2024
47f342e
[WIP] Intergrate subscription_oca
tarteo Mar 12, 2024
433dd79
[WIP] Intergrate subscription_oca: Demo products
tarteo Mar 12, 2024
1be8235
argocd_sale: documentation about subscriptions
tarteo Mar 13, 2024
2e761a4
argocd_sale: subscription integration
tarteo Mar 13, 2024
682b493
argocd_sale: grace period
tarteo Mar 13, 2024
bc50010
website_sale_recurring_payment: Remove website_sale dependency
tarteo Mar 14, 2024
646be5d
sale_recurring_payment: Remove branding
tarteo Mar 14, 2024
afa661d
argocd_website: Create subscription
tarteo Mar 14, 2024
5a2859e
argocd_website: Create subscription
tarteo Mar 14, 2024
e18c182
argocd_website: Create subscription
tarteo Mar 14, 2024
52b64f2
argocd_website: Create subscription
tarteo Mar 14, 2024
6ba1376
argocd_website: Create subscription
tarteo Mar 14, 2024
acfcb01
website_sale_recurring_payment: Move compatible provider filter to sa…
tarteo Mar 14, 2024
f48e960
sale_recurring_payment: filtered providers on invoice payments linked…
tarteo Mar 14, 2024
b7c49bd
sale_recurring_payment: subs directly from invoice
tarteo Mar 15, 2024
17ebae0
sale_recurring_payment: subs directly from invoice
tarteo Mar 15, 2024
496147e
argocd_sale: Fixup
tarteo Mar 15, 2024
dddcfa3
subscription portal
tarteo Mar 15, 2024
2673aaa
argocd_sale: res config settings
tarteo Mar 15, 2024
697f017
argocd_sale: res config settings
tarteo Mar 15, 2024
f1a0001
subscription_portal: Cancellation of subscriptions by customers (WIP)
tarteo Mar 15, 2024
a267786
subscription_portal: Cancellation of subscriptions by customers (WIP)
tarteo Mar 19, 2024
f60e5d6
subscription_portal: Cancellation of subscriptions by customers (WIP)
tarteo Mar 19, 2024
01affe5
subscription_portal: Cancel subscription from app page
tarteo Mar 19, 2024
ae3861b
subscription_portal: Grace period
tarteo Mar 20, 2024
2191f4b
subscription_portal: Fixup
tarteo Mar 20, 2024
f225d82
argocd_sale: Test tag action
tarteo Mar 20, 2024
d0f3512
argocd_sale: Test destroy app
tarteo Mar 21, 2024
44eddee
argocd_sale: Test destroy app
tarteo Mar 21, 2024
d3bfc0a
sale_recurring_payment: comment line
tarteo Mar 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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