Repository: saleor/saleor

Issue:
Error resolving ProductType with no TaxRateType
### What I'm trying to achieve
To resolve `TaxRateType` on `ProductType`, when it's set to null.

### Steps to reproduce the problem
1. Execute query
```
{
  productType(id: "UHJvZHVjdFR5cGU6MTA=") {
    id
    taxRate
  }
}
```
2. Get an error
```
{
  "message": "Expected a value of type \"TaxRateType\" but received: ",
  "path": [
    "productType",
    "taxRate"
  ]
}
```


Discussion:
Not only if it's set to null, but also when `tax_rate` is blank (as it is allowed to be blank in the models and an empty tax rate is inserted during the db populate), it still tries to convert it to the TaxRateType `enum`: https://github.com/mirumee/saleor/issues/3278#issuecomment-438065757.

We should fix this by ensuring we don't have products with invalid/missing tax rates. Everything that is not explicitly assigned to a special group should be assigned to default.
So maybe we should remove `blank=True` from models and ensure that we always set the default tax rate? Originally I thought that having `null` is fine - if you don't use taxes and even the "standard" rate doesn't make sense, you wouldn't ever set any value for that field.
Well, if you don't apply taxes then I guess "standard" is not to apply a tax 😄 

Resolving patch:
diff --git a/saleor/dashboard/product/forms.py b/saleor/dashboard/product/forms.py
--- a/saleor/dashboard/product/forms.py
+++ b/saleor/dashboard/product/forms.py
@@ -58,7 +58,7 @@ class ProductTypeSelectorForm(forms.Form):
 
 
 def get_tax_rate_type_choices():
-    rate_types = get_tax_rate_types() + [DEFAULT_TAX_RATE_NAME, '']
+    rate_types = get_tax_rate_types() + [DEFAULT_TAX_RATE_NAME]
     translations = dict(TaxRateType.CHOICES)
     choices = [
         (rate_name, translations.get(rate_name, '---------'))
diff --git a/saleor/product/migrations/0079_default_tax_rate_instead_of_empty_field.py b/saleor/product/migrations/0079_default_tax_rate_instead_of_empty_field.py
new file mode 100644
--- /dev/null
+++ b/saleor/product/migrations/0079_default_tax_rate_instead_of_empty_field.py
@@ -0,0 +1,27 @@
+# Generated by Django 2.1.3 on 2018-11-26 14:25
+
+from django.db import migrations, models
+
+from ...core.utils.taxes import DEFAULT_TAX_RATE_NAME
+
+
+def add_default_tax_rate_instead_of_empty_field(apps, schema_editor):
+    ProductType = apps.get_model('product', 'ProductType')
+    product_types = ProductType.objects.filter(tax_rate='')
+    product_types.update(tax_rate=DEFAULT_TAX_RATE_NAME)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('product', '0078_auto_20181120_0437'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='producttype',
+            name='tax_rate',
+            field=models.CharField(choices=[('accommodation', 'accommodation'), ('admission to cultural events', 'admission to cultural events'), ('admission to entertainment events', 'admission to entertainment events'), ('admission to sporting events', 'admission to sporting events'), ('advertising', 'advertising'), ('agricultural supplies', 'agricultural supplies'), ('baby foodstuffs', 'baby foodstuffs'), ('bikes', 'bikes'), ('books', 'books'), ('childrens clothing', 'childrens clothing'), ('domestic fuel', 'domestic fuel'), ('domestic services', 'domestic services'), ('e-books', 'e-books'), ('foodstuffs', 'foodstuffs'), ('hotels', 'hotels'), ('medical', 'medical'), ('newspapers', 'newspapers'), ('passenger transport', 'passenger transport'), ('pharmaceuticals', 'pharmaceuticals'), ('property renovations', 'property renovations'), ('restaurants', 'restaurants'), ('social housing', 'social housing'), ('standard', 'standard'), ('water', 'water')], default='standard', max_length=128),
+        ),
+        migrations.RunPython(add_default_tax_rate_instead_of_empty_field)
+    ]
diff --git a/saleor/product/models.py b/saleor/product/models.py
--- a/saleor/product/models.py
+++ b/saleor/product/models.py
@@ -77,7 +77,7 @@ class ProductType(models.Model):
     has_variants = models.BooleanField(default=True)
     is_shipping_required = models.BooleanField(default=False)
     tax_rate = models.CharField(
-        max_length=128, default=DEFAULT_TAX_RATE_NAME, blank=True,
+        max_length=128, default=DEFAULT_TAX_RATE_NAME,
         choices=TaxRateType.CHOICES)
     weight = MeasurementField(
         measurement=Weight, unit_choices=WeightUnits.CHOICES,


Test patch: