diff --git a/Admin/Install/db.json b/Admin/Install/db.json index 54e44ca..83336e7 100755 --- a/Admin/Install/db.json +++ b/Admin/Install/db.json @@ -355,6 +355,18 @@ "null": false, "default": null }, + "billing_tax_tax1_account": { + "name": "billing_tax_tax1_account", + "type": "VARCHAR(10)", + "null": false, + "default": null + }, + "billing_tax_tax2_account": { + "name": "billing_tax_tax2_account", + "type": "VARCHAR(10)", + "null": false, + "default": null + }, "billing_tax_refund_account": { "name": "billing_tax_refund_account", "type": "VARCHAR(10)", @@ -367,6 +379,24 @@ "null": false, "default": null }, + "billing_tax_cashback_account": { + "name": "billing_tax_cashback_account", + "type": "VARCHAR(10)", + "null": false, + "default": null + }, + "billing_tax_overpayment_account": { + "name": "billing_tax_overpayment_account", + "type": "VARCHAR(10)", + "null": false, + "default": null + }, + "billing_tax_underpayment_account": { + "name": "billing_tax_underpayment_account", + "type": "VARCHAR(10)", + "null": false, + "default": null + }, "billing_tax_min_price": { "name": "billing_tax_min_price", "type": "BIGINT", diff --git a/Admin/Routes/Web/Backend.php b/Admin/Routes/Web/Backend.php index 98d7e08..127427f 100755 --- a/Admin/Routes/Web/Backend.php +++ b/Admin/Routes/Web/Backend.php @@ -40,6 +40,17 @@ return [ ], ], ], + '^.*/sales/bill/archive(\?.*$|$)' => [ + [ + 'dest' => '\Modules\Billing\Controller\BackendController:viewBillingSalesArchive', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => BackendController::NAME, + 'type' => PermissionType::READ, + 'state' => PermissionCategory::SALES_INVOICE, + ], + ], + ], '^.*/sales/bill(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewBillingSalesInvoice', @@ -74,6 +85,17 @@ return [ ], ], ], + '^.*/purchase/bill/archive(\?.*$|$)' => [ + [ + 'dest' => '\Modules\Billing\Controller\BackendController:viewBillingPurchaseArchive', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => BackendController::NAME, + 'type' => PermissionType::READ, + 'state' => PermissionCategory::PURCHASE_INVOICE, + ], + ], + ], '^.*/purchase/bill(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewBillingPurchaseInvoice', @@ -119,6 +141,17 @@ return [ ], ], ], + '^.*/warehouse/bill/archive(\?.*$|$)' => [ + [ + 'dest' => '\Modules\Billing\Controller\BackendController:viewBillingStockArchive', + 'verb' => RouteVerb::GET, + 'permission' => [ + 'module' => BackendController::NAME, + 'type' => PermissionType::READ, + 'state' => PermissionCategory::PURCHASE_INVOICE, + ], + ], + ], '^.*/warehouse/bill(\?.*$|$)' => [ [ 'dest' => '\Modules\Billing\Controller\BackendController:viewBillingStockInvoice', diff --git a/Controller/ApiBillController.php b/Controller/ApiBillController.php index c28c0c8..8b1d978 100755 --- a/Controller/ApiBillController.php +++ b/Controller/ApiBillController.php @@ -170,6 +170,27 @@ final class ApiBillController extends Controller public function apiBillFinalize(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void { + if (!$this->app->accountManager->get($request->header->account)->hasPermission( + PermissionType::READ, + $this->app->unitId, + null, + self::NAME, + PermissionCategory::SALES_INVOICE + ) + && !$this->app->accountManager->get($request->header->account)->hasPermission( + PermissionType::READ, + $this->app->unitId, + null, + self::NAME, + PermissionCategory::PURCHASE_INVOICE + ) + ) { + $this->fillJsonResponse($request, $response, NotificationLevel::HIDDEN, '', '', []); + $response->header->status = RequestStatusCode::R_403; + + return; + } + // Archive bill /** @var \Modules\Billing\Models\Bill $bill */ $old = BillMapper::get() @@ -182,22 +203,22 @@ final class ApiBillController extends Controller $this->updateModel($request->header->account, $old, $new, BillMapper::class, 'bill', $request->getOrigin()); - $this->app->eventManager->triggerSimilar('PRE:Module:' . self::NAME . '-bill-finalize', '', [ - $request->header->account, - null, $bill, - null, self::NAME . '-bill-finalize', - self::NAME, - (string) $bill->id, - null, - $request->getOrigin(), - ]); - // Create final pdf $this->apiBillPdfArchiveCreate($request, $response, $data); $media = $response->getDataArray($request->uri->__toString())['response']; + $this->app->eventManager->triggerSimilar('PRE:Module:' . self::NAME . '-bill-finalize', '', [ + $request->header->account, + null, $new, + null, self::NAME . '-bill-finalize', + self::NAME, + (string) $new->id, + null, + $request->getOrigin(), + ]); + // Send bill via email - $this->apiBillEmail($request, $response, ['bill' => $old, 'media' => $media]); + $this->apiBillEmail($request, $response, ['bill' => $new, 'media' => $media]); $this->createStandardUpdateResponse($request, $response, $new); } @@ -1334,6 +1355,14 @@ final class ApiBillController extends Controller $response->set($request->uri->__toString(), new FormValidation(['status' => $status])); $response->header->status = RequestStatusCode::R_400; + \phpOMS\Log\FileLogger::getInstance()->error( + \phpOMS\Log\FileLogger::MSG_FULL, [ + 'message' => 'Couldn\'t create bill path: ' . $bill->id, + 'line' => __LINE__, + 'file' => self::class, + ] + ); + return; // @codeCoverageIgnoreEnd } @@ -1352,12 +1381,22 @@ final class ApiBillController extends Controller \file_put_contents($pdfDir . '/' . $billFileName, $pdf); if (!\is_file($pdfDir . '/' . $billFileName)) { $response->header->status = RequestStatusCode::R_400; + $response->set($request->uri->__toString(), []); + + \phpOMS\Log\FileLogger::getInstance()->error( + \phpOMS\Log\FileLogger::MSG_FULL, [ + 'message' => 'Couldn\'t render bill pdf: ' . $bill->id, + 'line' => __LINE__, + 'file' => self::class, + ] + ); return; } $media = null; if ($oldFile->id === 0) { + // Creating new bill archive pdf $media = $this->app->moduleManager->get('Media', 'Api')->createDbEntry( status: [ 'status' => UploadStatus::OK, @@ -1397,12 +1436,12 @@ final class ApiBillController extends Controller $request->getOrigin() ); } else { + // Updating existing bill archive pdf $media = clone $oldFile; if (\realpath($pdfDir . '/' . $billFileName) !== \realpath($oldFile->getAbsolutePath())) { \unlink($oldFile->getAbsolutePath()); } - $media->setPath(\Modules\Media\Controller\ApiController::normalizeDbPath($pdfDir . '/' . $billFileName)); $media->setVirtualPath($path); $media->size = \filesize($media->getAbsolutePath()); diff --git a/Controller/BackendController.php b/Controller/BackendController.php index 0107a04..8511c8d 100755 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -70,6 +70,54 @@ final class BackendController extends Controller ->with('type') ->with('type/l11n') ->with('client') + ->where('status', BillStatus::DRAFT) + ->where('type/transferType', BillTransferType::SALES) + ->where('type/l11n/language', $response->header->l11n->language) + ->sort('id', OrderType::DESC) + ->where('unit', $this->app->unitId) + ->limit(25); + + if ($request->getData('ptype') === 'p') { + $view->data['bills'] = $mapperQuery + ->where('id', $request->getDataInt('id') ?? 0, '<') + ->where('client', null, '!=') + ->execute(); + } elseif ($request->getData('ptype') === 'n') { + $view->data['bills'] = $mapperQuery->where('id', $request->getDataInt('id') ?? 0, '>') + ->where('client', null, '!=') + ->execute(); + } else { + $view->data['bills'] = $mapperQuery->where('id', 0, '>') + ->where('client', null, '!=') + ->execute(); + } + + return $view; + } + + /** + * Routing end-point for application behavior. + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param array $data Generic data + * + * @return RenderableInterface + * + * @since 1.0.0 + * @codeCoverageIgnore + */ + public function viewBillingSalesArchive(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface + { + $view = new View($this->app->l11nManager, $request, $response); + $view->setTemplate('/Modules/Billing/Theme/Backend/sales-bill-list'); + $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1005104001, $request, $response); + + $mapperQuery = SalesBillMapper::getAll() + ->with('type') + ->with('type/l11n') + ->with('client') + ->where('status', BillStatus::DRAFT, '!=') ->where('type/transferType', BillTransferType::SALES) ->where('type/l11n/language', $response->header->l11n->language) ->sort('id', OrderType::DESC) diff --git a/Models/Tax/TaxCombination.php b/Models/Tax/TaxCombination.php index 7debae1..56bd8cf 100755 --- a/Models/Tax/TaxCombination.php +++ b/Models/Tax/TaxCombination.php @@ -49,10 +49,24 @@ class TaxCombination implements \JsonSerializable public string $account = ''; + // Tax accounts can be defined in: + // 1. Account (gross postings are automatically split) + // 2. Tax code + // 3. Tax combination + public string $taxAccount1 = ''; + + public string $taxAccount2 = ''; + public string $refundAccount = ''; public string $discountAccount = ''; + public string $cashbackAccount = ''; + + public string $overpaymentAccount = ''; + + public string $underpaymentAccount = ''; + public ?int $minPrice = null; public ?int $maxPrice = null; diff --git a/Models/Tax/TaxCombinationMapper.php b/Models/Tax/TaxCombinationMapper.php index 4d498a7..5239c37 100755 --- a/Models/Tax/TaxCombinationMapper.php +++ b/Models/Tax/TaxCombinationMapper.php @@ -47,8 +47,13 @@ final class TaxCombinationMapper extends DataMapperFactory 'billing_tax_code' => ['name' => 'billing_tax_code', 'type' => 'string', 'internal' => 'taxCode'], 'billing_tax_type' => ['name' => 'billing_tax_type', 'type' => 'int', 'internal' => 'taxType'], 'billing_tax_account' => ['name' => 'billing_tax_account', 'type' => 'string', 'internal' => 'account'], + 'billing_tax_tax1_account' => ['name' => 'billing_tax_tax1_account', 'type' => 'string', 'internal' => 'taxAccount1'], + 'billing_tax_tax2_account' => ['name' => 'billing_tax_tax2_account', 'type' => 'string', 'internal' => 'taxAccount2'], 'billing_tax_refund_account' => ['name' => 'billing_tax_refund_account', 'type' => 'string', 'internal' => 'refundAccount'], 'billing_tax_discount_account' => ['name' => 'billing_tax_discount_account', 'type' => 'string', 'internal' => 'discountAccount'], + 'billing_tax_cashback_account' => ['name' => 'billing_tax_cashback_account', 'type' => 'string', 'internal' => 'cashbackAccount'], + 'billing_tax_overpayment_account' => ['name' => 'billing_tax_overpayment_account', 'type' => 'string', 'internal' => 'overpaymentAccount'], + 'billing_tax_underpayment_account' => ['name' => 'billing_tax_underpayment_account', 'type' => 'string', 'internal' => 'underpaymentAccount'], 'billing_tax_min_price' => ['name' => 'billing_tax_min_price', 'type' => 'int', 'internal' => 'minPrice'], 'billing_tax_max_price' => ['name' => 'billing_tax_max_price', 'type' => 'int', 'internal' => 'maxPrice'], 'billing_tax_start' => ['name' => 'billing_tax_start', 'type' => 'DateTime', 'internal' => 'start'], diff --git a/Theme/Backend/bill-create.tpl.php b/Theme/Backend/bill-create.tpl.php index 7869866..b1c7d66 100755 --- a/Theme/Backend/bill-create.tpl.php +++ b/Theme/Backend/bill-create.tpl.php @@ -333,11 +333,11 @@ echo $this->data['nav']->render(); ?>