1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
22 from osv import fields, osv
23 from tools import config
26 import decimal_precision as dp
30 ('none', 'Non Member'),
31 ('canceled', 'Cancelled Member'),
32 ('old', 'Old Member'),
33 ('waiting', 'Waiting Member'),
34 ('invoiced', 'Invoiced Member'),
35 ('free', 'Free Member'),
36 ('paid', 'Paid Member'),
49 #~ REQUETE = '''SELECT partner, state FROM (
50 #~ SELECT members.partner AS partner,
51 #~ CASE WHEN MAX(members.state) = 0 THEN 'none'
52 #~ ELSE CASE WHEN MAX(members.state) = 1 THEN 'canceled'
53 #~ ELSE CASE WHEN MAX(members.state) = 2 THEN 'old'
54 #~ ELSE CASE WHEN MAX(members.state) = 3 THEN 'waiting'
55 #~ ELSE CASE WHEN MAX(members.state) = 4 THEN 'invoiced'
56 #~ ELSE CASE WHEN MAX(members.state) = 6 THEN 'free'
57 #~ ELSE CASE WHEN MAX(members.state) = 7 THEN 'paid'
58 #~ END END END END END END END END
61 #~ CASE WHEN MAX(inv_digit.state) = 4 THEN 7
62 #~ ELSE CASE WHEN MAX(inv_digit.state) = 3 THEN 4
63 #~ ELSE CASE WHEN MAX(inv_digit.state) = 2 THEN 3
64 #~ ELSE CASE WHEN MAX(inv_digit.state) = 1 THEN 1
68 #~ SELECT p.id as partner,
69 #~ CASE WHEN ai.state = 'paid' THEN 4
70 #~ ELSE CASE WHEN ai.state = 'open' THEN 3
71 #~ ELSE CASE WHEN ai.state = 'proforma' THEN 2
72 #~ ELSE CASE WHEN ai.state = 'draft' THEN 2
73 #~ ELSE CASE WHEN ai.state = 'cancel' THEN 1
74 #~ END END END END END
77 #~ JOIN account_invoice ai ON (
78 #~ p.id = ai.partner_id
80 #~ JOIN account_invoice_line ail ON (
81 #~ ail.invoice_id = ai.id
83 #~ JOIN membership_membership_line ml ON (
84 #~ ml.account_invoice_line = ail.id
86 #~ WHERE ml.date_from <= '%s'
87 #~ AND ml.date_to >= '%s'
95 #~ SELECT p.id AS partner,
96 #~ CASE WHEN p.free_member THEN 6
97 #~ ELSE CASE WHEN p.associate_member IN (
98 #~ SELECT ai.partner_id FROM account_invoice ai JOIN
99 #~ account_invoice_line ail ON (ail.invoice_id = ai.id AND ai.state = 'paid')
100 #~ JOIN membership_membership_line ml ON (ml.account_invoice_line = ail.id)
101 #~ WHERE ml.date_from <= '%s'
102 #~ AND ml.date_to >= '%s'
107 #~ FROM res_partner p
108 #~ WHERE p.free_member
109 #~ OR p.associate_member > 0
111 #~ SELECT p.id as partner,
112 #~ MAX(CASE WHEN ai.state = 'paid' THEN 2
116 #~ FROM res_partner p
117 #~ JOIN account_invoice ai ON (
118 #~ p.id = ai.partner_id
120 #~ JOIN account_invoice_line ail ON (
121 #~ ail.invoice_id = ai.id
123 #~ JOIN membership_membership_line ml ON (
124 #~ ml.account_invoice_line = ail.id
126 #~ WHERE ml.date_from < '%s'
127 #~ AND ml.date_to < '%s'
128 #~ AND ml.date_from <= ml.date_to
133 #~ GROUP BY members.partner
139 class membership_line(osv.osv):
142 def _check_membership_date(self, cr, uid, ids, context=None):
143 """Check if membership product is not in the past
144 @param self: The object pointer
145 @param cr: the current row, from the database cursor,
146 @param uid: the current user’s ID for security checks,
147 @param ids: List of Membership Line IDs
148 @param context: A standard dictionary for contextual values
152 SELECT MIN(ml.date_to - ai.date_invoice)
153 FROM membership_membership_line ml
154 JOIN account_invoice_line ail ON (
155 ml.account_invoice_line = ail.id
157 JOIN account_invoice ai ON (
158 ai.id = ail.invoice_id)
159 WHERE ml.id IN %s''',(tuple(ids),))
162 if r[0] and r[0] < 0:
166 def _state(self, cr, uid, ids, name, args, context=None):
167 """Compute the state lines
168 @param self: The object pointer
169 @param cr: the current row, from the database cursor,
170 @param uid: the current user’s ID for security checks,
171 @param ids: List of Membership Line IDs
172 @param name: Field Name
173 @param context: A standard dictionary for contextual values
174 @param return: Dictionary of state Value
177 for line in self.browse(cr, uid, ids):
179 SELECT i.state, i.id FROM
183 SELECT l.invoice_id FROM
184 account_invoice_line l WHERE
186 SELECT ml.account_invoice_line FROM
187 membership_membership_line ml WHERE
192 fetched = cr.fetchone()
194 res[line.id] = 'canceled'
198 if (istate == 'draft') | (istate == 'proforma'):
200 elif istate == 'open':
202 elif istate == 'paid':
204 inv = self.pool.get('account.invoice').browse(cr, uid, fetched[1])
205 for payment in inv.payment_ids:
206 if payment.invoice and payment.invoice.type == 'out_refund':
208 elif istate == 'cancel':
214 _description = __doc__
215 _name = 'membership.membership_line'
217 'partner': fields.many2one('res.partner', 'Partner', ondelete='cascade', select=1),
218 'date_from': fields.date('From', readonly=True),
219 'date_to': fields.date('To', readonly=True),
220 'date_cancel' : fields.date('Cancel date'),
221 'account_invoice_line': fields.many2one('account.invoice.line', 'Account Invoice line', readonly=True),
222 'state': fields.function(_state, method=True, string='State', type='selection', selection=STATE),
224 _rec_name = 'partner'
227 (_check_membership_date, 'Error, this membership product is out of date', [])
233 class Partner(osv.osv):
235 _inherit = 'res.partner'
237 def _get_partner_id(self, cr, uid, ids, context=None):
238 member_line_obj = self.pool.get('membership.membership_line')
239 res_obj = self.pool.get('res.partner')
240 data_inv = member_line_obj.browse(cr, uid, ids, context)
242 for data in data_inv:
243 list_partner.append(data.partner.id)
246 ids2 = res_obj.search(cr, uid, [('associate_member','in',ids2)], context=context)
250 def _get_invoice_partner(self, cr, uid, ids, context=None):
251 inv_obj = self.pool.get('account.invoice')
252 res_obj = self.pool.get('res.partner')
253 data_inv = inv_obj.browse(cr, uid, ids, context)
255 for data in data_inv:
256 list_partner.append(data.partner_id.id)
259 ids2 = res_obj.search(cr, uid, [('associate_member','in',ids2)], context=context)
263 def _membership_state(self, cr, uid, ids, name, args, context=None):
264 """This Function return Membership State For Given Partner.
265 @param self: The object pointer
266 @param cr: the current row, from the database cursor,
267 @param uid: the current user’s ID for security checks,
268 @param ids: List of Partner IDs
269 @param name: Field Name
270 @param context: A standard dictionary for contextual values
271 @param return: Dictionary of Membership state Value
276 today = time.strftime('%Y-%m-%d')
278 partner_data = self.browse(cr, uid, id)
279 if partner_data.membership_cancel and today > partner_data.membership_cancel:
282 if partner_data.membership_stop and today > partner_data.membership_stop:
286 if partner_data.member_lines:
287 for mline in partner_data.member_lines:
288 if mline.date_to >= today:
289 if mline.account_invoice_line and mline.account_invoice_line.invoice_id:
290 mstate = mline.account_invoice_line.invoice_id.state
293 inv = mline.account_invoice_line.invoice_id
294 for payment in inv.payment_ids:
295 if payment.invoice.type == 'out_refund':
298 elif mstate == 'open' and s!=0:
300 elif mstate == 'cancel' and s!=0 and s!=1:
302 elif (mstate == 'draft' or mstate == 'proforma') and s!=0 and s!=1:
305 for mline in partner_data.member_lines:
306 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':
322 if partner_data.free_member and s!=0:
324 if partner_data.associate_member:
325 res_state = self._membership_state(cr, uid, [partner_data.associate_member.id], name, args, context)
326 res[id] = res_state[partner_data.associate_member.id]
329 def _membership_date(self, cr, uid, ids, name, args, context=None):
331 """Return date of membership"""
335 member_line_obj = self.pool.get('membership.membership_line')
337 for partner in self.browse(cr, uid, ids):
339 if partner.associate_member:
340 partner_id = partner.associate_member.id
342 partner_id = partner.id
345 'membership_start': False,
346 'membership_stop': False,
347 'membership_cancel': False
350 if name == 'membership_start':
351 line_id = member_line_obj.search(cr, uid, [('partner', '=', partner_id)],
352 limit=1, order='date_from')
354 res[partner.id]['membership_start'] = member_line_obj.read(cr, uid, line_id[0],
355 ['date_from'])['date_from']
357 if name == 'membership_stop':
358 line_id1 = member_line_obj.search(cr, uid, [('partner', '=', partner_id)],
359 limit=1, order='date_to desc')
361 res[partner.id]['membership_stop'] = member_line_obj.read(cr, uid, line_id1[0],
362 ['date_to'])['date_to']
363 if name == 'membership_cancel':
364 if partner.membership_state == 'canceled':
365 line_id2 = member_line_obj.search(cr, uid, [('partner', '=', partner.id)],limit=1, order='date_cancel')
367 res[partner.id]['membership_cancel'] = member_line_obj.read(cr, uid, line_id2[0],['date_cancel'])['date_cancel']
371 def _get_partners(self, cr, uid, ids, context={}):
374 ids2 = self.search(cr, uid, [('associate_member','in',ids2)], context=context)
378 def __get_membership_state(self, *args, **kwargs):
379 return self._membership_state(*args, **kwargs)
382 'associate_member': fields.many2one('res.partner', 'Associate member'),
383 'member_lines': fields.one2many('membership.membership_line', 'partner', 'Membership'),
384 'member': fields.boolean('Member'),
385 'free_member': fields.boolean('Free member'),
386 'membership_amount': fields.float(
387 'Membership amount', digits=(16, 2),
388 help='The price negociated by the partner'),
389 'membership_state': fields.function(
390 __get_membership_state, method = True,
391 string = 'Current membership state', type = 'selection',
392 selection = STATE ,store = {
393 'account.invoice':(_get_invoice_partner,['state'], 10),
394 'membership.membership_line':(_get_partner_id,['state'], 10),
395 'res.partner':(_get_partners, ['free_member', 'membership_state','associate_member'], 10)
398 'membership_start': fields.function(
399 _membership_date, method=True, multi='membeship_start',
400 string = 'Start membership date', type = 'date',
402 'account.invoice':(_get_invoice_partner,['state'], 10),
403 'membership.membership_line':(_get_partner_id,['state'], 10, ),
404 'res.partner':(lambda self,cr,uid,ids,c={}:ids, ['free_member'], 10)
407 'membership_stop': fields.function(
408 _membership_date, method = True,
409 string = 'Stop membership date', type = 'date', multi='membership_stop',
411 'account.invoice':(_get_invoice_partner,['state'], 10),
412 'membership.membership_line':(_get_partner_id,['state'], 10),
413 'res.partner':(lambda self,cr,uid,ids,c={}:ids, ['free_member'], 10)
417 'membership_cancel': fields.function(
418 _membership_date, method = True,
419 string = 'Cancel membership date', type='date', multi='membership_cancel',
421 'account.invoice':(_get_invoice_partner,['state'], 11),
422 'membership.membership_line':(_get_partner_id,['state'], 10),
423 'res.partner':(lambda self,cr,uid,ids,c={}:ids, ['free_member'], 10)
428 'free_member': lambda *a: False,
429 'membership_cancel' : lambda *d : False,
432 def _check_recursion(self, cr, uid, ids):
433 """Check Recursive for Associated Members.
437 cr.execute('select distinct associate_member from res_partner where id IN %s',(tuple(ids),))
438 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
445 (_check_recursion, 'Error ! You can not create recursive associated members.', ['associate_member'])
448 def copy(self, cr, uid, id, default=None, context=None):
453 default = default.copy()
454 default['member_lines'] = []
455 return super(Partner, self).copy(cr, uid, id, default, context)
459 class product_template(osv.osv):
460 _inherit = 'product.template'
462 'member_price':fields.float('Member Price', digits_compute= dp.get_precision('Sale Price')),
466 class Product(osv.osv):
468 def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
469 model_obj = self.pool.get('ir.model.data')
471 if ('product' in context) and (context['product']=='membership_product'):
472 model_data_ids_form = model_obj.search(cr,user,[('model','=','ir.ui.view'),('name','in',['membership_products_form','membership_products_tree'])])
473 resource_id_form = model_obj.read(cr, user, model_data_ids_form, fields=['res_id','name'])
475 for i in resource_id_form:
476 dict_model[i['name']]=i['res_id']
477 if view_type=='form':
478 view_id = dict_model['membership_products_form']
480 view_id = dict_model['membership_products_tree']
481 return super(Product,self).fields_view_get(cr, user, view_id, view_type, context, toolbar, submenu)
484 _inherit = 'product.product'
486 'membership': fields.boolean('Membership', help='Specify if this product is a membership product'),
487 'membership_date_from': fields.date('Date from'),
488 'membership_date_to': fields.date('Date to'),
489 # 'member_price':fields.float('Member Price'),
493 'membership': lambda *args: False
498 class Invoice(osv.osv):
501 _inherit = 'account.invoice'
503 def action_cancel(self, cr, uid, ids, context=None):
504 '''Create a 'date_cancel' on the membership_line object'''
507 member_line_obj = self.pool.get('membership.membership_line')
508 today = time.strftime('%Y-%m-%d')
509 for invoice in self.browse(cr, uid, ids):
510 mlines = member_line_obj.search(cr, uid,
511 [('account_invoice_line','in',
512 [ l.id for l in invoice.invoice_line])], context)
513 member_line_obj.write(cr, uid, mlines, {'date_cancel':today}, context)
514 return super(Invoice, self).action_cancel(cr, uid, ids, context)
517 class account_invoice_line(osv.osv):
518 _inherit='account.invoice.line'
520 def write(self, cr, uid, ids, vals, context=None):
521 """Overrides orm write method
525 res = super(account_invoice_line, self).write(cr, uid, ids, vals, context=context)
526 member_line_obj = self.pool.get('membership.membership_line')
527 for line in self.browse(cr, uid, ids):
528 if line.invoice_id.type == 'out_invoice':
529 ml_ids = member_line_obj.search(cr, uid, [('account_invoice_line','=',line.id)])
530 if line.product_id and line.product_id.membership and not ml_ids:
531 # Product line has changed to a membership product
532 date_from = line.product_id.membership_date_from
533 date_to = line.product_id.membership_date_to
534 if line.invoice_id.date_invoice > date_from and line.invoice_id.date_invoice < date_to:
535 date_from = line.invoice_id.date_invoice
536 line_id = member_line_obj.create(cr, uid, {
537 'partner': line.invoice_id.partner_id.id,
538 'date_from': date_from,
540 'account_invoice_line': line.id,
542 if line.product_id and not line.product_id.membership and ml_ids:
543 # Product line has changed to a non membership product
544 member_line_obj.unlink(cr, uid, ml_ids, context=context)
547 def unlink(self, cr, uid, ids, context=None):
548 """Remove Membership Line Record for Account Invoice Line
552 member_line_obj = self.pool.get('membership.membership_line')
554 ml_ids = member_line_obj.search(cr, uid, [('account_invoice_line','=',id)])
555 member_line_obj.unlink(cr, uid, ml_ids, context=context)
556 return super(account_invoice_line, self).unlink(cr, uid, ids, context=context)
558 def create(self, cr, uid, vals, context={}):
559 """Overrides orm create method
561 result = super(account_invoice_line, self).create(cr, uid, vals, context)
562 line = self.browse(cr, uid, result)
563 member_line_obj = self.pool.get('membership.membership_line')
564 if line.invoice_id.type == 'out_invoice':
566 ml_ids = member_line_obj.search(cr, uid, [('account_invoice_line','=',line.id)])
567 if line.product_id and line.product_id.membership and not ml_ids:
568 # Product line is a membership product
569 date_from = line.product_id.membership_date_from
570 date_to = line.product_id.membership_date_to
571 if line.invoice_id.date_invoice > date_from and line.invoice_id.date_invoice < date_to:
572 date_from = line.invoice_id.date_invoice
573 line_id = member_line_obj.create(cr, uid, {
574 'partner': line.invoice_id.partner_id and line.invoice_id.partner_id.id or False,
575 'date_from': date_from,
577 'account_invoice_line': line.id,
581 account_invoice_line()
582 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: