コード例 #1
0
    def post(self, request, *args, **kwargs):
        transaction = self.get_object(**kwargs)
        requested_action = kwargs.get('requested_action')

        if requested_action not in self.allowed_actions:
            error_message = "{} is not an allowed".format(requested_action)
            return Response({"errors": error_message},
                            status=status.HTTP_400_BAD_REQUEST)

        action_to_execute = getattr(transaction, requested_action, None)
        if not action_to_execute:
            raise Http404

        try:
            errors = action_to_execute()
            transaction.save()
        except TransitionNotAllowed:
            errors = "Can't execute action because the transaction is in an " \
                     "incorrect state: {}".format(transaction.state)

        if errors:
            return Response({"errors": errors},
                            status=status.HTTP_400_BAD_REQUEST)

        transaction_serialized = TransactionSerializer(
            transaction, context={'request': request})
        return Response(transaction_serialized.data, status=status.HTTP_200_OK)
コード例 #2
0
    def get_transactions(self, document):
        if document.kind == 'invoice':
            transactions = document.invoice_transactions.all()
        elif document.kind == 'proforma':
            transactions = document.proforma_transactions.all()
        else:
            return []

        for transaction in transactions:
            # This is done to avoid prefetching already prefetched resources
            transaction.payment_method.customer = document.customer
            transaction.provider = document.provider

        return TransactionSerializer(transactions, many=True,
                                     context=self.context).data
コード例 #3
0
class ProformaSerializer(AutoCleanSerializerMixin,
                         serializers.HyperlinkedModelSerializer):
    proforma_entries = DocumentEntrySerializer(many=True, required=False)
    pdf_url = PDFUrl(view_name='pdf', source='*', read_only=True)
    customer = CustomerUrl(view_name='customer-detail',
                           queryset=Customer.objects.all())
    transactions = TransactionSerializer(many=True, read_only=True)

    class Meta:
        model = Proforma
        fields = ('id', 'series', 'number', 'provider', 'customer',
                  'archived_provider', 'archived_customer', 'due_date',
                  'issue_date', 'paid_date', 'cancel_date', 'sales_tax_name',
                  'sales_tax_percent', 'currency', 'transaction_currency',
                  'transaction_xe_rate', 'transaction_xe_date', 'state',
                  'invoice', 'proforma_entries', 'total',
                  'total_in_transaction_currency', 'pdf_url', 'transactions')
        read_only_fields = ('archived_provider', 'archived_customer', 'total',
                            'total_in_transaction_currency')
        extra_kwargs = {
            'transaction_currency': {
                'required': False
            },
            'number': {
                'required': False
            },
            'series': {
                'required': False
            },
            'invoice': {
                'source': 'related_document',
                'view_name': 'invoice-detail'
            },
        }

    def create(self, validated_data):
        entries = validated_data.pop('proforma_entries', [])

        proforma = Proforma.objects.create(**validated_data)

        for entry in entries:
            entry_dict = dict()
            entry_dict['proforma'] = proforma
            for field in entry.items():
                entry_dict[field[0]] = field[1]

            DocumentEntry.objects.create(**entry_dict)

        return proforma

    def update(self, instance, validated_data):
        # The provider has changed => force the generation of the correct number
        # corresponding to the count of the new provider
        current_provider = instance.provider
        new_provider = validated_data.get('provider')
        if new_provider and new_provider != current_provider:
            instance.number = None

        updateable_fields = instance.updateable_fields
        for field_name in updateable_fields:
            field_value = validated_data.get(field_name,
                                             getattr(instance, field_name))
            setattr(instance, field_name, field_value)
        instance.save()

        return instance

    def instantiate_object(self, data):
        proforma = super(ProformaSerializer, self).instantiate_object(data)
        # after clean_defaults is moved into full_clean this call won't be needed
        proforma.clean_defaults()

        return proforma

    def validate(self, data):
        data = super(ProformaSerializer, self).validate(data)

        if self.instance and 'state' in data and data[
                'state'] != self.instance.state:
            msg = "Direct state modification is not allowed." \
                  " Use the corresponding endpoint to update the state."
            raise serializers.ValidationError(msg)
        return data
コード例 #4
0
class InvoiceSerializer(serializers.HyperlinkedModelSerializer):
    invoice_entries = DocumentEntrySerializer(many=True)
    pdf_url = PDFUrl(view_name='pdf', source='*', read_only=True)
    customer = CustomerPrimaryKey(queryset=Customer.objects.all())
    provider = ProviderPrimaryKey(queryset=Provider.objects.all())
    transactions = TransactionSerializer(many=True, read_only=True)

    class Meta:
        model = Invoice
        fields = ('id', 'series', 'number', 'provider', 'customer',
                  'archived_provider', 'archived_customer', 'due_date',
                  'issue_date', 'paid_date', 'cancel_date', 'sales_tax_name',
                  'sales_tax_percent', 'currency', 'transaction_currency',
                  'transaction_xe_rate', 'transaction_xe_date', 'state',
                  'proforma', 'invoice_entries', 'total',
                  'total_in_transaction_currency', 'pdf_url', 'transactions')
        read_only_fields = ('archived_provider', 'archived_customer', 'total',
                            'total_in_transaction_currency')
        extra_kwargs = {
            'transaction_currency': {
                'required': False
            },
            'proforma': {
                'source': 'related_document',
                'view_name': 'proforma-detail'
            }
        }

    def create(self, validated_data):
        entries = validated_data.pop('invoice_entries', None)

        # Create the new invoice object
        invoice = Invoice.objects.create(**validated_data)

        # Add the invoice entries
        for entry in entries:
            entry_dict = dict()
            entry_dict['invoice'] = invoice
            for field in entry.items():
                entry_dict[field[0]] = field[1]

            DocumentEntry.objects.create(**entry_dict)

        return invoice

    def update(self, instance, validated_data):
        # The provider has changed => force the generation of the correct number
        # corresponding to the count of the new provider
        current_provider = instance.provider
        new_provider = validated_data.get('provider')
        if new_provider and new_provider != current_provider:
            instance.number = None

        updateable_fields = instance.updateable_fields
        for field_name in updateable_fields:
            field_value = validated_data.get(field_name,
                                             getattr(instance, field_name))
            setattr(instance, field_name, field_value)
        instance.save()

        return instance

    def validate(self, data):
        data = super(InvoiceSerializer, self).validate(data)

        if self.instance:
            self.instance.clean()

        if self.instance and data['state'] != self.instance.state:
            msg = "Direct state modification is not allowed." \
                  " Use the corresponding endpoint to update the state."
            raise serializers.ValidationError(msg)
        return data
