Exemple #1
0
class ItemFieldSerializer(serializers.ModelSerializer):
    image = S3ObjectFieldSerializer()
    
    class Meta:
        model = AItem
        fields = ('description', 'id', 'width', 'depth', 'height', 'unit_price', 'total', 'quantity', 'image', 'status')
        read_only_fields = ('total', 'description', 'id', 'width', 'depth', 'height', 'unit_price', 'quantity', 'image', 'status')
Exemple #2
0
 def get_image(self, instance):
     """
     Get Supply Image
     """
     try:
         return S3ObjectFieldSerializer(instance.supply.image).data
     except AttributeError as e:
         logger.warn(e)
         return None
Exemple #3
0
class FileSerializer(serializers.ModelSerializer):
    acknowledgement = serializers.HiddenField(
        default=serializers.CreateOnlyDefault(DefaultAcknowledgement))
    file = S3ObjectFieldSerializer()

    class Meta:
        model = File
        fields = '__all__'
        list_serializer_class = FileListSerializer

    @classmethod
    def many_init(cls, *args, **kwargs):
        # Instantiate the child serializer.
        kwargs['child'] = cls()
        # Instantiate the parent list serializer.
        return cls.Meta.list_serializer_class(*args, **kwargs)
class ShippingSerializer(serializers.ModelSerializer):
    items = ItemSerializer(many=True, required=True)
    customer = CustomerSerializer(required=True)
    project = ProjectFieldSerializer(required=False, allow_null=True)
    room = RoomFieldSerializer(allow_null=True, required=False)
    acknowledgement = AcknowledgementFieldSerializer(required=False,
                                                     allow_null=True)
    comments = serializers.CharField(required=False,
                                     allow_null=True,
                                     allow_blank=True)
    pdf = S3ObjectFieldSerializer(read_only=True, required=False)
    employee = EmployeeSerializer(required=False, read_only=True)

    class Meta:
        model = Shipping
        fields = '__all__'

    def to_internal_value(self, data):
        ret = super(ShippingSerializer, self).to_internal_value(data)

        try:
            ret['customer'] = Customer.objects.get(pk=data['customer']['id'])
        except (Customer.DoesNotExist, KeyError) as e:
            try:
                ret['customer'] = Customer.objects.get(
                    name=data['customer']['name'])
            except Customer.DoesNotExist as e:
                ret['customer'] = Customer.objects.create(**data['customer'])
            except Customer.MultipleObjectsReturned as e:
                logger.warn(e)

        library = {'project': Project, 'room': Room, 'phase': Phase}
        for key in library:
            try:
                ret[key] = library[key].objects.get(pk=data[key]['id'])
            except (library[key].DoesNotExist, KeyError, TypeError) as e:

                try:
                    ret[key] = library[key].objects.create(**data[key])
                except (KeyError, TypeError) as e:
                    pass

        try:
            ret['acknowledgement'] = Acknowledgement.objects.get(
                pk=data['acknowledgement']['id'])
        except (Acknowledgement.DoesNotExist, KeyError, TypeError) as e:
            try:
                del ret['acknowledgement']
            except KeyError as e:
                pass

        return ret

    def create(self, validated_data):
        """
        Override the 'create' method in order to create items from nested data
        """
        employee = self.context['request'].user

        items_data = validated_data.pop('items')
        items_data = self.initial_data['items']

        try:
            instance = self.Meta.model.objects.create(employee=employee,
                                                      **validated_data)
        except AttributeError:
            instance = self.Meta.model.objects.create(employee=employee,
                                                      **validated_data)

        instance.comments = validated_data.pop('comments', instance.comments)
        instance.save()

        item_serializer = ItemSerializer(data=items_data,
                                         context={'shipping': instance},
                                         many=True)

        if item_serializer.is_valid(raise_exception=True):
            item_serializer.save()

        if instance.acknowledgement:
            if instance.acknowledgement.items.count(
            ) == instance.acknowledgement.items.filter(
                    status='SHIPPED').count():
                instance.acknowledgement.status = 'SHIPPED'
            else:
                instance.acknowledgement.status = 'PARTIALLY SHIPPED'
            instance.acknowledgement.save()

        instance.create_and_upload_pdf()

        instance.save()

        # Add Shipping Doc to Ack
        if instance.acknowledgement:
            ack_service.add_file(acknowledgement=instance.acknowledgement,
                                 media_obj=instance.pdf)

        # Update the delivery date for the acknowledgement
        # Tries as there maybe shipping documents with no corresponding
        # Acknowledgements
        try:
            instance.acknowledgement.delivery_date = instance.delivery_date
            instance.acknowledgement.save()
        except AttributeError as e:
            pass

        # Update the calendar event
        try:
            instance.acknowledgement.update_calendar_event()
        except Exception as e:
            logger.warn(e)

        return instance

    def update(self, instance, validated_data):
        """
        Override the 'update' method
        """
        delivery_date = validated_data.pop('delivery_date',
                                           instance.delivery_date)
        instance.comments = validated_data.pop('comments', instance.comments)

        items_data = validated_data.pop('items', [])

        for item_data in items_data:
            try:
                item_data['item'] = item_data['item']['id']
            except KeyError:
                pass
            except TypeError:
                item_data['item'] = item_data['item'].id

            item = Item.objects.get(pk=item_data['id'])
            item_serializer = ItemSerializer(item,
                                             data=item_data,
                                             context={'shipping': instance})
            if item_serializer.is_valid(raise_exception=True):
                item_serializer.save()

        if instance.delivery_date != delivery_date:
            instance.delivery_date = delivery_date

            # Update the delivery date for the acknowledgement
            instance.acknowledgement.delivery_date = instance.delivery_date
            instance.acknowledgement.save()

            # Update the calendar event
            try:
                instance.acknowledgement.update_calendar_event()
            except Exception as e:
                logger.warn(e)

        instance.create_and_upload_pdf()
        instance.save()

        return instance

    def xto_representation(self, instance):
        """
        Override the 'to_representation' method in order to customize the output of 
        customer and acknowledgement
        """
        ret = super(ShippingSerializer, self).to_representation(instance)

        ret['customer'] = {
            'id': instance.customer.id,
            'name': instance.customer.name
        }

        try:
            ret['employee'] = {
                'id': instance.employee.id,
                'name': instance.employee.name
            }
        except:
            pass

        try:
            ret['acknowledgement'] = {
                'id': instance.acknowledgement.id,
                'status': instance.acknowledgement.status
            }
        except AttributeError:
            pass

        try:
            ret['project'] = {
                'id':
                instance.project.id or instance.acknowledgement.project.id,
                'codename':
                instance.project.codename
                or instance.acknowledgement.project.codename
            }
        except AttributeError:
            pass

        try:
            ret['room'] = {
                'id': instance.room.id,
                'description': instance.room.description
            }
        except AttributeError:
            pass

        try:
            ret['pdf'] = {
                'id': instance.pdf.id,
                'filename': instance.pdf.key.split('/')[-1],
                'url': instance.pdf.generate_url()
            }
        except AttributeError as e:
            logger.warn(e)

        return ret
Exemple #5
0
class ProjectSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False, allow_null=True)
    customer = CustomerFieldSerializer(required=False, allow_null=True)
    supplies = serializers.ListField(child=serializers.DictField(), write_only=True, allow_null=True,
                                     required=False)
    files = S3ObjectFieldSerializer(allow_null=True, required=False, many=True)
    codename = serializers.CharField(allow_blank=False, allow_null=False)
    acknowledgements = AcknowledgementFieldSerializer(read_only=True, many=True)
    purchase_orders = PurchaseOrderFieldSerializer(read_only=True, many=True)
    #phases = PhaseSerializer(many=True)
    
    class Meta:
        model = Project
        fields = ('id', 'codename', 'rooms', 'quantity', 'phases', 'supplies', 'status', 'website', 'files', 'customer', 'acknowledgements', 'purchase_orders')
        depth = 1
        
    def create(self, validated_data):
        """
        Create
        """
        supplies = validated_data.pop('supplies', [])
        files = validated_data.pop('files', [])
        
        
        instance = self.Meta.model.objects.create(**validated_data)
        
        self._create_or_update_supplies(supplies, instance)

        #Update attached files
        for f_data in files:
            try: 
                file = File.objects.get(file_id=f_data['id'], project=instance)
            except File.DoesNotExist:
                file = File.objects.create(file=S3Object.objects.get(pk=f_data['id']),
                                           project=instance)

            file.web_active = f_data.get('web_active', file.web_active)
            file.primary = f_data.get('primary', file.primary)
            file.save()
        
        return instance
        
    def update(self, instance, validated_data):
        """
        Update
        """
        supplies = validated_data.pop('supplies', [])
        logger.debug(validated_data)
        #Update attached files
        files = validated_data.pop('files', [])

        id_list = [f['id'] for f in files]

        # Add of create new files
        for f_data in files:
            try: 
                file = File.objects.get(file_id=f_data['id'], project=instance)
            except File.DoesNotExist:
                file = File.objects.create(file=S3Object.objects.get(pk=f_data['id']),
                                           project=instance)

            file.web_active = f_data.get('web_active', file.web_active)
            file.primary = f_data.get('primary', file.primary)
            file.save()

        for f in instance.files.all():
            if f.id not in id_list:
                File.objects.get(file=f, project=instance).delete()
        
        self._create_or_update_supplies(supplies, instance)
        
        return super(ProjectSerializer, self).update(instance, validated_data)
        
    def _create_or_update_supplies(self, supplies, project):
        #Create or update new supplies
        id_list = [supply['id'] for supply in supplies]
        
        for supply_data in supplies:
            try:
                project_supply = ProjectSupply.objects.get(supply=Supply.objects.get(pk=supply_data['id']),
                                                project=project)
            except ProjectSupply.DoesNotExist:
                project_supply = ProjectSupply.objects.create(supply=Supply.objects.get(pk=supply_data['id']),
                                                        project=project)
                id_list.append(project_supply.supply.id)
                
            project_supply.quantity = supply_data.get('quantity', project_supply.quantity)
            project_supply.save()
            
        #Remove delete supplies
        for supply in project.supplies.all():
            if supply.id not in id_list:
                ProjectSupply.objects.get(supply=supply, project=project).delete()
                
    def _serialize_supply(self, supply, project):
        ret = {'id': supply.id,
               'description': supply.description}
               
        ret['quantity'] = ProjectSupply.objects.get(supply=supply, project=project).quantity
        
        logger.debug(self.context)
        
        iam_credentials = self.context['request'].user.aws_credentials
        key = iam_credentials.access_key_id
        secret = iam_credentials.secret_access_key
        
        try:
            ret['image'] = {'url': supply.image.generate_url(key, secret)}
        except AttributeError:
            pass
            
        return ret
