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')
def get_image(self, instance): """ Get Supply Image """ try: return S3ObjectFieldSerializer(instance.supply.image).data except AttributeError as e: logger.warn(e) return None
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
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
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
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
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
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)
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
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
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()
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
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')
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()