php-sap / saprfc-kralik

PHP/SAP implementation for Gregor Kraliks sapnwrfc module
https://php-sap.github.io/
MIT License
10 stars 2 forks source link

ParamTrait (V.2.x) has a problem with cast output values for Multidimensional Struct #5

Closed ynnoig closed 3 years ago

ynnoig commented 3 years ago

Hello,

we updated the php-sap packages (saprfc-kralik and its dependencies) in our PHP Middleware Project (APPS -> SAP) and we noticed a problem in the ParamTrait.

We use your library with success since about two years and now we have decided to upgrade to latest version/tag to take advantage of the new capabilities of the input/output type cast etc ..

In SAP it is possible to define as a export variable a structure that can be an array of elements. We have so many RFCs that export more than one parameter that in turn is an array. This is because we cannot use tables, since it is possible to define only one table. This has been possible in SAP for some years now precisely to overcome the problem of a single table as an output parameter.

Calling RFCs with these multidimensional output structures the \ phpsap \ classes \ Api \ Struct :: cast class returns an Exception ArrayElementMissingException since it tries to check with _array_keyexists if the first member is present in the array, but the latter is a array of array, where each element in the array is exactly of the current type of the structure.

We tried to investigate in detail and try to find a solution to add directly to this issue.

The castOutputValues function in ParamTrait is the one responsible for doing this job. At this point I tried to solve the problem by modifying the function like this:

    /**
     * @param \phpsap\interfaces\Api\IValue[] $outputs
     * @param array                           $result
     * @return array
     */
    private function castOutputValues(array $outputs, array $result): array
    {
        foreach ($outputs as $output) {
            $key = $output->getName();
            $value = $result[$key];

            if (is_array($value) && count($value) !== count($value, COUNT_RECURSIVE)) {
                foreach ($value as $index => $subValue) {
                    $value[$index] = $output->cast($subValue);
                }
            } else {
                $value = $output->cast($result[$key]);
            }

            $type = $output->getType();
            if (
                $type === IElement::TYPE_STRING
                || $type === IArray::TYPE_ARRAY
            ) {
                $value = $this->rtrimStrings($value, $output);
            }
            $return[$key] = $value;
        }

        return $return;
    }

Perhaps it is not the best solution, but for how the code is structured and its workflow I have not found better alternatives or more elegant ones.

We would need this Fix as soon as possible, and I hope you can first confirm the bug, and then release a patch release that fix the indicated problem.

I hope for your help (@gregor-j)!

Greetings, Gio

ynnoig commented 3 years ago

With the suggested Solution above I have already intensive tested all RFCs that we have in SAP (only those that we use in our project, ca. 50) and it seems to work very good. Casting of Multidimensional Struct works as expected and no exception is thrown.

gregor-j commented 3 years ago

Hi Gio, it's nice to read that you have been successfully using PHP/SAP in your project.

I didn't know that cascading structs are possible. The code at \phpsap\saprfc\Traits\ApiTrait::createMembers() assumes that all members of a struct have to be \phpsap\classes\Api\Element, which I think is the root of this \phpsap\exceptions\ArrayElementMissingException.

It would be great if you could provide me with the return value of \SAPNWRFC\RemoteFunction::getFunctionDescription() and JSON encode it, so I can use it for testing.

In the light that this is publicly available on the internet, I suggest you remove all non-essential parts from the JSON and anonymize possibly identifying strings like return value names.

Best regards, Gregor

ynnoig commented 3 years ago

Hi Gregor,

thank u very much for the quick reply and confirmation of the issue as bug.

As u asked, here the json encoded of a FunctionDescription of RFC that has this complex return.

