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
25 from tools.translate import _
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 class membership_line(osv.osv):
52 def _check_membership_date(self, cr, uid, ids, context=None):
53 """Check if membership product is not in the past
54 @param self: The object pointer
55 @param cr: the current row, from the database cursor,
56 @param uid: the current user’s ID for security checks,
57 @param ids: List of Membership Line IDs
58 @param context: A standard dictionary for contextual values
62 SELECT MIN(ml.date_to - ai.date_invoice)
63 FROM membership_membership_line ml
64 JOIN account_invoice_line ail ON (
65 ml.account_invoice_line = ail.id
67 JOIN account_invoice ai ON (
68 ai.id = ail.invoice_id)
69 WHERE ml.id IN %s''',(tuple(ids),))
76 def _state(self, cr, uid, ids, name, args, context=None):
77 """Compute the state lines
78 @param self: The object pointer
79 @param cr: the current row, from the database cursor,
80 @param uid: the current user’s ID for security checks,
81 @param ids: List of Membership Line IDs
82 @param name: Field Name
83 @param context: A standard dictionary for contextual values
84 @param return: Dictionary of state Value
87 for line in self.browse(cr, uid, ids):
89 SELECT i.state, i.id FROM
93 SELECT l.invoice_id FROM
94 account_invoice_line l WHERE
96 SELECT ml.account_invoice_line FROM
97 membership_membership_line ml WHERE
102 fetched = cr.fetchone()
104 res[line.id] = 'canceled'
108 if (istate == 'draft') | (istate == 'proforma'):
110 elif istate == 'open':
112 elif istate == 'paid':
114 inv = self.pool.get('account.invoice').browse(cr, uid, fetched[1])
115 for payment in inv.payment_ids:
116 if payment.invoice and payment.invoice.type == 'out_refund':
118 elif istate == 'cancel':
124 _description = __doc__
125 _name = 'membership.membership_line'
127 'partner': fields.many2one('res.partner', 'Partner', ondelete='cascade', select=1),
128 'membership_id': fields.many2one('product.product', string="Membership Product", required=True),
129 'date_from': fields.date('From', readonly=True),
130 'date_to': fields.date('To', readonly=True),
131 'date_cancel' : fields.date('Cancel date'),
132 'date': fields.date('Join Date'),
133 'member_price':fields.float('Member Price', digits_compute= dp.get_precision('Sale Price'), required=True),
134 'account_invoice_line': fields.many2one('account.invoice.line', 'Account Invoice line', readonly=True),
135 'account_invoice_id': fields.related('account_invoice_line', 'invoice_id', type='many2one', relation='account.invoice', string='Invoice', readonly=True),
136 'state': fields.function(_state, method=True, string='Membership State', type='selection', selection=STATE, store=True),
137 'company_id': fields.related('account_invoice_line', 'invoice_id', 'company_id', type="many2one", relation="res.company", string="Company", readonly=True, store=True)
139 _rec_name = 'partner'
142 (_check_membership_date, 'Error, this membership product is out of date', [])
148 class Partner(osv.osv):
150 _inherit = 'res.partner'
152 def _get_partner_id(self, cr, uid, ids, context=None):
153 member_line_obj = self.pool.get('membership.membership_line')
154 res_obj = self.pool.get('res.partner')
155 data_inv = member_line_obj.browse(cr, uid, ids, context)
157 for data in data_inv:
158 list_partner.append(data.partner.id)
161 ids2 = res_obj.search(cr, uid, [('associate_member','in',ids2)], context=context)
165 def _get_invoice_partner(self, cr, uid, ids, context=None):
166 inv_obj = self.pool.get('account.invoice')
167 res_obj = self.pool.get('res.partner')
168 data_inv = inv_obj.browse(cr, uid, ids, context)
170 for data in data_inv:
171 list_partner.append(data.partner_id.id)
174 ids2 = res_obj.search(cr, uid, [('associate_member','in',ids2)], context=context)
178 def _membership_state(self, cr, uid, ids, name, args, context=None):
179 """This Function return Membership State For Given Partner.
180 @param self: The object pointer
181 @param cr: the current row, from the database cursor,
182 @param uid: the current user’s ID for security checks,
183 @param ids: List of Partner IDs
184 @param name: Field Name
185 @param context: A standard dictionary for contextual values
186 @param return: Dictionary of Membership state Value
191 today = time.strftime('%Y-%m-%d')
193 partner_data = self.browse(cr, uid, id)
194 if partner_data.membership_cancel and today > partner_data.membership_cancel:
197 if partner_data.membership_stop and today > partner_data.membership_stop:
201 if partner_data.member_lines:
202 for mline in partner_data.member_lines:
203 if mline.date_to >= today:
204 if mline.account_invoice_line and mline.account_invoice_line.invoice_id:
205 mstate = mline.account_invoice_line.invoice_id.state
208 inv = mline.account_invoice_line.invoice_id
209 for payment in inv.payment_ids:
210 if payment.invoice.type == 'out_refund':
213 elif mstate == 'open' and s!=0:
215 elif mstate == 'cancel' and s!=0 and s!=1:
217 elif (mstate == 'draft' or mstate == 'proforma') and s!=0 and s!=1:
220 for mline in partner_data.member_lines:
221 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':
237 if partner_data.free_member and s!=0:
239 if partner_data.associate_member:
240 res_state = self._membership_state(cr, uid, [partner_data.associate_member.id], name, args, context)
241 res[id] = res_state[partner_data.associate_member.id]
244 def _membership_date(self, cr, uid, ids, name, args, context=None):
246 """Return date of membership"""
250 member_line_obj = self.pool.get('membership.membership_line')
252 for partner in self.browse(cr, uid, ids):
254 if partner.associate_member:
255 partner_id = partner.associate_member.id
257 partner_id = partner.id
260 'membership_start': False,
261 'membership_stop': False,
262 'membership_cancel': False
265 if name == 'membership_start':
266 line_id = member_line_obj.search(cr, uid, [('partner', '=', partner_id)],
267 limit=1, order='date_from')
269 res[partner.id]['membership_start'] = member_line_obj.read(cr, uid, line_id[0],
270 ['date_from'])['date_from']
272 if name == 'membership_stop':
273 line_id1 = member_line_obj.search(cr, uid, [('partner', '=', partner_id)],
274 limit=1, order='date_to desc')
276 res[partner.id]['membership_stop'] = member_line_obj.read(cr, uid, line_id1[0],
277 ['date_to'])['date_to']
278 if name == 'membership_cancel':
279 if partner.membership_state == 'canceled':
280 line_id2 = member_line_obj.search(cr, uid, [('partner', '=', partner.id)],limit=1, order='date_cancel')
282 res[partner.id]['membership_cancel'] = member_line_obj.read(cr, uid, line_id2[0],['date_cancel'])['date_cancel']
286 def _get_partners(self, cr, uid, ids, context={}):
289 ids2 = self.search(cr, uid, [('associate_member','in',ids2)], context=context)
293 def __get_membership_state(self, *args, **kwargs):
294 return self._membership_state(*args, **kwargs)
297 'associate_member': fields.many2one('res.partner', 'Associate member'),
298 'member_lines': fields.one2many('membership.membership_line', 'partner', 'Membership'),
299 'free_member': fields.boolean('Free member'),
300 'membership_amount': fields.float(
301 'Membership amount', digits=(16, 2),
302 help='The price negociated by the partner'),
303 'membership_state': fields.function(
304 __get_membership_state, method = True,
305 string = 'Current Membership State', type = 'selection',
306 selection = STATE ,store = {
307 'account.invoice':(_get_invoice_partner,['state'], 10),
308 'membership.membership_line':(_get_partner_id,['state'], 10),
309 'res.partner':(_get_partners, ['free_member', 'membership_state','associate_member'], 10)
312 'membership_start': fields.function(
313 _membership_date, method=True, multi='membeship_start',
314 string = 'Start membership date', type = 'date',
316 'account.invoice':(_get_invoice_partner,['state'], 10),
317 'membership.membership_line':(_get_partner_id,['state'], 10, ),
318 'res.partner':(lambda self,cr,uid,ids,c={}:ids, ['free_member'], 10)
321 'membership_stop': fields.function(
322 _membership_date, method = True,
323 string = 'Stop membership date', type = 'date', multi='membership_stop',
325 'account.invoice':(_get_invoice_partner,['state'], 10),
326 'membership.membership_line':(_get_partner_id,['state'], 10),
327 'res.partner':(lambda self,cr,uid,ids,c={}:ids, ['free_member'], 10)
331 'membership_cancel': fields.function(
332 _membership_date, method = True,
333 string = 'Cancel membership date', type='date', multi='membership_cancel',
335 'account.invoice':(_get_invoice_partner,['state'], 11),
336 'membership.membership_line':(_get_partner_id,['state'], 10),
337 'res.partner':(lambda self,cr,uid,ids,c={}:ids, ['free_member'], 10)
342 'free_member': lambda *a: False,
343 'membership_cancel' : lambda *d : False,
346 def _check_recursion(self, cr, uid, ids):
347 """Check Recursive for Associated Members.
351 cr.execute('select distinct associate_member from res_partner where id IN %s',(tuple(ids),))
352 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
359 (_check_recursion, 'Error ! You can not create recursive associated members.', ['associate_member'])
362 def copy(self, cr, uid, id, default=None, context=None):
367 default = default.copy()
368 default['member_lines'] = []
369 return super(Partner, self).copy(cr, uid, id, default, context)
371 def create_membership_invoice(self, cr, uid, ids, product_id=None, datas=None, context=None):
372 """ Create Customer Invoice of Membership for partners.
373 @param datas: datas has dictionary value which consist Id of Membership product and Cost Amount of Membership.
374 datas = {'membership_product_id': None, 'amount':None}
376 invoice_obj = self.pool.get('account.invoice')
377 product_obj = self.pool.get('product.product')
378 invoice_line_obj = self.pool.get('account.invoice.line')
379 invoice_tax_obj = self.pool.get('account.invoice.tax')
380 product_id = product_id or datas.get('membership_product_id',False)
381 amount = datas.get('amount', 0.0)
385 if type(ids) in (int,long,):
387 for partner in self.browse(cr, uid, ids, context=context):
388 account_id = partner.property_account_receivable and partner.property_account_receivable.id or False
389 fpos_id = partner.property_account_position and partner.property_account_position.id or False
390 addr = self.address_get(cr, uid, [partner.id], ['invoice'])
391 if partner.free_member:
392 raise osv.except_osv(_('Error !'),
393 _("Partner is a free Member."))
394 if not addr.get('invoice', False):
395 raise osv.except_osv(_('Error !'),
396 _("Partner doesn't have an address to make the invoice."))
399 'product_id' : product_id,
402 line_dict = invoice_line_obj.product_id_change(cr, uid, {},
403 product_id, False, quantity, '', 'out_invoice', partner.id, fpos_id, price_unit=amount, context=context)
404 line_value.update(line_dict['value'])
405 if line_value.get('invoice_line_tax_id', False):
406 tax_tab = [(6, 0, line_value['invoice_line_tax_id'])]
407 line_value['invoice_line_tax_id'] = tax_tab
409 invoice_id = invoice_obj.create(cr, uid, {
410 'partner_id' : partner.id,
411 'address_invoice_id': addr.get('invoice', False),
412 'account_id': account_id,
413 'fiscal_position': fpos_id or False
416 line_value['invoice_id'] = invoice_id
417 invoice_line_id = invoice_line_obj.create(cr, uid, line_value, context=context)
418 invoice_obj.write(cr, uid, invoice_id, {'invoice_line':[(6,0,[invoice_line_id])]}, context=context)
419 invoice_list.append(invoice_id)
420 if line_value['invoice_line_tax_id']:
421 tax_value = invoice_tax_obj.compute(cr, uid, invoice_id).values()
422 for tax in tax_value:
423 invoice_tax_obj.create(cr, uid, tax, context=context)
428 class product_template(osv.osv):
429 _inherit = 'product.template'
431 'member_price':fields.float('Member Price', digits_compute= dp.get_precision('Sale Price')),
435 class Product(osv.osv):
437 def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
438 model_obj = self.pool.get('ir.model.data')
440 if ('product' in context) and (context['product']=='membership_product'):
441 model_data_ids_form = model_obj.search(cr,user,[('model','=','ir.ui.view'),('name','in',['membership_products_form','membership_products_tree'])])
442 resource_id_form = model_obj.read(cr, user, model_data_ids_form, fields=['res_id','name'])
444 for i in resource_id_form:
445 dict_model[i['name']]=i['res_id']
446 if view_type=='form':
447 view_id = dict_model['membership_products_form']
449 view_id = dict_model['membership_products_tree']
450 return super(Product,self).fields_view_get(cr, user, view_id, view_type, context, toolbar, submenu)
453 _inherit = 'product.product'
455 'membership': fields.boolean('Membership', help='Specify if this product is a membership product'),
456 'membership_date_from': fields.date('Date from', help='Active Membership since this date'),
457 'membership_date_to': fields.date('Date to', help='Expired date of Membership'),
461 'membership': lambda *args: False
466 class Invoice(osv.osv):
469 _inherit = 'account.invoice'
471 def action_cancel(self, cr, uid, ids, context=None):
472 '''Create a 'date_cancel' on the membership_line object'''
475 member_line_obj = self.pool.get('membership.membership_line')
476 today = time.strftime('%Y-%m-%d')
477 for invoice in self.browse(cr, uid, ids):
478 mlines = member_line_obj.search(cr, uid,
479 [('account_invoice_line','in',
480 [ l.id for l in invoice.invoice_line])], context)
481 member_line_obj.write(cr, uid, mlines, {'date_cancel':today}, context)
482 return super(Invoice, self).action_cancel(cr, uid, ids, context)
485 class account_invoice_line(osv.osv):
486 _inherit='account.invoice.line'
488 def write(self, cr, uid, ids, vals, context=None):
489 """Overrides orm write method
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 'membership_id': line.product_id.id,
507 'member_price': line.price_unit,
508 'date': time.strftime('%Y-%m-%d'),
509 'date_from': date_from,
511 'account_invoice_line': line.id,
513 if line.product_id and not line.product_id.membership and ml_ids:
514 # Product line has changed to a non membership product
515 member_line_obj.unlink(cr, uid, ml_ids, context=context)
518 def unlink(self, cr, uid, ids, context=None):
519 """Remove Membership Line Record for Account Invoice Line
523 member_line_obj = self.pool.get('membership.membership_line')
525 ml_ids = member_line_obj.search(cr, uid, [('account_invoice_line','=',id)])
526 member_line_obj.unlink(cr, uid, ml_ids, context=context)
527 return super(account_invoice_line, self).unlink(cr, uid, ids, context=context)
529 def create(self, cr, uid, vals, context={}):
530 """Overrides orm create method
532 result = super(account_invoice_line, self).create(cr, uid, vals, context)
533 line = self.browse(cr, uid, result)
534 member_line_obj = self.pool.get('membership.membership_line')
535 if line.invoice_id.type == 'out_invoice':
537 ml_ids = member_line_obj.search(cr, uid, [('account_invoice_line','=',line.id)])
538 if line.product_id and line.product_id.membership and not ml_ids:
539 # Product line is a membership product
540 date_from = line.product_id.membership_date_from
541 date_to = line.product_id.membership_date_to
542 if line.invoice_id.date_invoice > date_from and line.invoice_id.date_invoice < date_to:
543 date_from = line.invoice_id.date_invoice
544 line_id = member_line_obj.create(cr, uid, {
545 'partner': line.invoice_id.partner_id and line.invoice_id.partner_id.id or False,
546 'membership_id': line.product_id.id,
547 'member_price': line.price_unit,
548 'date': time.strftime('%Y-%m-%d'),
549 'date_from': date_from,
551 'account_invoice_line': line.id,
555 account_invoice_line()
556 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: