Ejemplo n.º 1
0
class TaskDashboardSerializer(TaskSerializer):
    answers_result_count = serializers.IntegerField(read_only=True)
    answers = AnswerSerializer(many=True)
    tx_hash = serializers.CharField(source='initial_tx_hash', required=False)
    type = serializers.CharField(read_only=True)
    reward_per_click = MoneyField(max_digits=11, decimal_places=5)
    remaining_balance = MoneyField(max_digits=9,
                                   decimal_places=3,
                                   required=False)
    initial_budget = MoneyField(max_digits=9, decimal_places=3, required=False)

    class Meta:
        model = models.Task
        fields = [
            'id',
            'title',
            'description',
            'chain',
            'user',
            'og_image_link',
            'uuid',
            'website_link',
            'reward_per_click',
            'reward_usd_per_click',
            'time_duration',
            'questions',
            'answers_result_count',
            'answers',
            'remaining_balance',
            'initial_budget',
            'tx_hash',
            'type',
        ]
Ejemplo n.º 2
0
class PriceSerializer(ModelCleanFieldsSerializer):
    """The price serializer."""

    service = AccountServiceForeignKey()
    price = MoneyField(
        max_digits=settings.D8B_MONEY_MAX_DIGITS,
        decimal_places=settings.D8B_MONEY_DECIMAL_PLACES,
        required=False,
        allow_null=True,
    )
    start_price = MoneyField(
        max_digits=settings.D8B_MONEY_MAX_DIGITS,
        decimal_places=settings.D8B_MONEY_DECIMAL_PLACES,
        required=False,
        allow_null=True,
    )
    end_price = MoneyField(
        max_digits=settings.D8B_MONEY_MAX_DIGITS,
        decimal_places=settings.D8B_MONEY_DECIMAL_PLACES,
        required=False,
        allow_null=True,
    )

    class Meta:
        """The metainformation."""

        model = Price
        fields = ("id", "service", "price", "price_currency", "start_price",
                  "start_price_currency", "end_price", "end_price_currency",
                  "is_price_fixed", "payment_methods", "modified",
                  "created_by", "modified_by")
        read_only_fields = ("created", "modified", "created_by", "modified_by")
        extra_kwargs = {"is_price_fixed": {"required": True}}
Ejemplo n.º 3
0
class LotDetailSerializer(BaseLotSerializer):
    bids = serializers.SerializerMethodField()
    highest_bid = serializers.SerializerMethodField()
    highest_bid_price = MoneyField(source="highest_bid.price",
                                   max_digits=10,
                                   decimal_places=2)

    class Meta:
        model = BaseLotSerializer.Meta.model
        read_only_fields = BaseLotSerializer.Meta.read_only_fields + (
            "bids",
            "highest_bid",
            "highest_bid_price",
            "modified_at",
        )
        fields = read_only_fields + BaseLotSerializer.Meta.fields + (
            "description", )

    def _get_highest_bid(self, object) -> Optional[Bid]:
        return object.highest_bid

    def get_highest_bid(self, object) -> str:
        highest_bid = self._get_highest_bid(object)
        view_name = "api:auction:v1:bids:retrieve"
        path = reverse(view_name,
                       kwargs={"bid_public_id": highest_bid.public_id})
        url = self.context["request"].build_absolute_uri(path)
        return url

    def get_bids(self, object) -> str:
        view_name = "api:auction:v1:lot:bid_history"
        path = reverse(view_name, kwargs={"lot_public_id": object.public_id})
        url = self.context["request"].build_absolute_uri(path)
        return url
Ejemplo n.º 4
0
class PrepaymentSerializer(serializers.ModelSerializer):
    created_by = UsernameSerializer(read_only=True)
    updated_by = UsernameSerializer(read_only=True)
    value = MoneyField(max_digits=14, decimal_places=2, required=True)

    class Meta:
        model = Prepayment
        fields = (
            'id',
            'record',
            'created_at',
            'created_by',
            'updated_at',
            'updated_by',
            'value',
        )
        read_only_fields = ('created_at', 'created_by', 'updated_at',
                            'updated_by')

    def update(self, instance, validated_data):
        validated_data['updated_by'] = self.context['request'].user
        validated_data['updated_at'] = timezone.now()
        return super(PrepaymentSerializer,
                     self).update(instance, validated_data)

    def create(self, validated_data):
        validated_data['created_by'] = self.context['request'].user
        return Prepayment.create_payment(**validated_data)
            def __new__(cls, name, bases, attrs):
                from djmoney.contrib.django_rest_framework import MoneyField

                if field_name is not None and field_kwargs is not None:
                    attrs[field_name] = MoneyField(max_digits=10,
                                                   decimal_places=2,
                                                   **field_kwargs)
                return super().__new__(cls, name, bases, attrs)
Ejemplo n.º 6
0
class ProductModelSerializer(ModelSerializer):
    created_at = serializers.ReadOnlyField()
    updated_at = serializers.ReadOnlyField()
    price = MoneyField(max_digits=10, decimal_places=2)

    class Meta:
        model = Product
        fields = '__all__'
Ejemplo n.º 7
0
class OrderProductSerializer(ModelSerializer):
    product_price = MoneyField(max_digits=10, decimal_places=2)
    product_name = serializers.CharField(source='product.name')
    product_quantity = serializers.IntegerField()

    class Meta:
        model = OrderProduct
        fields = ('product_name', 'product_price', 'product_quantity')
