def create(self, validated_data): # prepare data kwargs = {} user = self.context.get("request").user company = user.operator_set.all()[0].company security = Security.objects.get( company=company, title=validated_data.get("security").get('title')) kwargs.update({ "company": company, "board_approved_at": validated_data.get("board_approved_at"), "title": validated_data.get("title"), "security": security, "count": validated_data.get("count"), "exercise_price": validated_data.get("exercise_price"), "comment": validated_data.get("comment"), }) # segments must be ordered, have no duplicates and must be list... if security.track_numbers and validated_data.get("number_segments"): kwargs.update({ "number_segments": string_list_to_json(validated_data.get("number_segments")) }) option_plan = OptionPlan.objects.create(**kwargs) # assign all initial options to company shareholder cs = company.get_company_shareholder() kwargs = { 'option_plan': option_plan, 'bought_at': validated_data.get("board_approved_at"), 'buyer': cs, 'count': validated_data.get("count") } # segments must be ordered, have no duplicates and must be list... if security.track_numbers and validated_data.get("number_segments"): kwargs.update({ "number_segments": string_list_to_json(validated_data.get("number_segments")) }) OptionTransaction.objects.create(**kwargs) return option_plan
def test_string_list_to_json(self): with self.assertRaises(ValueError): string_list_to_json('[]') string_list_to_json('1,2,3,4--10') string_list_to_json('1,,2,3,4-10,11-12X') self.assertEqual(string_list_to_json('1,2,3,4-10'), [u'1-10']) self.assertEqual(string_list_to_json('1,2,3,,4-10'), [u'1-10']) # test removal of duplicates and that is ordered self.assertEqual(string_list_to_json('0, 3,,, 3, 5-10, 9-12, 2'), [0, u'2-3', u'5-12'])
def owns_segments(self, segments, security): """ check if shareholder owns all those segments either as share does not check any kind of options. use owns_options_segments for this """ logger.info('checking if {} owns {}'.format(self, segments)) if isinstance(segments, str): segments = string_list_to_json(segments) logger.info('converted string to json') logger.info('getting current segments...') segments_owning = inflate_segments( self.current_segments(security=security)) failed_segments = [] logger.info('calculating segments not owning...') # shareholder does not own this failed_segments = substract_list(inflate_segments(segments), segments_owning) logger.info('check segment ownership done') return (len(failed_segments) == 0, deflate_segments(failed_segments), deflate_segments(segments_owning))
def is_valid(self, raise_exception=False): """ validate cross data relations """ res = super(OptionPlanSerializer, self).is_valid(raise_exception) initial_data = self.initial_data security = initial_data.get('security') if security and Security.objects.get(id=security['pk']).track_numbers: security = Security.objects.get(id=security['pk']) if (isinstance(initial_data.get('number_segments'), str) or isinstance(initial_data.get('number_segments'), unicode)): segments = string_list_to_json( initial_data.get('number_segments')) else: segments = initial_data.get('number_segments') # we need number_segments if this is a security with .track_numbers if not segments: raise serializers.ValidationError({ 'number_segments': [_('Invalid or empty security numbers segments.')] }) # segments must be available by company shareholder cs = security.company.get_company_shareholder() owning, failed_segments, owned_segments = cs.owns_segments( segments, security) # segments must be owned by seller if not owning: raise serializers.ValidationError({ 'number_segments': [ _('Segments "{}" must be owned by company shareholder ' '"{}". Available are {}').format( failed_segments, cs.user.last_name, owned_segments) ] }) # validate segment count == share count elif (security.count_in_segments(segments) != initial_data.get('count')): raise serializers.ValidationError({ 'count': [ _('Number of shares in segments ({}) ' 'does not match count {}').format( security.count_in_segments(segments), initial_data.get('count')) ] }) return res
def owns_options_segments(self, segments, security): """ check if shareholder owns all those segments either as share """ if isinstance(segments, str): segments = string_list_to_json(segments) segments_owning = inflate_segments( self.current_options_segments(security=security)) failed_segments = [] for segment in inflate_segments(segments): # shareholder does not own this if segment not in segments_owning: failed_segments.append(segment) return (len(failed_segments) == 0, deflate_segments(failed_segments), deflate_segments(segments_owning))
def count_in_segments(self, segments=None): """ returns number of shares contained in segments """ if not segments: segments = self.number_segments if isinstance(segments, str) or isinstance(segments, unicode): segments = string_list_to_json(segments) count = 0 for segment in segments: if isinstance(segment, int): count += 1 else: start, end = segment.split('-') # 3-5 means 3,4,5 means 3 shares delta = int(end) - int(start) + 1 count += delta return count
def validate_number_segments(self, value): # FIXME: this regex is weak because it only checks chars but not order # making string like '--00--' valid pattern = re.compile(r'[^0-9,\- ]') try: if isinstance(value, unicode): value = string_list_to_json(value) except ValueError: raise serializers.ValidationError( _("Invalid number segment. " "Please use 1, 2, 3, 4-10.") ) logger.warning("Invalid number segment: {}".format(value)) for part in value: # validate value as we have to track value... if pattern.findall(str(part)): raise serializers.ValidationError( _("Invalid number segment. " "Please use 1, 2, 3, 4-10.") ) logger.warning("Invalid number segment: {}".format(part)) if isinstance(part, int): continue # --- VALIDATE u'X-Z' only # validate that start and end of u'1-9' are valid start, end = part.split('-') if int(start) >= int(end): raise serializers.ValidationError( _("Number Segment Range start smaller/equal then end '{}'. " "Please use 1, 2, 3, 4-10.".format(part)) ) logger.warning("Invalid number segment: {}".format(part)) return value
def create(self, validated_data): # prepare data kwargs = {} user = self.context.get("request").user company = user.operator_set.all()[0].company option_plan = validated_data.get('option_plan') buyer = Shareholder.objects.get( company=company, user__email=validated_data.get("buyer").get("user").get("email")) seller = Shareholder.objects.get( company=company, user__email=validated_data.get("seller").get("user").get("email")) kwargs.update({"seller": seller}) kwargs.update({"buyer": buyer}) kwargs.update({ "bought_at": validated_data.get("bought_at"), "count": validated_data.get("count"), "option_plan": option_plan, "vesting_months": validated_data.get("vesting_months"), }) # segments must be ordered, have no duplicates and must be list... if (option_plan.security.track_numbers and validated_data.get("number_segments")): kwargs.update({ "number_segments": string_list_to_json(validated_data.get("number_segments")) }) option_transaction = OptionTransaction.objects.create(**kwargs) return option_transaction
def is_valid(self, raise_exception=False): """ validate cross data relations """ res = super(OptionTransactionSerializer, self).is_valid(raise_exception) initial_data = self.initial_data option_plan = OptionPlan.objects.get( id=int(initial_data.get('option_plan').split('/')[-1])) security = option_plan.security if not security.track_numbers: return res if (isinstance(initial_data.get('number_segments'), str) or isinstance(initial_data.get('number_segments'), unicode)): segments = string_list_to_json(initial_data.get('number_segments')) else: segments = initial_data.get('number_segments') # we need number_segments if this is a security with .track_numbers if not segments: raise serializers.ValidationError( {'number_segments': [_('Invalid security numbers segments.')]}) # if we have seller (non capital increase) if initial_data.get('seller'): seller = Shareholder.objects.get( pk=initial_data.get('seller')['pk']) owning, failed_segments, owned_segments = seller.\ owns_options_segments(segments, security) # segments must be owned by seller if initial_data.get('seller') and not owning: raise serializers.ValidationError({ 'number_segments': [ _('Segments "{}" must be owned by seller "{}". ' 'Available are {}').format(failed_segments, seller.user.last_name, owned_segments) ] }) # validate segment count == share count elif (security.count_in_segments(segments) != initial_data.get('count')): raise serializers.ValidationError({ 'count': [ _('Number of shares in segments ({}) ' 'does not match count {}').format( security.count_in_segments(segments), initial_data.get('count')) ] }) # segments must be inside option plans segments for segment in inflate_segments(segments): if segment not in inflate_segments(option_plan.number_segments): raise serializers.ValidationError({ 'number_segments': [ _('Segment {} is not reserved for options inside a' ' option plan and cannot be' ' transfered to a option holder. Available are: ' '{}').format(segment, option_plan.number_segments) ] }) return res
def is_valid(self, raise_exception=False): """ validate cross data relations """ logger.info('position create validation...') res = super(PositionSerializer, self).is_valid(raise_exception) initial_data = self.initial_data security = initial_data.get('security') user = self.context.get("request").user company = user.operator_set.all()[0].company if security and Security.objects.get( company=company, title=security.get('title')).track_numbers: logger.info('validation: prepare data...') security = Security.objects.get(id=security['pk']) if (isinstance(initial_data.get('number_segments'), str) or isinstance(initial_data.get('number_segments'), unicode)): segments = string_list_to_json( initial_data.get('number_segments')) else: segments = initial_data.get('number_segments') # if we have seller (non capital increase) if initial_data.get('seller'): logger.info('validation: get seller segments...') seller = Shareholder.objects.get( pk=initial_data.get('seller')['pk']) owning, failed_segments, owned_segments = seller.owns_segments( segments, security) logger.info( 'validation: seller segs {} for security {} done'.format( segments, security)) # we need number_segments if this is a security with .track_numbers if not segments: raise serializers.ValidationError({ 'number_segments': [_('Invalid security numbers segments.')] }) # segments must be owned by seller elif initial_data.get('seller') and not owning: raise serializers.ValidationError({ 'number_segments': [ _('Segments "{}" must be owned by seller "{}". ' 'Available are {}').format(failed_segments, seller.user.last_name, owned_segments) ] }) # validate segment count == share count elif (security.count_in_segments(segments) != initial_data.get('count')): logger.info('validation: checking count...') raise serializers.ValidationError({ 'count': [ _('Number of shares in segments ({}) ' 'does not match count {}').format( security.count_in_segments(segments), initial_data.get('count')) ] }) # segment must not be used by option plan logger.info('validation: option plan validation...') inflated_segments = inflate_segments(segments) oplan_segments = inflate_segments( security.company.get_all_option_plan_segments()) if substract_list(inflated_segments, oplan_segments) != inflated_segments: raise serializers.ValidationError({ 'number_segments': [ _('Segment {} is blocked for options and cannot be' ' transfered to a shareholder.').format(segments) ] }) return res