amabnl / amadeus-ws-client

PHP Amadeus SOAP Web Service client library
Apache License 2.0
183 stars 191 forks source link

pnrCreatePnr creates multiple fares and the price diffrence is way higher than MPTB #97

Closed MyDcode closed 7 years ago

MyDcode commented 7 years ago

Thank you very much for your time and concern towards the library & I am enjoying working with it.

Once again I am having a problem with pnrCreatePnr, I'll try to brief you as short as possible and detailed.

Search criteria: 2 Adults From: CMB To: PER Type: Round trip (Seg1: CMB->KUL->PER === Seg2: PER->KUL->CMB)

As you and the Amadeus workflow suggested, I have done MPTB search then used the Air_SellFromRecommendations then checked the status of the segments and applied PNR_AddMultiElements with Option code 0 and now using same segments and itineraries to Perform PNR Pricing with Fare_PricePNRWithBookingClass once I applied it, instead of giving 1 price its giving me several price like in below picture.

image

is this behavior is common? if it is, which one would be the one which has been selected by the user? and even in one of those fareList it has 2 fare data, which one would be the price (I understand that, I am supposed to ask this from my Amadeus support, but any help from you is very much appreciated). Please refer the below image for the above statements.

image

Also I have included the option array used to create PNR as attachment for your further clarifications.

//Request structure for PNR request
        $opt = new PnrCreatePnrOptions([
            'actionCode' => PnrCreatePnrOptions::ACTION_NO_PROCESSING, //0 Do not yet save the PNR
            'travellers' => $travellers,
            'itineraries' => $itineraries,
            'elements' => $elements
        ]);

pnrCreatePnr.txt

Also my time zone is set to the default server one ([timezone] => Asia/Colombo), will this be a problem, because I read something about the time restriction in Amadeus doc, supposed to be not more than 30 minutes to their server time.

Please do ask me if you need further clarification or results.

And Please clarify!

Thank you.

MyDcode commented 7 years ago

Forgot to mention, after getting the success message from the PnrCreatePnrOptions I am using that output straight inside the pnrCreatePnr library function like below,

$client->pnrCreatePnr($opt);

once the success message comes, I am applying

$result = $client->farePricePnrWithBookingClass(
            new FarePricePnrWithBookingClassOptions()
        );

$result is the one containing fareList which is mentioned above, also not for this operations I am using the Stateful transaction from PNR Creation on-wards.

DerMika commented 7 years ago

Yes it's certainly possible that you'll get multiple pricings if you do the pricing without any additional options. You'll get to choose from all fares available for the booking classes that you chose

If you have sold the segments from the Fare_MasterPricerTravelboardSearch correctly, at least one of the returned pricings should be the same price as what you got in the MPTB reply. Unless that was some kind of special fare like a negotiated fare.

I think MPTB usually gives you the fare basis per recommendation. You can use that fare basis to do your pricing to get the exact same fare as MPTB. You also have to check if it's a public fare or some kind of negotiated fare. Also, the validating carrier of your pricing should match the one from MPTB. There are probably other things to consider, but for that, I suggest you contact Amadeus Support as they will be able to help you better ;)

Those 2 fares in 1 fareList, that's normal. They base fare (qualifier B) and total fare (qualifier 712) - which includes taxes. You can look this up on the Amadeus Extranet by looking at the Reply Structure --> Browse Structure for this message. That will open a page where you can click through elements of the response message and see what each code means. I took a screenshot of the codesets for fareDataQualifier for your information: codesets

Why do you think your timezone could be a problem? Where did you read that 30 minutes thing?

MyDcode commented 7 years ago

Regarding the time, I think I been influenced with the 'Amadeus Web Services Implementation Guide (SOAP 4.0)', in this document page 12, actually its taking about the NONCE which I believe its handled by the library, and now I also believe that that the timezone is not affecting our individual request, am I correct?

image

Please clarify this statement "public fare or some kind of negotiated fare", is it possible to have a negotiated fare set to the company id in default without programmatically instructing to do so. Because I can assure that there are non such setting has been coded in the current system, still I am getting huge difference between fares.. I will try with the fare basis as you suggested.

Thank you for clarifying the base fare part with screenshot. One tick.. 👍

and the Amadeus support person part is thoroughly noted... ;)

Also about the fares, I will come back with more example to show u more inside of my system and I would appreciate if you can recommend me to produce some request/response so that you can help solve this mystery of mine.

DerMika commented 7 years ago

The nonce generated by the library should be independent of the timezone you're using locally, since it generates a UTC date. So if your system clock isn't off by more than 30 minutes, you should be fine.

I think you can get negotiated fares by default in MasterPricer depending on the MasterPricer setup on your Amadeus Office ID. But that's just my guess, you should really talk to Amadeus Support to get a clear answer on that.

Huge differences in pricings are possible. You should just make sure you adjust your pricing call to include the necessary details so that the pricing can be done the same way as what was returned by MasterPricer. Fare basis, validating carrier, those two seem the most logical since those should both be available in your MasterPricer response.

But really, answering your questions is highly dependant on your office id setup, so Amadeus Support are the best placed to give you a good answer for your situation!

MyDcode commented 7 years ago

Hi, back again and need a small clarifications with the fare basis.

Now for example, one customer is trying to reserve a return trip to CMB & PER with 2 Adults and 1 child and 1 Infant (my point is multiple segments, multiple pax type), also please consider there is transfer flights as well.

Now in this case how can I set the fare basis in PNR pricing? or is there any simpler way to do than this method? FYI: I have a multidimensional array that has the [pax type][return/single][segments][fare basis] return/single in the sense if it is return it will have 2 indexes if it is just one way it will have 1 index.

DerMika commented 7 years ago

You'll probably have to price the different pax types separately. They'll get different fares anyway.

The following sample will price segments 1 and 2 for passengers 1 and 2 with a given fare basis:

$pricingResponse = $client->farePricePnrWithBookingClass(
    new FarePricePnrWithBookingClassOptions([
        'pricingsFareBasis' => [
                new FareBasis([
                    'fareBasisCode' => 'QNC469W2',
                    'references' => [
                        new PaxSegRef([
                            'reference' => 1,
                            'type' => PaxSegRef::TYPE_SEGMENT
                        ]),
                        new PaxSegRef([
                            'reference' => 2,
                            'type' => PaxSegRef::TYPE_SEGMENT
                        ]),
                        new PaxSegRef([
                            'reference' => 1,
                            'type' => PaxSegRef::TYPE_PASSENGER
                        ]),
                        new PaxSegRef([
                            'reference' => 2,
                            'type' => PaxSegRef::TYPE_PASSENGER
                        ]),
                    ]
                ])
            ]
    ])
);

And you'll have to do a similar call for the other pax types.

But again: Amadeus support can help you much better with these questions.

MyDcode commented 7 years ago

Once again thank you very much with the reply, let me try the input u gave me and will get back.

"Amadeus support", they are helpful but their respond time is very long sometime no response at all.

Thank you.

DerMika commented 7 years ago

I understand. But I cannot be a substitute for Amadeus Support, I just don't have the time for that.

MyDcode commented 7 years ago

Apologies for taking your time, and I hope all these questions may help others as well please bear with me.

MyDcode commented 7 years ago

Hi DerMika, found the issue in price difference and Amadeus suppor also confirmed it with log. This is the comment given by the support..

Provide query for Air_SellFromRecommendation with correct RBD (as received in MPTB response) to avoid any fare difference.

image

In the library we have option to set the Cabin class, can you give me an example to add the RBD?

I am using the following structure only..

image

DerMika commented 7 years ago

Hi,

What (I think) they mean is that you must use the correct Booking class (in the MasterPricer response found in the <rbd> node. This must be entered in your Air_SellFromRecommendationOptionsas the booking class code (where you have 'Y' in your example)

EDIT: it's important to keep in mind that all data passed to the Air_SellFromRecommendationmessage is data of the flights to book that must be extracted from the Fare_MasterPricerTravelBoardReply.

MyDcode commented 7 years ago

Hi, Once again thank you for the reply.

I understood the edited part and yes I am extracting the details from the MPTB, above I just gave you the structure which I am using and used your document as an example.

Point is I am now sending the cabin class properly, bellow 1st image is extracted from the Air_SellFromRecommendation, as you can see I am passing the cabin class but as per the support I need to pass the RBD as well, 2nd image (Extracted from the MPTB search) shows that the rbd is return by the respond.

Do I need to include this in the Air_SellFromRecommendation query? if that so, how can I do that?

image

image

MyDcode commented 7 years ago

Excuse me, just went through your answer once again and realized that you want me to give the Q (just the value) to the libraries booking class (instead of Y).

But aren't they different values?

DerMika commented 7 years ago

AFAIK you should not send the cabin class to Air_SellFromRecommendation, what it needs is booking class. And again, AFAIK, booking class is the same as rbd.

So I think your problem should be fixed by sending Air_SellFromRecommendation with the value from MasterPricer <rbd> in the booking class in Air_SellFromRecommendation.

MyDcode commented 7 years ago

I think, I am confused with the terms and codes...

will send the request as you mentioned and get back to you.

DerMika commented 7 years ago

Well, it's not like Amadeus is making it any easier by giving the same thing different names.

MyDcode commented 7 years ago

Exactly.. making developers life a nightmare!! ;-)