Exemple #6
0
class SupplierSerializer(ContactMixin, serializers.ModelSerializer):
    addresses = AddressSerializer(required=False, many=True, allow_null=True)
    address = AddressSerializer(required=False,
                                write_only=True,
                                allow_null=True)
    email = serializers.CharField(default="",
                                  allow_null=True,
                                  allow_blank=True)
    telephone = serializers.CharField(default="",
                                      allow_null=True,
                                      allow_blank=True)
    #name_th = serializers.CharField(required=False)
    contacts = ContactSerializer(required=False,
                                 many=True,
                                 write_only=True,
                                 allow_null=True)
    notes = serializers.CharField(default="",
                                  allow_null=True,
                                  allow_blank=True)
    bank = serializers.CharField(default="", allow_null=True, allow_blank=True)
    bank_account_number = serializers.CharField(default="",
                                                allow_null=True,
                                                allow_blank=True)
    purchase_orders = serializers.SerializerMethodField()
    terms = serializers.CharField(required=False, default="50/net")
    files = S3ObjectFieldSerializer(many=True, allow_null=True, required=False)

    open_orders = serializers.SerializerMethodField()

    class Meta:
        model = Supplier
        exclude = ('contact', 'google_contact_id', 'trcloud_id', 'fax',
                   'company')

    def to_representation(self, instance):

        ret = super(SupplierSerializer, self).to_representation(instance)

        if "pk" in self.context['view'].kwargs or self.context[
                'request'].method.lower() in ['put', 'post']:

            ret['contacts'] = ContactSerializer(
                SupplierContact.objects.filter(supplier=instance.id),
                many=True).data

        #ret['addresses'] = AddressSerializer(Address.objects.filter(contact=instance.id), many=True).data

        return ret

    def create(self, validated_data):
        """
        Override the base method 'create'.
        
        Calls the parent create and then also creates all the addresses and 
        contacts that are nested
        """
        addresses_data = validated_data.pop(
            'addresses', [validated_data.pop('address', [])])

        contacts_data = validated_data.pop('contacts', None)
        files = validated_data.pop('files', [])

        try:
            first = validated_data['first_name']
            try:
                last = validated_data['last_name']
            except KeyError:
                last = ''
            name = u"{0} {1}".format(first, last)
            validated_data.pop('name', None)
        except KeyError:
            name = validated_data.pop('name')

        instance = supplier_service.create(
            user=self.context['request'].user,
            company=self.context['request'].user.company,
            name=name,
            **validated_data)

        if addresses_data:
            # Delete any ids, as a new supplier should not have a pre existing id
            try:
                for a in addresses_data:
                    try:
                        a.pop('id', None)
                    except (TypeError, KeyError, AttributeError) as e:
                        pass
            except Exception as e:
                pass

            address_serializer = AddressSerializer(
                data=addresses_data, context={'contact': instance}, many=True)
            if address_serializer.is_valid(raise_exception=True):
                address_serializer.save()

        if contacts_data:
            self._sync_contacts(instance, contacts_data)

        # Add Files
        for file in files:
            File.objects.create(file=S3Object.objects.get(pk=file['id']),
                                contact=instance)

        return instance

    def update(self, instance, validated_data):
        """
        Override 'update' method
        """
        addresses_data = validated_data.pop(
            'addresses', [validated_data.pop('address', [])])

        address_serializer = AddressSerializer(instance.addresses.all(),
                                               data=addresses_data,
                                               many=True,
                                               context={'contact': instance})
        if address_serializer.is_valid(raise_exception=True):
            address_serializer.save()

        contacts_data = validated_data.pop('contacts', None)
        try:
            if contacts_data is not None:
                self._update_contacts(instance, contacts_data)
        except Exception as e:
            logger.error(e)

        #Update attached files
        files = validated_data.pop('files', [])
        for file in files:
            try:
                File.objects.get(file_id=file['id'], contact=instance)
            except File.DoesNotExist:
                File.objects.create(file=S3Object.objects.get(pk=file['id']),
                                    contact=instance)

        supplier_service.update(instance, validated_data,
                                self.context['request'].user)

        return instance

    def _update_contacts(self, instance, contacts_data):
        """
        Create, Update and Deletes contacts
        """
        #Get list of ids
        id_list = [
            contact_data.get('id', None) for contact_data in contacts_data
        ]
        #Create and update
        for contact_data in contacts_data:
            try:
                contact = SupplierContact.objects.get(pk=contact_data['id'])
            except KeyError:
                contact = SupplierContact.objects.create(supplier=instance)
                id_list.append(contact.id)

            for field in contact_data.keys():
                setattr(contact, field, contact_data[field])
            contact.supplier = instance
            contact.save()

        #Delete contacts
        for contact in instance.contacts.all():
            if contact.id not in id_list:
                contact.delete()

    def get_purchase_orders(self, instance):
        request = self.context['request']

        if "pk" in self.context['view'].kwargs:
            year = (datetime.now() - timedelta(days=365)).year
            pos = instance.purchase_orders.filter(
                order_date__year__gte=year).order_by('-id')
            return PurchaseOrderFieldSerializer(pos, many=True).data
        else:
            return []

    def get_open_orders(self, instance):

        today = datetime.now()
        orders = instance.purchase_orders.filter(order_date__year=2018)
        orders = orders.exclude(
            status__in=["paid", u'invoiced', u'cancelled', u'closed'])

        serializer = PurchaseOrderFieldSerializer(orders, many=True)
        return serializer.data
Exemple #7
0
class CustomerOrderFieldSerializer(ContactMixin, serializers.ModelSerializer):
    id = serializers.IntegerField(
        required=False,
        allow_null=True,
    )
    addresses = AddressSerializer(required=False, many=True, allow_null=True)
    address = serializers.CharField(required=False,
                                    allow_null=True,
                                    allow_blank=True)
    branch = serializers.CharField(required=False,
                                   allow_null=True,
                                   allow_blank=True)
    name = serializers.CharField(required=False,
                                 allow_blank=True,
                                 allow_null=True)
    first_name = serializers.CharField(required=False,
                                       allow_null=True,
                                       allow_blank=True)
    last_name = serializers.CharField(required=False,
                                      allow_null=True,
                                      allow_blank=True)
    telephone = serializers.CharField(required=False,
                                      allow_null=True,
                                      allow_blank=True)
    contacts = serializers.ListField(child=serializers.DictField(),
                                     required=False,
                                     allow_null=True,
                                     write_only=True)
    bank = serializers.CharField(required=False,
                                 allow_null=True,
                                 allow_blank=True)
    bank_account_number = serializers.CharField(required=False,
                                                allow_null=True,
                                                allow_blank=True)
    currency = serializers.CharField(required=False,
                                     allow_null=True,
                                     allow_blank=True)
    email = serializers.CharField(required=False,
                                  allow_null=True,
                                  allow_blank=True)
    terms = serializers.CharField(required=False, default="50/net")
    files = S3ObjectFieldSerializer(many=True, allow_null=True, required=False)

    class Meta:
        model = Customer
        exclude = ('contact', 'google_contact_id', 'type', 'job_title',
                   'trcloud_id', 'fax', 'company', 'account_receivable',
                   'account_payable')

    def create(self, validated_data):
        """
        Override the base method 'create'.
        
        Calls the parent create and then also creates all the addresses and 
        contacts that are nested
        """
        addresses_data = validated_data.pop('addresses', [])

        contacts_data = validated_data.pop('contacts', None)
        files = validated_data.pop('files', [])

        try:
            first = validated_data['first_name']
            try:
                last = validated_data['last_name']
            except KeyError:
                last = ''
            name = u"{0} {1}".format(first, last)
            validated_data.pop('name', None)
        except KeyError:
            name = validated_data.pop('name')

        instance = customer_service.create(user=self.context['request'].user,
                                           name=name,
                                           **validated_data)

        if addresses_data:
            # Delete any ids, as a new customer should not have a pre existing id
            try:
                for a in addresses_data:
                    try:
                        a.pop('id', None)
                    except (TypeError, KeyError, AttributeError) as e:
                        pass
            except Exception as e:
                pass

            address_serializer = AddressSerializer(
                data=addresses_data, context={'contact': instance}, many=True)
            if address_serializer.is_valid(raise_exception=True):
                address_serializer.save()

        if contacts_data:
            self._sync_contacts(instance, contacts_data)

        # Add Files
        for file in files:
            File.objects.create(file=S3Object.objects.get(pk=file['id']),
                                contact=instance)

        return instance

    def update(self, instance, validated_data):
        """
        Override 'update' method
        """
        addresses_data = validated_data.pop('addresses', [])

        contacts_data = validated_data.pop('contacts', None)

        address_serializer = AddressSerializer(instance.addresses.all(),
                                               data=addresses_data,
                                               many=True)
        if address_serializer.is_valid(raise_exception=True):
            address_serializer.save()

        if contacts_data:
            self._sync_contacts(instance, contacts_data)

        #Update attached files
        files = validated_data.pop('files', [])
        for file in files:
            try:
                File.objects.get(file_id=file['id'], contact=instance)
            except File.DoesNotExist:
                File.objects.create(file=S3Object.objects.get(pk=file['id']),
                                    contact=instance)

        customer_service.update(instance, validated_data,
                                self.context['request'].user)

        return instance
Exemple #8
0
class ItemSerializer(serializers.ModelSerializer):
    product = ProductSerializer(required=False, allow_null=True)
    pillows = PillowSerializer(required=False, many=True)

    comments = serializers.CharField(default='',
                                     allow_blank=True,
                                     allow_null=True)
    fabric = serializers.PrimaryKeyRelatedField(required=False,
                                                allow_null=True,
                                                queryset=Fabric.objects.all())
    image = S3ObjectFieldSerializer(required=False, allow_null=True)
    units = serializers.CharField(default='mm', allow_null=True)
    width = serializers.IntegerField(default=0)
    depth = serializers.IntegerField(default=0)
    height = serializers.IntegerField(default=0)

    #custom_price = serializers.DecimalField(decimal_places=2, max_digits=12, write_only=True, required=False,
    #                                        allow_null=True)
    fabric_quantity = serializers.DecimalField(decimal_places=2,
                                               max_digits=12,
                                               write_only=True,
                                               required=False,
                                               allow_null=True)
    type = serializers.CharField(required=False, allow_null=True)

    # Price Related
    unit_price = serializers.DecimalField(default=0,
                                          min_value=0,
                                          decimal_places=2,
                                          max_digits=12)
    quantity = serializers.DecimalField(decimal_places=2,
                                        max_digits=12,
                                        default=1,
                                        min_value=1)

    class Meta:
        model = Item
        exclude = ('location', 'inventory')
        read_only_fields = ('total', 'estimate')
        list_serializer_class = ItemListSerializer

    def to_internal_value(self, data):
        ret = super(ItemSerializer, self).to_internal_value(data)

        try:
            ret['product'] = Product.objects.get(pk=data['product']['id'])
        except (KeyError, Product.DoesNotExist, TypeError) as e:
            try:
                ret['product'] = Product.objects.get(
                    description=data['description'])
            except (Product.DoesNotExist) as e:
                try:
                    ret['product'] = Product.objects.get(pk=10436)
                except Product.DoesNotExist as e:
                    ret['product'] = Product.objects.create()

        try:
            ret['image'] = S3Object.objects.get(pk=data['image']['id'])
        except (KeyError, S3Object.DoesNotExist, TypeError) as e:
            if "image" in ret:
                del ret['image']

        return ret

    def create(self, validated_data):
        """
        Populates the instance after the parent 'restore_object' method is
        called.
        """
        estimate = self.context['estimate']
        product = validated_data['product']
        logger.debug(product)
        pillow_data = validated_data.pop('pillows', None)
        unit_price = validated_data.pop(
            'unit_price', validated_data.pop('price', product.price))
        width = validated_data.pop('width') or product.width
        depth = validated_data.pop('depth') or product.depth
        height = validated_data.pop('height') or product.height
        fabric_quantity = validated_data.pop('fabric_quantity', None)

        instance = self.Meta.model.objects.create(estimate=estimate,
                                                  unit_price=unit_price,
                                                  width=width,
                                                  depth=depth,
                                                  height=height,
                                                  **validated_data)

        #attach fabric quantity
        instance.fabric_quantity = fabric_quantity

        #Calculate the total price of the item
        if instance.is_custom_size and product.price == unit_price:
            instance.total = instance.quantity * instance.unit_price
        else:
            instance.total = instance.quantity * instance.unit_price

        instance.save()

        if pillow_data:
            pillow_serializer = PillowSerializer(data=pillow_data,
                                                 context={'item': instance},
                                                 many=True)

            if pillow_serializer.is_valid(raise_exception=True):
                pillow_serializer.save()

        return instance

    def update(self, instance, validated_data):
        """
        Updates the instance after the parent method is called
        """
        #instance = super(ItemSerializer, self).update(instance, validated_data)
        instance.description = validated_data.get('description',
                                                  instance.description)

        instance.width = validated_data.get('width', instance.width)
        instance.depth = validated_data.get('depth', instance.depth)
        instance.height = validated_data.get('height', instance.height)
        instance.image = validated_data.get('image', instance.image)
        instance.quantity = validated_data.get('quantity', instance.quantity)
        instance.unit_price = validated_data.get('unit_price',
                                                 instance.unit_price)
        instance.comments = validated_data.get('comments', instance.comments)

        instance.total = instance.quantity * instance.unit_price

        instance.save()

        return instance

    def to_representation(self, instance):
        """
        Override the 'to_representation' method to transform the output for related and nested items
        """
        ret = super(ItemSerializer, self).to_representation(instance)
        try:
            ret['fabric'] = {
                'id': instance.fabric.id,
                'description': instance.fabric.description
            }
        except AttributeError:
            pass
        """
        try:
            ret['image'] = {'id': instance.image.id,
                            'url': instance.image.generate_url()}
        except AttributeError:
            pass
        """

        return ret
Exemple #9
0
class EstimateSerializer(serializers.ModelSerializer):
    item_queryset = Item.objects.exclude(deleted=True)
    company = CompanySerializer(write_only=True, required=False)
    company_name = serializers.CharField(default="Alinea Group Co., Ltd.")
    customer_name = serializers.CharField(required=False, allow_null=True)
    customer = CustomerOrderFieldSerializer(required=True)
    employee = EmployeeSerializer(required=False, read_only=True)

    project = ProjectSerializer(allow_null=True, required=False)
    items = ItemSerializer(item_queryset, many=True, required=True)
    remarks = serializers.CharField(default="",
                                    allow_blank=True,
                                    allow_null=True)
    #shipping_method = serializers.CharField(default="Truck", allow_null=True)
    #fob = serializers.CharField(default="Bangkok", allow_null=True)
    lead_time = serializers.CharField(default="4 Weeks")
    vat = serializers.DecimalField(required=True,
                                   decimal_places=2,
                                   max_digits=12,
                                   min_value=0,
                                   max_value=100)
    discount = serializers.IntegerField(default=0, min_value=0, max_value=100)
    second_discount = serializers.IntegerField(default=0,
                                               min_value=0,
                                               max_value=100)
    files = S3ObjectFieldSerializer(many=True, allow_null=True, required=False)
    acknowledgement = AcknowledgementSerializer(required=False,
                                                allow_null=True)
    delivery_date = serializers.DateTimeField(required=False,
                                              allow_null=True,
                                              default=datetime.now())
    logs = LogFieldSerializer(many=True, read_only=True)

    # Totals

    class Meta:
        model = Estimate
        read_only_fields = ('grand_total', 'total', 'post_discount_total',
                            'subtotal', 'time_created', 'employee', 'logs')
        write_only_fields = ('company', )
        exclude = ('pdf', 'deal', 'po_id', 'fob', 'shipping_method')
        depth = 1

    def to_internal_value(self, data):
        ret = super(EstimateSerializer, self).to_internal_value(data)

        try:
            ret['customer'] = Customer.objects.get(pk=data['customer']['id'])
        except (Customer.DoesNotExist, KeyError) as e:
            ret['customer'] = Customer.objects.create(**data['customer'])

        try:
            ret['project'] = Project.objects.get(pk=data['project']['id'])
        except (Project.DoesNotExist, KeyError, TypeError) as e:

            try:
                ret['project'] = Project.objects.create(**data['project'])
            except (TypeError) as e:
                logger.warn(e)
                logger.debug(ret['project'])
                try:
                    del ret['project']
                except Exception as e:
                    logger.warn(e)
            except KeyError as e:
                pass

        try:
            ret['acknowledgement'] = Acknowledgement.objects.get(
                pk=data['acknowledgement']['id'])
        except (Acknowledgement.DoesNotExist, KeyError, TypeError) as e:
            try:
                del ret['acknowledgement']
            except KeyError as e:
                pass

        logger.debug("\n\nEstimate to internal value\n\n")

        return ret

    def create(self, validated_data):
        """
        Override the 'create' method in order to create nested items
        """
        items_data = self.initial_data['items']
        validated_data.pop('items', [])
        #files = validated_data.pop('files', [])

        for item_data in items_data:
            for field in ['fabric']:
                try:
                    item_data[field] = item_data[field].id
                except KeyError:
                    pass
                except AttributeError:
                    pass

        c_name = validated_data.pop('customer_name',
                                    validated_data['customer'].name)
        currency = validated_data.pop(
            'currency', validated_data['customer'].currency or 'THB')
        discount = validated_data.pop('discount',
                                      validated_data['customer'].discount)
        status = validated_data.pop('status', None)

        try:
            files = validated_data.pop('files', [])
        except KeyError as e:
            files = []

        #Get User
        employee = self.context['request'].user

        instance = self.Meta.model.objects.create(employee=employee,
                                                  customer_name=c_name,
                                                  company=employee.company,
                                                  currency=currency,
                                                  discount=discount,
                                                  status="open",
                                                  **validated_data)

        item_serializer = ItemSerializer(data=items_data,
                                         context={'estimate': instance},
                                         many=True)

        if item_serializer.is_valid(raise_exception=True):
            item_serializer.save()

        instance.calculate_totals()

        try:
            instance.create_and_update_deal()
        except Exception as e:
            logger.warn(e)

        instance.create_and_upload_pdf()

        # Add pdfs to files list
        filenames = ['pdf']
        for filename in filenames:
            try:
                File.objects.create(file=getattr(instance, filename),
                                    estimate=instance)
            except Exception as e:
                logger.warn(e)

        # Assign files
        for file_obj in files:
            File.objects.create(file=S3Object.objects.get(pk=file_obj['id']),
                                estimate=instance)

        # Log Creation
        message = u"Quotation {0} created."
        message = message.format(instance.id)
        ELog.create(message=message, estimate=instance, user=employee)
        """
        #Extract fabric quantities
        fabrics = {}

        for item in item_serializer.instance:
            if item.fabric:
                if item.fabric in fabrics:
                    fabrics[item.fabric] += Decimal(str(item.quantity)) * item.fabric_quantity
                else:
                    fabrics[item.fabric] = Decimal(str(item.quantity)) * item.fabric_quantity

        #Log Fabric Reservations
        for fabric in fabrics:
            self.reserve_fabric(fabric, fabrics[fabric], instance.id)
        """

        return instance

    def update(self, instance, validated_data):
        employee = self.context['request'].user

        instance.acknowledgement = validated_data.pop('acknowledgement',
                                                      instance.acknowledgement)
        instance.project = validated_data.pop('project', instance.project)

        # Loops through attributes and logs changes
        updatable_attributes = [
            'vat', 'discount', 'second_discount', 'remarks', 'lead_time',
            'currency'
        ]

        for attr in updatable_attributes:
            new_attr_value = validated_data.pop(attr, getattr(instance, attr))

            if getattr(instance, attr) != new_attr_value:
                old_attr_value = getattr(instance, attr)
                setattr(instance, attr, new_attr_value)

                # Log data changes
                message = u"Updated Quotation {0}: {1} changed from {2} to {3}"
                message = message.format(instance.id, attr, old_attr_value,
                                         new_attr_value)
                ELog.create(message=message, estimate=instance, user=employee)

        #Update attached files
        files = validated_data.pop('files', [])
        for file_obj in files:
            try:
                File.objects.get(file_id=file_obj['id'], estimate=instance)
            except File.DoesNotExist:
                File.objects.create(
                    file=S3Object.objects.get(pk=file_obj['id']),
                    estimate=instance)

        new_status = validated_data.pop('status', instance.status)

        # Set the corresponding deal as closed lost
        if new_status.lower() != instance.status.lower() and new_status.lower(
        ) == 'cancelled':
            try:
                instance.deal.status = 'closed lost'
                instance.deal.save()
            except (AttributeError, TypeError) as e:
                logger.debug(e)

        instance.status = new_status

        items_data = validated_data.pop('items')

        items_data = self.initial_data['items']

        self._update_items(instance, items_data)

        instance.save()

        instance.calculate_totals()

        instance.create_and_upload_pdf()

        instance.save()

        return instance

    def xto_representation(self, instance):
        """
        Override the default 'to_representation' method to customize the output data
        """
        ret = super(EstimateSerializer, self).to_representation(instance)

        ret['employee'] = {
            'id':
            instance.employee.id,
            'name':
            "{0} {1}".format(instance.employee.first_name,
                             instance.employee.last_name)
        }

        try:
            ret['files'] = [{
                'id': instance.id,
                'filename': instance.pdf.key.split('/')[-1],
                'url': instance.pdf.generate_url()
            }]
        except AttributeError as e:
            ret['files'] = []

        return ret

    def _update_items(self, instance, items_data):
        """
        Handles creation, update, and deletion of items
        """
        #Maps of id
        id_list = [item_data.get('id', None) for item_data in items_data]
        logger.debug(id_list)

        #Delete Items
        for item in instance.items.all():
            if item.id not in id_list:
                item.delete()
                instance.items.filter(pk=item.id).delete()
                logger.debug(item)
                logger.debug(instance.items.all())
                #item.deleted = True
                #item.save()

        #Update or Create Item
        for item_data in items_data:
            try:
                item = Item.objects.get(pk=item_data['id'], estimate=instance)
                serializer = ItemSerializer(item,
                                            context={
                                                'customer': instance.customer,
                                                'estimate': instance
                                            },
                                            data=item_data)
            except (KeyError, Item.DoesNotExist) as e:
                serializer = ItemSerializer(data=item_data,
                                            context={
                                                'customer': instance.customer,
                                                'estimate': instance
                                            })

            if serializer.is_valid(raise_exception=True):
                item = serializer.save()
                id_list.append(item.id)
Exemple #10
0
class ItemSerializer(serializers.ModelSerializer):
    product = ProductSerializer(required=False, allow_null=True)
    acknowledgement_item = AckItemFieldSerializer(required=False,
                                                  allow_null=True)
    unit_price = serializers.DecimalField(required=False,
                                          decimal_places=2,
                                          max_digits=12,
                                          default=0)
    comments = serializers.CharField(required=False,
                                     allow_null=True,
                                     allow_blank=True)
    status = serializers.CharField(required=False, default='invoiced')
    #location = serializers.CharField(required=False, allow_null=True)
    image = S3ObjectFieldSerializer(required=False, allow_null=True)
    id = serializers.IntegerField(required=False, allow_null=True)

    grades = {'A1': 15, 'A2': 20, 'A3': 25, 'A4': 30, 'A5': 35, 'A6': 40}

    class Meta:
        model = Item
        fields = ('description', 'id', 'unit_price', 'total', 'product',
                  'acknowledgement_item', 'comments', 'image', 'quantity',
                  'status')
        read_only_fields = ('total', )
        list_serializer_class = ItemListSerializer

    def to_internal_value(self, data):
        ret = super(ItemSerializer, self).to_internal_value(data)

        try:
            ret['product'] = Product.objects.get(pk=data['product']['id'])
        except (KeyError, Product.DoesNotExist, TypeError) as e:
            try:
                ret['product'] = Product.objects.get(
                    description=data['description'])
            except (Product.DoesNotExist) as e:
                try:
                    ret['product'] = Product.objects.get(pk=10436)
                except Product.DoesNotExist as e:
                    ret['product'] = Product.objects.create()

        try:
            ret['acknowledgement_item'] = AckItem.objects.get(
                pk=data['acknowledgement_item']['id'])
        except (KeyError, AckItem.DoesNotExist, TypeError) as e:
            if 'acknowledgement_item' in ret:
                del ret['acknowledgement_item']

        try:
            ret['image'] = S3Object.objects.get(pk=data['image']['id'])
        except (KeyError, S3Object.DoesNotExist, TypeError) as e:
            if "image" in ret:
                del ret['image']

        return ret

    def create(self, validated_data):
        """
        Populates the instance after the parent 'restore_object' method is
        called.
        """
        invoice = self.context['invoice']

        instance = self.Meta.model.objects.create(invoice=invoice,
                                                  **validated_data)

        instance.total = (instance.quantity or 1) * (instance.unit_price or 0)

        instance.save()

        return instance

    def update(self, instance, validated_data):
        """
        Updates the instance after the parent method is called
        """

        # Update attributes from client side details
        invoice = self.context['invoice']
        employee = self.context['employee']

        # Loops through attributes and logs changes
        updatable_attributes = [
            'quantity', 'unit_price', 'description', 'comments', 'status'
        ]

        for attr in updatable_attributes:
            new_attr_value = validated_data.pop(attr, getattr(instance, attr))

            if getattr(instance, attr) != new_attr_value:
                old_attr_value = getattr(instance, attr)
                setattr(instance, attr, new_attr_value)

                # Log data changes
                message = u"{0}: {1} changed from {2} to {3}"
                message = message.format(instance.description, attr,
                                         old_attr_value, new_attr_value)
                InvoiceLog.create(message=message,
                                  invoice=instance.invoice,
                                  user=employee)

        # Set the price of the total for this item
        instance.total = instance.quantity * instance.unit_price
        instance.save()

        return instance
Exemple #11
0
class InvoiceSerializer(serializers.ModelSerializer):
    item_queryset = Item.objects.exclude(deleted=True)
    company = serializers.CharField(required=False,
                                    allow_null=True,
                                    allow_blank=True)
    customer = CustomerOrderFieldSerializer()
    acknowledgement = AcknowledgementFieldSerializer(required=False,
                                                     allow_null=True)
    employee = UserFieldSerializer(required=False, read_only=True)
    project = ProjectFieldSerializer(required=False, allow_null=True)
    room = RoomFieldSerializer(allow_null=True, required=False)
    phase = PhaseFieldSerializer(allow_null=True, required=False)
    items = ItemSerializer(item_queryset, many=True)
    remarks = serializers.CharField(required=False,
                                    allow_null=True,
                                    allow_blank=True)
    files = S3ObjectFieldSerializer(many=True, allow_null=True, required=False)
    due_date = serializers.DateTimeField(required=True)
    logs = LogFieldSerializer(many=True, read_only=True)
    journal_entry = serializers.SerializerMethodField()

    class Meta:
        model = Invoice
        read_only_fields = ('total', 'subtotal', 'time_created', 'logs')
        exclude = ('pdf', 'trcloud_id', 'trcloud_document_number')
        depth = 3

    def to_internal_value(self, data):
        ret = super(InvoiceSerializer, self).to_internal_value(data)

        try:
            ret['acknowledgement'] = Acknowledgement.objects.get(
                pk=data['acknowledgement']['id'])
        except (Acknowledgement.DoesNotExist, KeyError) as e:
            try:
                del ret['acknowledgement']
            except Exception as e:
                logger.warn(e)

        try:
            ret['customer'] = Customer.objects.get(pk=data['customer']['id'])
        except (Customer.DoesNotExist, KeyError) as e:
            ret['customer'] = Customer.objects.create(**data['customer'])

        try:
            ret['project'] = Project.objects.get(pk=data['project']['id'])
        except (Project.DoesNotExist, KeyError, TypeError) as e:

            try:
                ret['project'] = Project.objects.create(**data['project'])
            except (TypeError) as e:
                logger.warn(e)
                logger.debug(ret['project'])
                try:
                    del ret['project']
                except Exception as e:
                    logger.warn(e)
            except KeyError as e:
                pass

        logger.debug("\n\nInvoice to internal value\n\n")

        return ret

    def create(self, validated_data):
        """
        Override the 'create' method in order to create nested items
        """

        #Discard STatus
        status = validated_data.pop('status', 'invoiced')

        items_data = validated_data.pop('items')
        items_data = self.initial_data['items']
        files = validated_data.pop('files', [])

        # Get user
        employee = self.context['request'].user

        try:
            assert isinstance(employee, User)
        except AssertionError as e:
            employee = User.objects.get(pk=1)

        for item_data in items_data:
            for field in ['product']:
                try:
                    item_data[field] = item_data[field].id
                except KeyError:
                    pass
                except AttributeError:
                    pass

        discount = validated_data.pop('discount',
                                      validated_data['customer'].discount)
        due_date = timezone('Asia/Bangkok').normalize(
            validated_data.pop('due_date'))

        instance = self.Meta.model.objects.create(employee=employee,
                                                  discount=discount,
                                                  status='invoiced',
                                                  _due_date=due_date,
                                                  **validated_data)

        item_serializer = ItemSerializer(data=items_data,
                                         context={'invoice': instance},
                                         many=True)

        if item_serializer.is_valid(raise_exception=True):
            item_serializer.save()

        instance.calculate_totals()

        instance.create_and_upload_pdf()

        # Add pdfs to files list
        filenames = [
            'pdf',
        ]
        for filename in filenames:
            try:
                File.objects.create(file=getattr(instance, filename),
                                    invoice=instance)
            except Exception as e:
                logger.warn(e)

        # Assign files
        for file in files:
            File.objects.create(file=S3Object.objects.get(pk=file['id']),
                                invoice=instance)

        # Create Sales Order File Link
        # - So Invoices will show up in acknowledgement Files
        if instance.acknowledgement:
            AckFile.objects.create(acknowledgement=instance.acknowledgement,
                                   file=instance.pdf)

        # Create a calendar event
        try:
            instance.create_calendar_event(employee)
        except Exception as e:
            message = u"Unable to create calendar event for invoice {0} because:\n{1}"
            message = message.format(instance.id, e)
            log = InvoiceLog.create(message=message,
                                    invoice=instance,
                                    user=employee,
                                    type="GOOGLE CALENDAR")

        # Log Opening of an order
        message = u"Created Invoice #{0}.".format(instance.id)
        log = InvoiceLog.create(message=message,
                                invoice=instance,
                                user=employee)

        if instance.vat > 0:
            try:
                pass  #instance.create_in_trcloud()
            except Exception as e:
                message = u"Unable to create invoice because:\n{0}"
                message = message.format(e)
                log = InvoiceLog.create(message=message,
                                        invoice=instance,
                                        user=employee,
                                        type="TRCLOUD")

        # Create Journal Entry in Accouting
        invoice_service.create_journal_entry(instance)

        # Update Sales Order/ Acknowledgement status
        instance.acknowledgement.status = 'invoiced' if instance.acknowledgement.balance == 0 else 'partially invoiced'
        instance.acknowledgement.save()

        return instance

    def update(self, instance, validated_data):

        # Get user
        try:
            employee = self.context['request'].user
        except KeyError as e:
            employee = self.context['employee']

        if settings.DEBUG:
            employee = User.objects.get(pk=1)

        instance.current_user = employee
        dd = timezone('Asia/Bangkok').normalize(
            validated_data.pop('due_date', instance.due_date))
        instance.project = validated_data.pop('project', instance.project)
        instance.acknowledgement = validated_data.pop('acknowledgement',
                                                      instance.acknowledgement)
        instance.vat = validated_data.pop('vat', instance.vat)
        instance.discount = validated_data.pop('discount', instance.discount)
        instance.room = validated_data.pop('room', instance.room)
        status = validated_data.pop('status', instance.status)

        if instance.due_date != dd:
            old_dd = instance.due_date
            instance.due_date = dd

            # Log Changing delivery date
            message = u"Invoice #{0} due date changed from {1} to {2}."
            message = message.format(instance.id, old_dd.strftime('%d/%m/%Y'),
                                     dd.strftime('%d/%m/%Y'))
            InvoiceLog.create(message=message, invoice=instance, user=employee)

        if status.lower() != instance.status.lower():

            message = u"Updated Invoice #{0} from {1} to {2}."
            message = message.format(instance.id, instance.status.lower(),
                                     status.lower())
            InvoiceLog.create(message=message, invoice=instance, user=employee)

            instance.status = status

        old_qty = sum([item.quantity for item in instance.items.all()])

        # Extract items data
        items_data = validated_data.pop('items')
        items_data = self.initial_data['items']
        fabrics = {}

        self._update_items(instance, items_data)

        #Update attached files
        files = validated_data.pop('files', [])
        for file in files:
            try:
                File.objects.get(file_id=file['id'], invoice=instance)
            except File.DoesNotExist:
                File.objects.create(file=S3Object.objects.get(pk=file['id']),
                                    invoice=instance)

        #if instance.status.lower() in ['invoiced', 'in production', 'ready to ship']:

        # Store old total and calculate new total
        old_total = instance.total
        instance.calculate_totals()

        try:
            instance.create_and_upload_pdf()
        except IOError as e:

            tb = traceback.format_exc()
            logger.error(tb)

            message = u"Unable to update PDF for invoice {0} because:\n{1}"
            message = message.format(instance.id, e)
            log = InvoiceLog.create(message=message,
                                    invoice=instance,
                                    user=employee,
                                    type="PDF CREATION ERROR")

        try:
            instance.update_calendar_event()
        except Exception as e:
            logger.debug(e)
            message = u"Unable to update calendar event for invoice {0} because:\n{1}"
            message = message.format(instance.id, e)
            log = InvoiceLog.create(message=message,
                                    invoice=instance,
                                    user=employee,
                                    type="GOOGLE CALENDAR ERROR")

        instance.save()

        if instance.vat > 0 and instance.trcloud_id:
            try:
                pass  #instance.update_in_trcloud()
            except Exception as e:
                message = u"Unable to update invoice {0} because:\n{1}"
                message = message.format(instance.id, e)
                log = InvoiceLog.create(message=message,
                                        invoice=instance,
                                        user=employee,
                                        type="TRCLOUD ERROR")

        return instance

    def _update_items(self, instance, items_data):
        """
        Handles creation, update, and deletion of items
        """

        #Maps of id
        id_list = [item_data.get('id', None) for item_data in items_data]
        logger.debug(id_list)

        #Delete Items
        for item in instance.items.all():
            if item.id not in id_list:
                item.delete()
                instance.items.filter(pk=item.id).delete()
                #item.deleted = True
                #item.save()

        #Update or Create Item
        for item_data in items_data:
            try:
                item = Item.objects.get(pk=item_data['id'], invoice=instance)
                serializer = ItemSerializer(item,
                                            context={
                                                'customer': instance.customer,
                                                'invoice': instance,
                                                'employee': instance.employee
                                            },
                                            data=item_data)
            except (KeyError, Item.DoesNotExist) as e:
                serializer = ItemSerializer(data=item_data,
                                            context={
                                                'customer': instance.customer,
                                                'invoice': instance,
                                                'employee': instance.employee
                                            })

            if serializer.is_valid(raise_exception=True):
                item = serializer.save()
                id_list.append(item.id)

    def get_journal_entry(self, instance):

        return JournalEntrySerializer(instance.journal_entry).data
Exemple #12
0
class AcknowledgementSerializer(serializers.ModelSerializer):
    item_queryset = Item.objects.exclude(deleted=True)

    # Internal Fields
    company = serializers.HiddenField(
        default=serializers.CreateOnlyDefault(CompanyDefault))
    employee = UserFieldSerializer(read_only=True,
                                   default=serializers.CurrentUserDefault())

    #Business Fields
    company_name = serializers.CharField(required=False,
                                         allow_null=True,
                                         allow_blank=True)
    customer_name = serializers.CharField(default="")
    remarks = serializers.CharField(required=False,
                                    allow_null=True,
                                    allow_blank=True)
    shipping_method = serializers.CharField(required=False, allow_null=True)
    fob = serializers.CharField(required=False, allow_null=True)
    delivery_date = serializers.DateTimeField(
        required=True, default_timezone=timezone('Asia/Bangkok'))
    balance = serializers.DecimalField(read_only=True,
                                       decimal_places=2,
                                       max_digits=15)

    # Nested Fields
    customer = CustomerOrderFieldSerializer()
    project = ProjectFieldSerializer(required=False, allow_null=True)
    room = RoomFieldSerializer(allow_null=True, required=False)
    phase = PhaseFieldSerializer(allow_null=True, required=False)

    # Nested Many Fields
    items = ItemSerializer(item_queryset, many=True)
    files = S3ObjectFieldSerializer(many=True, allow_null=True, required=False)
    logs = LogFieldSerializer(many=True, read_only=True)

    # Method Fields
    invoices = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = Acknowledgement
        read_only_fields = ('total', 'subtotal', 'time_created', 'logs',
                            'balance')
        exclude = ('acknowledgement_pdf', 'production_pdf',
                   'original_acknowledgement_pdf', 'label_pdf', 'trcloud_id',
                   'trcloud_document_number')
        depth = 3

    def to_internal_value(self, data):
        ret = super(AcknowledgementSerializer, self).to_internal_value(data)

        try:
            ret['customer'] = Customer.objects.get(pk=data['customer']['id'])
        except (Customer.DoesNotExist, KeyError) as e:
            ret['customer'] = Customer.objects.create(**data['customer'])

        try:
            ret['project'] = Project.objects.get(pk=data['project']['id'])
        except (Project.DoesNotExist, KeyError, TypeError) as e:

            try:
                ret['project'] = Project.objects.create(**data['project'])
            except (TypeError) as e:
                logger.warn(e)
                logger.debug(ret['project'])
                try:
                    del ret['project']
                except Exception as e:
                    logger.warn(e)
            except KeyError as e:
                pass

        logger.debug("\n\nAcknowledgement to internal value\n\n")

        return ret

    def create(self, validated_data):
        """
        Override the 'create' method in order to create nested items
        """

        #Discard STatus
        status = validated_data.pop('status', 'acknowledged')

        items_data = validated_data.pop('items')
        items_data = self.initial_data['items']
        files = validated_data.pop('files', [])

        for item_data in items_data:
            for field in ['fabric']:
                try:
                    item_data[field] = item_data[field].id
                except KeyError:
                    pass
                except AttributeError:
                    pass

        discount = validated_data.pop('discount',
                                      validated_data['customer'].discount)

        instance = self.Meta.model.objects.create(discount=discount,
                                                  status='acknowledged',
                                                  **validated_data)
        self.instance = instance

        item_serializer = ItemSerializer(data=items_data,
                                         context={
                                             'acknowledgement': instance,
                                             request: self.context['request']
                                         },
                                         many=True)

        if item_serializer.is_valid(raise_exception=True):
            item_serializer.save()

        instance.calculate_totals()

        instance.create_and_upload_pdfs()

        # Add pdfs to files list
        filenames = ['acknowledgement_pdf', 'production_pdf', 'label_pdf']
        for filename in filenames:
            self._add_file(getattr(instance, filename))

        # Add files
        for file in files:
            self._add_file(S3Object.objects.get(pk=file['id']))

        # Create a calendar event
        try:
            instance.create_calendar_event(employee)
        except Exception as e:
            message = u"Unable to create calendar event for acknowledgement {0} because:\n{1}"
            message = message.format(instance.document_number, e)
            self._log(message, instance, self.context['request'])

        # Log Opening of an order
        message = u"Created Sales Order #{0}.".format(instance.document_number)
        self._log(message, instance, self.context['request'])

        return instance

    def update(self, instance, validated_data):

        attrs_to_update = {
            'delivery_date': lambda x: x.strftime('%d/%m/%Y'),
            'status': lambda x: x.lower(),
            'project': None,
            'vat': None,
            'discount': None
        }

        for attr, attr_formatting in attrs_to_update.items():
            self._update_attr(instance, attr, validated_data, attr_formatting)

        # Extract items data
        items_data = validated_data.pop('items')
        items_data = self.initial_data['items']
        fabrics = {}
        logger.debug(items_data)
        items_serializer = ItemSerializer(instance=instance.items.all(),
                                          data=items_data,
                                          many=True,
                                          context={
                                              'request':
                                              self.context['request'],
                                              'acknowledgement': instance,
                                              'employee':
                                              self.context['request'].user,
                                              'customer': instance.customer
                                          })

        if items_serializer.is_valid(raise_exception=True):
            items_serializer.save()

        #Update attached files
        files_data = validated_data.pop('files', None)
        if files_data:
            files_data = [{'file': f} for f in files_data]
            files_serializer = FileSerializer(
                File.objects.filter(acknowledgement=instance),
                data=files_data,
                context={
                    'request': self.context['request'],
                    'acknowledgement': instance
                },
                many=True)

        # Store old total and calculate new total
        instance.calculate_totals()

        try:
            instance.create_and_upload_pdfs()
        except IOError as e:

            tb = traceback.format_exc()
            logger.error(tb)

            message = u"Unable to update PDF for acknowledgement {0} because:\n{1}"
            message = message.format(instance.id, e)
            self._log(message, instance)

        try:
            instance.update_calendar_event()
        except Exception as e:
            logger.debug(e)
            message = u"Unable to update calendar event for acknowledgement {0} because:\n{1}"
            message = message.format(instance.id, e)
            self._log(message, instance)

        instance.save()

        return instance

    def get_invoices(self, instance):

        data = [{
            'id': inv.id,
            'grand_total': inv.grand_total
        } for inv in instance.invoices.all()]

        return data

    def reserve_fabric(self,
                       fabric,
                       quantity,
                       acknowledgement_id,
                       employee=None):
        """
        Internal method to apply the new quantity to the obj and
        create or update a log of the quantity change
        """

        #Create log to track quantity changes
        try:
            log = Log.objects.get(acknowledgement_id=acknowledgement_id,
                                  supply_id=fabric.id)
        except Log.DoesNotExist:
            log = Log(supply=fabric, acknowledgement_id=acknowledgement_id)

        # Get log quantity for fabric cut later
        original_qty = log.quantity or 0

        # Set log attributes
        log.action = "RESERVE"
        log.quantity = quantity
        log.employee = employee
        log.message = u"Reserve {0}{1} of {2} for Ack#{3}".format(
            quantity, fabric.units, fabric.description, acknowledgement_id)

        # Save log
        log.save()

    def _add_file(self, file):
        """
        Adds a file to the acknowledgement
        """
        File.objects.create(acknowledgement=self.instance, file=file)

        # Log addition of file
        msg = u"Added '{0}' to Sales Order #{1} files"
        msg = msg.format(file.filename, self.instance.document_number)
        self._log(msg)

    def _update_attr(self, instance, attr_name, data_mapping, format=None):
        """
        Update Attribute in instance if there is a change
        """
        new_val = data_mapping.get(attr_name, getattr(instance, attr_name))
        old_val = getattr(instance, attr_name)

        # Format if callable is set
        if callable(format):
            new_val = format(new_val)
            old_val = format(old_val)

        if old_val != new_val:
            setattr(instance, attr_name,
                    data_mapping.get(attr_name, getattr(instance, attr_name)))
            msg = u"{0} for Sales Order # {1} changed from {2} to {3}"
            msg = msg.format(attr_name, instance.document_number, old_val,
                             new_val)
            self._log(msg)

    def _log(self, message, instance=None):
        """
        Create Acknowledgement link Log
        """
        if not isinstance(instance, self.Meta.model):
            instance = self.instance

        serializer = AcknowledgementLogSerializer(data={'message': message},
                                                  context={
                                                      'acknowledgement':
                                                      instance,
                                                      'request':
                                                      self.context['request']
                                                  })

        if serializer.is_valid(raise_exception=True):
            serializer.save()
Exemple #13
0
class ItemSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False, allow_null=True)
    # Business Fields
    unit_price = serializers.DecimalField(required=False,
                                          decimal_places=2,
                                          max_digits=12,
                                          default=0)
    comments = serializers.CharField(required=False,
                                     allow_null=True,
                                     allow_blank=True)
    status = serializers.CharField(required=False, default='acknowledged')
    #location = serializers.CharField(required=False, allow_null=True)
    units = serializers.CharField(required=False, allow_null=True)
    width = serializers.IntegerField(required=False, allow_null=True)
    depth = serializers.IntegerField(required=False, allow_null=True)
    height = serializers.IntegerField(required=False, allow_null=True)
    fabric_quantity = serializers.DecimalField(decimal_places=2,
                                               max_digits=12,
                                               required=False,
                                               allow_null=True)
    quantity = serializers.DecimalField(decimal_places=2,
                                        max_digits=12,
                                        min_value=1)
    type = serializers.CharField(required=False, allow_null=True)

    # Nested Relationships
    product = ProductSerializer(required=False, allow_null=True)
    fabric = serializers.PrimaryKeyRelatedField(required=False,
                                                allow_null=True,
                                                queryset=Fabric.objects.all())
    image = S3ObjectFieldSerializer(required=False, allow_null=True)

    # Nested Many Relationship
    pillows = PillowSerializer(required=False, many=True)
    components = ComponentSerializer(required=False, many=True)

    # Non Model Properties
    grades = {'A1': 15, 'A2': 20, 'A3': 25, 'A4': 30, 'A5': 35, 'A6': 40}

    class Meta:
        model = Item
        fields = ('description', 'id', 'width', 'depth', 'height',
                  'fabric_quantity', 'unit_price', 'total', 'product',
                  'pillows', 'comments', 'image', 'units', 'fabric',
                  'quantity', 'components', 'type', 'status')
        read_only_fields = ('total', )
        list_serializer_class = ItemListSerializer

    @classmethod
    def many_init(cls, *args, **kwargs):
        # Instantiate the child serializer.
        kwargs['child'] = cls()
        # Instantiate the parent list serializer.
        return cls.Meta.list_serializer_class(*args, **kwargs)

    def to_internal_value(self, data):
        ret = super(ItemSerializer, self).to_internal_value(data)

        try:
            ret['product'] = Product.objects.get(pk=data['product']['id'])
        except (KeyError, Product.DoesNotExist, TypeError) as e:
            try:
                ret['product'] = Product.objects.get(
                    description=data['description'])
            except (Product.DoesNotExist) as e:
                try:
                    ret['product'] = Product.objects.get(pk=10436)
                except Product.DoesNotExist as e:
                    ret['product'] = Product.objects.create()

        try:
            ret['image'] = S3Object.objects.get(pk=data['image']['id'])
        except (KeyError, S3Object.DoesNotExist, TypeError) as e:
            if "image" in ret:
                del ret['image']

        return ret

    def create(self, validated_data):
        """
        Populates the instance after the parent 'restore_object' method is
        called.
        """
        components_data = validated_data.pop('components', None)
        pillow_data = validated_data.pop('pillows', None)
        product = validated_data['product']

        width = validated_data.pop('width', None) or product.width
        depth = validated_data.pop('depth', None) or product.depth
        height = validated_data.pop('height', None) or product.height

        instance = self.Meta.model.objects.create(
            acknowledgement=self.context['acknowledgement'],
            width=width,
            depth=depth,
            height=height,
            **validated_data)

        #Calculate the total price of the item
        self._calculate_total(instance)

        instance.save()

        if pillow_data:
            pillow_serializer = PillowSerializer(data=pillow_data,
                                                 context={'item': instance},
                                                 many=True)

            if pillow_serializer.is_valid(raise_exception=True):
                pillow_serializer.save()

        if components_data:
            component_serializer = ComponentSerializer(
                data=components_data, context={'item': instance}, many=True)

            if component_serializer.is_valid(raise_exception=True):
                component_serializer.save()

        return instance

    def update(self, instance, validated_data):
        """
        Updates the instance after the parent method is called
        """
        # Loops through attributes and logs changes
        updatable_attributes = [
            'quantity', 'unit_price', 'description', 'fabric', 'comments',
            'width', 'depth', 'height', 'status'
        ]

        for attr in updatable_attributes:
            new_attr_value = validated_data.pop(attr, getattr(instance, attr))

            if getattr(instance, attr) != new_attr_value:
                old_attr_value = getattr(instance, attr)
                setattr(instance, attr, new_attr_value)

                # Log data changes
                message = u"{0}: {1} changed from {2} to {3}"
                message = message.format(instance.description, attr,
                                         old_attr_value, new_attr_value)
                ack_service.log(message, instance.acknowledgement,
                                self.context['request'])

        # Set the price of the total for this item
        self._calculate_total(instance)
        instance.save()

        pillows = validated_data.pop('pillows', [])
        for pillow_data in pillows:
            try:
                pillow = Pillow.objects.get(type=pillow_data['type'],
                                            item=instance,
                                            fabric=pillow_data['fabric'])
                serializer = PillowSerializer(pillow, data=pillow_data)
            except Pillow.DoesNotExist as e:
                serializer = PillowSerializer(data=pillow_data,
                                              context={'item': instance})

            if serializer.is_valid(raise_exception=True):
                serializer.save()

        components = validated_data.pop('components', [])
        for component_data in components:
            try:
                component = Component.objects.get(id=component_data['id'],
                                                  item=instance)
                serializer = ComponentSerializer(component,
                                                 data=component_data)
            except KeyError as e:
                logger.debug(e)
                serializer = ComponentSerializer(data=component_data,
                                                 context={'item': instance})

            if serializer.is_valid(raise_exception=True):
                serializer.save()

        return instance

    def _calculate_total(self, instance):
        """
        Calculate the total of the instance
        """
        total = instance.quantity * (instance.unit_price or 0)
        instance.total = total

        return instance.total
Exemple #14
0
class PurchaseOrderSerializer(serializers.ModelSerializer):
    supplier = SupplierSerializer()
    project = ProjectFieldSerializer(required=False, allow_null=True)
    room = RoomFieldSerializer(allow_null=True, required=False)
    phase = PhaseFieldSerializer(allow_null=True, required=False)
    acknowledgement = AcknowledgementFieldSerializer(required=False,
                                                     allow_null=True)
    items = ItemSerializer(many=True)
    order_date = serializers.DateTimeField(read_only=True)
    pdf = S3ObjectFieldSerializer(read_only=True)
    auto_print_pdf = S3ObjectFieldSerializer(read_only=True)
    logs = LogFieldSerializer(many=True, read_only=True)
    approval_pass = serializers.SerializerMethodField(read_only=True)
    files = S3ObjectFieldSerializer(many=True, allow_null=True, required=False)
    comments = serializers.CharField(allow_null=True,
                                     allow_blank=True,
                                     required=False)
    terms = serializers.CharField(allow_null=False, required=True)

    # Payment Documents
    deposit_document = S3ObjectFieldSerializer(required=False, allow_null=True)
    balance_document = S3ObjectFieldSerializer(required=False, allow_null=True)

    class Meta:
        model = PurchaseOrder
        fields = ('company', 'vat', 'supplier', 'id', 'items', 'project',
                  'grand_total', 'room', 'subtotal', 'total', 'revision',
                  'paid_date', 'receive_date', 'deposit', 'discount', 'status',
                  'terms', 'order_date', 'currency', 'phase', 'comments',
                  'acknowledgement', 'pdf', 'auto_print_pdf', 'logs',
                  'approval_pass', 'files', 'deposit_document',
                  'balance_document', 'terms')
        depth = 1
        read_only_fields = ('pdf', 'revision', 'auto_print_pdf', 'logs',
                            'approval_pass')

    def to_internal_value(self, data):
        ret = super(PurchaseOrderSerializer, self).to_internal_value(data)

        try:
            ret['supplier'] = Supplier.objects.get(pk=data['supplier']['id'])
        except (Supplier.DoesNotExist, KeyError) as e:
            try:
                ret['supplier'] = Supplier.objects.get(
                    name=data['supplier']['name'])
            except Supplier.DoesNotExist as e:
                ret['supplier'] = Supplier.objects.create(**data['supplier'])
            except Supplier.MultipleObjectsReturned as e:
                logger.warn(e)

        library = {'project': Project, 'room': Room, 'phase': Phase}
        for key in library:
            try:
                ret[key] = library[key].objects.get(pk=data[key]['id'])
            except (library[key].DoesNotExist, KeyError, TypeError) as e:

                try:
                    ret[key] = library[key].objects.create(**data[key])
                except (KeyError, TypeError) as e:
                    pass

        try:
            ret['acknowledgement'] = Acknowledgement.objects.get(
                pk=data['acknowledgement']['id'])
        except (Acknowledgement.DoesNotExist, KeyError, TypeError) as e:
            try:
                del ret['acknowledgement']
            except KeyError as e:
                pass

        for doc_type in ['deposit_document', 'balance_document']:
            try:
                ret[doc_type] = S3Object.objects.get(pk=data[doc_type]['id'])
            except (KeyError, S3Object.DoesNotExist, TypeError) as e:
                try:
                    del ret[doc_type]
                except KeyError:
                    pass

        return ret

    def create(self, validated_data):
        """
        Override the 'create' method to customize how items are created and pass the supplier instance
        to the item serializer via context
        """
        employee = self.context['request'].user

        #Discard status
        status = validated_data.pop('status', 'AWAITING APPROVAL')
        items_data = validated_data.pop('items')
        files = validated_data.pop('files', [])

        data = {}
        for key in ['currency', 'discount', 'terms']:
            try:
                data[key] = validated_data.pop(
                    key, getattr(validated_data['supplier'], key))
            except AttributeError as e:
                data[key] = getattr(validated_data['supplier'], key)

        #currency = validated_data.pop('currency', validated_data['supplier'].currency)
        #discount = validated_data.pop('discount', None) or validated_data['supplier'].discount
        #terms = validated_data.pop('terms', validated_data['supplier'].terms)
        receive_date = timezone('Asia/Bangkok').normalize(
            validated_data.pop('receive_date'))

        instance = po_service.create(employee=employee,
                                     currency=data['currency'],
                                     terms=data['terms'],
                                     discount=data['discount'],
                                     receive_date=receive_date,
                                     status="AWAITING APPROVAL",
                                     **validated_data)

        raw_items_data = self.initial_data['items']
        # Format items data by moving supply id to supply attribute
        for item_data in raw_items_data:
            if "id" in item_data:
                item_data['supply'] = {'id': item_data['id']}
                del item_data['id']

        item_serializer = ItemSerializer(data=raw_items_data,
                                         context={
                                             'supplier': instance.supplier,
                                             'po': instance,
                                             'request': self.context['request']
                                         },
                                         many=True)

        if item_serializer.is_valid(raise_exception=True):
            item_serializer.save()

        po_service.post_create(employee=employee, po=instance, files=files)

        return instance

    def update(self, instance, validated_data):
        """
        Update Purchase Order

        1. Get current user
        2. Update associated models
        3. Update Order terms and details
        4. Attach files
        5. Attach Payment Documents
        6. Update Status
        7. Update Items
        8. Calcuate a new total
        9. Create a new pdf
        """

        # Get the current user
        try:
            employee = self.context['request'].user
        except KeyError as e:
            employee = self.context['employee']

        instance.current_user = employee

        # Section: Update order terms and details
        #
        # We loop through properties, apply changes
        # and log the changes
        fields = [
            'vat', 'discount', 'deposit', 'currency', 'terms',
            'acknowledgement', 'project', 'room', 'phase'
        ]
        for field in fields:
            old_val = getattr(instance, field)
            new_val = validated_data.pop(field, old_val)

            if new_val != old_val:
                setattr(instance, field, new_val)

                self._log_change(field, old_val, new_val)

        receive_date = timezone('Asia/Bangkok').normalize(
            validated_data.pop('receive_date'))
        if receive_date != instance.receive_date:
            old_rd = instance.receive_date
            instance.receive_date = receive_date

            self._log_change('receive date', old_rd.strftime('%d/%m/%Y'),
                             receive_date.strftime('%d/%m/%Y'))

        instance.save()

        # Section: Update payment documents
        #
        # We loop through properties, apply changes
        # and log the changes
        if employee.has_perm('po.change_purchaseorder_deposit_document'):
            instance.deposit_document = validated_data.pop(
                'deposit_document', instance.deposit_document)

        if employee.has_perm('po.change_purchaseorder_balance_document'):
            instance.balance_document = validated_data.pop(
                'balance_document', instance.balance_document)

        #Update attached files
        files = validated_data.pop('files', [])
        for file_obj in files:
            try:
                File.objects.get(file_id=file_obj['id'],
                                 purchase_order=instance)
            except File.DoesNotExist:
                File.objects.create(
                    file=S3Object.objects.get(pk=file_obj['id']),
                    purchase_order=instance)

        instance.save()

        # Process if status has changed
        new_status = validated_data.pop('status', instance.status)
        old_status = instance.status

        if new_status.lower() != old_status.lower():

            # Check permissions
            if old_status.lower(
            ) == 'awaiting approval' and new_status.lower() != 'received':
                if employee.has_perm('po.approve_purchaseorder'):
                    instance.approve(user=employee)

                    self._log_change('status', old_status, new_status)
                else:
                    logger.warn(u"{0} is not qualified.".format(
                        employee.username))
            else:

                instance.status = new_status

                if old_status.lower() != "ordered" and instance.status.lower(
                ) == "received":
                    self.receive_order(instance, validated_data)

                instance.save()

                self._log_change('status', old_status, new_status)

        items_data = self.initial_data[
            'items']  #validated_data.pop('items', self.context['request'].data['items'])

        self._update_items(instance, items_data)

        instance.calculate_totals()

        instance.revision += 1

        instance.save()

        instance.create_and_upload_pdf()

        instance.save()

        try:
            instance.update_calendar_event()
        except Exception as e:
            try:
                message = "Unable to create calendar event because: {0}"
                message = message.format(e)
                POLog.objects.create(message=message,
                                     purchase_order=instance,
                                     user=employee)
            except ValueError as e:
                logger.warn(e)

        return instance

    def get_approval_pass(self, instance):
        """
        Returns the approval pass if this order has been approved
        """
        if instance.approval_pass:
            if instance.approval_pass == instance.create_approval_pass():
                return instance.approval_pass

        return None

    def receive_order(self, instance, validated_data):
        """
        Will received the order and then process the items and the corresponding supplies.
        The quantities for the supplies will automatically increase based on the supplies received
        """
        for item in instance.items.all():
            item.status = "RECEIVED"
            self._apply_new_quantity(item, instance)

            item.supply.save()
            item.save()
            self._log_receiving_item(item)

        if instance.status.lower() != 'paid':
            instance.status = "RECEIVED"
        instance.receive_date = datetime.now()
        instance.save()

        for item_data in validated_data['items']:
            item_data['status'] = "RECEIVED"

        self._email_purchaser(instance)

        return instance

    def _apply_new_quantity(self, item, po):
        # Retrieve product responding to this item and supplier
        try:
            product = Product.objects.get(supply=item.supply,
                                          supplier=po.supplier)
        except Product.MultipleObjectsReturned as e:
            logger.warn(e)
            products = Product.objects.filter(supply=item.supply,
                                              supplier=po.supplier)
            product = products.order_by('id')[0]
        except Product.DoesNotExist as e:
            msg = u"There is no product for supply {0}: {1} and supplier {2}: {3}\n\n{4}"
            msg = msg.format(item.supply.id, item.supply.description,
                             po.supplier.id, po.supplier.name, e)
            try:
                product = Product.objects.create(supply=item.supply,
                                                 supplier=po.supplier,
                                                 cost=item.unit_cost)
            except TypeError as e:
                product = Product.objects.create(supply=item.supply,
                                                 supplier=po.supplier,
                                                 cost=item.cost)

        #Calculate the quantity to add to current supply qty
        try:
            qty_to_add = Decimal(str(
                item.quantity)) * product.quantity_per_purchasing_unit
        except TypeError:
            qty_to_add = float(str(
                item.quantity)) * product.quantity_per_purchasing_unit

        #Change the supply's current quantity
        try:
            item.supply.quantity += Decimal(str(qty_to_add))
        except TypeError:
            item.supply.quantity += float(str(qty_to_add))

        return item

    def _update_items(self, instance, items_data):
        """
        Handles creation, update, and deletion of items
        """
        #Maps of id
        id_list = [item_data.get('id', None) for item_data in items_data]

        #Update or Create Item
        for item_data in items_data:

            try:
                item = po_service.get_item(pk=item_data['id'],
                                           purchase_order=instance)
                item_data['purchase_order'] = instance
                serializer = ItemSerializer(item,
                                            context={
                                                'supplier': instance.supplier,
                                                'po': instance,
                                                'request':
                                                self.context['request']
                                            },
                                            data=item_data)
            except (KeyError, Item.DoesNotExist) as e:
                serializer = ItemSerializer(data=item_data,
                                            context={
                                                'supplier': instance.supplier,
                                                'po': instance,
                                                'request':
                                                self.context['request']
                                            })

            if serializer.is_valid(raise_exception=True):
                saved_item = serializer.save()
                if saved_item.id not in id_list:
                    instance.items.add(saved_item)
                    id_list.append(saved_item.id)

        logger.debug(id_list)
        #Delete Items
        for d_item in instance.items.all():
            if d_item.id not in id_list:
                logger.debug(u'Deleting item {0}'.format(d_item.id))
                d_item.delete()

        # Check correct quantity of items
        assert len(filter(lambda x: x if x is not None else False,
                          id_list)) == instance.items.all().count()

    def _change_supply_cost(self, supply, cost):
        """
        Method to change the cost of a supply

        This will change the supply's product cost, respective of supplier, in the database
        and will log the event as 'PRICE CHANGE'
        """
        try:
            product = Product.objects.get(supply=supply,
                                          supplier=supply.supplier)
        except Product.MultipleObjectsReturned:
            product = Product.objects.filter(
                supply=supply, supplier=supply.supplier).order_by('id')[0]

        old_price = product.cost
        product.cost = cost
        product.save()

        log = Log(
            supply=supply,
            supplier=supply.supplier,
            action="PRICE CHANGE",
            quantity=None,
            cost=product.cost,
            message=
            u"Price change from {0:.2f}{2} to {1:.2f}{2} for {3} [Supplier: {4}]"
            .format(old_price, product.cost, supply.supplier.currency,
                    supply.description, supply.supplier.name))
        log.save()

    def _log_receiving_item(self, item):
        supply = item.supply
        supply.supplier = item.purchase_order.supplier

        log = Log(supply=item.supply,
                  supplier=item.purchase_order.supplier,
                  action="ADD",
                  quantity=item.quantity,
                  message=u"Received {0:.0f}{1} of {2} from {3}".format(
                      item.quantity, supply.purchasing_units,
                      supply.description, item.purchase_order.supplier.name))
        log.save()

    def _log_change(self,
                    prop,
                    old_value,
                    new_value,
                    instance=None,
                    employee=None):
        # Note: Log Changes to specified properties
        if instance is None:
            instance = self.instance

        if employee is None:
            employee = self.context['request'].user

        message = u"Purchase Order #{0}: {1} changed from {2} to {3}."
        message = message.format(instance.id, prop, old_value, new_value)
        POLog.create(message=message, purchase_order=instance, user=employee)

    def _email_purchaser(self, purchase_order):
        logger.debug(purchase_order.employee)
        if purchase_order.employee.email:
            conn = boto.ses.connect_to_region('us-east-1')
            recipients = [
                purchase_order.employee.email, '*****@*****.**'
            ]

            #Build the email body
            body = u"""<table><tr><td><h1>Purchase Order Received</h1></td><td></td><td></td></tr>
                             <tr><td>Purchase Order #</td><td>{0}</td><td></td></tr>
                             <tr><td>Supplier</td><td>{1}</td><td></td></tr>
                             <tr><td><h3>Description</h3></td><td><h3>Quantity</h3></td><td><h3>Status</h3></td</tr>
                   """.format(purchase_order.id, purchase_order.supplier.name)
            #Loop through all items to add to the body
            for item in purchase_order.items.all():
                supply = item.supply
                color = u"green" if item.status.lower(
                ) == "received" else "red"
                supply.supplier = purchase_order.supplier
                body += u"""<tr><td>{0}</td><td>{1:,.2f}{2}</td><td style="color:{3}">{4}</td></tr>
                        """.format(item.description, item.quantity,
                                   supply.purchasing_units, color, item.status)
            #Closing table tag
            body += u"</table>"

            #Send email
            conn.send_email('*****@*****.**',
                            u'Purchase Order from {0} Received'.format(
                                purchase_order.supplier.name),
                            body,
                            recipients,
                            format='html')
