mervit / idoklad-v3
PHP 类,用于简化对 iDoklad api v3 的请求。
Requires
- php: >=7.1.0
This package is auto-updated.
Last update: 2024-09-20 13:05:51 UTC
README
iDoklad v3
用于发送对 iDoklad api v3 请求的 PHP 类。
感谢 @malcanek 创建了 v2 版本,我已经将其升级到 v3 并将其提供给其他感兴趣的人。
与 v2 版本相比的代码变更
- 主要变化在于响应,将 TotalItems 和 TotalPages 移动到 data 参数下。因此,列表项不能通过 getData 方法读取,而需要使用新创建的 getItems 方法。因此,在从 v2 版本升级到 v3 版本时,需要修改所有返回列表结果的调用。
- 过滤器已重做,现在允许对条件和组合使用逻辑运算符(或和与)进行分组和嵌套。
- GET 请求现在支持 select 参数,用于限制结果中的数据。
- GET 请求现在还支持 include 参数,允许在响应中包含关联实体的数据。
将库添加到项目中
我们将通过包含 src/iDoklad.php 文件或将库添加到 composer 中来将库添加到项目中。之后,我们使用 use 关键字来引用库。
composer require mervit/idoklad-v3
我们输入我们的 client ID、client secret,如果需要使用 OAuth2 认证,还要输入 redirect URI。最后,我们调用 iDoklad 对象,它负责所有通信。
include_once 'src/iDoklad.php'; use mervit\iDoklad\iDoklad; use mervit\iDoklad\auth\iDokladCredentials; use mervit\iDoklad\iDokladException; $clientId = 'Your client ID'; $clientSecret = 'Your client secret'; $redirectUri = 'Your redirect URI for OAuth2'; $iDoklad = new iDoklad($clientId, $clientSecret, $redirectUri);
OAuth2 - 授权代码流
OAuth 认证分几个步骤。我们使用从开发门户获取的 client ID 和 client secret 作为 client ID 和 client secret。
首先,我们提供用户输入其登录凭据的 URL。我们可以使用以下方法获取该 URL
echo '<a href="'.$iDoklad->getAuthenticationUrl().'">Odkaz</a>';
用户输入登录凭据后,用户将被重定向到指定的 redirect URI,并附带代码。我们可以使用以下方法处理代码
$iDoklad->requestCredentials($_GET['code']);
现在,实例对象中已经有了凭证,我们可以向 iDoklad api 发送查询。我们可以通过两种方式获取凭证。从对象中直接获取凭证
$credentials = $iDoklad->getCredentials(); echo $credentials->toJson();
使用 credentials callback 处理凭证
Callback 的工作方式是库在凭证更改时调用 callback 函数。这很有用,因为当 token 过期时,它会自动刷新 token。
$iDoklad->setCredentialsCallback(function($credentials){ file_put_contents('credentials.json', $credentials->toJson()); });
将凭证上传到 iDoklad 对象
使用现有的凭证创建对象
$iDoklad = new iDoklad($clientId, $clientSecret, $redirectUri, $credentials);
将凭证添加到现有的对象中
$iDoklad->setCredentials($credentials);
一旦对象包含凭证,就可以向 iDoklad api 发送查询。
OAuth2 - 客户端凭证流
这种方法更简单。我们将根据从用户账户设置中获取的 client ID 和 client secret 获取凭证。对象创建后,我们只需调用
$iDoklad->authCCF();
与 OAuth2 - 授权代码流一样,这里也使用了 credentials callback。
发送对 iDoklad api 的请求
用于发送 api 请求的是 iDokladRequest 对象。它可以用最简单的方式创建,只需一个参数,它根据文档指定操作,然后直接发送到 api。
$request = new iDokladRequest('IssuedInvoices'); $response = $iDoklad->sendRequest($request);
从 api 获取数据
如果请求成功,则作为 iDokladResponse 对象返回数据。首先,我们检查请求是否成功(返回值应该是 200)
$response->getCode();
然后我们可以从数组中获取实际数据
$response->getData();
错误捕获
该类抛出 iDokladException 类型的异常。
创建新的发票
$request->addMethodType('POST'); $data = array( 'PurchaserId' => 3739927, 'IssuedInvoiceItems' => [array( 'Name' => 'Testovaci polozka', 'UnitPrice' => 100, 'Amount' => 1 )] ); $request->addPostParameters($data);
或者我们可以通过 fce 设置 method type,这将看起来像这样
$data = array( 'PurchaserId' => 3739927, 'IssuedInvoiceItems' => [array( 'Name' => 'Testovaci polozka', 'UnitPrice' => 100, 'Amount' => 1 )] ); $request->post()->addPostParameters($data);
使用过滤和排序
为了使用过滤器,我们将使用 iDokladFilter 类。参数可以在创建类时立即设置,第一个参数是要过滤的字段名称,第二个参数是操作符,最后一个参数是值。
$filter = new iDokladFilter('DocumentNumber', '==', '20170013'); $request->addFilter($filter);
我们可以通过 setFilterType 方法定义各个过滤器之间的关系。
$request->setFilterType('and');
我们可以添加多个过滤器,并使用 filterGroup 类将它们分组。
$filterGroup = new \mervit\iDoklad\request\iDokladFilterGroup('or'); $filter2 = new \mervit\iDoklad\request\iDokladFilter('Id', '!eq', '123'); $filterGroup->addFilter($filter2); $filter3 = new \mervit\iDoklad\request\iDokladFilter('Id', 'eq', '456'); $filterGroup->addFilter($filter3); $request->addFilter($filterGroup);
为了使用排序,我们将使用 iDokladSort 类。同样,我们可以立即添加参数,其中第一个参数是字段名称,第二个参数是可选的,可以指定是升序(asc)还是降序(desc)。
$sort = new iDokladSort('DocumentNumber', 'desc'); $request->addSort($sort);
分页和返回的项目数量
$request->setPage(2); $request->setPageSize(5);
限制结果
通过 addSelect 方法,我们可以限制 API 返回的数据,从而显著减少请求处理时间。
$request->addSelect('Id'); $request->addSelect('CurrencyId');
嵌套数据用点分隔
$request->addSelect('DeliveryAddress.Name');
我们还可以添加多个用逗号分隔的变量
$request->addSelect('Items.Id,Items.Name');
加载附加实体
通过 addInclude 方法,我们可以扩展返回的数据,包括附加实体的详细信息。有了这个功能,我们就不必单独调用附加实体的详细信息。
$request->addInclude('Currency');
嵌套数据用点分隔
$request->addInclude('Items.PriceListItem.Currency');
我们还可以添加多个用逗号分隔的变量
$request->addInclude('Currency,Items.PriceListItem.Currency');
返回代码高于或等于 400 时抛出异常
如果我们要在 HTTP 返回代码高于或等于 400 时抛出异常,我们只需要调用该函数即可。
$iDoklad->httpExceptionsOn()
上传附件
如果我们要上传附件,我们只需要在请求对象上使用 addFile 方法。
$request = new \mervit\iDoklad\request\iDokladRequest('Attachments/{documentId}/{documentType}'); $request->addFile(new CURLFile(path_to_your_file)); $response = $iDoklad->sendRequest($request);
其他修改
如果我们需要使用 POST、PUT、PATCH、DELETE 方法,我们将使用 iDokladRequest 对象上的 addMethodType 方法。
示例
示例使用可以在 acf.php 和 ccf.php 文件中看到。acf.php 包含了 authorization code flow 的使用示例,ccf 包含了 client credentials flow 的示例。只需要添加自己的 client ID、client secret 和 redirect URI。
创建发票的示例
// Generate company name $companyName = $order->getFirstname() . ' ' . $order->getLastname(); if($order->getCompanyName()){ $companyName = $order->getCompanyName(); } // Try to find existing company in address book $filter = new iDokladFilter('CompanyName', '==', $companyName); $contactRequest = new iDokladRequest('Contacts'); $contactRequest->addFilter($filter); $contactResponse = $this->sendRequest($contactRequest); if($contactResponse->getTotalItems() > 0){ $contactId = $contactResponse->getItems()[0]["Id"]; } // Create or update company in address book $contactRequest = new iDokladRequest('Contacts'); $contactRequestPostParameters = []; $contactRequestPostParameters['CountryId'] = 1; $contactRequestPostParameters['Email'] = $order->getEmail(); $contactRequestPostParameters['Mobile'] = $order->getPhone(); $contactRequestPostParameters['Firstname'] = $order->getFirstname(); $contactRequestPostParameters['Surname'] = $order->getLastname(); $contactRequestPostParameters['CompanyName'] = $companyName; if($order->getCin()) { $contactRequestPostParameters['IdentificationNumber'] = $order->getCin(); } if($order->getVat()){ $contactRequestPostParameters['VatIdentificationNumber'] = $order->getVat(); } if($order->getAddress()){ $contactRequestPostParameters['Street'] = $order->getAddress(); } if(isset($contactId)){ $contactRequestPostParameters['Id'] = $contactId; $contactRequest->addMethodType('PATCH'); } else { $contactRequest->addMethodType('POST'); } $contactRequest->addPostParameters($contactRequestPostParameters); $contactResponse = $this->sendRequest($contactRequest); $contact = $contactResponse->getData(); // Get default numeric sequence $numericSequenceRequest = new iDokladRequest('NumericSequences'); $numericSequenceRequest->addFilter(new iDokladFilter('IsDefault', '==', 'true')); $numericSequenceRequest->addFilter(new iDokladFilter('DocumentType', '==', '0')); // 0 = IssuedInvoices $numericSequenceResponse = $this->sendRequest($numericSequenceRequest); $numericSequences = $numericSequenceResponse->getItems(); $defaultNumericSequenceId = $numericSequences[0]['Id']; $lastDocumentSerialNumber = $numericSequences[0]['LastNumber']; // Get default payment option $paymentOptionRequest = new iDokladRequest('PaymentOptions'); $paymentOptionResponse = $this->sendRequest($paymentOptionRequest); $paymentOptions = $paymentOptionResponse->getItems(); $paymentOptionId = null; foreach($paymentOptions as $po){ if($po['IsDefault'] == 'true'){ $paymentOptionId = $po['Id']; break; } } // Create Issued Invoice $dateOfIssue = new \DateTime(); $dateOfMaturity = clone $dateOfIssue; $dateOfMaturity->modify('+14 days'); $invoicePostParameters = []; $invoicePostParameters['PartnerId'] = $contact['Id']; $invoicePostParameters['CurrencyId'] = 1; $invoicePostParameters['Description'] = 'Nákup v eshopu'; $invoicePostParameters['DateOfIssue'] = $dateOfIssue->format('Y-m-d'); $invoicePostParameters['DateOfMaturity'] = $dateOfMaturity->format('Y-m-d'); $invoicePostParameters['DateOfTaxing'] = $dateOfIssue->format('Y-m-d'); $invoicePostParameters['DocumentSerialNumber'] = (string)((int) $lastDocumentSerialNumber + 1); $invoicePostParameters['IsEet'] = false; $invoicePostParameters['IsIncomeTax'] = true; $invoicePostParameters['NumericSequenceId'] = $defaultNumericSequenceId; $invoicePostParameters['PaymentOptionId'] = $paymentOptionId; $invoicePostParameters['Items'] = []; foreach ($order->getItems() as $item) { $invoiceItem = []; $invoiceItem['Amount'] = $item->getQuantity(); $invoiceItem['PriceType'] = 0; // Cena s daní $invoiceItem['VatRateType'] = 1; // Základní sazba DPH $invoiceItem['UnitPrice'] = $item->getPrice(); $invoiceItem['Name'] = $item->getName(); $invoiceItem['DiscountPercentage'] = 0; $invoiceItem['IsTaxMovement'] = false; $invoicePostParameters['Items'][] = $invoiceItem; } $invoiceRequest = new iDokladRequest('IssuedInvoices'); $invoiceRequest->addMethodType('POST'); $invoiceRequest->addPostParameters($invoicePostParameters); return $this->sendRequest($invoiceRequest);