Merge pull request '2879 - Autocomplete item field from imported buys' (#690) from 2879-import_buys into dev
gitea/salix/pipeline/head This commit looks good Details

Reviewed-on: #690
Reviewed-by: Carlos Jimenez Ruiz <carlosjr@verdnatura.es>
This commit is contained in:
Carlos Jimenez Ruiz 2021-08-06 10:35:35 +00:00
commit 2b866cd38f
11 changed files with 265 additions and 48 deletions

View File

@ -0,0 +1,14 @@
create table `vn`.`itemMatchProperties`
(
itemFk int not null,
name varchar(80) not null,
producer varchar(80) not null,
size int not null,
constraint itemMatchProperties_pk
primary key (itemFk, name, producer, size),
constraint itemFk___fk
foreign key (itemFk) references item (id)
on update cascade on delete cascade
)
comment 'Propiedades para encontrar articulos equivalentes en verdnatura';

View File

@ -29,9 +29,6 @@ describe('Entry import, create and edit buys path', () => {
});
it('should fill the form, import the designated JSON file and select items for each import and confirm import', async() => {
await page.write(selectors.entryBuys.ref, 'a reference');
await page.write(selectors.entryBuys.observation, 'an observation');
let currentDir = process.cwd();
let filePath = `${currentDir}/e2e/assets/07_import_buys.json`;
@ -41,6 +38,9 @@ describe('Entry import, create and edit buys path', () => {
]);
await fileChooser.accept([filePath]);
await page.waitForTextInField(selectors.entryBuys.ref, '200573095, 200573106, 200573117, 200573506');
await page.waitForTextInField(selectors.entryBuys.observation, '729-6340 2846');
await page.autocompleteSearch(selectors.entryBuys.firstImportedItem, 'Ranged Reinforced weapon pistol 9mm');
await page.autocompleteSearch(selectors.entryBuys.secondImportedItem, 'Melee Reinforced weapon heavy shield 1x0.5m');
await page.autocompleteSearch(selectors.entryBuys.thirdImportedItem, 'Container medical box 1m');

View File

@ -11,11 +11,6 @@ module.exports = Self => {
description: 'The entry id',
http: {source: 'path'}
},
{
arg: 'options',
type: 'object',
description: 'Callback options',
},
{
arg: 'ref',
type: 'string',
@ -28,11 +23,11 @@ module.exports = Self => {
},
{
arg: 'buys',
type: ['Object'],
type: ['object'],
description: 'The buys',
}],
returns: {
type: ['Object'],
type: ['object'],
root: true
},
http: {
@ -41,23 +36,27 @@ module.exports = Self => {
}
});
Self.importBuys = async(ctx, id, options = {}) => {
Self.importBuys = async(ctx, id, options) => {
const conn = Self.dataSource.connector;
const args = ctx.args;
const models = Self.app.models;
let tx;
const myOptions = {};
if (!options.transaction) {
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
options.transaction = tx;
myOptions.transaction = tx;
}
try {
const entry = await models.Entry.findById(id, null, options);
const entry = await models.Entry.findById(id, null, myOptions);
await entry.updateAttributes({
observation: args.observation,
ref: args.ref
}, options);
}, myOptions);
const buys = [];
for (let buy of args.buys) {
@ -71,9 +70,16 @@ module.exports = Self => {
buyingValue: buy.buyingValue,
packageFk: buy.packageFk
});
await models.ItemMatchProperties.upsert({
itemFk: buy.itemFk,
name: buy.description,
producer: buy.companyName,
size: buy.size
}, myOptions);
}
const createdBuys = await models.Buy.create(buys, options);
const createdBuys = await models.Buy.create(buys, myOptions);
const buyIds = createdBuys.map(buy => buy.id);
const stmts = [];
@ -90,7 +96,7 @@ module.exports = Self => {
stmts.push('CALL buy_recalcPrices()');
const sql = ParameterizedSQL.join(stmts, ';');
await conn.executeStmt(sql, options);
await conn.executeStmt(sql, myOptions);
if (tx) await tx.commit();
} catch (e) {
if (tx) await tx.rollback();

View File

@ -37,7 +37,21 @@ module.exports = Self => {
where: {volume: {gte: buy.volume}},
order: 'volume ASC'
}, myOptions);
buy.packageFk = packaging.id;
if (packaging)
buy.packageFk = packaging.id;
const reference = await models.ItemMatchProperties.findOne({
fields: ['itemFk'],
where: {
name: buy.description,
producer: buy.companyName,
size: buy.size
}
}, myOptions);
if (reference)
buy.itemFk = reference.itemFk;
}
return buys;

View File

@ -5,6 +5,9 @@
"Buy": {
"dataSource": "vn"
},
"ItemMatchProperties": {
"dataSource": "vn"
},
"EntryLog": {
"dataSource": "vn"
},

View File

@ -0,0 +1,32 @@
{
"name": "ItemMatchProperties",
"base": "VnModel",
"options": {
"mysql": {
"table": "itemMatchProperties"
}
},
"properties": {
"itemFk": {
"type": "number",
"id": true,
"description": "Identifier"
},
"name": {
"type": "string"
},
"producer": {
"type": "string"
},
"size": {
"type": "string"
}
},
"relations": {
"item": {
"type": "belongsTo",
"model": "Item",
"foreignKey": "itemFk"
}
}
}

View File

@ -9,20 +9,6 @@
class="vn-ma-md">
<div class="vn-w-lg">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield vn-focus
vn-one
label="Reference"
ng-model="$ctrl.import.ref">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textarea
vn-one
label="Observation"
ng-model="$ctrl.import.observation">
</vn-textarea>
</vn-horizontal>
<vn-horizontal>
<vn-input-file
vn-one
@ -40,6 +26,20 @@
</append>
</vn-input-file>
</vn-horizontal>
<vn-horizontal ng-show="$ctrl.import.ref">
<vn-textfield vn-focus
vn-one
label="Reference"
ng-model="$ctrl.import.ref">
</vn-textfield>
</vn-horizontal>
<vn-horizontal ng-show="$ctrl.import.observation">
<vn-textarea
vn-one
label="Observation"
ng-model="$ctrl.import.observation">
</vn-textarea>
</vn-horizontal>
<vn-horizontal ng-show="$ctrl.import.buys.length > 0">
<table class="vn-table">
<thead>
@ -51,7 +51,6 @@
<th translate center>Grouping</th>
<th translate center>Buying value</th>
<th translate center>Box</th>
<th translate center>Volume</th>
</tr>
</thead>
<tbody ng-repeat="buy in $ctrl.import.buys">
@ -70,20 +69,19 @@
<tpl-item>
{{::id}} - {{::name}}
</tpl-item>
<append>
<vn-icon-button
icon="filter_alt"
vn-click-stop="$ctrl.showFilterDialog(buy)"
vn-tooltip="Filter...">
</vn-icon-button>
</append>
</vn-autocomplete>
</td>
<td title="{{::buy.description}}" expand>{{::buy.description | dashIfEmpty}}</td>
<td center title="{{::buy.size}}">{{::buy.size | dashIfEmpty}}</td>
<td center>
<vn-chip>
<span>{{::buy.packing | dashIfEmpty}}</span>
</vn-chip>
</td>
<td center>
<vn-chip>
<span>{{::buy.grouping | dashIfEmpty}}</span>
</vn-chip>
</vn-td>
<td center>{{::buy.packing | dashIfEmpty}}</td>
<td center>{{::buy.grouping | dashIfEmpty}}</td>
<td>{{::buy.buyingValue | currency: 'EUR':2}}</td>
<td center title="{{::buy.packageFk | dashIfEmpty}}">
<vn-autocomplete
@ -95,7 +93,6 @@
ng-model="buy.packageFk">
</vn-autocomplete>
</td>
<td center title="{{::buy.volume}}">{{::buy.volume | number}}</td>
</tr>
</tbody>
</table>
@ -110,7 +107,95 @@
label="Cancel"
ui-sref="entry.card.buy.index">
</vn-button>
</vn-button>
</vn-button-bar>
</div>
</form>
<vn-dialog
vn-id="filterDialog"
on-accept="$ctrl.addTime()"
message="Filter item">
<tpl-body class="itemFilter">
<vn-horizontal>
<vn-textfield
label="Name"
ng-model="$ctrl.itemFilterParams.name"
vn-focus>
</vn-textfield>
<vn-textfield
label="Size"
ng-model="$ctrl.itemFilterParams.size">
</vn-textfield>
<vn-autocomplete
label="Producer"
ng-model="$ctrl.itemFilterParams.producerFk"
url="Producers"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete
label="Type"
ng-model="$ctrl.itemFilterParams.typeFk"
url="ItemTypes"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete
label="Color"
ng-model="$ctrl.itemFilterParams.inkFk"
url="Inks"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal class="vn-mb-md">
<vn-button vn-none
label="Search"
ng-click="$ctrl.filter()">
</vn-button>
</vn-horizontal>
<vn-crud-model
vn-id="itemsModel"
url="Items"
filter="$ctrl.itemFilter"
data="items"
limit="10">
</vn-crud-model>
<vn-data-viewer
model="itemsModel"
class="vn-w-lg">
<vn-table class="scrollable">
<vn-thead>
<vn-tr>
<vn-th shrink>ID</vn-th>
<vn-th expand>Item</vn-th>
<vn-th number>Size</vn-th>
<vn-th expand>Producer</vn-th>
<vn-th>Color</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<a ng-repeat="item in items"
class="clickable vn-tr search-result"
ng-click="$ctrl.selectItem(item.id)">
<vn-td shrink>
<span
ng-click="itemDescriptor.show($event, item.id)"
class="link">
{{::item.id}}
</span>
</vn-td>
<vn-td expand>{{::item.name}}</vn-td>
<vn-td number>{{::item.size}}</vn-td>
<vn-td expand>{{::item.producer.name}}</vn-td>
<vn-td>{{::item.ink.name}}</vn-td>
</a>
</vn-tbody>
</vn-table>
</vn-data-viewer>
<vn-item-descriptor-popover
vn-id="item-descriptor"
warehouse-fk="$ctrl.vnConfig.warehouseFk">
</vn-item-descriptor-popover>
</tpl-body>
</vn-dialog>

View File

@ -29,6 +29,7 @@ class Controller extends Section {
this.$.$applyAsync(() => {
this.import.observation = invoice.tx_awb;
const companyName = invoice.tx_company;
const boxes = invoice.boxes;
const buys = [];
for (let box of boxes) {
@ -37,11 +38,12 @@ class Controller extends Section {
const packing = product.nu_stems_bunch * product.nu_bunches;
buys.push({
description: product.nm_product,
companyName: companyName,
size: product.nu_length,
packing: packing,
grouping: product.nu_stems_bunch,
buyingValue: parseFloat(product.mny_rate_stem),
volume: boxVolume
volume: boxVolume,
});
}
}
@ -86,6 +88,59 @@ class Controller extends Section {
? {id: $search}
: {name: {like: '%' + $search + '%'}};
}
showFilterDialog(buy) {
this.activeBuy = buy;
this.itemFilterParams = {};
this.itemFilter = {
include: [
{
relation: 'producer',
scope: {
fields: ['name']
}
},
{
relation: 'ink',
scope: {
fields: ['name']
}
}
]
};
this.$.filterDialog.show();
}
selectItem(id) {
this.activeBuy['itemFk'] = id;
this.$.filterDialog.hide();
}
filter() {
const filter = this.itemFilter;
const params = this.itemFilterParams;
const where = {};
for (let key in params) {
const value = params[key];
if (!value) continue;
switch (key) {
case 'name':
where[key] = {like: `%${value}%`};
break;
case 'producerFk':
case 'typeFk':
case 'size':
case 'ink':
where[key] = value;
}
}
filter.where = where;
this.$.itemsModel.applyFilter(filter);
}
}
Controller.$inject = ['$element', '$scope'];

View File

@ -2,4 +2,10 @@ vn-entry-buy-import {
.vn-table > tbody td:nth-child(1) {
width: 250px
}
}
.itemFilter {
vn-table.scrollable {
height: 500px
}
}

View File

@ -3,4 +3,6 @@ Observation: Observación
Box: Embalaje
Import buys: Importar compras
Some of the imported buys doesn't have an item: Algunas de las compras importadas no tienen un artículo
JSON files only: Solo ficheros JSON
JSON files only: Solo ficheros JSON
Filter item: Filtrar artículo
Filter...: Filtrar...

View File

@ -1,6 +1,6 @@
Ink: Tinta
Origin: Origen
Producer: Productor.
Producer: Productor
With visible: Con visible
Field: Campo
More fields: Más campos