# -*- coding: utf-8 -*- # # Copyright 2016-2018 Fabien Bourgeois # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . """ GOLEM Members """ import logging from odoo import models, fields, api, _ from odoo.exceptions import UserError _LOGGER = logging.getLogger(__name__) class ResPartner(models.Model): """ GOLEM Member partner adaptations """ _inherit = 'res.partner' @api.model def _get_default_nationality_id(self): return self.env.ref('base.main_company').country_id nationality_id = fields.Many2one('res.country', 'Nationality', auto_join=True, default=_get_default_nationality_id) country_id = fields.Many2one(default=_get_default_nationality_id) # Gender overwriting : no need for 'other' choice gender = fields.Selection([('male', _('Male')), ('female', _('Female'))]) member_id = fields.One2many('golem.member', 'partner_id', 'GOLEM Member', readonly=True) is_member = fields.Boolean(compute='_compute_is_member') member_number = fields.Char(related='member_id.number') @api.depends('member_id') def _compute_is_member(self): """ Computes is member """ for partner in self: partner.is_member = len(partner.member_id) > 0 @api.multi def view_member(self): """ Go to member form """ self.ensure_one() return {'type': 'ir.actions.act_window', 'res_model': 'golem.member', 'view_mode': 'form', 'res_id': self[0].member_id.id if self[0].member_id else False} @api.multi def create_golem_member(self): """ Member creation from partner form """ self.ensure_one() gm_obj = self.env['golem.member'] gm_obj.create({'partner_id': self[0].id}) class GolemMember(models.Model): """ GOLEM Member model """ _name = 'golem.member' _description = 'GOLEM Member' _inherit = 'mail.thread' _inherits = {'res.partner': 'partner_id'} _sql_constraints = [('golem_member_number_manual_uniq', 'UNIQUE (number_manual)', _('This member number has already been used.'))] partner_id = fields.Many2one('res.partner', required=True, index=True, ondelete='cascade') @api.model def default_season(self): """ Get default season """ domain = [('is_default', '=', True)] return self.env['golem.season'].search(domain, limit=1) number_name = fields.Char('Member computed name', compute='_compute_number_name') number = fields.Char('Member number', store=True, readonly=True) number_manual = fields.Char('Manual number', size=50, index=True, help='Manual number overwriting automatic ' 'numbering') pictures_agreement = fields.Boolean('Pictures agreement?', default=True) electronic_processing_agreement = fields.Boolean('Electronic Processing Agreement?', default=True) opt_out_sms = fields.Boolean('Out of SMS campaigns?', help='If this field has been checked, it ' 'tells that the user refuses to receive SMS') season_ids = fields.Many2many('golem.season', string='Seasons', required=True, default=default_season, auto_join=True, ondelete='restrict') is_default = fields.Boolean('Default season?', compute='_compute_is_default', search='_search_is_default') is_number_manual = fields.Boolean('Is number manual?', store=False, compute='_compute_is_number_manual') @api.onchange('country_id') def onchange_country_domain_state(self): """ On country change : adapts state domain """ member = self[0] if member.country_id: return { 'domain': {'state_id': [('country_id', '=', member.country_id.id)]} } return {'domain': {'state_id': []}} @api.depends('number', 'name') def _compute_number_name(self): """ Computes a name composed with number and name """ for member in self: vals = [] if member.number: vals.append(member.number) if member.name: vals.append(member.name) member.number_name = u' - '.join(vals) @api.depends('season_ids') def _compute_is_default(self): """ Computes is current according to seasons """ default_s = self.default_season() for member in self: member.is_default = default_s in member.season_ids @api.multi def _search_is_default(self, operator, value): """ Search function for is default """ if operator in ('in', '='): operator = '=' if value else '!=' elif operator in ('not in', '!='): operator = '!=' if value else '=' else: err = _('Unsupported operator for defautl season search') raise NotImplementedError(err) return [('season_ids', operator, self.default_season().id)] @api.depends('number') def _compute_is_number_manual(self): conf = self.env['ir.config_parameter'] is_num_man = (conf.get_param('golem_numberconfig_isautomatic') == '0') self.update({'is_number_manual': is_num_man}) @api.multi def generate_number_perseason(self): """ Number generation in case of per season configuration """ res = None conf = self.env['ir.config_parameter'] member_number_obj = self.env['golem.member.number'] for member in self: for season in member.season_ids: domain = ['&', ('member_id', '=', member.id), ('season_id', '=', season.id)] member_num = member_number_obj.search(domain) if not member_num: season.write({'member_counter': season.member_counter}) pkey = 'golem_numberconfig_prefix' pfx = conf.get_param(pkey, '') number = u'{}{}'.format(pfx, unicode(season.member_counter)) data = {'member_id': member.id, 'season_id': season.id, 'number': number} member_num = member_number_obj.create(data) season.member_counter += 1 if season.is_default: res = member_num.number return res @api.multi def generate_number_global(self): """ Number generation in case of global configuration """ self.ensure_one() conf = self.env['ir.config_parameter'] domain = ['&', ('member_id', '=', self[0].id), ('season_id', '=', None)] member_number_obj = self.env['golem.member.number'] member_num = member_number_obj.search(domain) if not member_num: last = int(conf.get_param('golem_number_counter', 1)) pfx = conf.get_param('golem_numberconfig_prefix', '') number = pfx + str(last) data = {'member_id': self[0].id, 'season_id': None, 'number': number} member_num = member_number_obj.create(data) last += 1 conf.set_param('golem_number_counter', str(last)) else: member_num = member_num[0] return member_num.number @api.multi def generate_number(self): """ Computes number according to pre-existing number and chosen seasons """ conf = self.env['ir.config_parameter'] isauto = conf.get_param('golem_numberconfig_isautomatic') == '1' isperseason = conf.get_param('golem_numberconfig_isperseason') == '1' isfornew = conf.get_param('golem_numberconfig_isfornewmembersonly') == '1' for member in self: if not isauto or (isfornew and member.number_manual): member.number = member.number_manual else: if isperseason: member.number = member.generate_number_perseason() else: member.number = member.generate_number_global() @api.model def create(self, values): """ Number generation after creation """ new_member = super(GolemMember, self).create(values) new_member.generate_number() return new_member @api.multi def write(self, values): """ Number generation after updates """ res = super(GolemMember, self).write(values) if 'season_ids' in values or 'number_manual' in values: self.generate_number() return res class GolemMemberNumber(models.Model): """ GOLEM Member Numbers """ _name = 'golem.member.number' _description = 'GOLEM Member Numbers' name = fields.Char(compute='_compute_name') member_id = fields.Many2one('golem.member', string='Member', index=True, required=True, ondelete='cascade', auto_join=True) season_id = fields.Many2one('golem.season', string='Season', index=True, auto_join=True) number = fields.Char(index=True, readonly=True) @api.depends('season_id') def _compute_name(self): for number in self: number.name = number.season_id.name class GolemNumberConfig(models.TransientModel): """ Configuration for number computing """ _name = 'golem.member.numberconfig' _description = 'Configuration for number computing' @api.model def _default_is_automatic(self): conf = self.env['ir.config_parameter'] return conf.get_param('golem_numberconfig_isautomatic', '1') @api.model def _default_is_per_season(self): conf = self.env['ir.config_parameter'] return conf.get_param('golem_numberconfig_isperseason', '0') @api.model def _default_prefix(self): conf = self.env['ir.config_parameter'] return conf.get_param('golem_numberconfig_prefix', '') is_automatic = fields.Selection([('1', _('Yes')), ('0', _('No'))], string='Computed automatically?', default=_default_is_automatic) is_per_season = fields.Selection([('1', _('Yes')), ('0', _('No'))], string='Per season number?', default=_default_is_per_season) prefix = fields.Char('Optional prefix', default=_default_prefix) number_from = fields.Integer('First number', default=1, help='Number starting from, default to 1') @api.multi def apply_config(self): """ Apply new configuration """ self.ensure_one() conf = self.env['ir.config_parameter'] conf.set_param('golem_numberconfig_isautomatic', self.is_automatic) conf.set_param('golem_numberconfig_isperseason', self.is_per_season) conf.set_param('golem_numberconfig_prefix', self.prefix or '') if self.number_from: _LOGGER.warning('New number_from %s', self.number_from) conf.set_param('golem_number_counter', unicode(self.number_from)) self.env['golem.season'].search([]).write({ 'member_counter': self.number_from }) @api.multi def apply_nocompute(self): """ Apply new configuration only for new members (keep old numbers) """ self.ensure_one() self.apply_config() conf = self.env['ir.config_parameter'] conf.set_param('golem_numberconfig_isfornewmembersonly', '1') @api.multi def apply_recompute(self): """ Recomputes all member numbers according to new configuration """ self.ensure_one() self.apply_config() conf = self.env['ir.config_parameter'] conf.set_param('golem_numberconfig_isfornewmembersonly', '0') self.env['golem.member.number'].search([]).unlink() self.env['golem.season'].search([]).write({ 'member_counter': int(self.number_from) }) self.env['golem.member'].search([]).generate_number() class MergePartnerAutomatic(models.TransientModel): """ Merge Partner Automatic adaptations """ _inherit = 'base.partner.merge.automatic.wizard' @api.multi def action_merge(self): """ Merge adaptations : warn if there is a member """ for merge in self: for partner in merge.partner_ids: if partner.member_id: emsg = _('GOLEM Members merge has not been implemented yet. ' 'Please only merge partners, not members, or delete ' 'GOLEM Members manually before merging.') raise UserError(emsg) return super(MergePartnerAutomatic, self).action_merge()