Fakultas Ilmu Komputer UI

serializers.py 19.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import decimal

from django.contrib.auth import password_validation
from django.core import exceptions
from django.utils.translation import gettext_lazy as _
from phonenumber_field import serializerfields
from rest_framework import serializers

from api import constants, models


class PhoneNumberSerializer(serializers.Serializer): # pylint: disable=abstract-method
    phone_number = serializerfields.PhoneNumberField(label=_('Phone number'))


class OTPSerializer(serializers.Serializer): # pylint: disable=abstract-method
    otp = serializers.CharField(label=_('OTP'), max_length=6)


class CartUpdateSerializer(serializers.Serializer): # pylint: disable=abstract-method
    product = serializers.UUIDField(label=_('Product'))
    quantity = serializers.IntegerField(label=_('Quantity'), min_value=0)


class CartCheckoutSerializer(serializers.Serializer): # pylint: disable=abstract-method
    payment_method = serializers.ChoiceField(
        choices=constants.PAYMENT_METHOD_CHOICES,
        label=_('Payment method')
    )
    donation = serializers.DecimalField(
        decimal_places=2,
        default=decimal.Decimal('0'),
        label=_('Donation'),
        max_digits=12,
        min_value=decimal.Decimal('0')
    )


class CartUploadPOPSerializer(serializers.Serializer): # pylint: disable=abstract-method
    transaction = serializers.UUIDField(label=_('Transaction'))
    proof_of_payment = serializers.ImageField(label=_('Proof of payment'))
WILLIAM GATES's avatar
WILLIAM GATES committed
42
43
44
45
    user_bank_name = serializers.CharField(
        label=_('User bank name'),
        max_length=100
    )
46
47
48
49
    user_bank_account_name = serializers.CharField(
        label=_('User bank account name'),
        max_length=200
    )
WILLIAM GATES's avatar
WILLIAM GATES committed
50
51
    bank_account_transfer_destination = serializers.UUIDField(
        label=_('Bank account transfer destination')
52
53
54
55
56
57
58
59
60
    )


class CartCompleteOrCancelTransactionSerializer(serializers.Serializer): # pylint: disable=abstract-method
    transaction = serializers.UUIDField(label=_('Transaction'))


class DonationCreateSerializer(serializers.Serializer): # pylint: disable=abstract-method
    program = serializers.UUIDField(label=_('Program'))
61
62
63
64
    donation_type = serializers.ChoiceField(
        choices=constants.DONATION_TYPE_CHOICES,
        label=_('Donation type')
    )
65
66
67
68
    amount = serializers.DecimalField(
        decimal_places=2,
        label=_('Amount'),
        max_digits=12,
Rayhan Muzakki's avatar
Rayhan Muzakki committed
69
70
        min_value=decimal.Decimal('0.01'),
        required=False
71
    )
72
73
74
75
    proof_of_bank_transfer = serializers.ImageField(
        label=_('Proof of bank transfer'),
        required=False
    )
WILLIAM GATES's avatar
WILLIAM GATES committed
76
77
    user_bank_name = serializers.CharField(
        label=_('User bank name'),
Rayhan Muzakki's avatar
Rayhan Muzakki committed
78
79
        max_length=100,
        required=False
WILLIAM GATES's avatar
WILLIAM GATES committed
80
    )
81
82
    user_bank_account_name = serializers.CharField(
        label=_('User bank account name'),
Rayhan Muzakki's avatar
Rayhan Muzakki committed
83
84
        max_length=200,
        required=False
85
    )
WILLIAM GATES's avatar
WILLIAM GATES committed
86
    bank_account_transfer_destination = serializers.UUIDField(
Rayhan Muzakki's avatar
Rayhan Muzakki committed
87
88
        label=_('Bank account transfer destination'),
        required=False
89
    )
90
91
92
93
    goods_quantity = serializers.DecimalField(
        decimal_places=0,
        label=_('Goods quantity'),
        max_digits=12,
Rayhan Muzakki's avatar
Rayhan Muzakki committed
94
95
        min_value=decimal.Decimal('1'),
        required=False
96
    )
Rayhan Muzakki's avatar
Rayhan Muzakki committed
97
    goods_description = serializers.CharField(
98
        label=_('Goods description'),
Rayhan Muzakki's avatar
Rayhan Muzakki committed
99
100
        max_length=200,
        required=False
101
102
103
    )
    delivery_method = serializers.ChoiceField(
        choices=constants.DONATION_GOODS_DELIVERY_METHOD_CHOICES,
Rayhan Muzakki's avatar
Rayhan Muzakki committed
104
105
        label=_('Goods delivery method'),
        required=False
106
107
108
    )
    delivery_address = serializers.CharField(
        label=_('Goods delivery address'),
Rayhan Muzakki's avatar
Rayhan Muzakki committed
109
110
        max_length=200,
        required=False
111
    )
112
113
114
115


class DonationReuploadProofOfBankTransferSerializer(serializers.Serializer): # pylint: disable=abstract-method
    program_donation = serializers.UUIDField(label=_('Program donasi'))
WILLIAM GATES's avatar
WILLIAM GATES committed
116
117
118
119
120
121
    amount = serializers.DecimalField(
        decimal_places=2,
        label=_('Amount'),
        max_digits=12,
        min_value=decimal.Decimal('0.01')
    )
122
    proof_of_bank_transfer = serializers.ImageField(label=_('Proof of bank transfer'))
WILLIAM GATES's avatar
WILLIAM GATES committed
123
124
    user_bank_name = serializers.CharField(
        label=_('User bank name'),
Rayhan Muzakki's avatar
Pbi67    
Rayhan Muzakki committed
125
        max_length=100,
WILLIAM GATES's avatar
WILLIAM GATES committed
126
    )
127
128
129
130
    user_bank_account_name = serializers.CharField(
        label=_('User bank account name'),
        max_length=200
    )
WILLIAM GATES's avatar
WILLIAM GATES committed
131
132
133
134
135
136
137
    bank_account_transfer_destination = serializers.UUIDField(
        label=_('Bank account transfer destination')
    )


class ReportTransactionSerializer(serializers.Serializer): # pylint: disable=abstract-method
    created_at_date_range_after = serializers.DateField(
WILLIAM GATES's avatar
WILLIAM GATES committed
138
        label=_('Created from date'),
WILLIAM GATES's avatar
WILLIAM GATES committed
139
140
141
        required=False
    )
    created_at_date_range_before = serializers.DateField(
WILLIAM GATES's avatar
WILLIAM GATES committed
142
        label=_('Created to date'),
WILLIAM GATES's avatar
WILLIAM GATES committed
143
144
145
146
147
148
        required=False
    )


class ReportProgramDonationSerializer(serializers.Serializer): # pylint: disable=abstract-method
    created_at_date_range_after = serializers.DateField(
WILLIAM GATES's avatar
WILLIAM GATES committed
149
        label=_('Created from date'),
WILLIAM GATES's avatar
WILLIAM GATES committed
150
151
152
        required=False
    )
    created_at_date_range_before = serializers.DateField(
WILLIAM GATES's avatar
WILLIAM GATES committed
153
        label=_('Created to date'),
WILLIAM GATES's avatar
WILLIAM GATES committed
154
        required=False
155
156
157
158
    )


class UserSerializer(serializers.ModelSerializer):
inez nabila's avatar
fixing    
inez nabila committed
159
160
161
    total_transactions = serializers.SerializerMethodField('get_total_transactions')
    total_program_donations_goods = serializers.SerializerMethodField('get_total_program_donations_goods')
    total_program_donations_cash = serializers.SerializerMethodField('get_total_program_donations_cash')
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176

    class Meta:
        extra_kwargs = {'password': {'write_only': True}}
        fields = [
            'id',
            'username',
            'password',
            'full_name',
            'phone_number',
            'address',
            'neighborhood',
            'hamlet',
            'urban_village',
            'sub_district',
            'profile_picture',
inez nabila's avatar
fixing    
inez nabila committed
177
178
179
            'total_transactions',
            'total_program_donations_goods',
            'total_program_donations_cash',
180
181
182
183
        ]
        model = models.User
        read_only_fields = ['id']

inez nabila's avatar
fixing    
inez nabila committed
184
185
186
187
188
189
190
191
192
    def get_total_transactions(self, obj): # pylint: disable=no-self-use
        return obj.transactions.filter(transaction_status='005').count()

    def get_total_program_donations_goods(self, obj): # pylint: disable=no-self-use
        return obj.program_donations.filter(donation_type='GDS').filter(donation_status='002').count()
    
    def get_total_program_donations_cash(self, obj): # pylint: disable=no-self-use
        return obj.program_donations.filter(donation_type='CSH').filter(donation_status='002').count()

193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
    def create(self, validated_data):
        password = validated_data.pop('password', None)
        instance = self.Meta.model(**validated_data)
        if password is not None:
            instance.set_password(password)
        instance.save()
        return instance

    def update(self, instance, validated_data):
        for attr, value in validated_data.items():
            if attr == 'password':
                instance.set_password(value)
            else:
                setattr(instance, attr, value)
        instance.save()
        return instance

    def validate(self, attrs):
        user = models.User(**attrs)
        password = attrs.get('password')
        errors = {}
        if password is not None:
            try:
                password_validation.validate_password(password=password, user=user)
            except exceptions.ValidationError as err:
                errors['password'] = list(err.messages)
        if errors:
            raise serializers.ValidationError(errors)
        return super().validate(attrs)


WILLIAM GATES's avatar
WILLIAM GATES committed
224
225
226
227
228
229
230
class BankAccountTransferDestinationSerializer(serializers.ModelSerializer):
    class Meta:
        fields = ['id', 'bank_name', 'bank_account_number', 'bank_account_name']
        model = models.BankAccountTransferDestination
        read_only_fields = ['id']


231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        fields = ['id', 'name', 'image']
        model = models.Category
        read_only_fields = ['id']


class SubcategorySerializer(serializers.ModelSerializer):
    category_name = serializers.ReadOnlyField(source='category.name')

    class Meta:
        fields = ['id', 'name', 'category', 'category_name', 'image']
        model = models.Subcategory
        read_only_fields = ['id']


class ProductSerializer(serializers.ModelSerializer):
    category = serializers.ReadOnlyField(source='subcategory.category.pk')
    category_name = serializers.ReadOnlyField(source='subcategory.category.name')
    subcategory_name = serializers.ReadOnlyField(source='subcategory.name')

    class Meta:
        fields = [
            'id',
            'code',
            'name',
            'category',
            'category_name',
            'subcategory',
            'subcategory_name',
            'description',
            'price',
            'stock',
264
265
            'modal',
            'profit',
266
            'image',
267
            'total_profit'
268
269
270
271
        ]
        model = models.Product
        read_only_fields = ['id', 'code']

Rayhan Muzakki's avatar
Rayhan Muzakki committed
272
    
Rayhan Muzakki's avatar
Rayhan Muzakki committed
273

274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328

class CartItemSerializer(serializers.ModelSerializer):
    product = ProductSerializer(read_only=True)

    class Meta:
        fields = ['id', 'product', 'quantity']
        model = models.CartItem
        read_only_fields = ['id', 'quantity']


class ShoppingCartSerializer(serializers.ModelSerializer):
    user_username = serializers.ReadOnlyField(source='user.username')
    cart_items = CartItemSerializer(many=True, read_only=True)
    cart_item_subtotal = serializers.SerializerMethodField('get_cart_item_subtotal')

    class Meta:
        fields = ['id', 'user', 'user_username', 'cart_items', 'cart_item_subtotal']
        model = models.ShoppingCart
        read_only_fields = ['id', 'user']

    def get_cart_item_subtotal(self, obj): # pylint: disable=no-self-use
        cart_item_subtotal = sum(
            cart_item.product.price * cart_item.quantity for cart_item in obj.cart_items.all()
        )
        return str(cart_item_subtotal)


class TransactionItemSerializer(serializers.ModelSerializer):
    product_code = serializers.ReadOnlyField(source='product.code')

    class Meta:
        fields = [
            'id',
            'product',
            'product_code',
            'product_name',
            'product_price',
            'quantity',
        ]
        model = models.TransactionItem
        read_only_fields = [
            'id',
            'product',
            'product_name',
            'product_price',
            'quantity',
        ]


class TransactionSerializer(serializers.ModelSerializer):
    user_username = serializers.ReadOnlyField(source='user.username')
    readable_payment_method = serializers.SerializerMethodField('get_readable_payment_method')
    readable_transaction_status = serializers.SerializerMethodField(
        'get_readable_transaction_status'
    )
WILLIAM GATES's avatar
WILLIAM GATES committed
329
330
    transaction_items = TransactionItemSerializer(many=True, read_only=True)
    transaction_item_subtotal = serializers.SerializerMethodField('get_transaction_item_subtotal')
331
    subtotal = serializers.SerializerMethodField('get_subtotal')
inez nabila's avatar
inez nabila committed
332

333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
    class Meta:
        fields = [
            'id',
            'transaction_number',
            'user',
            'user_username',
            'user_full_name',
            'user_phone_number',
            'shipping_address',
            'shipping_neighborhood',
            'shipping_hamlet',
            'shipping_urban_village',
            'shipping_sub_district',
            'shipping_costs',
            'payment_method',
            'readable_payment_method',
            'donation',
            'transaction_status',
            'readable_transaction_status',
            'proof_of_payment',
WILLIAM GATES's avatar
WILLIAM GATES committed
353
            'user_bank_name',
354
            'user_bank_account_name',
WILLIAM GATES's avatar
WILLIAM GATES committed
355
356
357
358
            'bank_account_transfer_destination',
            'transfer_destination_bank_name',
            'transfer_destination_bank_account_name',
            'transfer_destination_bank_account_number',
359
360
            'created_at',
            'updated_at',
WILLIAM GATES's avatar
WILLIAM GATES committed
361
362
            'transaction_items',
            'transaction_item_subtotal',
363
            'subtotal',
rayhan muzakki's avatar
rayhan muzakki committed
364
            'batch',
365
            'batch_name',
366
            'end_date'
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
        ]
        model = models.Transaction
        read_only_fields = [
            'id',
            'transaction_number',
            'user',
            'user_full_name',
            'user_phone_number',
            'shipping_address',
            'shipping_neighborhood',
            'shipping_hamlet',
            'shipping_urban_village',
            'shipping_sub_district',
            'shipping_costs',
            'payment_method',
            'donation',
            'proof_of_payment',
WILLIAM GATES's avatar
WILLIAM GATES committed
384
            'user_bank_name',
385
            'user_bank_account_name',
WILLIAM GATES's avatar
WILLIAM GATES committed
386
387
388
389
            'bank_account_transfer_destination',
            'transfer_destination_bank_name',
            'transfer_destination_bank_account_name',
            'transfer_destination_bank_account_number',
390
391
            'created_at',
            'updated_at',
rayhan muzakki's avatar
rayhan muzakki committed
392
            'batch',
393
394
            'batch_name',
            'end_date'
395
396
        ]

WILLIAM GATES's avatar
WILLIAM GATES committed
397
398
399
400
401
402
    def get_readable_payment_method(self, obj): # pylint: disable=no-self-use
        return obj.get_payment_method_display()

    def get_readable_transaction_status(self, obj): # pylint: disable=no-self-use
        return obj.get_transaction_status_display()

403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
    def get_transaction_item_subtotal(self, obj): # pylint: disable=no-self-use
        transaction_item_subtotal = sum(
            transaction_item.product_price * transaction_item.quantity
            for transaction_item in obj.transaction_items.all()
        )
        return str(transaction_item_subtotal)

    def get_subtotal(self, obj):
        subtotal = (decimal.Decimal(self.get_transaction_item_subtotal(obj)) + obj.shipping_costs +
                    obj.donation)
        return str(subtotal)

    def validate(self, attrs):
        transaction_status = attrs['transaction_status']
        errors = {}
        if transaction_status == '007':
            errors['transaction_status'] = _('Cannot update transaction status to failed.')
        if errors:
            raise serializers.ValidationError(errors)
        return super().validate(attrs)


class ProgramSerializer(serializers.ModelSerializer):
WILLIAM GATES's avatar
WILLIAM GATES committed
426
    total_donation_amount = serializers.SerializerMethodField('get_total_donation_amount')
WILLIAM GATES's avatar
WILLIAM GATES committed
427

428
429
430
431
432
433
434
435
436
437
    class Meta:
        fields = [
            'id',
            'code',
            'name',
            'description',
            'start_date_time',
            'end_date_time',
            'location',
            'speaker',
WILLIAM GATES's avatar
WILLIAM GATES committed
438
439
            'open_donation',
            'program_minutes',
440
            'poster_image',
WILLIAM GATES's avatar
WILLIAM GATES committed
441
442
            'link',
            'total_donation_amount',
443
444
445
446
        ]
        model = models.Program
        read_only_fields = ['id', 'code']

WILLIAM GATES's avatar
WILLIAM GATES committed
447
448
449
    def get_total_donation_amount(self, obj): # pylint: disable=no-self-use
        total_donation_amount = sum(
            program_donation.amount for program_donation in obj.program_donations.filter(
Rayhan Muzakki's avatar
Pbi4    
Rayhan Muzakki committed
450
451
                donation_status='002',
                donation_type='CSH'
WILLIAM GATES's avatar
WILLIAM GATES committed
452
            )
WILLIAM GATES's avatar
WILLIAM GATES committed
453
        )
WILLIAM GATES's avatar
WILLIAM GATES committed
454
        return str(total_donation_amount)
WILLIAM GATES's avatar
WILLIAM GATES committed
455

456
457
458
459
460
461
462
463
464
465
466
467
468
    def validate(self, attrs):
        instance = self.instance
        start_date_time = attrs.get('start_date_time', getattr(instance, 'start_date_time', None))
        end_date_time = attrs.get('end_date_time', getattr(instance, 'end_date_time', None))
        errors = {}
        if ((start_date_time is not None) and (end_date_time is not None) and
                (end_date_time <= start_date_time)):
            errors['end_date_time'] = _('End date time should be greater than start date time.')
        if errors:
            raise serializers.ValidationError(errors)
        return super().validate(attrs)


469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
class ProgramProgressSerializer(serializers.ModelSerializer):

    class Meta:
        fields = [
            'id',
            'program',
            'date',
            'description',
            'image',
        ]
        model = models.ProgramProgress
        read_only_fields = [
            'id',
            'program'
        ]

    def validate(self, attrs):
        instance = self.instance
        date = attrs.get('date', getattr(instance, 'date', None))
        errors = {}
        if errors:
            raise serializers.ValidationError(errors)
        return super().validate(attrs)


494
495
class ProgramDonationSerializer(serializers.ModelSerializer):
    program_code = serializers.ReadOnlyField(source='program.code')
WILLIAM GATES's avatar
WILLIAM GATES committed
496
    user_username = serializers.ReadOnlyField(source='user.username')