Ejemplo n.º 8
0
class InvoiceSerializer(serializers.HyperlinkedModelSerializer):
    lnnode_id = serializers.StringRelatedField(read_only=True,
                                               source='object_id')

    tax_currency_ex_rate = MoneyField(max_digits=10, decimal_places=2)
    info_currency_ex_rate = MoneyField(max_digits=10, decimal_places=2)

    price_in_tax_currency = serializers.CharField()
    tax_in_tax_currency = serializers.CharField()
    price_in_info_currency = serializers.CharField()

    class Meta:
        model = Invoice
        exclude = (
            'content_type',
            'object_id',
            'preimage'  # never reveal the preimage!
        )
Ejemplo n.º 9
0
class JobDetailSerializer(serializers.ModelSerializer):
    price = MoneyField(
        max_digits=14,
        decimal_places=2,
    )
    author = UserPublicSerializer(read_only=True)

    class Meta:
        model = Job
        fields = '__all__'
Ejemplo n.º 10
0
class JobListSerializer(serializers.ModelSerializer):
    price = MoneyField(
        max_digits=14,
        decimal_places=2,
    )
    author = UserPublicSerializer(read_only=True)

    class Meta:
        model = Job
        fields = ('id', 'title', 'description', 'author', 'price',
                  'price_currency', 'is_price_fixed', 'views', 'deadline')
Ejemplo n.º 11
0
class ProductSerializer(rest.TransformDatesMixin, serializers.ModelSerializer):
    show_name = serializers.SerializerMethodField()

    price_purchase = MoneyField(max_digits=10, decimal_places=2)
    price_selling = MoneyField(max_digits=10, decimal_places=2)
    price_selling_alt = MoneyField(max_digits=10, decimal_places=2)

    price_purchase_ex = MoneyField(max_digits=10, decimal_places=2)
    price_selling_ex = MoneyField(max_digits=10, decimal_places=2)
    price_selling_alt_ex = MoneyField(max_digits=10, decimal_places=2)

    image = Base64ImageField(required=False)

    def get_show_name(self, obj):
        if hasattr(obj, 'show_name'):
            return obj.show_name()

        return ''

    class Meta:
        model = models.Product
        fields = ('id', 'identifier', 'show_name', 'name', 'name_short',
                  'unit', 'supplier', 'product_type', 'modified',
                  'price_purchase', 'price_selling', 'price_selling_alt',
                  'price_purchase_ex', 'price_selling_ex',
                  'price_selling_alt_ex', 'image')
Ejemplo n.º 12
0
class BookingSerializer(ModelSerializer):

    amusement_park_prebookings = PrimaryKeyRelatedField(
        many=True,
        queryset=AmusementParkPreBooking.objects.all(),
    )
    museum_prebookings = PrimaryKeyRelatedField(
        many=True,
        queryset=MuseumPreBooking.objects.all(),
    )
    restaurant_prebookings = PrimaryKeyRelatedField(
        many=True,
        queryset=RestaurantPreBooking.objects.all()
    )
    payment_card_token = CharField(write_only=True)
    total = MoneyField(max_digits=6, decimal_places=2, read_only=True)
    reference = CharField(read_only=True)

    @staticmethod
    def validate_at_least_one_prebooking(data):
        keys = (
            'amusement_park_prebookings',
            'museum_prebookings',
            'restaurant_prebookings',
        )

        if not any(data[key] for key in keys):
            raise ValidationError('At least one prebooking is required')
        return data

    def validate(self, data):
        data = super().validate(data)
        data = self.validate_at_least_one_prebooking(data)
        return data

    def create(self, validated_data) -> Booking:
        booking: Booking = super().create(validated_data)
        return booking

    class Meta:
        model = Booking
        read_only_fields = (
            'total',
            'reference',
        )
        fields = read_only_fields + (
            'amusement_park_prebookings',
            'museum_prebookings',
            'restaurant_prebookings',
            'payment_card_token',
        )
Ejemplo n.º 13
0
class EmployeeRecordPaymentSerializer(serializers.ModelSerializer):

    employee = ProfileUsernameSerializer(read_only=True)
    amount = MoneyField(max_digits=14, decimal_places=2, read_only=True)

    class Meta:
        model = EmployeeRecordPayment
        fields = (
            'id',
            'type',
            'employee',
            'amount',
        )
        read_only_fields = fields
Ejemplo n.º 14
0
class CrateSerializer(serializers.ModelSerializer):
    crate_price = MoneyField(max_digits=10, decimal_places=2)
    bottle_price = MoneyField(max_digits=10, decimal_places=2, read_only=True)

    class Meta:
        model = Crate
        fields = (
            "id",
            "billed",
            "billed_at",
            "billed_document",
            "created_at",
            "updated_at",
            "name",
            "crate_price",
            "bottle_price",
            "bottles",
            "calories",
            "bottle_contents",
            "bottle_calories",
        )

        extra_kwargs = map_kwargs("billed", read_only=True)
Ejemplo n.º 15
0
class BookingSerializer(serializers.ModelSerializer):
    user_id = serializers.IntegerField()
    service = serializers.CharField()
    price = MoneyField(max_digits=14, decimal_places=2)
    date = serializers.DateField()
    time = serializers.CharField()

    def create(self, validated_data):
        # instance = self.Meta.model(**validated_data)
        print(validated_data)
        user = User.objects.get(id=validated_data['user_id'])
        service = ServiceNail.objects.get(service_ID=validated_data['service'])
        time = Slots.objects.get(slots_ID=validated_data['time'])
        profile = Profile.objects.get(user=user)
        #print(user)
        #print(profile.home_address)
        # print(profile.contact)
        time.is_booked = True
        time.save()

        instance = Booking.objects.create(user=user,
                                          price=validated_data['price'],
                                          date=validated_data['date'],
                                          service=service,
                                          time=time)
        instance.save()

        subject = 'iNailForFung Booking Confirmation'
        message = (
            'Thank you for choosing iNailForFung Services. I am pleased to confirm your booking as below:\n\n'
            + 'Username:'******'\n' + 'Price:' +
            str(validated_data['price']) + '\n' + 'Date of Booking:' +
            str(validated_data['date']) + '\n' + 'Service Type:' +
            str(service) + '\n' + 'Time Booked:' + str(time) + '\n' +
            'Email:' + str(user.email) + '\n' + 'Contact:' +
            str(profile.contact) + '\n\n' +
            'If you have to change or cancel your booking, please DM me at my instagram profile via the social media links in the website.'
        )
        email_from = settings.EMAIL_HOST_USER
        recipient_list = [
            user.email,
        ]
        send_mail(subject, message, email_from, recipient_list)
        return instance

    #     return category
    class Meta:
        model = Booking
        fields = ('user_id', 'service', 'price', 'date', 'time')
