apiBillCreate * -> [createBill] * -> apiMediaAddToBill * -> apiInvoiceParse * -> cliParseSupplierBill * -> [updateBill] * -> apiBillPdfArchiveCreate * -> eventBillArchive * * @since 1.0.0 */ public function apiSupplierBillUpload(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void { if (!empty($val = $this->validateSupplierBillUpload($request))) { $response->header->status = RequestStatusCode::R_400; $this->createInvalidCreateResponse($request, $response, $val); return; } $bills = $this->createSupplierBillUploadFromRequest($request, $response, $data); if (empty($bills)) { $response->header->status = RequestStatusCode::R_400; $this->createInvalidCreateResponse($request, $response, $val); } $this->createStandardCreateResponse($request, $response, $bills); } /** * Method to upload supplier Bill from request. * * @param RequestAbstract $request Request * @param ResponseAbstract $response Response * @param array $data Generic data * * @return array * * @since 1.0.0 */ private function createSupplierBillUploadFromRequest(RequestAbstract $request, ResponseAbstract $response, $data) : array { /** @var \Model\Setting $setting */ $setting = $this->app->appSettings->get( names: SettingsEnum::EXTERNAL_MEDIA_TYPE, module: self::NAME ); $internalType = $request->getDataInt('type') ?? ((int) $setting->content); /** @var \Modules\Billing\Models\BillType $purchaseTransferType */ $purchaseTransferType = BillTypeMapper::get() ->where('transferType', BillTransferType::PURCHASE) ->limit(1) ->execute(); $bills = []; $files = \array_merge($request->files, $request->getDataJson('media')); /** @var \Model\Setting[] $settings */ $settings = $this->app->appSettings->get(null, [SettingsEnum::BILLING_DOCUMENT_SPACER_COLOR, SettingsEnum::BILLING_DOCUMENT_SPACER_TOLERANCE], unit: $this->app->unitId, module: self::NAME ); if (empty($settings)) { /** @var \Model\Setting[] $settings */ $settings = $this->app->appSettings->get(null, [SettingsEnum::BILLING_DOCUMENT_SPACER_COLOR, SettingsEnum::BILLING_DOCUMENT_SPACER_TOLERANCE], unit: null, module: self::NAME ); } foreach ($files as $file) { // 1. convert to image pdftoppm // 2. search for color pages by using averageColorRandom (tolerance < 175 = color match) // 3. split pdf document if necessary // sudo apt-get --yes install pdftk // pdftk foo-bar.pdf cat 1-12 output foo.pdf // pdftk foo-bar.pdf cat 13-end output bar.pdf // alternatively, pdfseparate -f 1 -l 5 input.pdf output-page%d.pdf // alternatively, pdfjam -o // alternatively, pdfly cat in.pdf 2:4 -o out.pdf // 4. add to documents array } $documents = $files; foreach ($documents as $file) { // Create default bill $billRequest = new HttpRequest(); $billRequest->header->account = $request->header->account; $billRequest->header->l11n = $request->header->l11n; $billRequest->setData('supplier', 0); $billRequest->setData('status', BillStatus::UNPARSED); $billRequest->setData('type', $purchaseTransferType->id); $billResponse = new HttpResponse(); $billResponse->header->l11n = $response->header->l11n; $this->app->moduleManager->get('Billing', 'ApiBill')->apiBillCreate($billRequest, $billResponse, $data); $billId = $billResponse->getDataArray('')['response']->id; $bills[] = $billId; // Upload and assign document to bill $mediaResponse = new HttpResponse(); $mediaRequest = new HttpRequest(); $mediaResponse->header->l11n = $response->header->l11n; $mediaRequest->header->account = $request->header->account; $mediaRequest->header->l11n = $request->header->l11n; if (\is_array($file)) { $mediaRequest->addFile($file); } else { $mediaRequest->setData('media', \json_encode($file)); } $mediaRequest->setData('bill', $billId); $mediaRequest->setData('type', $internalType); $mediaRequest->setData('parse_content', true, true); $this->app->moduleManager->get('Billing', 'ApiBill')->apiMediaAddToBill($mediaRequest, $mediaResponse, $data); if (\is_array($file)) { /** @var \Modules\Media\Models\Media[] $uploaded */ $uploaded = $mediaResponse->getDataArray('')['response']['upload']; if (empty($uploaded)) { return []; } $in = \reset($uploaded)->getAbsolutePath(); if (!\is_file($in)) { return []; } } $request->setData('id', $billId, true); $request->setData('bill', $billId, true); $this->apiInvoiceParse($request, $response, $data); } return $bills; } /** * Validate supplier Bill upload request * * @param RequestAbstract $request Request * * @return array * * @since 1.0.0 */ private function validateSupplierBillUpload(RequestAbstract $request) : array { $val = []; if (($val['files'] = empty($request->files))) { return $val; } return []; } /** * Api method to create bill files * * @param RequestAbstract $request Request * @param ResponseAbstract $response Response * @param array $data Generic data * * @return void * * @api * * @throws \Exception * * @since 1.0.0 */ public function apiInvoiceParse(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void { if (!empty($val = $this->validateInvoiceParse($request))) { $response->header->status = RequestStatusCode::R_400; $this->createInvalidCreateResponse($request, $response, $val); return; } $bill = BillMapper::get() ->where('id', (int) $request->getData('id')) ->execute(); // After a bill is "closed" its values shouldn't change if ($bill->status !== BillStatus::DRAFT && $bill->status !== BillStatus::UNPARSED && $bill->status !== BillStatus::ACTIVE ) { $response->header->status = RequestStatusCode::R_423; $this->createInvalidCreateResponse($request, $response, $val); return; } // Offload bill parsing to cli $cliPath = \realpath(__DIR__ . '/../../../cli.php'); if ($cliPath === false) { return; } try { SystemUtils::runProc( OperatingSystem::getSystem() === SystemType::WIN ? 'php.exe' : 'php', '-dxdebug.remote_enable=1 -dxdebug.start_with_request=yes -dxdebug.mode=coverage,develop,debug ' . \escapeshellarg($cliPath) . ' /billing/bill/purchase/parse ' . '-i ' . \escapeshellarg((string) $bill->id), $request->getDataBool('async') ?? true ); } catch (\Throwable $t) { $response->header->status = RequestStatusCode::R_400; $this->app->logger?->error($t->getMessage()); } $this->createStandardUpdateResponse($request, $response, $bill); } /** * Validate Bill parse request * * @param RequestAbstract $request Request * * @return array * * @since 1.0.0 */ private function validateInvoiceParse(RequestAbstract $request) : array { $val = []; if (($val['id'] = !$request->hasData('id'))) { return $val; } return []; } }