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'''
146 SELECT MIN(ml.date_to - ai.date_invoice)
147 FROM membership_membership_line ml
148 JOIN account_invoice_line ail ON (
149 ml.account_invoice_line = ail.id
151 JOIN account_invoice ai ON (
152 ai.id = ail.invoice_id)
153 WHERE ml.id IN %s''',(tuple(ids),))
156 if r[0] and r[0] < 0:
160 def _state(self, cr, uid, ids, name, args, context=None):
161 '''Compute the state lines'''
163 for line in self.browse(cr, uid, ids):
165 SELECT i.state, i.id FROM
169 SELECT l.invoice_id FROM
170 account_invoice_line l WHERE
172 SELECT ml.account_invoice_line FROM
173 membership_membership_line ml WHERE
178 fetched = cr.fetchone()
180 res[line.id] = 'canceled'
184 if (istate == 'draft') | (istate == 'proforma'):
186 elif istate == 'open':
188 elif istate == 'paid':
190 inv = self.pool.get('account.invoice').browse(cr, uid, fetched[1])
191 for payment in inv.payment_ids:
192 if payment.invoice and payment.invoice.type == 'out_refund':
194 elif istate == 'cancel':
200 _description = __doc__
201 _name = 'membership.membership_line'
203 'partner': fields.many2one('res.partner', 'Partner', ondelete='cascade', select=1),
204 'date_from': fields.date('From'),
205 'date_to': fields.date('To'),
206 'date_cancel' : fields.date('Cancel date'),
207 'account_invoice_line': fields.many2one('account.invoice.line', 'Account Invoice line'),
208 'state': fields.function(_state, method=True, string='State', type='selection', selection=STATE),
210 _rec_name = 'partner'
213 (_check_membership_date, 'Error, this membership product is out of date', [])
219 class Partner(osv.osv):
221 _inherit = 'res.partner'
223 def _get_partner_id(self, cr, uid, ids, context=None):
225 data_inv = self.pool.get('membership.membership_line').browse(cr, uid, ids, context)
227 for data in data_inv:
228 list_partner.append(data.partner.id)
231 ids2 = self.pool.get('res.partner').search(cr, uid, [('associate_member','in',ids2)], context=context)
235 def _get_invoice_partner(self, cr, uid, ids, context=None):
236 data_inv = self.pool.get('account.invoice').browse(cr, uid, ids, context)
238 for data in data_inv:
239 list_partner.append(data.partner_id.id)
242 ids2 = self.pool.get('res.partner').search(cr, uid, [('associate_member','in',ids2)], context=context)
246 def _membership_state(self, cr, uid, ids, name, args, context=None):
250 today = time.strftime('%Y-%m-%d')
252 partner_data = self.browse(cr,uid,id)
253 if partner_data.membership_cancel and today > partner_data.membership_cancel:
256 if partner_data.membership_stop and today > partner_data.membership_stop:
260 if partner_data.member_lines:
261 for mline in partner_data.member_lines:
262 if mline.date_to >= today:
263 if mline.account_invoice_line and mline.account_invoice_line.invoice_id:
264 mstate = mline.account_invoice_line.invoice_id.state
267 inv = mline.account_invoice_line.invoice_id
268 for payment in inv.payment_ids:
269 if payment.invoice.type == 'out_refund':
272 elif mstate == 'open' and s!=0:
274 elif mstate == 'cancel' and s!=0 and s!=1:
276 elif (mstate == 'draft' or mstate == 'proforma') and s!=0 and s!=1:
279 for mline in partner_data.member_lines:
280 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':
296 if partner_data.free_member and s!=0:
298 if partner_data.associate_member:
299 res_state = self._membership_state(cr, uid, [partner_data.associate_member.id], name, args, context)
300 res[id] = res_state[partner_data.associate_member.id]
303 def _membership_date(self, cr, uid, ids, name, args, context=None):
305 '''Return date of membership'''
309 member_line_obj = self.pool.get('membership.membership_line')
311 for partner in self.browse(cr, uid, ids):
313 if partner.associate_member:
314 partner_id = partner.associate_member.id
316 partner_id = partner.id
319 'membership_start': False,
320 'membership_stop': False,
321 'membership_cancel': False
324 if name == 'membership_start':
325 line_id = member_line_obj.search(cr, uid, [('partner', '=', partner_id)],
326 limit=1, order='date_from')
328 res[partner.id]['membership_start'] = member_line_obj.read(cr, uid, line_id[0],
329 ['date_from'])['date_from']
331 if name == 'membership_stop':
332 line_id1 = member_line_obj.search(cr, uid, [('partner', '=', partner_id)],
333 limit=1, order='date_to desc')
335 res[partner.id]['membership_stop'] = member_line_obj.read(cr, uid, line_id1[0],
336 ['date_to'])['date_to']
337 if name == 'membership_cancel':
338 if partner.membership_state == 'canceled':
339 line_id2 = member_line_obj.search(cr, uid, [('partner', '=', partner.id)],limit=1, order='date_cancel')
341 res[partner.id]['membership_cancel'] = member_line_obj.read(cr, uid, line_id2[0],['date_cancel'])['date_cancel']
345 def _get_partners(self, cr, uid, ids, context={}):
348 ids2 = self.search(cr, uid, [('associate_member','in',ids2)], context=context)
352 def __get_membership_state(self, *args, **kwargs):
353 return self._membership_state(*args, **kwargs)
356 'associate_member': fields.many2one('res.partner', 'Associate member'),
357 'member_lines': fields.one2many('membership.membership_line', 'partner', 'Membership'),
358 'member': fields.boolean('Member'),
359 'free_member': fields.boolean('Free member'),
360 'membership_amount': fields.float(
361 'Membership amount', digits=(16, 2),
362 help='The price negociated by the partner'),
363 'membership_state': fields.function(
364 __get_membership_state, method = True,
365 string = 'Current membership state', type = 'selection',
366 selection = STATE ,store = {
367 'account.invoice':(_get_invoice_partner,['state'], 10),
368 'membership.membership_line':(_get_partner_id,['state'], 10),
369 'res.partner':(_get_partners, ['free_member', 'membership_state','associate_member'], 10)
372 'membership_start': fields.function(
373 _membership_date, method=True, multi='membeship_start',
374 string = 'Start membership date', type = 'date',
376 'account.invoice':(_get_invoice_partner,['state'], 10),
377 'membership.membership_line':(_get_partner_id,['state'], 10, ),
378 'res.partner':(lambda self,cr,uid,ids,c={}:ids, ['free_member'], 10)
381 'membership_stop': fields.function(
382 _membership_date, method = True,
383 string = 'Stop membership date', type = 'date', multi='membership_stop',
385 'account.invoice':(_get_invoice_partner,['state'], 10),
386 'membership.membership_line':(_get_partner_id,['state'], 10),
387 'res.partner':(lambda self,cr,uid,ids,c={}:ids, ['free_member'], 10)
391 'membership_cancel': fields.function(
392 _membership_date, method = True,
393 string = 'Cancel membership date', type='date', multi='membership_cancel',
395 'account.invoice':(_get_invoice_partner,['state'], 11),
396 'membership.membership_line':(_get_partner_id,['state'], 10),
397 'res.partner':(lambda self,cr,uid,ids,c={}:ids, ['free_member'], 10)
402 'free_member': lambda *a: False,
403 'membership_cancel' : lambda *d : False,
406 def _check_recursion(self, cr, uid, ids):
409 cr.execute('select distinct associate_member from res_partner where id IN %s',(tuple(ids),))
410 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
417 (_check_recursion, 'Error ! You can not create recursive associated members.', ['associate_member'])
420 def copy(self, cr, uid, id, default=None, context=None):
425 default = default.copy()
426 default['member_lines'] = []
427 return super(Partner, self).copy(cr, uid, id, default, context)
431 class product_template(osv.osv):
432 _inherit = 'product.template'
434 'member_price':fields.float('Member Price', digits_compute= dp.get_precision('Sale Price')),
438 class Product(osv.osv):
440 def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
441 if ('product' in context) and (context['product']=='membership_product'):
442 model_data_ids_form = self.pool.get('ir.model.data').search(cr,user,[('model','=','ir.ui.view'),('name','in',['membership_products_form','membership_products_tree'])])
443 resource_id_form = self.pool.get('ir.model.data').read(cr, user, model_data_ids_form, fields=['res_id','name'])
445 for i in resource_id_form:
446 dict_model[i['name']]=i['res_id']
447 if view_type=='form':
448 view_id = dict_model['membership_products_form']
450 view_id = dict_model['membership_products_tree']
451 return super(Product,self).fields_view_get(cr, user, view_id, view_type, context, toolbar, submenu)
454 _inherit = 'product.product'
456 'membership': fields.boolean('Membership', help='Specify if this product is a membership product'),
457 'membership_date_from': fields.date('Date from'),
458 'membership_date_to': fields.date('Date to'),
459 # 'member_price':fields.float('Member Price'),
463 'membership': lambda *args: False
468 class Invoice(osv.osv):
471 _inherit = 'account.invoice'
473 def action_cancel(self, cr, uid, ids, context=None):
474 '''Create a 'date_cancel' on the membership_line object'''
477 member_line_obj = self.pool.get('membership.membership_line')
478 today = time.strftime('%Y-%m-%d')
479 for invoice in self.browse(cr, uid, ids):
480 mlines = member_line_obj.search(cr, uid,
481 [('account_invoice_line','in',
482 [ l.id for l in invoice.invoice_line])], context)
483 member_line_obj.write(cr, uid, mlines, {'date_cancel':today}, context)
484 return super(Invoice, self).action_cancel(cr, uid, ids, context)
487 class account_invoice_line(osv.osv):
488 _inherit='account.invoice.line'
490 def write(self, cr, uid, ids, vals, context=None):
493 res = super(account_invoice_line, self).write(cr, uid, ids, vals, context=context)
494 member_line_obj = self.pool.get('membership.membership_line')
495 for line in self.browse(cr, uid, ids):
496 if line.invoice_id.type == 'out_invoice':
497 ml_ids = member_line_obj.search(cr, uid, [('account_invoice_line','=',line.id)])
498 if line.product_id and line.product_id.membership and not ml_ids:
499 # Product line has changed to a membership product
500 date_from = line.product_id.membership_date_from
501 date_to = line.product_id.membership_date_to
502 if line.invoice_id.date_invoice > date_from and line.invoice_id.date_invoice < date_to:
503 date_from = line.invoice_id.date_invoice
504 line_id = member_line_obj.create(cr, uid, {
505 'partner': line.invoice_id.partner_id.id,
506 'date_from': date_from,
508 'account_invoice_line': line.id,
510 if line.product_id and not line.product_id.membership and ml_ids:
511 # Product line has changed to a non membership product
512 member_line_obj.unlink(cr, uid, ml_ids, context=context)
515 def unlink(self, cr, uid, ids, context=None):
518 member_line_obj = self.pool.get('membership.membership_line')
520 ml_ids = member_line_obj.search(cr, uid, [('account_invoice_line','=',id)])
521 member_line_obj.unlink(cr, uid, ml_ids, context=context)
522 return super(account_invoice_line, self).unlink(cr, uid, ids, context=context)
524 def create(self, cr, uid, vals, context={}):
525 result = super(account_invoice_line, self).create(cr, uid, vals, context)
526 line = self.browse(cr, uid, result)
527 if line.invoice_id.type == 'out_invoice':
528 member_line_obj = self.pool.get('membership.membership_line')
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 is 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 and line.invoice_id.partner_id.id or False,
538 'date_from': date_from,
540 'account_invoice_line': line.id,
544 account_invoice_line()
545 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: