[MIG] membership: migrate to new api,

- Split compute function in res.partner into 4 functions as they no more share the same dependencies
- removed dead constraint from 'membership.membership_line' model. That constraint checks if the invoice's date
  is greater than membership line's date but the membership line is created before the draft invoice is created
  (and that too without date set), hence it can't be raised.(Though, it can be raised if we check it on 'state' field).
This commit is contained in:
Mansi Gandhi
2016-08-03 11:07:10 +02:00
committed by Yannick Tivisse
parent 4357afa94b
commit 6f1fb4fd09
17 changed files with 340 additions and 613 deletions
+1 -4
View File
@@ -4,7 +4,7 @@
{
'name': 'Membership Management',
'version': '0.1',
'version': '1.0',
'category': 'Sales',
'description': """
This module allows you to manage all operations for managing memberships.
@@ -35,8 +35,5 @@ invoice and send propositions for membership renewal.
'website': 'https://www.odoo.com/page/community-builder',
'test': [
'../account/test/account_minimal_test.xml',
'test/test_membership.yml'
],
'installable': True,
'auto_install': False,
}
+2 -2
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<odoo>
<data noupdate="1">
<record id="ir_cron_update_membership" model="ir.cron">
<field name="name">Check membership payment</field>
@@ -9,4 +9,4 @@
<field name="function">_cron_update_membership</field>
</record>
</data>
</openerp>
</odoo>
+3 -5
View File
@@ -1,6 +1,5 @@
<?xml version="1.0"?>
<openerp>
<data>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Demo Data for Memberships -->
<record id="product.membership_0" model="product.product">
@@ -22,5 +21,4 @@
<field name="active" eval="True"/>
</record>
</data>
</openerp>
</odoo>
+69 -81
View File
@@ -1,99 +1,87 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import time
from openerp.osv import fields, osv
from odoo import api, fields, models
class Invoice(osv.osv):
class Invoice(models.Model):
_inherit = 'account.invoice'
def action_cancel(self, cr, uid, ids, context=None):
@api.multi
def action_cancel(self):
'''Create a 'date_cancel' on the membership_line object'''
member_line_obj = self.pool.get('membership.membership_line')
today = time.strftime('%Y-%m-%d')
for invoice in self.browse(cr, uid, ids, context=context):
mlines = member_line_obj.search(cr, uid,
[('account_invoice_line', 'in',
[l.id for l in invoice.invoice_line_ids])])
member_line_obj.write(cr, uid, mlines, {'date_cancel': today})
return super(Invoice, self).action_cancel(cr, uid, ids, context=context)
self.env['membership.membership_line'].search([
('account_invoice_line', 'in', self.mapped('invoice_line_ids').ids)
]).write({'date_cancel': fields.Date.today()})
return super(Invoice, self).action_cancel()
# TODO master: replace by ondelete='cascade'
def unlink(self, cr, uid, ids, context=None):
member_line_obj = self.pool.get('membership.membership_line')
for invoice in self.browse(cr, uid, ids, context=context):
mlines = member_line_obj.search(cr, uid,
[('account_invoice_line', 'in',
[l.id for l in invoice.invoice_line_ids])])
member_line_obj.unlink(cr, uid, mlines, context=context)
return super(Invoice, self).unlink(cr, uid, ids, context=context)
@api.multi
def unlink(self):
self.env['membership.membership_line'].search([
('account_invoice_line', 'in', self.mapped('invoice_line_ids').ids)
]).unlink()
return super(Invoice, self).unlink()
class account_invoice_line(osv.osv):
class AccountInvoiceLine(models.Model):
_inherit = 'account.invoice.line'
def write(self, cr, uid, ids, vals, context=None):
"""Overrides orm write method
"""
member_line_obj = self.pool.get('membership.membership_line')
res = super(account_invoice_line, self).write(cr, uid, ids, vals, context=context)
for line in self.browse(cr, uid, ids, context=context):
if line.invoice_id.type == 'out_invoice':
ml_ids = member_line_obj.search(cr, uid, [('account_invoice_line', '=', line.id)], context=context)
if line.product_id and line.product_id.membership and not ml_ids:
# Product line has changed to a membership product
date_from = line.product_id.membership_date_from
date_to = line.product_id.membership_date_to
if line.invoice_id.date_invoice > date_from and line.invoice_id.date_invoice < date_to:
date_from = line.invoice_id.date_invoice
member_line_obj.create(cr, uid, {
'partner': line.invoice_id.partner_id.id,
'membership_id': line.product_id.id,
'member_price': line.price_unit,
'date': time.strftime('%Y-%m-%d'),
'date_from': date_from,
'date_to': date_to,
'account_invoice_line': line.id,
}, context=context)
if line.product_id and not line.product_id.membership and ml_ids:
# Product line has changed to a non membership product
member_line_obj.unlink(cr, uid, ml_ids, context=context)
return res
# TODO master: replace by ondelete='cascade'
def unlink(self, cr, uid, ids, context=None):
"""Remove Membership Line Record for Account Invoice Line
"""
member_line_obj = self.pool.get('membership.membership_line')
for id in ids:
ml_ids = member_line_obj.search(cr, uid, [('account_invoice_line', '=', id)], context=context)
member_line_obj.unlink(cr, uid, ml_ids, context=context)
return super(account_invoice_line, self).unlink(cr, uid, ids, context=context)
def create(self, cr, uid, vals, context=None):
"""Overrides orm create method
"""
member_line_obj = self.pool.get('membership.membership_line')
result = super(account_invoice_line, self).create(cr, uid, vals, context=context)
line = self.browse(cr, uid, result, context=context)
if line.invoice_id.type == 'out_invoice':
ml_ids = member_line_obj.search(cr, uid, [('account_invoice_line', '=', line.id)], context=context)
if line.product_id and line.product_id.membership and not ml_ids:
# Product line is a membership product
@api.multi
def write(self, vals):
MemberLine = self.env['membership.membership_line']
res = super(AccountInvoiceLine, self).write(vals)
for line in self.filtered(lambda line: line.invoice_id.type == 'out_invoice'):
member_lines = MemberLine.search([('account_invoice_line', '=', line.id)])
if line.product_id.membership and not member_lines:
# Product line has changed to a membership product
date_from = line.product_id.membership_date_from
date_to = line.product_id.membership_date_to
if line.invoice_id.date_invoice > date_from and line.invoice_id.date_invoice < date_to:
date_from = line.invoice_id.date_invoice
values = {
'partner': line.invoice_id.partner_id and line.invoice_id.partner_id.id or False,
'membership_id': line.product_id.id,
'member_price': line.price_unit,
'date': time.strftime('%Y-%m-%d'),
'date_from': date_from,
'date_to': date_to,
'account_invoice_line': line.id,
}
member_line_obj.create(cr, uid, values, context=context)
return result
MemberLine.create({
'partner': line.invoice_id.partner_id.id,
'membership_id': line.product_id.id,
'member_price': line.price_unit,
'date': fields.Date.today(),
'date_from': date_from,
'date_to': date_to,
'account_invoice_line': line.id,
})
if line.product_id and not line.product_id.membership and member_lines:
# Product line has changed to a non membership product
member_lines.unlink()
return res
# TODO master: replace by ondelete='cascade'
@api.multi
def unlink(self):
"""Remove Membership Line Record for Account Invoice Line
"""
self.env['membership.membership_line'].search([
('account_invoice_line', 'in', self.ids)
]).unlink()
return super(AccountInvoiceLine, self).unlink()
@api.model
def create(self, vals):
MemberLine = self.env['membership.membership_line']
invoice_line = super(AccountInvoiceLine, self).create(vals)
if invoice_line.invoice_id.type == 'out_invoice' and \
invoice_line.product_id.membership and \
not MemberLine.search([('account_invoice_line', '=', invoice_line.id)]):
# Product line is a membership product
date_from = invoice_line.product_id.membership_date_from
date_to = invoice_line.product_id.membership_date_to
if invoice_line.invoice_id.date_invoice > date_from and invoice_line.invoice_id.date_invoice < date_to:
date_from = invoice_line.invoice_id.date_invoice
MemberLine.create({
'partner': invoice_line.invoice_id.partner_id.id,
'membership_id': invoice_line.product_id.id,
'member_price': invoice_line.price_unit,
'date': fields.Date.today(),
'date_from': date_from,
'date_to': date_to,
'account_invoice_line': invoice_line.id,
})
return invoice_line
+45 -91
View File
@@ -1,13 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import time
from openerp import tools
from openerp.osv import fields, osv
import openerp.addons.decimal_precision as dp
from openerp.tools.translate import _
from openerp.exceptions import UserError
from odoo import api, fields, models
import odoo.addons.decimal_precision as dp
STATE = [
('none', 'Non Member'),
@@ -20,50 +15,41 @@ STATE = [
]
class membership_line(osv.osv):
class MembershipLine(models.Model):
_name = 'membership.membership_line'
_description = __doc__
_rec_name = 'partner'
_order = 'id desc'
def _get_partners(self, cr, uid, ids, context=None):
list_membership_line = []
member_line_obj = self.pool.get('membership.membership_line')
for partner in self.pool.get('res.partner').browse(cr, uid, ids, context=context):
if partner.member_lines:
list_membership_line += member_line_obj.search(cr, uid, [('id', 'in', [ l.id for l in partner.member_lines])], context=context)
return list_membership_line
partner = fields.Many2one('res.partner', string='Partner', ondelete='cascade', index=True)
membership_id = fields.Many2one('product.product', string="Membership", required=True)
date_from = fields.Date(string='From', readonly=True)
date_to = fields.Date(string='To', readonly=True)
date_cancel = fields.Date(string='Cancel date')
date = fields.Date(string='Join Date',
help="Date on which member has joined the membership")
member_price = fields.Float(string='Membership Fee',
digits_compute=dp.get_precision('Product Price'), required=True,
help='Amount for the membership')
account_invoice_line = fields.Many2one('account.invoice.line', string='Account Invoice line', readonly=True)
account_invoice_id = fields.Many2one('account.invoice', related='account_invoice_line.invoice_id', string='Invoice', readonly=True)
company_id = fields.Many2one('res.company', related='account_invoice_line.invoice_id.company_id', string="Company", readonly=True, store=True)
state = fields.Selection(STATE, compute='_compute_state', string='Membership Status', store=True,
help="It indicates the membership status.\n"
"-Non Member: A member who has not applied for any membership.\n"
"-Cancelled Member: A member who has cancelled his membership.\n"
"-Old Member: A member whose membership date has expired.\n"
"-Waiting Member: A member who has applied for the membership and whose invoice is going to be created.\n"
"-Invoiced Member: A member whose invoice has been created.\n"
"-Paid Member: A member who has paid the membership amount.")
def _get_membership_lines(self, cr, uid, ids, context=None):
list_membership_line = []
member_line_obj = self.pool.get('membership.membership_line')
for invoice in self.pool.get('account.invoice').browse(cr, uid, ids, context=context):
if invoice.invoice_line_ids:
list_membership_line += member_line_obj.search(cr, uid, [('account_invoice_line', 'in', [ l.id for l in invoice.invoice_line_ids])], context=context)
return list_membership_line
def _check_membership_date(self, cr, uid, ids, context=None):
"""Check if membership product is not in the past """
cr.execute('''
SELECT MIN(ml.date_to - ai.date_invoice)
FROM membership_membership_line ml
JOIN account_invoice_line ail ON (
ml.account_invoice_line = ail.id
)
JOIN account_invoice ai ON (
ai.id = ail.invoice_id)
WHERE ml.id IN %s''', (tuple(ids),))
res = cr.fetchall()
for r in res:
if r[0] and r[0] < 0:
return False
return True
def _state(self, cr, uid, ids, name, args, context=None):
@api.depends('account_invoice_line.invoice_id.state',
'account_invoice_line.invoice_id.payment_ids',
'account_invoice_line.invoice_id.payment_ids.invoice_ids.type')
def _compute_state(self):
"""Compute the state lines """
res = {}
inv_obj = self.pool.get('account.invoice')
for line in self.browse(cr, uid, ids, context=context):
cr.execute('''
Invoice = self.env['account.invoice']
for line in self:
self._cr.execute('''
SELECT i.state, i.id FROM
account_invoice i
WHERE
@@ -77,53 +63,21 @@ class membership_line(osv.osv):
)
)
''', (line.id,))
fetched = cr.fetchone()
fetched = self._cr.fetchone()
if not fetched:
res[line.id] = 'canceled'
line.state = 'canceled'
continue
istate = fetched[0]
state = 'none'
if (istate == 'draft') | (istate == 'proforma'):
state = 'waiting'
if istate in ('draft', 'proforma'):
line.state = 'waiting'
elif istate == 'open':
state = 'invoiced'
line.state = 'invoiced'
elif istate == 'paid':
state = 'paid'
inv = inv_obj.browse(cr, uid, fetched[1], context=context)
for payment in inv.payment_ids:
if payment.invoice_ids and any(inv.type == 'out_refund' for inv in payment.invoice_ids):
state = 'canceled'
line.state = 'paid'
invoices = Invoice.browse(fetched[1]).payment_ids.mapped('invoice_ids')
if invoices.filtered(lambda invoice: invoice.type == 'out_refund'):
line.state = 'canceled'
elif istate == 'cancel':
state = 'canceled'
res[line.id] = state
return res
_columns = {
'partner': fields.many2one('res.partner', 'Partner', ondelete='cascade', select=1),
'membership_id': fields.many2one('product.product', string="Membership", required=True),
'date_from': fields.date('From', readonly=True),
'date_to': fields.date('To', readonly=True),
'date_cancel': fields.date('Cancel date'),
'date': fields.date('Join Date', help="Date on which member has joined the membership"),
'member_price': fields.float('Membership Fee', digits_compute= dp.get_precision('Product Price'), required=True, help='Amount for the membership'),
'account_invoice_line': fields.many2one('account.invoice.line', 'Account Invoice line', readonly=True),
'account_invoice_id': fields.related('account_invoice_line', 'invoice_id', type='many2one', relation='account.invoice', string='Invoice', readonly=True),
'state': fields.function(_state,
string='Membership Status', type='selection',
selection=STATE, store = {
'account.invoice': (_get_membership_lines, ['state'], 10),
'res.partner': (_get_partners, ['membership_state'], 12),
}, help="""It indicates the membership status.
-Non Member: A member who has not applied for any membership.
-Cancelled Member: A member who has cancelled his membership.
-Old Member: A member whose membership date has expired.
-Waiting Member: A member who has applied for the membership and whose invoice is going to be created.
-Invoiced Member: A member whose invoice has been created.
-Paid Member: A member who has paid the membership amount."""),
'company_id': fields.related('account_invoice_line', 'invoice_id', 'company_id', type="many2one", relation="res.company", string="Company", readonly=True, store=True)
}
_rec_name = 'partner'
_order = 'id desc'
_constraints = [
(_check_membership_date, 'Error, this membership product is out of date', [])
]
line.state = 'canceled'
else:
line.state = 'none'
+141 -193
View File
@@ -1,253 +1,201 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import time
from openerp import tools
from openerp.osv import fields, osv
from openerp.tools.translate import _
from openerp.exceptions import UserError
from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError
from . import membership
class Partner(osv.osv):
class Partner(models.Model):
_inherit = 'res.partner'
def _get_partner_id(self, cr, uid, ids, context=None):
member_line_obj = self.pool.get('membership.membership_line')
res_obj = self.pool.get('res.partner')
data_inv = member_line_obj.browse(cr, uid, ids, context=context)
list_partner = []
for data in data_inv:
list_partner.append(data.partner.id)
ids2 = list_partner
while ids2:
ids2 = res_obj.search(cr, uid, [('associate_member', 'in', ids2)], context=context)
list_partner += ids2
return list_partner
associate_member = fields.Many2one('res.partner', string='Associate Member',
help="A member with whom you want to associate your membership. "
"It will consider the membership state of the associated member.")
member_lines = fields.One2many('membership.membership_line', 'partner', string='Membership')
free_member = fields.Boolean(string='Free Member',
help="Select if you want to give free membership.")
membership_amount = fields.Float(string='Membership Amount', digits=(16, 2),
help='The price negotiated by the partner')
membership_state = fields.Selection(membership.STATE, compute='_compute_membership_state',
string='Current Membership Status', store=True,
help='It indicates the membership state.\n'
'-Non Member: A partner who has not applied for any membership.\n'
'-Cancelled Member: A member who has cancelled his membership.\n'
'-Old Member: A member whose membership date has expired.\n'
'-Waiting Member: A member who has applied for the membership and whose invoice is going to be created.\n'
'-Invoiced Member: A member whose invoice has been created.\n'
'-Paying member: A member who has paid the membership fee.')
membership_start = fields.Date(compute='_compute_membership_start',
string ='Membership Start Date', store=True,
help="Date from which membership becomes active.")
membership_stop = fields.Date(compute='_compute_membership_stop',
string ='Membership End Date', store=True,
help="Date until which membership remains active.")
membership_cancel = fields.Date(compute='_compute_membership_cancel',
string ='Cancel Membership Date', store=True,
help="Date on which membership has been cancelled")
def _get_invoice_partner(self, cr, uid, ids, context=None):
inv_obj = self.pool.get('account.invoice')
res_obj = self.pool.get('res.partner')
data_inv = inv_obj.browse(cr, uid, ids, context=context)
list_partner = []
for data in data_inv:
list_partner.append(data.partner_id.id)
ids2 = list_partner
while ids2:
ids2 = res_obj.search(cr, uid, [('associate_member', 'in', ids2)], context=context)
list_partner += ids2
return list_partner
@api.depends('member_lines.account_invoice_line.invoice_id.state',
'member_lines.account_invoice_line.invoice_id.invoice_line_ids',
'member_lines.account_invoice_line.invoice_id.payment_ids',
'free_member',
'member_lines.date_to', 'member_lines.date_from',
'associate_member.membership_state')
def _compute_membership_state(self):
values = self._membership_state()
for partner in self:
partner.membership_state = values[partner.id]
def _cron_update_membership(self, cr, uid, context=None):
partner_ids = self.search(cr, uid, [('membership_state', 'in', ['invoiced', 'paid'])], context=context)
if partner_ids:
self._store_set_values(cr, uid, partner_ids, ['membership_state'], context=context)
@api.depends('member_lines.account_invoice_line.invoice_id.state',
'member_lines.account_invoice_line.invoice_id.invoice_line_ids',
'member_lines.account_invoice_line.invoice_id.payment_ids',
'free_member',
'member_lines.date_to', 'member_lines.date_from',
'membership_state',
'associate_member.membership_state')
def _compute_membership_start(self):
"""Return date of membership"""
for partner in self:
partner.membership_start = self.env['membership.membership_line'].search([
('partner', '=', partner.associate_member.id or partner.id), ('date_cancel','=',False)
], limit=1, order='date_from').date_from
def _membership_state(self, cr, uid, ids, name, args, context=None):
@api.depends('member_lines.account_invoice_line.invoice_id.state',
'member_lines.account_invoice_line.invoice_id.invoice_line_ids',
'member_lines.account_invoice_line.invoice_id.payment_ids',
'free_member',
'member_lines.date_to', 'member_lines.date_from',
'membership_state',
'associate_member.membership_state')
def _compute_membership_stop(self):
MemberLine = self.env['membership.membership_line']
for partner in self:
partner.membership_stop = self.env['membership.membership_line'].search([
('partner', '=', partner.associate_member.id or partner.id),('date_cancel','=',False)
], limit=1, order='date_to desc').date_to
@api.depends('member_lines.account_invoice_line.invoice_id.state',
'member_lines.account_invoice_line.invoice_id.invoice_line_ids',
'member_lines.account_invoice_line.invoice_id.payment_ids',
'free_member',
'member_lines.date_to', 'member_lines.date_from',
'membership_state',
'associate_member.membership_state')
def _compute_membership_cancel(self):
for partner in self:
if partner.membership_state == 'canceled':
partner.membership_cancel = self.env['membership.membership_line'].search([
('partner', '=', partner.id)
], limit=1, order='date_cancel').date_cancel
else:
partner.membership_cancel = False
def _membership_state(self):
"""This Function return Membership State For Given Partner. """
res = {}
for id in ids:
res[id] = 'none'
today = time.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
for id in ids:
partner_data = self.browse(cr, uid, id, context=context)
if partner_data.membership_cancel and today > partner_data.membership_cancel:
res[id] = 'free' if partner_data.free_member else 'canceled'
today = fields.Date.today()
for partner in self:
res[partner.id] = 'none'
if partner.membership_cancel and today > partner.membership_cancel:
res[partner.id] = 'free' if partner.free_member else 'canceled'
continue
if partner_data.membership_stop and today > partner_data.membership_stop:
res[id] = 'free' if partner_data.free_member else 'old'
if partner.membership_stop and today > partner.membership_stop:
res[partner.id] = 'free' if partner.free_member else 'old'
continue
s = 4
if partner_data.member_lines:
for mline in partner_data.member_lines:
if partner.member_lines:
for mline in partner.member_lines:
if mline.date_to >= today and mline.date_from <= today:
if mline.account_invoice_line and mline.account_invoice_line.invoice_id:
if mline.account_invoice_line.invoice_id:
mstate = mline.account_invoice_line.invoice_id.state
if mstate == 'paid':
s = 0
inv = mline.account_invoice_line.invoice_id
for payment in inv.payment_ids:
if payment.invoice_ids and any(inv.type == 'out_refund' for inv in payment.invoice_ids):
if any(payment.invoice_ids.filtered(lambda inv: inv.type == 'out_refund')):
s = 2
break
elif mstate == 'open' and s!=0:
elif mstate == 'open' and s != 0:
s = 1
elif mstate == 'cancel' and s!=0 and s!=1:
elif mstate == 'cancel' and s != 0 and s != 1:
s = 2
elif (mstate == 'draft' or mstate == 'proforma') and s!=0 and s!=1:
elif (mstate == 'draft' or mstate == 'proforma') and s != 0 and s != 1:
s = 3
if s==4:
for mline in partner_data.member_lines:
if s == 4:
for mline in partner.member_lines:
if mline.date_from < today and mline.date_to < today and mline.date_from <= mline.date_to and mline.account_invoice_line and mline.account_invoice_line.invoice_id.state == 'paid':
s = 5
else:
s = 6
if s==0:
res[id] = 'paid'
elif s==1:
res[id] = 'invoiced'
elif s==2:
res[id] = 'canceled'
elif s==3:
res[id] = 'waiting'
elif s==5:
res[id] = 'old'
elif s==6:
res[id] = 'none'
if partner_data.free_member and s!=0:
res[id] = 'free'
if partner_data.associate_member:
res_state = self._membership_state(cr, uid, [partner_data.associate_member.id], name, args, context=context)
res[id] = res_state[partner_data.associate_member.id]
return res
def _membership_date(self, cr, uid, ids, name, args, context=None):
"""Return date of membership"""
res = {}
member_line_obj = self.pool.get('membership.membership_line')
for partner in self.browse(cr, uid, ids, context=context):
if s == 0:
res[partner.id] = 'paid'
elif s == 1:
res[partner.id] = 'invoiced'
elif s == 2:
res[partner.id] = 'canceled'
elif s == 3:
res[partner.id] = 'waiting'
elif s == 5:
res[partner.id] = 'old'
elif s == 6:
res[partner.id] = 'none'
if partner.free_member and s != 0:
res[partner.id] = 'free'
if partner.associate_member:
partner_id = partner.associate_member.id
else:
partner_id = partner.id
res[partner.id] = dict()
if 'membership_start' in name:
res[partner.id]['membership_start'] = False
line_id = member_line_obj.search(cr, uid, [('partner', '=', partner_id),('date_cancel','=',False)],
limit=1, order='date_from', context=context)
if line_id:
res[partner.id]['membership_start'] = member_line_obj.read(cr, uid, [line_id[0]],
['date_from'], context=context)[0]['date_from']
if 'membership_stop' in name:
res[partner.id]['membership_stop'] = False
line_id1 = member_line_obj.search(cr, uid, [('partner', '=', partner_id),('date_cancel','=',False)],
limit=1, order='date_to desc', context=context)
if line_id1:
res[partner.id]['membership_stop'] = member_line_obj.read(cr, uid, [line_id1[0]],
['date_to'], context=context)[0]['date_to']
if 'membership_cancel' in name:
res[partner.id]['membership_cancel'] = False
if partner.membership_state == 'canceled':
line_id2 = member_line_obj.search(cr, uid, [('partner', '=', partner.id)], limit=1, order='date_cancel', context=context)
if line_id2:
res[partner.id]['membership_cancel'] = member_line_obj.read(cr, uid, [line_id2[0]], ['date_cancel'], context=context)[0]['date_cancel']
res_state = partner.associate_member._membership_state()
res[partner.id] = res_state[partner.associate_member.id]
return res
def _get_partners(self, cr, uid, ids, context=None):
ids2 = ids
while ids2:
ids2 = self.search(cr, uid, [('associate_member', 'in', ids2)], context=context)
ids += ids2
return ids
def __get_membership_state(self, *args, **kwargs):
return self._membership_state(*args, **kwargs)
_columns = {
'associate_member': fields.many2one('res.partner', 'Associate Member',help="A member with whom you want to associate your membership.It will consider the membership state of the associated member."),
'member_lines': fields.one2many('membership.membership_line', 'partner', 'Membership'),
'free_member': fields.boolean('Free Member', help = "Select if you want to give free membership."),
'membership_amount': fields.float(
'Membership Amount', digits=(16, 2),
help = 'The price negotiated by the partner'),
'membership_state': fields.function(
__get_membership_state,
string = 'Current Membership Status', type = 'selection',
selection = membership.STATE,
store = {
'account.invoice': (_get_invoice_partner, ['state', 'invoice_line_ids', 'payment_ids'], 20),
'membership.membership_line': (_get_partner_id, ['state'], 20),
'res.partner': (_get_partners, ['free_member', 'membership_state', 'associate_member'], 20)
}, help='It indicates the membership state.\n'
'-Non Member: A partner who has not applied for any membership.\n'
'-Cancelled Member: A member who has cancelled his membership.\n'
'-Old Member: A member whose membership date has expired.\n'
'-Waiting Member: A member who has applied for the membership and whose invoice is going to be created.\n'
'-Invoiced Member: A member whose invoice has been created.\n'
'-Paying member: A member who has paid the membership fee.'),
'membership_start': fields.function(
_membership_date,
string = 'Membership Start Date', type = 'date', multi='_membership_date',
store = {
'account.invoice': (_get_invoice_partner, ['state', 'invoice_line_ids', 'payment_ids'], 10),
'membership.membership_line': (_get_partner_id, ['state'], 10, ),
'res.partner': (_get_partners, ['free_member', 'membership_state', 'associate_member'], 10)
}, help="Date from which membership becomes active."),
'membership_stop': fields.function(
_membership_date,
string = 'Membership End Date', type='date', multi='_membership_date',
store = {
'account.invoice': (_get_invoice_partner, ['state', 'invoice_line_ids', 'payment_ids'], 10),
'membership.membership_line': (_get_partner_id, ['state'], 10),
'res.partner': (_get_partners, ['free_member', 'membership_state', 'associate_member'], 10)
}, help="Date until which membership remains active."),
'membership_cancel': fields.function(
_membership_date,
string = 'Cancel Membership Date', type='date', multi='_membership_date',
store = {
'account.invoice': (_get_invoice_partner, ['state', 'invoice_line_ids', 'payment_ids'], 11),
'membership.membership_line': (_get_partner_id, ['state'], 10),
'res.partner': (_get_partners, ['free_member', 'membership_state', 'associate_member'], 10)
}, help="Date on which membership has been cancelled"),
}
def _check_recursion(self, cr, uid, ids, context=None):
"""Check Recursive for Associated Members.
"""
@api.one
@api.constrains('associate_member')
def _check_recursion(self):
level = 100
while len(ids):
cr.execute('SELECT DISTINCT associate_member FROM res_partner WHERE id IN %s', (tuple(ids),))
ids = filter(None, map(lambda x:x[0], cr.fetchall()))
while self:
self = self.associate_member
if not level:
return False
raise ValidationError(_('Error ! You cannot create recursive associated members.'))
level -= 1
return True
_constraints = [
(_check_recursion, 'Error ! You cannot create recursive associated members.', ['associate_member'])
]
@api.model
def _cron_update_membership(self):
partners = self.search([('membership_state', 'in', ['invoiced', 'paid'])])
if partners:
partners._store_set_values(['membership_state'])
def create_membership_invoice(self, cr, uid, ids, product_id=None, datas=None, context=None):
@api.multi
def create_membership_invoice(self, product_id=None, datas=None):
""" Create Customer Invoice of Membership for partners.
@param datas: datas has dictionary value which consist Id of Membership product and Cost Amount of Membership.
datas = {'membership_product_id': None, 'amount': None}
"""
invoice_obj = self.pool.get('account.invoice')
invoice_line_obj = self.pool.get('account.invoice.line')
product_id = product_id or datas.get('membership_product_id', False)
product_id = product_id or datas.get('membership_product_id')
amount = datas.get('amount', 0.0)
invoice_list = []
if type(ids) in (int, long,):
ids = [ids]
for partner in self.browse(cr, uid, ids, context=context):
account_id = partner.property_account_receivable_id and partner.property_account_receivable_id.id or False
fpos_id = partner.property_account_position_id and partner.property_account_position_id.id or False
addr = self.address_get(cr, uid, [partner.id], ['invoice'])
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_values = {
invoice = self.env['account.invoice'].create({
'partner_id': partner.id,
'account_id': account_id,
'fiscal_position_id': fpos_id or False
}
invoice_id = invoice_obj.create(cr, uid, invoice_values, context=context)
'account_id': partner.property_account_receivable_id.id,
'fiscal_position_id': partner.property_account_position_id.id
})
line_values = {
'product_id': product_id,
'price_unit': amount,
'invoice_id': invoice_id,
'invoice_id': invoice.id,
}
# create a record in cache, apply onchange then revert back to a dictionnary
invoice_line = invoice_line_obj.new(cr, uid, line_values, context=context)
invoice_line = self.env['account.invoice.line'].new(line_values)
invoice_line._onchange_product_id()
line_values = invoice_line._convert_to_write({name: invoice_line[name] for name in invoice_line._cache})
line_values['price_unit'] = amount
invoice_obj.write(cr, uid, [invoice_id], {'invoice_line_ids': [(0, 0, line_values)]}, context=context)
invoice_list.append(invoice_id)
invoice_obj.compute_taxes(cr, uid, [invoice_id])
invoice.write({'invoice_line_ids': [(0, 0, line_values)]})
invoice_list.append(invoice.id)
invoice.compute_taxes()
return invoice_list
+17 -20
View File
@@ -1,30 +1,27 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.osv import fields, osv
from odoo import api, fields, models
class Product(osv.osv):
class Product(models.Model):
_inherit = 'product.template'
def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
ModelData = self.pool['ir.model.data']
if context is None:
context = {}
membership = fields.Boolean(help='Check if the product is eligible for membership.')
membership_date_from = fields.Date(string='Membership Start Date',
help='Date from which membership becomes active.')
membership_date_to = fields.Date(string='Membership End Date',
help='Date until which membership remains active.')
if ('product' in context) and (context['product']=='membership_product'):
_sql_constraints = [
('membership_date_greater', 'check(membership_date_to >= membership_date_from)', 'Error ! Ending Date cannot be set before Beginning Date.')
]
@api.model
def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
if self._context.get('product') == 'membership_product':
if view_type == 'form':
view_id = ModelData.xmlid_to_res_id(
cr, user, 'membership.membership_products_form', context=context)
view_id = self.env.ref('membership.membership_products_form').id
else:
view_id = ModelData.xmlid_to_res_id(
cr, user, 'membership.membership_products_tree', context=context)
return super(Product,self).fields_view_get(cr, user, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
_columns = {
'membership': fields.boolean('Membership', help='Check if the product is eligible for membership.'),
'membership_date_from': fields.date('Membership Start Date', help='Date from which membership becomes active.'),
'membership_date_to': fields.date('Membership End Date', help='Date until which membership remains active.'),
}
_sql_constraints = [('membership_date_greater','check(membership_date_to >= membership_date_from)','Error ! Ending Date cannot be set before Beginning Date.')]
view_id = self.env.ref('membership.membership_products_tree').id
return super(Product, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu)
+24 -25
View File
@@ -1,9 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.osv import fields, osv
from openerp import tools
import openerp.addons.decimal_precision as dp
from odoo import api, fields, models, tools
STATE = [
('none', 'Non Member'),
@@ -15,35 +13,36 @@ STATE = [
('paid', 'Paid Member'),
]
class report_membership(osv.osv):
class ReportMembership(models.Model):
'''Membership Analysis'''
_name = 'report.membership'
_description = __doc__
_auto = False
_rec_name = 'start_date'
_columns = {
'start_date': fields.date('Start Date', readonly=True),
'date_to': fields.date('End Date', readonly=True, help="End membership date"),
'num_waiting': fields.integer('# Waiting', readonly=True),
'num_invoiced': fields.integer('# Invoiced', readonly=True),
'num_paid': fields.integer('# Paid', readonly=True),
'tot_pending': fields.float('Pending Amount', digits=0, readonly=True),
'tot_earned': fields.float('Earned Amount', digits=0, readonly=True),
'partner_id': fields.many2one('res.partner', 'Member', readonly=True),
'associate_member_id': fields.many2one('res.partner', 'Associate Member', readonly=True),
'membership_id': fields.many2one('product.product', 'Membership Product', readonly=True),
'membership_state': fields.selection(STATE, 'Current Membership State', readonly=True),
'user_id': fields.many2one('res.users', 'Salesperson', readonly=True),
'company_id': fields.many2one('res.company', 'Company', readonly=True),
'quantity': fields.integer("Quantity", readonly=True),
}
def init(self, cr):
start_date = fields.Date(string='Start Date', readonly=True)
date_to = fields.Date(string='End Date', readonly=True, help="End membership date")
num_waiting = fields.Integer(string='# Waiting', readonly=True)
num_invoiced = fields.Integer(string='# Invoiced', readonly=True)
num_paid = fields.Integer(string='# Paid', readonly=True)
tot_pending = fields.Float(string='Pending Amount', digits=0, readonly=True)
tot_earned = fields.Float(string='Earned Amount', digits=0, readonly=True)
partner_id = fields.Many2one('res.partner', string='Member', readonly=True)
associate_member_id = fields.Many2one('res.partner', string='Associate Member', readonly=True)
membership_id = fields.Many2one('product.product', string='Membership Product', readonly=True)
membership_state = fields.Selection(STATE, string='Current Membership State', readonly=True)
user_id = fields.Many2one('res.users', string='Salesperson', readonly=True)
company_id = fields.Many2one('res.company', string='Company', readonly=True)
quantity = fields.Integer(readonly=True)
@api.model_cr
def init(self):
'''Create the view'''
tools.drop_view_if_exists(cr, 'report_membership')
cr.execute("""
CREATE OR REPLACE VIEW report_membership AS (
tools.drop_view_if_exists(self._cr, self._table)
self._cr.execute("""
CREATE OR REPLACE VIEW %s AS (
SELECT
MIN(id) AS id,
partner_id,
@@ -105,4 +104,4 @@ class report_membership(osv.osv):
membership_state,
associate_member_id,
membership_amount
)""")
)""" % (self._table,))
@@ -1,6 +1,5 @@
<?xml version="1.0"?>
<openerp>
<data>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- REPORTING/MEMBERSHIP BY YEAR -->
<record model="ir.ui.view" id="view_report_membership_search">
<field name="name">report.membership.search</field>
@@ -72,5 +71,4 @@
id="menu_report_membership"
groups="base.group_partner_manager"/>
</data>
</openerp>
</odoo>
-130
View File
@@ -1,130 +0,0 @@
- |
Test Datas
-
!record {model: res.partner, id: test_partner_0}:
name: Seagate
free_member: False
- |
Test Datas
-
!record {model: res.partner, id: test_partner_1}:
name: Ms. Jonhson
free_member: True
- |
Test Datas
-
!record {model: res.partner, id: test_partner_2}:
name: Ms. Smith
free_member: False
- |
In Order to test the Membership in OpenERP, which allows us to manage all operations for managing memberships.
- |
I'm creating "Gold Membership" which has Membership fee 80 EURO and It's started from 1st June to 31st Dec.
-
!record {model: product.product, id: test_membership_0}:
categ_id: product.product_category_1
membership: 1
membership_date_from: !eval datetime.today().strftime("%Y-%m-%d")
membership_date_to: !eval "(datetime.now() + timedelta(6*31)).strftime('%Y-%m-%d')"
name: Gold Membership
type: service
list_price: 80.00
- |
"Seagate" want to join "Gold Membership", so I'm checking "Current Membership State" of "Seagate". It is an "Non Member" or not.
-
!assert {model: res.partner, id: test_partner_0, string: Member should be has Current Membership State in Non Member}:
- membership_state == 'none'
- |
I'm doing to make membership invoice for "Seagate" on joining "Gold Membership".
-
!python {model: res.partner}: |
self.create_membership_invoice(cr, uid, [ref("test_partner_0")], product_id=ref("test_membership_0"), datas={"amount":80.00})
- |
I'm checking "Current Membership State" of "Seagate". It is an "Waiting Member" or not.
-
!assert {model: res.partner, id: test_partner_0, string: Member should be has Current Membership State in Waiting Member}:
- membership_state == 'waiting'
- |
I'm Opening that Invoice which is created for "Seagate".
-
!python {model: res.partner}: |
invoice_pool = self.pool.get('account.invoice')
partner_pool = self.pool.get('res.partner')
membership_line_pool = self.pool.get('membership.membership_line')
membership_pool = self.pool.get('product.product')
membership_line_ids = membership_line_pool.search(cr, uid, [('membership_id','=',ref('test_membership_0')),('partner','=',ref('test_partner_0'))])
membership_lines = membership_line_pool.browse(cr, uid, membership_line_ids)
assert membership_lines, 'Membership is not registrated.'
membership_line = membership_lines[0]
membership_line.account_invoice_id.signal_workflow('invoice_open')
- |
I'm checking "Current membership state" of "Seagate". It is an "Invoiced Member" or not.
-
!assert {model: res.partner, id: test_partner_0, string: Member should be has Current Membership State in Invoiced Member}:
- membership_state == 'invoiced'
- |
I'm checking "Current membership state" of "Ms. Johnson". It is an "Free Member" or not.
-
!assert {model: res.partner, id: test_partner_1, string: Member should be has Current Membership State in Free Member}:
- membership_state == 'free'
- |
I'm set "Seagate" as a associated member of "Ms. Johnson" and also set Non free member.
-
!python {model: res.partner}: |
self.write(cr, uid, [ref("test_partner_1")], {'free_member': False, 'associate_member': ref("test_partner_0")})
- |
I'm checking "Current membership state" of "Ms. Johnson". It is an "Paid Member" or not.
-
!assert {model: res.partner, id: test_partner_1, string: Member should be has Current Membership State in Invoiced Member}:
- membership_state == 'invoiced'
- |
I'm creating new "Regular Membership" which has Membership fee 50 EURO and It's started from 1st June to 31st Dec.
-
!record {model: product.product, id: product_product_membershipproduct1}:
categ_id: product.product_category_1
membership: 1
membership_date_from: '2013-01-01'
membership_date_to: '2013-12-31'
name: Regular Membership
type: service
list_price: 50.00
- |
I'm making invoice of "Smith" member on joining new membership "Regular Membership".
-
!python {model: res.partner}: |
self.create_membership_invoice(cr, uid, [ref("test_partner_2")], product_id=ref("product_product_membershipproduct1"), datas={"amount":50.00})
- |
I'm checking "Current membership state" of "Smith". It is an "Old Member" or not.
-
!assert {model: res.partner, id: test_partner_2, string: Member should be has Current Membership State in Old Member}:
- membership_state == 'old'
- |
I'm doing to make credit note of invoice which is paid by "Seagate" to cancel membership.
-
!python {model: account.invoice}: |
invoice_pool = self.pool.get('account.invoice')
partner_pool = self.pool.get('res.partner')
membership_line_pool = self.pool.get('membership.membership_line')
membership_pool = self.pool.get('product.product')
invoice_refund_pool = self.pool.get('account.invoice.refund')
membership_line_ids = membership_line_pool.search(cr, uid, [('membership_id','=',ref('test_membership_0')),('partner','=',ref('test_partner_0'))])
membership_lines = membership_line_pool.browse(cr, uid, membership_line_ids)
assert membership_lines, 'Membership is not registrated.'
membership_line = membership_lines[0]
refund_id = invoice_refund_pool.create(cr, uid, {'description': 'Refund of Membership', 'filter_refund': 'refund'}, {'active_id': membership_line.account_invoice_id.id})
invoice_refund_pool.invoice_refund(cr, uid, [refund_id], {'active_id': membership_line.account_invoice_id.id, 'active_ids': [membership_line.account_invoice_id.id]})
membership_line.account_invoice_id.journal_id.write({'update_posted': True})
membership_line.account_invoice_id.signal_workflow('invoice_cancel')
- |
I'm checking "Current membership state" of "Seagate". It is an "Cancelled Member" or not.
-
!assert {model: res.partner, id: test_partner_0, string: Member should be has Current Membership State in Cancelled Member}:
- membership_state == 'canceled'
+1 -1
View File
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
from openerp.addons.membership.tests import test_membership
from odoo.addons.membership.tests import test_membership
+2 -3
View File
@@ -3,7 +3,7 @@
import datetime
from dateutil.relativedelta import relativedelta
from openerp.addons.account.tests.account_test_classes import AccountingTestCase
from odoo.addons.account.tests.account_test_classes import AccountingTestCase
class TestMembershipCommon(AccountingTestCase):
@@ -12,11 +12,10 @@ class TestMembershipCommon(AccountingTestCase):
super(TestMembershipCommon, self).setUp()
# Usefull models
Product = self.env['product.product']
Partner = self.env['res.partner']
# Test memberships
self.membership_1 = Product.create({
self.membership_1 = self.env['product.product'].create({
'membership': True,
'membership_date_from': datetime.date.today() + relativedelta(days=-2),
'membership_date_to': datetime.date.today() + relativedelta(months=1),
+3 -3
View File
@@ -3,7 +3,7 @@
import datetime
from dateutil.relativedelta import relativedelta
from openerp.addons.membership.tests.common import TestMembershipCommon
from odoo.addons.membership.tests.common import TestMembershipCommon
class TestMembership(TestMembershipCommon):
@@ -28,7 +28,7 @@ class TestMembership(TestMembershipCommon):
self.partner_1.create_membership_invoice(product_id=self.membership_1.id, datas={'amount': 75.0})
# checks for invoices
invoice = self.env['account.invoice'].search([('partner_id', '=', self.partner_1.id)], limit=1)[0]
invoice = self.env['account.invoice'].search([('partner_id', '=', self.partner_1.id)], limit=1)
self.assertEqual(
invoice.state, 'draft',
'membership: new subscription should create a draft invoice')
@@ -74,7 +74,7 @@ class TestMembership(TestMembershipCommon):
self.partner_1.create_membership_invoice(product_id=self.membership_1.id, datas={'amount': 75.0})
# checks for invoices
invoice = self.env['account.invoice'].search([('partner_id', '=', self.partner_1.id)], limit=1)[0]
invoice = self.env['account.invoice'].search([('partner_id', '=', self.partner_1.id)], limit=1)
# the invoice is canceled -> membership state of the customer goes to canceled
invoice.signal_workflow('invoice_cancel')
+2 -4
View File
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<!-- MEMBERSHIP/CURRENT MEMBERS -->
<record model="ir.ui.view" id="membership_members_tree">
@@ -147,5 +146,4 @@
</notebook>
</field>
</record>
</data>
</openerp>
</odoo>
+3 -5
View File
@@ -1,6 +1,5 @@
<?xml version="1.0"?>
<openerp>
<data>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- MEMBERSHIP -->
@@ -106,5 +105,4 @@
<menuitem name="Configuration" id="base.menu_marketing_config_association"
parent="base.menu_association" sequence="100" action="action_membership_products"/>
</data>
</openerp>
</odoo>
+21 -36
View File
@@ -1,56 +1,41 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.osv import fields, osv
import openerp.addons.decimal_precision as dp
from odoo import api, fields, models
import odoo.addons.decimal_precision as dp
class membership_invoice(osv.osv_memory):
"""Membership Invoice"""
class MembershipInvoice(models.TransientModel):
_name = "membership.invoice"
_description = "Membership Invoice"
_columns = {
'product_id': fields.many2one('product.product','Membership', required=True),
'member_price': fields.float('Member Price', digits_compute= dp.get_precision('Product Price'), required=True),
}
def onchange_product(self, cr, uid, ids, product_id=False):
product_id = fields.Many2one('product.product', string='Membership', required=True)
member_price = fields.Float(string='Member Price', digits_compute= dp.get_precision('Product Price'), required=True)
@api.onchange('product_id')
def onchange_product(self):
"""This function returns value of product's member price based on product id.
"""
if not product_id:
return {'value': {'member_price': False}}
return {'value': {'member_price': self.pool.get('product.product').price_compute(cr, uid, [product_id], 'list_price')[product_id]}}
price_dict = self.product_id.price_compute('list_price')
self.member_price = price_dict.get(self.product_id.id) or False
def membership_invoice(self, cr, uid, ids, context=None):
mod_obj = self.pool.get('ir.model.data')
partner_obj = self.pool.get('res.partner')
datas = {}
if context is None:
context = {}
data = self.browse(cr, uid, ids, context=context)
if data:
data = data[0]
@api.multi
def membership_invoice(self):
if self:
datas = {
'membership_product_id': data.product_id.id,
'amount': data.member_price
'membership_product_id': self.product_id.id,
'amount': self.member_price
}
invoice_list = partner_obj.create_membership_invoice(cr, uid, context.get('active_ids', []), datas=datas, context=context)
invoice_list = self.env['res.partner'].browse(self._context.get('active_ids')).create_membership_invoice(datas=datas)
try:
search_view_id = mod_obj.get_object_reference(cr, uid, 'account', 'view_account_invoice_filter')[1]
except ValueError:
search_view_id = False
try:
form_view_id = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_form')[1]
except ValueError:
form_view_id = False
search_view_ref = self.env.ref('account.view_account_invoice_filter', False)
form_view_ref = self.env.ref('account.invoice_form', False)
return {
'domain': [('id', 'in', invoice_list)],
'name': 'Membership Invoices',
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'account.invoice',
'type': 'ir.actions.act_window',
'views': [(False, 'tree'), (form_view_id, 'form')],
'search_view_id': search_view_id,
'views': [(False, 'tree'), (form_view_ref and form_view_ref.id, 'form')],
'search_view_id': search_view_ref and search_view_ref.id,
}
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<record id="view_membership_invoice_view" model="ir.ui.view">
<field name="name">membership.invoice.view.form</field>
@@ -8,7 +7,7 @@
<field name="arch" type="xml">
<form string="Membership Invoice">
<group>
<field name="product_id" domain="[('membership','=',True)]" widget="selection" on_change="onchange_product(product_id)"/>
<field name="product_id" domain="[('membership','=',True)]" widget="selection"/>
<field name="member_price"/>
</group>
<footer>
@@ -36,5 +35,4 @@
view_mode="form"
id="action_membership_invoice_view"/>
</data>
</openerp>
</odoo>