497
498
499
500
501
502
503
504
505
506
    readable_donation_status = serializers.SerializerMethodField(
        'get_readable_donation_status'
    )

    class Meta:
        fields = [
            'id',
            'donation_number',
            'program',
            'program_code',
WILLIAM GATES's avatar
WILLIAM GATES committed
507
508
            'user',
            'user_username',
509
510
511
            'user_full_name',
            'user_phone_number',
            'program_name',
512
            'donation_type',
513
            'donation_status',
514
            'amount',
515
516
            'readable_donation_status',
            'proof_of_bank_transfer',
WILLIAM GATES's avatar
WILLIAM GATES committed
517
            'user_bank_name',
518
            'user_bank_account_name',
WILLIAM GATES's avatar
WILLIAM GATES committed
519
520
521
522
            'bank_account_transfer_destination',
            'transfer_destination_bank_name',
            'transfer_destination_bank_account_name',
            'transfer_destination_bank_account_number',
523
524
525
526
            'goods_quantity',
            'goods_description',
            'delivery_method',
            'delivery_address',
527
528
529
530
531
532
533
534
            'created_at',
            'updated_at',
        ]
        model = models.ProgramDonation
        read_only_fields = [
            'id',
            'donation_number',
            'program',
WILLIAM GATES's avatar
WILLIAM GATES committed
535
            'user',
536
537
538
            'user_full_name',
            'user_phone_number',
            'program_name',
539
            'donation_type',
540
541
            'amount',
            'proof_of_bank_transfer',
WILLIAM GATES's avatar
WILLIAM GATES committed
542
            'user_bank_name',
543
            'user_bank_account_name',
WILLIAM GATES's avatar
WILLIAM GATES committed
544
545
546
547
            'bank_account_transfer_destination',
            'transfer_destination_bank_name',
            'transfer_destination_bank_account_name',
            'transfer_destination_bank_account_number',
548
549
550
551
552
553
554
555
556
557
558
559
560
561
            'created_at',
            'updated_at',
        ]

    def get_readable_donation_status(self, obj): # pylint: disable=no-self-use
        return obj.get_donation_status_display()


class AppConfigSerializer(serializers.ModelSerializer):
    class Meta:
        fields = ['send_sms']
        model = models.AppConfig


WILLIAM GATES's avatar
WILLIAM GATES committed
562
class HelpContactConfigSerializer(serializers.ModelSerializer):
563
    class Meta:
WILLIAM GATES's avatar
WILLIAM GATES committed
564
565
        fields = ['email']
        model = models.HelpContactConfig
566
567
568
569
570
571
572
573
574
575
576
577
578


class ShipmentConfigSerializer(serializers.ModelSerializer):
    class Meta:
        fields = [
            'hamlet',
            'urban_village',
            'sub_district',
            'same_hamlet_costs',
            'same_urban_village_different_hamlet_costs',
            'same_sub_district_different_urban_village_costs',
        ]
        model = models.ShipmentConfig
inez nabila's avatar
inez nabila committed
579
580
581
582

class BatchSerializer(serializers.ModelSerializer):
    class Meta:
        fields = [
inez nabila's avatar
inez nabila committed
583
            'id',
inez nabila's avatar
inez nabila committed
584
            'batch_name',
585
586
            'start_date',
            'end_date',
inez nabila's avatar
inez nabila committed
587
588
589
590
591
592
593
594
595
            'shipping_cost'
        ]
        model = models.Batch

class BatchCreateSerializer(serializers.Serializer): # pylint: disable=abstract-method
    batch_name = serializers.CharField(
        label=_('Batch name'),
        max_length=200
    )
596
597
    start_date = serializers.DateField(label=_('Start Date'))
    end_date = serializers.DateField(label=_('End Date'))
inez nabila's avatar
inez nabila committed
598
599
600
601
602
603
    shipping_cost = serializers.DecimalField(
        decimal_places=2,
        label=_('Amount'),
        max_digits=12,
        min_value=decimal.Decimal('0.01')
    )