コード例 #5
0
    def post(self, request, *args, **kwargs):
        """ Create a one off transaction by way of creating an invoice,
        payment method and document, and return the created transaction.
        """

        rq = request.data

        customer_one_off_defaults = {
            "currency": "USD",
        }

        ## Customer creation
        #
        # Check to see if we're getting an `account_id`, and try to
        # retrieve that customer first.
        #
        customer = None
        has_uuid = rq.get('customer', {}).get('account_id', False)

        if has_uuid:
            _u = uuid.UUID(has_uuid)
            # This will raise Customer.DoesNotExist if an invalid UUID is given
            customer = Customer.objects.get(account_id=_u)

        # If we have no customer and no exceptions were raised, create a
        # new customer
        #
        if customer == None:
            new_customer = customer_one_off_defaults
            new_customer.update(**rq.get('customer'))
            customer = Customer(**new_customer)
            customer.save()

        ## Create a customer payment method
        #
        payment_processor_name = rq.get("payment_processor", "manual")

        # Check if a method for the customer with this payment_processor
        # exists already
        #
        try:
            has_method = PaymentMethod.objects.get(
                customer=customer, payment_processor=payment_processor_name)
        except PaymentMethod.DoesNotExist:
            has_method = False

        # Create a new method
        #
        if not has_method:
            # TODO: what are our sensible defaults?
            customer_default_payment_method = {
                "customer":
                customer,
                "payment_processor":
                payment_processor_name,
                "verified":
                True,
                "canceled":
                False,
                "valid_until":
                dt.now() + timedelta(days=7),
                "display_info":
                "pytest",
                "data":
                json.dumps({
                    "attempt_retries_after": 2,
                    "stop_retry_attempts": 5
                })
            }
            new_pm = PaymentMethod(**customer_default_payment_method)
            new_pm.save()
        else:
            new_pm = has_method

        ## Get a provider

        # First we'll try to get our internal provider, otherwise create
        # it if it doesn't exist.
        #
        # TODO: add a request parameter for the provider name or
        # something.
        #
        provider = Provider.objects.filter(
            invoice_series="BPInvoiceSeries").first()
        if provider is None:
            prv = {
                "name": "Internal Billing Provider",
                "company": "Internal Billing Provider",
                "invoice_series": "BPInvoiceSeries",
                "flow": "invoice",
                "email": "",
                "address_1": "1 Mulberry Lane",
                "address_2": "",
                "city": "Pacoima",
                "state": "CA",
                "zip_code": "",
                "country": "US",
                "invoice_starting_number": 1
            }
            provider = Provider(**prv)
            provider.save()

        ## Create an invoice

        # Some defaults to save effort from the client user
        #
        invoice_one_off_defaults = {
            "provider": provider,
            "series": provider.invoice_series,
            "customer": customer,
            "transaction_currency": "USD",
            "transaction_xe_rate": Decimal('1.0000'),
            "currency": "USD",
            "state": "draft",
        }

        invoice_entry_defaults = {
            "quantity": 1.0,
            "unit_price": rq.get("amount", 0.0),
            "start_date": None,
            "end_date": None,
            "prorated": False,
            "product_code": None
        }

        # Override these with any request data
        #
        new_entry = invoice_entry_defaults.copy()
        new_invoice = invoice_one_off_defaults.copy()
        new_invoice.update(**rq.get("invoice", {}))
        if 'issue_date' in new_invoice:
            new_invoice['issue_date'] = dtparse(
                new_invoice.get('issue_date')).date()

        # Create the invoice
        inv = Invoice(**new_invoice)
        inv.save()

        # Add the entry (save first, or else)
        entr = DocumentEntry(**new_entry)
        entr.save()
        inv.invoice_entries.add(entr)
        inv.save()

        # Issue the invoice to generate transactions.
        inv.issue()
        inv.save()

        transaction = Transaction.objects.filter(invoice=inv).first()

        _ser_kwargs = {"context": {"request": request}}

        return Response({
            "customer":
            CustomerSerializer(customer, **_ser_kwargs).data,
            "payment_method":
            PaymentMethodSerializer(new_pm, **_ser_kwargs).data,
            "provider":
            ProviderSerializer(provider, **_ser_kwargs).data,
            "invoice":
            InvoiceSerializer(inv, **_ser_kwargs).data,
            "transaction":
            TransactionSerializer(transaction, **_ser_kwargs).data,
        })