{
  "ES_API_ANGEBOT_KOPF": {
    "name": "ES_API_ANGEBOT_KOPF",
    "type": "RFCTYPE_STRUCTURE",
    "direction": "RFC_EXPORT",
    "description": "Kopfstrukturfür denFubaZEL_API_ANGEBOT",
    "ucLength": 464,
    "nucLength": 242,
    "decimals": 0,
    "optional": false,
    "default": "",
    "typedef": {
      "MANDT": {
        "name": "MANDT",
        "type": "RFCTYPE_CHAR",
        "ucLength": 6,
        "ucOffset": 0,
        "nucLength": 3,
        "nucOffset": 0,
        "decimals": 0
      },
      "VBELN": {
        "name": "VBELN",
        "type": "RFCTYPE_CHAR",
        "ucLength": 20,
        "ucOffset": 6,
        "nucLength": 10,
        "nucOffset": 3,
        "decimals": 0
      },
      "KUNNR": {
        "name": "KUNNR",
        "type": "RFCTYPE_CHAR",
        "ucLength": 20,
        "ucOffset": 26,
        "nucLength": 10,
        "nucOffset": 13,
        "decimals": 0
      },
      "ERDAT": {
        "name": "ERDAT",
        "type": "RFCTYPE_DATE",
        "ucLength": 16,
        "ucOffset": 46,
        "nucLength": 8,
        "nucOffset": 23,
        "decimals": 0
      },
      "ERZET": {
        "name": "ERZET",
        "type": "RFCTYPE_TIME",
        "ucLength": 12,
        "ucOffset": 62,
        "nucLength": 6,
        "nucOffset": 31,
        "decimals": 0
      },
      "AUART": {
        "name": "AUART",
        "type": "RFCTYPE_CHAR",
        "ucLength": 8,
        "ucOffset": 74,
        "nucLength": 4,
        "nucOffset": 37,
        "decimals": 0
      },
      "VDATU": {
        "name": "VDATU",
        "type": "RFCTYPE_DATE",
        "ucLength": 16,
        "ucOffset": 82,
        "nucLength": 8,
        "nucOffset": 41,
        "decimals": 0
      },
      "WAERK": {
        "name": "WAERK",
        "type": "RFCTYPE_CHAR",
        "ucLength": 10,
        "ucOffset": 98,
        "nucLength": 5,
        "nucOffset": 49,
        "decimals": 0
      },
      "ZEL_CRM_TEXT": {
        "name": "ZEL_CRM_TEXT",
        "type": "RFCTYPE_CHAR",
        "ucLength": 80,
        "ucOffset": 108,
        "nucLength": 40,
        "nucOffset": 54,
        "decimals": 0
      },
      "GBSTK": {
        "name": "GBSTK",
        "type": "RFCTYPE_CHAR",
        "ucLength": 2,
        "ucOffset": 188,
        "nucLength": 1,
        "nucOffset": 94,
        "decimals": 0
      },
      "BSTKD": {
        "name": "BSTKD",
        "type": "RFCTYPE_CHAR",
        "ucLength": 70,
        "ucOffset": 190,
        "nucLength": 35,
        "nucOffset": 95,
        "decimals": 0
      },
      "VTEXT": {
        "name": "VTEXT",
        "type": "RFCTYPE_CHAR",
        "ucLength": 60,
        "ucOffset": 260,
        "nucLength": 30,
        "nucOffset": 130,
        "decimals": 0
      },
      "ERNAM": {
        "name": "ERNAM",
        "type": "RFCTYPE_CHAR",
        "ucLength": 24,
        "ucOffset": 320,
        "nucLength": 12,
        "nucOffset": 160,
        "decimals": 0
      },
      "NAME": {
        "name": "NAME",
        "type": "RFCTYPE_CHAR",
        "ucLength": 60,
        "ucOffset": 344,
        "nucLength": 30,
        "nucOffset": 172,
        "decimals": 0
      },
      "GROSS_VAL": {
        "name": "GROSS_VAL",
        "type": "RFCTYPE_BCD",
        "ucLength": 6,
        "ucOffset": 404,
        "nucLength": 6,
        "nucOffset": 202,
        "decimals": 2
      },
      "SUM_NETWR": {
        "name": "SUM_NETWR",
        "type": "RFCTYPE_BCD",
        "ucLength": 8,
        "ucOffset": 410,
        "nucLength": 8,
        "nucOffset": 208,
        "decimals": 2
      },
      "SUM_MWSBP": {
        "name": "SUM_MWSBP",
        "type": "RFCTYPE_BCD",
        "ucLength": 7,
        "ucOffset": 418,
        "nucLength": 7,
        "nucOffset": 216,
        "decimals": 2
      },
      "KALSM": {
        "name": "KALSM",
        "type": "RFCTYPE_CHAR",
        "ucLength": 12,
        "ucOffset": 426,
        "nucLength": 6,
        "nucOffset": 223,
        "decimals": 0
      },
      "ZZRECHNART_KZ": {
        "name": "ZZRECHNART_KZ",
        "type": "RFCTYPE_CHAR",
        "ucLength": 4,
        "ucOffset": 438,
        "nucLength": 2,
        "nucOffset": 229,
        "decimals": 0
      },
      "ZZVERSART_DEB": {
        "name": "ZZVERSART_DEB",
        "type": "RFCTYPE_CHAR",
        "ucLength": 4,
        "ucOffset": 442,
        "nucLength": 2,
        "nucOffset": 231,
        "decimals": 0
      },
      "ZEL_ANGFREI": {
        "name": "ZEL_ANGFREI",
        "type": "RFCTYPE_CHAR",
        "ucLength": 2,
        "ucOffset": 446,
        "nucLength": 1,
        "nucOffset": 233,
        "decimals": 0
      },
      "BNDDT": {
        "name": "BNDDT",
        "type": "RFCTYPE_DATE",
        "ucLength": 16,
        "ucOffset": 448,
        "nucLength": 8,
        "nucOffset": 234,
        "decimals": 0
      }
    }
  },
  "ET_API_ANGEBOT_ADRESSE": {
    "name": "ET_API_ANGEBOT_ADRESSE",
    "type": "RFCTYPE_TABLE",
    "direction": "RFC_EXPORT",
    "description": "Adressstrukturfür den FubaZEL_API_GET_ANGEBOT",
    "ucLength": 1320,
    "nucLength": 660,
    "decimals": 0,
    "optional": false,
    "default": "",
    "typedef": {
      "KUNNR": {
        "name": "KUNNR",
        "type": "RFCTYPE_CHAR",
        "ucLength": 20,
        "ucOffset": 0,
        "nucLength": 10,
        "nucOffset": 0,
        "decimals": 0
      },
      "LIFNR": {
        "name": "LIFNR",
        "type": "RFCTYPE_CHAR",
        "ucLength": 20,
        "ucOffset": 20,
        "nucLength": 10,
        "nucOffset": 10,
        "decimals": 0
      },
      "PARVW": {
        "name": "PARVW",
        "type": "RFCTYPE_CHAR",
        "ucLength": 4,
        "ucOffset": 40,
        "nucLength": 2,
        "nucOffset": 20,
        "decimals": 0
      },
      "ADRNR": {
        "name": "ADRNR",
        "type": "RFCTYPE_CHAR",
        "ucLength": 20,
        "ucOffset": 44,
        "nucLength": 10,
        "nucOffset": 22,
        "decimals": 0
      },
      "TITLE": {
        "name": "TITLE",
        "type": "RFCTYPE_CHAR",
        "ucLength": 8,
        "ucOffset": 64,
        "nucLength": 4,
        "nucOffset": 32,
        "decimals": 0
      },
      "NAME1": {
        "name": "NAME1",
        "type": "RFCTYPE_CHAR",
        "ucLength": 80,
        "ucOffset": 72,
        "nucLength": 40,
        "nucOffset": 36,
        "decimals": 0
      },
      "NAME2": {
        "name": "NAME2",
        "type": "RFCTYPE_CHAR",
        "ucLength": 80,
        "ucOffset": 152,
        "nucLength": 40,
        "nucOffset": 76,
        "decimals": 0
      },
      "NAME3": {
        "name": "NAME3",
        "type": "RFCTYPE_CHAR",
        "ucLength": 80,
        "ucOffset": 232,
        "nucLength": 40,
        "nucOffset": 116,
        "decimals": 0
      },
      "NAME4": {
        "name": "NAME4",
        "type": "RFCTYPE_CHAR",
        "ucLength": 80,
        "ucOffset": 312,
        "nucLength": 40,
        "nucOffset": 156,
        "decimals": 0
      },
      "STREET": {
        "name": "STREET",
        "type": "RFCTYPE_CHAR",
        "ucLength": 120,
        "ucOffset": 392,
        "nucLength": 60,
        "nucOffset": 196,
        "decimals": 0
      },
      "HOUSE_NUM1": {
        "name": "HOUSE_NUM1",
        "type": "RFCTYPE_CHAR",
        "ucLength": 20,
        "ucOffset": 512,
        "nucLength": 10,
        "nucOffset": 256,
        "decimals": 0
      },
      "CITY2": {
        "name": "CITY2",
        "type": "RFCTYPE_CHAR",
        "ucLength": 80,
        "ucOffset": 532,
        "nucLength": 40,
        "nucOffset": 266,
        "decimals": 0
      },
      "POST_CODE1": {
        "name": "POST_CODE1",
        "type": "RFCTYPE_CHAR",
        "ucLength": 20,
        "ucOffset": 612,
        "nucLength": 10,
        "nucOffset": 306,
        "decimals": 0
      },
      "CITY1": {
        "name": "CITY1",
        "type": "RFCTYPE_CHAR",
        "ucLength": 80,
        "ucOffset": 632,
        "nucLength": 40,
        "nucOffset": 316,
        "decimals": 0
      },
      "COUNTRY": {
        "name": "COUNTRY",
        "type": "RFCTYPE_CHAR",
        "ucLength": 6,
        "ucOffset": 712,
        "nucLength": 3,
        "nucOffset": 356,
        "decimals": 0
      },
      "TEL_NUMBER": {
        "name": "TEL_NUMBER",
        "type": "RFCTYPE_CHAR",
        "ucLength": 60,
        "ucOffset": 718,
        "nucLength": 30,
        "nucOffset": 359,
        "decimals": 0
      },
      "FAX_NUMBER": {
        "name": "FAX_NUMBER",
        "type": "RFCTYPE_CHAR",
        "ucLength": 60,
        "ucOffset": 778,
        "nucLength": 30,
        "nucOffset": 389,
        "decimals": 0
      },
      "SMTP_ADDR": {
        "name": "SMTP_ADDR",
        "type": "RFCTYPE_CHAR",
        "ucLength": 482,
        "ucOffset": 838,
        "nucLength": 241,
        "nucOffset": 419,
        "decimals": 0
      }
    }
  },
  "ET_API_ANGEBOT_KOND": {
    "name": "ET_API_ANGEBOT_KOND",
    "type": "RFCTYPE_TABLE",
    "direction": "RFC_EXPORT",
    "description": "Konditionstabellefür den FubaZEL_API_GET_ANGEBOT",
    "ucLength": 162,
    "nucLength": 92,
    "decimals": 0,
    "optional": false,
    "default": "",
    "typedef": {
      "KNUMV": {
        "name": "KNUMV",
        "type": "RFCTYPE_CHAR",
        "ucLength": 20,
        "ucOffset": 0,
        "nucLength": 10,
        "nucOffset": 0,
        "decimals": 0
      },
      "KPOSN": {
        "name": "KPOSN",
        "type": "RFCTYPE_NUM",
        "ucLength": 12,
        "ucOffset": 20,
        "nucLength": 6,
        "nucOffset": 10,
        "decimals": 0
      },
      "KSCHL": {
        "name": "KSCHL",
        "type": "RFCTYPE_CHAR",
        "ucLength": 8,
        "ucOffset": 32,
        "nucLength": 4,
        "nucOffset": 16,
        "decimals": 0
      },
      "KAWRT": {
        "name": "KAWRT",
        "type": "RFCTYPE_BCD",
        "ucLength": 8,
        "ucOffset": 40,
        "nucLength": 8,
        "nucOffset": 20,
        "decimals": 2
      },
      "KBETR": {
        "name": "KBETR",
        "type": "RFCTYPE_BCD",
        "ucLength": 6,
        "ucOffset": 48,
        "nucLength": 6,
        "nucOffset": 28,
        "decimals": 2
      },
      "WAERS": {
        "name": "WAERS",
        "type": "RFCTYPE_CHAR",
        "ucLength": 10,
        "ucOffset": 54,
        "nucLength": 5,
        "nucOffset": 34,
        "decimals": 0
      },
      "KPEIN": {
        "name": "KPEIN",
        "type": "RFCTYPE_BCD",
        "ucLength": 3,
        "ucOffset": 64,
        "nucLength": 3,
        "nucOffset": 39,
        "decimals": 0
      },
      "KMEIN": {
        "name": "KMEIN",
        "type": "RFCTYPE_CHAR",
        "ucLength": 6,
        "ucOffset": 68,
        "nucLength": 3,
        "nucOffset": 42,
        "decimals": 0
      },
      "KWERT": {
        "name": "KWERT",
        "type": "RFCTYPE_BCD",
        "ucLength": 7,
        "ucOffset": 74,
        "nucLength": 7,
        "nucOffset": 45,
        "decimals": 2
      },
      "BEZ": {
        "name": "BEZ",
        "type": "RFCTYPE_CHAR",
        "ucLength": 80,
        "ucOffset": 82,
        "nucLength": 40,
        "nucOffset": 52,
        "decimals": 0
      }
    }
  },
  "ET_API_ANGEBOT_POSITIONEN": {
    "name": "ET_API_ANGEBOT_POSITIONEN",
    "type": "RFCTYPE_TABLE",
    "direction": "RFC_EXPORT",
    "description": "Positionsstrukturfür den FubaZEL_API_ANGEBOT",
    "ucLength": 510,
    "nucLength": 274,
    "decimals": 0,
    "optional": false,
    "default": "",
    "typedef": {
      "POSNR": {
        "name": "POSNR",
        "type": "RFCTYPE_NUM",
        "ucLength": 12,
        "ucOffset": 0,
        "nucLength": 6,
        "nucOffset": 0,
        "decimals": 0
      },
      "BISMT": {
        "name": "BISMT",
        "type": "RFCTYPE_CHAR",
        "ucLength": 36,
        "ucOffset": 12,
        "nucLength": 18,
        "nucOffset": 6,
        "decimals": 0
      },
      "KWMENG": {
        "name": "KWMENG",
        "type": "RFCTYPE_BCD",
        "ucLength": 8,
        "ucOffset": 48,
        "nucLength": 8,
        "nucOffset": 24,
        "decimals": 3
      },
      "VRKME": {
        "name": "VRKME",
        "type": "RFCTYPE_CHAR",
        "ucLength": 6,
        "ucOffset": 56,
        "nucLength": 3,
        "nucOffset": 32,
        "decimals": 0
      },
      "ZZMAKTTX3": {
        "name": "ZZMAKTTX3",
        "type": "RFCTYPE_CHAR",
        "ucLength": 160,
        "ucOffset": 62,
        "nucLength": 80,
        "nucOffset": 35,
        "decimals": 0
      },
      "RFMNG": {
        "name": "RFMNG",
        "type": "RFCTYPE_BCD",
        "ucLength": 8,
        "ucOffset": 222,
        "nucLength": 8,
        "nucOffset": 115,
        "decimals": 3
      },
      "WAERK": {
        "name": "WAERK",
        "type": "RFCTYPE_CHAR",
        "ucLength": 10,
        "ucOffset": 230,
        "nucLength": 5,
        "nucOffset": 123,
        "decimals": 0
      },
      "KDMAT": {
        "name": "KDMAT",
        "type": "RFCTYPE_CHAR",
        "ucLength": 70,
        "ucOffset": 240,
        "nucLength": 35,
        "nucOffset": 128,
        "decimals": 0
      },
      "GRPOS": {
        "name": "GRPOS",
        "type": "RFCTYPE_NUM",
        "ucLength": 12,
        "ucOffset": 310,
        "nucLength": 6,
        "nucOffset": 163,
        "decimals": 0
      },
      "POSEX": {
        "name": "POSEX",
        "type": "RFCTYPE_CHAR",
        "ucLength": 12,
        "ucOffset": 322,
        "nucLength": 6,
        "nucOffset": 169,
        "decimals": 0
      },
      "GROSS_VAL": {
        "name": "GROSS_VAL",
        "type": "RFCTYPE_BCD",
        "ucLength": 6,
        "ucOffset": 334,
        "nucLength": 6,
        "nucOffset": 175,
        "decimals": 2
      },
      "NETWR": {
        "name": "NETWR",
        "type": "RFCTYPE_BCD",
        "ucLength": 8,
        "ucOffset": 340,
        "nucLength": 8,
        "nucOffset": 181,
        "decimals": 2
      },
      "MWSBP": {
        "name": "MWSBP",
        "type": "RFCTYPE_BCD",
        "ucLength": 7,
        "ucOffset": 348,
        "nucLength": 7,
        "nucOffset": 189,
        "decimals": 2
      },
      "BSTKD": {
        "name": "BSTKD",
        "type": "RFCTYPE_CHAR",
        "ucLength": 70,
        "ucOffset": 356,
        "nucLength": 35,
        "nucOffset": 196,
        "decimals": 0
      },
      "ARKTX": {
        "name": "ARKTX",
        "type": "RFCTYPE_CHAR",
        "ucLength": 80,
        "ucOffset": 426,
        "nucLength": 40,
        "nucOffset": 231,
        "decimals": 0
      },
      "KPEIN": {
        "name": "KPEIN",
        "type": "RFCTYPE_BCD",
        "ucLength": 3,
        "ucOffset": 506,
        "nucLength": 3,
        "nucOffset": 271,
        "decimals": 0
      }
    }
  },
  "ET_RETURN": {
    "name": "ET_RETURN",
    "type": "RFCTYPE_TABLE",
    "direction": "RFC_EXPORT",
    "description": "",
    "ucLength": 940,
    "nucLength": 470,
    "decimals": 0,
    "optional": false,
    "default": "",
    "typedef": {
      "TYPE": {
        "name": "TYPE",
        "type": "RFCTYPE_CHAR",
        "ucLength": 2,
        "ucOffset": 0,
        "nucLength": 1,
        "nucOffset": 0,
        "decimals": 0
      },
      "ID": {
        "name": "ID",
        "type": "RFCTYPE_CHAR",
        "ucLength": 40,
        "ucOffset": 2,
        "nucLength": 20,
        "nucOffset": 1,
        "decimals": 0
      },
      "NUMBER": {
        "name": "NUMBER",
        "type": "RFCTYPE_NUM",
        "ucLength": 6,
        "ucOffset": 42,
        "nucLength": 3,
        "nucOffset": 21,
        "decimals": 0
      },
      "MESSAGE": {
        "name": "MESSAGE",
        "type": "RFCTYPE_CHAR",
        "ucLength": 440,
        "ucOffset": 48,
        "nucLength": 220,
        "nucOffset": 24,
        "decimals": 0
      },
      "LOG_NO": {
        "name": "LOG_NO",
        "type": "RFCTYPE_CHAR",
        "ucLength": 40,
        "ucOffset": 488,
        "nucLength": 20,
        "nucOffset": 244,
        "decimals": 0
      },
      "LOG_MSG_NO": {
        "name": "LOG_MSG_NO",
        "type": "RFCTYPE_NUM",
        "ucLength": 12,
        "ucOffset": 528,
        "nucLength": 6,
        "nucOffset": 264,
        "decimals": 0
      },
      "MESSAGE_V1": {
        "name": "MESSAGE_V1",
        "type": "RFCTYPE_CHAR",
        "ucLength": 100,
        "ucOffset": 540,
        "nucLength": 50,
        "nucOffset": 270,
        "decimals": 0
      },
      "MESSAGE_V2": {
        "name": "MESSAGE_V2",
        "type": "RFCTYPE_CHAR",
        "ucLength": 100,
        "ucOffset": 640,
        "nucLength": 50,
        "nucOffset": 320,
        "decimals": 0
      },
      "MESSAGE_V3": {
        "name": "MESSAGE_V3",
        "type": "RFCTYPE_CHAR",
        "ucLength": 100,
        "ucOffset": 740,
        "nucLength": 50,
        "nucOffset": 370,
        "decimals": 0
      },
      "MESSAGE_V4": {
        "name": "MESSAGE_V4",
        "type": "RFCTYPE_CHAR",
        "ucLength": 100,
        "ucOffset": 840,
        "nucLength": 50,
        "nucOffset": 420,
        "decimals": 0
      }
    }
  },
  "I_VBELN": {
    "name": "I_VBELN",
    "type": "RFCTYPE_CHAR",
    "direction": "RFC_IMPORT",
    "description": "Vertriebsbelegnummer",
    "ucLength": 20,
    "nucLength": 10,
    "decimals": 0,
    "optional": false,
    "default": ""
  }
}

As u See the ET_API_ANGEBOT_ADRESSE is a RFCTYPE_TABLE of the STRUCT defined in typedef. That means that ET_API_ANGEBOT_ADRESSE return an array of structures.

Here an example of output in ParamTrait:

[
  {
    "type": "array",
    "name": "ES_API_ANGEBOT_KOPF",
    "direction": "output",
    "optional": false,
    "members": [
      {
        "type": "string",
        "name": "MANDT"
      },
      {
        "type": "string",
        "name": "VBELN"
      },
      {
        "type": "string",
        "name": "KUNNR"
      },
      {
        "type": "date",
        "name": "ERDAT"
      },
      {
        "type": "time",
        "name": "ERZET"
      },
      {
        "type": "string",
        "name": "AUART"
      },
      {
        "type": "date",
        "name": "VDATU"
      },
      {
        "type": "string",
        "name": "WAERK"
      },
      {
        "type": "string",
        "name": "ZEL_CRM_TEXT"
      },
      {
        "type": "string",
        "name": "GBSTK"
      },
      {
        "type": "string",
        "name": "BSTKD"
      },
      {
        "type": "string",
        "name": "VTEXT"
      },
      {
        "type": "string",
        "name": "ERNAM"
      },
      {
        "type": "string",
        "name": "NAME"
      },
      {
        "type": "float",
        "name": "GROSS_VAL"
      },
      {
        "type": "float",
        "name": "SUM_NETWR"
      },
      {
        "type": "float",
        "name": "SUM_MWSBP"
      },
      {
        "type": "string",
        "name": "KALSM"
      },
      {
        "type": "string",
        "name": "ZZRECHNART_KZ"
      },
      {
        "type": "string",
        "name": "ZZVERSART_DEB"
      },
      {
        "type": "string",
        "name": "ZEL_ANGFREI"
      },
      {
        "type": "date",
        "name": "BNDDT"
      }
    ]
  },
  {
    "type": "array",
    "name": "ET_API_ANGEBOT_ADRESSE",
    "direction": "output",
    "optional": false,
    "members": [
      {
        "type": "string",
        "name": "KUNNR"
      },
      {
        "type": "string",
        "name": "LIFNR"
      },
      {
        "type": "string",
        "name": "PARVW"
      },
      {
        "type": "string",
        "name": "ADRNR"
      },
      {
        "type": "string",
        "name": "TITLE"
      },
      {
        "type": "string",
        "name": "NAME1"
      },
      {
        "type": "string",
        "name": "NAME2"
      },
      {
        "type": "string",
        "name": "NAME3"
      },
      {
        "type": "string",
        "name": "NAME4"
      },
      {
        "type": "string",
        "name": "STREET"
      },
      {
        "type": "string",
        "name": "HOUSE_NUM1"
      },
      {
        "type": "string",
        "name": "CITY2"
      },
      {
        "type": "string",
        "name": "POST_CODE1"
      },
      {
        "type": "string",
        "name": "CITY1"
      },
      {
        "type": "string",
        "name": "COUNTRY"
      },
      {
        "type": "string",
        "name": "TEL_NUMBER"
      },
      {
        "type": "string",
        "name": "FAX_NUMBER"
      },
      {
        "type": "string",
        "name": "SMTP_ADDR"
      }
    ]
  },
  {
    "type": "array",
    "name": "ET_API_ANGEBOT_KOND",
    "direction": "output",
    "optional": false,
    "members": [
      {
        "type": "string",
        "name": "KNUMV"
      },
      {
        "type": "int",
        "name": "KPOSN"
      },
      {
        "type": "string",
        "name": "KSCHL"
      },
      {
        "type": "float",
        "name": "KAWRT"
      },
      {
        "type": "float",
        "name": "KBETR"
      },
      {
        "type": "string",
        "name": "WAERS"
      },
      {
        "type": "float",
        "name": "KPEIN"
      },
      {
        "type": "string",
        "name": "KMEIN"
      },
      {
        "type": "float",
        "name": "KWERT"
      },
      {
        "type": "string",
        "name": "BEZ"
      }
    ]
  },
  {
    "type": "array",
    "name": "ET_API_ANGEBOT_POSITIONEN",
    "direction": "output",
    "optional": false,
    "members": [
      {
        "type": "int",
        "name": "POSNR"
      },
      {
        "type": "string",
        "name": "BISMT"
      },
      {
        "type": "float",
        "name": "KWMENG"
      },
      {
        "type": "string",
        "name": "VRKME"
      },
      {
        "type": "string",
        "name": "ZZMAKTTX3"
      },
      {
        "type": "float",
        "name": "RFMNG"
      },
      {
        "type": "string",
        "name": "WAERK"
      },
      {
        "type": "string",
        "name": "KDMAT"
      },
      {
        "type": "int",
        "name": "GRPOS"
      },
      {
        "type": "string",
        "name": "POSEX"
      },
      {
        "type": "float",
        "name": "GROSS_VAL"
      },
      {
        "type": "float",
        "name": "NETWR"
      },
      {
        "type": "float",
        "name": "MWSBP"
      },
      {
        "type": "string",
        "name": "BSTKD"
      },
      {
        "type": "string",
        "name": "ARKTX"
      },
      {
        "type": "float",
        "name": "KPEIN"
      }
    ]
  },
  {
    "type": "array",
    "name": "ET_RETURN",
    "direction": "output",
    "optional": false,
    "members": [
      {
        "type": "string",
        "name": "TYPE"
      },
      {
        "type": "string",
        "name": "ID"
      },
      {
        "type": "int",
        "name": "NUMBER"
      },
      {
        "type": "string",
        "name": "MESSAGE"
      },
      {
        "type": "string",
        "name": "LOG_NO"
      },
      {
        "type": "int",
        "name": "LOG_MSG_NO"
      },
      {
        "type": "string",
        "name": "MESSAGE_V1"
      },
      {
        "type": "string",
        "name": "MESSAGE_V2"
      },
      {
        "type": "string",
        "name": "MESSAGE_V3"
      },
      {
        "type": "string",
        "name": "MESSAGE_V4"
      }
    ]
  }
]

... and here an example of result (with all sensible data masked):

{
  "ES_API_ANGEBOT_KOPF": {
    "MANDT": "400",
    "VBELN": "masked",
    "KUNNR": "masked",
    "ERDAT": "20200405",
    "ERZET": "111558",
    "AUART": "ZAGB",
    "VDATU": "20200406",
    "WAERK": "EUR",
    "ZEL_CRM_TEXT": "",
    "GBSTK": "B",
    "BSTKD": "masked",
    "VTEXT": "masked",
    "ERNAM": "masked",
    "NAME": "masked",
    "GROSS_VAL": 217.29,
    "SUM_NETWR": 182.6,
    "SUM_MWSBP": 34.69,
    "KALSM": "ZELRDG",
    "ZZRECHNART_KZ": "RE",
    "ZZVERSART_DEB": "A1",
    "ZEL_ANGFREI": "J",
    "BNDDT": "masked"
  },
  "ET_API_ANGEBOT_ADRESSE": [
    {
      "KUNNR": "masked",
      "LIFNR": "",
      "PARVW": "masked",
      "ADRNR": "masked",
      "TITLE": "0003",
      "NAME1": "masked",
      "NAME2": "masked",
      "NAME3": "",
      "NAME4": "",
      "STREET": "masked",
      "HOUSE_NUM1": "masked",
      "CITY2": "",
      "POST_CODE1": "masked",
      "CITY1": "masked",
      "COUNTRY": "DE",
      "TEL_NUMBER": "masked",
      "FAX_NUMBER": "masked",
      "SMTP_ADDR": "masked"
    },
    {
      "KUNNR": "masked",
      "LIFNR": "",
      "PARVW": "masked",
      "ADRNR": "masked",
      "TITLE": "0003",
      "NAME1": "masked",
      "NAME2": "masked",
      "NAME3": "",
      "NAME4": "",
      "STREET": "masked",
      "HOUSE_NUM1": "masked",
      "CITY2": "",
      "POST_CODE1": "masked",
      "CITY1": "masked",
      "COUNTRY": "DE",
      "TEL_NUMBER": "masked",
      "FAX_NUMBER": "masked",
      "SMTP_ADDR": "masked"
    }
  ],
  "ET_API_ANGEBOT_KOND": [
    {
      "KNUMV": "masked",
      "KPOSN": "masked",
      "KSCHL": "ZNCU",
      "KAWRT": 23.4,
      "KBETR": 446.35,
      "WAERS": "EUR",
      "KPEIN": 0,
      "KMEIN": "",
      "KWERT": 4.27,
      "BEZ": "Kupferzuschlag"
    },
    {
      "KNUMV": "masked",
      "KPOSN": "masked",
      "KSCHL": "ZNCU",
      "KAWRT": 96.32,
      "KBETR": 446.35,
      "WAERS": "EUR",
      "KPEIN": 0,
      "KMEIN": "",
      "KWERT": 28.45,
      "BEZ": "Kupferzuschlag"
    },
    {
      "KNUMV": "masked",
      "KPOSN": "masked",
      "KSCHL": "ZNCU",
      "KAWRT": 4.75,
      "KBETR": 446.35,
      "WAERS": "EUR",
      "KPEIN": 0,
      "KMEIN": "",
      "KWERT": 0.57,
      "BEZ": "Kupferzuschlag"
    }
  ],
  "ET_API_ANGEBOT_POSITIONEN": [
    {
      "POSNR": "masked",
      "BISMT": "masked",
      "KWMENG": 3,
      "VRKME": "masked",
      "ZZMAKTTX3": "masked",
      "RFMNG": 0,
      "WAERK": "EUR",
      "KDMAT": "",
      "GRPOS": "masked",
      "POSEX": "",
      "GROSS_VAL": 29.56,
      "NETWR": 24.84,
      "MWSBP": 4.72,
      "BSTKD": "masked",
      "ARKTX": "masked",
      "KPEIN": 1
    },
    {
      "POSNR": "masked",
      "BISMT": "masked",
      "KWMENG": 200,
      "VRKME": "M",
      "ZZMAKTTX3": "masked",
      "RFMNG": 0,
      "WAERK": "EUR",
      "KDMAT": "",
      "GRPOS": "masked",
      "POSEX": "",
      "GROSS_VAL": 32.93,
      "NETWR": 27.67,
      "MWSBP": 5.26,
      "BSTKD": "masked",
      "ARKTX": "masked",
      "KPEIN": 100
    },
    {
      "POSNR": "masked",
      "BISMT": "masked",
      "KWMENG": 100,
      "VRKME": "M",
      "ZZMAKTTX3": "masked",
      "RFMNG": 0,
      "WAERK": "EUR",
      "KDMAT": "",
      "GRPOS": "masked",
      "POSEX": "",
      "GROSS_VAL": 148.47,
      "NETWR": 124.77,
      "MWSBP": 23.7,
      "BSTKD": "masked",
      "ARKTX": "masked",
      "KPEIN": 100
    },
    {
      "POSNR": "masked",
      "BISMT": "masked",
      "KWMENG": 1,
      "VRKME": "M",
      "ZZMAKTTX3": "masked",
      "RFMNG": 0,
      "WAERK": "EUR",
      "KDMAT": "",
      "GRPOS": "masked",
      "POSEX": "",
      "GROSS_VAL": 6.33,
      "NETWR": 5.32,
      "MWSBP": 1.01,
      "BSTKD": "masked",
      "ARKTX": "masked",
      "KPEIN": 100
    }
  ],
  "ET_RETURN": [
    {
      "TYPE": "I",
      "ID": "ZEL_API_N_ANGEBOTE",
      "NUMBER": "002",
      "MESSAGE": "Alles gut gelaufen",
      "LOG_NO": "",
      "LOG_MSG_NO": "000000",
      "MESSAGE_V1": "",
      "MESSAGE_V2": "",
      "MESSAGE_V3": "",
      "MESSAGE_V4": ""
    }
  ]
}

