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)
示例#2
0
    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)
示例#3
0
    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)
示例#4
0
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)
示例#6
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)
示例#8
0
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)
示例#10
0
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