diff --git a/backend/components/AdminMenuStructure.php b/backend/components/AdminMenuStructure.php index 20e37cc..fc27946 100644 --- a/backend/components/AdminMenuStructure.php +++ b/backend/components/AdminMenuStructure.php @@ -89,6 +89,7 @@ class AdminMenuStructure{ ///////////////////////////// $items = []; $items[] = ['label' => 'Tranzakciók', 'url' => ['/transfer/index' , 'TransferSearch[start]' =>$today,'TransferSearch[end]' => $tomorrow ] ]; + $items[] = ['label' => 'Bevétel', 'url' => ['/transfer/summary' , 'TransferSummarySearch[start]' =>$today,'TransferSummarySearch[end]' => $tomorrow ] ]; $items[] = ['label' => 'Kassza müveletek', 'url' => ['/account-state/index'] ]; $items[] = ['label' => 'Zárások', 'url' => ['/collection/index' , 'CollectionSearch[start]' =>$todayDatetime,'CollectionSearch[end]' => $tomorrowDatetime ] ]; $this->menuItems[] = ['label' => 'Pénzügy', 'url' => $this->emptyUrl, diff --git a/backend/controllers/TransferController.php b/backend/controllers/TransferController.php index 17327b5..b7f7217 100644 --- a/backend/controllers/TransferController.php +++ b/backend/controllers/TransferController.php @@ -9,6 +9,7 @@ use yii\web\NotFoundHttpException; use yii\filters\VerbFilter; use common\models\Account; use common\models\User; +use backend\models\TransferSummarySearch; /** * TransferController implements the CRUD actions for Transfer model. @@ -23,7 +24,7 @@ class TransferController extends \backend\controllers\BackendController 'rules' => [ // allow authenticated users [ - 'actions' => [ 'index','view' ], + 'actions' => [ 'index','view','summary' ], 'allow' => true, 'roles' => ['admin','employee','reception'], ], @@ -57,6 +58,29 @@ class TransferController extends \backend\controllers\BackendController ]); } + + + /** + * Lists all Transfer models. + * @return mixed + */ + public function actionSummary() + { + $searchModel = new TransferSummarySearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + + $accounts = Account::read(); + + $users = User::read(); + + return $this->render('summary', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + 'accounts' => $accounts, + 'users' => $users, + ]); + } + /** * Displays a single Transfer model. * @param integer $id diff --git a/backend/models/TransferSearch.php b/backend/models/TransferSearch.php index c76bc00..e2bd2a3 100644 --- a/backend/models/TransferSearch.php +++ b/backend/models/TransferSearch.php @@ -114,8 +114,9 @@ class TransferSearch extends Transfer $accounts = Account::read(); $accountMap = ArrayHelper::map( $accounts ,'id_account','name' ); - $idUser = Yii::$app->user->id; + $idUser = $this->id_user; + $this->totals = Transfer::mkTotals($this->timestampStart, $this->timestampEnd, $idUser, $this->types, $this->id_account, $accounts, $accountMap); diff --git a/backend/models/TransferSummarySearch.php b/backend/models/TransferSummarySearch.php new file mode 100644 index 0000000..5570ea2 --- /dev/null +++ b/backend/models/TransferSummarySearch.php @@ -0,0 +1,180 @@ + 'timestampStart' ,'timestampAttributeFormat' => 'yyyy-MM-dd' ], + [[ 'end' , ], 'date' , 'timestampAttribute' => 'timestampEnd' ,'timestampAttributeFormat' => 'yyyy-MM-dd' ], + ['types', 'each', 'rule' => ['integer']], + ]; + } + + /** + * @inheritdoc + */ + public function scenarios() + { + // bypass scenarios() implementation in the parent class + return Model::scenarios(); + } + + /** + * Creates data provider instance with search query applied + * + * @param array $params + * + * @return ActiveDataProvider + */ + public function search($params) + { + + + + $this->load($params); + + if (!$this->validate()) { +// $query->where('0=1'); +// return $dataProvider; + } + + + $this->readTicketMoney(); + $this->readProductsByCategory(); + $this->readMoneyMovements(); + $this->calcTotal(); + } + + protected function calcTotal(){ + $this->total = 0; + $this->total += $this->ticketMoney; + $this->total -= $this->moneyMovementMoneis['money_movement_money']; + foreach ($this->productMoneies as $pm ){ + $this->total += $pm['product_money']; + } + } + + + protected function addQueryFilters($query){ + if ( !RoleDefinition::isAdmin() ){ + $query->innerJoin("user_account_assignment",'transfer.id_account = user_account_assignment.id_account' ); + $query->andWhere(['user_account_assignment.id_user' => Yii::$app->user->id ]); + } + + $query->andFilterWhere([ + 'transfer.id_account' => $this->id_account, + 'transfer.type' => $this->type, + 'transfer.id_user' => $this->id_user, + ]); + + $created_condition = ['and',[ '>=', 'transfer.created_at', $this->timestampStart ] ,[ '<', 'transfer.created_at', $this->timestampEnd ] ]; + $paid_condition = ['and',[ '>=', 'transfer.paid_at', $this->timestampStart ] ,[ '<', 'transfer.paid_at', $this->timestampEnd ] ]; + + + $query->andFilterWhere(['or' , $created_condition , $paid_condition]); + + $query->andWhere(['transfer.status' => Transfer::STATUS_PAID]); + + } + + + protected function readTicketMoney(){ + + $query = (new \yii\db\Query()); + $query->select([' coalesce(sum(abs(transfer.money)),0) AS ticket_money']); + $query->from('transfer'); + $query->andWhere(['transfer.type' => Transfer::TYPE_TICKET]); + $query->innerJoin("ticket", "ticket.id_ticket = transfer.id_object"); + $this->addQueryFilters($query); + +// $query = Transfer::find(); +// $query->andWhere(['type' => Transfer::TYPE_TICKET]); +// $this->addQueryFilters($query); + $this->ticketMoney = $query->scalar(); + print_r($this->ticketMoney); + } + + protected function readProductsByCategory(){ + + + $query = (new \yii\db\Query()); + $query->select(['coalesce(sum(transfer.money),0) AS product_money', 'product_category.name as category_name']); + $query->from('transfer'); + $query->groupBy(['product_category.id_product_category','product_category.name']); + $query->andWhere(['transfer.type' => Transfer::TYPE_PRODUCT]); + $query->innerJoin("sale", "sale.id_sale = transfer.id_object"); + $query->innerJoin("product", "sale.id_product = product.id_product"); + $query->innerJoin("product_category", "product.id_product_category = product_category.id_product_category"); + $this->addQueryFilters($query); + + $this->productMoneies = $query->all(); + + } + + + protected function readMoneyMovements(){ + $query = (new \yii\db\Query()); + $query->select([' coalesce(sum(abs(transfer.money)),0) AS money_movement_money']); + $query->from('transfer'); + $query->andWhere(['transfer.type' => Transfer::TYPE_MONEY_MOVEMENT_OUT]); + $query->innerJoin("money_movement", "money_movement.id_money_movement = transfer.id_object"); + $this->addQueryFilters($query); + + $this->moneyMovementMoneis = $query->one(); + } + + + +} diff --git a/backend/views/transfer/_search_summary.php b/backend/views/transfer/_search_summary.php new file mode 100644 index 0000000..86b965a --- /dev/null +++ b/backend/views/transfer/_search_summary.php @@ -0,0 +1,58 @@ + + + diff --git a/backend/views/transfer/index.php b/backend/views/transfer/index.php index 8ddc5f9..02ad157 100644 --- a/backend/views/transfer/index.php +++ b/backend/views/transfer/index.php @@ -43,6 +43,10 @@ $this->params['breadcrumbs'][] = $this->title; 'dataProvider' => $dataProvider, 'columns' => [ + [ + 'attribute' => 'id_transfer', + 'value' => 'id_transfer' + ], [ 'attribute' => 'type', 'value' => 'transferTypeName' @@ -72,6 +76,7 @@ $this->params['breadcrumbs'][] = $this->title; 'value' => 'signedMoney' ], 'created_at:datetime', + 'paid_at:datetime', ['class' => 'yii\grid\ActionColumn', 'template' => '{view}' diff --git a/backend/views/transfer/summary.php b/backend/views/transfer/summary.php new file mode 100644 index 0000000..2f27a6e --- /dev/null +++ b/backend/views/transfer/summary.php @@ -0,0 +1,73 @@ +title = Yii::t('frontend/transfer', 'Transfers Summary'); +$this->params['breadcrumbs'][] = $this->title; +?> + + +
+ +

title) ?>

