diff --git a/membership_variable_period/README.rst b/membership_variable_period/README.rst new file mode 100644 index 0000000000000000000000000000000000000000..a61b25d14bbcbb8ebaf4fab6736b96c1ad1af550 --- /dev/null +++ b/membership_variable_period/README.rst @@ -0,0 +1,72 @@ +=============================== +Variable period for memberships +=============================== + +Current membership module allows to set products that define a fixed period +for a membership. This is good when the quotas are defined periodically, and +when you become a member, you are until the end of that quota cycle. + +But a lot of times, membership quotas express an amount of time that you +gain the membership. For example, one year since the subscription. + + +This module allows to make it in Odoo, using current membership features, +and adapting them for this purpose. As now the quota is not attached to a fixed +period, you can also invoice more than one quantity for being a member for +the corresponding number of periods. + +Finally, a cron has been included that triggers the recalculation of the +membership state, allowing to have "old members", which doesn't work well +on standard. + +Usage +===== + +Define a member product, and select 'Variable periods' in the field +*Membership type*. You will be able to select them the period for the +membership in quantity and interval. + + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/208/8.0 + + +Bug Tracker +=========== + +Bugs are tracked on `GitHub 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 +`here `_. + +Credits +======= + +Contributors +------------ + +* Pedro M. Baeza + +Icon +---- + +Original clipart from: + +* http://pixabay.com/es/en-contacto-con-tarjeta-de-cr%C3%A9dito-97574/ +* https://openclipart.org/detail/23920/sandglass + +Maintainer +---------- + +.. image:: http://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: http://odoo-community.org + +This module is maintained by the OCA. + +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. + +To contribute to this module, please visit http://odoo-community.org. diff --git a/membership_variable_period/__init__.py b/membership_variable_period/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9674e40272a0dd48021eda444ab29fb4b1435104 --- /dev/null +++ b/membership_variable_period/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Antiun Ingeniería S.L. - Pedro M. Baeza +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from . import models diff --git a/membership_variable_period/__openerp__.py b/membership_variable_period/__openerp__.py new file mode 100644 index 0000000000000000000000000000000000000000..561ca56893a98477372ee36cb211cbaaa254d8df --- /dev/null +++ b/membership_variable_period/__openerp__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Antiun Ingeniería S.L. - Pedro M. Baeza +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +{ + 'name': 'Variable period for memberships', + 'version': '8.0.1.0.0', + 'license': 'AGPL-3', + 'category': 'Association', + 'author': 'Antiun Ingeniería S.L., ' + 'Serv. Tecnol. Avanzados - Pedro M. Baeza, ' + 'Odoo Community Association (OCA)', + 'website': 'http://www.antiun.com', + 'depends': [ + 'membership', + ], + 'data': [ + 'views/product_template_views.xml', + 'views/res_partner_views.xml', + 'data/membership_data.xml', + ], + "installable": True, +} diff --git a/membership_variable_period/data/membership_data.xml b/membership_variable_period/data/membership_data.xml new file mode 100644 index 0000000000000000000000000000000000000000..47ca1a93567779d34aa441cbd94d8a2b7e3f656d --- /dev/null +++ b/membership_variable_period/data/membership_data.xml @@ -0,0 +1,18 @@ + + + + + + Check membership expiry + 1 + 10 + days + -1 + + + + + + + + diff --git a/membership_variable_period/i18n/de.po b/membership_variable_period/i18n/de.po new file mode 100644 index 0000000000000000000000000000000000000000..2fb6537deb54e054a0c272a6d0708aa8b9b14f95 --- /dev/null +++ b/membership_variable_period/i18n/de.po @@ -0,0 +1,107 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * membership_variable_period +# +# Translators: +# Rudolf Schnapka , 2016 +msgid "" +msgstr "" +"Project-Id-Version: vertical-association (8.0)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-01-19 12:02+0000\n" +"PO-Revision-Date: 2016-01-18 20:04+0000\n" +"Last-Translator: Rudolf Schnapka \n" +"Language-Team: German (http://www.transifex.com/oca/OCA-vertical-association-8-0/language/de/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: de\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: membership_variable_period +#: selection:product.template,membership_type:0 +msgid "Fixed dates" +msgstr "Feste Termine" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.membership_products_form_period +msgid "Interval" +msgstr "Intervall" + +#. module: membership_variable_period +#: field:product.template,membership_interval_qty:0 +msgid "Interval quantity" +msgstr "Intervallanzahl" + +#. module: membership_variable_period +#: field:product.template,membership_interval_unit:0 +msgid "Interval unit" +msgstr "Intervalleinheit" + +#. module: membership_variable_period +#: model:ir.model,name:membership_variable_period.model_account_invoice_line +msgid "Invoice Line" +msgstr "Rechnungsposition" + +#. module: membership_variable_period +#: field:account.invoice.line,membership_lines:0 +msgid "Membership lines" +msgstr "Mitgliedschafts-Positionen" + +#. module: membership_variable_period +#: field:product.template,membership_type:0 +msgid "Membership type" +msgstr "Art der Mitgliedschaft" + +#. module: membership_variable_period +#: model:ir.model,name:membership_variable_period.model_res_partner +msgid "Partner" +msgstr "Partner" + +#. module: membership_variable_period +#: model:ir.model,name:membership_variable_period.model_product_template +msgid "Product Template" +msgstr "Produktvorlage" + +#. module: membership_variable_period +#: selection:product.template,membership_type:0 +msgid "Variable periods" +msgstr "Variable Perioden" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "days" +msgstr "Tage" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "months" +msgstr "Monate" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "weeks" +msgstr "Wochen" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "years" +msgstr "Jahre" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.membership_products_form_period +msgid "{'invisible': [('membership_type', '!=', 'fixed')]}" +msgstr "" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.product_template_form_view_membership_period +msgid "" +"{'required': [('membership_type', '=', 'fixed'), ('membership', '=', True)]," +" 'invisible': [('membership_type', '!=', 'fixed')]}" +msgstr "" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.membership_products_form_period +msgid "" +"{'required': [('membership_type', '=', 'fixed'), ('membership', '=', True)]}" +msgstr "" diff --git a/membership_variable_period/i18n/es.po b/membership_variable_period/i18n/es.po new file mode 100644 index 0000000000000000000000000000000000000000..3cea35b8c4e1c30623f0f853fe2bab53bbc84283 --- /dev/null +++ b/membership_variable_period/i18n/es.po @@ -0,0 +1,106 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * membership_variable_period +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: vertical-association (8.0)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-09-07 20:26+0000\n" +"PO-Revision-Date: 2015-09-07 20:27+0000\n" +"Last-Translator: OCA Transbot \n" +"Language-Team: Spanish (http://www.transifex.com/oca/OCA-vertical-association-8-0/language/es/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: es\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: membership_variable_period +#: selection:product.template,membership_type:0 +msgid "Fixed dates" +msgstr "Fechas fijas" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.membership_products_form_period +msgid "Interval" +msgstr "Intervalo" + +#. module: membership_variable_period +#: field:product.template,membership_interval_qty:0 +msgid "Interval quantity" +msgstr "Cantidad del intervalo" + +#. module: membership_variable_period +#: field:product.template,membership_interval_unit:0 +msgid "Interval unit" +msgstr "Unidad del intervalo" + +#. module: membership_variable_period +#: model:ir.model,name:membership_variable_period.model_account_invoice_line +msgid "Invoice Line" +msgstr "Línea de factura" + +#. module: membership_variable_period +#: field:account.invoice.line,membership_lines:0 +msgid "Membership lines" +msgstr "Líneas de afiliación" + +#. module: membership_variable_period +#: field:product.template,membership_type:0 +msgid "Membership type" +msgstr "Tipo de afiliación" + +#. module: membership_variable_period +#: model:ir.model,name:membership_variable_period.model_res_partner +msgid "Partner" +msgstr "" + +#. module: membership_variable_period +#: model:ir.model,name:membership_variable_period.model_product_template +msgid "Product Template" +msgstr "Plantilla de producto" + +#. module: membership_variable_period +#: selection:product.template,membership_type:0 +msgid "Variable periods" +msgstr "Periodos variables" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "days" +msgstr "días" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "months" +msgstr "meses" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "weeks" +msgstr "semanas" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "years" +msgstr "años" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.membership_products_form_period +msgid "{'invisible': [('membership_type', '!=', 'fixed')]}" +msgstr "" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.product_template_form_view_membership_period +msgid "" +"{'required': [('membership_type', '=', 'fixed'), ('membership', '=', True)]," +" 'invisible': [('membership_type', '!=', 'fixed')]}" +msgstr "" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.membership_products_form_period +msgid "" +"{'required': [('membership_type', '=', 'fixed'), ('membership', '=', True)]}" +msgstr "" diff --git a/membership_variable_period/i18n/membership_variable_period.pot b/membership_variable_period/i18n/membership_variable_period.pot new file mode 100644 index 0000000000000000000000000000000000000000..9e4e1bdec55abe62a3dcde1106f5e9e13991432b --- /dev/null +++ b/membership_variable_period/i18n/membership_variable_period.pot @@ -0,0 +1,97 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * membership_variable_period +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-08-21 00:25+0000\n" +"PO-Revision-Date: 2015-08-21 00:25+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: membership_variable_period +#: selection:product.template,membership_type:0 +msgid "Fixed dates" +msgstr "" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.membership_products_form_period +msgid "Interval" +msgstr "" + +#. module: membership_variable_period +#: field:product.template,membership_interval_qty:0 +msgid "Interval quantity" +msgstr "" + +#. module: membership_variable_period +#: field:product.template,membership_interval_unit:0 +msgid "Interval unit" +msgstr "" + +#. module: membership_variable_period +#: model:ir.model,name:membership_variable_period.model_account_invoice_line +msgid "Invoice Line" +msgstr "" + +#. module: membership_variable_period +#: field:account.invoice.line,membership_lines:0 +msgid "Membership lines" +msgstr "" + +#. module: membership_variable_period +#: field:product.template,membership_type:0 +msgid "Membership type" +msgstr "" + +#. module: membership_variable_period +#: model:ir.model,name:membership_variable_period.model_res_partner +msgid "Partner" +msgstr "" + +#. module: membership_variable_period +#: model:ir.model,name:membership_variable_period.model_product_template +msgid "Product Template" +msgstr "" + +#. module: membership_variable_period +#: selection:product.template,membership_type:0 +msgid "Variable periods" +msgstr "" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "days" +msgstr "" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "months" +msgstr "" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "weeks" +msgstr "" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "years" +msgstr "" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.membership_products_form_period +msgid "{'invisible': [('membership_type', '!=', 'fixed')]}" +msgstr "" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.membership_products_form_period +msgid "{'required': [('membership_type', '=', 'fixed')]}" +msgstr "" + diff --git a/membership_variable_period/i18n/pt_BR.po b/membership_variable_period/i18n/pt_BR.po new file mode 100644 index 0000000000000000000000000000000000000000..36c58d7d06c472eeb5cb2d9d14a7d850c64e628a --- /dev/null +++ b/membership_variable_period/i18n/pt_BR.po @@ -0,0 +1,106 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * membership_variable_period +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: vertical-association (8.0)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-10-14 07:14+0000\n" +"PO-Revision-Date: 2015-09-07 20:26+0000\n" +"Last-Translator: <>\n" +"Language-Team: Portuguese (Brazil) (http://www.transifex.com/oca/OCA-vertical-association-8-0/language/pt_BR/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: pt_BR\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#. module: membership_variable_period +#: selection:product.template,membership_type:0 +msgid "Fixed dates" +msgstr "" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.membership_products_form_period +msgid "Interval" +msgstr "" + +#. module: membership_variable_period +#: field:product.template,membership_interval_qty:0 +msgid "Interval quantity" +msgstr "" + +#. module: membership_variable_period +#: field:product.template,membership_interval_unit:0 +msgid "Interval unit" +msgstr "" + +#. module: membership_variable_period +#: model:ir.model,name:membership_variable_period.model_account_invoice_line +msgid "Invoice Line" +msgstr "Linha de faturamento" + +#. module: membership_variable_period +#: field:account.invoice.line,membership_lines:0 +msgid "Membership lines" +msgstr "" + +#. module: membership_variable_period +#: field:product.template,membership_type:0 +msgid "Membership type" +msgstr "" + +#. module: membership_variable_period +#: model:ir.model,name:membership_variable_period.model_res_partner +msgid "Partner" +msgstr "" + +#. module: membership_variable_period +#: model:ir.model,name:membership_variable_period.model_product_template +msgid "Product Template" +msgstr "Modelo do Produto" + +#. module: membership_variable_period +#: selection:product.template,membership_type:0 +msgid "Variable periods" +msgstr "" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "days" +msgstr "" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "months" +msgstr "" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "weeks" +msgstr "" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "years" +msgstr "" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.membership_products_form_period +msgid "{'invisible': [('membership_type', '!=', 'fixed')]}" +msgstr "" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.product_template_form_view_membership_period +msgid "" +"{'required': [('membership_type', '=', 'fixed'), ('membership', '=', True)]," +" 'invisible': [('membership_type', '!=', 'fixed')]}" +msgstr "" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.membership_products_form_period +msgid "" +"{'required': [('membership_type', '=', 'fixed'), ('membership', '=', True)]}" +msgstr "" diff --git a/membership_variable_period/i18n/sl.po b/membership_variable_period/i18n/sl.po new file mode 100644 index 0000000000000000000000000000000000000000..a44848b817d567ef96e4023684f048aa5d31c47c --- /dev/null +++ b/membership_variable_period/i18n/sl.po @@ -0,0 +1,107 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * membership_variable_period +# +# Translators: +# Matjaž Mozetič , 2015 +msgid "" +msgstr "" +"Project-Id-Version: vertical-association (8.0)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-09-23 19:13+0000\n" +"PO-Revision-Date: 2015-09-20 19:07+0000\n" +"Last-Translator: Matjaž Mozetič \n" +"Language-Team: Slovenian (http://www.transifex.com/oca/OCA-vertical-association-8-0/language/sl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: sl\n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n" + +#. module: membership_variable_period +#: selection:product.template,membership_type:0 +msgid "Fixed dates" +msgstr "Fiksni datumi" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.membership_products_form_period +msgid "Interval" +msgstr "Interval" + +#. module: membership_variable_period +#: field:product.template,membership_interval_qty:0 +msgid "Interval quantity" +msgstr "Količina intervala" + +#. module: membership_variable_period +#: field:product.template,membership_interval_unit:0 +msgid "Interval unit" +msgstr "Enota intervala" + +#. module: membership_variable_period +#: model:ir.model,name:membership_variable_period.model_account_invoice_line +msgid "Invoice Line" +msgstr "Postavka računa" + +#. module: membership_variable_period +#: field:account.invoice.line,membership_lines:0 +msgid "Membership lines" +msgstr "Postavke članarin" + +#. module: membership_variable_period +#: field:product.template,membership_type:0 +msgid "Membership type" +msgstr "Tip članarine" + +#. module: membership_variable_period +#: model:ir.model,name:membership_variable_period.model_res_partner +msgid "Partner" +msgstr "Partner" + +#. module: membership_variable_period +#: model:ir.model,name:membership_variable_period.model_product_template +msgid "Product Template" +msgstr "Predloga proizvoda" + +#. module: membership_variable_period +#: selection:product.template,membership_type:0 +msgid "Variable periods" +msgstr "Variabilna obdobja" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "days" +msgstr "dni" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "months" +msgstr "mesecev" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "weeks" +msgstr "tednov" + +#. module: membership_variable_period +#: selection:product.template,membership_interval_unit:0 +msgid "years" +msgstr "let" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.membership_products_form_period +msgid "{'invisible': [('membership_type', '!=', 'fixed')]}" +msgstr "{'invisible': [('membership_type', '!=', 'fixed')]}" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.product_template_form_view_membership_period +msgid "" +"{'required': [('membership_type', '=', 'fixed'), ('membership', '=', True)]," +" 'invisible': [('membership_type', '!=', 'fixed')]}" +msgstr "{'required': [('membership_type', '=', 'fixed'), ('membership', '=', True)], 'invisible': [('membership_type', '!=', 'fixed')]}" + +#. module: membership_variable_period +#: view:product.template:membership_variable_period.membership_products_form_period +msgid "" +"{'required': [('membership_type', '=', 'fixed'), ('membership', '=', True)]}" +msgstr "{'required': [('membership_type', '=', 'fixed'), ('membership', '=', True)]}" diff --git a/membership_variable_period/models/__init__.py b/membership_variable_period/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d92d15df60511001578176d1af23e19f7a9ef491 --- /dev/null +++ b/membership_variable_period/models/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Antiun Ingeniería S.L. - Pedro M. Baeza +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from . import product_template +from . import account_invoice +from . import res_partner diff --git a/membership_variable_period/models/account_invoice.py b/membership_variable_period/models/account_invoice.py new file mode 100644 index 0000000000000000000000000000000000000000..88186ccf35d31513cda4ae987b9679f1d6f024f7 --- /dev/null +++ b/membership_variable_period/models/account_invoice.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Antiun Ingeniería S.L. - Pedro M. Baeza +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from openerp import models, api, fields +from datetime import timedelta +import math + + +class AccountInvoiceLine(models.Model): + _inherit = "account.invoice.line" + + membership_lines = fields.One2many( + comodel_name='membership.membership_line', + inverse_name='account_invoice_line') + + def _prepare_membership_line(self, invoice, product, price_unit, line_id): + memb_line_model = self.env['membership.membership_line'] + memb_lines = memb_line_model.search( + [('partner', '=', invoice.partner_id.id), + ('state', 'not in', ['none', 'canceled'])], + order="date_to desc") + if memb_lines and memb_lines[0].date_to: + date_from = (fields.Date.from_string(memb_lines[0].date_to) + + timedelta(days=1)) + else: + date_from = fields.Date.from_string( + invoice.date_invoice or fields.Date.today()) + date_to = (product.product_tmpl_id._get_next_date(date_from) - + timedelta(days=1)) + return { + 'partner': invoice.partner_id.id, + 'membership_id': product.id, + 'member_price': price_unit, + 'date': fields.Date.today(), + 'date_from': fields.Date.to_string(date_from), + 'date_to': fields.Date.to_string(date_to), + 'state': 'waiting', + 'account_invoice_line': line_id, + } + + @api.multi + def write(self, vals): + """Create before the lines of membership with variable period.""" + memb_line_model = self.env['membership.membership_line'] + if any(x in vals for x in ['product_id', 'quantity', 'invoice_id']): + for line in self: + product = ( + self.env['product.product'].browse(vals['product_id']) if + vals.get('product_id') else line.product_id) + invoice = ( + self.env['account.invoice'].browse(vals['invoice_id']) if + vals.get('invoice_id') else line.invoice_id) + if (invoice.type == 'out_invoice' and + product.membership and + product.membership_type == 'variable'): + quantity = vals.get('quantity', line.quantity) + memb_lines = memb_line_model.search( + [('account_invoice_line', '=', line.id), + ('state', 'not in', ['none', 'canceled'])], + order="date desc, id desc") + price_unit = vals.get('price_unit', line.price_unit) + membership_vals = self._prepare_membership_line( + invoice, product, price_unit, line.id) + if len(memb_lines) < quantity: + # Add missing membership lines + missing_number = quantity - len(memb_lines) + for i in range(int(missing_number)): + memb_line_model.create(membership_vals) + elif len(memb_lines) > quantity: + # Remove extra membership lines + extra_number = ( + len(memb_lines) - int(math.ceil(quantity))) + memb_lines[:extra_number].unlink() + else: + # Update data of current membership line + memb_lines[0].write(membership_vals) + return super(AccountInvoiceLine, self).write(vals) + + @api.model + def create(self, vals): + price_unit = vals.get('price_unit', 0.0) + line = super(AccountInvoiceLine, self).create(vals) + if (line.invoice_id.type == 'out_invoice' and + line.product_id.membership and + line.product_id.membership_type == 'variable'): + for i in range(int(line.quantity)): + membership_vals = self._prepare_membership_line( + line.invoice_id, line.product_id, price_unit, line.id) + # There's already the super line + line.membership_lines[0].write(membership_vals) + return line diff --git a/membership_variable_period/models/product_template.py b/membership_variable_period/models/product_template.py new file mode 100644 index 0000000000000000000000000000000000000000..8840a6ecc5987fc2db2b1896e9a760fe2c92e7fb --- /dev/null +++ b/membership_variable_period/models/product_template.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Antiun Ingeniería S.L. - Pedro M. Baeza +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from openerp import models, fields, api +from datetime import timedelta +from dateutil.relativedelta import relativedelta + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + @api.multi + def _get_next_date(self, date): + """Get the date that results on incrementing given date an interval of + time in time unit. + @param date: Original date. + @param unit: Interval time unit. + @param interval: Quantity of the time unit. + @rtype: date + @return: The date incremented in 'interval' units of 'unit'. + """ + self.ensure_one() + if isinstance(date, str): + date = fields.Date.from_string(date) + if self.membership_interval_unit == 'days': + return date + timedelta(days=self.membership_interval_qty) + elif self.membership_interval_unit == 'weeks': + return date + timedelta(weeks=self.membership_interval_qty) + elif self.membership_interval_unit == 'months': + return date + relativedelta(months=self.membership_interval_qty) + elif self.membership_interval_unit == 'years': + return date + relativedelta(years=self.membership_interval_qty) + + membership_type = fields.Selection( + selection=[('fixed', 'Fixed dates'), + ('variable', 'Variable periods')], + default='fixed', string="Membership type", required=True) + membership_interval_qty = fields.Integer( + string="Interval quantity", default=1) + membership_interval_unit = fields.Selection( + selection=[('days', 'days'), + ('weeks', 'weeks'), + ('months', 'months'), + ('years', 'years')], + string="Interval unit", default='years') + + def _correct_vals_membership_type(self, vals, membership_type): + if membership_type == 'variable': + vals['membership_date_from'] = False + vals['membership_date_to'] = False + return vals + + @api.model + def create(self, vals): + self._correct_vals_membership_type( + vals, vals.get('membership_type', 'fixed')) + return super(ProductTemplate, self).create(vals) + + @api.multi + def write(self, vals): + self._correct_vals_membership_type( + vals, vals.get('membership_type', self.membership_type)) + return super(ProductTemplate, self).write(vals) diff --git a/membership_variable_period/models/res_partner.py b/membership_variable_period/models/res_partner.py new file mode 100644 index 0000000000000000000000000000000000000000..5e757e4f5feab5785c7e311156351c78588b0ce6 --- /dev/null +++ b/membership_variable_period/models/res_partner.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Antiun Ingeniería S.L. - Pedro M. Baeza +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from openerp import models, api + + +class ResPartner(models.Model): + _inherit = "res.partner" + + @api.model + def check_membership_expiry(self): + """Force a recalculation on each partner that is member.""" + partners = self.search( + [('membership_state', 'not in', ['old', 'none', 'free']), + ('associate_member', '=', False)]) + # It has to be triggered one by one to avoid an error + for partner in partners: + partner.write({'membership_state': 'none'}) + return True diff --git a/membership_variable_period/static/description/icon.png b/membership_variable_period/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6c6662f1522dc0aca1c2b6dfa44f7a64bbbe55ca Binary files /dev/null and b/membership_variable_period/static/description/icon.png differ diff --git a/membership_variable_period/static/description/icon.svg b/membership_variable_period/static/description/icon.svg new file mode 100644 index 0000000000000000000000000000000000000000..6fb22c9d6ec32254f0e74f799a146d557ba98861 --- /dev/null +++ b/membership_variable_period/static/description/icon.svg @@ -0,0 +1,455 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/membership_variable_period/static/description/icon_main.svg b/membership_variable_period/static/description/icon_main.svg new file mode 100644 index 0000000000000000000000000000000000000000..c7f5f0a9cb7b2884bcfab6bde426af6a2d770102 --- /dev/null +++ b/membership_variable_period/static/description/icon_main.svg @@ -0,0 +1,735 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/membership_variable_period/tests/__init__.py b/membership_variable_period/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4a1e59e2bbdfb2fa2707ebf7e086c79e7efad260 --- /dev/null +++ b/membership_variable_period/tests/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Antiun Ingeniería S.L. - Pedro M. Baeza +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from . import test_membership_variable_period diff --git a/membership_variable_period/tests/test_membership_variable_period.py b/membership_variable_period/tests/test_membership_variable_period.py new file mode 100644 index 0000000000000000000000000000000000000000..691603c5037de5a06289598df9992cc0252f8e05 --- /dev/null +++ b/membership_variable_period/tests/test_membership_variable_period.py @@ -0,0 +1,156 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Pedro M. Baeza +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +import openerp.tests.common as common +from datetime import date + + +class TestMembershipVariablePeriod(common.TransactionCase): + + def setUp(self): + super(TestMembershipVariablePeriod, self).setUp() + self.product = self.env['product.product'].create( + { + 'name': 'Membership product with variable period', + 'membership': True, + 'membership_date_from': '2015-01-01', + 'membership_date_to': '2015-12-31', + 'membership_type': 'variable', + 'membership_interval_qty': 1, + 'membership_interval_unit': 'weeks', + }) + self.partner = self.env['res.partner'].create({'name': 'Test'}) + + def test_create_invoice_membership_product_days(self): + self.product.membership_interval_qty = 20 + self.product.membership_interval_unit = 'days' + invoice = self.env['account.invoice'].create( + {'partner_id': self.partner.id, + 'date_invoice': '2015-07-01', + 'account_id': self.partner.property_account_receivable.id, + 'invoice_line': [(0, 0, {'product_id': self.product.id, + 'name': 'Membership w/o prorrate'})]} + ) + membership_line = invoice.invoice_line[0].membership_lines[0] + self.assertEqual(membership_line.date_from, '2015-07-01') + self.assertEqual(membership_line.date_to, '2015-07-20') + self.assertEqual(self.partner.membership_start, '2015-07-01') + self.assertEqual(self.partner.membership_stop, '2015-07-20') + + def test_create_invoice_membership_product_week(self): + invoice = self.env['account.invoice'].create( + {'partner_id': self.partner.id, + 'date_invoice': '2015-07-01', + 'account_id': self.partner.property_account_receivable.id, + 'invoice_line': [(0, 0, {'product_id': self.product.id, + 'name': 'Membership w/o prorrate'})]} + ) + membership_line = invoice.invoice_line[0].membership_lines[0] + self.assertEqual(membership_line.date_from, '2015-07-01') + self.assertEqual(membership_line.date_to, '2015-07-07') + self.assertEqual(self.partner.membership_start, '2015-07-01') + self.assertEqual(self.partner.membership_stop, '2015-07-07') + + def test_create_invoice_membership_product_month(self): + self.product.membership_interval_unit = 'months' + invoice = self.env['account.invoice'].create( + {'partner_id': self.partner.id, + 'date_invoice': '2015-04-15', + 'account_id': self.partner.property_account_receivable.id, + 'invoice_line': [(0, 0, {'product_id': self.product.id, + 'name': 'Membership with prorrate'})]} + ) + membership_line = invoice.invoice_line[0].membership_lines[0] + self.assertEqual(membership_line.date_from, '2015-04-15') + self.assertEqual(membership_line.date_to, '2015-05-14') + self.assertEqual(self.partner.membership_start, '2015-04-15') + self.assertEqual(self.partner.membership_stop, '2015-05-14') + + def test_create_invoice_membership_product_year(self): + self.product.membership_interval_unit = 'years' + invoice = self.env['account.invoice'].create( + {'partner_id': self.partner.id, + 'date_invoice': '2016-07-01', # It's leap year + 'account_id': self.partner.property_account_receivable.id, + 'invoice_line': [(0, 0, {'product_id': self.product.id, + 'name': 'Membership with prorrate'})]} + ) + membership_line = invoice.invoice_line[0].membership_lines[0] + self.assertEqual(membership_line.date_from, '2016-07-01') + self.assertEqual(membership_line.date_to, '2017-06-30') + self.assertEqual(self.partner.membership_start, '2016-07-01') + self.assertEqual(self.partner.membership_stop, '2017-06-30') + + def test_modify_invoice_membership_product(self): + self.product.membership_interval_unit = 'years' + invoice = self.env['account.invoice'].create( + {'partner_id': self.partner.id, + 'date_invoice': '2015-07-01', + 'account_id': self.partner.property_account_receivable.id, + 'invoice_line': [(0, 0, {'product_id': self.product.id, + 'name': 'Membership w/o prorrate'})]} + ) + # Add quantity + invoice.invoice_line[0].quantity = 2.0 + membership_lines = invoice.invoice_line[0].membership_lines + self.assertEqual(len(membership_lines), 2) + self.assertEqual(membership_lines[1].date_from, '2015-07-01') + self.assertEqual(membership_lines[1].date_to, '2016-06-30') + self.assertEqual(membership_lines[0].date_from, '2016-07-01') + self.assertEqual(membership_lines[0].date_to, '2017-06-30') + self.assertEqual(self.partner.membership_start, '2015-07-01') + self.assertEqual(self.partner.membership_stop, '2017-06-30') + # Remove quantity + invoice.invoice_line[0].quantity = 1.0 + membership_lines = invoice.invoice_line[0].membership_lines + self.assertEqual(len(membership_lines), 1) + self.assertEqual(membership_lines[0].date_from, '2015-07-01') + self.assertEqual(membership_lines[0].date_to, '2016-06-30') + + def test_create_and_modify_invoice_line_membership_product(self): + self.product.membership_interval_qty = 20 + self.product.membership_interval_unit = 'days' + invoice_line = self.env['account.invoice.line'].create( + {'product_id': self.product.id, + 'name': 'Membership w/o prorrate'}) + invoice = self.env['account.invoice'].create( + {'partner_id': self.partner.id, + 'date_invoice': '2015-07-01', + 'account_id': self.partner.property_account_receivable.id}) + invoice_line.invoice_id = invoice.id + membership_line = invoice.invoice_line[0].membership_lines[0] + self.assertEqual(membership_line.date_from, '2015-07-01') + self.assertEqual(membership_line.date_to, '2015-07-20') + self.assertEqual(self.partner.membership_start, '2015-07-01') + self.assertEqual(self.partner.membership_stop, '2015-07-20') + + def test_check_membership_expiry(self): + self.env['membership.membership_line'].create( + {'partner': self.partner.id, + 'membership_id': self.product.id, + 'member_price': 1.0, + 'date': '2014-01-01', + 'date_from': '2014-01-01', + 'date_to': '2014-12-31', + 'state': 'paid', + 'account_invoice_line': self.env['account.invoice.line'].search( + [], limit=1).id}) + # Force state to let the calculation return to the computed one + self.partner.membership_stop = '2014-12-31' + self.env['res.partner'].check_membership_expiry() + self.assertEqual(self.partner.membership_state, 'none') + + def test_get_next_date(self): + test_suite = [ + # Add here more border cases that can be detected in the future + ('2015-01-01', "days", 25, date(day=26, month=1, year=2015)), + ('2015-01-01', "weeks", 1, date(day=8, month=1, year=2015)), + ('2015-01-01', "months", 3, date(day=1, month=4, year=2015)), + ('2015-01-01', "years", 1, date(day=1, month=1, year=2016)), + ] + template_model = self.env['product.template'] + for old_date, interval, qty, next_date in test_suite: + template = template_model.new() + template.membership_interval_unit = interval + template.membership_interval_qty = qty + self.assertEqual(template._get_next_date(old_date), next_date) diff --git a/membership_variable_period/views/product_template_views.xml b/membership_variable_period/views/product_template_views.xml new file mode 100644 index 0000000000000000000000000000000000000000..cb15846c1d77170c595b5886e517d986f465e274 --- /dev/null +++ b/membership_variable_period/views/product_template_views.xml @@ -0,0 +1,74 @@ + + + + + + Membership Products (variable period) + product.template + + + + + + Membership Products (regular - variable period) + product.template + + + + + + + + + {'required': [('membership_type', '=', 'fixed'), ('membership', '=', True)], 'invisible': [('membership_type', '!=', 'fixed')]} + 0 + + + {'required': [('membership_type', '=', 'fixed'), ('membership', '=', True)], 'invisible': [('membership_type', '!=', 'fixed')]} + 0 + + + + + + diff --git a/membership_variable_period/views/res_partner_views.xml b/membership_variable_period/views/res_partner_views.xml new file mode 100644 index 0000000000000000000000000000000000000000..67b0bc2ee5c964282cbb1366481749530bd4306d --- /dev/null +++ b/membership_variable_period/views/res_partner_views.xml @@ -0,0 +1,17 @@ + + + + + + res.partner.form.membership.period + res.partner + + + + + + + + + +