Ejemplo n.º 16
0
class TransactionSerializer(serializers.ModelSerializer):
    amount_total = MoneyField(max_digits=10, decimal_places=2, read_only=True)

    crate_name = serializers.CharField(source="crate.name", read_only=True)

    class Meta:
        model = Transaction
        fields = (
            "created_at",
            "updated_at",
            "id",
            "user",
            "crate",
            "crate_name",
            "amount",
            "amount_total",
        )
        extra_kwargs = map_kwargs("user", read_only=True)
Ejemplo n.º 17
0
class SentOrderSerializer(ModelCleanFieldsSerializer):
    """The sent order serializer."""

    client = serializers.HiddenField(default=serializers.CurrentUserDefault())
    service = serializers.PrimaryKeyRelatedField(
        many=False,
        queryset=Service.objects.get_list().filter(is_enabled=True),
    )
    service_location = serializers.PrimaryKeyRelatedField(
        many=False,
        queryset=ServiceLocation.objects.get_list(),
        allow_null=True,
        required=False,
    )
    client_location = serializers.PrimaryKeyRelatedField(
        many=False,
        queryset=UserLocation.objects.get_list(),
        allow_null=True,
        required=False,
    )
    price = serializers.HiddenField(default=None)
    price_amount = MoneyField(
        max_digits=settings.D8B_MONEY_MAX_DIGITS,
        decimal_places=settings.D8B_MONEY_DECIMAL_PLACES,
        read_only=True,
        source="price",
    )
    end_datetime = serializers.DateTimeField(allow_null=True, required=False)

    class Meta:
        """The metainformation."""

        model = Order

        fields = ("id", "service", "service_location", "client",
                  "client_location", "status", "note", "price", "price_amount",
                  "price_currency", "is_another_person", "first_name",
                  "last_name", "phone", "start_datetime", "end_datetime",
                  "duration", "created", "modified")
        read_only_fields = ("client", "status", "price", "price_currency",
                            "duration", "created", "modified", "created_by",
                            "modified_by")
Ejemplo n.º 18
0
class OrderItemSerializer(serializers.ModelSerializer):
    """
    A Basic serializer for order items
    """
    price = MoneyField(max_digits=14,
                       decimal_places=2,
                       required=False,
                       allow_null=True,
                       read_only=True)
    data_format = serializers.SlugRelatedField(
        required=False, queryset=DataFormat.objects.all(), slug_field='name')
    product = serializers.SlugRelatedField(queryset=Product.objects.all(),
                                           slug_field='label')
    product_id = serializers.PrimaryKeyRelatedField(read_only=True)

    available_formats = serializers.ListField(read_only=True)

    class Meta:
        model = OrderItem
        exclude = [
            '_price_currency', '_price', '_base_fee_currency', '_base_fee',
            'last_download', 'extract_result', 'validation_date', 'token'
        ]
        read_only_fields = ['price_status', 'order']
Ejemplo n.º 19
0
class PublicLoanSerializer(serializers.ModelSerializer):
    borrower = serializers.SerializerMethodField()
    amount = MoneyField(max_digits=19,
                        decimal_places=2,
                        source='original_amount')

    def get_borrower(self, obj):
        """
        Get user details for public viewers of a loan
        """
        borrower = obj.credit.user
        return {
            'firstname': borrower.first_name,
            'lastname': borrower.last_name
        }

    class Meta:
        model = Loan
        fields = (
            'amount',
            'borrower',
            'description',
            'id',
        )
Ejemplo n.º 20
0
class TaskSerializer(serializers.HyperlinkedModelSerializer):
    questions = QuestionSerializer(many=True)
    og_image_link = serializers.URLField(read_only=True)
    user = UserDetailsSerializer(read_only=True)
    tx_hash = serializers.CharField(source='initial_tx_hash', required=False)
    type = serializers.CharField(read_only=True)
    reward_per_click = MoneyField(max_digits=11, decimal_places=5)
    reward_usd_per_click = MoneyField(max_digits=11,
                                      decimal_places=5,
                                      read_only=True)
    remaining_balance = MoneyField(max_digits=9,
                                   decimal_places=3,
                                   read_only=True)
    initial_budget = MoneyField(max_digits=9, decimal_places=3, required=False)

    class Meta:
        model = models.Task
        fields = [
            'id',
            'title',
            'description',
            'chain',
            'user',
            'og_image_link',
            'uuid',
            'website_link',
            'website_image',
            'contract_address',
            'reward_per_click',
            'reward_usd_per_click',
            'time_duration',
            'questions',
            'warning_message',
            'is_active',
            'remaining_balance',
            'initial_budget',
            'tx_hash',
            'type',
        ]
        read_only_fields = [
            'user',
            'og_image_link',
            'remaining_balance',
            'website_image',
            'warning_message',
            'is_active',
        ]

    def create(self, validated_data):
        validated_data['is_active_web3'] = False
        questions = validated_data.pop('questions')
        task = super(TaskSerializer, self).create(validated_data)
        for question in questions:
            q = models.Question.objects.create(title=question['title'],
                                               task=task)
            for option in question['options']:
                models.Option.objects.create(title=option['title'],
                                             question=q,
                                             is_correct=option.get(
                                                 'is_correct', False))
        if task.initial_tx_hash:
            tasks.update_task_is_active_balance.delay(
                task_id=task.id, wait_for_tx=str(task.initial_tx_hash))
        else:
            tasks.update_task_is_active_balance.delay(task_id=task.id,
                                                      should_be_active=True,
                                                      retry=10)
        tasks.create_task_screenshot.delay(task.id)
        return task

    def validate_website_link(self, website_link):
        return convert_url(website_link)

    def validate_questions(self, questions):
        for question in questions:
            # Validate number of correct options is max one
            correct_question_sum = sum([
                option.get('is_correct', False)
                for option in question.get('options', [])
            ])
            if not correct_question_sum <= 1:
                raise serializers.ValidationError({
                    'question':
                    f"Question {question.get('title', '')} has {correct_question_sum} correct questions."
                })
        return questions

    def validate(self, attrs):
        website_link = attrs.get('website_link')
        if website_link:
            try:
                og = OpenGraph(url=website_link)
            except requests.exceptions.ConnectionError as e:
                L.warning(e)
                raise serializers.ValidationError(
                    {'website_link': f'Connection error for {website_link}'})
            if og.response.status_code != status.HTTP_200_OK:
                message = f'Website {website_link} responded with status code: {og.response.status_code}'
                L.warning(message)
                # raise serializers.ValidationError({'website_link': f'{message}. Has to be 200'})
            attrs.update({
                'og_image_link': og.image,
                'website_link': og.RESOLVED_URL or website_link,
            })
            if og.X_FRAME_OPTIONS:
                attrs.update({
                    'warning_message':
                    f'Website has strict X-Frame-Options: {og.X_FRAME_OPTIONS}',
                })
        return super().validate(attrs)
Ejemplo n.º 21
0
class BomItemSerializer(InvenTreeModelSerializer):
    """Serializer for BomItem object."""

    price_range = serializers.CharField(read_only=True)

    quantity = InvenTreeDecimalField(required=True)

    def validate_quantity(self, quantity):
        """Perform validation for the BomItem quantity field"""
        if quantity <= 0:
            raise serializers.ValidationError(
                _("Quantity must be greater than zero"))

        return quantity

    part = serializers.PrimaryKeyRelatedField(queryset=Part.objects.filter(
        assembly=True))

    substitutes = BomItemSubstituteSerializer(many=True, read_only=True)

    part_detail = PartBriefSerializer(source='part',
                                      many=False,
                                      read_only=True)

    sub_part = serializers.PrimaryKeyRelatedField(queryset=Part.objects.filter(
        component=True))

    sub_part_detail = PartBriefSerializer(source='sub_part',
                                          many=False,
                                          read_only=True)

    validated = serializers.BooleanField(read_only=True,
                                         source='is_line_valid')

    purchase_price_min = MoneyField(max_digits=19,
                                    decimal_places=4,
                                    read_only=True)

    purchase_price_max = MoneyField(max_digits=19,
                                    decimal_places=4,
                                    read_only=True)

    purchase_price_avg = serializers.SerializerMethodField()

    purchase_price_range = serializers.SerializerMethodField()

    on_order = serializers.FloatField(read_only=True)

    # Annotated fields for available stock
    available_stock = serializers.FloatField(read_only=True)
    available_substitute_stock = serializers.FloatField(read_only=True)
    available_variant_stock = serializers.FloatField(read_only=True)

    def __init__(self, *args, **kwargs):
        """Determine if extra detail fields are to be annotated on this serializer

        - part_detail and sub_part_detail serializers are only included if requested.
        - This saves a bunch of database requests
        """
        part_detail = kwargs.pop('part_detail', False)
        sub_part_detail = kwargs.pop('sub_part_detail', False)
        include_pricing = kwargs.pop('include_pricing', False)

        super(BomItemSerializer, self).__init__(*args, **kwargs)

        if part_detail is not True:
            self.fields.pop('part_detail')

        if sub_part_detail is not True:
            self.fields.pop('sub_part_detail')

        if not include_pricing:
            # Remove all pricing related fields
            self.fields.pop('price_range')
            self.fields.pop('purchase_price_min')
            self.fields.pop('purchase_price_max')
            self.fields.pop('purchase_price_avg')
            self.fields.pop('purchase_price_range')

    @staticmethod
    def setup_eager_loading(queryset):
        """Prefetch against the provided queryset to speed up database access"""
        queryset = queryset.prefetch_related('part')
        queryset = queryset.prefetch_related('part__category')
        queryset = queryset.prefetch_related('part__stock_items')

        queryset = queryset.prefetch_related('sub_part')
        queryset = queryset.prefetch_related('sub_part__category')

        queryset = queryset.prefetch_related(
            'sub_part__stock_items',
            'sub_part__stock_items__allocations',
            'sub_part__stock_items__sales_order_allocations',
        )

        queryset = queryset.prefetch_related(
            'substitutes',
            'substitutes__part__stock_items',
        )

        queryset = queryset.prefetch_related(
            'sub_part__supplier_parts__pricebreaks')
        return queryset

    @staticmethod
    def annotate_queryset(queryset):
        """Annotate the BomItem queryset with extra information:

        Annotations:
            available_stock: The amount of stock available for the sub_part Part object
        """
        """
        Construct an "available stock" quantity:
        available_stock = total_stock - build_order_allocations - sales_order_allocations
        """

        ref = 'sub_part__'

        # Annotate with the total "on order" amount for the sub-part
        queryset = queryset.annotate(
            on_order=part.filters.annotate_on_order_quantity(ref), )

        # Calculate "total stock" for the referenced sub_part
        # Calculate the "build_order_allocations" for the sub_part
        # Note that these fields are only aliased, not annotated
        queryset = queryset.alias(
            total_stock=part.filters.annotate_total_stock(reference=ref),
            allocated_to_sales_orders=part.filters.
            annotate_sales_order_allocations(reference=ref),
            allocated_to_build_orders=part.filters.
            annotate_build_order_allocations(reference=ref),
        )

        # Calculate 'available_stock' based on previously annotated fields
        queryset = queryset.annotate(available_stock=ExpressionWrapper(
            F('total_stock') - F('allocated_to_sales_orders') -
            F('allocated_to_build_orders'),
            output_field=models.DecimalField(),
        ))

        ref = 'substitutes__part__'

        # Extract similar information for any 'substitute' parts
        queryset = queryset.alias(
            substitute_stock=part.filters.annotate_total_stock(reference=ref),
            substitute_build_allocations=part.filters.
            annotate_build_order_allocations(reference=ref),
            substitute_sales_allocations=part.filters.
            annotate_sales_order_allocations(reference=ref))

        # Calculate 'available_substitute_stock' field
        queryset = queryset.annotate(
            available_substitute_stock=ExpressionWrapper(
                F('substitute_stock') - F('substitute_build_allocations') -
                F('substitute_sales_allocations'),
                output_field=models.DecimalField(),
            ))

        # Annotate the queryset with 'available variant stock' information
        variant_stock_query = part.filters.variant_stock_query(
            reference='sub_part__')

        queryset = queryset.alias(
            variant_stock_total=part.filters.annotate_variant_quantity(
                variant_stock_query, reference='quantity'),
            variant_bo_allocations=part.filters.annotate_variant_quantity(
                variant_stock_query,
                reference='sales_order_allocations__quantity'),
            variant_so_allocations=part.filters.annotate_variant_quantity(
                variant_stock_query, reference='allocations__quantity'),
        )

        queryset = queryset.annotate(available_variant_stock=ExpressionWrapper(
            F('variant_stock_total') - F('variant_bo_allocations') -
            F('variant_so_allocations'),
            output_field=FloatField(),
        ))

        return queryset

    def get_purchase_price_range(self, obj):
        """Return purchase price range."""
        try:
            purchase_price_min = obj.purchase_price_min
        except AttributeError:
            return None

        try:
            purchase_price_max = obj.purchase_price_max
        except AttributeError:
            return None

        if purchase_price_min and not purchase_price_max:
            # Get price range
            purchase_price_range = str(purchase_price_max)
        elif not purchase_price_min and purchase_price_max:
            # Get price range
            purchase_price_range = str(purchase_price_max)
        elif purchase_price_min and purchase_price_max:
            # Get price range
            if purchase_price_min >= purchase_price_max:
                # If min > max: use min only
                purchase_price_range = str(purchase_price_min)
            else:
                purchase_price_range = str(purchase_price_min) + " - " + str(
                    purchase_price_max)
        else:
            purchase_price_range = '-'

        return purchase_price_range

    def get_purchase_price_avg(self, obj):
        """Return purchase price average."""
        try:
            purchase_price_avg = obj.purchase_price_avg
        except AttributeError:
            return None

        if purchase_price_avg:
            # Get string representation of price average
            purchase_price_avg = str(purchase_price_avg)
        else:
            purchase_price_avg = '-'

        return purchase_price_avg

    class Meta:
        """Metaclass defining serializer fields"""
        model = BomItem
        fields = [
            'allow_variants',
            'inherited',
            'note',
            'optional',
            'overage',
            'pk',
            'part',
            'part_detail',
            'purchase_price_avg',
            'purchase_price_max',
            'purchase_price_min',
            'purchase_price_range',
            'quantity',
            'reference',
            'sub_part',
            'sub_part_detail',
            'substitutes',
            'price_range',
            'validated',

            # Annotated fields describing available quantity
            'available_stock',
            'available_substitute_stock',
            'available_variant_stock',

            # Annotated field describing quantity on order
            'on_order',
        ]
