Browse Source

[ADD] membership_initial_discount

16.0
RemiFr82 1 year ago
parent
commit
f88ee49a7c
  1. 101
      membership_initial_discount/README.rst
  2. 2
      membership_initial_discount/__init__.py
  3. 17
      membership_initial_discount/__manifest__.py
  4. 65
      membership_initial_discount/i18n/fr.po
  5. 6
      membership_initial_discount/models/__init__.py
  6. 65
      membership_initial_discount/models/account_move_line.py
  7. 18
      membership_initial_discount/models/membership.py
  8. 26
      membership_initial_discount/models/product_product.py
  9. 58
      membership_initial_discount/models/product_template.py
  10. 65
      membership_initial_discount/models/res_partner.py
  11. 18
      membership_initial_discount/views/product_template_views.xml

101
membership_initial_discount/README.rst

@ -0,0 +1,101 @@
===========================
Initial fee for memberships
===========================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fvertical--association-lightgray.png?logo=github
:target: https://github.com/OCA/vertical-association/tree/14.0/membership_initial_fee
:alt: OCA/vertical-association
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/vertical-association-14-0/vertical-association-14-0-membership_initial_fee
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/208/14.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
Charge an initial fee when a partner is invoiced for the first time with a
member product.
**Table of contents**
.. contents::
:local:
Usage
=====
Define a member product, and select 'Fixed amount' or 'Percentage of the price'
for invoicing an extra charge in the first invoice that is created with this
member product.
By default, a line with the description *Membership initial fee*. If you want
to change this text, you can set a different sale description in the product
used for the fee.
Known issues / Roadmap
======================
* Add initial fee information to membership analysis.
* Add some criteria for adding initial fee:
* Add initial fee if last membership ended x weeks/months/years ago
* Add initial fee if last membership product_id is different
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/vertical-association/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/vertical-association/issues/new?body=module:%20membership_initial_fee%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* Tecnativa
Contributors
~~~~~~~~~~~~
* `Tecnativa <https://www.tecnativa.com>`__:
* Pedro M. Baeza
* Rafael Blasco
* David Vidal
* `Onestein <https://onestein.eu>`__:
* Andrea Stirpe
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
This module is part of the `OCA/vertical-association <https://github.com/OCA/vertical-association/tree/14.0/membership_initial_fee>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

2
membership_initial_discount/__init__.py

@ -0,0 +1,2 @@
# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0
from . import models

17
membership_initial_discount/__manifest__.py

@ -0,0 +1,17 @@
# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0
{
"name": "Initial discount on 1st membership",
"version": "1.0.0",
"license": "AGPL-3",
"category": "Association",
"author": "RemiFr82",
"website": "https://remifr82.me",
# "website": "https://github.com/OCA/vertical-association",
"depends": [
"membership",
],
"data": [
"views/product_template_views.xml",
],
}

65
membership_initial_discount/i18n/fr.po

@ -0,0 +1,65 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * membership_initial_discount
#
# Translators:
# OCA Transbot <transbot@odoo-community.org>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-06-08 02:41+0000\n"
"PO-Revision-Date: 2022-05-30 18:05+0000\n"
"Last-Translator: Abdourahmane Wone <abdourahmanewone@gmail.com>\n"
"Language-Team: French (https://www.transifex.com/oca/teams/23907/fr/)\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 4.3.2\n"
#. module: membership_initial_discount
#: model:ir.model.fields,field_description:membership_initial_discount.field_product_product__fixed_discount
#: model:ir.model.fields,field_description:membership_initial_discount.field_product_template__fixed_discount
msgid "Discount amount"
msgstr "Montant de la remise"
#. module: membership_initial_discount
#: selection:product.template,initial_discount:0
msgid "Fixed amount"
msgstr "Montant fixe"
#. module: membership_initial_discount
#: model:ir.model.fields,field_description:membership_initial_discount.field_product_product__initial_discount
#: model:ir.model.fields,field_description:membership_initial_discount.field_product_template__initial_discount
#: model_terms:ir.ui.view,arch_db:membership_initial_discount.membership_products_form_initial_discount
msgid "Initial discount"
msgstr "Remise initiale"
#. module: membership_initial_discount
#: model:ir.model,name:membership_initial_discount.model_account_invoice_line
msgid "Invoice Line"
msgstr "Ligne de facture"
#. module: membership_initial_discount
#: selection:product.template,initial_discount:0
msgid "No initial discount"
msgstr "Pas de remise initiale"
#. module: membership_initial_discount
#: model:ir.model.fields,field_description:membership_initial_discount.field_product_product__percentage_discount
#: model:ir.model.fields,field_description:membership_initial_discount.field_product_template__percentage_discount
msgid "Discount (%)"
msgstr "Remise en %"
#. module: membership_initial_discount
#: selection:product.template,initial_discount:0
msgid "Percentage of the price"
msgstr "Pourcentage du prix"
#. module: membership_initial_discount
#: model:ir.model,name:membership_initial_discount.model_product_template
msgid "Product Template"
msgstr "Modèle d'article"

6
membership_initial_discount/models/__init__.py

@ -0,0 +1,6 @@
# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0
from . import account_move_line
from . import membership
from . import product_product
from . import product_template
from . import res_partner

65
membership_initial_discount/models/account_move_line.py

@ -0,0 +1,65 @@
# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0
from odoo import api, models, _
class AccountMoveLine(models.Model):
_inherit = "account.move.line"
def _get_computed_price_unit(self):
res = super()._get_computed_price_unit()
if self.move_id.is_invoice() and self.initial_membership_check():
product = self.product_id
if product.initial_discount == "fixed" and product.fixed_discount:
res -= product.fixed_discount
return res
@api.onchange("product_id")
def _onchange_product_id(self):
super()._onchange_product_id()
for line in self:
if not line.product_id or line.display_type in (
"line_section",
"line_note",
):
continue
if line.move_id.is_invoice() and line.initial_membership_check():
product = line.product_id
if product.initial_discount == "percent" and product.percent_discount:
line.discount = product.percent_discount
else:
pass
def initial_membership_check(self):
"""
Inherit this method to implement a custom method
to decide whether or not to create the initial discount
:return:
"""
self.ensure_one()
product = self.product_id
if not product or not product.membership or product.initial_discount == "none":
return False
# If we are associated to another partner membership, evaluate that
# partner lines
partner = self.partner_id.associate_member or self.move_id.partner_id
# By default, partner to check is the partner of the invoice, but
# if a special method is found, overwritten in other modules, then
# the partner is got from that method
if hasattr(self, "_get_partner_for_membership"): # pragma: no cover
partner = self._get_partner_for_membership()
# See if partner has any membership line to decide whether or not
# to create the initial discount
member_lines = self.env["membership.membership_line"].search(
[
("partner", "=", partner.id),
(
"account_invoice_line",
"not in",
[self.id or self._origin.id],
),
("state", "not in", ["none", "canceled"]),
]
)
return not bool(member_lines)

18
membership_initial_discount/models/membership.py

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models
class MembershipLine(models.Model):
_inherit = "membership.membership_line"
@api.model_create_multi
def create(self, vals_list):
res = super().create(vals_list)
for line in res:
discount = line.account_invoice_line.discount
if discount:
line.member_price *= discount / 100.0
return res

26
membership_initial_discount/models/product_product.py

@ -0,0 +1,26 @@
# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
DISCOUNTS = [
("none", _("No initial discount")),
("fixed", _("Fixed amount")),
("percent", _("Percentage of the price")),
]
class ProductProduct(models.Model):
_inherit = "product.product"
@api.constrains("lst_price", "initial_discount", "fixed_discount")
def check_discount_fixed(self):
for product in self:
if (
product.initial_discount == "fixed"
and product.lst_price <= product.initial_discount
):
raise ValidationError(
_(
"Fixed discount for 1st membership must be less than the product template or any of its variants sale price."
)
)

58
membership_initial_discount/models/product_template.py

@ -0,0 +1,58 @@
# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
DISCOUNTS = [
("none", _("No initial discount")),
("fixed", _("Fixed amount")),
("percent", _("Percentage of the price")),
]
class ProductTemplate(models.Model):
_inherit = "product.template"
initial_discount = fields.Selection(
selection=DISCOUNTS,
default="none",
string="Initial discount",
required=True,
)
fixed_discount = fields.Float(
string="Discount amount",
digits="Product Price",
)
percent_discount = fields.Float(
string="Discount (%)",
digits=(12, 2),
)
@api.constrains("list_price", "initial_discount", "fixed_discount")
def check_fixed_discount(self):
for product in self:
if (
product.initial_discount == "fixed"
and product.list_price <= product.fixed_discount
):
raise ValidationError(
_(
"Fixed discount for 1st membership must be less than the product template, or any of its variants, sale price."
)
)
@api.constrains("initial_discount", "percent_discount")
def check_percent_discount(self):
for product in self:
if product.initial_discount == "percent" and product.percent_discount < 0.0:
raise ValidationError(
_(
'Percent discount cannot handle a negative value.\nIf you wan to apply extra fees, please install "Membership initial fee" module (OCA/vertical-association)'
)
)
elif (
product.initial_discount == "percent"
and product.percent_discount >= 100.0
):
raise ValidationError(
_("Percent discount must handle a value smaller than 100%.")
)

65
membership_initial_discount/models/res_partner.py

@ -0,0 +1,65 @@
from odoo import models, api, _
from odoo.exceptions import UserError
class ResPartner(models.Model):
_inherit = "res.partner"
def initial_membership_check(self):
self.ensure_one()
partner = self.associate_member or self
member_lines = self.env["membership.membership_line"].search(
[
("partner", "=", partner.id),
("state", "not in", ["none", "canceled"]),
]
)
return not bool(member_lines)
def prepare_membership_invoice_vals(self, product, amount):
discount = 0.0
if self.initial_membership_check():
if product.initial_discount == "fixed" and product.fixed_discount:
print("fixed discount")
fixed_disc = product.fixed_discount
amount -= fixed_disc
elif product.initial_discount == "percent" and product.percent_discount:
print("percent discount")
discount = product.percent_discount
else:
pass
vals = {
"move_type": "out_invoice",
"partner_id": self.id,
"invoice_line_ids": [
(
0,
None,
{
"product_id": product.id,
"quantity": 1,
"price_unit": amount,
"discount": discount,
"tax_ids": [(6, 0, product.taxes_id.ids)],
},
)
],
}
return vals
def create_membership_invoice(self, product, amount):
"""Create Customer Invoice of Membership for partners."""
invoice_vals_list = []
for partner in self:
addr = partner.address_get(["invoice"])
if partner.free_member:
raise UserError(_("Partner is a free Member."))
if not addr.get("invoice", False):
raise UserError(
_("Partner doesn't have an address to make the invoice.")
)
invoice_vals_list.append(
partner.prepare_membership_invoice_vals(product, amount)
)
return self.env["account.move"].create(invoice_vals_list)

18
membership_initial_discount/views/product_template_views.xml

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="membership_products_form_initial_discount" model="ir.ui.view">
<field name="name">Membership Products (initial discount)</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="membership.membership_products_form" />
<field name="arch" type="xml">
<xpath expr="//group[field[@name='list_price']]" position="after">
<group string="Initial discount">
<field name="initial_discount" />
<field name="fixed_discount" attrs="{'required': [('initial_discount', '=', 'fixed')], 'invisible': [('initial_discount', '!=', 'fixed')]}" />
<field name="percent_discount" attrs="{'required': [('initial_discount', '=', 'percent')], 'invisible': [('initial_discount', '!=', 'percent')]}" />
<field name="product_variant_ids" invisible="1" colspan="4"/>
</group>
</xpath>
</field>
</record>
</odoo>
Loading…
Cancel
Save