From b01efafaba84f24a49d92d2b26d13e667e0235aa Mon Sep 17 00:00:00 2001 From: Ravi Gohil Date: Fri, 24 Jun 2016 16:46:19 +0530 Subject: [PATCH 1/4] [MOV] membership: Rename/Moved files as per new api guidelines. --- addons/membership/__init__.py | 2 +- addons/membership/__openerp__.py | 10 +++++----- addons/membership/{ => data}/membership_data.xml | 0 addons/membership/{ => data}/membership_demo.xml | 0 addons/membership/models/__init__.py | 4 ++++ addons/membership/{ => models}/membership.py | 0 ...membership_view.xml => report_membership_views.xml} | 0 .../membership_views.xml} | 0 ...p_invoice_view.xml => membership_invoice_views.xml} | 0 9 files changed, 10 insertions(+), 6 deletions(-) rename addons/membership/{ => data}/membership_data.xml (100%) rename addons/membership/{ => data}/membership_demo.xml (100%) create mode 100644 addons/membership/models/__init__.py rename addons/membership/{ => models}/membership.py (100%) rename addons/membership/report/{report_membership_view.xml => report_membership_views.xml} (100%) rename addons/membership/{membership_view.xml => views/membership_views.xml} (100%) rename addons/membership/wizard/{membership_invoice_view.xml => membership_invoice_views.xml} (100%) diff --git a/addons/membership/__init__.py b/addons/membership/__init__.py index 41a9305533f..8f7bf4b27dd 100644 --- a/addons/membership/__init__.py +++ b/addons/membership/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import membership +import models import wizard import report diff --git a/addons/membership/__openerp__.py b/addons/membership/__openerp__.py index c2774385682..13a91188f74 100644 --- a/addons/membership/__openerp__.py +++ b/addons/membership/__openerp__.py @@ -23,13 +23,13 @@ invoice and send propositions for membership renewal. 'depends': ['base', 'product', 'account'], 'data': [ 'security/ir.model.access.csv', - 'wizard/membership_invoice_view.xml', - 'membership_data.xml', - 'membership_view.xml', - 'report/report_membership_view.xml', + 'wizard/membership_invoice_views.xml', + 'data/membership_data.xml', + 'views/membership_views.xml', + 'report/report_membership_views.xml', ], 'demo': [ - 'membership_demo.xml', + 'data/membership_demo.xml', ], 'website': 'https://www.odoo.com/page/community-builder', 'test': [ diff --git a/addons/membership/membership_data.xml b/addons/membership/data/membership_data.xml similarity index 100% rename from addons/membership/membership_data.xml rename to addons/membership/data/membership_data.xml diff --git a/addons/membership/membership_demo.xml b/addons/membership/data/membership_demo.xml similarity index 100% rename from addons/membership/membership_demo.xml rename to addons/membership/data/membership_demo.xml diff --git a/addons/membership/models/__init__.py b/addons/membership/models/__init__.py new file mode 100644 index 00000000000..08753437083 --- /dev/null +++ b/addons/membership/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import membership diff --git a/addons/membership/membership.py b/addons/membership/models/membership.py similarity index 100% rename from addons/membership/membership.py rename to addons/membership/models/membership.py diff --git a/addons/membership/report/report_membership_view.xml b/addons/membership/report/report_membership_views.xml similarity index 100% rename from addons/membership/report/report_membership_view.xml rename to addons/membership/report/report_membership_views.xml diff --git a/addons/membership/membership_view.xml b/addons/membership/views/membership_views.xml similarity index 100% rename from addons/membership/membership_view.xml rename to addons/membership/views/membership_views.xml diff --git a/addons/membership/wizard/membership_invoice_view.xml b/addons/membership/wizard/membership_invoice_views.xml similarity index 100% rename from addons/membership/wizard/membership_invoice_view.xml rename to addons/membership/wizard/membership_invoice_views.xml From 4357afa94b908ae4b83d0ef91393973e0e3362c9 Mon Sep 17 00:00:00 2001 From: Ravi Gohil Date: Fri, 24 Jun 2016 17:32:34 +0530 Subject: [PATCH 2/4] [SPLIT] membership: Split files per module --- addons/membership/__openerp__.py | 3 +- addons/membership/models/__init__.py | 3 + addons/membership/models/account_invoice.py | 99 +++++ addons/membership/models/membership.py | 363 ------------------ addons/membership/models/partner.py | 253 ++++++++++++ addons/membership/models/product.py | 30 ++ ...membership_views.xml => partner_views.xml} | 109 +----- addons/membership/views/product_views.xml | 110 ++++++ 8 files changed, 498 insertions(+), 472 deletions(-) create mode 100644 addons/membership/models/account_invoice.py create mode 100644 addons/membership/models/partner.py create mode 100644 addons/membership/models/product.py rename addons/membership/views/{membership_views.xml => partner_views.xml} (60%) create mode 100644 addons/membership/views/product_views.xml diff --git a/addons/membership/__openerp__.py b/addons/membership/__openerp__.py index 13a91188f74..afa0b9e31fa 100644 --- a/addons/membership/__openerp__.py +++ b/addons/membership/__openerp__.py @@ -25,7 +25,8 @@ invoice and send propositions for membership renewal. 'security/ir.model.access.csv', 'wizard/membership_invoice_views.xml', 'data/membership_data.xml', - 'views/membership_views.xml', + 'views/product_views.xml', + 'views/partner_views.xml', 'report/report_membership_views.xml', ], 'demo': [ diff --git a/addons/membership/models/__init__.py b/addons/membership/models/__init__.py index 08753437083..ef27c919536 100644 --- a/addons/membership/models/__init__.py +++ b/addons/membership/models/__init__.py @@ -2,3 +2,6 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import membership +import partner +import product +import account_invoice diff --git a/addons/membership/models/account_invoice.py b/addons/membership/models/account_invoice.py new file mode 100644 index 00000000000..92cf5a5850b --- /dev/null +++ b/addons/membership/models/account_invoice.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import time + +from openerp.osv import fields, osv + + +class Invoice(osv.osv): + _inherit = 'account.invoice' + + def action_cancel(self, cr, uid, ids, context=None): + '''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) + + # 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) + + +class account_invoice_line(osv.osv): + _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 + 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 diff --git a/addons/membership/models/membership.py b/addons/membership/models/membership.py index e969fde2013..19df22cbd38 100644 --- a/addons/membership/models/membership.py +++ b/addons/membership/models/membership.py @@ -127,366 +127,3 @@ class membership_line(osv.osv): _constraints = [ (_check_membership_date, 'Error, this membership product is out of date', []) ] - - -class Partner(osv.osv): - _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 - - 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 - - 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) - - def _membership_state(self, cr, uid, ids, name, args, context=None): - """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' - continue - if partner_data.membership_stop and today > partner_data.membership_stop: - res[id] = 'free' if partner_data.free_member else 'old' - continue - - s = 4 - if partner_data.member_lines: - for mline in partner_data.member_lines: - if mline.date_to >= today and mline.date_from <= today: - if mline.account_invoice_line and 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): - s = 2 - break - elif mstate == 'open' and s!=0: - 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: - s = 3 - if s==4: - for mline in partner_data.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 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'] - 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 = 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. - """ - 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())) - if not level: - return False - level -= 1 - return True - - _constraints = [ - (_check_recursion, 'Error ! You cannot create recursive associated members.', ['associate_member']) - ] - - def create_membership_invoice(self, cr, uid, ids, product_id=None, datas=None, context=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) - 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']) - 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 = { - '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) - line_values = { - 'product_id': product_id, - 'price_unit': amount, - '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._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]) - - return invoice_list - - -class Product(osv.osv): - _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 = {} - - if ('product' in context) and (context['product']=='membership_product'): - if view_type == 'form': - view_id = ModelData.xmlid_to_res_id( - cr, user, 'membership.membership_products_form', context=context) - 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.')] - - - -class Invoice(osv.osv): - _inherit = 'account.invoice' - - def action_cancel(self, cr, uid, ids, context=None): - '''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) - - # 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) - - -class account_invoice_line(osv.osv): - _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 - 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 diff --git a/addons/membership/models/partner.py b/addons/membership/models/partner.py new file mode 100644 index 00000000000..d3bcb4fe833 --- /dev/null +++ b/addons/membership/models/partner.py @@ -0,0 +1,253 @@ +# -*- 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 . import membership + + +class Partner(osv.osv): + _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 + + 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 + + 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) + + def _membership_state(self, cr, uid, ids, name, args, context=None): + """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' + continue + if partner_data.membership_stop and today > partner_data.membership_stop: + res[id] = 'free' if partner_data.free_member else 'old' + continue + + s = 4 + if partner_data.member_lines: + for mline in partner_data.member_lines: + if mline.date_to >= today and mline.date_from <= today: + if mline.account_invoice_line and 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): + s = 2 + break + elif mstate == 'open' and s!=0: + 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: + s = 3 + if s==4: + for mline in partner_data.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 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'] + 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. + """ + 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())) + if not level: + return False + level -= 1 + return True + + _constraints = [ + (_check_recursion, 'Error ! You cannot create recursive associated members.', ['associate_member']) + ] + + def create_membership_invoice(self, cr, uid, ids, product_id=None, datas=None, context=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) + 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']) + 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 = { + '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) + line_values = { + 'product_id': product_id, + 'price_unit': amount, + '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._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]) + + return invoice_list diff --git a/addons/membership/models/product.py b/addons/membership/models/product.py new file mode 100644 index 00000000000..f75ea2cac66 --- /dev/null +++ b/addons/membership/models/product.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from openerp.osv import fields, osv + + +class Product(osv.osv): + _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 = {} + + if ('product' in context) and (context['product']=='membership_product'): + if view_type == 'form': + view_id = ModelData.xmlid_to_res_id( + cr, user, 'membership.membership_products_form', context=context) + 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.')] diff --git a/addons/membership/views/membership_views.xml b/addons/membership/views/partner_views.xml similarity index 60% rename from addons/membership/views/membership_views.xml rename to addons/membership/views/partner_views.xml index cddb1171787..c67440dae32 100644 --- a/addons/membership/views/membership_views.xml +++ b/addons/membership/views/partner_views.xml @@ -1,112 +1,6 @@ - + - - - - - - - membership.product.search.form - product.template - 50 - - - - - - - - - - - - - - - Membership products - product.template - 50 - - - - - - - - - - - - - - - Membership Products - product.template - 50 - -
-
- - - Membership Products - product.template - [('membership','=',True), ('type', '=', 'service')] - {'membership':True, 'type':'service', 'default_membership': True, 'default_type': 'service'} - - - - - - tree - - - - - - - form - - - - - - - - @@ -253,6 +147,5 @@ -
diff --git a/addons/membership/views/product_views.xml b/addons/membership/views/product_views.xml new file mode 100644 index 00000000000..a93d70f894a --- /dev/null +++ b/addons/membership/views/product_views.xml @@ -0,0 +1,110 @@ + + + + + + + + + + membership.product.search.form + product.template + 50 + + + + + + + + + + + + + + + Membership products + product.template + 50 + + + + + + + + + + + + + + + Membership Products + product.template + 50 + +
+
+ + + Membership Products + product.template + [('membership','=',True), ('type', '=', 'service')] + {'membership':True, 'type':'service', 'default_membership': True, 'default_type': 'service'} + + + + + + tree + + + + + + + form + + + + + + + +
+
From 6f1fb4fd09f9e41f89a1ba3d96864934242b1e6b Mon Sep 17 00:00:00 2001 From: Mansi Gandhi Date: Fri, 24 Jun 2016 17:57:13 +0530 Subject: [PATCH 3/4] [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). --- addons/membership/__openerp__.py | 5 +- addons/membership/data/membership_data.xml | 4 +- addons/membership/data/membership_demo.xml | 8 +- addons/membership/models/account_invoice.py | 150 ++++---- addons/membership/models/membership.py | 136 +++---- addons/membership/models/partner.py | 334 ++++++++---------- addons/membership/models/product.py | 37 +- addons/membership/report/report_membership.py | 49 ++- .../report/report_membership_views.xml | 8 +- addons/membership/test/test_membership.yml | 130 ------- addons/membership/tests/__init__.py | 2 +- addons/membership/tests/common.py | 5 +- addons/membership/tests/test_membership.py | 6 +- addons/membership/views/partner_views.xml | 6 +- addons/membership/views/product_views.xml | 8 +- .../membership/wizard/membership_invoice.py | 57 ++- .../wizard/membership_invoice_views.xml | 8 +- 17 files changed, 340 insertions(+), 613 deletions(-) delete mode 100644 addons/membership/test/test_membership.yml diff --git a/addons/membership/__openerp__.py b/addons/membership/__openerp__.py index afa0b9e31fa..fe6ab571c55 100644 --- a/addons/membership/__openerp__.py +++ b/addons/membership/__openerp__.py @@ -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, } diff --git a/addons/membership/data/membership_data.xml b/addons/membership/data/membership_data.xml index 64d0dc4c5c7..fea953d32d1 100644 --- a/addons/membership/data/membership_data.xml +++ b/addons/membership/data/membership_data.xml @@ -1,5 +1,5 @@ - + Check membership payment @@ -9,4 +9,4 @@ _cron_update_membership - + diff --git a/addons/membership/data/membership_demo.xml b/addons/membership/data/membership_demo.xml index 18ebfa7ba84..2f767b113aa 100644 --- a/addons/membership/data/membership_demo.xml +++ b/addons/membership/data/membership_demo.xml @@ -1,6 +1,5 @@ - - - + + @@ -22,5 +21,4 @@ - - + diff --git a/addons/membership/models/account_invoice.py b/addons/membership/models/account_invoice.py index 92cf5a5850b..ff5e495734d 100644 --- a/addons/membership/models/account_invoice.py +++ b/addons/membership/models/account_invoice.py @@ -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 diff --git a/addons/membership/models/membership.py b/addons/membership/models/membership.py index 19df22cbd38..870059f162b 100644 --- a/addons/membership/models/membership.py +++ b/addons/membership/models/membership.py @@ -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' diff --git a/addons/membership/models/partner.py b/addons/membership/models/partner.py index d3bcb4fe833..1fcdcdfaeb8 100644 --- a/addons/membership/models/partner.py +++ b/addons/membership/models/partner.py @@ -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 diff --git a/addons/membership/models/product.py b/addons/membership/models/product.py index f75ea2cac66..6107ad3b3a0 100644 --- a/addons/membership/models/product.py +++ b/addons/membership/models/product.py @@ -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) diff --git a/addons/membership/report/report_membership.py b/addons/membership/report/report_membership.py index 063dbb86c79..9e016a9890e 100644 --- a/addons/membership/report/report_membership.py +++ b/addons/membership/report/report_membership.py @@ -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,)) diff --git a/addons/membership/report/report_membership_views.xml b/addons/membership/report/report_membership_views.xml index e99793a5e58..9cb9e873526 100644 --- a/addons/membership/report/report_membership_views.xml +++ b/addons/membership/report/report_membership_views.xml @@ -1,6 +1,5 @@ - - - + + report.membership.search @@ -72,5 +71,4 @@ id="menu_report_membership" groups="base.group_partner_manager"/> - - + diff --git a/addons/membership/test/test_membership.yml b/addons/membership/test/test_membership.yml deleted file mode 100644 index 1297413dbc2..00000000000 --- a/addons/membership/test/test_membership.yml +++ /dev/null @@ -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' - diff --git a/addons/membership/tests/__init__.py b/addons/membership/tests/__init__.py index 3eb0fcacb78..a02bcfa7e5d 100644 --- a/addons/membership/tests/__init__.py +++ b/addons/membership/tests/__init__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -from openerp.addons.membership.tests import test_membership +from odoo.addons.membership.tests import test_membership diff --git a/addons/membership/tests/common.py b/addons/membership/tests/common.py index 322b083c134..2e8d889e58c 100644 --- a/addons/membership/tests/common.py +++ b/addons/membership/tests/common.py @@ -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), diff --git a/addons/membership/tests/test_membership.py b/addons/membership/tests/test_membership.py index 717e166a29f..797ef5d7926 100644 --- a/addons/membership/tests/test_membership.py +++ b/addons/membership/tests/test_membership.py @@ -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') diff --git a/addons/membership/views/partner_views.xml b/addons/membership/views/partner_views.xml index c67440dae32..6c34a5a175a 100644 --- a/addons/membership/views/partner_views.xml +++ b/addons/membership/views/partner_views.xml @@ -1,6 +1,5 @@ - - + @@ -147,5 +146,4 @@ - - + diff --git a/addons/membership/views/product_views.xml b/addons/membership/views/product_views.xml index a93d70f894a..18aaad1842b 100644 --- a/addons/membership/views/product_views.xml +++ b/addons/membership/views/product_views.xml @@ -1,6 +1,5 @@ - - - + + @@ -106,5 +105,4 @@ - - + diff --git a/addons/membership/wizard/membership_invoice.py b/addons/membership/wizard/membership_invoice.py index 987ce3078d1..2b70fc74a26 100644 --- a/addons/membership/wizard/membership_invoice.py +++ b/addons/membership/wizard/membership_invoice.py @@ -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, } diff --git a/addons/membership/wizard/membership_invoice_views.xml b/addons/membership/wizard/membership_invoice_views.xml index 484188a305c..366cbe14c61 100644 --- a/addons/membership/wizard/membership_invoice_views.xml +++ b/addons/membership/wizard/membership_invoice_views.xml @@ -1,6 +1,5 @@ - - + membership.invoice.view.form @@ -8,7 +7,7 @@
- +