1 # -*- encoding: utf-8 -*-
3 ##############################################################################
5 # Copyright (c) 2007 TINY SPRL. (http://tiny.be) All Rights Reserved.
8 # WARNING: This program as such is intended to be used by professional
9 # programmers who take the whole responsability of assessing all potential
10 # consequences resulting from its eventual inadequacies and bugs
11 # End users who are looking for a ready-to-use solution with commercial
12 # garantees and support are strongly adviced to contract a Free Software
15 # This program is Free Software; you can redistribute it and/or
16 # modify it under the terms of the GNU General Public License
17 # as published by the Free Software Foundation; either version 2
18 # of the License, or (at your option) any later version.
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 ##############################################################################
31 from osv import fields, osv
32 from tools import config
36 ('none', 'Non Member'),
37 ('canceled', 'Canceled Member'),
38 ('old', 'Old Member'),
39 ('waiting', 'Waiting Member'),
40 ('invoiced', 'Invoiced Member'),
41 ('associated', 'Associated Member'),
42 ('free', 'Free Member'),
43 ('paid', 'Paid Member'),
57 class res_partner(osv.osv):
58 _inherit = 'res.partner'
60 'associate_member': fields.many2one('res.partner', 'Associate member'),
64 REQUETE = '''SELECT partner, state FROM (
65 SELECT members.partner AS partner,
66 CASE WHEN MAX(members.state) = 0 THEN 'none'
67 ELSE CASE WHEN MAX(members.state) = 1 THEN 'canceled'
68 ELSE CASE WHEN MAX(members.state) = 2 THEN 'old'
69 ELSE CASE WHEN MAX(members.state) = 3 THEN 'waiting'
70 ELSE CASE WHEN MAX(members.state) = 4 THEN 'invoiced'
71 ELSE CASE WHEN MAX(members.state) = 5 THEN 'associated'
72 ELSE CASE WHEN MAX(members.state) = 6 THEN 'free'
73 ELSE CASE WHEN MAX(members.state) = 7 THEN 'paid'
74 END END END END END END END END
77 CASE WHEN MAX(inv_digit.state) = 4 THEN 7
78 ELSE CASE WHEN MAX(inv_digit.state) = 3 THEN 4
79 ELSE CASE WHEN MAX(inv_digit.state) = 2 THEN 3
80 ELSE CASE WHEN MAX(inv_digit.state) = 1 THEN 1
84 SELECT p.id as partner,
85 CASE WHEN ai.state = 'paid' THEN 4
86 ELSE CASE WHEN ai.state = 'open' THEN 3
87 ELSE CASE WHEN ai.state = 'proforma' THEN 2
88 ELSE CASE WHEN ai.state = 'draft' THEN 2
89 ELSE CASE WHEN ai.state = 'cancel' THEN 1
93 JOIN account_invoice ai ON (
96 JOIN account_invoice_line ail ON (
97 ail.invoice_id = ai.id
99 JOIN membership_membership_line ml ON (
100 ml.account_invoice_line = ail.id
102 WHERE ml.date_from <= '%s'
103 AND ml.date_to >= '%s'
111 SELECT p.id AS partner,
112 CASE WHEN p.free_member THEN 6
113 ELSE CASE WHEN p.associate_member IN (
114 SELECT ai.partner_id FROM account_invoice ai JOIN
115 account_invoice_line ail ON (ail.invoice_id = ai.id AND ai.state = 'paid')
116 JOIN membership_membership_line ml ON (ml.account_invoice_line = ail.id)
117 WHERE ml.date_from <= '%s'
118 AND ml.date_to >= '%s'
125 OR p.associate_member > 0
127 SELECT p.id as partner,
128 MAX(CASE WHEN ai.state = 'paid' THEN 2
133 JOIN account_invoice ai ON (
136 JOIN account_invoice_line ail ON (
137 ail.invoice_id = ai.id
139 JOIN membership_membership_line ml ON (
140 ml.account_invoice_line = ail.id
142 WHERE ml.date_from < '%s'
143 AND ml.date_to < '%s'
144 AND ml.date_from <= ml.date_to
149 GROUP BY members.partner
156 class membership_line(osv.osv):
159 def _check_membership_date(self, cr, uid, ids, context=None):
160 '''Check if membership product is not in the past'''
163 SELECT MIN(ml.date_to - ai.date_invoice)
164 FROM membership_membership_line ml
165 JOIN account_invoice_line ail ON (
166 ml.account_invoice_line = ail.id
168 JOIN account_invoice ai ON (
169 ai.id = ail.invoice_id)
171 ''' % ','.join([str(id) for id in ids]))
175 if r[0] and r[0] < 0:
179 def _state(self, cr, uid, ids, name, args, context=None):
180 '''Compute the state lines'''
182 for line in self.browse(cr, uid, ids):
185 account_invoice i WHERE
187 SELECT l.invoice_id FROM
188 account_invoice_line l WHERE
190 SELECT ml.account_invoice_line FROM
191 membership_membership_line ml WHERE
196 fetched = cr.fetchone()
198 res[line.id] = 'canceled'
202 if (istate == 'draft') | (istate == 'proforma'):
204 elif istate == 'open':
206 elif istate == 'paid':
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'),
219 'date_to': fields.date('To'),
220 'date_cancel' : fields.date('Cancel date'),
221 'account_invoice_line': fields.many2one('account.invoice.line', 'Account Invoice line'),
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):
236 def _membership_state_search_inv(self,cr,uid,ids):
237 data_inv = self.pool.get('account.invoice').browse(cr,uid,ids)
239 for data in data_inv:
240 list_partner.append(data.partner_id.id)
243 def _membership_state(self, cr, uid, ids, name, args, context=None):
247 today = time.strftime('%Y-%m-%d')
250 partner_data = self.browse(cr,uid,id)
251 if partner_data.membership_cancel and today > partner_data.membership_cancel:
254 if partner_data.membership_stop and today > partner_data.membership_stop:
258 if partner_data.member_lines:
259 for mline in partner_data.member_lines:
260 if mline.date_from <= today and mline.date_to >= today:
261 if mline.account_invoice_line and mline.account_invoice_line.invoice_id:
262 mstate = mline.account_invoice_line.invoice_id.state
266 elif mstate == 'open' and s!=0:
268 elif mstate == 'cancel' and s!=0 and s!=1:
270 elif (mstate == 'draft' or mstate == 'proforma') and s!=0 and s!=1:
273 for mline in partner_data.member_lines:
274 if mline.date_from < today and mline.date_to < today and mline.date_from<=mline.date_to and mline.account_invoice_line.invoice_id.state == 'paid':
290 if partner_data.free_member and s!=0:
292 if partner_data.associate_member:
293 assciate_partner = self.browse(cr,uid,partner_data.associate_member.id)
294 cr.execute('select membership_state from res_partner where id=%d', (partner_data.id,))
295 data_partner_state = cr.fetchall()
296 for i in assciate_partner.member_lines:
297 if i.date_from <= today and i.date_to >= today and i.account_invoice_line.invoice_id.state == 'paid' and s!=0 and data_partner_state[0][0] !='free':
298 res[id] = 'associated'
300 # '''Compute membership state of partners'''
301 # today = time.strftime('%Y-%m-%d')
305 # clause = 'WHERE partner IN (' + ','.join([str(id) for id in ids]) + ')'
306 # cr.execute(REQUETE % (today, today, today, today, today, today, clause))
307 # fetches = cr.fetchall()
308 # for fetch in fetches:
309 # res[fetch[0]] = fetch[1]
312 #no more need becaz of new functionality store attribut on function field
313 # def _membership_state_search(self, cr, uid, obj, name, args):
314 # '''Search on membership state'''
316 # today = time.strftime('%Y-%m-%d')
318 # for i in range(len(args)):
321 # clause += 'state '+args[i][1]+" '"+args[i][2]+"' "
322 # cr.execute(REQUETE % (today, today, today, today, today, today, clause))
323 # ids=[x[0] for x in cr.fetchall()]
325 # return [('id', 'in', ids)]
327 def _membership_start(self, cr, uid, ids, name, args, context=None):
328 '''Return the start date of membership'''
330 member_line_obj = self.pool.get('membership.membership_line')
331 for partner in self.browse(cr, uid, ids):
332 if partner.membership_state == 'associated':
333 partner_id = partner.associate_member.id
335 partner_id = partner.id
336 line_id = member_line_obj.search(cr, uid, [('partner', '=', partner_id)],
337 limit=1, order='date_from')
339 res[partner.id] = member_line_obj.read(cr, uid, line_id[0],
340 ['date_from'])['date_from']
342 res[partner.id] = False
345 def _membership_start_search(self, cr, uid, obj, name, args):
346 '''Search on membership start date'''
349 where = ' AND '.join(['date_from '+x[1]+' \''+str(x[2])+'\''
351 cr.execute('SELECT partner, MIN(date_from) \
353 SELECT partner, MIN(date_from) AS date_from \
354 FROM membership_membership_line \
361 return [('id', '=', '0')]
362 return [('id', 'in', [x[0] for x in res])]
364 def _membership_stop(self, cr, uid, ids, name, args, context=None):
365 '''Return the stop date of membership'''
367 member_line_obj = self.pool.get('membership.membership_line')
368 for partner in self.browse(cr, uid, ids):
369 cr.execute('select membership_state from res_partner where id=%d', (partner.id,))
370 data_state = cr.fetchall()
371 #if partner.membership_state == 'associated':
372 if data_state[0][0] == 'associated':
373 partner_id = partner.associate_member.id
375 partner_id = partner.id
376 line_id = member_line_obj.search(cr, uid, [('partner', '=', partner_id)],
377 limit=1, order='date_to desc')
379 res[partner.id] = member_line_obj.read(cr, uid, line_id[0],
380 ['date_to'])['date_to']
382 res[partner.id] = False
385 def _membership_stop_search(self, cr, uid, obj, name, args):
386 '''Search on membership stop date'''
389 where = ' AND '.join(['date_to '+x[1]+' \''+str(x[2])+'\''
391 cr.execute('SELECT partner, MAX(date_to) \
393 SELECT partner, MAX(date_to) AS date_to \
394 FROM membership_membership_line \
401 return [('id', '=', '0')]
402 return [('id', 'in', [x[0] for x in res])]
404 def _membership_cancel(self, cr, uid, ids, name, args, context=None):
405 '''Return the cancel date of membership'''
407 member_line_obj = self.pool.get('membership.membership_line')
408 for partner_id in ids:
409 line_id = member_line_obj.search(cr, uid, [('partner', '=', partner_id)],
410 limit=1, order='date_cancel')
412 res[partner_id] = member_line_obj.read(cr, uid, line_id[0],
413 ['date_cancel'])['date_cancel']
415 res[partner_id] = False
418 def _membership_cancel_search(self, cr, uid, obj, name, args):
419 '''Search on membership cancel date'''
422 where = ' AND '.join(['date_cancel '+x[1]+' \''+str(x[2])+'\''
424 cr.execute('SELECT partner, MIN(date_cancel) \
426 SELECT partner, MIN(date_cancel) AS date_cancel \
427 FROM membership_membership_line \
434 return [('id', '=', '0')]
435 return [('id', 'in', [x[0] for x in res])]
439 _inherit = 'res.partner'
441 'member_lines': fields.one2many('membership.membership_line', 'partner',
443 'membership_amount': fields.float('Membership amount', digites=(16, 2),
444 help='The price negociated by the partner'),
445 # 'membership_state': fields.function(_membership_state, method=True, string='Current membership state',
446 # type='selection', selection=STATE, fnct_search=_membership_state_search),
447 'membership_state': fields.function(_membership_state, method=True, string='Current membership state',
448 type='selection',selection=STATE,store={'account.invoice':(['state'],_membership_state_search_inv)}),
449 # 'associate_member': fields.many2one('res.partner', 'Associate member'),
450 'free_member': fields.boolean('Free member'),
451 'membership_start': fields.function(_membership_start, method=True,
452 string='Start membership date', type='date',
453 fnct_search=_membership_start_search),
454 'membership_stop': fields.function(_membership_stop, method=True,
455 string='Stop membership date', type='date',
456 fnct_search=_membership_stop_search),
457 'membership_cancel': fields.function(_membership_cancel, method=True,
458 string='Cancel membership date', type='date',
459 fnct_search=_membership_cancel_search),
462 'free_member': lambda *a: False,
463 'membership_cancel' : lambda *d : False,
468 class product_template(osv.osv):
469 _inherit = 'product.template'
471 'member_price':fields.float('Member Price', digits=(16, int(config['price_accuracy']))),
475 class Product(osv.osv):
477 def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False):
478 if ('product' in context) and (context['product']=='membership_product'):
479 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'])])
480 resource_id_form = self.pool.get('ir.model.data').read(cr,user,model_data_ids_form,fields=['res_id','name'])
482 for i in resource_id_form:
483 dict_model[i['name']]=i['res_id']
484 if view_type=='form':
485 view_id = dict_model['membership_products_form']
487 view_id = dict_model['membership_products_tree']
488 return super(Product,self).fields_view_get(cr, user, view_id, view_type, context, toolbar)
491 _inherit = 'product.product'
492 _description = 'product.product'
495 'membership': fields.boolean('Membership', help='Specify if this product is a membership product'),
496 'membership_date_from': fields.date('Date from'),
497 'membership_date_to': fields.date('Date to'),
498 # 'member_price':fields.float('Member Price'),
502 'membership': lambda *args: False
507 class Invoice(osv.osv):
510 _inherit = 'account.invoice'
513 def action_move_create(self, cr, uid, ids, context=None):
514 '''Create membership.membership_line if the product is for membership'''
517 member_line_obj = self.pool.get('membership.membership_line')
518 partner_obj = self.pool.get('res.partner')
519 for invoice in self.browse(cr, uid, ids):
521 # fetch already existing member lines
522 former_mlines = member_line_obj.search(cr,uid,
523 [('account_invoice_line','in',
524 [ l.id for l in invoice.invoice_line])], context)
527 member_line_obj.write(cr,uid,former_mlines, {'account_invoice_line':False}, context)
529 for line in invoice.invoice_line:
530 if line.product_id and line.product_id.membership:
531 date_from = line.product_id.membership_date_from
532 date_to = line.product_id.membership_date_to
533 if invoice.date_invoice > date_from and invoice.date_invoice < date_to:
534 date_from = invoice.date_invoice
535 member_line_obj.create(cr, uid, {
536 'partner': invoice.partner_id.id,
537 'date_from': date_from,
539 'account_invoice_line': line.id,
541 return super(Invoice, self).action_move_create(cr, uid, ids, context)
543 def action_cancel(self, cr, uid, ids, context=None):
544 '''Create a 'date_cancel' on the membership_line object'''
548 member_line_obj = self.pool.get('membership.membership_line')
549 today = time.strftime('%Y-%m-%d')
551 for invoice in self.browse(cr, uid, ids):
552 mlines = member_line_obj.search(cr,uid,
553 [('account_invoice_line','in',
554 [ l.id for l in invoice.invoice_line])], context)
555 member_line_obj.write(cr,uid,mlines, {'date_cancel':today}, context)
559 class ReportPartnerMemberYear(osv.osv):
560 '''Membership by Years'''
562 _name = 'report.partner_member.year'
563 _description = __doc__
567 'year': fields.char('Year', size='4', readonly=True, select=1),
568 'canceled_number': fields.integer('Canceled', readonly=True),
569 'waiting_number': fields.integer('Waiting', readonly=True),
570 'invoiced_number': fields.integer('Invoiced', readonly=True),
571 'paid_number': fields.integer('Paid', readonly=True),
572 'canceled_amount': fields.float('Canceled', digits=(16, 2), readonly=True),
573 'waiting_amount': fields.float('Waiting', digits=(16, 2), readonly=True),
574 'invoiced_amount': fields.float('Invoiced', digits=(16, 2), readonly=True),
575 'paid_amount': fields.float('Paid', digits=(16, 2), readonly=True),
576 'currency': fields.many2one('res.currency', 'Currency', readonly=True,
581 '''Create the view'''
583 CREATE OR REPLACE VIEW report_partner_member_year AS (
586 COUNT(ncanceled) as canceled_number,
587 COUNT(npaid) as paid_number,
588 COUNT(ninvoiced) as invoiced_number,
589 COUNT(nwaiting) as waiting_number,
590 SUM(acanceled) as canceled_amount,
591 SUM(apaid) as paid_amount,
592 SUM(ainvoiced) as invoiced_amount,
593 SUM(awaiting) as waiting_amount,
597 CASE WHEN ai.state = 'cancel' THEN ml.id END AS ncanceled,
598 CASE WHEN ai.state = 'paid' THEN ml.id END AS npaid,
599 CASE WHEN ai.state = 'open' THEN ml.id END AS ninvoiced,
600 CASE WHEN (ai.state = 'draft' OR ai.state = 'proforma')
601 THEN ml.id END AS nwaiting,
602 CASE WHEN ai.state = 'cancel'
603 THEN SUM(ail.price_unit * ail.quantity * (1 - ail.discount / 100))
604 ELSE 0 END AS acanceled,
605 CASE WHEN ai.state = 'paid'
606 THEN SUM(ail.price_unit * ail.quantity * (1 - ail.discount / 100))
608 CASE WHEN ai.state = 'open'
609 THEN SUM(ail.price_unit * ail.quantity * (1 - ail.discount / 100))
610 ELSE 0 END AS ainvoiced,
611 CASE WHEN (ai.state = 'draft' OR ai.state = 'proforma')
612 THEN SUM(ail.price_unit * ail.quantity * (1 - ail.discount / 100))
613 ELSE 0 END AS awaiting,
614 TO_CHAR(ml.date_from, 'YYYY') AS year,
615 ai.currency_id AS currency,
617 FROM membership_membership_line ml
618 JOIN (account_invoice_line ail
619 LEFT JOIN account_invoice ai
620 ON (ail.invoice_id = ai.id))
621 ON (ml.account_invoice_line = ail.id)
623 ON (ml.partner = p.id)
624 GROUP BY TO_CHAR(ml.date_from, 'YYYY'), ai.state,
625 ai.currency_id, ml.id) AS foo
626 GROUP BY year, currency)
629 ReportPartnerMemberYear()
632 class ReportPartnerMemberYearNew(osv.osv):
633 '''New Membership by Years'''
635 _name = 'report.partner_member.year_new'
636 _description = __doc__
641 'year': fields.char('Year', size='4', readonly=True, select=1),
642 'canceled_number': fields.integer('Canceled', readonly=True),
643 'waiting_number': fields.integer('Waiting', readonly=True),
644 'invoiced_number': fields.integer('Invoiced', readonly=True),
645 'paid_number': fields.integer('Paid', readonly=True),
646 'canceled_amount': fields.float('Canceled', digits=(16, 2), readonly=True),
647 'waiting_amount': fields.float('Waiting', digits=(16, 2), readonly=True),
648 'invoiced_amount': fields.float('Invoiced', digits=(16, 2), readonly=True),
649 'paid_amount': fields.float('Paid', digits=(16, 2), readonly=True),
650 'currency': fields.many2one('res.currency', 'Currency', readonly=True,
655 '''Create the view'''
657 CREATE OR REPLACE VIEW report_partner_member_year AS (
660 COUNT(ncanceled) as canceled_number,
661 COUNT(npaid) as paid_number,
662 COUNT(ninvoiced) as invoiced_number,
663 COUNT(nwaiting) as waiting_number,
664 SUM(acanceled) as canceled_amount,
665 SUM(apaid) as paid_amount,
666 SUM(ainvoiced) as invoiced_amount,
667 SUM(awaiting) as waiting_amount,
671 CASE WHEN ai.state = 'cancel' THEN ml.id END AS ncanceled,
672 CASE WHEN ai.state = 'paid' THEN ml.id END AS npaid,
673 CASE WHEN ai.state = 'open' THEN ml.id END AS ninvoiced,
674 CASE WHEN (ai.state = 'draft' OR ai.state = 'proforma')
675 THEN ml.id END AS nwaiting,
676 CASE WHEN ai.state = 'cancel'
677 THEN SUM(ail.price_unit * ail.quantity * (1 - ail.discount / 100))
678 ELSE 0 END AS acanceled,
679 CASE WHEN ai.state = 'paid'
680 THEN SUM(ail.price_unit * ail.quantity * (1 - ail.discount / 100))
682 CASE WHEN ai.state = 'open'
683 THEN SUM(ail.price_unit * ail.quantity * (1 - ail.discount / 100))
684 ELSE 0 END AS ainvoiced,
685 CASE WHEN (ai.state = 'draft' OR ai.state = 'proforma')
686 THEN SUM(ail.price_unit * ail.quantity * (1 - ail.discount / 100))
687 ELSE 0 END AS awaiting,
688 TO_CHAR(ml.date_from, 'YYYY') AS year,
689 ai.currency_id AS currency,
691 FROM membership_membership_line ml
692 JOIN (account_invoice_line ail
693 LEFT JOIN account_invoice ai
694 ON (ail.invoice_id = ai.id))
695 ON (ml.account_invoice_line = ail.id)
697 ON (ml.partner = p.id)
698 GROUP BY TO_CHAR(ml.date_from, 'YYYY'), ai.state,
699 ai.currency_id, ml.id) AS foo
700 GROUP BY year, currency)
703 ReportPartnerMemberYear()
706 class ReportPartnerMemberYearNew(osv.osv):
707 '''New Membership by Years'''
709 _name = 'report.partner_member.year_new'
710 _description = __doc__
714 'year': fields.char('Year', size='4', readonly=True, select=1),
715 'canceled_number': fields.integer('Canceled', readonly=True),
716 'waiting_number': fields.integer('Waiting', readonly=True),
717 'invoiced_number': fields.integer('Invoiced', readonly=True),
718 'paid_number': fields.integer('Paid', readonly=True),
719 'canceled_amount': fields.float('Canceled', digits=(16, 2), readonly=True),
720 'waiting_amount': fields.float('Waiting', digits=(16, 2), readonly=True),
721 'invoiced_amount': fields.float('Invoiced', digits=(16, 2), readonly=True),
722 'paid_amount': fields.float('Paid', digits=(16, 2), readonly=True),
723 'currency': fields.many2one('res.currency', 'Currency', readonly=True,
727 def init(self, cursor):
728 '''Create the view'''
730 CREATE OR REPLACE VIEW report_partner_member_year_new AS (
733 COUNT(ncanceled) AS canceled_number,
734 COUNT(npaid) AS paid_number,
735 COUNT(ninvoiced) AS invoiced_number,
736 COUNT(nwaiting) AS waiting_number,
737 SUM(acanceled) AS canceled_amount,
738 SUM(apaid) AS paid_amount,
739 SUM(ainvoiced) AS invoiced_amount,
740 SUM(awaiting) AS waiting_amount,
744 CASE WHEN ai.state = 'cancel' THEN ml2.id END AS ncanceled,
745 CASE WHEN ai.state = 'paid' THEN ml2.id END AS npaid,
746 CASE WHEN ai.state = 'open' THEN ml2.id END AS ninvoiced,
747 CASE WHEN (ai.state = 'draft' OR ai.state = 'proforma')
748 THEN ml2.id END AS nwaiting,
749 CASE WHEN ai.state = 'cancel'
750 THEN SUM(ail.price_unit * ail.quantity * (1 - ail.discount / 100))
751 ELSE 0 END AS acanceled,
752 CASE WHEN ai.state = 'paid'
753 THEN SUM(ail.price_unit * ail.quantity * (1 - ail.discount / 100))
755 CASE WHEN ai.state = 'open'
756 THEN SUM(ail.price_unit * ail.quantity * (1 - ail.discount / 100))
757 ELSE 0 END AS ainvoiced,
758 CASE WHEN (ai.state = 'draft' OR ai.state = 'proforma')
759 THEN SUM(ail.price_unit * ail.quantity * (1 - ail.discount / 100))
760 ELSE 0 END AS awaiting,
761 TO_CHAR(ml2.date_from, 'YYYY') AS year,
762 ai.currency_id AS currency,
766 MIN(date_from) AS date_from
767 FROM membership_membership_line
770 JOIN membership_membership_line ml2
771 JOIN (account_invoice_line ail
772 LEFT JOIN account_invoice ai
773 ON (ail.invoice_id = ai.id))
774 ON (ml2.account_invoice_line = ail.id)
775 ON (ml1.id = ml2.partner AND ml1.date_from = ml2.date_from)
777 ON (ml2.partner = p.id)
778 GROUP BY TO_CHAR(ml2.date_from, 'YYYY'), ai.state,
779 ai.currency_id, ml2.id) AS foo
780 GROUP BY year, currency
784 ReportPartnerMemberYearNew()
785 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: