def perform(self, *args, **kwargs): """ Kwargs must match those set in parameter_keys and parameter_values for a rule This one written as proof of concept for tests, however, not successful in writing test due to cyclos dependency / interactions / groups etc. :param id: :param amount: :param sender: :return: """ LOG.debug(kwargs) amount = kwargs['amount'] sender_id = kwargs['sender_id'] LOG.info( u"Action PayCC3Profile perform called id {0} amount {1} sender_id {2}" .format(id, amount, sender_id)) user = User.objects.get(pk=id) cc3_profile = user.get_cc3_profile() sender_user = User.objects.get(pk=sender_id) sender_profile = sender_user.get_cc3_profile() try: backends.user_payment(sender_profile, cc3_profile, amount, "Payment by action") except Exception, e: LOG.error(u"Action PayCC3Profile failed with {0}".format(e)) return u"Action PayCC3Profile failed {0}".format(e)
def post(self, request, format=None): serializer = self.serializer_class(data=request.data, context={'user': request.user}) if serializer.is_valid(): # Hack to make sure we have a cyclos User an auth one otherwise # AdPaymentTransaction complains. sender = User.objects.get(pk=request.user.pk) amount = serializer.object['amount'] description = serializer.object['description'] receiver = serializer.object['recipient'].user # Make the payment. try: transaction = backends.user_payment(sender, receiver, amount, description) # Log the payment. AdPaymentTransaction.objects.create( title=description, amount=amount, sender=sender, receiver=receiver, transfer_id=transaction.transfer_id) except TransactionException: raise APITransactionException() return Response(request.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def _perform_payment(self, data): sender = self.request.user receiver = data['profile'].user amount = data['amount'] description_elements = [] reward_category = data.get('reward_category', None) reward_category_quantity = data.get('reward_category_quantity', None) other_activity = data.get('other_activity', None) other_activity_quantity = data.get('other_activity_quantity', None) bonus = data.get('bonus', None) if reward_category: description_elements.append( (reward_category, reward_category_quantity)) if other_activity: description_elements.append( (other_activity, other_activity_quantity)) if bonus: description_elements.append((u'bonus', bonus)) description = u"".join([ u"{0} ({1}) | ".format(description_element[0], description_element[1]) for description_element in description_elements ]) description = description[:-3] # knock off last pipe try: # Cyclos transaction request. transaction = backends.user_payment(sender, receiver, amount, description) # Log the payment Transaction.objects.create( amount=amount, sender=sender, receiver=receiver, transfer_id=transaction.transfer_id, ) messages.add_message(self.request, messages.SUCCESS, _('Payment made successfully.')) except TransactionException, e: error_message = _('Could not make payment at this time.') if 'NOT_ENOUGH_CREDITS' in e.args[0]: error_message = _('You do not have sufficient credit to ' 'complete the payment plus the ' 'transaction fee.') messages.add_message(self.request, messages.ERROR, error_message)
def pay_reward_with_cause_donation(amount, sender, payee, description, make_cause_donation=True): """Make the specified user payment, then (by default) make the appropriate good cause payment :param amount: Transaction amount (Python ``Decimal``). :param sender: auth User :param payee: auth User :param description: Mandatory description of the transaction. :param make_cause_donation: make percentage good cause donation :return: the amount paid """ transaction = None amount_paid = 0 try: transaction = backends.user_payment(sender.username, payee.username, amount, description.decode('utf-8')) amount_paid = amount except TransactionException as e: LOG.error(u'Unable to perform user payment of {1} to {2} ' u'transaction: {0}'.format(e, amount, payee.username)) else: try: # Log the payment Transaction.objects.create( amount=amount, sender=sender, receiver=payee, transfer_id=transaction.transfer_id, ) except Exception, e: # Report this to ADMINS, but treat as success # because the payment was made message = u'User payment made (transfer id={0}), but ' \ u'failed to create Transaction: {1}'.format( transaction.transfer_id, e) LOG.error(message) if not settings.DEBUG: mail_admins(u'Failed to log payment', message, fail_silently=True)
def test_user_payment_not_enough_credit(self): # create two users to transact between sender = get_username() member = backends.new(sender, 'Sender', sender + '@example.com', 'Test Business', USER_GROUP_ID) self.assertIsNotNone(member) receiver = get_username() member = backends.new(receiver, 'Receiver', receiver + '@example.com', 'Test Business', USER_GROUP_ID) self.assertIsNotNone(member) try: transaction = backends.user_payment(sender, receiver, 10.00, 'a test transaction') self.assertIsNotNone(transaction) self.assertTrue(False) except TransactionException as e: self.assertTrue(e.args[0].find('NOT_ENOUGH_CREDITS') >= 0)
def create_new_stadlander_profile(self, rel_number): """Create SL profile and make reward payment""" from ..stadlander.models import StadlanderProfile sl = StadlanderProfile.objects.create( rel_number=rel_number, profile=self, ) LOG.info("Created StadlanderProfile {0}".format(sl)) # reward with X punten - ticket #1513 # MUST ONLY HAPPEN when the profile is created for the first time. amount = settings.STADLANDER_SSO_REWARD_AMOUNT description = settings.STADLANDER_SSO_REWARD_DESCRIPTION sender = settings.STADLANDER_SSO_REWARD_SENDER # receiver was shadowing django.dispatch.receiver _receiver = self.user transaction = backends.user_payment(sender, _receiver, amount, description) # reward charity with percentage of the transaction. cause_reward(amount, _receiver, transaction.transfer_id)
def test_user_payment(self): # create two users to transact between sender = get_username() email = sender + '@example.com' member = backends.new(sender, 'Sender', email, 'Test Business', USER_GROUP_ID) self.assertIsNotNone(member) self._set_credit_limit(email, 10000.0) receiver = get_username() member = backends.new(receiver, 'Receiver', receiver + '@example.com', 'Test Business', USER_GROUP_ID) self.assertIsNotNone(member) amount = 10.00 description = 'a test transaction' transaction = backends.user_payment(sender, receiver, amount, description) self.assertEqual(transaction.sender, sender) self.assertEqual(transaction.recipient, receiver) self.assertEqual(transaction.amount, Decimal(amount)) self.assertEqual(transaction.description, description) self.assertIsInstance(transaction.created, datetime.datetime)
class PayStadlander(object): @staticmethod def perform(*args, **kwargs): # does an active SSO person exist for personnumber == relnumber? LOG.info(u"Action PayStadlander perform called with {0} ".format( jsonify(args, kwargs))) sso_profile = None try: sso_profile = StadlanderProfile.objects.get( rel_number=kwargs['persoonsnummer'], profile__user__is_active=True) except StadlanderProfile.DoesNotExist: LOG.error(u'PayStadlander failed. ' u'Persoonsnummer {0} does not have a StadlanderProfile.'. format(kwargs['persoonsnummer'])) kwargs['payment'] = \ u'PayStadlander action failed, Persoonsnummer {0} does not ' \ u'have a StadlanderProfile.'.format(kwargs['persoonsnummer']) kwargs['cause_payment'] = \ u'PayStadlander actionfailed, Persoonsnummer {0} does not ' \ u'have a StadlanderProfile.'.format(kwargs['persoonsnummer']) except Exception, e: LOG.error(e) kwargs['payment'] = \ u'PayStadlander action failed, Unexpected error {0}.'.format(e) kwargs['cause_payment'] = \ u'PayStadlander action failed, Unexpected error {0}.'.format(e) if sso_profile: description = kwargs.get('description', None) if not description: rule = Rule.objects.get(pk=kwargs['rule_id']) description = u"{0} ({1})".format(rule.description, rule.name) transaction = None amount = int(kwargs['amount']) receiver_user = sso_profile.profile.user receiver = receiver_user.username try: transaction = backends.user_payment(kwargs['sender'], receiver, amount, description) kwargs['payment'] = u'success' except TransactionException as e: LOG.warning(u'Unable to perform the stadlander reward ' u'transaction: {0}'.format(e)) kwargs['payment'] = u'failed {0}'.format(e) try: if transaction: cause_reward(amount, receiver_user, transaction.transfer_id) kwargs['cause_payment'] = u'success' else: LOG.error(u'Donation in action PayStadlander failed. ' u'Stadlander Reward payment failed.'.format( receiver_user)) kwargs['cause_payment'] = \ u'failed, User {0} Stadlander Reward payment ' \ u'failed.'.format(receiver_user) except Exception, e: LOG.error(u'Donation in action PayStadlander failed. ' u'User {0} is not committed with any cause.'.format( sso_profile.profile.user.pk)) kwargs['cause_payment'] = \ u'failed, User {0} is not committed with any ' \ u'cause.'.format(receiver_user)
def test_account_status(self): sender = get_username() email = sender + '@example.com' member = backends.new(sender, 'Sender', email, 'Test Business', USER_GROUP_ID) self.assertIsNotNone(member) receiver = get_username() member = backends.new(receiver, 'Receiver', receiver + '@example.com', 'Test Business', USER_GROUP_ID) self.assertIsNotNone(member) # test initial account status status = backends.get_account_status(sender) self.assertEqual(status.accountStatus.balance, Decimal('0')) self.assertEqual(status.accountStatus.availableBalance, Decimal('0')) self.assertEqual(status.accountStatus.reservedAmount, Decimal('0')) self.assertEqual(status.accountStatus.creditLimit, Decimal('0')) self.assertIsNone(status.accountStatus.upperCreditLimit) status = backends.get_account_status(receiver) self.assertEqual(status.accountStatus.balance, Decimal('0')) self.assertEqual(status.accountStatus.availableBalance, Decimal('0')) self.assertEqual(status.accountStatus.reservedAmount, Decimal('0')) self.assertEqual(status.accountStatus.creditLimit, Decimal('0')) self.assertIsNone(status.accountStatus.upperCreditLimit) # set credit limit limit = 10000.0 self._set_credit_limit(email, limit) status = backends.get_account_status(sender) self.assertEqual(status.accountStatus.balance, Decimal('0')) self.assertEqual(status.accountStatus.availableBalance, Decimal(limit)) self.assertEqual(status.accountStatus.reservedAmount, Decimal('0')) self.assertEqual(status.accountStatus.creditLimit, Decimal(limit)) self.assertIsNone(status.accountStatus.upperCreditLimit) # transact amount = 10.00 description = 'a test transaction' transaction = backends.user_payment(sender, receiver, amount, description) self.assertIsNotNone(transaction) status = backends.get_account_status(sender) self.assertEqual(status.accountStatus.balance, Decimal(amount * -1.0)) self.assertEqual(status.accountStatus.availableBalance, Decimal(limit - amount)) self.assertEqual(status.accountStatus.reservedAmount, Decimal('0')) self.assertEqual(status.accountStatus.creditLimit, Decimal(limit)) self.assertIsNone(status.accountStatus.upperCreditLimit) status = backends.get_account_status(receiver) self.assertEqual(status.accountStatus.balance, Decimal(amount)) self.assertEqual(status.accountStatus.availableBalance, Decimal(amount)) self.assertEqual(status.accountStatus.reservedAmount, Decimal('0')) self.assertEqual(status.accountStatus.creditLimit, Decimal('0')) self.assertIsNone(status.accountStatus.upperCreditLimit)
def process_csv(form_data, make_payments=False, sender=None): """ Process the csv file. Keep counts of valid and invalid rows, etc. If make_payments is True, actually perform the transactions; otherwise just return the stats as a dict """ num_rows = 0 num_valid_rows = 0 unique_users = {} # dict keyed by userID invalid_user_rows = [] # list of row numbers total_amount = 0 num_large_amounts = 0 amount_paid = 0 csv_filename = uploaded_file_to_filename(form_data['csv_file']) has_headers = form_data.get('has_headers', False) uid_type = form_data['uid_type'] uid_column = form_data['uid_column'] amount_column = form_data['amount_column'] description_column = form_data.get('description_column', None) #date_column = form_data.get('date_column', None) threshold_amount = form_data.get('threshold_amount', None) fixed_donation_percentage = form_data.get('donation_percent', None) if has_headers: skip_rows = 1 row_number_adjust = 2 else: row_number_adjust = 1 skip_rows = 0 for i, row in enumerate( read_csv(csv_filename, delimiters=UPLOAD_CSV_DELIMITERS, skip_rows=skip_rows)): num_rows += 1 uid = row[uid_column] amount = int(row[amount_column]) if description_column is not None: description = row[description_column] else: description = '' #if date_column is not None: # txn_date = row[date_column] #else: # txn_date = '' payee = get_user_from_uid(uid, uid_type) if not payee: invalid_user_rows.append(i + row_number_adjust) continue # valid user, so in theory transaction can be made unique_users[payee.id] = 1 total_amount += amount num_valid_rows += 1 if threshold_amount is not None and (amount > threshold_amount): num_large_amounts += 1 if make_payments: # actually make the payment, and the cause_reward percentage payment amount = int(amount) if not description: description = _(u"Sum earned by user, business or institution") transaction = None try: transaction = backends.user_payment( sender, payee.username, amount, description.decode('utf-8')) amount_paid += amount except TransactionException as e: LOG.error(u'Unable to perform reward payment of {1} to {2} ' u'transaction: {0}'.format(e, amount, payee.username)) else: try: # Log the payment Transaction.objects.create( amount=amount, sender=sender, receiver=payee, transfer_id=transaction.transfer_id, ) except Exception, e: # Report this to ADMINS, but treat as success # because the payment was made message = u'Reward payment made (transfer id={0}), but ' \ u'failed to create Transaction: {1}'.format( transaction.transfer_id, e) LOG.error(message) if not settings.DEBUG: mail_admins(u'Failed to log payment', message, fail_silently=True) if fixed_donation_percentage is None: description = None else: description = _(u"{0}% Cause donation (chosen by {1})").format( fixed_donation_percentage, sender.cc3_profile.business_name) if transaction: donation = cause_reward( amount, payee, transaction.transfer_id, description=description, fixed_donation_percentage=fixed_donation_percentage) # catches and logs errors if necessary if donation: try: # Log the payment Transaction.objects.create( amount=donation.amount, sender=donation.sender, receiver=donation.recipient, transfer_id=donation.transfer_id, ) except Exception, e: # Report this to ADMINS, but treat as success # because the payment was made message = u'Good Cause donation made (transfer ' \ u'id={0}), but failed to create ' \ u'Transaction: {1}'.format( donation.transfer_id, e) LOG.error(message) if not settings.DEBUG: mail_admins(u'Failed to log payment', message, fail_silently=True)
def cause_reward(amount, receiver, reward_transfer_id, description=None, fixed_donation_percentage=None): """ Performs the transaction between a consumer user and the charity cause of his choice, if any. Returns ``None`` if the user did not selected any cause to contribute with donations or if there was a problem with the transaction in the Cyclos backend. If the transaction succeeds, returns the Cyclos transaction ``namedtuple`` object. :param amount: an integer representing the amount which was rewarded to the consumer initially :param receiver: the consumer, this is, the initial receiver of the reward :param reward_transfer_id: the cyclos transfer ID of the initial reward transaction from the business to the consumer :param fixed_donation_percentage: if set, all users pay that percentage; if None, use the user's own percentage, or fall back to the community default :return: the succeeded Cyclos ``Transaction`` ``namedtuple`` object or None """ try: user_cause = UserCause.objects.get(consumer=receiver) cause = user_cause.cause except UserCause.DoesNotExist: # use the default good cause if one hasn't been set for a user LOG.error(u'Donation in transaction {0} failed. User {1} is not ' u'committed with any cause.'.format(reward_transfer_id, receiver.pk)) return None # user = User.objects.get(pk=receiver.pk) # cc3_community = user.cc3_profile.community # cause = DefaultGoodCause.objects.get( # community=cc3_community).cause # Apply donation percentage: # If fixed_donation_percentage supplied, use that for everyone; if None, # use the user-specific value from user_cause, falling back to community # default if fixed_donation_percentage is not None: percent = fixed_donation_percentage else: percent = user_cause.donation_percent if percent is None: percent = receiver.get_community().default_donation_percent if percent: donation = int(amount * percent / 100) # The receiver of the initial payment now donates the specified amount # to cause. # construct custom fields containing the transfer id of the # originating reward payment custom_fields = { "originating_transfer_id": reward_transfer_id, } try: transaction = backends.user_payment(receiver, cause, donation, description or _('Cause donation'), custom_fields=custom_fields) except TransactionException as e: LOG.warning(u'Unable to perform the donation ' u'transaction: {0}'.format(e)) return None return transaction if percent == 0: # Legitimate zero percent configured, no donation to pay LOG.info(u"No donation for transaction {0}: donation percentage " u"is zero for User {1}".format(reward_transfer_id, receiver.pk)) else: LOG.error(u'Donation in transaction {0} failed. User {1} does not ' u'have a community'.format(reward_transfer_id, receiver.pk)) return None