Commit a3e3560c by François C.

Merge branch '6833--nouveaux-sans-statut' into 'dev_cooperatic'

Rectifications nécessaires suite prise de contrôle des changements de statuts par module lacagette_membership

See merge request !79
parents 13c541f0 f4187284
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
# #
'category': 'Uncategorized', 'category': 'Uncategorized',
'version': '0.0.7.4', 'version': '0.0.7.5',
# any module necessary for this one to work correctly # any module necessary for this one to work correctly
'depends': ['base', 'coop_membership', 'coop_shift'], 'depends': ['base', 'coop_membership', 'coop_shift'],
......
...@@ -7,4 +7,5 @@ from . import shift_shift ...@@ -7,4 +7,5 @@ from . import shift_shift
from . import member_state_change from . import member_state_change
from . import shift_template from . import shift_template
from . import shift_template_registration_line from . import shift_template_registration_line
from . import shift_template_registration
from . import shift_extension
...@@ -37,7 +37,8 @@ WORKING_STATE_SELECTION = [ ...@@ -37,7 +37,8 @@ WORKING_STATE_SELECTION = [
('delay', 'Delay'), ('delay', 'Delay'),
('blocked', 'Blocked'), ('blocked', 'Blocked'),
('gone', 'Gone'), ('gone', 'Gone'),
('associated', 'Associated') ('associated', 'Associated'),
('unsubscribed', 'Unsubscribed')
] ]
class ResPartner(models.Model): class ResPartner(models.Model):
...@@ -45,9 +46,14 @@ class ResPartner(models.Model): ...@@ -45,9 +46,14 @@ class ResPartner(models.Model):
# Columns Section # Columns Section
makeups_to_do = fields.Integer( makeups_to_do = fields.Integer(
"Number of make-ups to done", "Number of make-ups to do",
default= 0) default= 0)
makeups_to_add = fields.Integer(
"Number of make-ups to add (set by shift_registration)",
default= 0)
# To be removed
target_status = fields.Selection( target_status = fields.Selection(
selection=TARGET_STATUS_SELECTION, default='') selection=TARGET_STATUS_SELECTION, default='')
...@@ -57,7 +63,6 @@ class ResPartner(models.Model): ...@@ -57,7 +63,6 @@ class ResPartner(models.Model):
compute='_compute_lacagette_cooperative_state' compute='_compute_lacagette_cooperative_state'
) )
working_state = fields.Selection( working_state = fields.Selection(
selection=WORKING_STATE_SELECTION, string='Working State', store=True, selection=WORKING_STATE_SELECTION, string='Working State', store=True,
compute='_compute_lacagette_working_state', help="This state depends on the" compute='_compute_lacagette_working_state', help="This state depends on the"
...@@ -68,55 +73,95 @@ class ResPartner(models.Model): ...@@ -68,55 +73,95 @@ class ResPartner(models.Model):
extra_shift_done = fields.Integer( extra_shift_done = fields.Integer(
"Number of shift done with both of the associate", "Number of shift done with both of the associate",
default= 0) default= 0)
@api.multi @api.multi
def _compute_lacagette_working_state(self): def _compute_lacagette_working_state(self):
_logger.debug("Dans _compute_lacagette_working_state") _logger.debug("Dans _compute_lacagette_working_state")
conf = self.env['ir.config_parameter']
unsubscribe_limit = int(conf.get_param("lacagette_membership.points_limit_to_get_unsubscribed"))
current_datetime = fields.Datetime.now()
for partner in self: for partner in self:
_logger.debug("partner_id = %s, cooperative_state = %s", str(partner.id), str(partner.cooperative_state)) _logger.debug("partner_id = %s, cooperative_state = %s", str(partner.id), str(partner.cooperative_state))
if partner.is_associated_people is True: state = 'up_to_date'
partner.working_state = partner.parent_id.cooperative_state if partner.leave_ids:
else: is_exempted = partner.leave_ids.filtered(
if partner.cooperative_state in ['gone', 'associated']: lambda l: l.state == 'done' and l.start_date <= current_datetime and l.stop_date >=
partner.working_state = partner.cooperative_state current_datetime and l.partner_state == 'exempted')
# _logger.debug("is_exempted = %s", str(is_exempted))
if is_exempted:
_logger.debug("On met le statut exempté")
state = 'exempted'
if state != 'exempted':
if partner.is_associated_people is True:
state = partner.parent_id.cooperative_state
else: else:
potential_associated_record = self.env['res.partner'].search([('email','=',self.email), ('is_associated_people', '=', True)]) if partner.cooperative_state in ['gone', 'associated']:
if potential_associated_record: state = partner.cooperative_state
partner.working_state = 'associated' else:
potential_associated_record = self.env['res.partner'].search([('email','=',self.email), ('is_associated_people', '=', True)])
# Not sure, it is useful, but _compute_working_state is overriden, calling our own method if potential_associated_record:
state = 'associated'
else:
if partner.is_unsubscribed is False:
points = 0
if partner.shift_type == 'standard':
points = partner.final_standard_point
else:
points = partner.final_ftop_point
if points < 0:
if points <= unsubscribe_limit:
state = 'unsubscribed'
self.env['shift.counter.event'].unsubscribe_partner(partner, unsubscribe_limit, points)
else:
if partner.date_alert_stop:
if partner.date_delay_stop > current_datetime:
state = 'delay'
else:
state = 'suspended'
else:
state = 'suspended'
else:
state = 'unsubscribed'
partner.working_state = state
# Overriding by calling our own method (which is mentionned in field declaration)
@api.depends('is_blocked', 'final_standard_point', 'final_ftop_point', 'shift_type', 'date_alert_stop', 'date_delay_stop', 'leave_ids.state')
@api.multi @api.multi
def _compute_working_state(self): def _compute_working_state(self):
self._compute_lacagette_working_state() self._compute_lacagette_working_state()
@api.depends(
'working_state', 'is_unpayed', 'is_unsubscribed',
'is_worker_member', 'is_associated_people',
'parent_id.cooperative_state')
@api.multi @api.multi
def _compute_lacagette_cooperative_state(self): def _compute_lacagette_cooperative_state(self):
_logger.debug("Dans _compute_lacagette_cooperative_state") _logger.debug("Dans _compute_lacagette_cooperative_state")
for partner in self: for partner in self:
# _logger.debug("Partner = %s", str(partner.read())) # _logger.debug("Partner = %s", str(partner.read()))
_logger.debug("partner_id = %s, working_state = %s", str(partner.id), str(partner.working_state)) _logger.debug("partner_id = %s, working_state = %s", str(partner.id), str(partner.working_state))
if partner.is_member is False and partner.is_associated_people is False and partner.is_worker_member is False: state = 'up_to_date'
partner.cooperative_state = 'not_concerned' if partner.is_member is False and partner.is_associated_people is False:
state= 'not_concerned'
else: else:
if partner.is_associated_people: if partner.is_associated_people:
# Associated People # Associated People
partner.cooperative_state = partner.parent_id.cooperative_state state = partner.parent_id.cooperative_state
elif partner.is_worker_member: elif partner.is_member:
# Type A Subscriptor # Type A Subscriptor
if partner.is_unsubscribed and partner.working_state not in ['gone', 'associated']: if partner.is_unsubscribed and partner.working_state not in ['gone', 'associated']:
partner.cooperative_state = 'unsubscribed' state = 'unsubscribed'
elif partner.is_unpayed: elif partner.is_unpayed:
partner.cooperative_state = 'unpayed' state = 'unpayed'
elif partner.working_state:
state = partner.working_state
else: else:
partner.cooperative_state = partner.working_state partner.working_state = state
partner.cooperative_state = state
_logger.debug("A l'issue du traitement partner_id = %s, working_state = %s, cooperative_state = %s", str(partner.id), str(partner.working_state), str(partner.cooperative_state))
# Not sure, it is useful, but _compute_cooperative_state is overriden, calling our own method # Overriding by calling our own method (which is mentionned in field declaration)
@api.depends( @api.depends(
'working_state', 'is_unpayed', 'is_unsubscribed', 'working_state', 'is_unpayed', 'is_unsubscribed',
'is_worker_member', 'is_associated_people', 'is_worker_member', 'is_associated_people',
...@@ -252,9 +297,15 @@ class ResPartner(models.Model): ...@@ -252,9 +297,15 @@ class ResPartner(models.Model):
def set_special_state(self, cr, uid, partner, context=None): def set_special_state(self, cr, uid, partner, context=None):
new_state = partner['state']
if partner['state'] == 'cancel_special': if partner['state'] == 'cancel_special':
partner['state'] = 'unsubscribed' new_state = 'unsubscribed'
return self.write(cr, uid, [partner['id']], {'cooperative_state': partner['state'], 'working_state': partner['state']} , context=context) write_result = self.write(cr, uid, [partner['id']], {'cooperative_state': new_state, 'working_state': new_state} , context=context)
if new_state == 'unsubscribed':
member = self.pool.get('res.partner').browse(cr, uid, partner['id'], context)
return member.recompute_member_states()
else:
return write_result
def _write_state_change(self, state): def _write_state_change(self, state):
data = {'member_id': self.id, 'state': state} data = {'member_id': self.id, 'state': state}
...@@ -281,6 +332,9 @@ class ResPartner(models.Model): ...@@ -281,6 +332,9 @@ class ResPartner(models.Model):
_logger.info("valeurs recues pour write partner = %s", str(vals)) _logger.info("valeurs recues pour write partner = %s", str(vals))
if 'cooperative_state' in vals: if 'cooperative_state' in vals:
self._write_state_change(vals['cooperative_state']) self._write_state_change(vals['cooperative_state'])
if 'barcode_base' in vals:
vals['cooperative_state'] = 'unsubscribed'
vals['working_state'] = 'unsubscribed'
return super(ResPartner, self).write(vals) return super(ResPartner, self).write(vals)
......
...@@ -9,17 +9,19 @@ _logger = logging.getLogger(__name__) ...@@ -9,17 +9,19 @@ _logger = logging.getLogger(__name__)
class ShiftCounterEvent(models.Model): class ShiftCounterEvent(models.Model):
_inherit = 'shift.counter.event' _inherit = 'shift.counter.event'
def _alter_point_event_depending_on_makeups_total_count(self, vals): technical_note = fields.Char(string="Technical note such as 'no trigger'", required=True)
def _alter_point_event_depending_on_makeups_total_count(self, res_partner, vals, suspension_limit):
""" """
This code helps keeping the consistency between std_counter and makeup total count. This code helps keeping the consistency between std_counter and makeup total count.
It solves very specific discrepancy occuring when following conditions are met : It solves very specific discrepancy occuring when following conditions are met :
A. the total makeup count (makeups_to_do + makeups_to_come) is more than 2 and std_counter = -2 A. the total makeup count (makeups_to_do + makeups_to_come) is more than |suspension_limit| and std_counter = suspension_limit
B. some action increments std_counter B. some action increments std_counter
In that case, the std_counter becomes -1 whereas we would like it to be -2. In that case, the std_counter becomes -1 whereas we would like it to be suspension_limit.
The idea behind the restriction to the [std_counter = -2] situation is that discrepancy The idea behind the restriction to the [std_counter = -2] situation is that discrepancy
is always due to the fact that we are adding more makeups to a member where std_counter = -2. is always due to the fact that we are adding more makeups to a member where std_counter = suspension_limit.
TODO : take into account historically corrupted data where there is discrepancy with std_counter = -1 TODO : take into account historically corrupted data where there is discrepancy with std_counter = -1
Below typical scenario where A and B are met (there are probably more than those two !) : Below typical scenario where A and B are met (there are probably more than those two !) :
...@@ -31,15 +33,16 @@ class ShiftCounterEvent(models.Model): ...@@ -31,15 +33,16 @@ class ShiftCounterEvent(models.Model):
to be use on ftop shift members. to be use on ftop shift members.
TODO : WARNING odoo openerp.models: shift.registration.read() with unknown field 'final_standard_point' : is it caused by this piece of code ? TODO : WARNING odoo openerp.models: shift.registration.read() with unknown field 'final_standard_point' : is it caused by this piece of code ?
TODO : understand difference in relative position of update_partner_target_status and actual value write in "CREATE" and "WRITE" methods below TODO : nderstand difference in relative position of update_partner_target_status and actual value write in "CREATE" and "WRITE" methods below
TODO : _update_partner_target_status looks similar to run_process_target_status... is it necessary to call run_process_target_status in update_members_makeups_core ? TODO : _update_partner_target_status looks similar to run_process_target_status... is it necessary to call run_process_target_status in update_members_makeups_core ?
Que faire si le type de service est "ftop" ?
""" """
try: try:
if vals['type'] == 'standard' and vals['point_qty'] > 0: if vals['type'] == 'standard' and vals['point_qty'] > 0:
res_partner = self.env['res.partner'].search([('id', '=', vals['partner_id'])])
if res_partner: if res_partner:
p = res_partner[0] p = res_partner[0]
if p.final_standard_point == -2 and p.shift_type != "ftop": if p.final_standard_point == suspension_limit and p.shift_type != "ftop":
_logger.info("Le nombre de points est la limite pour être suspendu, et membre ABCD, on va chercher à ajuster les points")
cond = [ cond = [
['name', '=', p["name"]], ['name', '=', p["name"]],
['shift_type', '=', 'standard'], ['shift_type', '=', 'standard'],
...@@ -53,56 +56,165 @@ class ShiftCounterEvent(models.Model): ...@@ -53,56 +56,165 @@ class ShiftCounterEvent(models.Model):
makeups_total_count = p['makeups_to_do'] + len(res_shift_reg) makeups_total_count = p['makeups_to_do'] + len(res_shift_reg)
if points_after_required_change + makeups_total_count > 0: if points_after_required_change + makeups_total_count > 0:
points_target = - makeups_total_count points_target = - makeups_total_count
if points_target < -2: if points_target < suspension_limit:
points_target = -2 points_target = suspension_limit
vals['point_qty'] = points_target - p.final_standard_point vals['point_qty'] = points_target - p.final_standard_point
except Exception as e: except Exception as e:
# don't block process if an error occurs here # don't block process if an error occurs here
_logger.error("Error during _alter_point_event_depending_on_makeups_total_count : %s", str(e)) _logger.error("Error during _alter_point_event_depending_on_makeups_total_count : %s", str(e))
return vals
@api.model
def unsubscribe_partner(self, partner, unsubscribe_limit, current_points):
result = None
try:
conf = self.env['ir.config_parameter']
makeups_todo_after_unsubscribed = conf.get_param("lacagette_membership.makeups_to_do_after_unsubscribed")
result = 'done'
new_values = {'makeups_to_do': makeups_todo_after_unsubscribed,
'working_state': 'unsubscribed', 'cooperative_state': 'unsubscribed'}
# Get points difference to set points to unsubscribe_limit - 1
_logger.info("unsubscribe_partner : current_points = %s", str(current_points))
target_points = unsubscribe_limit + 1
points_diff = abs(current_points - target_points)
if points_diff != 0:
if current_points > target_points:
points_update = - points_diff
else:
points_update = points_diff
data = {
'name': "Désinscription : passage à " + str(target_points) + " pts",
'shift_id': False,
'type': partner.shift_type,
'partner_id': partner.id,
'point_qty': points_update,
'technical_note': 'no trigger'
}
self.env['shift.counter.event'].create(data)
"""
unlink model: "shift.template.registration"
to delete all future shifts linked to this coop.
"""
now = datetime.datetime.now().isoformat()
for streg in self.env['shift.template.registration'].search([('partner_id', '=', partner.id)]):
streg.unlink()
for sreg in self.env['shift.registration']\
.search([('partner_id', '=', partner.id),
('date_begin', '>', now)]):
sreg.unlink()
# Close extensions
for ext in self.env['shift.extension']\
.search([('partner_id', '=', partner.id),
('date_start', '<=', now),
('date_stop', '>=', now)]):
ext.update({'date_stop': now})
try:
mail_template = self.env.ref('coop_membership.unsubscribe_email')
if mail_template:
mail_template.send_mail(partner.id)
else:
_logger.error("run_unsubscribe_process - mail_template not found")
except Exception as e:
_logger.error("run_unsubscribe_process - send mail : %s - Process interrupted", str(e))
partner.update(new_values)
except Exception as e:
_logger.error("run_unsubscribe_process : %s", str(e))
return None
return result
def _update_partner_makeups_to_do(self, vals):
if 'technical_note' in vals and vals['technical_note'] == 'no trigger':
return 0
try:
if 'point_qty' in vals and vals['point_qty'] < 0:
res_partner = self.env['res.partner'].search([('id', '=', vals['partner_id'])])
to_add = abs(vals['point_qty'])
if res_partner[0].makeups_to_add != 0:
to_add = res_partner[0].makeups_to_add
res_partner[0].update({'makeups_to_do': res_partner[0].makeups_to_do + to_add, 'makeups_to_add': 0})
return 0
except Exception as e:
_logger.error("_update_partner_makeups_to_do : %s", str(e))
return 1
def _update_partner_target_status(self, vals): def _update_partner_target_status(self, vals):
"""actions when points are removed or added""" """actions when points are removed or added"""
partner = None
try: try:
conf = self.env['ir.config_parameter'] conf = self.env['ir.config_parameter']
suspension_limit = int(conf.get_param("lacagette_membership.points_limit_to_get_suspended")) suspension_limit = int(conf.get_param("lacagette_membership.points_limit_to_get_suspended"))
unsubscribe_limit = int(conf.get_param("lacagette_membership.points_limit_to_get_unsubscribed")) unsubscribe_limit = int(conf.get_param("lacagette_membership.points_limit_to_get_unsubscribed"))
if vals['point_qty'] != 0: if 'point_qty' in vals and vals['point_qty'] != 0:
res_partner = self.env['res.partner'].search([('id', '=', vals['partner_id'])]) res_partner = self.env['res.partner'].search([('id', '=', vals['partner_id'])])
# to_consider_vals = self._alter_point_event_depending_on_makeups_total_count(res_partner, vals, suspension_limit)
# _logger.info("to_consider_vals : %s", str(to_consider_vals))
to_consider_vals = vals # TODO : Reimplement this adjustments
if res_partner: if res_partner:
p = res_partner[0] partner = res_partner[0]
points_before_removing_points = p.final_ftop_point if p.shift_type == "ftop" else p.final_standard_point _logger.info("partner = : %s, makeups_to_do : %s", str(partner), str(partner.makeups_to_do))
points_after_removal = points_before_removing_points + vals['point_qty'] if to_consider_vals['type'] != partner.shift_type:
_logger.info("points_after_removal = %s, current_state = %s", str(points_after_removal), p.cooperative_state) _logger.error("Counter shift type is not corresponding to partner shift_type : %s", str(to_consider_vals))
if points_after_removal <= suspension_limit or points_after_removal <= unsubscribe_limit: return None
# Do not continue if method has been triggered by a 'no trigger' created shift_counter_event (while executing unsubscribe_partner)
if 'technical_note' in to_consider_vals and to_consider_vals['technical_note'] == 'no trigger':
return None
points_after_computation = partner.final_ftop_point if partner.shift_type == "ftop" else partner.final_standard_point
target_status = None
if points_after_computation <= suspension_limit or points_after_computation <= unsubscribe_limit:
target_status = None target_status = None
if (points_after_removal <= suspension_limit if (points_after_computation <= suspension_limit
and and
p.cooperative_state != 'delay'): partner.cooperative_state != 'delay'):
target_status = 'suspended' target_status = 'suspended'
if points_after_removal <= unsubscribe_limit: if points_after_computation <= unsubscribe_limit:
target_status = 'unsubscribed' target_status = 'unsubscribed'
if target_status is not None: elif points_after_computation < 0:
p.update({'target_status': target_status, target_status = 'alert'
'current_cooperative_state': p.cooperative_state}) elif points_after_computation >= 0:
if partner.cooperative_state not in ['gone', 'associated', 'exempted', 'vacation']:
if partner.makeups_to_do == 0:
target_status = 'up_to_date'
else:
already_choosen_makeups_to_do = partner.get_makeups_choosen_shifts()
_logger.info("already_choosen_makeups_to_do : %s", str(already_choosen_makeups_to_do))
target_status = 'delay'
if target_status is not None:
vals_for_update = {'working_state': target_status, 'cooperative_state': target_status}
partner.update(vals_for_update)
if target_status == 'unsubscribed':
self.unsubscribe_partner(partner, unsubscribe_limit, points_after_computation)
except Exception as e: except Exception as e:
# don't block process if an error occurs here # don't block process if an error occurs here
_logger.error("Error during _update_partner_target_status : %s", str(e)) _logger.error("Error during _update_partner_target_status : %s", str(e))
@api.model
def affect_associated_people(self, parent_id):
associated = self.env['res.partner'].search([('parent_id', '=', parent_id)])
if associated:
for a in associated:
a.recompute_member_states()
@api.model @api.model
def write(self, vals): def write(self, vals):
self._alter_point_event_depending_on_makeups_total_count(vals) _logger.debug("Vals recues = %s", str(vals))
self._update_partner_makeups_to_do(vals)
res = super(ShiftCounterEvent, self).write(vals) res = super(ShiftCounterEvent, self).write(vals)
_logger.info("Vals recues = %s", str(vals))
self._update_partner_target_status(vals) self._update_partner_target_status(vals)
self.affect_associated_people(vals['partner_id'])
return res return res
@api.model @api.model
def create(self, vals): def create(self, vals):
self._alter_point_event_depending_on_makeups_total_count(vals) _logger.debug("Vals recues creation = %s", str(vals))
self._update_partner_makeups_to_do(vals)
res = super(ShiftCounterEvent, self).create(vals)
self._update_partner_target_status(vals) self._update_partner_target_status(vals)
_logger.info("Vals recues creation = %s", str(vals)) self.affect_associated_people(vals['partner_id'])
return super(ShiftCounterEvent, self).create(vals) return res
# -*- coding: utf-8 -*-
from openerp import _, api, models, fields
import logging
import datetime
_logger = logging.getLogger(__name__)
class ShiftExtension(models.Model):
_inherit = 'shift.extension'
@api.model
def _update_member_status(self, vals):
res_partner = self.env['res.partner'].search([('id', '=', vals['partner_id'])])
if res_partner:
res_partner[0].recompute_member_states()
@api.model
def write(self, vals):
_logger.debug("Shift extension Vals recues = %s", str(vals))
res = super(ShiftExtension, self).write(vals)
self._update_member_status(vals)
return res
@api.model
def create(self, vals):
_logger.debug("Shift extension Vals recues creation = %s", str(vals))
res = super(ShiftExtension, self).create(vals)
self._update_member_status(vals)
return res
...@@ -41,6 +41,5 @@ class ShiftRegistration(models.Model): ...@@ -41,6 +41,5 @@ class ShiftRegistration(models.Model):
# Missing a makeup leads to have an additional makeup (the shift you initialy missed + the makeup you missed) # Missing a makeup leads to have an additional makeup (the shift you initialy missed + the makeup you missed)
if s.is_makeup is True: if s.is_makeup is True:
to_add += 1 to_add += 1
new_makeups_to_do = s.partner_id.makeups_to_do + to_add s.partner_id.update({'makeups_to_add': to_add})
s.partner_id.update({'makeups_to_do': new_makeups_to_do}) return super(ShiftRegistration, self).write(vals)
return super(ShiftRegistration, self).write(vals)
\ No newline at end of file
# -*- coding: utf-8 -*-
from openerp import api, models
import logging
_logger = logging.getLogger(__name__)
class ShiftTemplateRegistration(models.Model):
_inherit = 'shift.template.registration'
@api.model
def create(self, vals):
super_res = super(ShiftTemplateRegistration, self.with_context(
dict(self.env.context, **{'creation_in_progress': True}))).create(vals)
if 'partner_id' in vals:
member = self.env['res.partner'].search([('id', '=', vals['partner_id'])])
member.recompute_member_states()
return super_res
...@@ -33,4 +33,6 @@ class ShiftTemplateRegistrationLine(models.Model): ...@@ -33,4 +33,6 @@ class ShiftTemplateRegistrationLine(models.Model):
coop_state = 'unsubscribed' coop_state = 'unsubscribed'
member.update({'cooperative_state': coop_state}) member.update({'cooperative_state': coop_state})
_logger.info("new cooperative_state = %s for %s", coop_state, str(member.id)) _logger.info("new cooperative_state = %s for %s", coop_state, str(member.id))
_logger.info("On va recalculer le statut du membre = %s", str(member.id))
member.recompute_member_states()
return super_res return super_res
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment