We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
data_feeds - google_merchant doesn't fetch product's taxes
`saleor/data_feeds/google_merchant.py`

```
def item_tax(item, discounts):
    """Return item tax.
    For some countries you need to set tax info
    Read more:
    https://support.google.com/merchants/answer/6324454
    """
    price = item.get_price(discounts=discounts)
    return "US::%s:y" % price.tax
```
Current approach requires taxes passed explicitly to product. In this case `price.tax` will be always zero.
After we merge logic that extracts taxes outside product, we should fix this.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `saleor/data_feeds/google_merchant.py`
Content:
```
1 import csv
2 import gzip
3 from typing import Iterable
4 
5 from django.conf import settings
6 from django.contrib.sites.models import Site
7 from django.contrib.syndication.views import add_domain
8 from django.core.files.storage import default_storage
9 from django.utils import timezone
10 from django.utils.encoding import smart_text
11 
12 from ..core.taxes import zero_money
13 from ..discount import DiscountInfo
14 from ..discount.utils import fetch_discounts
15 from ..product.models import Attribute, AttributeValue, Category, ProductVariant
16 from ..warehouse.availability import is_variant_in_stock
17 
18 CATEGORY_SEPARATOR = " > "
19 
20 FILE_PATH = "google-feed.csv.gz"
21 
22 ATTRIBUTES = [
23     "id",
24     "title",
25     "product_type",
26     "google_product_category",
27     "image_link",
28     "condition",
29     "availability",
30     "price",
31     "tax",
32     "sale_price",
33     "mpn",
34     "brand",
35     "item_group_id",
36     "gender",
37     "age_group",
38     "color",
39     "size",
40     "description",
41 ]
42 
43 
44 def get_feed_file_url():
45     return default_storage.url(FILE_PATH)
46 
47 
48 def get_feed_items():
49     items = ProductVariant.objects.all()
50     items = items.select_related("product")
51     items = items.prefetch_related(
52         "images",
53         "product__category",
54         "product__images",
55         "product__product_type__product_attributes",
56         "product__product_type__variant_attributes",
57     )
58     return items
59 
60 
61 def item_id(item: ProductVariant):
62     return item.sku
63 
64 
65 def item_mpn(item: ProductVariant):
66     return str(item.sku)
67 
68 
69 def item_guid(item: ProductVariant):
70     return item.sku
71 
72 
73 def item_title(item: ProductVariant):
74     return item.display_product()
75 
76 
77 def item_description(item: ProductVariant):
78     return item.product.plain_text_description[:100]
79 
80 
81 def item_condition(item: ProductVariant):
82     """Return a valid item condition.
83 
84     Allowed values: new, refurbished, and used.
85     Read more:
86     https://support.google.com/merchants/answer/6324469
87     """
88     return "new"
89 
90 
91 def item_brand(item: ProductVariant, attributes_dict, attribute_values_dict):
92     """Return an item brand.
93 
94     This field is required.
95     Read more:
96     https://support.google.com/merchants/answer/6324351?hl=en&ref_topic=6324338
97     """
98     brand = None
99     brand_attribute_pk = attributes_dict.get("brand")
100     publisher_attribute_pk = attributes_dict.get("publisher")
101 
102     if brand_attribute_pk:
103         brand = item.attributes.get(str(brand_attribute_pk))
104         if brand is None:
105             brand = item.product.attributes.get(str(brand_attribute_pk))
106 
107     if brand is None and publisher_attribute_pk is not None:
108         brand = item.attributes.get(str(publisher_attribute_pk))
109         if brand is None:
110             brand = item.product.attributes.get(str(publisher_attribute_pk))
111 
112     if brand:
113         brand_name = attribute_values_dict.get(brand)
114         if brand_name is not None:
115             return brand_name
116     return brand
117 
118 
119 def item_tax(item: ProductVariant, discounts: Iterable[DiscountInfo]):
120     """Return item tax.
121 
122     For some countries you need to set tax info
123     Read more:
124     https://support.google.com/merchants/answer/6324454
125     """
126     # FIXME https://github.com/mirumee/saleor/issues/4311
127     return "US::%s:y" % zero_money()
128 
129 
130 def item_group_id(item: ProductVariant):
131     return str(item.product.pk)
132 
133 
134 def item_image_link(item: ProductVariant, current_site):
135     product_image = item.get_first_image()
136     if product_image:
137         image = product_image.image
138         return add_domain(current_site.domain, image.url, False)
139     return None
140 
141 
142 def item_availability(item: ProductVariant):
143     if is_variant_in_stock(item, settings.DEFAULT_COUNTRY):
144         return "in stock"
145     return "out of stock"
146 
147 
148 def item_google_product_category(item: ProductVariant, category_paths):
149     """Return a canonical product category.
150 
151     To have your categories accepted, please use names accepted by Google or
152     write custom function which maps your category names into to Google codes.
153     Read more:
154     https://support.google.com/merchants/answer/6324436
155     """
156     category = item.product.category
157     if not category:
158         raise Exception(f"Item {item} does not have category")
159     if category.pk in category_paths:
160         return category_paths[category.pk]
161     ancestors = [ancestor.name for ancestor in list(category.get_ancestors())]
162     category_path = CATEGORY_SEPARATOR.join(ancestors + [category.name])
163     category_paths[category.pk] = category_path
164     return category_path
165 
166 
167 def item_price(item: ProductVariant):
168     price = item.get_price(discounts=None)
169     return "%s %s" % (price.amount, price.currency)
170 
171 
172 def item_sale_price(item: ProductVariant, discounts: Iterable[DiscountInfo]):
173     sale_price = item.get_price(discounts=discounts)
174     return "%s %s" % (sale_price.amount, sale_price.currency)
175 
176 
177 def item_attributes(
178     item: ProductVariant,
179     categories,
180     category_paths,
181     current_site,
182     discounts: Iterable[DiscountInfo],
183     attributes_dict,
184     attribute_values_dict,
185 ):
186     product_data = {
187         "id": item_id(item),
188         "title": item_title(item),
189         "description": item_description(item),
190         "condition": item_condition(item),
191         "mpn": item_mpn(item),
192         "item_group_id": item_group_id(item),
193         "availability": item_availability(item),
194         "google_product_category": item_google_product_category(item, category_paths),
195     }
196 
197     image_link = item_image_link(item, current_site)
198     if image_link:
199         product_data["image_link"] = image_link
200 
201     price = item_price(item)
202     product_data["price"] = price
203     sale_price = item_sale_price(item, discounts)
204     if sale_price != price:
205         product_data["sale_price"] = sale_price
206 
207     tax = item_tax(item, discounts)
208     if tax:
209         product_data["tax"] = tax
210 
211     brand = item_brand(item, attributes_dict, attribute_values_dict)
212     if brand:
213         product_data["brand"] = brand
214 
215     return product_data
216 
217 
218 def write_feed(file_obj):
219     """Write feed contents info provided file object."""
220     writer = csv.DictWriter(file_obj, ATTRIBUTES, dialect=csv.excel_tab)
221     writer.writeheader()
222     categories = Category.objects.all()
223     discounts = fetch_discounts(timezone.now())
224     attributes_dict = {a.slug: a.pk for a in Attribute.objects.all()}
225     attribute_values_dict = {
226         smart_text(a.pk): smart_text(a) for a in AttributeValue.objects.all()
227     }
228     category_paths = {}
229     current_site = Site.objects.get_current()
230     for item in get_feed_items():
231         item_data = item_attributes(
232             item,
233             categories,
234             category_paths,
235             current_site,
236             discounts,
237             attributes_dict,
238             attribute_values_dict,
239         )
240         writer.writerow(item_data)
241 
242 
243 def update_feed(file_path=FILE_PATH):
244     """Save updated feed into path provided as argument.
245 
246     Default path is defined in module as FILE_PATH.
247     """
248     with default_storage.open(file_path, "wb") as output_file:
249         output = gzip.open(output_file, "wt")
250         write_feed(output)
251         output.close()
252
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/saleor/data_feeds/google_merchant.py b/saleor/data_feeds/google_merchant.py
--- a/saleor/data_feeds/google_merchant.py
+++ b/saleor/data_feeds/google_merchant.py
@@ -8,10 +8,12 @@
 from django.core.files.storage import default_storage
 from django.utils import timezone
 from django.utils.encoding import smart_text
+from django_countries.fields import Country
 
-from ..core.taxes import zero_money
+from ..core.taxes import charge_taxes_on_shipping
 from ..discount import DiscountInfo
 from ..discount.utils import fetch_discounts
+from ..plugins.manager import get_plugins_manager
 from ..product.models import Attribute, AttributeValue, Category, ProductVariant
 from ..warehouse.availability import is_variant_in_stock
 
@@ -116,15 +118,25 @@
     return brand
 
 
-def item_tax(item: ProductVariant, discounts: Iterable[DiscountInfo]):
+def item_tax(
+    item: ProductVariant,
+    discounts: Iterable[DiscountInfo],
+    is_charge_taxes_on_shipping: bool,
+):
     """Return item tax.
 
     For some countries you need to set tax info
     Read more:
     https://support.google.com/merchants/answer/6324454
     """
-    # FIXME https://github.com/mirumee/saleor/issues/4311
-    return "US::%s:y" % zero_money()
+    country = Country(settings.DEFAULT_COUNTRY)
+    tax_rate = get_plugins_manager().get_tax_rate_percentage_value(
+        item.product.product_type, country
+    )
+    if tax_rate:
+        tax_ship = "yes" if is_charge_taxes_on_shipping else "no"
+        return "%s::%s:%s" % (country.code, tax_rate, tax_ship)
+    return None
 
 
 def item_group_id(item: ProductVariant):
@@ -182,6 +194,7 @@
     discounts: Iterable[DiscountInfo],
     attributes_dict,
     attribute_values_dict,
+    is_charge_taxes_on_shipping: bool,
 ):
     product_data = {
         "id": item_id(item),
@@ -204,7 +217,7 @@
     if sale_price != price:
         product_data["sale_price"] = sale_price
 
-    tax = item_tax(item, discounts)
+    tax = item_tax(item, discounts, is_charge_taxes_on_shipping)
     if tax:
         product_data["tax"] = tax
 
@@ -217,6 +230,7 @@
 
 def write_feed(file_obj):
     """Write feed contents info provided file object."""
+    is_charge_taxes_on_shipping = charge_taxes_on_shipping()
     writer = csv.DictWriter(file_obj, ATTRIBUTES, dialect=csv.excel_tab)
     writer.writeheader()
     categories = Category.objects.all()
@@ -236,6 +250,7 @@
             discounts,
             attributes_dict,
             attribute_values_dict,
+            is_charge_taxes_on_shipping,
         )
         writer.writerow(item_data)

{"golden_diff": "diff --git a/saleor/data_feeds/google_merchant.py b/saleor/data_feeds/google_merchant.py\n--- a/saleor/data_feeds/google_merchant.py\n+++ b/saleor/data_feeds/google_merchant.py\n@@ -8,10 +8,12 @@\n from django.core.files.storage import default_storage\n from django.utils import timezone\n from django.utils.encoding import smart_text\n+from django_countries.fields import Country\n \n-from ..core.taxes import zero_money\n+from ..core.taxes import charge_taxes_on_shipping\n from ..discount import DiscountInfo\n from ..discount.utils import fetch_discounts\n+from ..plugins.manager import get_plugins_manager\n from ..product.models import Attribute, AttributeValue, Category, ProductVariant\n from ..warehouse.availability import is_variant_in_stock\n \n@@ -116,15 +118,25 @@\n     return brand\n \n \n-def item_tax(item: ProductVariant, discounts: Iterable[DiscountInfo]):\n+def item_tax(\n+    item: ProductVariant,\n+    discounts: Iterable[DiscountInfo],\n+    is_charge_taxes_on_shipping: bool,\n+):\n     \"\"\"Return item tax.\n \n     For some countries you need to set tax info\n     Read more:\n     https://support.google.com/merchants/answer/6324454\n     \"\"\"\n-    # FIXME https://github.com/mirumee/saleor/issues/4311\n-    return \"US::%s:y\" % zero_money()\n+    country = Country(settings.DEFAULT_COUNTRY)\n+    tax_rate = get_plugins_manager().get_tax_rate_percentage_value(\n+        item.product.product_type, country\n+    )\n+    if tax_rate:\n+        tax_ship = \"yes\" if is_charge_taxes_on_shipping else \"no\"\n+        return \"%s::%s:%s\" % (country.code, tax_rate, tax_ship)\n+    return None\n \n \n def item_group_id(item: ProductVariant):\n@@ -182,6 +194,7 @@\n     discounts: Iterable[DiscountInfo],\n     attributes_dict,\n     attribute_values_dict,\n+    is_charge_taxes_on_shipping: bool,\n ):\n     product_data = {\n         \"id\": item_id(item),\n@@ -204,7 +217,7 @@\n     if sale_price != price:\n         product_data[\"sale_price\"] = sale_price\n \n-    tax = item_tax(item, discounts)\n+    tax = item_tax(item, discounts, is_charge_taxes_on_shipping)\n     if tax:\n         product_data[\"tax\"] = tax\n \n@@ -217,6 +230,7 @@\n \n def write_feed(file_obj):\n     \"\"\"Write feed contents info provided file object.\"\"\"\n+    is_charge_taxes_on_shipping = charge_taxes_on_shipping()\n     writer = csv.DictWriter(file_obj, ATTRIBUTES, dialect=csv.excel_tab)\n     writer.writeheader()\n     categories = Category.objects.all()\n@@ -236,6 +250,7 @@\n             discounts,\n             attributes_dict,\n             attribute_values_dict,\n+            is_charge_taxes_on_shipping,\n         )\n         writer.writerow(item_data)\n", "issue": "data_feeds - google_merchant doesn't fetch product's taxes\n`saleor/data_feeds/google_merchant.py`\r\n\r\n```\r\ndef item_tax(item, discounts):\r\n    \"\"\"Return item tax.\r\n    For some countries you need to set tax info\r\n    Read more:\r\n    https://support.google.com/merchants/answer/6324454\r\n    \"\"\"\r\n    price = item.get_price(discounts=discounts)\r\n    return \"US::%s:y\" % price.tax\r\n```\r\nCurrent approach requires taxes passed explicitly to product. In this case `price.tax` will be always zero.\r\nAfter we merge logic that extracts taxes outside product, we should fix this.\n", "before_files": [{"content": "import csv\nimport gzip\nfrom typing import Iterable\n\nfrom django.conf import settings\nfrom django.contrib.sites.models import Site\nfrom django.contrib.syndication.views import add_domain\nfrom django.core.files.storage import default_storage\nfrom django.utils import timezone\nfrom django.utils.encoding import smart_text\n\nfrom ..core.taxes import zero_money\nfrom ..discount import DiscountInfo\nfrom ..discount.utils import fetch_discounts\nfrom ..product.models import Attribute, AttributeValue, Category, ProductVariant\nfrom ..warehouse.availability import is_variant_in_stock\n\nCATEGORY_SEPARATOR = \" > \"\n\nFILE_PATH = \"google-feed.csv.gz\"\n\nATTRIBUTES = [\n    \"id\",\n    \"title\",\n    \"product_type\",\n    \"google_product_category\",\n    \"image_link\",\n    \"condition\",\n    \"availability\",\n    \"price\",\n    \"tax\",\n    \"sale_price\",\n    \"mpn\",\n    \"brand\",\n    \"item_group_id\",\n    \"gender\",\n    \"age_group\",\n    \"color\",\n    \"size\",\n    \"description\",\n]\n\n\ndef get_feed_file_url():\n    return default_storage.url(FILE_PATH)\n\n\ndef get_feed_items():\n    items = ProductVariant.objects.all()\n    items = items.select_related(\"product\")\n    items = items.prefetch_related(\n        \"images\",\n        \"product__category\",\n        \"product__images\",\n        \"product__product_type__product_attributes\",\n        \"product__product_type__variant_attributes\",\n    )\n    return items\n\n\ndef item_id(item: ProductVariant):\n    return item.sku\n\n\ndef item_mpn(item: ProductVariant):\n    return str(item.sku)\n\n\ndef item_guid(item: ProductVariant):\n    return item.sku\n\n\ndef item_title(item: ProductVariant):\n    return item.display_product()\n\n\ndef item_description(item: ProductVariant):\n    return item.product.plain_text_description[:100]\n\n\ndef item_condition(item: ProductVariant):\n    \"\"\"Return a valid item condition.\n\n    Allowed values: new, refurbished, and used.\n    Read more:\n    https://support.google.com/merchants/answer/6324469\n    \"\"\"\n    return \"new\"\n\n\ndef item_brand(item: ProductVariant, attributes_dict, attribute_values_dict):\n    \"\"\"Return an item brand.\n\n    This field is required.\n    Read more:\n    https://support.google.com/merchants/answer/6324351?hl=en&ref_topic=6324338\n    \"\"\"\n    brand = None\n    brand_attribute_pk = attributes_dict.get(\"brand\")\n    publisher_attribute_pk = attributes_dict.get(\"publisher\")\n\n    if brand_attribute_pk:\n        brand = item.attributes.get(str(brand_attribute_pk))\n        if brand is None:\n            brand = item.product.attributes.get(str(brand_attribute_pk))\n\n    if brand is None and publisher_attribute_pk is not None:\n        brand = item.attributes.get(str(publisher_attribute_pk))\n        if brand is None:\n            brand = item.product.attributes.get(str(publisher_attribute_pk))\n\n    if brand:\n        brand_name = attribute_values_dict.get(brand)\n        if brand_name is not None:\n            return brand_name\n    return brand\n\n\ndef item_tax(item: ProductVariant, discounts: Iterable[DiscountInfo]):\n    \"\"\"Return item tax.\n\n    For some countries you need to set tax info\n    Read more:\n    https://support.google.com/merchants/answer/6324454\n    \"\"\"\n    # FIXME https://github.com/mirumee/saleor/issues/4311\n    return \"US::%s:y\" % zero_money()\n\n\ndef item_group_id(item: ProductVariant):\n    return str(item.product.pk)\n\n\ndef item_image_link(item: ProductVariant, current_site):\n    product_image = item.get_first_image()\n    if product_image:\n        image = product_image.image\n        return add_domain(current_site.domain, image.url, False)\n    return None\n\n\ndef item_availability(item: ProductVariant):\n    if is_variant_in_stock(item, settings.DEFAULT_COUNTRY):\n        return \"in stock\"\n    return \"out of stock\"\n\n\ndef item_google_product_category(item: ProductVariant, category_paths):\n    \"\"\"Return a canonical product category.\n\n    To have your categories accepted, please use names accepted by Google or\n    write custom function which maps your category names into to Google codes.\n    Read more:\n    https://support.google.com/merchants/answer/6324436\n    \"\"\"\n    category = item.product.category\n    if not category:\n        raise Exception(f\"Item {item} does not have category\")\n    if category.pk in category_paths:\n        return category_paths[category.pk]\n    ancestors = [ancestor.name for ancestor in list(category.get_ancestors())]\n    category_path = CATEGORY_SEPARATOR.join(ancestors + [category.name])\n    category_paths[category.pk] = category_path\n    return category_path\n\n\ndef item_price(item: ProductVariant):\n    price = item.get_price(discounts=None)\n    return \"%s %s\" % (price.amount, price.currency)\n\n\ndef item_sale_price(item: ProductVariant, discounts: Iterable[DiscountInfo]):\n    sale_price = item.get_price(discounts=discounts)\n    return \"%s %s\" % (sale_price.amount, sale_price.currency)\n\n\ndef item_attributes(\n    item: ProductVariant,\n    categories,\n    category_paths,\n    current_site,\n    discounts: Iterable[DiscountInfo],\n    attributes_dict,\n    attribute_values_dict,\n):\n    product_data = {\n        \"id\": item_id(item),\n        \"title\": item_title(item),\n        \"description\": item_description(item),\n        \"condition\": item_condition(item),\n        \"mpn\": item_mpn(item),\n        \"item_group_id\": item_group_id(item),\n        \"availability\": item_availability(item),\n        \"google_product_category\": item_google_product_category(item, category_paths),\n    }\n\n    image_link = item_image_link(item, current_site)\n    if image_link:\n        product_data[\"image_link\"] = image_link\n\n    price = item_price(item)\n    product_data[\"price\"] = price\n    sale_price = item_sale_price(item, discounts)\n    if sale_price != price:\n        product_data[\"sale_price\"] = sale_price\n\n    tax = item_tax(item, discounts)\n    if tax:\n        product_data[\"tax\"] = tax\n\n    brand = item_brand(item, attributes_dict, attribute_values_dict)\n    if brand:\n        product_data[\"brand\"] = brand\n\n    return product_data\n\n\ndef write_feed(file_obj):\n    \"\"\"Write feed contents info provided file object.\"\"\"\n    writer = csv.DictWriter(file_obj, ATTRIBUTES, dialect=csv.excel_tab)\n    writer.writeheader()\n    categories = Category.objects.all()\n    discounts = fetch_discounts(timezone.now())\n    attributes_dict = {a.slug: a.pk for a in Attribute.objects.all()}\n    attribute_values_dict = {\n        smart_text(a.pk): smart_text(a) for a in AttributeValue.objects.all()\n    }\n    category_paths = {}\n    current_site = Site.objects.get_current()\n    for item in get_feed_items():\n        item_data = item_attributes(\n            item,\n            categories,\n            category_paths,\n            current_site,\n            discounts,\n            attributes_dict,\n            attribute_values_dict,\n        )\n        writer.writerow(item_data)\n\n\ndef update_feed(file_path=FILE_PATH):\n    \"\"\"Save updated feed into path provided as argument.\n\n    Default path is defined in module as FILE_PATH.\n    \"\"\"\n    with default_storage.open(file_path, \"wb\") as output_file:\n        output = gzip.open(output_file, \"wt\")\n        write_feed(output)\n        output.close()\n", "path": "saleor/data_feeds/google_merchant.py"}], "after_files": [{"content": "import csv\nimport gzip\nfrom typing import Iterable\n\nfrom django.conf import settings\nfrom django.contrib.sites.models import Site\nfrom django.contrib.syndication.views import add_domain\nfrom django.core.files.storage import default_storage\nfrom django.utils import timezone\nfrom django.utils.encoding import smart_text\nfrom django_countries.fields import Country\n\nfrom ..core.taxes import charge_taxes_on_shipping\nfrom ..discount import DiscountInfo\nfrom ..discount.utils import fetch_discounts\nfrom ..plugins.manager import get_plugins_manager\nfrom ..product.models import Attribute, AttributeValue, Category, ProductVariant\nfrom ..warehouse.availability import is_variant_in_stock\n\nCATEGORY_SEPARATOR = \" > \"\n\nFILE_PATH = \"google-feed.csv.gz\"\n\nATTRIBUTES = [\n    \"id\",\n    \"title\",\n    \"product_type\",\n    \"google_product_category\",\n    \"image_link\",\n    \"condition\",\n    \"availability\",\n    \"price\",\n    \"tax\",\n    \"sale_price\",\n    \"mpn\",\n    \"brand\",\n    \"item_group_id\",\n    \"gender\",\n    \"age_group\",\n    \"color\",\n    \"size\",\n    \"description\",\n]\n\n\ndef get_feed_file_url():\n    return default_storage.url(FILE_PATH)\n\n\ndef get_feed_items():\n    items = ProductVariant.objects.all()\n    items = items.select_related(\"product\")\n    items = items.prefetch_related(\n        \"images\",\n        \"product__category\",\n        \"product__images\",\n        \"product__product_type__product_attributes\",\n        \"product__product_type__variant_attributes\",\n    )\n    return items\n\n\ndef item_id(item: ProductVariant):\n    return item.sku\n\n\ndef item_mpn(item: ProductVariant):\n    return str(item.sku)\n\n\ndef item_guid(item: ProductVariant):\n    return item.sku\n\n\ndef item_title(item: ProductVariant):\n    return item.display_product()\n\n\ndef item_description(item: ProductVariant):\n    return item.product.plain_text_description[:100]\n\n\ndef item_condition(item: ProductVariant):\n    \"\"\"Return a valid item condition.\n\n    Allowed values: new, refurbished, and used.\n    Read more:\n    https://support.google.com/merchants/answer/6324469\n    \"\"\"\n    return \"new\"\n\n\ndef item_brand(item: ProductVariant, attributes_dict, attribute_values_dict):\n    \"\"\"Return an item brand.\n\n    This field is required.\n    Read more:\n    https://support.google.com/merchants/answer/6324351?hl=en&ref_topic=6324338\n    \"\"\"\n    brand = None\n    brand_attribute_pk = attributes_dict.get(\"brand\")\n    publisher_attribute_pk = attributes_dict.get(\"publisher\")\n\n    if brand_attribute_pk:\n        brand = item.attributes.get(str(brand_attribute_pk))\n        if brand is None:\n            brand = item.product.attributes.get(str(brand_attribute_pk))\n\n    if brand is None and publisher_attribute_pk is not None:\n        brand = item.attributes.get(str(publisher_attribute_pk))\n        if brand is None:\n            brand = item.product.attributes.get(str(publisher_attribute_pk))\n\n    if brand:\n        brand_name = attribute_values_dict.get(brand)\n        if brand_name is not None:\n            return brand_name\n    return brand\n\n\ndef item_tax(\n    item: ProductVariant,\n    discounts: Iterable[DiscountInfo],\n    is_charge_taxes_on_shipping: bool,\n):\n    \"\"\"Return item tax.\n\n    For some countries you need to set tax info\n    Read more:\n    https://support.google.com/merchants/answer/6324454\n    \"\"\"\n    country = Country(settings.DEFAULT_COUNTRY)\n    tax_rate = get_plugins_manager().get_tax_rate_percentage_value(\n        item.product.product_type, country\n    )\n    if tax_rate:\n        tax_ship = \"yes\" if is_charge_taxes_on_shipping else \"no\"\n        return \"%s::%s:%s\" % (country.code, tax_rate, tax_ship)\n    return None\n\n\ndef item_group_id(item: ProductVariant):\n    return str(item.product.pk)\n\n\ndef item_image_link(item: ProductVariant, current_site):\n    product_image = item.get_first_image()\n    if product_image:\n        image = product_image.image\n        return add_domain(current_site.domain, image.url, False)\n    return None\n\n\ndef item_availability(item: ProductVariant):\n    if is_variant_in_stock(item, settings.DEFAULT_COUNTRY):\n        return \"in stock\"\n    return \"out of stock\"\n\n\ndef item_google_product_category(item: ProductVariant, category_paths):\n    \"\"\"Return a canonical product category.\n\n    To have your categories accepted, please use names accepted by Google or\n    write custom function which maps your category names into to Google codes.\n    Read more:\n    https://support.google.com/merchants/answer/6324436\n    \"\"\"\n    category = item.product.category\n    if not category:\n        raise Exception(f\"Item {item} does not have category\")\n    if category.pk in category_paths:\n        return category_paths[category.pk]\n    ancestors = [ancestor.name for ancestor in list(category.get_ancestors())]\n    category_path = CATEGORY_SEPARATOR.join(ancestors + [category.name])\n    category_paths[category.pk] = category_path\n    return category_path\n\n\ndef item_price(item: ProductVariant):\n    price = item.get_price(discounts=None)\n    return \"%s %s\" % (price.amount, price.currency)\n\n\ndef item_sale_price(item: ProductVariant, discounts: Iterable[DiscountInfo]):\n    sale_price = item.get_price(discounts=discounts)\n    return \"%s %s\" % (sale_price.amount, sale_price.currency)\n\n\ndef item_attributes(\n    item: ProductVariant,\n    categories,\n    category_paths,\n    current_site,\n    discounts: Iterable[DiscountInfo],\n    attributes_dict,\n    attribute_values_dict,\n    is_charge_taxes_on_shipping: bool,\n):\n    product_data = {\n        \"id\": item_id(item),\n        \"title\": item_title(item),\n        \"description\": item_description(item),\n        \"condition\": item_condition(item),\n        \"mpn\": item_mpn(item),\n        \"item_group_id\": item_group_id(item),\n        \"availability\": item_availability(item),\n        \"google_product_category\": item_google_product_category(item, category_paths),\n    }\n\n    image_link = item_image_link(item, current_site)\n    if image_link:\n        product_data[\"image_link\"] = image_link\n\n    price = item_price(item)\n    product_data[\"price\"] = price\n    sale_price = item_sale_price(item, discounts)\n    if sale_price != price:\n        product_data[\"sale_price\"] = sale_price\n\n    tax = item_tax(item, discounts, is_charge_taxes_on_shipping)\n    if tax:\n        product_data[\"tax\"] = tax\n\n    brand = item_brand(item, attributes_dict, attribute_values_dict)\n    if brand:\n        product_data[\"brand\"] = brand\n\n    return product_data\n\n\ndef write_feed(file_obj):\n    \"\"\"Write feed contents info provided file object.\"\"\"\n    is_charge_taxes_on_shipping = charge_taxes_on_shipping()\n    writer = csv.DictWriter(file_obj, ATTRIBUTES, dialect=csv.excel_tab)\n    writer.writeheader()\n    categories = Category.objects.all()\n    discounts = fetch_discounts(timezone.now())\n    attributes_dict = {a.slug: a.pk for a in Attribute.objects.all()}\n    attribute_values_dict = {\n        smart_text(a.pk): smart_text(a) for a in AttributeValue.objects.all()\n    }\n    category_paths = {}\n    current_site = Site.objects.get_current()\n    for item in get_feed_items():\n        item_data = item_attributes(\n            item,\n            categories,\n            category_paths,\n            current_site,\n            discounts,\n            attributes_dict,\n            attribute_values_dict,\n            is_charge_taxes_on_shipping,\n        )\n        writer.writerow(item_data)\n\n\ndef update_feed(file_path=FILE_PATH):\n    \"\"\"Save updated feed into path provided as argument.\n\n    Default path is defined in module as FILE_PATH.\n    \"\"\"\n    with default_storage.open(file_path, \"wb\") as output_file:\n        output = gzip.open(output_file, \"wt\")\n        write_feed(output)\n        output.close()\n", "path": "saleor/data_feeds/google_merchant.py"}]}