Steve
Steve

Reputation: 41

unsupported operand type(s) for *: 'NoneType' and 'decimal.Decimal'

I am trying to calculate the subtotal, VAT and Total for creating an invoice. I`ve got an error

unsupported operand type(s) for *: 'NoneType' and 'decimal.Decimal'

models.py

class InvoiceLine(models.Model):

    TAX_RATE = (      
        (0, '20% (VAT on Expenses)'),
        (1, '5% (VAT on Expenses)'),
        (2, 'EC Aquisitions (20%)'),
        (3, 'EC Aquisitions (Zero Rated)'),
        (4, 'Exempt Expenses'),
        (5, 'No VAT'),
        (6, 'Reverse Charge Expenses (20%)'),
        (7, 'Zero Rated Expenses'),
    )


    invoice = models.ForeignKey(Invoice,related_name="has_lines",on_delete=models.CASCADE, blank=True, null=True, unique=False)
    invoice_item = models.CharField(max_length=100, verbose_name="Line")
    tax_rate = models.IntegerField(choices=TAX_RATE,default=0, blank=True, null=True)
    total = models.CharField(max_length=250, null=True, blank=True)
    description = models.CharField(max_length=100, blank=True, null=True)
    unit_price_excl_tax = models.DecimalField(max_digits=8,decimal_places=2, null=True, blank=True)
    quantity = models.DecimalField(max_digits=8,decimal_places=2,default=1, null=True, blank=True)
    unit_price = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True)
    vat = models.CharField(max_length=250, null=True, blank=True)

    def subtotal(self):
        subtotal = Decimal(str(self.unit_price * self.quantity))
        return subtotal.quantize(Decimal('0.01'))

    def __unicode__(self):
        return self.description

subtotal will calculate the unit price X quantity but give me an unsupported operand type error. Then, I`ll have the total_lines and VAT by adding 20%.

Can I just calculate them using?

def total(self):
    total = Decimal(str(self.subtotal * (self.tax_rate/100)+ (self.tax_rate/100)))
    return total.quantize(Decimal('0.01'))

Upvotes: 1

Views: 913

Answers (1)

Marc Compte
Marc Compte

Reputation: 4819

The error says you are using a * operation where the first number is actually not a number, but None.

Since the last one is just a suggestion I assume the error will be on the first time you use it, when you calculate the subtotal.

def subtotal(self):
    subtotal = Decimal(str(self.unit_price * self.quantity))
    return subtotal.quantize(Decimal('0.01'))

And this would mean that unit_price is not set, thus is None.

You can do different things to avoid the error. You can either define a default value for the unit_price field:

class InvoiceLine(models.Model):

    TAX_RATE = (      
        (0, '20% (VAT on Expenses)'),
        (1, '5% (VAT on Expenses)'),
        (2, 'EC Aquisitions (20%)'),
        (3, 'EC Aquisitions (Zero Rated)'),
        (4, 'Exempt Expenses'),
        (5, 'No VAT'),
        (6, 'Reverse Charge Expenses (20%)'),
        (7, 'Zero Rated Expenses'),
    )


    invoice = models.ForeignKey(Invoice,related_name="has_lines",on_delete=models.CASCADE, blank=True, null=True, unique=False)
    invoice_item = models.CharField(max_length=100, verbose_name="Line")
    tax_rate = models.IntegerField(choices=TAX_RATE,default=0, blank=True, null=True)
    total = models.CharField(max_length=250, null=True, blank=True)
    description = models.CharField(max_length=100, blank=True, null=True)
    unit_price_excl_tax = models.DecimalField(max_digits=8,decimal_places=2, null=True, blank=True)
    quantity = models.DecimalField(max_digits=8,decimal_places=2,default=1, null=True, blank=True)
    unit_price = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True, default=0)
    vat = models.CharField(max_length=250, null=True, blank=True)

You can also prevent the null value to happen if you don't allow null values at the database level:

class InvoiceLine(models.Model):

    TAX_RATE = (      
        (0, '20% (VAT on Expenses)'),
        (1, '5% (VAT on Expenses)'),
        (2, 'EC Aquisitions (20%)'),
        (3, 'EC Aquisitions (Zero Rated)'),
        (4, 'Exempt Expenses'),
        (5, 'No VAT'),
        (6, 'Reverse Charge Expenses (20%)'),
        (7, 'Zero Rated Expenses'),
    )


    invoice = models.ForeignKey(Invoice,related_name="has_lines",on_delete=models.CASCADE, blank=True, null=True, unique=False)
    invoice_item = models.CharField(max_length=100, verbose_name="Line")
    tax_rate = models.IntegerField(choices=TAX_RATE,default=0, blank=True, null=True)
    total = models.CharField(max_length=250, null=True, blank=True)
    description = models.CharField(max_length=100, blank=True, null=True)
    unit_price_excl_tax = models.DecimalField(max_digits=8,decimal_places=2, null=True, blank=True)
    quantity = models.DecimalField(max_digits=8,decimal_places=2,default=1, null=True, blank=True)
    unit_price = models.DecimalField(max_digits=8, decimal_places=2, null=False, blank=False)
    vat = models.CharField(max_length=250, null=True, blank=True)

Or, you could leave this part untouched and do a check on you subtotal calculation:

def subtotal(self):
    if self.unit_price and self.quantity:
        subtotal = Decimal(str(self.unit_price * self.quantity))
        return subtotal.quantize(Decimal('0.01'))
    else:
        return None

Upvotes: 2

Related Questions