Fakultas Ilmu Komputer UI

serializers.py 19.7 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
class BankAccountTransferDestinationSerializer(serializers.ModelSerializer):
    class Meta:
226
        fields = ['id', 'bank_name', 'bank_code_number', 'bank_account_number','bank_account_name']
WILLIAM GATES's avatar
WILLIAM GATES committed
227
228
229
230
        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
268
            'total_profit',
            'unit'
269
270
271
272
        ]
        model = models.Product
        read_only_fields = ['id', 'code']

Rayhan Muzakki's avatar
Rayhan Muzakki committed
273
    
Rayhan Muzakki's avatar
Rayhan Muzakki committed
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
329

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
330
331
    transaction_items = TransactionItemSerializer(many=True, read_only=True)
    transaction_item_subtotal = serializers.SerializerMethodField('get_transaction_item_subtotal')
332
    subtotal = serializers.SerializerMethodField('get_subtotal')
inez nabila's avatar
inez nabila committed
333

334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
    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
354
            'user_bank_name',
355
            'user_bank_account_name',
WILLIAM GATES's avatar
WILLIAM GATES committed
356
357
358
            'bank_account_transfer_destination',
            'transfer_destination_bank_name',
            'transfer_destination_bank_account_name',
359
            'transfer_destination_bank_code_number',
WILLIAM GATES's avatar
WILLIAM GATES committed
360
            'transfer_destination_bank_account_number',
361
362
            'created_at',
            'updated_at',
WILLIAM GATES's avatar
WILLIAM GATES committed
363
364
            'transaction_items',
            'transaction_item_subtotal',
365
            'subtotal',
rayhan muzakki's avatar
rayhan muzakki committed
366
            'batch',
367
            'batch_name',
368
            'end_date'
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
        ]
        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
386
            'user_bank_name',
387
            'user_bank_account_name',
WILLIAM GATES's avatar
WILLIAM GATES committed
388
389
390
            'bank_account_transfer_destination',
            'transfer_destination_bank_name',
            'transfer_destination_bank_account_name',
391
            'transfer_destination_bank_code_number',
WILLIAM GATES's avatar
WILLIAM GATES committed
392
            'transfer_destination_bank_account_number',
393
394
            'created_at',
            'updated_at',
rayhan muzakki's avatar
rayhan muzakki committed
395
            'batch',
396
397
            'batch_name',
            'end_date'
398
399
        ]

WILLIAM GATES's avatar
WILLIAM GATES committed
400
401
402
403
404
405
    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()

406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
    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
429
    total_donation_amount = serializers.SerializerMethodField('get_total_donation_amount')
WILLIAM GATES's avatar
WILLIAM GATES committed
430

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

WILLIAM GATES's avatar
WILLIAM GATES committed
450
451
452
    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
453
454
                donation_status='002',
                donation_type='CSH'
WILLIAM GATES's avatar
WILLIAM GATES committed
455
            )
WILLIAM GATES's avatar
WILLIAM GATES committed
456
        )
WILLIAM GATES's avatar
WILLIAM GATES committed
457
        return str(total_donation_amount)
WILLIAM GATES's avatar
WILLIAM GATES committed
458

459
460
461
462
463
464
465
466
467
468
469
470
471
    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)


472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
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)


497
498
class ProgramDonationSerializer(serializers.ModelSerializer):
    program_code = serializers.ReadOnlyField(source='program.code')
WILLIAM GATES's avatar
WILLIAM GATES committed
499
    user_username = serializers.ReadOnlyField(source='user.username')
500
501
502
503
504
505
506
507
508
509
    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
510
511
            'user',
            'user_username',
512
513
514
            'user_full_name',
            'user_phone_number',
            'program_name',
515
            'donation_type',
516
            'donation_status',
517
            'amount',
518
519
            'readable_donation_status',
            'proof_of_bank_transfer',
WILLIAM GATES's avatar
WILLIAM GATES committed
520
            'user_bank_name',
521
            'user_bank_account_name',
WILLIAM GATES's avatar
WILLIAM GATES committed
522
523
524
            'bank_account_transfer_destination',
            'transfer_destination_bank_name',
            'transfer_destination_bank_account_name',
525
            'transfer_destination_bank_code_number',
WILLIAM GATES's avatar
WILLIAM GATES committed
526
            'transfer_destination_bank_account_number',
527
528
529
530
            'goods_quantity',
            'goods_description',
            'delivery_method',
            'delivery_address',
531
532
533
534
535
536
537
538
            'created_at',
            'updated_at',
        ]
        model = models.ProgramDonation
        read_only_fields = [
            'id',
            'donation_number',
            'program',
WILLIAM GATES's avatar
WILLIAM GATES committed
539
            'user',
540
541
542
            'user_full_name',
            'user_phone_number',
            'program_name',
543
            'donation_type',
544
545
            'amount',
            'proof_of_bank_transfer',
WILLIAM GATES's avatar
WILLIAM GATES committed
546
            'user_bank_name',
547
            'user_bank_account_name',
WILLIAM GATES's avatar
WILLIAM GATES committed
548
549
550
            'bank_account_transfer_destination',
            'transfer_destination_bank_name',
            'transfer_destination_bank_account_name',
551
            'transfer_destination_bank_code_number',
WILLIAM GATES's avatar
WILLIAM GATES committed
552
            'transfer_destination_bank_account_number',
553
554
555
556
557
558
559
560
561
562
563
564
565
566
            '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
567
class HelpContactConfigSerializer(serializers.ModelSerializer):
568
    class Meta:
WILLIAM GATES's avatar
WILLIAM GATES committed
569
570
        fields = ['email']
        model = models.HelpContactConfig
571
572
573
574
575
576
577
578
579
580
581
582
583


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
584
585
586
587

class BatchSerializer(serializers.ModelSerializer):
    class Meta:
        fields = [
inez nabila's avatar
inez nabila committed
588
            'id',
inez nabila's avatar
inez nabila committed
589
            'batch_name',
590
591
            'start_date',
            'end_date',
inez nabila's avatar
inez nabila committed
592
593
594
595
596
597
598
599
600
            'shipping_cost'
        ]
        model = models.Batch

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