Skip to content

Commit

Permalink
#324 first attempt for product index
Browse files Browse the repository at this point in the history
  • Loading branch information
Jorr.it committed Jun 23, 2022
1 parent b884ab1 commit c60d011
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 91 deletions.
1 change: 1 addition & 0 deletions app/config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ nelmio_cors:
fos_rest:
zone:
- { path: ^/api/* }
- { path: ^/vue/* }
body_listener: true
format_listener:
rules:
Expand Down
5 changes: 5 additions & 0 deletions app/config/routing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ api:
type: rest
prefix: /api

vue_product:
resource: "@AppBundle/Controller/VueProductController.php"
type: rest
prefix: /vue/product

app:
resource: "@AppBundle/Controller/"
type: annotation
Expand Down
1 change: 1 addition & 0 deletions app/config/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ security:
- { path: ^/public/, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/leergeld-bestelling, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
- { path: ^/vue, roles: IS_AUTHENTICATED_FULLY }
- { path: ^/admin/, role: ROLE_ADMIN }
- { path: ^/logistics/, role: ROLE_LOGISTICS }
- { path: ^/product/, role: ROLE_LOCAL }
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,10 @@
"vue-loader": "^15.9.8",
"vue-template-compiler": "^2.6.*",
"webpack-notifier": "^1.6.0"
},
"dependencies": {
"axios": "^0.27.2",
"core-js": "^3.23.2",
"vue-axios": "^3.4.1"
}
}
38 changes: 9 additions & 29 deletions src/AppBundle/Controller/ProductController.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@
use AppBundle\Form\ProductForm;
use AppBundle\Form\ProductSplitForm;
use AppBundle\Form\ChecklistForm;
use AppBundle\Form\IndexSearchForm;
use AppBundle\Form\IndexBulkEditForm;
use AppBundle\Form\ProductBulkEditForm;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
Expand All @@ -55,32 +53,14 @@ public function indexAction(Request $request)
{
$repo = $this->getDoctrine()->getRepository('AppBundle:Product');

$products = array();

$container = new \AppBundle\Helper\IndexSearchContainer($this->getUser(), Product::class);

$form = $this->createForm(IndexSearchForm::class, $container);

$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid() && $container->isSearchable())
{
$products = $repo->findBySearchQuery($container);
$pageLength = 200;
}
else
{
$products = $repo->findStock($this->getUser());
$pageLength = 20;
}

$paginator = $this->get('knp_paginator');
$productsPage = $paginator->paginate($products, $request->query->getInt('page', 1), $pageLength);
$productCount = $repo->queryStock($this->getUser())->select("count(p.id)")->getQuery()->getSingleScalarResult();
$pageLength = 20;
$pageCount = ceil($productCount / $pageLength);

return $this->render('AppBundle:Product:index.html.twig', array(
'products' => $productsPage,
'form' => $form->createView(),
'formBulkEdit' => $this->createForm(IndexBulkEditForm::class, $products, ['index_class' => Product::class])->createView()
'productCount' => $productCount,
'pageLength' => $pageLength,
'pageCount' => $pageCount
));
}

Expand Down Expand Up @@ -306,7 +286,7 @@ public function splitAction(Request $request, $id)
$product = $repo->find($id);

$data = array('quantity' => 1, 'status' => $product->getStatus());
$options = array('stock' => $product->getQuantityInStock());
$options = array('stock' => $product->getStock()->getStock());

$form = $this->createForm(ProductSplitForm::class, $data, $options);

Expand All @@ -328,10 +308,10 @@ public function splitAction(Request $request, $id)
$quantity = $data['quantity'];
break;
case "individualize_stock":
$quantity = $product->getQuantityInStock() - 1;
$quantity = $product->getStock()->getStock() - 1;
break;
case "individualize_bundle":
$quantity = $product->getQuantityPurchased() - 1;
$quantity = $product->getStock()->getPurchased() - 1;
$sales = true;
break;
}
Expand Down
48 changes: 48 additions & 0 deletions src/AppBundle/Controller/VueProductController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/*
* Nexxus Stock Keeping (online voorraad beheer software)
* Copyright (C) 2018 Copiatek Scan & Computer Solution BV
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see licenses.
*
* Copiatek – [email protected] – Postbus 547 2501 CM Den Haag
*/

namespace AppBundle\Controller;

use AppBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Request;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Controller\FOSRestController;

/**
* This controller will replace the ProductController gradually
* because of implementation of Vue (issue #324)
* It uses ajax/rest
*/
class VueProductController extends FOSRestController
{
/**
* @Rest\Get("/{offset}/{limit}")
*/
public function indexAction(Request $request, $offset, $limit)
{
$repo = $this->getDoctrine()->getRepository(Product::class);

$products = $repo->queryStock($this->getUser(), $offset, $limit)->getQuery()->getResult();

return $products;
}
}
56 changes: 5 additions & 51 deletions src/AppBundle/Resources/views/Product/index.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,11 @@
{% block body %}
<h1>Stock</h1>

{{ form_start(form) }}
<div class="row">
<div class="col-sm-3">
{{ form_widget(form.query, {'attr': {'placeholder': 'Zoeken op Id, Sku of (deel van) naam'} }) }}
</div>
<div class="col-sm-2">
{{ form_widget(form.availability) }}
</div>
<div class="col-sm-2">
{{ form_widget(form.status) }}
</div>
<div class="col-sm-2">
{{ form_widget(form.producttype) }}
</div>
<div class="col-sm-2">
{{ form_widget(form.location) }}
</div>
<div class="col-sm-1">
{{ form_widget(form.submit) }}
</div>
</div>
{{ form_end(form) }}

<hr />

{{ form_start(formBulkEdit, {'action': path('product_bulkedit'), 'attr': { 'target': '_blank' }}) }}

<div class="row" style="margin-bottom: 20px; margin-top: 10px">
<div class="col-sm-1 col-xs-3">
<button type="button" class="btn btn-default btn-selectall">Select all</button>
</div>
<div class="col-sm-3 col-xs-9">
{{ form_widget(formBulkEdit.action) }}
</div>
<div class="col-sm-3 col-sm-offset-5 col-xs-12">
<a href="{{ path('purchaseorder_edit', { 'id': 0 }) }}" class="btn btn-primary pull-right" role="button">Create new purchase</a>
</div>
</div>



<div id="product-stock" data-products="{{ products.items|json_encode|escape('html_attr') }}"></div>




<div class="navigation">
{{ knp_pagination_render(products) }}
</div>

{{ form_end(formBulkEdit, {'render_rest': false}) }}
<div id="product-stock"
data-productcount="{{ productCount }}"
data-pagelength="{{ pageLength }}"
data-pagecount="{{ pageCount }}"
></div>

{% endblock %}

Expand Down
77 changes: 75 additions & 2 deletions src/AppBundle/Resources/vue/ProductStock/App.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,58 @@
<template>

<table class="table table-striped">
<thead>
<tr>
<th></th>
<th>SKU</th>
<th>Name</th>
<th>Type</th>
<th>Location</th>
<th width="10%">Price</th>
<th width="5%">Purch</th>
<th width="5%">Stock</th>
<th width="5%">Hold</th>
<th width="5%">Sale</th>
<th width="5%">Sold</th>
<th width="1%"></th>
</tr>
</thead>
<tbody>
<tr v-for="product in products" :key="product.id">
<td><input type="checkbox" :id="'index_bulk_edit_form_index_'+product.id" name="index_bulk_edit_form[index][]" :value="product.id"></td>
<td><a href="#" data-target="#modalEditor" class="btn-modal" data-toggle="tooltip" title="product.getAttributesList()">{{ product.sku }}</a></td>
<td><a href="#" data-target="#modalEditor" class="btn-modal" data-toggle="tooltip" title="product.getAttributesList()">{{ product.name }}</a></td>
<td>{{ product.type.name }}</td>
<td>{{ product.location.name }}</td>
<td>&euro; {{ product.price|number_format(2, ',', '.') }}</td>
<td>{{ product.stock.purchased }}</td>
<td>{{ product.stock.stock }}</td>
<td>{{ product.stock.hold }}</td>
<td>{{ product.stock.saleable }}</td>
<td>{{ product.stock.sold }}</td>
<td nowrap align="right">
<a v-if="product.purchaseOrderRelation && product.type && product.type.tasks.length > 0"
class="btn btn-default btn-modal"
href="#"
data-target="#modalEditor"
title="Checklist of tasks">
<span class="glyphicon glyphicon-check" aria-label="Checklist" style="margin-right: 3px"></span>
{{ product.purchaseOrderRelation.servicesDone }} / {{ product.type.tasks.length }}
</a>
<a v-if="product.stock.purchased > 1"
class="btn btn-default btn-modal"
href="#"
data-target="#modalSplitter"
title="Split bundle">
<span class="glyphicon glyphicon-flash" aria-label="Split"></span>
</a>
<a class="btn btn-success btn-modal" href="#" data-target="#modalEditor" title="Edit"><span class="glyphicon glyphicon-pencil" aria-label="Edit"></span></a>
<a class="btn btn-danger btn-delete btn-modal" href="#" title="Delete" data-class="product" :data-name="product.name"><span class="glyphicon glyphicon-remove" aria-label="Delete"></span></a>
</td>
</tr>
</tbody>
</table>

</template>

<script>
Expand All @@ -8,12 +61,32 @@ export default {
name: 'App',
data() {
var dataElement = document.querySelector('div#product-stock');
var products = dataElement.dataset.products;
var productCount = parseInt(dataElement.dataset.productcount);
var pageLength = parseInt(dataElement.dataset.pagelength);
var pageCount = parseInt(dataElement.dataset.pagecount);
return {
products
productCount,
pageLength,
pageCount,
page: 1,
products: []
}
},
computed: {
offset() {
return (this.page-1) * this.pageLength
}
},
mounted() {
this.axios.get("../vue/product/" + this.offset + "/" + this.pageLength)
.then(response => {
this.products = response.data
})
.catch(err => { throw err })
}
}
</script>


4 changes: 4 additions & 0 deletions src/AppBundle/Resources/vue/ProductStock/main.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import App from './App.vue'

Vue.use(VueAxios, axios)

new Vue({ render: h => h(App) }).$mount('#product-stock')
14 changes: 5 additions & 9 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ Encore
// enables hashed filenames (e.g. app.abc123.css)
.enableVersioning(Encore.isProduction())

.configureBabel((config) => {
config.plugins.push('@babel/plugin-proposal-class-properties');
})

// enables @babel/preset-env polyfills
.configureBabelPresetEnv((config) => {
config.useBuiltIns = 'usage';
Expand Down Expand Up @@ -71,12 +75,4 @@ Encore
})
;

const config = Encore.getWebpackConfig();

// Change the kind of source map generated in development mode
if (!Encore.isProduction()) {
// config.devtool = 'cheap-eval-source-map';
config.output.devtoolModuleFilenameTemplate = 'file:///[absolute-resource-path]';
}

module.exports = config;
module.exports = Encore.getWebpackConfig();

0 comments on commit c60d011

Please sign in to comment.