XeroAPI / xero-php-oauth2

Xero PHP SDK for oAuth 2 generated from Xero API OpenAPI Spec 3.0
MIT License
93 stars 66 forks source link

Create Timesheet PayrollAU 500 Error #168

Closed CraigSheppardSec closed 4 years ago

CraigSheppardSec commented 4 years ago

SDK you're using (please complete the following information):

Describe the bug When using createTimesheet method with AU Payroll I get a 500 error. All get methods are working which indicates the auth etc is all fine.

To Reproduce Tried xml (which has been working for years in a private app)

<Timesheet><EmployeeID>fded7be1[redacted]aedd58c9</EmployeeID><StartDate>2020-09-14</StartDate><EndDate>2020-09-20</EndDate><Status>Draft</Status><TimesheetLines><TimesheetLine><EarningsRateID>cab7e2b2-bd4d[redacted]705658f7e714</EarningsRateID><NumberOfUnits><NumberOfUnit>0.00</NumberOfUnit><NumberOfUnit>0.00</NumberOfUnit><NumberOfUnit>0.00</NumberOfUnit><NumberOfUnit>0.00</NumberOfUnit><NumberOfUnit>12.00</NumberOfUnit><NumberOfUnit>0.00</NumberOfUnit><NumberOfUnit>0.00</NumberOfUnit></NumberOfUnits></TimesheetLine><TimesheetLine><EarningsRateID>cab7e2b2-bd4d-40f9-b0cd-705658f7e714</EarningsRateID><NumberOfUnits><NumberOfUnit>0.00</NumberOfUnit><NumberOfUnit>0.00</NumberOfUnit><NumberOfUnit>0.00</NumberOfUnit><NumberOfUnit>0.00</NumberOfUnit><NumberOfUnit>0.00</NumberOfUnit><NumberOfUnit>0.00</NumberOfUnit><NumberOfUnit>12.00</NumberOfUnit></NumberOfUnits></TimesheetLine></TimesheetLines></Timesheet>

and JSON

[{"EmployeeID":"fded7be1[redacted]aedd58c9","StartDate":"/Date(1600041600000+0000)/","EndDate": "/Date(1600560000000+0000)/","Status": "DRAFT"}]

(which I know is blank, but should behave as such)

and many variations of elements, all without success.

Using

$config = XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken( (string)$storage->getSession()['token'] );
$payrollAuApi = new XeroAPI\XeroPHP\Api\PayrollAuApi(
   new GuzzleHttp\Client(),
   $config
 );

try {
$result = $payrollAuApi->createTimesheet($xeroTenantId, $xt_timesheet);
} catch (Exception $e) {
 echo 'Exception when calling PayrollAuApi->createTimesheet: ', $e->getMessage(), PHP_EOL;
}

Expected behavior Draft Timesheet lands in Xero

Additional context The scope is set to allow

The 500 Error is remarkably unhelpful, if anyone is able to spot something in the formatting of the timesheet (which is all I can imagine it is?) or anything else I'm missing it would be very much appreciated.

dev-welli commented 4 years ago

Hi @CraigSheppardSec

I checked the log for your application and see that both the JSON and XML produced by your app are enclosed by double quotes thus turned them into strings and not valid.

You can check the logs yourself by login to the Xero Developer portal and then go to the application's History tab.

SidneyAllen commented 4 years ago

@CraigSheppardSec - the PHP SDK does not accept raw XML or JSON. Instead you'll create an instance of the Timesheet object and set the properties on it.

Let me see if someone can mock up an example. @wobinb

CraigSheppardSec commented 4 years ago

Thank you @SidneyAllen, that would be very helpful (I'm sure not just to me!).

wobinb commented 4 years ago

OK, so here goes, this retrieves the list of employees and finds one with the payroll calendar set. It then retrieves the calendar to establish how long the period should be. Finally it creates the timesheet.

Please note that you would need to add in extra logic to calculate the length of the calendar types (Monthly, twice-monthly etc.)

    $config = XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken( (string)$storage->getAccessToken() );
    $payrollApi = new XeroAPI\XeroPHP\Api\PayrollAuApi(
      new GuzzleHttp\Client(),
      $config
    );

    //first retrieve a list of active employees
    $apiResponse = $payrollApi->getEmployees($tenantId, null, "Status==\"ACTIVE\"");
    $employees = $apiResponse->getEmployees();
    foreach ($employees AS $employee) {
      print $employee->getFirstName()." ".$employee->getLastName()." - ".$employee->getPayrollCalendarID();
       if ($employee->getPayrollCalendarID() != null) {
         $employeeID = $employee->getEmployeeID();
         $payrollCalendarID = $employee->getPayrollCalendarID();
         $ordinaryEarningsRateID = $employee->getOrdinaryEarningsRateID();
       }
       print "<br />";
    }
    print "<br />";
    //and now the payroll calendar
    $apiResponse = $payrollApi->getPayrollCalendar($tenantId,$payrollCalendarID);
    $calendar = $apiResponse->getPayrollCalendars()[0];
    print $calendar->getName()." - ".$calendar->getCalendarType();
    print "<br />";

    //finally create the timesheet
    $timesheet = new XeroAPI\XeroPHP\Models\PayrollAu\Timesheet();
    $timesheet->setEmployeeID($employeeID);
    $timesheet->setStartDateAsDate($calendar->getStartDateAsDate());
    //need to calculate how many days the timesheet will cover
    switch ($calendar->getCalendarType()) {
      case "WEEKLY":
        $lengthofCalendar = 7;
        break;
      case "FORTNIGHTLY":
        $lengthofCalendar = 14;
        break;
      case "FOURWEEKLY":
        $lengthofCalendar = 28;
        break;
    //monthly pay runs will be more complicated to calculate  
    }
    $endDate = $calendar->getStartDateAsDate();
    //end date will be start date plus the length and minus one day
    $period = "P". ($lengthofCalendar - 1) . "D";
    $endDate->add(new DateInterval($period));
    $timesheet->setEndDateAsDate($endDate);
    $timesheet->setStatus("DRAFT");
    $timesheetLine = new XeroAPI\XeroPHP\Models\PayrollAu\TimesheetLine();
    $timesheetLine->setEarningsRateId($ordinaryEarningsRateID);
    for ($day = 1; $day <= $lengthofCalendar; $day++) { 
      $numberOfUnits[] = $day;
    }
    $timesheetLine->setNumberOfUnits($numberOfUnits);
    $timesheetLines[] = $timesheetLine;
    $timesheet->setTimeSheetLines($timesheetLines);
    $timesheets[] = $timesheet;
    $apiResponse = $payrollApi->createTimesheet($tenantId,$timesheets);
CraigSheppardSec commented 4 years ago

Huge thanks @wobinb ! Very much appreciated.