I hope now u can test with these data ;).

Thank u again and Best Regards Gio

gregor-j commented 3 years ago

Hi Gio, thank you for submitting all this data. From your initial description I thought this might be a struct within a struct kind of bug, but this is something completely different.

Until now I assumed that type=RFCTYPE_TABLE requires to have the direction=RFC_TABLES. Apparently that's not the case. There are type=RFCTYPE_TABLE with direction=RFC_EXPORT now ... And because type=RFCTYPE_TABLE and type=RFCTYPE_STRUCTURE are both mapped to array according to PHP/SAP interfaces, this bug just got upgraded to a design flaw.

I already started working on this. No ETA for now though.

Best regards, Gregor

gregor-j commented 3 years ago

Hi Gio, the master branch contains a fixed version and is ready for testing. Any stored JSON encoded APIs will need an update. The former type array is now split into struct and table.

Best regards, Gregor

ynnoig commented 3 years ago

Hi Gregor,

thank u very much for update.

I will try, if I get a little time today, to pull the dev-master and try local if everything works as expected in our middleware project.

Thank u again that u so fast have fixed the problem.

Please let me know when the tag is releases.

BR Gio

ynnoig commented 3 years ago

Hi @gregor-j

unfortunately I did n get time to test locally your changes. Do u have already planned a Release (ETA)? What do u think?

Thanks in advance Gio

gregor-j commented 3 years ago

Hi @ynnoig

I will wait for your feedback, because I can only test the output tables with mockups.

Best regards, Gregor

ynnoig commented 3 years ago

Hi @gregor-j

ok! I will test right now on my system. I will come back to u today morning.

Thanks

BR Gio

ynnoig commented 3 years ago

Hi @gregor-j ,

good news for u!

Everything works as expected. No errors.

All Unit-Tests are also successful.

Good job done! :)

BR Gio

ynnoig commented 3 years ago

Hi @gregor-j ,

did u planned already the new release?

Thanks in advance for ur Feedback.

BR Gio

gregor-j commented 3 years ago

@ynnoig I will create a new release today.

Best regards, Gregor

gregor-j commented 3 years ago

I can't get version v4.0.0 of the package php-sap/common via composer or API.

The tag exists in the repository on the Packagist website but is missing in the API which lists v3.2.0 as the latest version.

I already tried removing and adding the tag in the repository and on the Packagist website, but with no success.

This is most likely a problem with packagist. I have to wait until this problem is resolved.

gregor-j commented 3 years ago

The problem has been resolved (https://twitter.com/seldaek/status/1357336889783558145).

The composer.json now requires sapnwrfc module version 1.4.0 or higher, because lower versions don't support the getDescription() method used for auto-typecasting. I already tested whether composer correctly recognizes the module version.

@ynnoig because of the delays today the new release will be created tomorrow morning.

ynnoig commented 3 years ago

Hi @gregor-j

Thank u very much for feedback!

Br Gio