Exemple #15
0
class InvoiceSerializer(serializers.ModelSerializer):
    item_queryset = Item.objects.exclude(deleted=True)

    # Business Related
    document_number = serializers.IntegerField(default=0)
    customer_name = serializers.CharField()
    customer_branch = serializers.CharField(default="Headquarters")
    customer_address = serializers.CharField()
    customer_telephone = serializers.CharField(required=False,
                                               allow_null=True,
                                               allow_blank=True)
    customer_email = serializers.CharField(required=False,
                                           allow_null=True,
                                           allow_blank=True)
    customer_tax_id = serializers.CharField()
    remarks = serializers.CharField(required=False,
                                    allow_null=True,
                                    allow_blank=True)
    due_date = serializers.DateTimeField(required=True)
    issue_date = serializers.DateField(default=date.today)
    tax_date = serializers.DateField(default=date.today)

    # Relationships
    customer = CustomerOrderFieldSerializer()
    acknowledgement = AcknowledgementFieldSerializer(required=False,
                                                     allow_null=True)
    employee = UserFieldSerializer(required=False, read_only=True)
    project = ProjectFieldSerializer(required=False, allow_null=True)
    room = RoomFieldSerializer(allow_null=True, required=False)
    phase = PhaseFieldSerializer(allow_null=True, required=False)
    items = ItemSerializer(item_queryset, many=True)
    files = S3ObjectFieldSerializer(many=True, allow_null=True, required=False)
    logs = LogFieldSerializer(many=True, read_only=True)
    journal_entry = serializers.SerializerMethodField()

    class Meta:
        model = Invoice
        read_only_fields = ('total', 'subtotal', 'time_created', 'logs',
                            'grand_total')
        exclude = ('pdf', 'trcloud_id', 'company', 'company_name')
        depth = 3

    def to_internal_value(self, data):
        ret = super(InvoiceSerializer, self).to_internal_value(data)

        try:
            ret['acknowledgement'] = Acknowledgement.objects.get(
                pk=data['acknowledgement']['id'])
        except (Acknowledgement.DoesNotExist, KeyError) as e:
            try:
                del ret['acknowledgement']
            except Exception as e:
                logger.warn(e)

        try:
            ret['customer'] = Customer.objects.get(pk=data['customer']['id'])
        except (Customer.DoesNotExist, KeyError) as e:
            ret['customer'] = Customer.objects.create(**data['customer'])

        try:
            ret['project'] = Project.objects.get(pk=data['project']['id'])
        except (Project.DoesNotExist, KeyError, TypeError) as e:
            try:
                del ret['project']
            except Exception as e:
                logger.warn(e)

        logger.debug("\n\nInvoice to internal value\n\n")

        return ret

    @transaction.atomic
    def create(self, validated_data):
        """
        Override the 'create' method in order to create nested items
        """

        #Discard STatus
        status = validated_data.pop('status', 'invoiced')

        items_data = validated_data.pop('items')
        items_data = self.initial_data['items']
        files = validated_data.pop('files', [])

        # Get user
        employee = self.context['request'].user

        try:
            assert isinstance(employee, User)
        except AssertionError as e:
            employee = User.objects.get(pk=1)

        for item_data in items_data:
            for field in ['product']:
                try:
                    item_data[field] = item_data[field].id
                except KeyError:
                    pass
                except AttributeError:
                    pass

        discount = validated_data.pop('discount',
                                      validated_data['customer'].discount)
        due_date = timezone('Asia/Bangkok').normalize(
            validated_data.pop('due_date'))

        instance = self.Meta.model.objects.create(employee=employee,
                                                  company=employee.company,
                                                  discount=discount,
                                                  status='invoiced',
                                                  _due_date=due_date,
                                                  **validated_data)
        self.instance = instance

        item_serializer = ItemSerializer(data=items_data,
                                         context={
                                             'invoice': instance,
                                             'request': self.context['request']
                                         },
                                         many=True)

        if item_serializer.is_valid(raise_exception=True):
            item_serializer.save()

        instance.calculate_totals()

        instance.create_and_upload_pdf()

        # Add pdfs to files list
        filenames = [
            'pdf',
        ]
        for filename in filenames:
            self._add_file(getattr(instance, filename))

        # Assign files
        for file in files:
            self._add_file(S3Object.objects.get(pk=file['id']))

        # Create Sales Order File Link
        # - So Invoices will show up in acknowledgement Files
        if instance.acknowledgement:
            AckFile.objects.create(acknowledgement=instance.acknowledgement,
                                   file=instance.pdf)

        # Create a calendar event
        try:
            instance.create_calendar_event(employee)
        except Exception as e:
            message = u"Unable to create calendar event for invoice {0} because:\n{1}"
            message = message.format(instance.document_number, e)
            self._log(message)

        # Log Opening of an order
        message = u"Created Invoice #{0}.".format(instance.document_number)
        self._log(message)

        # Create Journal Entry in Accouting
        invoice_service.create_journal_entry(instance,
                                             company=employee.company)

        # Update Sales Order/ Acknowledgement status
        instance.acknowledgement.status = 'invoiced' if instance.acknowledgement.balance == 0 else 'partially invoiced'
        instance.acknowledgement.save()

        return instance

    @transaction.atomic
    def update(self, instance, validated_data):
        dd = timezone('Asia/Bangkok').normalize(
            validated_data.pop('due_date', instance.due_date))
        instance.project = validated_data.pop('project', instance.project)
        instance.acknowledgement = validated_data.pop('acknowledgement',
                                                      instance.acknowledgement)
        instance.vat = validated_data.pop('vat', instance.vat)
        instance.discount = validated_data.pop('discount', instance.discount)
        instance.room = validated_data.pop('room', instance.room)

        if instance.due_date != dd:
            old_dd = instance.due_date
            instance.due_date = dd

            # Log Changing delivery date
            message = u"Invoice #{0} due date changed from {1} to {2}."
            message = message.format(instance.document_number,
                                     old_dd.strftime('%d/%m/%Y'),
                                     dd.strftime('%d/%m/%Y'))
            InvoiceLog.create(message=message, invoice=instance, user=employee)

        old_qty = sum([item.quantity for item in instance.items.all()])

        # Extract items data
        items_data = validated_data.pop('items')
        items_data = self.initial_data['items']

        item_serializer = ItemSerializer(instance=instance.items.all(),
                                         data=items_data,
                                         context={
                                             'invoice':
                                             instance,
                                             'request':
                                             self.context['request'],
                                             'employee':
                                             self.context['request'].user
                                         },
                                         many=True)

        if item_serializer.is_valid(raise_exception=True):
            item_serializer.save()

        #Update attached files
        files_data = validated_data.pop('files', None)
        if (files_data):
            files_data = [{'file': f} for f in files_data]
            files_serializer = FileSerializer(
                File.objects.filter(invoice=instance),
                data=files_data,
                context={
                    'request': self.context['request'],
                    'invoice': instance
                },
                many=True)

        # Store old total and calculate new total
        instance.calculate_totals()

        instance.save()

        try:
            instance.create_and_upload_pdf()
        except IOError as e:

            tb = traceback.format_exc()
            logger.error(tb)

            message = u"Unable to update PDF for invoice {0} because:\n{1}"
            message = message.format(instance.document_number, e)
            self._log(message, instance)

        try:
            instance.update_calendar_event()
        except Exception as e:
            logger.debug(e)
            message = u"Unable to update calendar event for invoice {0} because:\n{1}"
            message = message.format(instance.document_number, e)
            self._log(message, instance)

        instance.save()

        return instance

    def _update_items(self, instance, items_data):
        """
        Handles creation, update, and deletion of items
        """

        #Maps of id
        id_list = [item_data.get('id', None) for item_data in items_data]
        logger.debug(id_list)

        #Delete Items
        for item in instance.items.all():
            if item.id not in id_list:
                item.delete()
                instance.items.filter(pk=item.id).delete()
                #item.deleted = True
                #item.save()

        #Update or Create Item
        for item_data in items_data:
            try:
                item = Item.objects.get(pk=item_data['id'], invoice=instance)
                serializer = ItemSerializer(item,
                                            context={
                                                'customer': instance.customer,
                                                'invoice': instance,
                                                'employee': instance.employee
                                            },
                                            data=item_data)
            except (KeyError, Item.DoesNotExist) as e:
                serializer = ItemSerializer(data=item_data,
                                            context={
                                                'customer': instance.customer,
                                                'invoice': instance,
                                                'employee': instance.employee
                                            })

            if serializer.is_valid(raise_exception=True):
                item = serializer.save()
                id_list.append(item.id)

    def get_journal_entry(self, instance):

        return JournalEntrySerializer(instance.journal_entry).data

    def create_journal_entry(self, instance):
        data = {
            'description': u'Invoice {0}'.format(invoice.document_number),
            'journal': {
                'id': journal_service.get(name='Revenue', company=company).id
            },
            'transactions': []
        }

        # Add to Receivable
        receivable_acc = instance.customer.account_receivable or account_service.get(
            name='Accounts Receivable (A/R)', company=company)
        receivable_desc = u'Invoice {0}: {1}'.format(instance.document_number,
                                                     instance.customer.name)
        data['transactions'].append(
            trx_service.create_trx_data(receivable_acc,
                                        receivable_desc,
                                        debit=instance.grand_total))

        # Add Sales VAT
        if invoice.vat_amount > 0:
            vat_acc = account_service.get(name='VAT Payable', company=company)
            vat_desc = u'Invoice {0}: {1}'.format(instance.document_number,
                                                  instance.customer.name)
            data['transactions'].append(
                trx_service.create_trx_data(vat_acc,
                                            vat_desc,
                                            credit=instance.vat_amount))

        #Add Transactions for income for each item
        for item in instance.items.all():
            income_acc = account_service.get(name='Sales of Product Income',
                                             company=company)
            income_desc = u'Invoice {0}: {1}'.format(instance.document_number,
                                                     item.description)
            data['transactions'].append(
                trx_service.create_trx_data(income_acc,
                                            income_desc,
                                            credit=item.total))

        serializer = JournalEntrySerializer(data=data)
        if serializer.is_valid(raise_exception=True):
            serializer.save()

            invoice.journal_entry = serializer.instance
            invoice.save()

    def _add_file(self, file):
        """
        Adds a file to the acknowledgement
        """
        File.objects.create(invoice=self.instance, file=file)

        # Log addition of file
        msg = u"Added '{0}' to Invoice #{1} files"
        msg = msg.format(file.filename, self.instance.document_number)
        self._log(msg)

    def _log(self, message, instance=None):
        """
        Create Invoice link Log
        """
        if not isinstance(instance, self.Meta.model):
            instance = self.instance

        serializer = InvoiceLogSerializer(data={'message': message},
                                          context={
                                              'invoice': instance,
                                              'request':
                                              self.context['request']
                                          })

        if serializer.is_valid(raise_exception=True):
            serializer.save()