entry buy index
This commit is contained in:
parent
d46c643dc0
commit
daa94e4eb5
|
@ -21,6 +21,9 @@ vn-chip {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.white {
|
||||||
|
background-color: $color-bg-panel;
|
||||||
|
}
|
||||||
&.colored {
|
&.colored {
|
||||||
background-color: $color-main;
|
background-color: $color-main;
|
||||||
color: $color-font-bg;
|
color: $color-font-bg;
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
|
||||||
|
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('addBuy', {
|
||||||
|
description: 'Inserts or updates buy for the current entry',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The entry id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'itemFk',
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'quantity',
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'packageFk',
|
||||||
|
type: 'string',
|
||||||
|
required: true
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/addBuy`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.addBuy = async(ctx, options) => {
|
||||||
|
const conn = Self.dataSource.connector;
|
||||||
|
let tx;
|
||||||
|
let myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
const newBuy = await models.Buy.create({
|
||||||
|
itemFk: ctx.args.itemFk,
|
||||||
|
entryFk: ctx.args.id,
|
||||||
|
packageFk: ctx.args.packageFk,
|
||||||
|
quantity: ctx.args.quantity
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
let filter = {
|
||||||
|
fields: [
|
||||||
|
'id',
|
||||||
|
'itemFk',
|
||||||
|
'stickers',
|
||||||
|
'packing',
|
||||||
|
'grouping',
|
||||||
|
'quantity',
|
||||||
|
'packageFk',
|
||||||
|
'weight',
|
||||||
|
'buyingValue',
|
||||||
|
'price2',
|
||||||
|
'price3'
|
||||||
|
],
|
||||||
|
include: {
|
||||||
|
relation: 'item',
|
||||||
|
scope: {
|
||||||
|
fields: [
|
||||||
|
'id',
|
||||||
|
'typeFk',
|
||||||
|
'name',
|
||||||
|
'size',
|
||||||
|
'minPrice',
|
||||||
|
'tag5',
|
||||||
|
'value5',
|
||||||
|
'tag6',
|
||||||
|
'value6',
|
||||||
|
'tag7',
|
||||||
|
'value7',
|
||||||
|
'tag8',
|
||||||
|
'value8',
|
||||||
|
'tag9',
|
||||||
|
'value9',
|
||||||
|
'tag10',
|
||||||
|
'value10',
|
||||||
|
'groupingMode'
|
||||||
|
],
|
||||||
|
include: {
|
||||||
|
relation: 'itemType',
|
||||||
|
scope: {
|
||||||
|
fields: ['code', 'description']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let stmts = [];
|
||||||
|
let stmt;
|
||||||
|
|
||||||
|
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.buyRecalc');
|
||||||
|
stmt = new ParameterizedSQL(
|
||||||
|
`CREATE TEMPORARY TABLE tmp.buyRecalc
|
||||||
|
(INDEX (id))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT ? AS id`, [newBuy.id]);
|
||||||
|
|
||||||
|
stmts.push(stmt);
|
||||||
|
stmts.push('CALL buy_recalcPrices()');
|
||||||
|
|
||||||
|
const sql = ParameterizedSQL.join(stmts, ';');
|
||||||
|
await conn.executeStmt(sql, myOptions);
|
||||||
|
|
||||||
|
const buy = await models.Buy.findById(newBuy.id, filter, myOptions);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return buy;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,50 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('deleteBuys', {
|
||||||
|
description: 'Deletes the selected buys',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'buys',
|
||||||
|
type: ['object'],
|
||||||
|
required: true,
|
||||||
|
description: 'The buys to delete'
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/deleteBuys`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.deleteBuys = async(ctx, options) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
let tx;
|
||||||
|
let myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let promises = [];
|
||||||
|
for (let buy of ctx.args.buys) {
|
||||||
|
const buysToDelete = models.Buy.destroyById(buy.id, myOptions);
|
||||||
|
promises.push(buysToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleted = await Promise.all(promises);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
return deleted;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,42 @@
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
|
describe('entry addBuy()', () => {
|
||||||
|
const activeCtx = {
|
||||||
|
accessToken: {userId: 18},
|
||||||
|
};
|
||||||
|
|
||||||
|
const ctx = {
|
||||||
|
req: activeCtx
|
||||||
|
};
|
||||||
|
|
||||||
|
const entryId = 2;
|
||||||
|
it('should create a new buy for the given entry', async() => {
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
const itemId = 4;
|
||||||
|
const quantity = 10;
|
||||||
|
|
||||||
|
ctx.args = {
|
||||||
|
id: entryId,
|
||||||
|
itemFk: itemId,
|
||||||
|
quantity: quantity,
|
||||||
|
packageFk: 3
|
||||||
|
};
|
||||||
|
|
||||||
|
const tx = await app.models.Entry.beginTransaction({});
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const newBuy = await app.models.Entry.addBuy(ctx, options);
|
||||||
|
|
||||||
|
expect(newBuy.itemFk).toEqual(itemId);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,23 @@
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
|
describe('sale deleteBuys()', () => {
|
||||||
|
const activeCtx = {
|
||||||
|
accessToken: {userId: 18},
|
||||||
|
};
|
||||||
|
|
||||||
|
const ctx = {
|
||||||
|
req: activeCtx
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should delete the buy', async() => {
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
ctx.args = {buys: [{id: 1}]};
|
||||||
|
|
||||||
|
let result = await app.models.Buy.deleteBuys(ctx);
|
||||||
|
|
||||||
|
expect(result).toEqual([{count: 1}]);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,4 +1,5 @@
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
require('../methods/entry/editLatestBuys')(Self);
|
require('../methods/entry/editLatestBuys')(Self);
|
||||||
require('../methods/entry/latestBuysFilter')(Self);
|
require('../methods/entry/latestBuysFilter')(Self);
|
||||||
|
require('../methods/entry/deleteBuys')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -57,14 +57,12 @@
|
||||||
"entry": {
|
"entry": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Entry",
|
"model": "Entry",
|
||||||
"foreignKey": "entryFk",
|
"foreignKey": "entryFk"
|
||||||
"required": true
|
|
||||||
},
|
},
|
||||||
"item": {
|
"item": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Item",
|
"model": "Item",
|
||||||
"foreignKey": "itemFk",
|
"foreignKey": "itemFk"
|
||||||
"required": true
|
|
||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
|
|
|
@ -2,6 +2,7 @@ module.exports = Self => {
|
||||||
require('../methods/entry/filter')(Self);
|
require('../methods/entry/filter')(Self);
|
||||||
require('../methods/entry/getEntry')(Self);
|
require('../methods/entry/getEntry')(Self);
|
||||||
require('../methods/entry/getBuys')(Self);
|
require('../methods/entry/getBuys')(Self);
|
||||||
|
require('../methods/entry/addBuy')(Self);
|
||||||
require('../methods/entry/importBuys')(Self);
|
require('../methods/entry/importBuys')(Self);
|
||||||
require('../methods/entry/importBuysPreview')(Self);
|
require('../methods/entry/importBuysPreview')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,189 @@
|
||||||
|
<vn-crud-model
|
||||||
|
auto-load="true"
|
||||||
|
vn-id="model"
|
||||||
|
url="Entries/{{$ctrl.$params.id}}/getBuys"
|
||||||
|
data="$ctrl.buys">
|
||||||
|
</vn-crud-model>
|
||||||
|
<vn-watcher
|
||||||
|
vn-id="watcher"
|
||||||
|
data="$ctrl.buys">
|
||||||
|
</vn-watcher>
|
||||||
|
<div class="vn-w-xl">
|
||||||
|
<vn-card class="vn-pa-lg">
|
||||||
|
<vn-horizontal class="header">
|
||||||
|
<vn-tool-bar class="vn-mb-md">
|
||||||
|
<vn-button
|
||||||
|
disabled="$ctrl.selectedBuys() == 0"
|
||||||
|
ng-click="deleteBuys.show()"
|
||||||
|
vn-tooltip="Delete buy(s)"
|
||||||
|
icon="delete">
|
||||||
|
</vn-button>
|
||||||
|
</vn-tool-bar>
|
||||||
|
<vn-one class="taxes" ng-if="$ctrl.sales.length > 0">
|
||||||
|
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.ticket.totalWithoutVat | currency: 'EUR':2}}</p>
|
||||||
|
<p><vn-label translate>VAT</vn-label> {{$ctrl.ticket.totalWithVat - $ctrl.ticket.totalWithoutVat | currency: 'EUR':2}}</p>
|
||||||
|
<p><vn-label><strong>Total</strong></vn-label> <strong>{{$ctrl.ticket.totalWithVat | currency: 'EUR':2}}</strong></p>
|
||||||
|
</vn-one>
|
||||||
|
</vn-horizontal>
|
||||||
|
<table class="vn-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th shrink>
|
||||||
|
<vn-multi-check model="model" on-change="$ctrl.resetChanges()">
|
||||||
|
</vn-multi-check>
|
||||||
|
</th>
|
||||||
|
<th translate center>Item</th>
|
||||||
|
<th translate center>Quantity</th>
|
||||||
|
<th translate center>Package</th>
|
||||||
|
<th translate>Stickers</th>
|
||||||
|
<th translate>Weight</th>
|
||||||
|
<th translate>Packing</th>
|
||||||
|
<th translate>Grouping</th>
|
||||||
|
<th translate>Buying value</th>
|
||||||
|
<th translate expand>Grouping price</th>
|
||||||
|
<th translate expand>Packing price</th>
|
||||||
|
<th translate>Import</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody ng-repeat="buy in $ctrl.buys">
|
||||||
|
<tr>
|
||||||
|
<td shrink>
|
||||||
|
<vn-check tabindex="-1" ng-model="buy.checked">
|
||||||
|
</vn-check>
|
||||||
|
</td>
|
||||||
|
<td shrink>
|
||||||
|
<span
|
||||||
|
ng-if="buy.id"
|
||||||
|
ng-click="itemDescriptor.show($event, buy.item.id)"
|
||||||
|
class="link">
|
||||||
|
{{::buy.item.id | zeroFill:6}}
|
||||||
|
</span>
|
||||||
|
<vn-autocomplete ng-if="!buy.id" class="dense"
|
||||||
|
vn-focus
|
||||||
|
url="Items"
|
||||||
|
ng-model="buy.itemFk"
|
||||||
|
show-field="name"
|
||||||
|
value-field="id"
|
||||||
|
search-function="$ctrl.itemSearchFunc($search)"
|
||||||
|
on-change="$ctrl.saveBuy(buy)"
|
||||||
|
order="id DESC"
|
||||||
|
tabindex="1">
|
||||||
|
<tpl-item>
|
||||||
|
{{::id}} - {{::name}}
|
||||||
|
</tpl-item>
|
||||||
|
</vn-autocomplete>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<vn-input-number class="dense"
|
||||||
|
ng-model="buy.quantity"
|
||||||
|
on-change="$ctrl.saveBuy(buy)">
|
||||||
|
</vn-input-number>
|
||||||
|
</td>
|
||||||
|
<td center title="{{::buy.packageFk | dashIfEmpty}}">
|
||||||
|
<vn-autocomplete
|
||||||
|
vn-one
|
||||||
|
url="Packagings"
|
||||||
|
show-field="id"
|
||||||
|
value-field="id"
|
||||||
|
where="{isBox: true}"
|
||||||
|
ng-model="buy.packageFk"
|
||||||
|
on-change="$ctrl.saveBuy(buy)">
|
||||||
|
</vn-autocomplete>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<vn-input-number class="dense"
|
||||||
|
ng-model="buy.stickers"
|
||||||
|
on-change="$ctrl.saveBuy(buy)">
|
||||||
|
</vn-input-number>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<vn-input-number class="dense"
|
||||||
|
ng-model="buy.weight"
|
||||||
|
on-change="$ctrl.saveBuy(buy)">
|
||||||
|
</vn-input-number>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<vn-input-number class="dense"
|
||||||
|
ng-model="buy.packing"
|
||||||
|
on-change="$ctrl.saveBuy(buy)">
|
||||||
|
</vn-input-number>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<vn-input-number class="dense"
|
||||||
|
ng-model="buy.grouping"
|
||||||
|
on-change="$ctrl.saveBuy(buy)">
|
||||||
|
</vn-input-number>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<vn-input-number class="dense"
|
||||||
|
ng-model="buy.buyingValue"
|
||||||
|
on-change="$ctrl.saveBuy(buy)">
|
||||||
|
</vn-input-number>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<vn-input-number class="dense"
|
||||||
|
ng-model="buy.price2"
|
||||||
|
on-change="$ctrl.saveBuy(buy)">
|
||||||
|
</vn-input-number>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<vn-input-number class="dense"
|
||||||
|
ng-model="buy.price3"
|
||||||
|
on-change="$ctrl.saveBuy(buy)">
|
||||||
|
</vn-input-number>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
ng-if="buy.quantity != null && buy.buyingValue != null"
|
||||||
|
title="{{buy.quantity * buy.buyingValue | currency: 'EUR':2}}">
|
||||||
|
{{buy.quantity * buy.buyingValue | currency: 'EUR':2}}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="dark-row">
|
||||||
|
<td shrink>
|
||||||
|
</td>
|
||||||
|
<td shrink>
|
||||||
|
<span translate-attr="{title: 'Item type'}">
|
||||||
|
{{::buy.item.itemType.code}}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td number shrink>
|
||||||
|
<span translate-attr="{title: 'Item size'}">
|
||||||
|
{{::buy.item.size}}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td center>
|
||||||
|
<span translate-attr="{title: 'Minimum price'}">
|
||||||
|
{{::buy.item.minPrice | currency: 'EUR':2}}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td vn-fetched-tags colspan="9">
|
||||||
|
<vn-one title="{{::buy.item.name}}">{{::buy.item.name}}</vn-one>
|
||||||
|
<vn-fetched-tags
|
||||||
|
max-length="6"
|
||||||
|
item="::buy.item"
|
||||||
|
tabindex="-1">
|
||||||
|
</vn-fetched-tags>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div>
|
||||||
|
<vn-icon-button
|
||||||
|
vn-one
|
||||||
|
vn-tooltip="Add buy"
|
||||||
|
vn-bind="+"
|
||||||
|
icon="add_circle"
|
||||||
|
ng-click="model.insert({})">
|
||||||
|
</vn-icon-button>
|
||||||
|
</div>
|
||||||
|
</vn-card>
|
||||||
|
</div>
|
||||||
<div fixed-bottom-right>
|
<div fixed-bottom-right>
|
||||||
<vn-vertical style="align-items: center;">
|
<vn-vertical style="align-items: center;">
|
||||||
<a ui-sref="entry.card.buy.import"
|
<a ui-sref="entry.card.buy.import"
|
||||||
vn-bind="+"
|
vn-bind="+">
|
||||||
vn-acl="buyer"
|
|
||||||
vn-acl-action="remove">
|
|
||||||
<vn-button class="round md vn-mb-sm"
|
<vn-button class="round md vn-mb-sm"
|
||||||
icon="publish"
|
icon="publish"
|
||||||
vn-tooltip="Import buys"
|
vn-tooltip="Import buys"
|
||||||
|
@ -11,4 +191,14 @@
|
||||||
</vn-button>
|
</vn-button>
|
||||||
</a>
|
</a>
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
</div>
|
</div>
|
||||||
|
<vn-item-descriptor-popover
|
||||||
|
vn-id="itemDescriptor">
|
||||||
|
</vn-item-descriptor-popover>
|
||||||
|
|
||||||
|
<vn-confirm
|
||||||
|
vn-id="delete-buys"
|
||||||
|
question="You are going to delete buy(s) from this entry"
|
||||||
|
message="Continue anyway?"
|
||||||
|
on-accept="$ctrl.deleteBuys()">
|
||||||
|
</vn-confirm>
|
|
@ -1,9 +1,65 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
|
import './style.scss';
|
||||||
import Section from 'salix/components/section';
|
import Section from 'salix/components/section';
|
||||||
|
|
||||||
|
export default class Controller extends Section {
|
||||||
|
saveBuy(buy) {
|
||||||
|
const missingData = !buy.itemFk || !buy.quantity || !buy.packageFk;
|
||||||
|
if (missingData) return;
|
||||||
|
|
||||||
|
if (buy.id) {
|
||||||
|
const query = `Buys/${buy.id}`;
|
||||||
|
this.$http.patch(query, buy).then(res => {
|
||||||
|
if (!res.data) return;
|
||||||
|
|
||||||
|
buy = Object.assign(buy, res.data);
|
||||||
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const query = `Entries/${this.entry.id}/addBuy`;
|
||||||
|
this.$http.post(query, buy).then(res => {
|
||||||
|
if (!res.data) return;
|
||||||
|
|
||||||
|
buy = Object.assign(buy, res.data);
|
||||||
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns checked instances
|
||||||
|
*
|
||||||
|
* @return {Array} Checked instances
|
||||||
|
*/
|
||||||
|
selectedBuys() {
|
||||||
|
if (!this.buys) return;
|
||||||
|
|
||||||
|
return this.buys.filter(buy => {
|
||||||
|
return buy.checked;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteBuys() {
|
||||||
|
const buys = this.selectedBuys();
|
||||||
|
const actualInstances = buys.filter(buy => buy.id);
|
||||||
|
|
||||||
|
const params = {buys: actualInstances};
|
||||||
|
|
||||||
|
if (actualInstances.length) {
|
||||||
|
this.$http.post(`Buys/deleteBuys`, params).then(() => {
|
||||||
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
buys.forEach(buy => {
|
||||||
|
const index = this.buys.indexOf(buy);
|
||||||
|
this.buys.splice(index, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnEntryBuyIndex', {
|
ngModule.vnComponent('vnEntryBuyIndex', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
controller: Section,
|
controller: Controller,
|
||||||
bindings: {
|
bindings: {
|
||||||
entry: '<'
|
entry: '<'
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
import './index.js';
|
||||||
|
|
||||||
|
describe('Entry buy', () => {
|
||||||
|
let controller;
|
||||||
|
let $httpBackend;
|
||||||
|
|
||||||
|
beforeEach(ngModule('entry'));
|
||||||
|
|
||||||
|
beforeEach(angular.mock.inject(($componentController, $compile, $rootScope, _$httpParamSerializer_, _$httpBackend_) => {
|
||||||
|
$httpBackend = _$httpBackend_;
|
||||||
|
let $element = $compile('<vn-entry-buy-index></vn-entry-buy-index')($rootScope);
|
||||||
|
controller = $componentController('vnEntryBuyIndex', {$element});
|
||||||
|
$httpBackend.whenGET('Entries//getBuys?filter=%7B%7D').respond([{id: 1}]);
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('saveBuy()', () => {
|
||||||
|
it(`should call the buys patch route if the received buy has an ID`, () => {
|
||||||
|
const buy = {id: 1, itemFk: 1, quantity: 1, packageFk: 1};
|
||||||
|
|
||||||
|
const query = `Buys/${buy.id}`;
|
||||||
|
|
||||||
|
$httpBackend.expectPATCH(query).respond(200);
|
||||||
|
controller.saveBuy(buy);
|
||||||
|
$httpBackend.flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should call the entry addBuy post route if the received buy has no ID`, () => {
|
||||||
|
controller.entry = {id: 1};
|
||||||
|
const buy = {itemFk: 1, quantity: 1, packageFk: 1};
|
||||||
|
|
||||||
|
const query = `Entries/${controller.entry.id}/addBuy`;
|
||||||
|
|
||||||
|
$httpBackend.expectPOST(query).respond(200);
|
||||||
|
controller.saveBuy(buy);
|
||||||
|
$httpBackend.flush();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('deleteBuys()', () => {
|
||||||
|
it(`should perform no queries if all buys to delete were not actual instances`, () => {
|
||||||
|
controller.buys = [
|
||||||
|
{checked: true},
|
||||||
|
{checked: true},
|
||||||
|
{checked: false}];
|
||||||
|
|
||||||
|
controller.deleteBuys();
|
||||||
|
|
||||||
|
expect(controller.buys.length).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should perform a query to delete as there's an actual instance at least`, () => {
|
||||||
|
controller.buys = [
|
||||||
|
{checked: true, id: 1},
|
||||||
|
{checked: true},
|
||||||
|
{checked: false}];
|
||||||
|
|
||||||
|
const query = 'Buys/deleteBuys';
|
||||||
|
|
||||||
|
$httpBackend.expectPOST(query).respond(200);
|
||||||
|
controller.deleteBuys();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.buys.length).toEqual(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,28 @@
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
|
|
||||||
|
vn-entry-buy-index vn-card {
|
||||||
|
max-width: $width-xl;
|
||||||
|
|
||||||
|
.dark-row {
|
||||||
|
background-color: lighten($color-marginal, 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:nth-child(1) {
|
||||||
|
border-top: 1px solid $color-marginal;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody{
|
||||||
|
border-bottom: 1px solid $color-marginal;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:nth-child(3) {
|
||||||
|
height: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$color-font-link-medium: lighten($color-font-link, 20%)
|
|
@ -83,12 +83,12 @@
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number>
|
<vn-td number>
|
||||||
<vn-chip translate-attr="buy.groupingMode == 2 ? {title: 'Minimun amount'} : {title: 'Packing'}" ng-class="{'message': buy.groupingMode == 2}">
|
<vn-chip class="white" translate-attr="buy.groupingMode == 2 ? {title: 'Minimun amount'} : {title: 'Packing'}" ng-class="{'message': buy.groupingMode == 2}">
|
||||||
<span translate>{{::buy.packing | dashIfEmpty}}</span>
|
<span translate>{{::buy.packing | dashIfEmpty}}</span>
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number>
|
<vn-td number>
|
||||||
<vn-chip translate-attr="buy.groupingMode == 1 ? {title: 'Minimun amount'} : {title: 'Grouping'}" ng-class="{'message': buy.groupingMode == 1}">
|
<vn-chip class="white" translate-attr="buy.groupingMode == 1 ? {title: 'Minimun amount'} : {title: 'Grouping'}" ng-class="{'message': buy.groupingMode == 1}">
|
||||||
<span translate>{{::buy.grouping | dashIfEmpty}}</span>
|
<span translate>{{::buy.grouping | dashIfEmpty}}</span>
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
|
|
|
@ -87,7 +87,8 @@
|
||||||
"url": "/buy",
|
"url": "/buy",
|
||||||
"state": "entry.card.buy",
|
"state": "entry.card.buy",
|
||||||
"abstract": true,
|
"abstract": true,
|
||||||
"component": "ui-view"
|
"component": "ui-view",
|
||||||
|
"acl": ["buyer"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url" : "/index",
|
"url" : "/index",
|
||||||
|
|
|
@ -10,67 +10,67 @@
|
||||||
</h5>
|
</h5>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-one>
|
<vn-one>
|
||||||
<vn-label-value label="Commission"
|
<vn-label-value label="Commission"
|
||||||
value="{{$ctrl.entryData.commission}}">
|
value="{{$ctrl.entryData.commission}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Currency"
|
<vn-label-value label="Currency"
|
||||||
value="{{$ctrl.entryData.currency.name}}">
|
value="{{$ctrl.entryData.currency.name}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Company"
|
<vn-label-value label="Company"
|
||||||
value="{{$ctrl.entryData.company.code}}">
|
value="{{$ctrl.entryData.company.code}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Reference"
|
<vn-label-value label="Reference"
|
||||||
value="{{$ctrl.entryData.ref}}">
|
value="{{$ctrl.entryData.ref}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Notes"
|
<vn-label-value label="Notes"
|
||||||
value="{{$ctrl.entryData.notes}}">
|
value="{{$ctrl.entryData.notes}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
<vn-one>
|
<vn-one>
|
||||||
<vn-label-value label="Agency">
|
<vn-label-value label="Agency">
|
||||||
<span
|
<span
|
||||||
ng-click="travelDescriptor.show($event, $ctrl.entry.travel.agencyFk)"
|
ng-click="travelDescriptor.show($event, $ctrl.entry.travel.agencyFk)"
|
||||||
class="link">
|
class="link">
|
||||||
{{$ctrl.entryData.travel.agency.name}}
|
{{$ctrl.entryData.travel.agency.name}}
|
||||||
</span>
|
</span>
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Shipped"
|
<vn-label-value label="Shipped"
|
||||||
value="{{$ctrl.entryData.travel.shipped | date: 'dd/MM/yyyy'}}">
|
value="{{$ctrl.entryData.travel.shipped | date: 'dd/MM/yyyy'}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Warehouse Out"
|
<vn-label-value label="Warehouse Out"
|
||||||
value="{{$ctrl.entryData.travel.warehouseOut.name}}">
|
value="{{$ctrl.entryData.travel.warehouseOut.name}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Landed"
|
<vn-label-value label="Landed"
|
||||||
value="{{$ctrl.entryData.travel.landed | date: 'dd/MM/yyyy'}}">
|
value="{{$ctrl.entryData.travel.landed | date: 'dd/MM/yyyy'}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Warehouse In"
|
<vn-label-value label="Warehouse In"
|
||||||
value="{{$ctrl.entryData.travel.warehouseIn.name}}">
|
value="{{$ctrl.entryData.travel.warehouseIn.name}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
<vn-one>
|
<vn-one>
|
||||||
<vn-vertical>
|
<vn-vertical>
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Ordered"
|
label="Ordered"
|
||||||
ng-model="$ctrl.entryData.isOrdered"
|
ng-model="$ctrl.entryData.isOrdered"
|
||||||
disabled="true">
|
disabled="true">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Confirmed"
|
label="Confirmed"
|
||||||
ng-model="$ctrl.entryData.isConfirmed"
|
ng-model="$ctrl.entryData.isConfirmed"
|
||||||
disabled="true">
|
disabled="true">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Booked"
|
label="Booked"
|
||||||
ng-model="$ctrl.entryData.isBooked"
|
ng-model="$ctrl.entryData.isBooked"
|
||||||
disabled="true">
|
disabled="true">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Raid"
|
label="Raid"
|
||||||
ng-model="$ctrl.entryData.isRaid"
|
ng-model="$ctrl.entryData.isRaid"
|
||||||
disabled="true">
|
disabled="true">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Inventory"
|
label="Inventory"
|
||||||
ng-model="$ctrl.entryData.isInventory"
|
ng-model="$ctrl.entryData.isInventory"
|
||||||
disabled="true">
|
disabled="true">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
|
@ -102,12 +102,12 @@
|
||||||
<td center title="{{::line.packageFk | dashIfEmpty}}">{{::line.packageFk | dashIfEmpty}}</td>
|
<td center title="{{::line.packageFk | dashIfEmpty}}">{{::line.packageFk | dashIfEmpty}}</td>
|
||||||
<td center title="{{::line.weight}}">{{::line.weight}}</td>
|
<td center title="{{::line.weight}}">{{::line.weight}}</td>
|
||||||
<td center>
|
<td center>
|
||||||
<vn-chip translate-attr="line.groupingMode == 2 ? {title: 'Minimun amount'} : {title: 'Packing'}" ng-class="{'message': line.groupingMode == 2}">
|
<vn-chip class="white" translate-attr="line.groupingMode == 2 ? {title: 'Minimun amount'} : {title: 'Packing'}" ng-class="{'message': line.groupingMode == 2}">
|
||||||
<span translate>{{::line.packing | dashIfEmpty}}</span>
|
<span translate>{{::line.packing | dashIfEmpty}}</span>
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
</td>
|
</td>
|
||||||
<td center>
|
<td center>
|
||||||
<vn-chip translate-attr="line.groupingMode == 1 ? {title: 'Minimun amount'} : {title: 'Grouping'}" ng-class="{'message': line.groupingMode == 1}">
|
<vn-chip class="white" translate-attr="line.groupingMode == 1 ? {title: 'Minimun amount'} : {title: 'Grouping'}" ng-class="{'message': line.groupingMode == 1}">
|
||||||
<span translate>{{::line.grouping | dashIfEmpty}}</span>
|
<span translate>{{::line.grouping | dashIfEmpty}}</span>
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
|
|
|
@ -91,81 +91,81 @@
|
||||||
vn-tooltip="{{::$ctrl.$t('Reserved')}}">
|
vn-tooltip="{{::$ctrl.$t('Reserved')}}">
|
||||||
</vn-icon>
|
</vn-icon>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink>
|
<vn-td shrink>
|
||||||
<img
|
<img
|
||||||
ng-src="{{::$root.imagePath('catalog', '50x50', sale.itemFk)}}"
|
ng-src="{{::$root.imagePath('catalog', '50x50', sale.itemFk)}}"
|
||||||
zoom-image="{{::$root.imagePath('catalog', '1600x900', sale.itemFk)}}"
|
zoom-image="{{::$root.imagePath('catalog', '1600x900', sale.itemFk)}}"
|
||||||
on-error-src/>
|
on-error-src/>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink>
|
<vn-td shrink>
|
||||||
<span class="link" ng-if="sale.id"
|
<span class="link" ng-if="sale.id"
|
||||||
ng-click="descriptor.show($event, sale.itemFk, sale.id)">
|
ng-click="descriptor.show($event, sale.itemFk, sale.id)">
|
||||||
{{sale.itemFk}}
|
{{sale.itemFk}}
|
||||||
</span>
|
</span>
|
||||||
<vn-autocomplete ng-if="!sale.id" class="dense"
|
<vn-autocomplete ng-if="!sale.id" class="dense"
|
||||||
vn-focus
|
|
||||||
url="Items"
|
|
||||||
ng-model="sale.itemFk"
|
|
||||||
show-field="name"
|
|
||||||
value-field="id"
|
|
||||||
search-function="$ctrl.itemSearchFunc($search)"
|
|
||||||
on-change="$ctrl.changeQuantity(sale)"
|
|
||||||
order="id DESC"
|
|
||||||
tabindex="1">
|
|
||||||
<tpl-item>
|
|
||||||
{{::id}} - {{::name}}
|
|
||||||
</tpl-item>
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-td>
|
|
||||||
<vn-td-editable disabled="!$ctrl.isEditable" shrink>
|
|
||||||
<text>{{sale.quantity}}</text>
|
|
||||||
<field>
|
|
||||||
<vn-input-number class="dense"
|
|
||||||
vn-focus
|
vn-focus
|
||||||
ng-model="sale.quantity"
|
url="Items"
|
||||||
on-change="$ctrl.changeQuantity(sale)">
|
ng-model="sale.itemFk"
|
||||||
</vn-input-number>
|
show-field="name"
|
||||||
</field>
|
value-field="id"
|
||||||
</vn-td-editable>
|
search-function="$ctrl.itemSearchFunc($search)"
|
||||||
<vn-td-editable vn-fetched-tags wide disabled="!sale.id || !$ctrl.isEditable">
|
on-change="$ctrl.changeQuantity(sale)"
|
||||||
<text>
|
order="id DESC"
|
||||||
<vn-one title="{{sale.concept}}">{{sale.concept}}</vn-one>
|
tabindex="1">
|
||||||
<vn-one ng-if="::sale.subName">
|
<tpl-item>
|
||||||
<h3 title="{{::sale.subName}}">{{::sale.subName}}</h3>
|
{{::id}} - {{::name}}
|
||||||
</vn-one>
|
</tpl-item>
|
||||||
<vn-fetched-tags
|
</vn-autocomplete>
|
||||||
max-length="6"
|
</vn-td>
|
||||||
item="::sale.item"
|
<vn-td-editable disabled="!$ctrl.isEditable" shrink>
|
||||||
tabindex="-1">
|
<text>{{sale.quantity}}</text>
|
||||||
</vn-fetched-tags>
|
<field>
|
||||||
</text>
|
<vn-input-number class="dense"
|
||||||
<field>
|
vn-focus
|
||||||
<vn-textfield class="dense" vn-focus
|
ng-model="sale.quantity"
|
||||||
vn-id="concept"
|
on-change="$ctrl.changeQuantity(sale)">
|
||||||
ng-model="sale.concept"
|
</vn-input-number>
|
||||||
on-change="$ctrl.updateConcept(sale)">
|
</field>
|
||||||
</vn-textfield>
|
</vn-td-editable>
|
||||||
</field>
|
<vn-td-editable vn-fetched-tags wide disabled="!sale.id || !$ctrl.isEditable">
|
||||||
</vn-td-editable>
|
<text>
|
||||||
<vn-td number>
|
<vn-one title="{{sale.concept}}">{{sale.concept}}</vn-one>
|
||||||
<span ng-class="{'link': $ctrl.isEditable}"
|
<vn-one ng-if="::sale.subName">
|
||||||
translate-attr="{title: $ctrl.isEditable ? 'Edit price' : ''}"
|
<h3 title="{{::sale.subName}}">{{::sale.subName}}</h3>
|
||||||
ng-click="$ctrl.showEditPricePopover($event, sale)">
|
</vn-one>
|
||||||
{{sale.price | currency: 'EUR':2}}
|
<vn-fetched-tags
|
||||||
</span>
|
max-length="6"
|
||||||
</vn-td>
|
item="::sale.item"
|
||||||
<vn-td number>
|
tabindex="-1">
|
||||||
<span ng-class="{'link': !$ctrl.isLocked}"
|
</vn-fetched-tags>
|
||||||
translate-attr="{title: !$ctrl.isLocked ? 'Edit discount' : ''}"
|
</text>
|
||||||
ng-click="$ctrl.showEditDiscountPopover($event, sale)"
|
<field>
|
||||||
ng-if="sale.id">
|
<vn-textfield class="dense" vn-focus
|
||||||
{{(sale.discount / 100) | percentage}}
|
vn-id="concept"
|
||||||
</span>
|
ng-model="sale.concept"
|
||||||
</vn-td>
|
on-change="$ctrl.updateConcept(sale)">
|
||||||
<vn-td number>
|
</vn-textfield>
|
||||||
{{$ctrl.getSaleTotal(sale) | currency: 'EUR':2}}
|
</field>
|
||||||
</vn-td>
|
</vn-td-editable>
|
||||||
</vn-tr>
|
<vn-td number>
|
||||||
|
<span ng-class="{'link': $ctrl.isEditable}"
|
||||||
|
translate-attr="{title: $ctrl.isEditable ? 'Edit price' : ''}"
|
||||||
|
ng-click="$ctrl.showEditPricePopover($event, sale)">
|
||||||
|
{{sale.price | currency: 'EUR':2}}
|
||||||
|
</span>
|
||||||
|
</vn-td>
|
||||||
|
<vn-td number>
|
||||||
|
<span ng-class="{'link': !$ctrl.isLocked}"
|
||||||
|
translate-attr="{title: !$ctrl.isLocked ? 'Edit discount' : ''}"
|
||||||
|
ng-click="$ctrl.showEditDiscountPopover($event, sale)"
|
||||||
|
ng-if="sale.id">
|
||||||
|
{{(sale.discount / 100) | percentage}}
|
||||||
|
</span>
|
||||||
|
</vn-td>
|
||||||
|
<vn-td number>
|
||||||
|
{{$ctrl.getSaleTotal(sale) | currency: 'EUR':2}}
|
||||||
|
</vn-td>
|
||||||
|
</vn-tr>
|
||||||
</vn-tbody>
|
</vn-tbody>
|
||||||
</vn-table>
|
</vn-table>
|
||||||
<div>
|
<div>
|
||||||
|
|
Loading…
Reference in New Issue