Ejemplo n.º 22
0
class RecordSerializer(serializers.ModelSerializer):
    status_display = serializers.CharField(source='get_status_display',
                                           read_only=True)
    parlor = PrimaryKeyField(
        serializer=ParlorSerializer,
        queryset=Parlor.objects.all(),
        model=Parlor,
    )
    created_by = PrimaryKeyField(
        serializer=ProfileSerializer,
        queryset=Profile.objects.all(),
        model=Profile,
    )
    customer = PrimaryKeyField(
        serializer=CustomerSerializer,
        queryset=Customer.objects.all(),
        model=Customer,
    )
    performer = PrimaryKeyField(
        serializer=ProfileSerializer,
        queryset=Profile.objects.all(),
        model=Profile,
    )
    type = PrimaryKeyField(
        serializer=SessionTypeSerializer,
        queryset=SessionType.objects.all(),
        model=SessionType,
    )
    sketch = Base64ImageField(required=False, allow_null=True)
    prepayments = PrepaymentSerializer(many=True, read_only=True)
    prepayment = MoneyField(write_only=True,
                            allow_null=True,
                            max_digits=14,
                            decimal_places=2,
                            default_currency=settings.DEFAULT_CURRENCY,
                            min_value=Money(
                                0, currency=settings.DEFAULT_CURRENCY))
    price = MoneyField(required=False,
                       max_digits=14,
                       decimal_places=2,
                       default_currency=settings.DEFAULT_CURRENCY,
                       min_value=Money(0, currency=settings.DEFAULT_CURRENCY))
    used_coupon = PrimaryKeyField(
        serializer=CouponSerializer,
        queryset=Coupon.objects.all(),
        model=Coupon,
        required=False,
        allow_null=True,
    )

    employee_payments = EmployeeRecordPaymentSerializer(many=True,
                                                        read_only=True)

    class Meta:
        model = Record
        fields = (
            'id',
            'status',
            'status_display',
            'status_changed',
            'created_at',
            'created_by',
            'parlor',
            'customer',
            'performer',
            'type',
            'datetime',
            'approximate_time',
            'comment',
            'sketch',
            'prepayments',
            'prepayment',
            'reason',
            'rollback_prepayment',
            'price',
            'used_coupon',
            'employee_payments',
        )
        extra_kwargs = {
            'status': {
                'required': False
            },
            'status_changed': {
                'read_only': True
            },
            'created_at': {
                'read_only': True
            },
            'rollback_prepayment': {
                'required': False
            },
            'reason': {
                'required': False
            },
        }

    def validate(self, attrs):

        if self.context['request'].method == "PATCH":
            if (status := attrs.get('status', None)) is not None:
                if status == Record.STATUS.canceled:
                    if attrs.get('reason', None) is None:
                        # Показать ошибку, если не указана причина отмены записи.
                        raise serializers.ValidationError(
                            {'reason': settings.STRINGS['REQUIRED']})
                    if attrs.get('rollback_prepayment', None) is None:
                        # Показать ошибку, если не указан тип возврата предоплаты.
                        raise serializers.ValidationError({
                            'rollback_prepayment':
                            settings.STRINGS['REQUIRED']
                        })
                elif status == Record.STATUS.in_work:
                    # Показать ошибку, если нет мастера и пытаемся начать сеанс.
                    if not self.instance.performer:
                        raise serializers.ValidationError(
                            "Перед началом сеанса выберите исполнителя!")
                elif status == Record.STATUS.finished:

                    if attrs.get('price', None) is None:
                        raise serializers.ValidationError(
                            {'price': settings.STRINGS['REQUIRED']})

            elif self.instance.status not in Record.EDIT_STATUSES:
                # Если статус записи не "Новый" и не "Ожидается" - запретить изменение данных.
                raise serializers.ValidationError(
                    f"Нельзя изменить запись в статусе: {self.instance.get_status_display()}"
                )

            if (_type := attrs.get('type', None)) is not None:
                if self.instance.performer:
                    if not self.instance.performer.session_motivations.filter(
                            session_type=_type).exists():
                        # Обнулить исполнителя, если у текущего нет мотивации за сеанс.
                        attrs['performer'] = None
Ejemplo n.º 23
0
class BomItemSerializer(InvenTreeModelSerializer):
    """ Serializer for BomItem object """

    price_range = serializers.CharField(read_only=True)

    quantity = serializers.FloatField()

    part = serializers.PrimaryKeyRelatedField(queryset=Part.objects.filter(
        assembly=True))

    part_detail = PartBriefSerializer(source='part',
                                      many=False,
                                      read_only=True)

    sub_part = serializers.PrimaryKeyRelatedField(queryset=Part.objects.filter(
        component=True))

    sub_part_detail = PartBriefSerializer(source='sub_part',
                                          many=False,
                                          read_only=True)

    validated = serializers.BooleanField(read_only=True,
                                         source='is_line_valid')

    purchase_price_min = MoneyField(max_digits=10,
                                    decimal_places=6,
                                    read_only=True)

    purchase_price_max = MoneyField(max_digits=10,
                                    decimal_places=6,
                                    read_only=True)

    purchase_price_avg = serializers.SerializerMethodField()

    purchase_price_range = serializers.SerializerMethodField()

    def __init__(self, *args, **kwargs):
        # part_detail and sub_part_detail serializers are only included if requested.
        # This saves a bunch of database requests

        part_detail = kwargs.pop('part_detail', False)
        sub_part_detail = kwargs.pop('sub_part_detail', False)

        super(BomItemSerializer, self).__init__(*args, **kwargs)

        if part_detail is not True:
            self.fields.pop('part_detail')

        if sub_part_detail is not True:
            self.fields.pop('sub_part_detail')

    @staticmethod
    def setup_eager_loading(queryset):
        queryset = queryset.prefetch_related('part')
        queryset = queryset.prefetch_related('part__category')
        queryset = queryset.prefetch_related('part__stock_items')

        queryset = queryset.prefetch_related('sub_part')
        queryset = queryset.prefetch_related('sub_part__category')
        queryset = queryset.prefetch_related('sub_part__stock_items')
        queryset = queryset.prefetch_related(
            'sub_part__supplier_parts__pricebreaks')
        return queryset

    def get_purchase_price_range(self, obj):
        """ Return purchase price range """

        try:
            purchase_price_min = obj.purchase_price_min
        except AttributeError:
            return None

        try:
            purchase_price_max = obj.purchase_price_max
        except AttributeError:
            return None

        if purchase_price_min and not purchase_price_max:
            # Get price range
            purchase_price_range = str(purchase_price_max)
        elif not purchase_price_min and purchase_price_max:
            # Get price range
            purchase_price_range = str(purchase_price_max)
        elif purchase_price_min and purchase_price_max:
            # Get price range
            if purchase_price_min >= purchase_price_max:
                # If min > max: use min only
                purchase_price_range = str(purchase_price_min)
            else:
                purchase_price_range = str(purchase_price_min) + " - " + str(
                    purchase_price_max)
        else:
            purchase_price_range = '-'

        return purchase_price_range

    def get_purchase_price_avg(self, obj):
        """ Return purchase price average """

        try:
            purchase_price_avg = obj.purchase_price_avg
        except AttributeError:
            return None

        if purchase_price_avg:
            # Get string representation of price average
            purchase_price_avg = str(purchase_price_avg)
        else:
            purchase_price_avg = '-'

        return purchase_price_avg

    class Meta:
        model = BomItem
        fields = [
            'allow_variants',
            'inherited',
            'note',
            'optional',
            'overage',
            'pk',
            'part',
            'part_detail',
            'purchase_price_avg',
            'purchase_price_max',
            'purchase_price_min',
            'purchase_price_range',
            'quantity',
            'reference',
            'sub_part',
            'sub_part_detail',
            'price_range',
            'validated',
        ]