can you please check the below objects created to send as option for Air_SellFromRecommendation, I have return flight for KUL from CMB.

object(Amadeus\Client\RequestOptions\AirSellFromRecommendationOptions)#67 (2) {
  ["algorithm"]=>
  string(2) "M1"
  ["itinerary"]=>
  array(2) {
    [0]=>
    object(Amadeus\Client\RequestOptions\Air\SellFromRecommendation\Itinerary)#69 (3) {
      ["from"]=>
      string(3) "CMB"
      ["to"]=>
      string(3) "KUL"
      ["segments"]=>
      array(2) {
        [0]=>
        object(Amadeus\Client\RequestOptions\Air\SellFromRecommendation\Segment)#77 (8) {
          ["departureDate"]=>
          object(DateTime)#76 (3) {
            ["date"]=>
            string(26) "2017-11-28 17:59:33.000000"
            ["timezone_type"]=>
            int(3)
            ["timezone"]=>
            string(12) "Asia/Colombo"
          }
          ["from"]=>
          string(3) "CMB"
          ["to"]=>
          string(3) "SIN"
          ["companyCode"]=>
          string(2) "SQ"
          ["flightNumber"]=>
          string(3) "469"
          ["bookingClass"]=>
          string(1) "N"
          ["nrOfPassengers"]=>
          string(1) "1"
          ["statusCode"]=>
          string(2) "NN"
        }
        [1]=>
        object(Amadeus\Client\RequestOptions\Air\SellFromRecommendation\Segment)#75 (8) {
          ["departureDate"]=>
          object(DateTime)#74 (3) {
            ["date"]=>
            string(26) "2017-11-28 17:59:33.000000"
            ["timezone_type"]=>
            int(3)
            ["timezone"]=>
            string(12) "Asia/Colombo"
          }
          ["from"]=>
          string(3) "SIN"
          ["to"]=>
          string(3) "KUL"
          ["companyCode"]=>
          string(2) "SQ"
          ["flightNumber"]=>
          string(3) "106"
          ["bookingClass"]=>
          string(1) "N"
          ["nrOfPassengers"]=>
          string(1) "1"
          ["statusCode"]=>
          string(2) "NN"
        }
      }
    }
    [1]=>
    object(Amadeus\Client\RequestOptions\Air\SellFromRecommendation\Itinerary)#68 (3) {
      ["from"]=>
      string(3) "KUL"
      ["to"]=>
      string(3) "CMB"
      ["segments"]=>
      array(2) {
        [0]=>
        object(Amadeus\Client\RequestOptions\Air\SellFromRecommendation\Segment)#73 (8) {
          ["departureDate"]=>
          object(DateTime)#72 (3) {
            ["date"]=>
            string(26) "2017-12-01 17:59:33.000000"
            ["timezone_type"]=>
            int(3)
            ["timezone"]=>
            string(12) "Asia/Colombo"
          }
          ["from"]=>
          string(3) "KUL"
          ["to"]=>
          string(3) "SIN"
          ["companyCode"]=>
          string(2) "SQ"
          ["flightNumber"]=>
          string(4) "5329"
          ["bookingClass"]=>
          string(1) "N"
          ["nrOfPassengers"]=>
          string(1) "1"
          ["statusCode"]=>
          string(2) "NN"
        }
        [1]=>
        object(Amadeus\Client\RequestOptions\Air\SellFromRecommendation\Segment)#71 (8) {
          ["departureDate"]=>
          object(DateTime)#70 (3) {
            ["date"]=>
            string(26) "2017-12-01 17:59:33.000000"
            ["timezone_type"]=>
            int(3)
            ["timezone"]=>
            string(12) "Asia/Colombo"
          }
          ["from"]=>
          string(3) "SIN"
          ["to"]=>
          string(3) "CMB"
          ["companyCode"]=>
          string(2) "SQ"
          ["flightNumber"]=>
          string(3) "468"
          ["bookingClass"]=>
          string(1) "N"
          ["nrOfPassengers"]=>
          string(1) "1"
          ["statusCode"]=>
          string(2) "NN"
        }
      }
    }
  }
}
DerMika commented 7 years ago

Looks good to me. Are you getting the expected prices?

MyDcode commented 7 years ago

Excuse me the belated reply, Nope Still I didn't get the correct price yet and the reason is not only in the Air_SellFromRecommendationOptions I had the booking class also had it in the PNR creation as well...

Now a new problem arises, issue now is the farePricePnrWithBookingClass giving lowest price which is lot lower than the MPTB... I am overwriting the farePricePnrWithBookingClass request with OVERRIDE_RETURN_LOWEST that must be the reason, I am trying removing it and will see whether I am getting the right response.

but at-least customer will be happy to see price reduction! ;-)

DerMika commented 7 years ago

It may be better to give the customer what they asked for, the lower price may come with conditions they didn't ask for :)

MyDcode commented 7 years ago

Exactly the same worry that I had... removed the RLO but still giving price difference.

currently checking the PNR Creating section for any bugs, do I need to give the itineraries section in the pnrCreatePnr because if I removed it in there and try the Air_SellFromRecommendationOptions its giving me an error saying itineraries required.

But, those details supposed to come from Air_SellFromRecommendationOptions right? or I might be wrong.

Also the stateful request I am starting from pnrCreatePnr and passing the same session to the next service, is this OK or do I need to start from Air_SellFromRecommendationOptions ?

DerMika commented 7 years ago

The Air_SellFromRecommendation will write the AIR segments you want into the PNR. However a PNR needs some kind of segment before you can save it and get a record locator.

What you should do is do a pnrAddMultiElements or a pnrCreatPnr with actionCode => ACTION_NO_PROCESSING and NOT include any AIR segments. This will allow you to open a new PNR without saving it already. It is active in the context of the session.

Now you do your Air_SellFromRecommendation to add the AIR segments into your PNR. After you've done that, you can do a new pnrAddMultiElements with an actionCode to save the PNR and get a record locator.

You need to start your stateful session from the PNR creation moment, because the Air_SellFromRecommendation will automatically try to write into the PNR in session.

MyDcode commented 7 years ago

That's awesome suggestion I did the following,

  1. MPTB
  2. pnrCreatePnr (Stateful/Action Code:0/saved current session)
  3. Air_SellFromRecommendation (used created session)
  4. pnrCreatePnr (again with customer info/ used created session)
  5. farePricePnrWithBookingClass (used created session)

this time it didn't gave me the error saying itineraries required, because of the 2nd step. but the result is the same as before. MPTB gives higher value fare, farePricePnrWithBookingClassgives the lowest one (FYI i have removed the RLO).

I'll give a buzz to Amadeus support and will check with the Log. But I really appreciate your support, I always discuss about your support with my DevHub members and tell them how much you support for the fellow developers even though its not your responsibility.

I will get back to the thread once I hear from the Amadeus Support.

MyDcode commented 7 years ago

by the way... as per the Amadeus support's instructions I am sending below request to farePricePnrWithBookingClass to price the PNR.

<ns1:Fare_PricePNRWithBookingClass>
            <ns1:overrideInformation>
                <ns1:attributeDetails>
                    <ns1:attributeType>RP</ns1:attributeType>
                </ns1:attributeDetails>
                <ns1:attributeDetails>
                    <ns1:attributeType>RU</ns1:attributeType>
                </ns1:attributeDetails>
                <ns1:attributeDetails>
                    <ns1:attributeType>RLO</ns1:attributeType>
                </ns1:attributeDetails>
                <ns1:attributeDetails>
                    <ns1:attributeType>VC</ns1:attributeType>
                </ns1:attributeDetails>
            </ns1:overrideInformation>
            <ns1:validatingCarrier>
                <ns1:carrierInformation>
                    <ns1:carrierCode>SQ</ns1:carrierCode>
                </ns1:carrierInformation>
            </ns1:validatingCarrier>
        </ns1:Fare_PricePNRWithBookingClass>
MyDcode commented 7 years ago

I owe you a biggest because, the price difference is simply because of my MPTB price calculation.

What I was doing all this time, when the recommendations are received I was running a loop through the Monetary data and adding them and displayed that as the total fare (Ouccchhhh!).

Turns out that the the first record is the 712 not the base fare. I know my bad... I did MPTB when I had less experience... So I can be forgiven.. ;-)

image

even though still my query to the Amadeus support is not responded.. and no one is picking up the phone.

But with all this mess 2 best think happened...

  1. I changed the structure to better support the query (MPTB->PNR_M(0)->Air_Sell->PNR_M->Fare_PNR)
  2. And I understood Booking Class = RBD, Booking Class != Cabin Class

with that explanation, and hope you will bear my mistake I am Closing this issue.

THANK YOU!!!!

DerMika commented 7 years ago

Ah, well, at least you figured it out.

MyDcode commented 7 years ago

I know... and 1000s of Thanks to you. I will buzz you with the new issue sooner! ;-)