fjffk简�的咖啡店科开�2方法2
环æ»�皆山也。其西å�—诸峰,林壑尤美,望之蔚然而深秀者,ç�…ç�Šä¹Ÿã€‚山行å…七里,æ¸�闻水声潺潺,而泻出于两峰之间者,酿泉也
???????????????????????????????
??????????????/?????????????????
............/...................
.........................../...
???????????????????????????????
>>>>>>>>>>>>>/>>>>>>>>>>>>>>>>>
ÿØÿà JFIF ÿÛ „ ( %!1!%)+//.383,7(-.+
-%%-////---/-.+/--+------/------/--0+--/-/-----.-----ÿÀ ¥2" ÿÄ ÿÄ J ! 1AQ"aq�2‘#BR‚¡�Ñ3br’¢±Âð$CSƒ²á4c“%DsÓñÿÄ ÿÄ * !1AQa‘"2q3�±ð#b¡ÿÚ ? ¼QxJQa�uò¸Zö Úü8,�Úú
"SSn<rçù–´âE—^ªBÖ9À\†¸Â�ÔÂ�Ã�TÂÃÛ5
ëd´³Ã�#Ã�;Þ38ϔÂ� ¶H£M:wÃŽ3…³…âpÔF&‚FK¸9„â4à GEõªfÂ�ÿ ‘ñ(ßwÂpŽF|È¥ù®häðÃ�Ѷ¹‘[Ã’inÙW¶ùñYËœQ{›K"išÒ[Ú8žë\F¹@-?v"ÔUâ€�,ìöžkÿ {I‡£šÃ�?e
rÂ�ÃV
?>
.........................................
.............................................................................
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
>>>>>>>>>>>>>/>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
dsdfPK ºqW]Ÿ„¾¥= = laravel-xero/readme.mdnu „[µü¤ ## Community
There is a Discord community. https://discord.gg/VYau8hgwrm For quick help, ask questions in the appropriate channel.
[](https://packagist.org/packages/dcblogdev/laravel-xero)
[](https://packagist.org/packages/dcblogdev/laravel-xero)

Laravel package for working with the Xero API
Watch a video walkthrough https://www.youtube.com/watch?v=sORX2z-AH1k
Xero API documentation can be found at:
https://developer.xero.com/documentation/
Before you can integrate with Xero you will need to create an app, go to https://developer.xero.com/myapps to register a new app.
For the grant type select, Auth code (web app)
For OAuth 2.0 redirect URI enter the full URL you want to use for connection to Xero from your application such as https://domain.com/xero/connect
## Documentation
Complete docs at https://dcblog.dev/docs/laravel-xero/v1/introduction
## Change log
Please see the [changelog][3] for more information on what has changed recently.
## Contributing
Contributions are welcome and will be fully credited.
Contributions are accepted via Pull Requests on [Github][4].
## Pull Requests
- **Document any change in behaviour** - Make sure the `readme.md` and any other relevant documentation are kept up-to-date.
- **Consider our release cycle** - We try to follow [SemVer v2.0.0][5]. Randomly breaking public APIs is not an option.
- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
## Security
If you discover any security related issues, please email dave@dcblog.dev email instead of using the issue tracker.
## License
license. Please see the [license file][6] for more information.
[3]: changelog.md
[4]: https://github.com/dcblogdev/laravel-xero
[5]: http://semver.org/
[6]: license.md
PK ºqW]Â:4N¼ ¼ * laravel-xero/tests/DTOs/InvoiceDTOTest.phpnu „[µü¤ toBeInstanceOf(InvoiceDTO::class)
->and($invoiceDTO->invoiceID)->toBeNull()
->and($invoiceDTO->type)->toBe(InvoiceType::AccRec->value)
->and($invoiceDTO->invoiceNumber)->toBeNull()
->and($invoiceDTO->reference)->toBeNull()
->and($invoiceDTO->date)->toBeNull()
->and($invoiceDTO->dueDate)->toBeNull()
->and($invoiceDTO->status)->toBe(InvoiceStatus::Draft->value)
->and($invoiceDTO->lineAmountTypes)->toBe(InvoiceLineAmountType::Exclusive->value)
->and($invoiceDTO->currencyCode)->toBeNull()
->and($invoiceDTO->currencyRate)->toBeNull()
->and($invoiceDTO->subTotal)->toBeNull()
->and($invoiceDTO->totalTax)->toBeNull()
->and($invoiceDTO->total)->toBeNull()
->and($invoiceDTO->contactID)->toBeNull()
->and($invoiceDTO->contact)->toBeNull()
->and($invoiceDTO->lineItems)->toBe([])
->and($invoiceDTO->payments)->toBe([])
->and($invoiceDTO->creditNotes)->toBe([])
->and($invoiceDTO->prepayments)->toBe([])
->and($invoiceDTO->overpayments)->toBe([])
->and($invoiceDTO->hasAttachments)->toBeFalse()
->and($invoiceDTO->isDiscounted)->toBeFalse()
->and($invoiceDTO->hasErrors)->toBeFalse();
});
test('invoice dto can be instantiated with custom values', function () {
$invoiceDTO = new InvoiceDTO(
invoiceID: 'INV-123',
type: InvoiceType::AccPay->value,
invoiceNumber: '123',
reference: 'REF-123',
date: '2023-01-01',
dueDate: '2023-01-31',
status: InvoiceStatus::Submitted->value,
lineAmountTypes: InvoiceLineAmountType::Inclusive->value,
currencyCode: 'USD',
currencyRate: '1.0',
subTotal: '100.00',
totalTax: '10.00',
total: '110.00',
contactID: 'CONTACT-123',
contact: [['ContactID' => 'CONTACT-123', 'Name' => 'Test Contact']],
lineItems: [['Description' => 'Test Item']],
payments: [['PaymentID' => 'PAYMENT-123']],
creditNotes: [['CreditNoteID' => 'CREDIT-123']],
prepayments: [['PrepaymentID' => 'PREPAY-123']],
overpayments: [['OverpaymentID' => 'OVERPAY-123']],
hasAttachments: true,
isDiscounted: true,
hasErrors: true
);
expect($invoiceDTO)->toBeInstanceOf(InvoiceDTO::class)
->and($invoiceDTO->invoiceID)->toBe('INV-123')
->and($invoiceDTO->type)->toBe(InvoiceType::AccPay->value)
->and($invoiceDTO->invoiceNumber)->toBe('123')
->and($invoiceDTO->reference)->toBe('REF-123')
->and($invoiceDTO->date)->toBe('2023-01-01')
->and($invoiceDTO->dueDate)->toBe('2023-01-31')
->and($invoiceDTO->status)->toBe(InvoiceStatus::Submitted->value)
->and($invoiceDTO->lineAmountTypes)->toBe(InvoiceLineAmountType::Inclusive->value)
->and($invoiceDTO->currencyCode)->toBe('USD')
->and($invoiceDTO->currencyRate)->toBe('1.0')
->and($invoiceDTO->subTotal)->toBe('100.00')
->and($invoiceDTO->totalTax)->toBe('10.00')
->and($invoiceDTO->total)->toBe('110.00')
->and($invoiceDTO->contactID)->toBe('CONTACT-123')
->and($invoiceDTO->contact)->toBe([['ContactID' => 'CONTACT-123', 'Name' => 'Test Contact']])
->and($invoiceDTO->lineItems)->toBe([['Description' => 'Test Item']])
->and($invoiceDTO->payments)->toBe([['PaymentID' => 'PAYMENT-123']])
->and($invoiceDTO->creditNotes)->toBe([['CreditNoteID' => 'CREDIT-123']])
->and($invoiceDTO->prepayments)->toBe([['PrepaymentID' => 'PREPAY-123']])
->and($invoiceDTO->overpayments)->toBe([['OverpaymentID' => 'OVERPAY-123']])
->and($invoiceDTO->hasAttachments)->toBeTrue()
->and($invoiceDTO->isDiscounted)->toBeTrue()
->and($invoiceDTO->hasErrors)->toBeTrue();
});
test('createLineItem static method returns correct array with all values', function () {
$lineItem = InvoiceDTO::createLineItem(
description: 'Test Item',
quantity: 2,
unitAmount: 10.50,
accountCode: 123,
itemCode: 'ITEM-123',
taxType: 'OUTPUT',
taxAmount: '2.10',
lineAmount: '21.00',
discountRate: '10',
tracking: [['Name' => 'Region', 'Option' => 'North']]
);
expect($lineItem)->toBe([
'Description' => 'Test Item',
'Quantity' => 2,
'UnitAmount' => 10.50,
'AccountCode' => 123,
'ItemCode' => 'ITEM-123',
'TaxType' => 'OUTPUT',
'TaxAmount' => '2.10',
'LineAmount' => '21.00',
'DiscountRate' => '10',
'Tracking' => [['Name' => 'Region', 'Option' => 'North']],
]);
});
test('createLineItem static method filters out null values', function () {
$lineItem = InvoiceDTO::createLineItem(
description: 'Test Item',
quantity: 2,
unitAmount: 10.50,
accountCode: null,
itemCode: null
);
expect($lineItem)->toBe([
'Description' => 'Test Item',
'Quantity' => 2,
'UnitAmount' => 10.50,
]);
});
test('createLineItem accepts string values for numeric fields', function () {
$lineItem = InvoiceDTO::createLineItem(
description: 'Test Item',
quantity: '2',
unitAmount: '10.50'
);
expect($lineItem)->toBe([
'Description' => 'Test Item',
'Quantity' => '2',
'UnitAmount' => '10.50',
]);
});
test('toArray method returns correct array structure', function () {
$invoiceDTO = new InvoiceDTO(
type: InvoiceType::AccRec->value,
invoiceNumber: '123',
date: '2023-01-01',
dueDate: '2023-01-31',
status: InvoiceStatus::Draft->value,
contactID: 'CONTACT-123',
lineItems: [
InvoiceDTO::createLineItem(
description: 'Test Item',
quantity: 2,
unitAmount: 10.50
),
]
);
$array = $invoiceDTO->toArray();
expect($array)->toBeArray()
->and($array)->toHaveKey('Type', InvoiceType::AccRec->value)
->and($array)->toHaveKey('InvoiceNumber', '123')
->and($array)->toHaveKey('Date', '2023-01-01')
->and($array)->toHaveKey('DueDate', '2023-01-31')
->and($array)->toHaveKey('Status', InvoiceStatus::Draft->value)
->and($array)->toHaveKey('LineAmountTypes', InvoiceLineAmountType::Exclusive->value)
->and($array)->toHaveKey('Contact')
->and($array['Contact'])->toBe(['ContactID' => 'CONTACT-123'])
->and($array)->toHaveKey('LineItems')
->and($array['LineItems'][0])->toHaveKey('Description', 'Test Item')
->and($array['LineItems'][0])->toHaveKey('Quantity', 2)
->and($array['LineItems'][0])->toHaveKey('UnitAmount', 10.50);
});
test('toArray method uses contact array when contactID is null', function () {
$invoiceDTO = new InvoiceDTO(
contact: [
'ContactID' => 'CONTACT-123',
'Name' => 'Test Contact',
]
);
$array = $invoiceDTO->toArray();
expect($array)->toHaveKey('Contact')
->and($array['Contact'])->toBe([
'ContactID' => 'CONTACT-123',
'Name' => 'Test Contact',
]);
});
test('toArray method filters out null and empty array values', function () {
$invoiceDTO = new InvoiceDTO(
invoiceNumber: '123',
reference: null,
lineItems: []
);
$array = $invoiceDTO->toArray();
expect($array)->toHaveKey('InvoiceNumber')
->and($array)->toHaveKey('Type')
->and($array)->toHaveKey('Status')
->and($array)->toHaveKey('LineAmountTypes')
->and($array)->not->toHaveKey('Reference')
->and($array)->not->toHaveKey('LineItems');
});
PK ºqW]µè¸c * laravel-xero/tests/DTOs/ContactDTOTest.phpnu „[µü¤ toBeInstanceOf(ContactDTO::class)
->and($contactDTO->name)->toBeNull()
->and($contactDTO->firstName)->toBeNull()
->and($contactDTO->lastName)->toBeNull()
->and($contactDTO->emailAddress)->toBeNull()
->and($contactDTO->accountNumber)->toBeNull()
->and($contactDTO->bankAccountDetails)->toBeNull()
->and($contactDTO->taxNumber)->toBeNull()
->and($contactDTO->accountsReceivableTaxType)->toBeNull()
->and($contactDTO->accountsPayableTaxType)->toBeNull()
->and($contactDTO->contactStatus)->toBe(ContactStatus::Active->value)
->and($contactDTO->isSupplier)->toBeFalse()
->and($contactDTO->isCustomer)->toBeFalse()
->and($contactDTO->defaultCurrency)->toBeNull()
->and($contactDTO->website)->toBeNull()
->and($contactDTO->purchasesDefaultAccountCode)->toBeNull()
->and($contactDTO->salesDefaultAccountCode)->toBeNull()
->and($contactDTO->addresses)->toBe([])
->and($contactDTO->phones)->toBe([])
->and($contactDTO->contactPersons)->toBe([])
->and($contactDTO->hasAttachments)->toBeFalse()
->and($contactDTO->hasValidationErrors)->toBeFalse();
});
test('contact dto can be instantiated with custom values', function () {
$contactDTO = new ContactDTO(
name: 'Test Company',
firstName: 'John',
lastName: 'Doe',
emailAddress: 'john.doe@example.com',
accountNumber: 'ACC123',
bankAccountDetails: '123456789',
taxNumber: 'TAX123',
accountsReceivableTaxType: 'OUTPUT',
accountsPayableTaxType: 'INPUT',
contactStatus: ContactStatus::Archived->value,
isSupplier: true,
isCustomer: true,
defaultCurrency: 'USD',
website: 'https://example.com',
purchasesDefaultAccountCode: 'P123',
salesDefaultAccountCode: 'S123',
addresses: [['AddressType' => 'POBOX']],
phones: [['PhoneType' => 'MOBILE']],
contactPersons: [['FirstName' => 'Jane']],
hasAttachments: true,
hasValidationErrors: true
);
expect($contactDTO)->toBeInstanceOf(ContactDTO::class)
->and($contactDTO->name)->toBe('Test Company')
->and($contactDTO->firstName)->toBe('John')
->and($contactDTO->lastName)->toBe('Doe')
->and($contactDTO->emailAddress)->toBe('john.doe@example.com')
->and($contactDTO->accountNumber)->toBe('ACC123')
->and($contactDTO->bankAccountDetails)->toBe('123456789')
->and($contactDTO->taxNumber)->toBe('TAX123')
->and($contactDTO->accountsReceivableTaxType)->toBe('OUTPUT')
->and($contactDTO->accountsPayableTaxType)->toBe('INPUT')
->and($contactDTO->contactStatus)->toBe(ContactStatus::Archived->value)
->and($contactDTO->isSupplier)->toBeTrue()
->and($contactDTO->isCustomer)->toBeTrue()
->and($contactDTO->defaultCurrency)->toBe('USD')
->and($contactDTO->website)->toBe('https://example.com')
->and($contactDTO->purchasesDefaultAccountCode)->toBe('P123')
->and($contactDTO->salesDefaultAccountCode)->toBe('S123')
->and($contactDTO->addresses)->toBe([['AddressType' => 'POBOX']])
->and($contactDTO->phones)->toBe([['PhoneType' => 'MOBILE']])
->and($contactDTO->contactPersons)->toBe([['FirstName' => 'Jane']])
->and($contactDTO->hasAttachments)->toBeTrue()
->and($contactDTO->hasValidationErrors)->toBeTrue();
});
test('createAddress static method returns correct array', function () {
$address = ContactDTO::createAddress(
addressType: 'POBOX',
addressLine1: '123 Main St',
addressLine2: 'Suite 100',
addressLine3: 'Building A',
addressLine4: 'Floor 2',
city: 'Anytown',
region: 'State',
postalCode: '12345',
country: 'USA',
attentionTo: 'John Doe'
);
expect($address)->toBe([
'AddressType' => 'POBOX',
'AddressLine1' => '123 Main St',
'AddressLine2' => 'Suite 100',
'AddressLine3' => 'Building A',
'AddressLine4' => 'Floor 2',
'City' => 'Anytown',
'Region' => 'State',
'PostalCode' => '12345',
'Country' => 'USA',
'AttentionTo' => 'John Doe',
]);
});
test('createPhone static method returns correct array', function () {
$phone = ContactDTO::createPhone(
phoneType: 'MOBILE',
phoneNumber: '555-1234',
phoneAreaCode: '123',
phoneCountryCode: '1'
);
expect($phone)->toBe([
'PhoneType' => 'MOBILE',
'PhoneNumber' => '555-1234',
'PhoneAreaCode' => '123',
'PhoneCountryCode' => '1',
]);
});
test('createContactPerson static method returns correct array', function () {
$contactPerson = ContactDTO::createContactPerson(
firstName: 'Jane',
lastName: 'Smith',
emailAddress: 'jane.smith@example.com',
includeInEmails: true
);
expect($contactPerson)->toBe([
'FirstName' => 'Jane',
'LastName' => 'Smith',
'EmailAddress' => 'jane.smith@example.com',
'IncludeInEmails' => true,
]);
});
test('toArray method returns correct array structure', function () {
$contactDTO = new ContactDTO(
name: 'Test Company',
firstName: 'John',
lastName: 'Doe',
emailAddress: 'john.doe@example.com',
isSupplier: true,
addresses: [
ContactDTO::createAddress('POBOX', '123 Main St'),
],
phones: [
ContactDTO::createPhone('MOBILE', '555-1234'),
],
contactPersons: [
ContactDTO::createContactPerson('Jane', 'Smith'),
]
);
$array = $contactDTO->toArray();
expect($array)->toBeArray()
->and($array)->toHaveKey('Name', 'Test Company')
->and($array)->toHaveKey('FirstName', 'John')
->and($array)->toHaveKey('LastName', 'Doe')
->and($array)->toHaveKey('EmailAddress', 'john.doe@example.com')
->and($array)->toHaveKey('ContactStatus', ContactStatus::Active->value)
->and($array)->toHaveKey('IsSupplier', true)
->and($array)->toHaveKey('IsCustomer', false)
->and($array)->toHaveKey('Addresses')
->and($array['Addresses'][0])->toHaveKey('AddressType', 'POBOX')
->and($array['Addresses'][0])->toHaveKey('AddressLine1', '123 Main St')
->and($array)->toHaveKey('Phones')
->and($array['Phones'][0])->toHaveKey('PhoneType', 'MOBILE')
->and($array['Phones'][0])->toHaveKey('PhoneNumber', '555-1234')
->and($array)->toHaveKey('ContactPersons')
->and($array['ContactPersons'][0])->toHaveKey('FirstName', 'Jane')
->and($array['ContactPersons'][0])->toHaveKey('LastName', 'Smith');
});
test('toArray method filters out null values', function () {
$contactDTO = new ContactDTO(
name: 'Test Company',
emailAddress: null,
website: null
);
$array = $contactDTO->toArray();
expect($array)->toHaveKey('Name')
->and($array)->not->toHaveKey('EmailAddress')
->and($array)->not->toHaveKey('Website');
});
PK ºqW]ü°U¡ ¡ , laravel-xero/tests/Enums/InvoiceTypeTest.phpnu „[µü¤ �rsâ â . laravel-xero/tests/Enums/ContactStatusTest.phpnu „[µü¤ create();
expect($token)->toBeInstanceOf(XeroToken::class)
->and($token->tenant_id)->not->toBeEmpty()
->and($token->tenant_name)->not->toBeEmpty()
->and($token->access_token)->not->toBeEmpty()
->and($token->refresh_token)->not->toBeEmpty()
->and($token->expires_in)->toBeInt();
});
test('xero token can be created with mass assignment', function () {
$data = [
'tenant_id' => 'test_tenant_id',
'tenant_name' => 'Test Tenant',
'access_token' => 'test_access_token',
'refresh_token' => 'test_refresh_token',
'expires_in' => 3600,
'scopes' => 'test_scope',
];
$token = XeroToken::create($data);
expect($token)->toBeInstanceOf(XeroToken::class)
->and($token->tenant_id)->toBe('test_tenant_id')
->and($token->tenant_name)->toBe('Test Tenant')
->and($token->access_token)->toBe('test_access_token')
->and($token->refresh_token)->toBe('test_refresh_token')
->and($token->expires_in)->toBe(3600)
->and($token->scopes)->toBe('test_scope');
});
test('expires attribute returns correct expiration time', function () {
// Freeze time for predictable testing
Carbon::setTestNow('2023-01-01 12:00:00');
$token = XeroToken::create([
'tenant_id' => 'test_tenant_id',
'access_token' => 'test_access_token',
'refresh_token' => 'test_refresh_token',
'expires_in' => 3600, // 1 hour
'scopes' => 'test_scope',
]);
// The expires attribute should be 1 hour after the updated_at timestamp
expect($token->expires->format('Y-m-d H:i:s'))->toBe('2023-01-01 13:00:00');
// Clean up
Carbon::setTestNow();
});
test('expires_in is cast to integer', function () {
$token = XeroToken::create([
'tenant_id' => 'test_tenant_id',
'access_token' => 'test_access_token',
'refresh_token' => 'test_refresh_token',
'expires_in' => '3600', // String value
'scopes' => 'test_scope',
]);
expect($token->expires_in)->toBeInt()
->and($token->expires_in)->toBe(3600);
});
PK ºqW]Í<*í í laravel-xero/tests/TestCase.phpnu „[µü¤ set('database.default', 'mysql');
$app['config']->set('database.connections.mysql', [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
]);
require_once 'src/database/migrations/create_xero_tokens_table.php';
// run the up() method of that migration class
(new CreateXeroTokensTable)->up();
}
}
PK ºqW]l.ëâ â 3 laravel-xero/tests/Actions/StoreTokenActionTest.phpnu „[µü¤ 'test_id_token',
'access_token' => 'test_access_token',
'expires_in' => 3600,
'token_type' => 'Bearer',
'refresh_token' => 'test_refresh_token',
'scope' => 'test_scope',
];
$result = app(StoreTokenAction::class)($token);
expect($result)->toBeInstanceOf(XeroToken::class);
assertDatabaseCount(XeroToken::class, 1);
assertDatabaseHas('xero_tokens', [
'id' => 1,
'access_token' => config('xero.encrypt') ? null : 'test_access_token',
'refresh_token' => config('xero.encrypt') ? null : 'test_refresh_token',
'scopes' => 'test_scope',
]);
});
test('store token action creates a new token with tenant data', function () {
$token = [
'id_token' => 'test_id_token',
'access_token' => 'test_access_token',
'expires_in' => 3600,
'token_type' => 'Bearer',
'refresh_token' => 'test_refresh_token',
'scope' => 'test_scope',
];
$tenantData = [
'tenant_id' => 'test_tenant_id',
'tenant_name' => 'Test Tenant',
];
$result = app(StoreTokenAction::class)($token, $tenantData);
expect($result)->toBeInstanceOf(XeroToken::class);
assertDatabaseCount(XeroToken::class, 1);
assertDatabaseHas('xero_tokens', [
'tenant_id' => 'test_tenant_id',
'tenant_name' => 'Test Tenant',
'access_token' => config('xero.encrypt') ? null : 'test_access_token',
'refresh_token' => config('xero.encrypt') ? null : 'test_refresh_token',
'scopes' => 'test_scope',
]);
});
test('store token action updates an existing token with tenant id and tenant data', function () {
// First, create a token
$token = XeroToken::factory()->create([
'tenant_id' => 'test_tenant_id',
'tenant_name' => 'Old Tenant Name',
]);
// Then update it with new data
$newToken = [
'id_token' => 'new_id_token',
'access_token' => 'new_access_token',
'expires_in' => 7200,
'token_type' => 'Bearer',
'refresh_token' => 'new_refresh_token',
'scope' => 'new_scope',
];
$tenantData = [
'tenant_name' => 'New Tenant Name',
];
$result = app(StoreTokenAction::class)($newToken, $tenantData, 'test_tenant_id');
expect($result)->toBeInstanceOf(XeroToken::class);
assertDatabaseCount(XeroToken::class, 1);
assertDatabaseHas('xero_tokens', [
'tenant_id' => 'test_tenant_id',
'tenant_name' => 'New Tenant Name',
'access_token' => config('xero.encrypt') ? null : 'new_access_token',
'refresh_token' => config('xero.encrypt') ? null : 'new_refresh_token',
'scopes' => 'new_scope',
]);
});
PK ºqW]Ë]¼‹ ‹ ; laravel-xero/tests/Actions/formatQueryStringsActionTest.phpnu „[µü¤ 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
$response = app(formatQueryStringsAction::class)($params);
expect($response)->toBe('key1=value1&key2=value2&key3=value3');
});
test('throws type error when non-array is passed', function ($value) {
$this->expectException(TypeError::class);
app(formatQueryStringsAction::class)($value);
})->with([
'string',
123,
1.23,
true,
false,
null,
new stdClass(),
]);
PK ºqW]–:b‰ ; laravel-xero/tests/Actions/HandleTokenExpiredActionTest.phpnu „[µü¤ create();
$result = ['error' => 'invalid_grant'];
app(tokenExpiredAction::class)($result, $token);
assertDatabaseCount(XeroToken::class, 0);
})->throws(Exception::class, 'Xero token has expired, please re-authenticate.');
test('token refresh does not throw an exception and token is not deleted', function () {
$token = XeroToken::factory()->create();
$result = [];
$response = app(tokenExpiredAction::class)($result, $token);
expect($response)->toBeNull();
assertDatabaseCount(XeroToken::class, 1);
});
test('token refresh redirects when expired and not running in console', function () {
// Create a test-specific version of the tokenExpiredAction class
// that overrides isRunningInConsole to return false
$testAction = new class extends tokenExpiredAction
{
protected function isRunningInConsole(): bool
{
return false;
}
};
// Set a test redirect URI
Config::set('xero.redirectUri', 'https://example.com/auth');
$token = XeroToken::factory()->create();
$result = ['error' => 'invalid_grant'];
$response = $testAction($result, $token);
// Assert token was deleted
assertDatabaseCount(XeroToken::class, 0);
// Assert response is a redirect to the configured URI
expect($response)->toBeInstanceOf(RedirectResponse::class);
expect($response->getTargetUrl())->toBe('https://example.com/auth');
});
PK ºqW]èìè8i i laravel-xero/tests/Pest.phpnu „[µü¤ in(__DIR__);
PK ºqW]žü&ï� � D laravel-xero/tests/Console/Commands/XeroShowAllTokensCommandTest.phpnu „[µü¤ app->singleton('command.xero.show-all-tokens', fn () => new XeroShowAllTokensCommand());
Artisan::registerCommand($this->app->make('command.xero.show-all-tokens'));
});
test('command displays message when no tokens exist', function () {
// Ensure no tokens exist
XeroToken::query()->delete();
// Run the command
$this->artisan('xero:show-all-tokens')
->expectsOutput('All XERO Tokens in storage')
->assertExitCode(0);
});
test('command displays tokens in table format', function () {
// Create a token
$token = XeroToken::factory()->create([
'tenant_name' => 'Test Tenant',
'tenant_id' => 'test-tenant-id',
]);
// Run the command
$this->artisan('xero:show-all-tokens')
->expectsOutput('All XERO Tokens in storage')
->assertExitCode(0);
// Verify the token exists in the database
$this->assertDatabaseHas('xero_tokens', [
'tenant_name' => 'Test Tenant',
'tenant_id' => 'test-tenant-id',
]);
});
test('command handles encrypted tokens correctly', function () {
// Enable encryption
Config::set('xero.encrypt', true);
// Create a token with encrypted values
$token = XeroToken::factory()->create([
'tenant_name' => 'Encrypted Tenant',
'tenant_id' => 'encrypted-tenant-id',
'access_token' => Crypt::encryptString('encrypted-access-token'),
'refresh_token' => Crypt::encryptString('encrypted-refresh-token'),
]);
// Run the command
$this->artisan('xero:show-all-tokens')
->expectsOutput('All XERO Tokens in storage')
->assertExitCode(0);
// Verify the token exists in the database with encrypted values
$this->assertDatabaseHas('xero_tokens', [
'tenant_name' => 'Encrypted Tenant',
'tenant_id' => 'encrypted-tenant-id',
]);
// Reset config
Config::set('xero.encrypt', false);
});
test('command handles decryption exceptions', function () {
// Enable encryption
Config::set('xero.encrypt', true);
// Create a token with non-encrypted values that will cause decryption to fail
$token = XeroToken::factory()->create([
'tenant_name' => 'Exception Tenant',
'tenant_id' => 'exception-tenant-id',
'access_token' => 'non-encrypted-access-token',
'refresh_token' => 'non-encrypted-refresh-token',
]);
// Run the command
$this->artisan('xero:show-all-tokens')
->expectsOutput('All XERO Tokens in storage')
->assertExitCode(0);
// Verify the token exists in the database
$this->assertDatabaseHas('xero_tokens', [
'tenant_name' => 'Exception Tenant',
'tenant_id' => 'exception-tenant-id',
'access_token' => 'non-encrypted-access-token',
'refresh_token' => 'non-encrypted-refresh-token',
]);
// Reset config
Config::set('xero.encrypt', false);
});
PK ºqW]¯7¼ó‡ ‡ @ laravel-xero/tests/Console/Commands/XeroKeepAliveCommandTest.phpnu „[µü¤ app->singleton('command.xero.keep-alive', fn () => new XeroKeepAliveCommand());
Artisan::registerCommand($this->app->make('command.xero.keep-alive'));
});
test('command handles no tokens gracefully', function () {
// Ensure no tokens exist
XeroToken::query()->delete();
// Run the command
$this->artisan('xero:keep-alive')
->assertExitCode(0);
});
test('command refreshes token for connected tenant', function () {
// Create a token
$token = XeroToken::factory()->create([
'tenant_name' => 'Connected Tenant',
'tenant_id' => 'connected-tenant-id',
]);
// Mock the Xero facade
Xero::shouldReceive('setTenantId')
->once()
->with('connected-tenant-id');
Xero::shouldReceive('isConnected')
->once()
->andReturn(true);
Xero::shouldReceive('getAccessToken')
->once()
->with(false)
->andReturn('refreshed-token');
// Run the command
$this->artisan('xero:keep-alive')
->expectsOutput('Refreshing Token for Tenant: Connected Tenant - Successful')
->assertExitCode(0);
});
test('command handles not connected tenant', function () {
// Create a token
$token = XeroToken::factory()->create([
'tenant_name' => 'Not Connected Tenant',
'tenant_id' => 'not-connected-tenant-id',
]);
// Mock the Xero facade
Xero::shouldReceive('setTenantId')
->once()
->with('not-connected-tenant-id');
Xero::shouldReceive('isConnected')
->once()
->andReturn(false);
// Run the command
$this->artisan('xero:keep-alive')
->expectsOutput('Refreshing Token for Tenant: Not Connected Tenant - Not Connected')
->assertExitCode(0);
});
test('command handles multiple tenants', function () {
// Create multiple tokens
$token1 = XeroToken::factory()->create([
'tenant_name' => 'Tenant 1',
'tenant_id' => 'tenant-id-1',
]);
$token2 = XeroToken::factory()->create([
'tenant_name' => 'Tenant 2',
'tenant_id' => 'tenant-id-2',
]);
// Mock the Xero facade for first tenant (connected)
Xero::shouldReceive('setTenantId')
->once()
->with('tenant-id-1');
Xero::shouldReceive('isConnected')
->once()
->andReturn(true);
Xero::shouldReceive('getAccessToken')
->once()
->with(false)
->andReturn('refreshed-token-1');
// Mock the Xero facade for second tenant (not connected)
Xero::shouldReceive('setTenantId')
->once()
->with('tenant-id-2');
Xero::shouldReceive('isConnected')
->once()
->andReturn(false);
// Run the command
$this->artisan('xero:keep-alive')
->expectsOutput('Refreshing Token for Tenant: Tenant 1 - Successful')
->expectsOutput('Refreshing Token for Tenant: Tenant 2 - Not Connected')
->assertExitCode(0);
});
PK ºqW]}o�‘O O laravel-xero/tests/XeroTest.phpnu „[µü¤ XeroMock = Mockery::mock(Xero::class);
});
test('can initialise', function () {
$this->assertInstanceOf(Xero::class, $this->XeroMock);
});
test('redirected when connect is called', function () {
$connect = XeroFacade::connect();
$this->assertInstanceOf(RedirectResponse::class, $connect);
});
test('is connected returns false when no data in db', function () {
$connect = XeroFacade::isConnected();
expect($connect)->toBeFalse();
});
test('is connected returns true when data exists in db', function () {
XeroToken::create([
'id' => 0,
'access_token' => '1234',
'expires_in' => strtotime('+1 day'),
'scopes' => 'contacts',
]);
$connect = XeroFacade::isConnected();
expect($connect)->toBeTrue();
});
test('disconnect returns true when data exists in db', function () {
Http::fake();
XeroToken::create([
'id' => 0,
'access_token' => '1234',
'expires_in' => strtotime('+1 day'),
'scopes' => 'contacts',
]);
XeroFacade::disconnect();
$this->assertDatabaseCount('xero_tokens', 0);
});
test('getTokenData returns XeroToken data', function () {
XeroToken::create([
'id' => 0,
'access_token' => '1234',
'expires_in' => strtotime('+1 day'),
'scopes' => 'contacts',
]);
$data = XeroFacade::getTokenData();
expect($data)->toBeObject();
});
test('getTokenData when no tokens exist returns null', function () {
expect(XeroFacade::getTokenData())->toBeNull;
});
test('getTenantId returns id', function () {
XeroToken::create([
'id' => 0,
'access_token' => '1234',
'expires_in' => strtotime('+1 day'),
'scopes' => 'contacts',
'tenant_id' => '1234',
]);
expect(XeroFacade::getTenantId())->toBe('1234');
});
test('getTenantName returns name', function () {
XeroToken::create([
'id' => 0,
'access_token' => '1234',
'expires_in' => strtotime('+1 day'),
'scopes' => 'contacts',
'tenant_id' => '1234',
'tenant_name' => 'Jones',
]);
expect(XeroFacade::getTenantName())->toBe('Jones');
});
test('can return getAccessToken when it has not expired ', function () {
Http::fake();
XeroToken::create([
'id' => 0,
'access_token' => '1234',
'expires_in' => now()->addMinutes(25),
'updated_at' => strtotime('+1 day'),
'scopes' => 'contacts',
]);
$data = XeroFacade::getAccessToken();
expect($data)->toBe('1234');
});
test('can get tokens when not-encrypted but encryption is enabled', function () {
Config::set('xero.encrypt', true);
XeroToken::create([
'id' => 0,
'access_token' => '1234',
'expires_in' => strtotime('+1 day'),
'scopes' => 'contacts',
]);
$data = XeroFacade::getAccessToken();
expect($data)->toBe('1234');
});
test('can get tokens when encrypted', function () {
Config::set('xero.encrypt', true);
XeroToken::create([
'id' => 0,
'access_token' => Crypt::encryptString('1234'),
'expires_in' => strtotime('+1 day'),
'scopes' => 'contacts',
]);
$data = XeroFacade::getAccessToken();
expect($data)->toBe('1234');
});
test('formats Microsoft JSON date with timezone offset', function () {
$input = '/Date(1663257600000+0100)/';
$formatted = Xero::formatDate($input);
expect($formatted)->toBe('2022-09-15 16:00:00');
});
test('formats standard ISO 8601 date string', function () {
$input = '2023-05-19T14:00:00+00:00';
$formatted = Xero::formatDate($input);
expect($formatted)->toBe('2023-05-19 14:00:00');
});
test('returns empty string for invalid date input', function () {
$input = 'invalid-date-format';
$formatted = Xero::formatDate($input);
expect($formatted)->toBe('');
});
test('formats Microsoft JSON date with UTC offset', function () {
$input = '/Date(1663257600000+0000)/';
$formatted = Xero::formatDate($input);
expect($formatted)->toBe('2022-09-15 16:00:00');
});
PK ºqW]Ý,ƒù� � , laravel-xero/tests/XeroAuthenticatedTest.phpnu „[µü¤ once()->andReturn(false);
Xero::shouldReceive('connect')->once()->andReturn(new RedirectResponse('xero-connect-url'));
// Create middleware instance
$middleware = new XeroAuthenticated();
// Create a request
$request = Request::create('/test', 'GET');
// Execute middleware
$response = $middleware->handle($request, function () {
return 'next middleware';
});
// Assert that we get a redirect response
expect($response)->toBeInstanceOf(RedirectResponse::class)
->and($response->getTargetUrl())->toBe('xero-connect-url');
});
test('middleware continues when connected', function () {
// Mock the Xero facade
Xero::shouldReceive('isConnected')->once()->andReturn(true);
// Create middleware instance
$middleware = new XeroAuthenticated();
// Create a request
$request = Request::create('/test', 'GET');
// Execute middleware
$response = $middleware->handle($request, function () {
return 'next middleware';
});
// Assert that we continue to the next middleware
expect($response)->toBe('next middleware');
});
PK ºqW]PäSîÒ Ò 0 laravel-xero/tests/Resources/CreditNotesTest.phpnu „[µü¤ filter('bogus', 1)
->get();
})->throws(InvalidArgumentException::class, "Filter option 'bogus' is not valid.");
test('filter returns object', function () {
$filter = (new CreditNotes)->filter('ids', '1234');
expect($filter)->toBeObject();
});
PK ºqW]'3Ê Ê - laravel-xero/tests/Resources/WebhooksTest.phpnu „[µü¤ webhooks = new Webhooks();
});
// Helper function to mock file_get_contents
function mockFileGetContents($payload)
{
// Create a stream wrapper to mock php://input
stream_wrapper_unregister('php');
stream_wrapper_register('php', MockPhpStream::class);
file_put_contents('php://input', $payload);
}
// Mock stream wrapper class
class MockPhpStream
{
protected static $data = '';
public function stream_open($path, $mode, $options, &$opened_path)
{
return true;
}
public function stream_read($count)
{
$ret = mb_substr(self::$data, 0, $count);
self::$data = mb_substr(self::$data, $count);
return $ret;
}
public function stream_write($data)
{
self::$data = $data;
return mb_strlen($data);
}
public function stream_tell()
{
return 0;
}
public function stream_eof()
{
return true;
}
public function stream_stat()
{
return [];
}
}
afterEach(function () {
// Restore the original stream wrapper
if (in_array('php', stream_get_wrappers())) {
stream_wrapper_unregister('php');
stream_wrapper_restore('php');
}
});
test('getSignature returns correct signature', function () {
// Mock the payload
$payload = json_encode(['events' => [['eventType' => 'test']]]);
// Use reflection to set protected property
$reflection = new ReflectionClass($this->webhooks);
$property = $reflection->getProperty('payload');
$property->setAccessible(true);
$property->setValue($this->webhooks, $payload);
// Set webhook key in config
Config::set('xero.webhookKey', 'test-webhook-key');
// Calculate expected signature
$expectedSignature = base64_encode(hash_hmac('sha256', $payload, 'test-webhook-key', true));
// Test the method
expect($this->webhooks->getSignature())->toBe($expectedSignature);
});
test('validate returns true when signatures match', function () {
// Mock the payload and server variables
$payload = json_encode(['events' => [['eventType' => 'test']]]);
// Mock file_get_contents to return our payload
mockFileGetContents($payload);
// Set webhook key in config
Config::set('xero.webhookKey', 'test-webhook-key');
// Calculate signature
$signature = base64_encode(hash_hmac('sha256', $payload, 'test-webhook-key', true));
// Mock $_SERVER
$_SERVER['HTTP_X_XERO_SIGNATURE'] = $signature;
// Test the method
expect($this->webhooks->validate())->toBeTrue();
});
test('validate returns false when signatures do not match', function () {
// Mock the payload and server variables
$payload = json_encode(['events' => [['eventType' => 'test']]]);
// Mock file_get_contents to return our payload
mockFileGetContents($payload);
// Set webhook key in config
Config::set('xero.webhookKey', 'test-webhook-key');
// Set incorrect signature
$_SERVER['HTTP_X_XERO_SIGNATURE'] = 'incorrect-signature';
// Test the method
expect($this->webhooks->validate())->toBeFalse();
});
test('getEvents returns events from payload', function () {
// Create a mock of Webhooks with validate method always returning true
$webhooks = Mockery::mock(Webhooks::class)->makePartial();
$webhooks->shouldReceive('validate')->andReturn(true);
// Mock payload with events
$events = [
(object) ['eventType' => 'test1', 'resourceId' => '1'],
(object) ['eventType' => 'test2', 'resourceId' => '2'],
];
$payload = json_encode(['events' => $events]);
// Use reflection to set protected property
$reflection = new ReflectionClass($webhooks);
$property = $reflection->getProperty('payload');
$property->setAccessible(true);
$property->setValue($webhooks, $payload);
// Test the method
$result = $webhooks->getEvents();
// Verify each event has the expected properties
expect($result[0]->eventType)->toBe('test1');
expect($result[0]->resourceId)->toBe('1');
expect($result[1]->eventType)->toBe('test2');
expect($result[1]->resourceId)->toBe('2');
});
PK ºqW]+gä2É É - laravel-xero/tests/Resources/ContactsTest.phpnu „[µü¤ filter('bogus', 1)
->get();
})->throws(InvalidArgumentException::class, "Filter option 'bogus' is not valid.");
test('filter returns object', function () {
$filter = (new Contacts)->filter('ids', '1234');
expect($filter)->toBeObject();
});
PK ºqW]|‰ÖóÉ É - laravel-xero/tests/Resources/InvoicesTest.phpnu „[µü¤ filter('bogus', 1)
->get();
})->throws(InvalidArgumentException::class, "Filter option 'bogus' is not valid.");
test('filter returns object', function () {
$filter = (new Invoices)->filter('ids', '1234');
expect($filter)->toBeObject();
});
PK ºqW]�rÎgT T laravel-xero/pint.jsonnu „[µü¤ {
"preset": "laravel",
"rules": {
"array_push": true,
"backtick_to_shell_exec": true,
"date_time_immutable": true,
"declare_strict_types": true,
"lowercase_keywords": true,
"lowercase_static_reference": true,
"final_class": false,
"final_internal_class": false,
"final_public_method_for_abstract_class": false,
"fully_qualified_strict_types": true,
"global_namespace_import": {
"import_classes": true,
"import_constants": true,
"import_functions": true
},
"mb_str_functions": true,
"modernize_types_casting": true,
"new_with_parentheses": false,
"no_superfluous_elseif": true,
"no_useless_else": true,
"no_multiple_statements_per_line": true,
"ordered_class_elements": {
"order": [
"use_trait",
"case",
"constant",
"constant_public",
"constant_protected",
"constant_private",
"property_public",
"property_protected",
"property_private",
"construct",
"destruct",
"magic",
"phpunit",
"method_abstract",
"method_public_static",
"method_public",
"method_protected_static",
"method_protected",
"method_private_static",
"method_private"
],
"sort_algorithm": "none"
},
"ordered_interfaces": true,
"ordered_traits": true,
"protected_to_private": true,
"self_accessor": true,
"self_static_accessor": true,
"strict_comparison": true,
"visibility_required": true
}
}
PK ºqW] öþÊ Ê laravel-xero/phpunit.xmlnu „[µü¤
src/
./tests
./src
PK ºqW]HŠC‰* * laravel-xero/phpstan.neonnu „[µü¤ includes:
- vendor/larastan/larastan/extension.neon
parameters:
paths:
- src/
# Level 9 is the highest level
level: 5
# ignoreErrors:
# - '#PHPDoc tag @var#'
#
# excludePaths:
# - ./*/*/FileToBeExcluded.php
#
# checkMissingIterableValueType: falsePK ºqW]¾ ¦5§ § ! laravel-xero/src/Facades/Xero.phpnu „[µü¤ value, // ACCREC for sales invoices, ACCPAY for bills
public ?string $invoiceNumber = null,
public ?string $reference = null,
public ?string $date = null,
public ?string $dueDate = null,
public ?string $status = InvoiceStatus::Draft->value,
public ?string $lineAmountTypes = InvoiceLineAmountType::Exclusive->value,
public ?string $currencyCode = null,
public ?string $currencyRate = null,
public ?string $subTotal = null,
public ?string $totalTax = null,
public ?string $total = null,
public ?string $contactID = null,
/** @var array> */
public ?array $contact = null,
/** @var array> */
public ?array $lineItems = [],
/** @var array> */
public ?array $payments = [],
/** @var array> */
public ?array $creditNotes = [],
/** @var array> */
public ?array $prepayments = [],
/** @var array> */
public ?array $overpayments = [],
public ?bool $hasAttachments = false,
public ?bool $isDiscounted = false,
public ?bool $hasErrors = false,
) {}
/**
* Create a line item array for the invoice
*
* @param array>|null $tracking
* @return array
*/
public static function createLineItem(
?string $description = null,
string|int|null $quantity = null,
string|float|null $unitAmount = null,
?int $accountCode = null,
?string $itemCode = null,
?string $taxType = null,
?string $taxAmount = null,
?string $lineAmount = null,
?string $discountRate = null,
?array $tracking = null,
): array {
return array_filter([
'Description' => $description,
'Quantity' => $quantity,
'UnitAmount' => $unitAmount,
'AccountCode' => $accountCode,
'ItemCode' => $itemCode,
'TaxType' => $taxType,
'TaxAmount' => $taxAmount,
'LineAmount' => $lineAmount,
'DiscountRate' => $discountRate,
'Tracking' => $tracking,
], function (mixed $value) {
return $value !== null;
});
}
/**
* Convert the DTO to an array for the Xero API
*
* @return array
*/
public function toArray(): array
{
return array_filter([
'InvoiceID' => $this->invoiceID,
'Type' => $this->type,
'InvoiceNumber' => $this->invoiceNumber,
'Reference' => $this->reference,
'Date' => $this->date,
'DueDate' => $this->dueDate,
'Status' => $this->status,
'LineAmountTypes' => $this->lineAmountTypes,
'CurrencyCode' => $this->currencyCode,
'CurrencyRate' => $this->currencyRate,
'SubTotal' => $this->subTotal,
'TotalTax' => $this->totalTax,
'Total' => $this->total,
'Contact' => $this->contactID ? ['ContactID' => $this->contactID] : $this->contact,
'LineItems' => $this->lineItems,
'HasAttachments' => $this->hasAttachments,
'IsDiscounted' => $this->isDiscounted,
], fn (mixed $value) => $value !== null && $value !== []);
}
}
PK ºqW]¡…Ÿ8” ” $ laravel-xero/src/DTOs/ContactDTO.phpnu „[µü¤ value,
public ?bool $isSupplier = false,
public ?bool $isCustomer = false,
public ?string $defaultCurrency = null,
public ?string $website = null,
public ?string $purchasesDefaultAccountCode = null,
public ?string $salesDefaultAccountCode = null,
/** @var array> */
public ?array $addresses = [],
/** @var array> */
public ?array $phones = [],
/** @var array> */
public ?array $contactPersons = [],
public ?bool $hasAttachments = false,
public ?bool $hasValidationErrors = false,
) {}
/**
* Create an address array for the contact
*
* @return array
*/
public static function createAddress(
string $addressType,
?string $addressLine1 = null,
?string $addressLine2 = null,
?string $addressLine3 = null,
?string $addressLine4 = null,
?string $city = null,
?string $region = null,
?string $postalCode = null,
?string $country = null,
?string $attentionTo = null
): array {
return [
'AddressType' => $addressType,
'AddressLine1' => $addressLine1,
'AddressLine2' => $addressLine2,
'AddressLine3' => $addressLine3,
'AddressLine4' => $addressLine4,
'City' => $city,
'Region' => $region,
'PostalCode' => $postalCode,
'Country' => $country,
'AttentionTo' => $attentionTo,
];
}
/**
* Create a phone array for the contact
*
* @return array
*/
public static function createPhone(
string $phoneType,
?string $phoneNumber = null,
?string $phoneAreaCode = null,
?string $phoneCountryCode = null
): array {
return [
'PhoneType' => $phoneType,
'PhoneNumber' => $phoneNumber,
'PhoneAreaCode' => $phoneAreaCode,
'PhoneCountryCode' => $phoneCountryCode,
];
}
/**
* Create a contact person array for the contact
*
* @return array
*/
public static function createContactPerson(
?string $firstName = null,
?string $lastName = null,
?string $emailAddress = null,
?bool $includeInEmails = false
): array {
return [
'FirstName' => $firstName,
'LastName' => $lastName,
'EmailAddress' => $emailAddress,
'IncludeInEmails' => $includeInEmails,
];
}
/**
* Convert the DTO to an array for the Xero API
*
* @return array
*/
public function toArray(): array
{
return array_filter([
'Name' => $this->name,
'FirstName' => $this->firstName,
'LastName' => $this->lastName,
'EmailAddress' => $this->emailAddress,
'AccountNumber' => $this->accountNumber,
'BankAccountDetails' => $this->bankAccountDetails,
'TaxNumber' => $this->taxNumber,
'AccountsReceivableTaxType' => $this->accountsReceivableTaxType,
'AccountsPayableTaxType' => $this->accountsPayableTaxType,
'ContactStatus' => $this->contactStatus,
'IsSupplier' => $this->isSupplier,
'IsCustomer' => $this->isCustomer,
'DefaultCurrency' => $this->defaultCurrency,
'Website' => $this->website,
'PurchasesDefaultAccountCode' => $this->purchasesDefaultAccountCode,
'SalesDefaultAccountCode' => $this->salesDefaultAccountCode,
'Addresses' => $this->addresses,
'Phones' => $this->phones,
'ContactPersons' => $this->contactPersons,
'HasAttachments' => $this->hasAttachments,
'HasValidationErrors' => $this->hasValidationErrors,
], function (mixed $value) {
return $value !== null;
});
}
}
PK ºqW]¤GÆÛ Û ( laravel-xero/src/Enums/InvoiceStatus.phpnu „[µü¤ $case->value, self::cases());
return in_array($value, $validValues);
}
}
PK ºqW]¸;� � 0 laravel-xero/src/Enums/InvoiceLineAmountType.phpnu „[µü¤ $case->value, self::cases());
return in_array($value, $validValues);
}
}
PK ºqW]ZjpS_ _ &