diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml index adb8716..75cb759 100644 --- a/.github/workflows/greetings.yml +++ b/.github/workflows/greetings.yml @@ -9,5 +9,5 @@ jobs: - uses: actions/first-interaction@v1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - issue-message: 'Thank you for createing this issue. We will check it as soon as possible.' + issue-message: 'Thank you for creating this issue. We will check it as soon as possible.' pr-message: 'Thank you for your pull request. We will check it as soon as possible.' diff --git a/Admin/Routes/Web/Backend.php b/Admin/Routes/Web/Backend.php index 7eb7bbe..db90001 100644 --- a/Admin/Routes/Web/Backend.php +++ b/Admin/Routes/Web/Backend.php @@ -19,9 +19,9 @@ return [ ], ], ], - '^/warehouse/stocktaking/overview(\?.*$|$)' => [ + '^/warehouse/stocktaking/view(\?.*$|$)' => [ [ - 'dest' => '\Modules\StockTaking\Controller\BackendController:viewStockTakingOverview', + 'dest' => '\Modules\StockTaking\Controller\BackendController:viewStockTakingView', 'verb' => RouteVerb::GET, 'active' => true, 'permission' => [ diff --git a/Controller/ApiController.php b/Controller/ApiController.php index 9c2e8ef..fd9f7e9 100644 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -14,6 +14,7 @@ declare(strict_types=1); namespace Modules\StockTaking\Controller; +use Modules\ItemManagement\Models\NullItem; use Modules\StockTaking\Models\StockTaking; use Modules\StockTaking\Models\StockTakingMapper; use Modules\WarehouseManagement\Models\NullStock; @@ -58,9 +59,9 @@ final class ApiController extends Controller return; } - $paymentTerm = $this->createStockTakingFromRequest($request); - $this->createModel($request->header->account, $paymentTerm, StockTakingMapper::class, 'stocktaking', $request->getOrigin()); - $this->createStandardCreateResponse($request, $response, $paymentTerm); + $stockTaking = $this->createStockTakingFromRequest($request); + $this->createModel($request->header->account, $stockTaking, StockTakingMapper::class, 'stocktaking', $request->getOrigin()); + $this->createStandardCreateResponse($request, $response, $stockTaking); } /** @@ -127,7 +128,9 @@ final class ApiController extends Controller if (empty($stockTypeList)) { foreach ($stock->locations as $location) { - $stockTypeList[] = $location->type->id; + if ($location->type->id !== 0) { + $stockTypeList[$location->type->id] = $location->type->id; + } } } @@ -149,11 +152,18 @@ final class ApiController extends Controller // @todo In the future create create a snapshot of distributions and reference those // This would also allow us to continue creating bills while doing the stocktaking? + + // @todo Limit items if items are defined $stocktaking->distributions = StockDistributionMapper::getAll() ->where('stock', $stockList) ->where('stockType', $stockTypeList) ->executeGetArray(); + // @bug This doesn't include items that never had a distribution created + foreach ($stocktaking->distributions as $distribution) { + $stocktaking->items[$distribution->item] = new NullItem($distribution->item); + } + return $stocktaking; } } diff --git a/Controller/BackendController.php b/Controller/BackendController.php index f06277a..7ddec6e 100644 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -14,6 +14,8 @@ declare(strict_types=1); namespace Modules\StockTaking\Controller; +use Modules\StockTaking\Models\StockTakingMapper; +use phpOMS\Asset\AssetType; use phpOMS\Contract\RenderableInterface; use phpOMS\Message\RequestAbstract; use phpOMS\Message\ResponseAbstract; @@ -47,6 +49,53 @@ final class BackendController extends Controller $view->setTemplate('/Modules/StockTaking/Theme/Backend/stocktaking-list'); $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1006701001, $request, $response); + $view->data['list'] = StockTakingMapper::getAll() + ->where('unit', $this->app->unitId) + ->execute(); + + return $view; + } + + /** + * Routing end-point for application behavior. + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param array $data Generic data + * + * @return RenderableInterface Returns a renderable object + * + * @since 1.0.0 + * @codeCoverageIgnore + */ + public function viewStockTakingView(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface + { + $head = $response->data['Content']->head; + $nonce = $this->app->appSettings->getOption('script-nonce'); + + $head->addAsset(AssetType::JSLATE, 'Resources/zxing/zxing.min.js?v=' . $this->app->version, ['nonce' => $nonce, 'type' => 'module']); + $head->addAsset(AssetType::JSLATE, 'Modules/StockTaking/Controller/Controller.js?v=' . self::VERSION, ['nonce' => $nonce, 'type' => 'module']); + + $view = new View($this->app->l11nManager, $request, $response); + $view->setTemplate('/Modules/StockTaking/Theme/Backend/stocktaking-view'); + $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1006701001, $request, $response); + + $view->data['inventory'] = StockTakingMapper::get() + ->with('stocks') + ->with('stocks/locations') + ->with('types') + ->with('items') + ->with('items/l11n') + ->with('items/l11n/type') + ->with('items/files') + ->with('items/files/tags') + ->with('distributions') + ->where('id', (int) $request->getData('id')) + ->where('items/l11n/language', $response->header->l11n->language) + ->where('item/sl11n/type/title', ['name1', 'name2'], 'IN') + ->where('items/files/tags/name', 'profile_image') + ->execute(); + return $view; } diff --git a/Controller/Controller.js b/Controller/Controller.js new file mode 100644 index 0000000..32f530a --- /dev/null +++ b/Controller/Controller.js @@ -0,0 +1,114 @@ +import { Autoloader } from '../../../jsOMS/Autoloader.js'; +import { BrowserMultiFormatReader } from '../../../Resources/zxing/zxing.min.js'; + +Autoloader.defineNamespace('omsApp.Modules'); + +/* global omsApp */ +omsApp.Modules.StockTaking = class { + /** + * @constructor + * + * @since 1.0.0 + */ + constructor (app) + { + this.scan = null; + this.canvas = null; + this.ctx = null; + this.video = null; + this.videoBtn = null; + this.input = null; + this.deviceId = null; + this.data = ''; + this.codeReader = null; + this.capabilities = null; + this.settings = null; + this.zoom = null; + this.track = null; + }; + + bind (id) + { + const e = typeof id === 'undefined' + ? [document.getElementById('iScanner')] + : [document.getElementById(id)]; + + const length = e.length; + + for (let i = 0; i < length; ++i) { + this.bindElement(e[i]); + } + }; + + bindElement (scan) + { + this.scan = scan; + this.video = scan.getElementsByTagName('video')[0]; + this.input = document.getElementById('iScanData'); + this.canvas = scan.getElementsByTagName('canvas')[0]; + this.canvas.style.background = 'transparent'; + this.videoBtn = scan.getElementsByTagName('button')[0]; + this.ctx = this.canvas.getContext('2d', { willReadFrequently: true }); + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + this.zoom = scan.querySelector('input[type=range]'); + this.data = ''; + + const self = this; + + this.videoBtn.addEventListener('click', () => { + if (!self.deviceId && 'mediaDevices' in navigator) { + navigator.mediaDevices.getUserMedia({audio: false, video: {facingMode: 'environment'}}) + .then(stream => { + self.codeReader = new BrowserMultiFormatReader(); + + self.detectVideo(true); + + stream.getTracks().forEach(t => { + if (t.enabled && (t.kind === 'video' || t.kind === 'videoinput')) { + self.deviceId = t.id; + self.track = t; + + self.capabilities = t.getCapabilities(); + self.settings = t.getSettings(); + } + }); + + if ("zoom" in self.settings) { + self.zoom.min = self.capabilities.zoom.min; + self.zoom.max = self.capabilities.zoom.max; + self.zoom.step = self.capabilities.zoom.step; + self.zoom.value = self.settings.zoom; + + self.zoom.addEventListener("input", async () => { + await self.track.applyConstraints({ advanced: [{ zoom: self.input.value }] }); + }); + } + }); + } else { + self.detectVideo(false); + } + }); + + this.video.addEventListener('click', () => { + self.input.value = self.data; + }); + }; + + detectVideo (repeat) { + if (!repeat) { + this.codeReader.reset(); + this.deviceId = null; + + return; + } + + const self = this; + this.codeReader.decodeFromVideoDevice(this.deviceId, this.video.id, (result, err) => { + if (result) { + self.data = result.text; + } + }); + }; +}; + +window.omsApp.moduleManager.get('StockTaking').bind(); diff --git a/Models/StockTaking.php b/Models/StockTaking.php index 8b81f57..15865b8 100644 --- a/Models/StockTaking.php +++ b/Models/StockTaking.php @@ -38,6 +38,8 @@ class StockTaking public int $unit = 0; + public int $status = StockTakingStatus::ACTIVE; + /** * Constructor. * diff --git a/Models/StockTakingMapper.php b/Models/StockTakingMapper.php index d98c9b4..93ba770 100644 --- a/Models/StockTakingMapper.php +++ b/Models/StockTakingMapper.php @@ -14,7 +14,10 @@ declare(strict_types=1); namespace Modules\StockTaking\Models; +use Modules\ItemManagement\Models\ItemMapper; use Modules\WarehouseManagement\Models\StockDistributionMapper; +use Modules\WarehouseManagement\Models\StockMapper; +use Modules\WarehouseManagement\Models\StockTypeMapper; use phpOMS\DataStorage\Database\Mapper\DataMapperFactory; /** @@ -53,7 +56,25 @@ final class StockTakingMapper extends DataMapperFactory 'mapper' => StockDistributionMapper::class, 'table' => 'stocktaking_distribution', 'self' => 'stocktaking_distribution_stocktaking', - 'external' => null, + 'external' => 'stocktaking_distribution_distribution', + ], + 'stocks' => [ + 'mapper' => StockMapper::class, + 'table' => 'stocktaking_stock', + 'self' => 'stocktaking_stock_stocktaking', + 'external' => 'stocktaking_stock_stock', + ], + 'types' => [ + 'mapper' => StockTypeMapper::class, + 'table' => 'stocktaking_type', + 'self' => 'stocktaking_type_stocktaking', + 'external' => 'stocktaking_type_type', + ], + 'items' => [ + 'mapper' => ItemMapper::class, + 'table' => 'stocktaking_item', + 'self' => 'stocktaking_item_stocktaking', + 'external' => 'stocktaking_item_item', ], ]; diff --git a/Models/StockTakingStatus.php b/Models/StockTakingStatus.php new file mode 100644 index 0000000..23b4b29 --- /dev/null +++ b/Models/StockTakingStatus.php @@ -0,0 +1,32 @@ + [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.cs.lang.php b/Theme/Backend/Lang/Navigation.cs.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.cs.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.da.lang.php b/Theme/Backend/Lang/Navigation.da.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.da.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.el.lang.php b/Theme/Backend/Lang/Navigation.el.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.el.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.es.lang.php b/Theme/Backend/Lang/Navigation.es.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.es.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.fi.lang.php b/Theme/Backend/Lang/Navigation.fi.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.fi.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.fr.lang.php b/Theme/Backend/Lang/Navigation.fr.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.fr.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.hu.lang.php b/Theme/Backend/Lang/Navigation.hu.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.hu.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.it.lang.php b/Theme/Backend/Lang/Navigation.it.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.it.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.ja.lang.php b/Theme/Backend/Lang/Navigation.ja.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.ja.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.ko.lang.php b/Theme/Backend/Lang/Navigation.ko.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.ko.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.no.lang.php b/Theme/Backend/Lang/Navigation.no.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.no.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.pl.lang.php b/Theme/Backend/Lang/Navigation.pl.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.pl.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.pt.lang.php b/Theme/Backend/Lang/Navigation.pt.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.pt.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.ru.lang.php b/Theme/Backend/Lang/Navigation.ru.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.ru.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.sv.lang.php b/Theme/Backend/Lang/Navigation.sv.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.sv.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.th.lang.php b/Theme/Backend/Lang/Navigation.th.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.th.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.tr.lang.php b/Theme/Backend/Lang/Navigation.tr.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.tr.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.uk.lang.php b/Theme/Backend/Lang/Navigation.uk.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.uk.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/Navigation.zh.lang.php b/Theme/Backend/Lang/Navigation.zh.lang.php deleted file mode 100644 index 458edd6..0000000 --- a/Theme/Backend/Lang/Navigation.zh.lang.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Budgeting' => '', - 'Dashboard' => '', -]]; diff --git a/Theme/Backend/Lang/de.lang.php b/Theme/Backend/Lang/de.lang.php index c73d277..ebbf717 100644 --- a/Theme/Backend/Lang/de.lang.php +++ b/Theme/Backend/Lang/de.lang.php @@ -16,4 +16,14 @@ return ['StockTaking' => [ 'Stocktaking' => 'Inventur', 'Date' => 'Datum', 'Status' => 'Status', + 'Entry' => 'Eingabe', + 'Scan' => 'Scanning', + 'Data' => 'Daten', + 'Counted' => 'Gezählt', + 'Target' => 'Soll', + 'Camera' => 'Kamera', + 'Settings' => 'Settings', + 'Analysis' => 'Auswertung', + ':status-1' => 'Aktiv', + ':status-2' => 'Geschlossen', ]]; diff --git a/Theme/Backend/Lang/en.lang.php b/Theme/Backend/Lang/en.lang.php index 0a19ccc..1d85643 100644 --- a/Theme/Backend/Lang/en.lang.php +++ b/Theme/Backend/Lang/en.lang.php @@ -16,4 +16,14 @@ return ['StockTaking' => [ 'Stocktaking' => 'Stock Taking', 'Date' => 'Date', 'Status' => 'Status', + 'Entry' => 'Entry', + 'Scan' => 'Scan', + 'Data' => 'Data', + 'Counted' => 'Counted', + 'Target' => 'Target', + 'Camera' => 'Camera', + 'Settings' => 'Settings', + 'Analysis' => 'Analysis', + ':status-1' => 'Active', + ':status-2' => 'Closed', ]]; diff --git a/Theme/Backend/stocktaking-list.tpl.php b/Theme/Backend/stocktaking-list.tpl.php index 42854d6..2ea8fad 100644 --- a/Theme/Backend/stocktaking-list.tpl.php +++ b/Theme/Backend/stocktaking-list.tpl.php @@ -16,7 +16,7 @@ declare(strict_types=1); use phpOMS\Uri\UriFactory; /** @var \phpOMS\Views\View $this */ -$assets = $this->data['assets'] ?? []; +$list = $this->data['list'] ?? []; echo $this->data['nav']->render(); ?>
| - | = $this->getHtml('Date'); ?> + | = $this->getHtml('ID', '0', '0'); ?> | = $this->getHtml('Status'); ?> + | = $this->getHtml('Date'); ?> | |
| - | = $value->id; ?> - | + | = $value->id; ?> + | = $this->getHtml(':status-' . $value->status); ?> + | = $value->createdAt->format('Y-m-d'); ?> |
| = $this->getHtml('Empty', '0', '0'); ?>
diff --git a/Theme/Backend/stocktaking-view.tpl.php b/Theme/Backend/stocktaking-view.tpl.php
new file mode 100644
index 0000000..1041f67
--- /dev/null
+++ b/Theme/Backend/stocktaking-view.tpl.php
@@ -0,0 +1,283 @@
+data['inventory'] ?? [];
+
+echo $this->data['nav']->render(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
+
+
+ request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>>
+
+
+
+
+ request->uri->fragment === 'c-tab-2' ? ' checked' : ''; ?>>
+
+
+
+
+ = $this->getHtml('Items', 'ItemManagement', 'Backend'); ?>download
+
+
+
+
+ request->uri->fragment === 'c-tab-3' ? ' checked' : ''; ?>>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ = $this->getHtml('Items', 'ItemManagement', 'Backend'); ?>download
+
+ | |||||