class OrderOfferDiscountSerializer(OfferDiscountSerializer): name = serializers.CharField(source="offer_name") amount = serializers.DecimalField(decimal_places=2, max_digits=12)
class HeartBeatSerializer(serializers.Serializer): room_id = serializers.IntegerField() # ac_status = serializers.CharField(max_length=10) temp = serializers.DecimalField(max_digits=3, decimal_places=1)
class OrderSettlementSerializer(serializers.Serializer): """订单结算序列化器类""" freight = serializers.DecimalField(label='运费', max_digits=10, decimal_places=2) skus = OrderSKUSerializer(label='结算商品', many=True)
class CreateProjectSerializer(serializers.Serializer): title = serializers.CharField(max_length=500) description = serializers.CharField(max_length=4000, allow_null=True) summary = serializers.CharField(max_length=1000) deadline = serializers.DateField(allow_null=True) budget = serializers.DecimalField(max_digits=10, decimal_places=2, allow_null=True) categories = serializers.ListField() images = serializers.ListField() def validate(self, data): error = {} if len(data) <= 3: error['title'] = ['O título tem que ter pelomenos 3 caracteres'] if len(data['categories']) == 0: error['categories'] = [ 'O projeto tem que se relacionar com pelomenos uma categoria' ] else: for category in data['categories']: if category.get('category'): if len(Category.objects.filter( name=category['category'])) == 0: categories = [x.name for x in Category.objects.all()] error['categories'] = [ "Categoria com valor '%s' está incorreta. Categorias disponíveis são %s" % (category['category'], categories) ] break else: error['categories'] = [ 'Em categories, tem que ter um objeto com atributo category' ] break if date.today() > data['deadline']: error['deadline'] = [ 'O campo prazo não pode ser inferior à data presente - %s' % date.today().strftime("%d/%m/%Y") ] for image in data['images']: if not image.get('image'): error['images'] = [ 'Em images, tem que ter um objeto com atributo image' ] break if '/media/project/' in image['image']: continue try: image = image['image'].split(',') if ';base64' not in image[0] or 'data:' not in image[0]: raise Exception("Error") except: error['images'] = ['Imagem não foi enviada de forma correta.'] break if error != {}: raise serializers.ValidationError(error) return data def create_categories(self, project, categories): for category_data in categories: cat = Category.objects.get(name=category_data['category']) project.categories.add(cat) @transaction.atomic def create(self, user): data = self.data categories = data['categories'] del data['categories'] images = data['images'] del data['images'] project = Project.objects.create(**data) self.create_categories(project=project, categories=categories) project.save() if images is not None: for image_data in images: photo = image_data['image'].split(',') image64 = photo[1] image = store_image( directory='project', photo_name="%s - %s - %s" % (user.id, project.title, str(datetime.now())), image64=image64) ProjectImage.objects.create(project=project, image=image) UserProject.objects.create(user=user, project=project) return project @transaction.atomic def update(self, instance, validated_data): instance.title = validated_data['title'] instance.description = validated_data['description'] instance.summary = validated_data['summary'] instance.deadline = validated_data['deadline'] instance.budget = validated_data['budget'] new_categories_values = [ x['category'] for x in validated_data['categories'] ] new_categories = [] for category_value in new_categories_values: new_categories.append(Category.objects.get(name=category_value)) instance.categories.set(new_categories) image_to_verify = [] image_to_stored = [] validated_data['images'] = [ x['image'] for x in validated_data['images'] ] for image_data in validated_data['images']: if '/media/project/' in image_data: image_to_verify.append(image_data) else: image_to_stored.append(image_data) image_to_remove = [] for projectImage in instance.images.all(): image_exist = list( filter(lambda x: projectImage.image.url in x, image_to_verify)) if not image_exist: image_to_remove.append(projectImage) for image_data in image_to_stored: photo = image_data.split(',') image64 = photo[1] image = store_image(directory='project', photo_name="%s - %s - %s" % (instance.userproject_set.last().user.id, instance.title, str(datetime.now())), image64=image64) ProjectImage.objects.create(project=instance, image=image) instance.save() for remove_image in image_to_remove: direct_name = str(remove_image.image) remove_image.delete() delete_image(direct_name) return instance
class ProformaAnticipoSerializer(serializers.ModelSerializer): valor_total_con_impuesto = serializers.DecimalField(max_digits=12, decimal_places=2, read_only=True) estado_display = serializers.CharField(source='get_estado_display', read_only=True) valor_total_sin_impuesto = serializers.DecimalField(max_digits=12, decimal_places=2, read_only=True) color_estado = serializers.SerializerMethodField() porcentaje_a_verificacion = serializers.SerializerMethodField() fecha_seguimiento = serializers.DateField( format="%Y-%m-%d", input_formats=['%Y-%m-%dT%H:%M:%S.%fZ', 'iso-8601'], allow_null=True, required=False) fecha_cobro = serializers.DateField( format="%Y-%m-%d", input_formats=['%Y-%m-%dT%H:%M:%S.%fZ', 'iso-8601'], allow_null=True, required=False) to_string = serializers.SerializerMethodField() def get_to_string(self, instance): return 'Cobro con Id %s' % instance.id def get_porcentaje_a_verificacion(self, obj): fecha_ini = obj.fecha_cambio_estado fecha_seg = obj.fecha_seguimiento if obj.estado not in ['ENVIADA', 'RECIBIDA']: return None if fecha_ini and fecha_seg: fecha_act = timezone.datetime.now().date() delta = (fecha_act - fecha_ini).days dias = (fecha_seg - fecha_ini).days if dias == 0: porcentaje = 1 else: porcentaje = delta / dias return round(porcentaje * 100, 2) else: return None def get_color_estado(self, obj): fecha_ini = obj.fecha_cambio_estado fecha_seg = obj.fecha_seguimiento fecha_act = timezone.datetime.now().date() if obj.estado not in ['ENVIADA', 'RECIBIDA']: return 'white' else: if not fecha_seg: return 'tomato' elif fecha_ini and fecha_seg: delta = (fecha_act - fecha_ini).days dias = (fecha_seg - fecha_ini).days if dias == 0: porcentaje = 1 else: porcentaje = delta / dias if porcentaje >= 0.9: return 'tomato' elif porcentaje > 0.66: return 'yellow' else: return 'lightgreen' def create(self, validated_data): from .services import proforma_anticipo_crear_actualizar informacion_locatario = validated_data.get('informacion_locatario', None) tipo_documento = validated_data.get('tipo_documento') informacion_cliente = validated_data.get('informacion_cliente') divisa = validated_data.get('divisa') nit = validated_data.get('nit') nombre_cliente = validated_data.get('nombre_cliente') fecha = validated_data.get('fecha') nro_orden_compra = validated_data.get('nro_orden_compra') condicion_pago = validated_data.get('condicion_pago') email_destinatario = validated_data.get('email_destinatario', None) email_destinatario_dos = validated_data.get('email_destinatario_dos', None) impuesto = validated_data.get('impuesto', 0) anticipo = proforma_anticipo_crear_actualizar( email_destinatario_dos=email_destinatario_dos, email_destinatario=email_destinatario, informacion_locatario=informacion_locatario, informacion_cliente=informacion_cliente, divisa=divisa, nit=nit, nombre_cliente=nombre_cliente, fecha=fecha, nro_orden_compra=nro_orden_compra, condicion_pago=condicion_pago, impuesto=impuesto, tipo_documento=tipo_documento) return anticipo def update(self, instance, validated_data): from .services import proforma_anticipo_crear_actualizar informacion_locatario = validated_data.get('informacion_locatario', None) informacion_cliente = validated_data.get('informacion_cliente') fecha_seguimiento = validated_data.get('fecha_seguimiento') email_destinatario = validated_data.get('email_destinatario', None) email_destinatario_dos = validated_data.get('email_destinatario_dos', None) impuesto = validated_data.get('impuesto', 0) tipo_documento = validated_data.get('tipo_documento') divisa = validated_data.get('divisa') observacion = validated_data.get('observacion') nit = validated_data.get('nit') nombre_cliente = validated_data.get('nombre_cliente') fecha = validated_data.get('fecha') fecha_cobro = validated_data.get('fecha_cobro') nro_orden_compra = validated_data.get('nro_orden_compra') condicion_pago = validated_data.get('condicion_pago') anticipo = proforma_anticipo_crear_actualizar( observacion=observacion, fecha_cobro=fecha_cobro, email_destinatario_dos=email_destinatario_dos, email_destinatario=email_destinatario, informacion_locatario=informacion_locatario, informacion_cliente=informacion_cliente, divisa=divisa, fecha_seguimiento=fecha_seguimiento, nit=nit, nombre_cliente=nombre_cliente, fecha=fecha, nro_orden_compra=nro_orden_compra, condicion_pago=condicion_pago, tipo_documento=tipo_documento, impuesto=impuesto, id=instance.id) return anticipo class Meta: model = ProformaAnticipo fields = [ 'url', 'id', 'cliente', 'nro_consecutivo', 'informacion_locatario', 'informacion_cliente', 'tipo_documento', 'divisa', 'nit', 'editable', 'envios', 'to_string', 'documentos', 'fecha_cobro', 'items', 'observacion', 'nombre_cliente', 'observacion', 'estado_display', 'fecha_seguimiento', 'estado', 'literales', 'email_destinatario', 'email_destinatario_dos', 'cobrado', 'fecha', 'nro_orden_compra', 'recibo_pago', 'editable', 'condicion_pago', 'impuesto', 'estado', 'porcentaje_a_verificacion', 'color_estado', 'valor_total_sin_impuesto', 'version', 'valor_total_con_impuesto', ] extra_kwargs = { 'nro_consecutivo': { 'read_only': True }, 'informacion_locatario': { 'allow_blank': True }, 'email_destinatario': { 'allow_blank': True }, 'email_destinatario_dos': { 'allow_blank': True }, 'observacion': { 'allow_blank': True }, 'editable': { 'read_only': True }, 'items': { 'read_only': True }, 'version': { 'read_only': True }, 'documentos': { 'read_only': True }, 'envios': { 'read_only': True }, 'literales': { 'read_only': True }, }
class CertificateSerializer(serializers.Serializer): """ This is the Gift Certificates REST API layer """ language = serializers.ChoiceField(choices=settings.LANGUAGES, required=True) account_number = serializers.RegexField(regex='^[a-zA-Z0-9]{1,10}$', allow_blank=False, required=False) recipient_name = serializers.CharField(max_length=60, required=True) recipient_email = serializers.EmailField(required=True) department_id = serializers.UUIDField(format='hex_verbose', required=True) email_signature = serializers.IntegerField(required=True) cert_type = serializers.IntegerField(required=True) primary_course = serializers.IntegerField(required=True) secondary_course = serializers.IntegerField(required=False) expiry_date = serializers.DateField(format='%Y-%m-%d', input_formats=['%Y-%m-%d'], required=False) quantity = serializers.DecimalField(max_digits=6, decimal_places=2, required=False) power_cart = serializers.ChoiceField(choices=Certificate.POWER_CART_CHOICES, required=False) note = serializers.CharField(max_length=255, required=False) message = serializers.CharField(max_length=250, required=False) def validate(self, data): self.certificate_batch_data = {} self.certificate_data = {} if data.get('language') is not None: self.certificate_batch_data['language'] = data.get('language') if data.get('recipient_name') is not None: self.certificate_batch_data['recipient_name'] = data.get('recipient_name') if data.get('recipient_email') is not None: self.certificate_batch_data['recipient_email'] = data.get('recipient_email') if data.get('note') is not None: self.certificate_data['note'] = data.get('note') if data.get('message') is not None: self.certificate_data['message'] = data.get('message') # Account Number if data.get('account_number') is not None: try: User.objects.get(membership_number=data.get('account_number')) self.certificate_batch_data['account_number'] = data.get('account_number') except User.DoesNotExist: if data.get('account_number') != settings.DEFAULT_CERTIFICATE_MEMBERSHIP_NUMBER: raise serializers.ValidationError({"account_number": _('Account number does not exist')}) else: self.certificate_batch_data['account_number'] = settings.DEFAULT_CERTIFICATE_MEMBERSHIP_NUMBER else: self.certificate_batch_data['account_number'] = settings.DEFAULT_CERTIFICATE_MEMBERSHIP_NUMBER # Department try: self.certificate_batch_data['department'] = Department.objects.get(pk=data.get('department_id')) except Department.DoesNotExist: raise serializers.ValidationError({"department_id":_('Invalid department.')}) # TODO: This will come with the authentication layer # if not Department.objects.filter(admins__in=self.context['employee']): # raise serializers.ValidationError({"department_id":_('Employee does not have access to this department')}) # Email Signature ( 1 is the default email_signature that gives access to all GC) if data.get('email_signature') is not None and data.get('email_signature') != 1: try: self.certificate_batch_data['email_signature'] = EmailSignature.objects.\ get(id=data.get('email_signature'), department=self.certificate_batch_data['department']) except EmailSignature.DoesNotExist: raise serializers.ValidationError({"email_signature":_('Email signature invalid access')}) # Certificate Type try: self.certificate_data['type'] = CertificateType.objects.get(pk=data.get('cert_type')) except CertificateType.DoesNotExist: raise serializers.ValidationError({"cert_type":_('Certificate type does not exist')}) # Primary Course try: self.certificate_data['club'] = Club.objects.get(pk=data.get('primary_course')) except Club.DoesNotExist: raise serializers.ValidationError({"primary_course":_('Invalid primary course')}) # Secondary Course if data.get('secondary_course') is not None: try: self.certificate_data['club_secondary'] = Club.objects.get(pk=data.get('secondary_course')) except Club.DoesNotExist: raise serializers.ValidationError({"secondary_course":_('Invalid secondary course')}) # Quantity if data.get('quantity') is None: if self.certificate_data['type'].quantity: if self.certificate_data['type'].category in DOLLAR_VALUE_CATEGORIES: self.certificate_data['quantity'] = self.certificate_data['type'].quantity else: self.certificate_data['quantity'] = int(self.certificate_data['type'].quantity) else: raise serializers.ValidationError({"quantity":_('quantity can not be empty.')}) else: if self.certificate_data['type'].category in DOLLAR_VALUE_CATEGORIES: self.certificate_data['quantity'] = data.get('quantity') else: self.certificate_data['quantity'] = int(data.get('quantity')) # Expiry Date if data.get('expiry_date') is None: if self.certificate_data['type'].expiry_date: self.certificate_data['expiry_date'] = self.certificate_data['type'].expiry_date else: raise serializers.ValidationError({"expiry_date":_('expiry_date can not be empty.')}) else: if data.get('expiry_date') < datetime.date.today(): raise serializers.ValidationError({"expiry_date": _('expiry_date must be after today.')}) self.certificate_data['expiry_date'] = data.get('expiry_date') # Power Cart if data.get('power_cart') is None: if self.certificate_data['type'].power_cart: self.certificate_data['power_cart'] = self.certificate_data['type'].power_cart else: raise serializers.ValidationError({"power_cart":_('power_cart can not be empty.')}) else: self.certificate_data['power_cart'] = data.get('power_cart') return data
class ExchangeResultSerializer(slz.Serializer): dt = slz.DateTimeField() currency = slz.ChoiceField(Currency.choices, source="money.ccy") amount = slz.DecimalField(max_digits=20, decimal_places=10, source="money.amount")
class TransactionSerializer(serializers.ModelSerializer): company = serializers.PrimaryKeyRelatedField( queryset=Company.objects.all()) related_transaction = serializers.PrimaryKeyRelatedField( queryset=Transaction.objects.all(), required=False) can_be_sold = serializers.SerializerMethodField(read_only=True) user_data = serializers.SerializerMethodField(read_only=True) balance = serializers.DecimalField(max_digits=50, decimal_places=4, min_value=0, read_only=True) from_user = UserSerializer(read_only=True) user = UserSerializer(read_only=True) account = AccountField() class Meta: model = Transaction fields = '__all__' def get_user_data(self, transaction): user_data = { 'full_name': transaction.user.first_name.title() + ' ' + transaction.user.last_name.title() } return user_data def get_can_be_sold(self, transaction): can_be_sold = True is_sale = transaction.tipo == 'venta' has_related_transaction = hasattr( transaction, 'related_transaction' ) and transaction.related_transaction is not None transaction_is_sale = True if ( has_related_transaction and transaction.related_transaction.tipo == 'venta') else False has_related_sale = True if (has_related_transaction and transaction_is_sale) else False if (transaction.tipo == 'compra' and has_related_sale) or is_sale or transaction.tipo == 'abono': can_be_sold = False return can_be_sold def get_total(self, validated_data): precio = validated_data.get('precio', decimal.Decimal(0)) precio_fundicion = validated_data.get('meltCost', decimal.Decimal(0)) precio_legalizacion = validated_data.get('evalCost', decimal.Decimal(0)) total = precio + precio_fundicion + precio_legalizacion return total def validate(self, validated_data): fundido = validated_data.get('fundido', None) meltCost = validated_data.get('meltCost', None) legal = validated_data.get('legal', None) evalCost = validated_data.get('evalCost', None) pesoPost = validated_data.get('pesoPost', None) tipo = validated_data.get('tipo') cantidad = validated_data.get('cantidad', None) weightUnit = validated_data.get('weightUnit', None) currency = validated_data.get('currency', None) account = validated_data.get('account', None) precio = validated_data.get('precio', None) if tipo != 'abono': if tipo == 'compra' and (account.balance < self.get_total(validated_data)): raise serializers.ValidationError(_('Insufficient funds.')) if not cantidad: raise serializers.ValidationError(_('Missing amount.')) if fundido and not (meltCost or pesoPost): raise ValidationError( _('Melt cost and remaining amount are required.')) if legal and not evalCost: raise ValidationError(_('Evaluation cost is required.')) return validated_data def send_broker_notification(self, transaction): request = self.context['request'] current_site = get_current_site(request) message_context = { 'full_name': transaction.user.first_name.title() + ' ' + transaction.user.last_name.title(), 'funder': transaction.from_user.first_name.title() + ' ' + transaction.from_user.last_name.title(), 'company_name': transaction.company.name.title(), 'currency': transaction.account.account_type.currency_nick.title(), 'amount': intcomma(transaction.precio), 'current_site': current_site } subject = loader.render_to_string( 'account/email/notify_broker_funded_subject.txt') message = loader.render_to_string( 'account/email/notify_broker_funded_message.txt', message_context) send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [transaction.user.email]) def create_fund_transaction(self, validated_data): ''' method that handles transactions where the owner funds the brokers wallet and broker is notified via email ''' request = self.context['request'] validated_data['from_user'] = request.user transaction = Transaction.objects.create(**validated_data) self.send_broker_notification(transaction) def create_regular_transaction(self, validated_data): tipo = validated_data.get('tipo') request = self.context['request'] user = request.user user_gold_balance = decimal.Decimal(user.userprofile.gold_balance) #validated_data['cantidad'] = validated_data.get('cantidad')#, validated_data.get('weightUnit') cantidad = validated_data.get('cantidad') #validated_data['pesoPost'] =validated_data.get('pesoPost')#, validated_data.get('weightUnit') pesoPost = validated_data.get('pesoPost') validated_data['related_transaction'] = validated_data.get( 'related_transaction') if tipo == 'venta' else None # mult = decimal.Decimal(((1 if tipo=='compra' else -1))) # new_bal = user_gold_balance+( mult*(decimal.Decimal(pesoPost) if pesoPost else cantidad) ) # user.userprofile.gold_balance = new_bal # user.userprofile.save() #validated_data['balance'] =new_bal #request.user.userprofile.save() transaction = Transaction.objects.create(**validated_data) return transaction def create(self, validated_data): tipo = validated_data.get('tipo') transaction = self.create_fund_transaction( ) if tipo == 'abono' else self.create_regular_transaction( validated_data) return transaction def update(self, instance, validated_data): instance.legal = validated_data.get('legal', instance.legal) instance.fundido = validated_data.get('fundido', instance.fundido) instance.pesoPost = validated_data.get('pesoPost', instance.pesoPost) instance.meltCost = validated_data.get('meltCost', instance.meltCost) instance.evalCost = validated_data.get('evalCost', instance.evalCost) instance.save() return instance
class StoreSerializer(serializers.Serializer): name = serializers.CharField() total = serializers.IntegerField() lat = serializers.DecimalField(max_digits=20, decimal_places=10) lon = serializers.DecimalField(max_digits=20, decimal_places=10) product = ProductSerializer(many=True)
class OrderShowSerializer(serializers.Serializer): # 最大10位,最小2位小数 freight = serializers.DecimalField(max_digits=10, decimal_places=2) # skus可以按照SKUSerializer所指定的内容进行返回 skus = SKUSerializer(many=True)
class BrokerSerializer(RegisterSerializer): first_name = serializers.CharField(required=True, max_length=100) last_name = serializers.CharField(required=True, max_length=100) company = serializers.PrimaryKeyRelatedField( queryset=Company.objects.all(), required=True) id_number = serializers.CharField(required=True, max_length=100) mobile = serializers.CharField(required=False, max_length=100, allow_blank=True) office = serializers.CharField(required=False, max_length=100, allow_blank=True) other = serializers.CharField(required=False, max_length=100, allow_blank=True) location = serializers.CharField(required=True, max_length=250) gold_balance = serializers.DecimalField(max_digits=50, decimal_places=4, min_value=0) def validate_username(self, username): username = get_adapter().clean_username(username) return username def validate_email(self, email): email = get_adapter().clean_email(email) ''' get role, raise exception if email exists and pertains to an owner ''' if allauth_settings.UNIQUE_EMAIL: if email and email_address_exists(email): #get the role of existing email address user = User.objects.get(email=email) role = user.userprofile.basic_role if role == 'owner': raise serializers.ValidationError( _("An owner is already registered with this e-mail address." )) return email def custom_signup(self, request, user): ''' we create the new brokers user profile ''' ''' need to create a role and add it to company relationshiop ''' data = { 'user': user, #'company': company, 'location': self.validated_data.get('location', None), 'id_number': self.validated_data.get('id_number', None), 'mobile': self.validated_data.get('mobile', None), 'office': self.validated_data.get('office', None), 'other': self.validated_data.get('other', None), 'gold_balance': self.validated_data.get('gold_balance', None) } userprofile = UserProfile.objects.create(**data) user.userprofile = userprofile user.first_name = self.validated_data.get('first_name', None) user.last_name = self.validated_data.get('last_name', None) user.save() company = self.validated_data.get('company', None) role = Role.objects.create(name="broker", company=company, user=userprofile) return user def save(self, request): ''' we attempt to get the user, if an exception is thrown we create the broker ''' try: cleaned_data = self.get_cleaned_data() return User.objects.get(email=cleaned_data.get('email'), username=cleaned_data.get('username')) except: adapter = get_adapter() user = adapter.new_user(request) self.cleaned_data = self.get_cleaned_data() adapter.save_user(request, user, self) user = self.custom_signup(request, user) setup_user_email(request, user, []) return user
class CreatePaymentOfflineSerializer(serializers.Serializer): bill_id = serializers.IntegerField(required=True) value = serializers.DecimalField(max_digits=6, decimal_places=2, required=True) noruh_fee = serializers.DecimalField(max_digits=6, decimal_places=2, required=False) promocode = serializers.CharField(required=False) def validate(self, data): user = self.context.get('request').user bill = get_object_or_404(Bill, id=data.get('bill_id')) bill_member = get_object_or_404(BillMember, customer=user, bill=bill, leave_at__isnull=True) noruh_fee = data.get('noruh_fee') if not data.get('noruh_fee'): noruh_fee = 0.0 if not BillMember.objects.filter(bill=bill, customer=user): raise NotPartOfThisBillForPaymentException() if BillMember.objects.filter(bill=bill, leave_at__isnull=True).count() == 1: if data.get('value') < ( ((bill.orders_total() + bill.couvert_for_all()) - bill.value_paid_without_noruh_fee())): raise MustPayTheValueFromAllBillPaymentException() value = float(data.get('value')) + float(noruh_fee) all_value_bill = float("{:.2f}".format(bill.all_value_bill())) if value > (all_value_bill - float(bill.value_paid)): raise YouCanOnlyPayWhatsMissingFromTheBill() if BillPayment.objects.filter(bill_member=bill_member, bill=bill, status_payment__in=[ BillPayment.STATUS_AUTHORIZED, BillPayment.STATUS_OFFLINE_APPROVED, BillPayment.STATUS_OFFLINE_PENDING, BillPayment.STATUS_IN_ANALYSIS ]).exists(): raise UserHasBeenPaymentException() if data.get('noruh_fee'): if bill.bill_noruh_fee_count() >= bill.number_of_customers(): raise LimitsForPayNoruhFee() if data.get('promocode') is not None: if EstablishmentPromotions.objects.filter( establishment=bill.establishment, enabled=True, promocode__exact=data.get('promocode')).exists(): return data raise serializers.ValidationError('This promocode doesnt exist') return data def create(self, validated_data): return super(CreatePaymentOfflineSerializer(**validated_data))
class TakeProfitSerializer(serializers.Serializer): """Serializer for take profit params when posting an order to Alpaca api.""" limit_price = serializers.DecimalField(max_digits=12, decimal_places=5)
class CheckoutSerializer(serializers.Serializer, OrderPlacementMixin): basket = serializers.HyperlinkedRelatedField( view_name="basket-detail", queryset=Basket.objects ) guest_email = serializers.EmailField(allow_blank=True, required=False) total = serializers.DecimalField(decimal_places=2, max_digits=12, required=False) shipping_method_code = serializers.CharField(max_length=128, required=False) shipping_charge = PriceSerializer(many=False, required=False) shipping_address = ShippingAddressSerializer(many=False, required=False) billing_address = BillingAddressSerializer(many=False, required=False) def get_initial_order_status(self, basket): return overridable("OSCARAPI_INITIAL_ORDER_STATUS", default="new") def validate(self, attrs): request = self.context["request"] if request.user.is_anonymous: if not settings.OSCAR_ALLOW_ANON_CHECKOUT: message = _("Anonymous checkout forbidden") raise serializers.ValidationError(message) if not attrs.get("guest_email"): # Always require the guest email field if the user is anonymous message = _("Guest email is required for anonymous checkouts") raise serializers.ValidationError(message) else: if "guest_email" in attrs: # Don't store guest_email field if the user is authenticated del attrs["guest_email"] basket = attrs.get("basket") basket = assign_basket_strategy(basket, request) if basket.num_items <= 0: message = _("Cannot checkout with empty basket") raise serializers.ValidationError(message) shipping_method = self._shipping_method( request, basket, attrs.get("shipping_method_code"), attrs.get("shipping_address"), ) shipping_charge = shipping_method.calculate(basket) posted_shipping_charge = attrs.get("shipping_charge") if posted_shipping_charge is not None: posted_shipping_charge = prices.Price(**posted_shipping_charge) # test submitted data. if not posted_shipping_charge == shipping_charge: message = _( "Shipping price incorrect %s != %s" % (posted_shipping_charge, shipping_charge) ) raise serializers.ValidationError(message) posted_total = attrs.get("total") total = OrderTotalCalculator().calculate(basket, shipping_charge) if posted_total is not None: if posted_total != total.incl_tax: message = _("Total incorrect %s != %s" % (posted_total, total.incl_tax)) raise serializers.ValidationError(message) # update attrs with validated data. attrs["order_total"] = total attrs["shipping_method"] = shipping_method attrs["shipping_charge"] = shipping_charge attrs["basket"] = basket return attrs def create(self, validated_data): try: basket = validated_data.get("basket") order_number = self.generate_order_number(basket) request = self.context["request"] if "shipping_address" in validated_data: shipping_address = ShippingAddress(**validated_data["shipping_address"]) else: shipping_address = None if "billing_address" in validated_data: billing_address = BillingAddress(**validated_data["billing_address"]) else: billing_address = None return self.place_order( order_number=order_number, user=request.user, basket=basket, shipping_address=shipping_address, shipping_method=validated_data.get("shipping_method"), shipping_charge=validated_data.get("shipping_charge"), billing_address=billing_address, order_total=validated_data.get("order_total"), guest_email=validated_data.get("guest_email") or "", ) except ValueError as e: raise exceptions.NotAcceptable(str(e)) def _shipping_method(self, request, basket, shipping_method_code, shipping_address): repo = Repository() default = repo.get_default_shipping_method( basket=basket, user=request.user, request=request, shipping_addr=shipping_address, ) if shipping_method_code is not None: methods = repo.get_shipping_methods( basket=basket, user=request.user, request=request, shipping_addr=shipping_address, ) find_method = (s for s in methods if s.code == shipping_method_code) shipping_method = next(find_method, default) return shipping_method return default
class MetricActualSerializer(serializers.Serializer): datetime = serializers.CharField() value = serializers.DecimalField(source='value__avg', max_digits=8, decimal_places=3)
class OptimizedPresetSerializer(rf_serializers.Serializer): size = do_serializers.SizeSerializer() preset = serializers.PresetSerializer() quantity = rf_serializers.IntegerField() price = rf_serializers.DecimalField(max_digits=22, decimal_places=10)
class BenthicPITCSVSerializer(CollectRecordCSVSerializer): protocol = "benthicpit" sample_unit = "benthic_transect" observations_fields = ["data__obs_benthic_pits"] ordering_field = "data__obs_benthic_pits__interval" additional_group_fields = CollectRecordCSVSerializer.additional_group_fields.copy( ) additional_group_fields.append("data__benthic_transect__label") header_map = CollectRecordCSVSerializer.header_map.copy() header_map.update({ "Sample time": "data__benthic_transect__sample_time", "Depth *": "data__benthic_transect__depth", "Visibility": "data__benthic_transect__visibility", "Current": "data__benthic_transect__current", "Relative depth": "data__benthic_transect__relative_depth", "Tide": "data__benthic_transect__tide", "Observation interval *": "data__obs_benthic_pits__interval", "Interval size *": "data__interval_size", "Interval start *": "data__interval_start", "Transect length surveyed *": "data__benthic_transect__len_surveyed", "Transect number *": "data__benthic_transect__number", "Transect label": "data__benthic_transect__label", "Reef slope": "data__benthic_transect__reef_slope", "Benthic attribute *": "data__obs_benthic_pits__attribute", "Growth form": "data__obs_benthic_pits__growth_form", }) data__benthic_transect__sample_time = serializers.TimeField( required=False, allow_null=True) data__benthic_transect__depth = serializers.DecimalField(max_digits=3, decimal_places=1) data__benthic_transect__visibility = LazyChoiceField( choices=visibility_choices, required=False, allow_null=True, allow_blank=True) data__benthic_transect__current = LazyChoiceField(choices=current_choices, required=False, allow_null=True, allow_blank=True) data__benthic_transect__relative_depth = LazyChoiceField( choices=relative_depth_choices, required=False, allow_null=True, allow_blank=True, ) data__benthic_transect__tide = LazyChoiceField(choices=tide_choices, required=False, allow_null=True, allow_blank=True) data__interval_size = serializers.DecimalField(max_digits=4, decimal_places=2) data__interval_start = serializers.DecimalField(max_digits=4, decimal_places=2) data__benthic_transect__len_surveyed = serializers.DecimalField( max_digits=4, decimal_places=1) data__benthic_transect__number = serializers.IntegerField(min_value=0) data__benthic_transect__label = serializers.CharField(allow_blank=True, required=False, default="") data__benthic_transect__reef_slope = LazyChoiceField( choices=reef_slopes_choices, required=False, allow_null=True, allow_blank=True) data__obs_benthic_pits__interval = serializers.DecimalField( max_digits=7, decimal_places=2) data__obs_benthic_pits__attribute = LazyChoiceField( choices=benthic_attributes_choices) data__obs_benthic_pits__growth_form = LazyChoiceField( choices=growth_form_choices, required=False, allow_null=True, allow_blank=True) def get_sample_event_time(self, row): return row.get("data__benthic_transect__sample_time") def clean(self, data): data = super().clean(data) interval_start = data.get("data__interval_start") if interval_start is None or interval_start.strip() == "": data["data__interval_start"] = data.get("data__interval_size") return data def validate(self, data): data = super().validate(data) return data
def refund(self, request, **kwargs): payment = self.get_object() amount = serializers.DecimalField( max_digits=10, decimal_places=2).to_internal_value( request.data.get('amount', str(payment.amount))) mark_refunded = request.data.get('mark_refunded', False) if payment.state != OrderPayment.PAYMENT_STATE_CONFIRMED: return Response({'detail': 'Invalid state of payment.'}, status=status.HTTP_400_BAD_REQUEST) full_refund_possible = payment.payment_provider.payment_refund_supported( payment) partial_refund_possible = payment.payment_provider.payment_partial_refund_supported( payment) available_amount = payment.amount - payment.refunded_amount if amount <= 0: return Response({'amount': ['Invalid refund amount.']}, status=status.HTTP_400_BAD_REQUEST) if amount > available_amount: return Response( { 'amount': [ 'Invalid refund amount, only {} are available to refund.' .format(available_amount) ] }, status=status.HTTP_400_BAD_REQUEST) if amount != payment.amount and not partial_refund_possible: return Response( { 'amount': ['Partial refund not available for this payment method.'] }, status=status.HTTP_400_BAD_REQUEST) if amount == payment.amount and not full_refund_possible: return Response( { 'amount': ['Full refund not available for this payment method.'] }, status=status.HTTP_400_BAD_REQUEST) r = payment.order.refunds.create( payment=payment, source=OrderRefund.REFUND_SOURCE_ADMIN, state=OrderRefund.REFUND_STATE_CREATED, amount=amount, provider=payment.provider) try: r.payment_provider.execute_refund(r) except PaymentException as e: r.state = OrderRefund.REFUND_STATE_FAILED r.save() return Response({'detail': 'External error: {}'.format(str(e))}, status=status.HTTP_400_BAD_REQUEST) else: payment.order.log_action( 'pretix.event.order.refund.created', { 'local_id': r.local_id, 'provider': r.provider, }, user=self.request.user if self.request.user.is_authenticated else None, auth=self.request.auth) if payment.order.pending_sum > 0: if mark_refunded: mark_order_refunded( payment.order, user=self.request.user if self.request.user.is_authenticated else None, auth=self.request.auth) else: payment.order.status = Order.STATUS_PENDING payment.order.set_expires( now(), payment.order.event.subevents.filter( id__in=payment.order.positions.values_list( 'subevent_id', flat=True))) payment.order.save(update_fields=['status', 'expires']) return Response(OrderRefundSerializer(r).data, status=status.HTTP_200_OK)
class ProductSerializer(serializers.ModelSerializer): is_on_sale = serializers.BooleanField(read_only=True) current_price = serializers.FloatField(read_only=True) description = serializers.CharField(min_length=2, max_length=200) cart_items = serializers.SerializerMethodField() # price = serializers.DecimalField(min_value=1.00, max_value=100000) price = serializers.DecimalField( min_value=1.00, max_value=100000, max_digits=None, decimal_places=2, ) sale_start = serializers.DateTimeField( required=False, input_formats=['%I:%M %p %d %B %Y'], format=None, allow_null=True, help_text='Accepted format is "12:00 PM 16 April 2019"', style={ 'input_type': 'text', 'placeholder': '12:01 PM 28 July 2019' }) sale_end = serializers.DateTimeField( required=False, input_formats=['%I:%M %p %d %B %Y'], format=None, allow_null=True, help_text='Accepted format is "12:00 PM 16 April 2019"', style={ 'input_type': 'text', 'placeholder': '12:01 PM 28 July 2019' }) photo = serializers.ImageField(default=None) warranty = serializers.FileField(write_only=True, default=None) class Meta: model = Product fields = ( 'id', 'name', 'description', 'price', 'sale_start', 'sale_end', 'is_on_sale', 'current_price', 'cart_items', 'photo', 'warranty', ) def get_cart_items(self, instance): items = ShoppingCartItem.objects.filter(product=instance) return CartItemSerializer(items, many=True).data def update(self, instance, validated_data): if validated_data.get('warranty', None): instance.description += '\n\nWarranty Information:\n' instance.description += b'; '.join( validated_data['warranty'].readlines()).decode() return super().update(instance, validated_data) def create(self, validated_data): validated_data.pop('warranty') return Product.objects.create(**validated_data) def to_representation(self, instance): data = super().to_representation(instance) data['is_on_sale'] = instance.is_on_sale() data['current_price'] = instance.current_price() return data
class MarkupSerializer(serializers.Serializer): """Serializer for cost markup.""" value = serializers.DecimalField(required=False, max_digits=19, decimal_places=10, coerce_to_string=True) unit = serializers.ChoiceField(choices=MARKUP_CHOICES, required=False)
class ProjectModelSerializer(DynamicFieldsModelSerializer): """Project model serializer.""" creator = serializers.StringRelatedField() donations = serializers.SerializerMethodField() amount = serializers.SerializerMethodField() # Nested # workers = serializers.SerializerMethodField() # jobs = serializers.SerializerMethodField() publications = serializers.SerializerMethodField() califications = serializers.SerializerMethodField() calified = serializers.SerializerMethodField() # Filtering report fields filter_amount = serializers.DecimalField(read_only=True, max_digits=12, decimal_places=2) publication_likes = serializers.IntegerField(read_only=True) # choices category = serializers.CharField(source="get_category_display") class Meta: """Meta class.""" model = Project fields = ( 'title', 'slug_name', 'description', 'cost', 'amount', 'donations', 'reputation', 'category', 'calified', 'creator', 'finished', 'created', # 'workers', 'jobs', 'publications', 'califications', # 'activities', # Filtered report fields 'filter_amount', 'publication_likes', ) read_only_fields = ( 'reputation', 'creator', 'cost', 'slug_name', # 'finished' ) # def get_workers(self, obj): # """Return users that works in the project.""" # view = self.context.get('view', None) # fields = WorkerModelSerializer.Meta.fields # if view is not None: # # Projects # if view.view_name == 'projects' and view.action in view.fields_to_return: # fields = view.fields_to_return[view.action]['workers'] # return WorkerModelSerializer( # Worker.objects.filter(worker__isnull=False, project=obj), # many=True, # fields=fields, # context=self.context # ).data # def get_jobs(self, obj): # """Handle jobs for workers.""" # view = self.context.get('view', None) # fields = WorkerModelSerializer.Meta.fields # if view is not None: # # Projects # if view.view_name == 'projects' and view.action in view.fields_to_return: # fields = view.fields_to_return[view.action]['jobs'] # return WorkerModelSerializer( # Worker.objects.filter(worker__isnull=True, project=obj), # many=True, # fields=fields, # context=self.context # ).data def get_publications(self, obj): """Reurn publications with PublicationModelSerializer.""" from apps.publications.serializers import PublicationModelSerializer view = self.context.get('view', None) fields = ( 'id', 'user', 'project', 'description', 'comments', 'likes', 'picture', 'created', 'updated', 'liked', ) if view is not None: # Projects if view.view_name == 'projects' and view.action in view.fields_to_return: fields = view.fields_to_return[view.action]['publications'] return PublicationModelSerializer( Publication.objects.filter(project=obj), many=True, fields=fields, context=self.context ).data def get_califications(self, obj): """Return calification serializer representation.""" # Serializer from apps.califications.serializers import CalificationToProjectModelSerializer return CalificationToProjectModelSerializer( CalificationToProject.objects.filter(project=obj), many=True ).data def get_amount(self, obj): """Return sum of donations.""" queryset = Donation.objects.filter(project=obj).values_list('amount') amounts = [item[0] for item in queryset] return sum(amounts) def get_donations(self, obj): """Return num of donations.""" return Donation.objects.filter(project=obj).count() def get_calified(self, obj): """Return if request user already calified the project.""" request = self.context.get('request', None) if request is not None: try: CalificationToProject.objects.get( _from=self.context['request'].user, project=obj ) return True except CalificationToProject.DoesNotExist: return False return False def update(self, instance, data): """Handle update to profile.""" # Updating user instance instance.title = data.get('title', instance.title) instance.description = data.get('description', instance.description) instance.category = int( data.get('get_category_display', instance.category)) instance.finished = int(data.get('finished', instance.finished)) instance.save() return instance
class ObjectClassFinancialSpendingSerializer(serializers.Serializer): major_object_class_code = serializers.CharField() major_object_class_name = serializers.CharField() obligated_amount = serializers.DecimalField(None, 2)
class ProductSerializer(serializers.ModelSerializer): """ Common serializer for our product model. """ price = serializers.DecimalField(source='get_unit_price', max_digits=10, decimal_places=3, read_only=True) availability = serializers.SerializerMethodField() product_type = serializers.CharField(read_only=True) product_model = serializers.CharField(read_only=True) product_url = serializers.URLField(source='get_absolute_url', read_only=True) detail_url = serializers.CharField(read_only=True, source='get_detail_url') units = serializers.ListField(source='get_units', read_only=True) media = serializers.SerializerMethodField() class Meta: model = ProductModel fields = '__all__' def __init__(self, *args, **kwargs): kwargs.setdefault('label', 'catalog') super(ProductSerializer, self).__init__(*args, **kwargs) def get_price(self, product): #TODO: not used with request price = product.get_price(self.context['request']) # TODO: check if this can be simplified using str(product.get_price(...)) if six.PY2: return u'{:f}'.format(price) return '{:f}'.format(price) def get_availability(self, product): return product.get_availability(self.context['request']) def get_media(self, entity): return self.render_html(entity, 'media') def render_html(self, product, postfix): """ Return a HTML snippet containing a rendered summary for this product. Build a template search path with `postfix` distinction. """ if not self.label: msg = "The Product Serializer must be configured using a `label` field." raise exceptions.ImproperlyConfigured(msg) app_label = product._meta.app_label.lower() request = self.context['request'] cache_key = 'product:{0}|{1}-{2}-{3}-{4}-{5}'.format(product.id, app_label, self.label, product.product_model, postfix, get_language_from_request(request)) content = cache.get(cache_key) if content: return mark_safe(content) params = [ (app_label, self.label, product.product_model, postfix), (app_label, self.label, 'product', postfix), ('shop', self.label, 'product', postfix), ] try: template = select_template(['{0}/products/{1}-{2}-{3}.html'.format(*p) for p in params]) except TemplateDoesNotExist: return SafeText("<!-- no such template: '{0}/products/{1}-{2}-{3}.html' -->".format(*params[0])) # when rendering emails, we require an absolute URI, so that media can be accessed from # the mail client absolute_base_uri = request.build_absolute_uri('/').rstrip('/') context = {'product': product, 'ABSOLUTE_BASE_URI': absolute_base_uri} content = strip_spaces_between_tags(template.render(context, request).strip()) cache.set(cache_key, content, app_settings.CACHE_DURATIONS['product_html_snippet']) return mark_safe(content)
class AgenciesFinancialBalancesSerializer(serializers.Serializer): budget_authority_amount = serializers.DecimalField(None, 2) obligated_amount = serializers.DecimalField(None, 2) outlay_amount = serializers.DecimalField(None, 2)
class SetModeRequestSerializer(serializers.Serializer): room_id = serializers.IntegerField() ac_status = serializers.CharField(max_length=10) # temp = serializers.DecimalField(max_digits=3, decimal_places=1) target_temp = serializers.DecimalField(max_digits=3, decimal_places=1)
class FederalAccountByObligationSerializer(serializers.Serializer): id = serializers.CharField() agency_name = serializers.CharField() account_title = serializers.CharField() obligated_amount = serializers.DecimalField(None, 2)
class BaseApplicationSerializer(serializers.ModelSerializer): readonly = serializers.SerializerMethodField(read_only=True) licence_type_short_name = serializers.ReadOnlyField() documents_url = serializers.SerializerMethodField() character_check_status = serializers.SerializerMethodField(read_only=True) application_fee = serializers.DecimalField(max_digits=8, decimal_places=2, coerce_to_string=False) licence_fee = serializers.DecimalField(max_digits=8, decimal_places=2, coerce_to_string=False) class_name = serializers.SerializerMethodField(read_only=True) activity_type_names = serializers.SerializerMethodField(read_only=True) amendment_requests = serializers.SerializerMethodField(read_only=True) can_current_user_edit = serializers.SerializerMethodField(read_only=True) if settings.WC_VERSION != "1.0": payment_status = serializers.SerializerMethodField(read_only=True) assigned_officer = serializers.CharField( source='assigned_officer.get_full_name') can_be_processed = serializers.SerializerMethodField(read_only=True) class Meta: model = Application fields = ( 'id', 'activity', 'title', 'region', 'data', 'schema', 'licence_type_data', 'licence_type_name', 'licence_type_short_name', 'licence_category', 'customer_status', 'processing_status', 'review_status', #'hard_copy', 'applicant', 'org_applicant', 'proxy_applicant', 'submitter', 'previous_application', 'lodgement_number', 'lodgement_date', 'documents', 'conditions', 'readonly', 'can_user_edit', 'can_user_view', 'has_amendment', 'amendment_requests', 'documents_url', 'id_check_status', 'character_check_status', 'application_fee', 'licence_fee', 'class_name', 'activity_type_names', 'can_current_user_edit') if settings.WC_VERSION != "1.0": fields += ('payment_status', 'assigned_officer', 'can_be_processed') read_only_fields = ('documents', ) def get_documents_url(self, obj): return '/media/applications/{}/documents/'.format(obj.id) def get_readonly(self, obj): return False def get_processing_status(self, obj): return obj.get_processing_status_display() def get_id_check_status(self, obj): return obj.get_id_check_status_display() def get_character_check_status(self, obj): return obj.get_character_check_status_display() def get_review_status(self, obj): return obj.get_review_status_display() def get_customer_status(self, obj): return obj.get_customer_status_display() def get_payment_status(self, obj): return obj.payment_status def get_class_name(self, obj): for item in obj.licence_type_data: if item == "name": return obj.licence_type_data["name"] return obj.licence_type_data["id"] def get_activity_type_names(self, obj): activity_type = [] for item in obj.licence_type_data["activity_type"]: if "short_name" in item: activity_type.append(item["short_name"]) else: activity_type.append(item["name"]) return activity_type def get_amendment_requests(self, obj): amendment_request_data = [] # qs = obj.amendment_requests # qs = qs.filter(status = 'requested') # if qs.exists(): # for item in obj.amendment_requests: # print("printing from serializer") # print(item.id) # print(str(item.licence_activity_type.name)) # print(item.licence_activity_type.id) # amendment_request_data.append({"licence_activity_type":str(item.licence_activity_type),"id":item.licence_activity_type.id}) return amendment_request_data def get_can_be_processed(self, obj): return obj.processing_status == 'under_review' def get_can_current_user_edit(self, obj): result = False is_proxy_applicant = False is_in_org_applicant = False is_officer = helpers.is_officer(self.context['request']) is_submitter = obj.submitter == self.context['request'].user if obj.proxy_applicant: is_proxy_applicant = obj.proxy_applicant == self.context[ 'request'].user if obj.org_applicant: user_orgs = [ org.id for org in self.context['request'].user. wildlifecompliance_organisations.all() ] is_in_org_applicant = obj.org_applicant_id in user_orgs if obj.can_user_edit and (is_officer or is_submitter or is_proxy_applicant or is_in_org_applicant): result = True return result
class QuerySigninSerializer(serializers.Serializer): id = serializers.DecimalField(max_digits=10, decimal_places=0)
class TransferSerializer(serializers.ModelSerializer): debit_signature = SignatureSerializer(write_only=True) debit_balance_signature = SignatureSerializer(write_only=True) debit_balance = serializers.DecimalField( max_digits=80, decimal_places=0, validators=[MinValueValidator(Decimal('0'))], write_only=True) wallet = WalletSerializer() recipient = WalletSerializer() sender_active_state = ActiveStateSerializer(read_only=True) time = serializers.IntegerField(source='get_timestamp', read_only=True) class Meta: model = Transfer fields = ('id', 'wallet', 'amount', 'time', 'eon_number', 'sender_active_state', 'recipient', 'nonce', 'debit_signature', 'debit_balance_signature', 'debit_balance', 'position', 'tx_id') read_only_fields = ('id', 'time', 'sender_active_state', 'position', 'tx_id') error_codes = [ ErrorCode.INVALID_DEBIT_AMOUNT, ErrorCode.CREDIT_WALLET_NOT_ADMITTED, ErrorCode.DEBIT_WALLET_NOT_ADMITTED, ErrorCode.DEBIT_CREDIT_WALLET_ADDRESS_MATCH, ErrorCode.EON_NUMBER_OUT_OF_SYNC, ErrorCode.DEBIT_WALLET_EXCEEDED_SLA, ErrorCode.CREDIT_WALLET_EXCEEDED_SLA, ErrorCode.DEBIT_WALLET_CANNOT_ADD_TRANSACTION, ErrorCode.CREDIT_WALLET_CANNOT_ADD_TRANSACTION, ErrorCode.DEBIT_WALLET_OVERSPENDING, ErrorCode.DEBIT_WALLET_BALANCE_MARKER_EXCEED_BALANCE, ErrorCode.INVALID_DEBIT_BALANCE_SIGNATURE, ErrorCode.INVALID_DEBIT_SIGNATURE, ] # noinspection PyMethodMayBeStatic def validate_amount(self, value): if value < 0: raise serializers.ValidationError( detail='', code=ErrorCode.INVALID_DEBIT_AMOUNT) return value # noinspection PyMethodMayBeStatic def validate(self, attrs): wallet = attrs.get('wallet') recipient = attrs.get('recipient') if recipient.registration_operator_authorization is None: raise serializers.ValidationError( detail='', code=ErrorCode.CREDIT_WALLET_NOT_ADMITTED) if wallet.registration_operator_authorization is None: raise serializers.ValidationError( detail='', code=ErrorCode.DEBIT_WALLET_NOT_ADMITTED) if wallet == recipient: raise serializers.ValidationError( detail='', code=ErrorCode.DEBIT_CREDIT_WALLET_ADDRESS_MATCH) return attrs def create(self, validated_data): active_state_signature_data = validated_data.pop('debit_signature') wallet = validated_data.pop('wallet') recipient = validated_data.pop('recipient') # get current eon current_eon = LocalViewInterface.latest().eon_number() # transfer eon should be the current eon number if validated_data.pop('eon_number') != current_eon: raise serializers.ValidationError( detail='', code=ErrorCode.EON_NUMBER_OUT_OF_SYNC) # TODO refactor this such that the recipient is only locked after the sender's details are verified wallets = sorted([wallet, recipient], key=lambda w: w.trail_identifier) with RootCommitment.read_write_lock( suffix=current_eon, auto_renewal=False), wallets[0].lock( auto_renewal=False), wallets[1].lock(auto_renewal=False): if RootCommitment.objects.filter(eon_number=current_eon + 1).exists(): raise serializers.ValidationError( detail='', code=ErrorCode.EON_NUMBER_OUT_OF_SYNC) transfer = Transfer(wallet=wallet, amount=validated_data.pop('amount'), eon_number=current_eon, recipient=recipient, nonce=validated_data.pop('nonce'), passive=True) wallet_view_context = WalletTransferContext(wallet=wallet, transfer=transfer) recipient_view_context = WalletTransferContext(wallet=recipient, transfer=transfer) # Minimal SLA if not wallet.is_sla_exempt() and not recipient.is_sla_exempt(): if not wallet.has_valid_sla(): sender_transfers_list = wallet_view_context.authorized_transfers_list( only_appended=False, force_append=True) if len(sender_transfers_list) > settings.SLA_THRESHOLD: raise serializers.ValidationError( detail='', code=ErrorCode.DEBIT_WALLET_EXCEEDED_SLA) elif not recipient.has_valid_sla(): recipient_transfers_list = recipient_view_context.authorized_transfers_list( only_appended=False, force_append=True) if len(recipient_transfers_list) > settings.SLA_THRESHOLD: raise serializers.ValidationError( detail='', code=ErrorCode.CREDIT_WALLET_EXCEEDED_SLA) # Ensure sender log consistency can_append_to_sender_log = wallet_view_context.can_schedule_transfer( ) if can_append_to_sender_log is not True: raise serializers.ValidationError( detail='Sender: {}'.format(can_append_to_sender_log), code=ErrorCode.DEBIT_WALLET_CANNOT_ADD_TRANSACTION) # Ensure recipient log consistency can_append_to_recipient_log = recipient_view_context.can_schedule_transfer( ) if can_append_to_recipient_log is not True: raise serializers.ValidationError( detail='Recipient: {}'.format(can_append_to_recipient_log), code=ErrorCode.CREDIT_WALLET_CANNOT_ADD_TRANSACTION) # Ensure transfer consistency can_spend, currently_available_funds = wallet_view_context.can_send_transfer( current_eon_number=current_eon, using_only_appended_funds=False) if can_spend is not True: raise serializers.ValidationError( detail=can_spend, code=ErrorCode.DEBIT_WALLET_OVERSPENDING) # Validate data concise_balance_marker_signature_data = validated_data.pop( 'debit_balance_signature') concise_balance_marker_amount = validated_data.pop('debit_balance') if concise_balance_marker_amount > currently_available_funds - transfer.amount: raise serializers.ValidationError( detail='', code=ErrorCode.DEBIT_WALLET_BALANCE_MARKER_EXCEED_BALANCE) concise_balance_marker = MinimumAvailableBalanceMarker( wallet=wallet, eon_number=transfer.eon_number, amount=concise_balance_marker_amount) concise_balance_marker_checksum = hex_value( concise_balance_marker.checksum()) concise_balance_marker_signature = Signature( wallet=transfer.wallet, checksum=concise_balance_marker_checksum, value=concise_balance_marker_signature_data.get('value')) if not concise_balance_marker_signature.is_valid(): raise serializers.ValidationError( detail='', code=ErrorCode.INVALID_DEBIT_BALANCE_SIGNATURE) tx_set_tree = wallet_view_context.optimized_authorized_transfers_tree( ) tx_set_hash = hex_value(tx_set_tree.root_hash()) transfer_index = tx_set_tree.merkle_tree_nonce_map.get( transfer.nonce) transfer_proof = tx_set_tree.proof(transfer_index) highest_spendings, highest_gains = wallet_view_context.off_chain_actively_sent_received_amounts( eon_number=transfer.eon_number, only_appended=False) active_state = ActiveState(wallet=wallet, updated_spendings=highest_spendings + transfer.amount, updated_gains=highest_gains, tx_set_hash=tx_set_hash, tx_set_proof_hashes=transfer_proof, tx_set_index=transfer_index, eon_number=transfer.eon_number) checksum = hex_value(active_state.checksum()) active_state_signature = Signature( wallet=transfer.wallet, checksum=checksum, value=active_state_signature_data.get('value')) if not active_state_signature.is_valid(): raise serializers.ValidationError( detail='', code=ErrorCode.INVALID_DEBIT_SIGNATURE) transfer.position = recipient_view_context.off_chain_passively_received_amount( eon_number=transfer.eon_number, only_appended=False) # locking context covers saving the state as well to make sure checkpoint creation is consistent with transaction.atomic(): Signature.objects.bulk_create( [concise_balance_marker_signature, active_state_signature]) concise_balance_marker.signature = concise_balance_marker_signature concise_balance_marker.save() active_state.wallet_signature = active_state_signature active_state.operator_signature = active_state.sign_active_state( settings.HUB_OWNER_ACCOUNT_ADDRESS, settings.HUB_OWNER_ACCOUNT_KEY) active_state.save() transfer.sender_active_state = active_state transfer.sender_balance_marker = concise_balance_marker # cache transfer index in sender active set transfer.sender_merkle_index = transfer_index # transfer.sender_merkle_root_cache = tx_set_hash # cache active set merkle mountains height array and hash array for recipient active set transfer.sender_merkle_hash_cache, transfer.sender_merkle_height_cache = tx_set_tree.merkle_cache_stacks( ) transfer.complete = True transfer.appended = True transfer.processed = True transfer.save() if transfer.appended: operator_celery.send_task('auditor.tasks.on_transfer_confirmation', args=[transfer.id]) return transfer
class ExpensesTotalSerializer(serializers.Serializer): amount = serializers.DecimalField(max_digits=20, decimal_places=2, read_only=True) currency = serializers.PrimaryKeyRelatedField(read_only=True)