+ render('_search_summary', ['model' => $searchModel, 'accounts' => $accounts,'users' => $users,]); ?> + + + +

Bérletek összesen

+ + + + + + + +
BérletekticketMoney?> FT
+

Termékek összesen

+ + + + + + + + + + productMoneies as $pm ){ + ?> + + + + + +
Termék kategóriaÖsszeg
FT
+

Pénzmozgások összesen

+ + + + + + + +
PénzmozgásokmoneyMovementMoneis['money_movement_money']?> FT
+

Bevétel összesen

+ + + + + + + +
Bérletektotal?> FT
+
diff --git a/backend/views/transfer/view.php b/backend/views/transfer/view.php index 7e648a4..d2f58e3 100644 --- a/backend/views/transfer/view.php +++ b/backend/views/transfer/view.php @@ -41,6 +41,7 @@ $this->params['breadcrumbs'][] = $this->title; 'money', 'comment', 'created_at', + 'paid_at', ], ]) ?> diff --git a/common/assets/TypeAheadAsset.php b/common/assets/TypeAheadAsset.php new file mode 100644 index 0000000..7794e34 --- /dev/null +++ b/common/assets/TypeAheadAsset.php @@ -0,0 +1,27 @@ + + * @since 2.0 + */ +class TypeAheadAsset extends AssetBundle +{ + public $sourcePath = '@vendor/bassjobsen/bootstrap-3-typeahead'; + public $js = [ + 'bootstrap3-typeahead.min.js', + ]; + public $depends = [ +// 'yii\bootstrap\BootstrapAsset', +// 'yii\bootstrap\BootstrapPluginAsset', + ]; +} diff --git a/common/messages/hu/frontend/transfer.php b/common/messages/hu/frontend/transfer.php index f8379da..816b973 100644 --- a/common/messages/hu/frontend/transfer.php +++ b/common/messages/hu/frontend/transfer.php @@ -28,4 +28,5 @@ return [ 'Update' => 'Módosítás', 'Update {modelClass}: ' => '{modelClass} módosítás: ', 'pieces' => 'db', + 'Transfers Summary' => 'Bevétel' ]; diff --git a/common/models/Product.php b/common/models/Product.php index 30544e8..94c04f7 100644 --- a/common/models/Product.php +++ b/common/models/Product.php @@ -168,6 +168,20 @@ class Product extends \common\models\BaseFitnessActiveRecord { ]); } + public static function modelToMapIdName($product,$default = null){ + + if ( $product == null ){ + return $default; + } + + return ArrayHelper::toArray($product, [ + 'common\models\Product' => [ + 'id_product', + 'name', + ], + ]); + } + public static function sellProduct($product,$count){ $product->stock = $product->stock - $count; } diff --git a/composer.json b/composer.json index 3f207c2..43d682f 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,8 @@ "yiisoft/yii2-jui": "^2.0", "bower-asset/moment": "^2.10", "bower-asset/accounting": "^0.3.2", - "dmstr/yii2-adminlte-asset": "2.*" + "dmstr/yii2-adminlte-asset": "2.*", + "bassjobsen/bootstrap-3-typeahead": "^4.0" }, "require-dev": { "yiisoft/yii2-codeception": "*", diff --git a/composer.lock b/composer.lock index ef0da64..f0c982d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "48dcd434e58d6b2167477f208c700dc3", - "content-hash": "2678695117e871d59f193589465751cf", + "hash": "acfc873fb659d08adc23d325138a56b2", + "content-hash": "28e2bce30da929c84bd2ac7cffd90f3f", "packages": [ { "name": "almasaeed2010/adminlte", @@ -47,6 +47,42 @@ ], "time": "2015-10-23 14:50:49" }, + { + "name": "bassjobsen/bootstrap-3-typeahead", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/bassjobsen/Bootstrap-3-Typeahead.git", + "reference": "516d96962e1d7fc7c3972c9bbba3b0a133ae9aae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bassjobsen/Bootstrap-3-Typeahead/zipball/516d96962e1d7fc7c3972c9bbba3b0a133ae9aae", + "reference": "516d96962e1d7fc7c3972c9bbba3b0a133ae9aae", + "shasum": "" + }, + "type": "component", + "extra": { + "component": { + "files": [ + "bootstrap3-typeahead.js", + "bootstrap3-typeahead.min.js" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Bass Jobsen", + "email": "bass@w3masters.nl" + } + ], + "description": "Bootstrap 3 Typeahead", + "time": "2015-11-14 22:02:27" + }, { "name": "bower-asset/accounting", "version": "v0.3.2", diff --git a/frontend/assets/ProductSellAsset.php b/frontend/assets/ProductSellAsset.php index 30bed56..e7a4430 100644 --- a/frontend/assets/ProductSellAsset.php +++ b/frontend/assets/ProductSellAsset.php @@ -26,5 +26,7 @@ class ProductSellAsset extends AssetBundle public $depends = [ 'frontend\assets\AppAsset', 'yii\jui\JuiAsset', + 'common\assets\TypeAheadAsset', + ]; } diff --git a/frontend/controllers/ProductController.php b/frontend/controllers/ProductController.php index abb7a33..866a2dc 100644 --- a/frontend/controllers/ProductController.php +++ b/frontend/controllers/ProductController.php @@ -42,7 +42,7 @@ class ProductController extends Controller ], 'access' => [ 'class' => \yii\filters\AccessControl::className(), - 'only' => [ 'sale','payout-customer-cart','payout-user-cart', 'lookup'], + 'only' => [ 'sale','payout-customer-cart','payout-user-cart', 'lookup','find'], 'rules' => [ // allow authenticated users [ @@ -82,6 +82,11 @@ class ProductController extends Controller $model->customer = $this->customer; $model->card = $this->card; + $products = Product::read(); + $products = Product::modelToMapIdName($products); + + $model->products = $products; + if (Yii::$app->request->isAjax) { \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; @@ -203,6 +208,23 @@ class ProductController extends Controller + return $result; + } + + /** + */ + public function actionFind($id=null) + { + $result = []; + $product = Product::findOne($id); + $product = Product::modelToArray($product); + + $result['product'] = $product; + + \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; + + + return $result; } diff --git a/frontend/models/ProductLookupForm.php b/frontend/models/ProductLookupForm.php index 5486ec2..3da4082 100644 --- a/frontend/models/ProductLookupForm.php +++ b/frontend/models/ProductLookupForm.php @@ -14,6 +14,9 @@ class ProductLookupForm extends Model { public $filter_text; + public $product_search; + + /** @@ -33,6 +36,7 @@ class ProductLookupForm extends Model { return [ 'filter_text' => Yii::t('frontend/product','Barcode or product code') , + 'product_search' => Yii::t("frontend/product", "Name"), ]; } diff --git a/frontend/models/ProductSaleForm.php b/frontend/models/ProductSaleForm.php index 800f4e0..bc9f298 100644 --- a/frontend/models/ProductSaleForm.php +++ b/frontend/models/ProductSaleForm.php @@ -76,6 +76,8 @@ class ProductSaleForm extends Model public $customerCart; + public $products; + /** * @inheritdoc */ diff --git a/frontend/views/product/_sale_form.php b/frontend/views/product/_sale_form.php index ad3bcf5..9dea5f8 100644 --- a/frontend/views/product/_sale_form.php +++ b/frontend/views/product/_sale_form.php @@ -47,6 +47,8 @@ $discountOptions = mkOptions( ArrayHelper::map($discounts, 'id_discount', 'name' ], ] )?>
+ + field($lookupModel,'product_search')->textInput(['id'=>'product_search']); ?> field($lookupModel,'filter_text')->textInput(['id'=>'filter_text']); ?>
diff --git a/frontend/views/product/sale.php b/frontend/views/product/sale.php index d07db23..82ebfd8 100644 --- a/frontend/views/product/sale.php +++ b/frontend/views/product/sale.php @@ -16,8 +16,10 @@ ProductSellAsset::register($this); $options = []; $options['lookup_product_url'] = Url::toRoute(['product/lookup']); +$options['find_product_url'] = Url::toRoute(['product/find']); $options['url_pay_user_cart'] = Url::toRoute(['product/payout-user-cart']); $options['user_cart'] = $userTransfers; +$options['products'] = $model->products; if ( isset($model->card) ){ $options['url_pay_customer_card'] = Url::toRoute(['product/payout-customer-cart','number' => $model->card->number]); $options['customer_cart'] = $model->customerCart; diff --git a/frontend/web/js/product.sell.js b/frontend/web/js/product.sell.js index 0fb5a37..dcdff21 100644 --- a/frontend/web/js/product.sell.js +++ b/frontend/web/js/product.sell.js @@ -18,6 +18,7 @@ function ProductSell(o){ url_pay_user_cart: '', /** ajax url for lookup service*/ lookup_product_url: '', + find_product_url: '', /**the id of form*/ selector_form: '#product_form', /**form contains error text*/ @@ -28,6 +29,7 @@ function ProductSell(o){ discounts: [], customer_cart: [], id_account: null, + products : [], }; @@ -51,6 +53,8 @@ function ProductSell(o){ productChanged(); addDocumentKeypressedListener(); + + initAutocomplete(); } /** @@ -117,7 +121,7 @@ function ProductSell(o){ var word; word = data.word; if ( word.length > 0){ - if ( word.length >= 6 && word.length <= 8 ){ + if ( word.length >= 6 && word.length <= 9 ){ //lookupuser }else{ $("#filter_text").val(data.word); @@ -175,11 +179,20 @@ function ProductSell(o){ $( app.defaults.selector_filter_text ).keypress(function( event ) { if ( event.which == 13 ) { event.preventDefault(); + $('#product_search').val(''); + $(this).val( fixBarcode($(this).val())); lookupProduct(); } }); } + function fixBarcode(s){ + var result; + result = s; + result = result.replace(/ö/g, "0"); + return result; + } + /**listen for enter key pressed on #productsaleform-count*/ function addBehaviorCountEnterPressedListener(){ $( '#productsaleform-count' ).keypress(function( event ) { @@ -249,6 +262,22 @@ function ProductSell(o){ }); } + function _findProduct(id){ + var data, url; + + url = app.defaults.find_product_url; + data = { + 'id' : id, + }; + + $.ajax({ + dataType: "json", + url: url, + data: data, + success: onLookupProductReady + }); + } + /**succes event handler after lookup product event*/ function onLookupProductReady( data ){ app.product = data.product; @@ -278,6 +307,7 @@ function ProductSell(o){ $('#productsaleform-id_product').val(''); $('#productsaleform-id_account').val( app.defaults.id_account ? app.defaults.id_account : ''); $("#productsaleform-count").val(1); +// } function applyProduct(product){ @@ -374,6 +404,7 @@ function ProductSell(o){ clearForm(); refreshCalculatedValues(); $("#filter_text").val(''); + $("#product_search").val(''); setFocus(); app.defaults.user_cart = response.transfers; app.defaults.customer_cart = response.customer_cart; @@ -486,7 +517,7 @@ function ProductSell(o){ function normalizePrice( price ){ var result; - result = hufRound(price); +// result = hufRound(price); return result; } @@ -499,4 +530,44 @@ function ProductSell(o){ } } + + function initAutocomplete(){ +// var colors = ["red", "blue", "green", "yellow", "brown", "black"]; +// $('#product_search').typeahead( {source: colors } ); + + var $input = $('#product_search'); + $input.typeahead({source: app.defaults.products, + autoSelect: true, + items: 12, + minLength: 3, +// displayText: function (item){ +// return item. +// } + + }); + $input.change(function() { + var current = $input.typeahead("getActive"); + $("#filter_text").val(''); + if (current) { + // Some item from your model is active! + if (current.name == $input.val()) { + // This means the exact match is found. Use toLowerCase() if you want case insensitive match. + console.info(current); + _findProduct(current.id_product); + } else { + // This means it is only a partial match, you can either add a new item + // or take the active if you don't want new items + console.info('partial'); + app.product = null; + productChanged(); + } + } else { + // Nothing is active so it is a new value (or maybe empty value) + console.info('incactive'); + app.product = null; + productChanged(); + } + }); + } + }