Repository: tefra/xsdata

Issue:
Prevent flattening base classes with a base class

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/tefra/xsdata/pull/474?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T) Report
> Merging [#474](https://codecov.io/gh/tefra/xsdata/pull/474?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T) (784a3cb) into [master](https://codecov.io/gh/tefra/xsdata/commit/9909d5251a9810835b09d3e52adaf3ecb39fc0dc?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T) (9909d52) will **not change** coverage.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/tefra/xsdata/pull/474/graphs/tree.svg?width=650&height=150&src=pr&token=YzDDLtywvl&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T)](https://codecov.io/gh/tefra/xsdata/pull/474?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T)

```diff
@@            Coverage Diff            @@
##            master      #474   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           77        77           
  Lines         7169      7169           
  Branches      1283      1283           
=========================================
  Hits          7169      7169           
```


| [Impacted Files](https://codecov.io/gh/tefra/xsdata/pull/474?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T) | Coverage Δ | |
|---|---|---|
| [xsdata/codegen/handlers/class\_extension.py](https://codecov.io/gh/tefra/xsdata/pull/474/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T#diff-eHNkYXRhL2NvZGVnZW4vaGFuZGxlcnMvY2xhc3NfZXh0ZW5zaW9uLnB5) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tefra/xsdata/pull/474?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tefra/xsdata/pull/474?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T). Last update [9909d52...784a3cb](https://codecov.io/gh/tefra/xsdata/pull/474?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+T).


Patch:
diff --git a/xsdata/codegen/handlers/class_extension.py b/xsdata/codegen/handlers/class_extension.py
index 2284be8a..ca16828c 100644
--- a/xsdata/codegen/handlers/class_extension.py
+++ b/xsdata/codegen/handlers/class_extension.py
@@ -184,14 +184,15 @@ class ClassExtensionHandler(HandlerInterface):
         Return whether the extension should be flattened because of rules.
 
         Rules:
-            1. Source class is a simple type
-            2. Source class has a suffix attr and target has its own attrs
-            3. Target class has a suffix attr
-            4. Target restrictions parent attrs in different sequential order
-            5. Target restricts parent attr with a not matching type.
+            1. Source doesn't have a parent class
+            2. Source class is a simple type
+            3. Source class has a suffix attr and target has its own attrs
+            4. Target class has a suffix attr
+            5. Target restrictions parent attrs in different sequential order
+            6. Target restricts parent attr with a not matching type.
         """
 
-        if (
+        if not source.extensions and (
             source.is_simple_type
             or target.has_suffix_attr
             or (source.has_suffix_attr and target.attrs)


Test patch:
diff --git a/tests/codegen/handlers/test_class_extension.py b/tests/codegen/handlers/test_class_extension.py
index 21c19a3b..628d1890 100644
--- a/tests/codegen/handlers/test_class_extension.py
+++ b/tests/codegen/handlers/test_class_extension.py
@@ -371,6 +371,10 @@ class ClassExtensionHandlerTests(FactoryTestCase):
         target.attrs.append(AttrFactory.create())
         self.assertTrue(self.processor.should_flatten_extension(source, target))
 
+        # Source has is a subclass
+        source.extensions.append(ExtensionFactory.create())
+        self.assertFalse(self.processor.should_flatten_extension(source, target))
+
         # Target has suffix attr
         source = ClassFactory.create()
         target = ClassFactory.elements(1)
diff --git a/tests/fixtures/defxmlschema/chapter13.json b/tests/fixtures/defxmlschema/chapter13.json
index 5ff8c5dc..39c69fc6 100644
--- a/tests/fixtures/defxmlschema/chapter13.json
+++ b/tests/fixtures/defxmlschema/chapter13.json
@@ -11,22 +11,22 @@
     ],
     "umbrella": [
         {
+            "routingNum": 123,
             "number": 557,
             "name": "Ten-Gallon Hat",
             "description": null,
-            "routingNum": 123,
-            "lang": null,
-            "effDate": "2002-04-02"
+            "effDate": "2002-04-02",
+            "lang": null
         }
     ],
     "shirt": [
         {
+            "routingNum": 124,
             "number": 557,
             "name": "Short-Sleeved Linen Blouse",
             "description": null,
-            "routingNum": 124,
-            "lang": null,
             "effDate": "2002-04-02",
+            "lang": null,
             "size": [
                 {
                     "value": 6,
diff --git a/tests/fixtures/defxmlschema/chapter13.py b/tests/fixtures/defxmlschema/chapter13.py
index 43e1b024..74485aad 100644
--- a/tests/fixtures/defxmlschema/chapter13.py
+++ b/tests/fixtures/defxmlschema/chapter13.py
@@ -24,54 +24,6 @@ class ItemType:
     )
 
 
-@dataclass
-class RestrictedProductType:
-    number: Optional[int] = field(
-        default=None,
-        metadata={
-            "type": "Element",
-            "namespace": "",
-            "required": True,
-        }
-    )
-    name: Optional[str] = field(
-        default=None,
-        metadata={
-            "type": "Element",
-            "namespace": "",
-            "required": True,
-        }
-    )
-    description: Optional[str] = field(
-        default=None,
-        metadata={
-            "type": "Element",
-            "namespace": "",
-        }
-    )
-    routing_num: Optional[int] = field(
-        default=None,
-        metadata={
-            "name": "routingNum",
-            "type": "Attribute",
-            "required": True,
-        }
-    )
-    lang: Optional[str] = field(
-        default=None,
-        metadata={
-            "type": "Attribute",
-        }
-    )
-    eff_date: XmlDate = field(
-        default=XmlDate(1900, 1, 1),
-        metadata={
-            "name": "effDate",
-            "type": "Attribute",
-        }
-    )
-
-
 @dataclass
 class SizeType:
     value: Optional[int] = field(
@@ -139,6 +91,33 @@ class SmallSizeType(SizeType):
     )
 
 
+@dataclass
+class RestrictedProductType(ProductType):
+    name: Optional[str] = field(
+        default=None,
+        metadata={
+            "type": "Element",
+            "namespace": "",
+            "required": True,
+        }
+    )
+    routing_num: Optional[int] = field(
+        default=None,
+        metadata={
+            "name": "routingNum",
+            "type": "Attribute",
+            "required": True,
+        }
+    )
+    eff_date: XmlDate = field(
+        default=XmlDate(1900, 1, 1),
+        metadata={
+            "name": "effDate",
+            "type": "Attribute",
+        }
+    )
+
+
 @dataclass
 class ShirtType(RestrictedProductType):
     size: List[SmallSizeType] = field(