Ejemplo n.º 24
0
class BomItemSerializer(InvenTreeModelSerializer):
    """
    Serializer for BomItem object
    """

    price_range = serializers.CharField(read_only=True)

    quantity = InvenTreeDecimalField(required=True)

    def validate_quantity(self, quantity):
        if quantity <= 0:
            raise serializers.ValidationError(
                _("Quantity must be greater than zero"))

        return quantity

    part = serializers.PrimaryKeyRelatedField(queryset=Part.objects.filter(
        assembly=True))

    substitutes = BomItemSubstituteSerializer(many=True, read_only=True)

    part_detail = PartBriefSerializer(source='part',
                                      many=False,
                                      read_only=True)

    sub_part = serializers.PrimaryKeyRelatedField(queryset=Part.objects.filter(
        component=True))

    sub_part_detail = PartBriefSerializer(source='sub_part',
                                          many=False,
                                          read_only=True)

    validated = serializers.BooleanField(read_only=True,
                                         source='is_line_valid')

    purchase_price_min = MoneyField(max_digits=19,
                                    decimal_places=4,
                                    read_only=True)

    purchase_price_max = MoneyField(max_digits=19,
                                    decimal_places=4,
                                    read_only=True)

    purchase_price_avg = serializers.SerializerMethodField()

    purchase_price_range = serializers.SerializerMethodField()

    # Annotated fields for available stock
    available_stock = serializers.FloatField(read_only=True)
    available_substitute_stock = serializers.FloatField(read_only=True)
    available_variant_stock = serializers.FloatField(read_only=True)

    def __init__(self, *args, **kwargs):
        # part_detail and sub_part_detail serializers are only included if requested.
        # This saves a bunch of database requests

        part_detail = kwargs.pop('part_detail', False)
        sub_part_detail = kwargs.pop('sub_part_detail', False)
        include_pricing = kwargs.pop('include_pricing', False)

        super(BomItemSerializer, self).__init__(*args, **kwargs)

        if part_detail is not True:
            self.fields.pop('part_detail')

        if sub_part_detail is not True:
            self.fields.pop('sub_part_detail')

        if not include_pricing:
            # Remove all pricing related fields
            self.fields.pop('price_range')
            self.fields.pop('purchase_price_min')
            self.fields.pop('purchase_price_max')
            self.fields.pop('purchase_price_avg')
            self.fields.pop('purchase_price_range')

    @staticmethod
    def setup_eager_loading(queryset):
        queryset = queryset.prefetch_related('part')
        queryset = queryset.prefetch_related('part__category')
        queryset = queryset.prefetch_related('part__stock_items')

        queryset = queryset.prefetch_related('sub_part')
        queryset = queryset.prefetch_related('sub_part__category')

        queryset = queryset.prefetch_related(
            'sub_part__stock_items',
            'sub_part__stock_items__allocations',
            'sub_part__stock_items__sales_order_allocations',
        )

        queryset = queryset.prefetch_related(
            'substitutes',
            'substitutes__part__stock_items',
        )

        queryset = queryset.prefetch_related(
            'sub_part__supplier_parts__pricebreaks')
        return queryset

    @staticmethod
    def annotate_queryset(queryset):
        """
        Annotate the BomItem queryset with extra information:

        Annotations:
            available_stock: The amount of stock available for the sub_part Part object
        """
        """
        Construct an "available stock" quantity:
        available_stock = total_stock - build_order_allocations - sales_order_allocations
        """

        build_order_filter = Q(build__status__in=BuildStatus.ACTIVE_CODES)
        sales_order_filter = Q(
            line__order__status__in=SalesOrderStatus.OPEN,
            shipment__shipment_date=None,
        )

        # Calculate "total stock" for the referenced sub_part
        # Calculate the "build_order_allocations" for the sub_part
        # Note that these fields are only aliased, not annotated
        queryset = queryset.alias(
            total_stock=Coalesce(
                SubquerySum('sub_part__stock_items__quantity',
                            filter=StockItem.IN_STOCK_FILTER),
                Decimal(0),
                output_field=models.DecimalField(),
            ),
            allocated_to_sales_orders=Coalesce(
                SubquerySum(
                    'sub_part__stock_items__sales_order_allocations__quantity',
                    filter=sales_order_filter,
                ),
                Decimal(0),
                output_field=models.DecimalField(),
            ),
            allocated_to_build_orders=Coalesce(
                SubquerySum(
                    'sub_part__stock_items__allocations__quantity',
                    filter=build_order_filter,
                ),
                Decimal(0),
                output_field=models.DecimalField(),
            ),
        )

        # Calculate 'available_stock' based on previously annotated fields
        queryset = queryset.annotate(available_stock=ExpressionWrapper(
            F('total_stock') - F('allocated_to_sales_orders') -
            F('allocated_to_build_orders'),
            output_field=models.DecimalField(),
        ))

        # Extract similar information for any 'substitute' parts
        queryset = queryset.alias(
            substitute_stock=Coalesce(
                SubquerySum(
                    'substitutes__part__stock_items__quantity',
                    filter=StockItem.IN_STOCK_FILTER,
                ),
                Decimal(0),
                output_field=models.DecimalField(),
            ),
            substitute_build_allocations=Coalesce(
                SubquerySum(
                    'substitutes__part__stock_items__allocations__quantity',
                    filter=build_order_filter,
                ),
                Decimal(0),
                output_field=models.DecimalField(),
            ),
            substitute_sales_allocations=Coalesce(
                SubquerySum(
                    'substitutes__part__stock_items__sales_order_allocations__quantity',
                    filter=sales_order_filter,
                ),
                Decimal(0),
                output_field=models.DecimalField(),
            ),
        )

        # Calculate 'available_substitute_stock' field
        queryset = queryset.annotate(
            available_substitute_stock=ExpressionWrapper(
                F('substitute_stock') - F('substitute_build_allocations') -
                F('substitute_sales_allocations'),
                output_field=models.DecimalField(),
            ))

        # Annotate the queryset with 'available variant stock' information
        variant_stock_query = StockItem.objects.filter(
            part__tree_id=OuterRef('sub_part__tree_id'),
            part__lft__gt=OuterRef('sub_part__lft'),
            part__rght__lt=OuterRef('sub_part__rght'),
        ).filter(StockItem.IN_STOCK_FILTER)

        queryset = queryset.alias(
            variant_stock_total=Coalesce(Subquery(
                variant_stock_query.annotate(total=Func(
                    F('quantity'), function='SUM',
                    output_field=FloatField())).values('total')),
                                         0,
                                         output_field=FloatField()),
            variant_stock_build_order_allocations=Coalesce(
                Subquery(
                    variant_stock_query.annotate(total=Func(
                        F('sales_order_allocations__quantity'),
                        function='SUM',
                        output_field=FloatField()), ).values('total')),
                0,
                output_field=FloatField(),
            ),
            variant_stock_sales_order_allocations=Coalesce(
                Subquery(
                    variant_stock_query.annotate(total=Func(
                        F('allocations__quantity'),
                        function='SUM',
                        output_field=FloatField()), ).values('total')),
                0,
                output_field=FloatField(),
            ))

        queryset = queryset.annotate(available_variant_stock=ExpressionWrapper(
            F('variant_stock_total') -
            F('variant_stock_build_order_allocations') -
            F('variant_stock_sales_order_allocations'),
            output_field=FloatField(),
        ))

        return queryset

    def get_purchase_price_range(self, obj):
        """ Return purchase price range """

        try:
            purchase_price_min = obj.purchase_price_min
        except AttributeError:
            return None

        try:
            purchase_price_max = obj.purchase_price_max
        except AttributeError:
            return None

        if purchase_price_min and not purchase_price_max:
            # Get price range
            purchase_price_range = str(purchase_price_max)
        elif not purchase_price_min and purchase_price_max:
            # Get price range
            purchase_price_range = str(purchase_price_max)
        elif purchase_price_min and purchase_price_max:
            # Get price range
            if purchase_price_min >= purchase_price_max:
                # If min > max: use min only
                purchase_price_range = str(purchase_price_min)
            else:
                purchase_price_range = str(purchase_price_min) + " - " + str(
                    purchase_price_max)
        else:
            purchase_price_range = '-'

        return purchase_price_range

    def get_purchase_price_avg(self, obj):
        """ Return purchase price average """

        try:
            purchase_price_avg = obj.purchase_price_avg
        except AttributeError:
            return None

        if purchase_price_avg:
            # Get string representation of price average
            purchase_price_avg = str(purchase_price_avg)
        else:
            purchase_price_avg = '-'

        return purchase_price_avg

    class Meta:
        model = BomItem
        fields = [
            'allow_variants',
            'inherited',
            'note',
            'optional',
            'overage',
            'pk',
            'part',
            'part_detail',
            'purchase_price_avg',
            'purchase_price_max',
            'purchase_price_min',
            'purchase_price_range',
            'quantity',
            'reference',
            'sub_part',
            'sub_part_detail',
            'substitutes',
            'price_range',
            'validated',

            # Annotated fields describing available quantity
            'available_stock',
            'available_substitute_stock',
            'available_variant_stock',
        ]
Ejemplo n.º 25
0
class AbstractPaymentEntitySerializer(serializers.ModelSerializer):
    created_by = UsernameSerializer(read_only=True)
    status_changed_by = UsernameSerializer(read_only=True)
    employee = PrimaryKeyField(serializer=ProfileUsernameSerializer,
                               model=Profile,
                               queryset=Profile.objects.all())
    amount = MoneyField(max_digits=14, decimal_places=2)

    class Meta:
        fields = (
            'id',
            'created_by',
            'created_at',
            'month',
            'year',
            'type',
            'employee',
            'amount',
            'note',
            'status',
            'href',
            'status_changed_by',
            'status_changed_at',
            'status_change_reason',
        )
        read_only_fields = ('created_at', 'status_changed_at')
        extra_kwargs = {
            'status': {
                'allow_null': True
            },
            'status_change_reason': {
                'required': False
            },
            'month': {
                'required': False
            },
            'year': {
                'required': False
            },
        }

    def validate(self, attrs):

        if self.context['request'].method == "PATCH":
            if attrs.get('status') is False:
                if not attrs.get('status_change_reason'):
                    raise serializers.ValidationError(
                        {'status_change_reason': _("Укажите причину отмены.")})
        return attrs

    def update(self, instance, validated_data):
        if (status := validated_data.get('status', "null")) != "null":
            timestamp = timezone.now()
            user = self.context['request'].user
            return self.Meta.model.proceed_status(
                entity_pk=instance.id,
                status=status,
                user=user,
                timestamp=timestamp,
                status_change_reason=validated_data.get(
                    'status_change_reason', None),
            )
        return super(AbstractPaymentEntitySerializer,
                     self).update(instance, validated_data)