Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2817-translate_ticket_request_message
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
d2879087ff
|
@ -2178,6 +2178,15 @@ INSERT INTO `hedera`.`imageCollectionSize`(`id`, `collectionFk`,`width`, `height
|
||||||
VALUES
|
VALUES
|
||||||
(1, 4, 160, 160);
|
(1, 4, 160, 160);
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`rateConfig`(`rate0`, `rate1`, `rate2`, `rate3`)
|
||||||
|
VALUES
|
||||||
|
(36, 31, 25, 21);
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`rate`(`dated`, `warehouseFk`, `rate0`, `rate1`, `rate2`, `rate3`)
|
||||||
|
VALUES
|
||||||
|
(DATE_ADD(CURDATE(), INTERVAL -1 YEAR), 1, 10, 15, 20, 25),
|
||||||
|
(CURDATE(), 1, 12, 17, 22, 27);
|
||||||
|
|
||||||
INSERT INTO `vn`.`awb` (id, code, package, weight, created, amount, transitoryFk, taxFk)
|
INSERT INTO `vn`.`awb` (id, code, package, weight, created, amount, transitoryFk, taxFk)
|
||||||
VALUES
|
VALUES
|
||||||
(1, '07546501420', 67, 671, CURDATE(), 1761, 1, 1),
|
(1, '07546501420', 67, 671, CURDATE(), 1761, 1, 1),
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
{
|
||||||
|
"invoices": [
|
||||||
|
{
|
||||||
|
"tx_company": "TESSAROSES S.A.",
|
||||||
|
"id_invoice": "20062926",
|
||||||
|
"id_purchaseorder": "20106319",
|
||||||
|
"tx_customer_ref": "",
|
||||||
|
"id_customer": "56116",
|
||||||
|
"id_customer_floricode": "",
|
||||||
|
"nm_bill": "VERDNATURA LEVANTE SL",
|
||||||
|
"nm_ship": "VERDNATURA LEVANTE SL",
|
||||||
|
"nm_cargo": "OYAMBARILLO",
|
||||||
|
"dt_purchaseorder": "06/19/2020",
|
||||||
|
"dt_fly": "06/20/2020",
|
||||||
|
"dt_invoice": "06/19/2020",
|
||||||
|
"nm_incoterm": "FOB UIO",
|
||||||
|
"tx_awb": "729-6340 2846",
|
||||||
|
"tx_hawb": "LA0061832844",
|
||||||
|
"tx_oe": "05520204000335992",
|
||||||
|
"nu_totalstemsPO": "850",
|
||||||
|
"mny_flower": "272.5000",
|
||||||
|
"mny_freight": "0.0000",
|
||||||
|
"mny_total": "272.5000",
|
||||||
|
"nu_boxes": "4",
|
||||||
|
"nu_fulls": "1.75",
|
||||||
|
"dt_posted": "2020-06-19T13:31:41",
|
||||||
|
"boxes": [
|
||||||
|
{
|
||||||
|
"id_box": "200573095",
|
||||||
|
"nm_box": "HB",
|
||||||
|
"tp_box": "HB",
|
||||||
|
"tx_label": "",
|
||||||
|
"nu_length": "96",
|
||||||
|
"nu_width": "32",
|
||||||
|
"nu_height": "30.5",
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"id_floricode": "27887",
|
||||||
|
"id_migros_variety": "",
|
||||||
|
"nm_product": "FREEDOM 60CM 25ST",
|
||||||
|
"nm_species": "ROSES",
|
||||||
|
"nm_variety": "FREEDOM",
|
||||||
|
"nu_length": "60",
|
||||||
|
"nu_stems_bunch": "25",
|
||||||
|
"nu_bunches": "10",
|
||||||
|
"mny_rate_stem": "0.3500",
|
||||||
|
"mny_freight_unit": "0.0000",
|
||||||
|
"barcodes": "202727621,202725344,202725345,202725571,202725730,202725731,202725732,202725925,202726131,202726685"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id_box": "200573106",
|
||||||
|
"nm_box": "HB",
|
||||||
|
"tp_box": "HB",
|
||||||
|
"tx_label": "",
|
||||||
|
"nu_length": "96",
|
||||||
|
"nu_width": "32",
|
||||||
|
"nu_height": "30.5",
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"id_floricode": "27887",
|
||||||
|
"id_migros_variety": "",
|
||||||
|
"nm_product": "FREEDOM 70CM 25ST",
|
||||||
|
"nm_species": "ROSES",
|
||||||
|
"nm_variety": "FREEDOM",
|
||||||
|
"nu_length": "70",
|
||||||
|
"nu_stems_bunch": "25",
|
||||||
|
"nu_bunches": "8",
|
||||||
|
"mny_rate_stem": "0.4000",
|
||||||
|
"mny_freight_unit": "0.0000",
|
||||||
|
"barcodes": "202727077,202727078,202727079,202727080,202727650,202727654,202727656,202727657"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id_box": "200573117",
|
||||||
|
"nm_box": "HB",
|
||||||
|
"tp_box": "HB",
|
||||||
|
"tx_label": "",
|
||||||
|
"nu_length": "96",
|
||||||
|
"nu_width": "32",
|
||||||
|
"nu_height": "30.5",
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"id_floricode": "28409",
|
||||||
|
"id_migros_variety": "",
|
||||||
|
"nm_product": "TIBET 40CM 25ST",
|
||||||
|
"nm_species": "ROSES",
|
||||||
|
"nm_variety": "TIBET",
|
||||||
|
"nu_length": "40",
|
||||||
|
"nu_stems_bunch": "25",
|
||||||
|
"nu_bunches": "12",
|
||||||
|
"mny_rate_stem": "0.2500",
|
||||||
|
"mny_freight_unit": "0.0000",
|
||||||
|
"barcodes": "202723350,202723351,202723352,202723353,202723354,202723355,202723356,202723357,202726690,202726745,202726813,202726814"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id_box": "200573506",
|
||||||
|
"nm_box": "QB 2",
|
||||||
|
"tp_box": "QB",
|
||||||
|
"tx_label": "",
|
||||||
|
"nu_length": "80",
|
||||||
|
"nu_width": "30",
|
||||||
|
"nu_height": "17.5",
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"id_floricode": "27887",
|
||||||
|
"id_migros_variety": "",
|
||||||
|
"nm_product": "FREEDOM 50CM 25ST",
|
||||||
|
"nm_species": "ROSES",
|
||||||
|
"nm_variety": "FREEDOM",
|
||||||
|
"nu_length": "50",
|
||||||
|
"nu_stems_bunch": "25",
|
||||||
|
"nu_bunches": "4",
|
||||||
|
"mny_rate_stem": "0.3000",
|
||||||
|
"mny_freight_unit": "0.0000",
|
||||||
|
"barcodes": "202727837,202727839,202727842,202726682"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
@ -1015,6 +1015,17 @@ export default {
|
||||||
travelsQuicklink: 'vn-entry-descriptor vn-quick-link[icon="local_airport"] > a',
|
travelsQuicklink: 'vn-entry-descriptor vn-quick-link[icon="local_airport"] > a',
|
||||||
entriesQuicklink: 'vn-entry-descriptor vn-quick-link[icon="icon-entry"] > a'
|
entriesQuicklink: 'vn-entry-descriptor vn-quick-link[icon="icon-entry"] > a'
|
||||||
},
|
},
|
||||||
|
entryBuys: {
|
||||||
|
importButton: 'vn-entry-buy-index vn-icon[icon="publish"]',
|
||||||
|
ref: 'vn-entry-buy-import vn-textfield[ng-model="$ctrl.import.ref"]',
|
||||||
|
observation: 'vn-entry-buy-import vn-textarea[ng-model="$ctrl.import.observation"]',
|
||||||
|
file: 'vn-entry-buy-import vn-input-file[ng-model="$ctrl.import.file"]',
|
||||||
|
firstImportedItem: 'vn-entry-buy-import tbody:nth-child(2) vn-autocomplete[ng-model="buy.itemFk"]',
|
||||||
|
secondImportedItem: 'vn-entry-buy-import tbody:nth-child(3) vn-autocomplete[ng-model="buy.itemFk"]',
|
||||||
|
thirdImportedItem: 'vn-entry-buy-import tbody:nth-child(4) vn-autocomplete[ng-model="buy.itemFk"]',
|
||||||
|
fourthImportedItem: 'vn-entry-buy-import tbody:nth-child(5) vn-autocomplete[ng-model="buy.itemFk"]',
|
||||||
|
importBuysButton: 'vn-entry-buy-import button[type="submit"]'
|
||||||
|
},
|
||||||
entryLatestBuys: {
|
entryLatestBuys: {
|
||||||
firstBuy: 'vn-entry-latest-buys vn-tbody > a:nth-child(1)',
|
firstBuy: 'vn-entry-latest-buys vn-tbody > a:nth-child(1)',
|
||||||
allBuysCheckBox: 'vn-entry-latest-buys vn-thead vn-check',
|
allBuysCheckBox: 'vn-entry-latest-buys vn-thead vn-check',
|
||||||
|
|
|
@ -38,7 +38,7 @@ describe('Travel thermograph path', () => {
|
||||||
|
|
||||||
it('should select the file to upload', async() => {
|
it('should select the file to upload', async() => {
|
||||||
let currentDir = process.cwd();
|
let currentDir = process.cwd();
|
||||||
let filePath = `${currentDir}/storage/dms/ecc/3.jpeg`;
|
let filePath = `${currentDir}/e2e/assets/thermograph.jpeg`;
|
||||||
|
|
||||||
const [fileChooser] = await Promise.all([
|
const [fileChooser] = await Promise.all([
|
||||||
page.waitForFileChooser(),
|
page.waitForFileChooser(),
|
||||||
|
|
|
@ -41,6 +41,6 @@ describe('Entry lastest buys path', () => {
|
||||||
|
|
||||||
it('should navigate to the entry.buy section by clicking one of the buys', async() => {
|
it('should navigate to the entry.buy section by clicking one of the buys', async() => {
|
||||||
await page.waitToClick(selectors.entryLatestBuys.firstBuy);
|
await page.waitToClick(selectors.entryLatestBuys.firstBuy);
|
||||||
await page.waitForState('entry.card.buy');
|
await page.waitForState('entry.card.buy.index');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
import selectors from '../../helpers/selectors.js';
|
||||||
|
import getBrowser from '../../helpers/puppeteer';
|
||||||
|
|
||||||
|
describe('Entry import buys path', () => {
|
||||||
|
let browser;
|
||||||
|
let page;
|
||||||
|
|
||||||
|
beforeAll(async() => {
|
||||||
|
browser = await getBrowser();
|
||||||
|
page = browser.page;
|
||||||
|
await page.loginAndModule('buyer', 'entry');
|
||||||
|
await page.accessToSearchResult('1');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async() => {
|
||||||
|
await browser.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should count the summary buys and find there only one at this point', async() => {
|
||||||
|
const buysCount = await page.countElement(selectors.entrySummary.anyBuyLine);
|
||||||
|
|
||||||
|
expect(buysCount).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should navigate to the buy section and then click the import button opening the import form', async() => {
|
||||||
|
await page.accessToSection('entry.card.buy.index');
|
||||||
|
await page.waitToClick(selectors.entryBuys.importButton);
|
||||||
|
await page.waitForState('entry.card.buy.import');
|
||||||
|
});
|
||||||
|
|
||||||
|
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`;
|
||||||
|
|
||||||
|
const [fileChooser] = await Promise.all([
|
||||||
|
page.waitForFileChooser(),
|
||||||
|
page.waitToClick(selectors.entryBuys.file)
|
||||||
|
]);
|
||||||
|
await fileChooser.accept([filePath]);
|
||||||
|
|
||||||
|
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');
|
||||||
|
await page.autocompleteSearch(selectors.entryBuys.fourthImportedItem, 'Container ammo box 1m');
|
||||||
|
|
||||||
|
await page.waitToClick(selectors.entryBuys.importBuysButton);
|
||||||
|
|
||||||
|
const message = await page.waitForSnackbar();
|
||||||
|
const state = await page.getState();
|
||||||
|
|
||||||
|
expect(message.text).toContain('Data saved!');
|
||||||
|
expect(state).toBe('entry.card.buy.index');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should navigate to the entry summary and count the buys to find 4 buys have been added', async() => {
|
||||||
|
await page.waitToClick('vn-icon[icon="preview"]');
|
||||||
|
await page.waitForNumberOfElements(selectors.entrySummary.anyBuyLine, 5);
|
||||||
|
});
|
||||||
|
});
|
|
@ -20,7 +20,8 @@ export default class Field extends FormInput {
|
||||||
super.$onInit();
|
super.$onInit();
|
||||||
|
|
||||||
if (this.info) this.classList.add('has-icons');
|
if (this.info) this.classList.add('has-icons');
|
||||||
this.input.addEventListener('change', () => this.onChange());
|
this.input.addEventListener('change', event =>
|
||||||
|
this.onChange(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
set field(value) {
|
set field(value) {
|
||||||
|
@ -82,6 +83,9 @@ export default class Field extends FormInput {
|
||||||
this._required = value;
|
this._required = value;
|
||||||
let required = this.element.querySelector('.required');
|
let required = this.element.querySelector('.required');
|
||||||
display(required, this._required);
|
display(required, this._required);
|
||||||
|
|
||||||
|
this.$.$applyAsync(() =>
|
||||||
|
this.input.setAttribute('required', value));
|
||||||
}
|
}
|
||||||
|
|
||||||
get required() {
|
get required() {
|
||||||
|
@ -186,10 +190,13 @@ export default class Field extends FormInput {
|
||||||
this.refreshHint();
|
this.refreshHint();
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange() {
|
onChange($event) {
|
||||||
// Changes doesn't reflect until appling async
|
// Changes doesn't reflect until appling async
|
||||||
this.$.$applyAsync(() => {
|
this.$.$applyAsync(() => {
|
||||||
this.emit('change', {value: this.field});
|
this.emit('change', {
|
||||||
|
value: this.field,
|
||||||
|
$event: $event
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,12 +71,23 @@ export default class InputFile extends Field {
|
||||||
this.input.click();
|
this.input.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange() {
|
onChange($event) {
|
||||||
this.emit('change', {
|
this.emit('change', {
|
||||||
value: this.field,
|
value: this.field,
|
||||||
$files: this.files
|
$files: this.files,
|
||||||
|
$event: $event
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get accept() {
|
||||||
|
return this._accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
set accept(value) {
|
||||||
|
this._accept = value;
|
||||||
|
this.$.$applyAsync(() =>
|
||||||
|
this.input.setAttribute('accept', value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnInputFile', {
|
ngModule.vnComponent('vnInputFile', {
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
|
||||||
|
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('importBuys', {
|
||||||
|
description: 'Imports the buys from a list',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The entry id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'options',
|
||||||
|
type: 'object',
|
||||||
|
description: 'Callback options',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'ref',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The buyed boxes ids',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'observation',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The observation',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'buys',
|
||||||
|
type: ['Object'],
|
||||||
|
description: 'The buys',
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['Object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/importBuys`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.importBuys = async(ctx, id, options = {}) => {
|
||||||
|
const conn = Self.dataSource.connector;
|
||||||
|
const args = ctx.args;
|
||||||
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
let tx;
|
||||||
|
if (!options.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
options.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const entry = await models.Entry.findById(id, null, options);
|
||||||
|
await entry.updateAttributes({
|
||||||
|
observation: args.observation,
|
||||||
|
ref: args.ref
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
const buys = [];
|
||||||
|
for (let buy of args.buys) {
|
||||||
|
buys.push({
|
||||||
|
entryFk: entry.id,
|
||||||
|
itemFk: buy.itemFk,
|
||||||
|
stickers: 1,
|
||||||
|
quantity: 1,
|
||||||
|
packing: buy.packing,
|
||||||
|
grouping: buy.grouping,
|
||||||
|
buyingValue: buy.buyingValue,
|
||||||
|
packageFk: buy.packageFk
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdBuys = await models.Buy.create(buys, options);
|
||||||
|
const buyIds = createdBuys.map(buy => buy.id);
|
||||||
|
|
||||||
|
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`, [buyIds]);
|
||||||
|
|
||||||
|
stmts.push(stmt);
|
||||||
|
stmts.push('CALL buy_recalcPrices()');
|
||||||
|
|
||||||
|
const sql = ParameterizedSQL.join(stmts, ';');
|
||||||
|
await conn.executeStmt(sql, options);
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,40 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('importBuysPreview', {
|
||||||
|
description: 'Calculates the preview buys for an entry import',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The entry id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'buys',
|
||||||
|
type: ['Object'],
|
||||||
|
description: 'The buys',
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['Object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/importBuysPreview`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.importBuysPreview = async(id, buys) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
for (let buy of buys) {
|
||||||
|
const packaging = await models.Packaging.findOne({
|
||||||
|
fields: ['id'],
|
||||||
|
where: {volume: {gte: buy.volume}},
|
||||||
|
order: 'volume ASC'
|
||||||
|
});
|
||||||
|
buy.packageFk = packaging.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buys;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,80 @@
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
|
describe('entry import()', () => {
|
||||||
|
let newEntry;
|
||||||
|
const buyerId = 35;
|
||||||
|
const companyId = 442;
|
||||||
|
const travelId = 1;
|
||||||
|
const supplierId = 1;
|
||||||
|
const activeCtx = {
|
||||||
|
accessToken: {userId: buyerId},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(async done => {
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should import the buy rows', async() => {
|
||||||
|
const expectedRef = '1, 2';
|
||||||
|
const expectedObservation = '123456';
|
||||||
|
const ctx = {
|
||||||
|
req: activeCtx,
|
||||||
|
args: {
|
||||||
|
observation: expectedObservation,
|
||||||
|
ref: expectedRef,
|
||||||
|
buys: [
|
||||||
|
{
|
||||||
|
itemFk: 1,
|
||||||
|
buyingValue: 5.77,
|
||||||
|
description: 'Bow',
|
||||||
|
grouping: 1,
|
||||||
|
packing: 1,
|
||||||
|
size: 1,
|
||||||
|
volume: 1200,
|
||||||
|
packageFk: '94'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
itemFk: 4,
|
||||||
|
buyingValue: 2.16,
|
||||||
|
description: 'Arrow',
|
||||||
|
grouping: 1,
|
||||||
|
packing: 1,
|
||||||
|
size: 25,
|
||||||
|
volume: 1125,
|
||||||
|
packageFk: '94'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const tx = await app.models.Entry.beginTransaction({});
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
newEntry = await app.models.Entry.create({
|
||||||
|
dated: new Date(),
|
||||||
|
supplierFk: supplierId,
|
||||||
|
travelFk: travelId,
|
||||||
|
companyFk: companyId,
|
||||||
|
observation: 'The entry',
|
||||||
|
ref: 'Entry ref'
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
await app.models.Entry.importBuys(ctx, newEntry.id, options);
|
||||||
|
|
||||||
|
const updatedEntry = await app.models.Entry.findById(newEntry.id, null, options);
|
||||||
|
const entryBuys = await app.models.Buy.find({
|
||||||
|
where: {entryFk: newEntry.id}
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
expect(updatedEntry.observation).toEqual(expectedObservation);
|
||||||
|
expect(updatedEntry.ref).toEqual(expectedRef);
|
||||||
|
expect(entryBuys.length).toEqual(2);
|
||||||
|
|
||||||
|
// Restores
|
||||||
|
await tx.rollback();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,41 @@
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
|
describe('entry importBuysPreview()', () => {
|
||||||
|
const entryId = 1;
|
||||||
|
beforeAll(async done => {
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the buys with the calculated packageFk', async() => {
|
||||||
|
const expectedPackageFk = '3';
|
||||||
|
const buys = [
|
||||||
|
{
|
||||||
|
itemFk: 1,
|
||||||
|
buyingValue: 5.77,
|
||||||
|
description: 'Bow',
|
||||||
|
grouping: 1,
|
||||||
|
size: 1,
|
||||||
|
volume: 1200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
itemFk: 4,
|
||||||
|
buyingValue: 2.16,
|
||||||
|
description: 'Arrow',
|
||||||
|
grouping: 1,
|
||||||
|
size: 25,
|
||||||
|
volume: 1125
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = await app.models.Entry.importBuysPreview(entryId, buys);
|
||||||
|
const randomIndex = Math.floor(Math.random() * result.length);
|
||||||
|
const buy = result[randomIndex];
|
||||||
|
|
||||||
|
expect(buy.packageFk).toEqual(expectedPackageFk);
|
||||||
|
});
|
||||||
|
});
|
|
@ -2,4 +2,6 @@ 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/importBuys')(Self);
|
||||||
|
require('../methods/entry/importBuysPreview')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
"notes": {
|
"notes": {
|
||||||
"type": "String"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"isConfirmed": {
|
"isConfirmed": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
<mg-ajax path="dms/upload" options="vnPost"></mg-ajax>
|
||||||
|
<vn-watcher
|
||||||
|
vn-id="watcher"
|
||||||
|
data="$ctrl.dms">
|
||||||
|
</vn-watcher>
|
||||||
|
<form
|
||||||
|
name="form"
|
||||||
|
ng-submit="$ctrl.onSubmit()"
|
||||||
|
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
|
||||||
|
label="File"
|
||||||
|
ng-model="$ctrl.import.file"
|
||||||
|
on-change="$ctrl.onFileChange($event)"
|
||||||
|
accept="application/json"
|
||||||
|
required="true">
|
||||||
|
<append>
|
||||||
|
<vn-icon vn-none
|
||||||
|
color-marginal
|
||||||
|
title="{{'JSON files only' | translate}}"
|
||||||
|
icon="info">
|
||||||
|
</vn-icon>
|
||||||
|
</append>
|
||||||
|
</vn-input-file>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal ng-show="$ctrl.import.buys.length > 0">
|
||||||
|
<table class="vn-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th translate>Item</th>
|
||||||
|
<th translate expand>Description</th>
|
||||||
|
<th translate center>Size</th>
|
||||||
|
<th translate center>Packing</th>
|
||||||
|
<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">
|
||||||
|
<tr>
|
||||||
|
<td title="{{::buy.itemFk}}">
|
||||||
|
<vn-autocomplete
|
||||||
|
class="dense"
|
||||||
|
vn-focus
|
||||||
|
url="Items"
|
||||||
|
ng-model="buy.itemFk"
|
||||||
|
show-field="name"
|
||||||
|
value-field="id"
|
||||||
|
search-function="$ctrl.itemSearchFunc($search)"
|
||||||
|
order="id DESC"
|
||||||
|
tabindex="1">
|
||||||
|
<tpl-item>
|
||||||
|
{{::id}} - {{::name}}
|
||||||
|
</tpl-item>
|
||||||
|
</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>{{::buy.buyingValue | currency: 'EUR':2}}</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">
|
||||||
|
</vn-autocomplete>
|
||||||
|
</td>
|
||||||
|
<td center title="{{::buy.volume}}">{{::buy.volume | number}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</vn-horizontal>
|
||||||
|
</vn-card>
|
||||||
|
<vn-button-bar>
|
||||||
|
<vn-submit
|
||||||
|
label="Import buys">
|
||||||
|
</vn-submit>
|
||||||
|
<vn-button
|
||||||
|
class="cancel"
|
||||||
|
label="Cancel"
|
||||||
|
ui-sref="entry.card.buy.index">
|
||||||
|
</vn-button>
|
||||||
|
</vn-button>
|
||||||
|
</vn-button-bar>
|
||||||
|
</div>
|
||||||
|
</form>
|
|
@ -0,0 +1,99 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
import Section from 'salix/components/section';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
class Controller extends Section {
|
||||||
|
constructor($element, $) {
|
||||||
|
super($element, $);
|
||||||
|
this.import = {
|
||||||
|
file: '',
|
||||||
|
invoice: null,
|
||||||
|
buys: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileChange($event) {
|
||||||
|
const input = $event.target;
|
||||||
|
const file = input.files[0];
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = event =>
|
||||||
|
this.fillData(event.target.result);
|
||||||
|
reader.readAsText(file, 'UTF-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
fillData(raw) {
|
||||||
|
const data = JSON.parse(raw);
|
||||||
|
const [invoice] = data.invoices;
|
||||||
|
|
||||||
|
this.$.$applyAsync(() => {
|
||||||
|
this.import.observation = invoice.tx_awb;
|
||||||
|
|
||||||
|
const boxes = invoice.boxes;
|
||||||
|
const buys = [];
|
||||||
|
for (let box of boxes) {
|
||||||
|
const boxVolume = box.nu_length * box.nu_width * box.nu_height;
|
||||||
|
for (let product of box.products) {
|
||||||
|
const packing = product.nu_stems_bunch * product.nu_bunches;
|
||||||
|
buys.push({
|
||||||
|
description: product.nm_product,
|
||||||
|
size: product.nu_length,
|
||||||
|
packing: packing,
|
||||||
|
grouping: product.nu_stems_bunch,
|
||||||
|
buyingValue: parseFloat(product.mny_rate_stem),
|
||||||
|
volume: boxVolume
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const boxesId = boxes.map(box => box.id_box);
|
||||||
|
this.import.ref = boxesId.join(', ');
|
||||||
|
|
||||||
|
this.fetchBuys(buys);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchBuys(buys) {
|
||||||
|
const params = {buys};
|
||||||
|
const query = `Entries/${this.entry.id}/importBuysPreview`;
|
||||||
|
this.$http.get(query, {params}).then(res => {
|
||||||
|
this.import.buys = res.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit() {
|
||||||
|
try {
|
||||||
|
const params = this.import;
|
||||||
|
const hasAnyEmptyRow = params.buys.some(buy => {
|
||||||
|
return buy.itemFk == null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasAnyEmptyRow)
|
||||||
|
throw new Error(`Some of the imported buys doesn't have an item`);
|
||||||
|
|
||||||
|
const query = `Entries/${this.entry.id}/importBuys`;
|
||||||
|
return this.$http.post(query, params)
|
||||||
|
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')))
|
||||||
|
.then(() => this.$state.go('entry.card.buy.index'));
|
||||||
|
} catch (e) {
|
||||||
|
this.vnApp.showError(this.$t(e.message));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
itemSearchFunc($search) {
|
||||||
|
return /^\d+$/.test($search)
|
||||||
|
? {id: $search}
|
||||||
|
: {name: {like: '%' + $search + '%'}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller.$inject = ['$element', '$scope'];
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnEntryBuyImport', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Controller,
|
||||||
|
bindings: {
|
||||||
|
worker: '<'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,145 @@
|
||||||
|
import './index.js';
|
||||||
|
|
||||||
|
describe('Entry', () => {
|
||||||
|
describe('Component vnEntryBuyImport', () => {
|
||||||
|
let controller;
|
||||||
|
let $httpParamSerializer;
|
||||||
|
let $httpBackend;
|
||||||
|
|
||||||
|
beforeEach(ngModule('entry'));
|
||||||
|
|
||||||
|
beforeEach(angular.mock.inject(($componentController, $compile, $rootScope, _$httpParamSerializer_, _$httpBackend_) => {
|
||||||
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpParamSerializer = _$httpParamSerializer_;
|
||||||
|
let $element = $compile('<vn-entry-buy-import-buys></vn-entry-latest-buys')($rootScope);
|
||||||
|
controller = $componentController('vnEntryBuyImport', {$element});
|
||||||
|
controller.entry = {
|
||||||
|
id: 1
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('fillData()', () => {
|
||||||
|
it(`should call to the fillData() method`, () => {
|
||||||
|
controller.fetchBuys = jest.fn();
|
||||||
|
|
||||||
|
const rawData = `{
|
||||||
|
"invoices": [
|
||||||
|
{
|
||||||
|
"tx_awb": "123456",
|
||||||
|
"boxes": [
|
||||||
|
{
|
||||||
|
"id_box": 1,
|
||||||
|
"nu_length": 1,
|
||||||
|
"nu_width": 15,
|
||||||
|
"nu_height": 80,
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"nm_product": "Bow",
|
||||||
|
"nu_length": 1,
|
||||||
|
"nu_stems_bunch": 1,
|
||||||
|
"nu_bunches": 1,
|
||||||
|
"mny_rate_stem": 5.77
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id_box": 2,
|
||||||
|
"nu_length": 25,
|
||||||
|
"nu_width": 1,
|
||||||
|
"nu_height": 45,
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"nm_product": "Arrow",
|
||||||
|
"nu_length": 25,
|
||||||
|
"nu_stems_bunch": 1,
|
||||||
|
"nu_bunches": 1,
|
||||||
|
"mny_rate_stem": 2.16
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]}`;
|
||||||
|
const expectedBuys = [
|
||||||
|
{'buyingValue': 5.77, 'description': 'Bow', 'grouping': 1, 'packing': 1, 'size': 1, 'volume': 1200},
|
||||||
|
{'buyingValue': 2.16, 'description': 'Arrow', 'grouping': 1, 'packing': 1, 'size': 25, 'volume': 1125}
|
||||||
|
];
|
||||||
|
controller.fillData(rawData);
|
||||||
|
controller.$.$apply();
|
||||||
|
|
||||||
|
const importData = controller.import;
|
||||||
|
|
||||||
|
expect(importData.observation).toEqual('123456');
|
||||||
|
expect(importData.ref).toEqual('1, 2');
|
||||||
|
|
||||||
|
expect(controller.fetchBuys).toHaveBeenCalledWith(expectedBuys);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fetchBuys()', () => {
|
||||||
|
it(`should perform a query to fetch the buys data`, () => {
|
||||||
|
const buys = [
|
||||||
|
{'buyingValue': 5.77, 'description': 'Bow', 'grouping': 1, 'packing': 1, 'size': 1, 'volume': 1200},
|
||||||
|
{'buyingValue': 2.16, 'description': 'Arrow', 'grouping': 1, 'packing': 1, 'size': 25, 'volume': 1125}
|
||||||
|
];
|
||||||
|
|
||||||
|
const serializedParams = $httpParamSerializer({buys});
|
||||||
|
const query = `Entries/1/importBuysPreview?${serializedParams}`;
|
||||||
|
$httpBackend.expectGET(query).respond(200, buys);
|
||||||
|
controller.fetchBuys(buys);
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
const importData = controller.import;
|
||||||
|
|
||||||
|
expect(importData.buys.length).toEqual(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onSubmit()', () => {
|
||||||
|
it(`should throw an error when some of the rows doesn't have an item`, () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showError');
|
||||||
|
|
||||||
|
controller.import = {
|
||||||
|
observation: '123456',
|
||||||
|
ref: '1, 2',
|
||||||
|
buys: [
|
||||||
|
{'buyingValue': 5.77, 'description': 'Bow', 'grouping': 1, 'packing': 1, 'size': 1, 'volume': 1200},
|
||||||
|
{'buyingValue': 2.16, 'description': 'Arrow', 'grouping': 1, 'packing': 1, 'size': 25, 'volume': 1125}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
controller.onSubmit();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showError).toHaveBeenCalledWith(`Some of the imported buys doesn't have an item`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should perform a query to update columns`, () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||||
|
controller.$state.go = jest.fn();
|
||||||
|
|
||||||
|
controller.import = {
|
||||||
|
observation: '123456',
|
||||||
|
ref: '1, 2',
|
||||||
|
buys: [
|
||||||
|
{'itemFk': 10, 'buyingValue': 5.77, 'description': 'Bow', 'grouping': 1, 'packing': 1, 'size': 1, 'volume': 1200},
|
||||||
|
{'itemFk': 11, 'buyingValue': 2.16, 'description': 'Arrow', 'grouping': 1, 'packing': 1, 'size': 25, 'volume': 1125}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const params = controller.import;
|
||||||
|
|
||||||
|
const query = `Entries/1/importBuys`;
|
||||||
|
$httpBackend.expectPOST(query, params).respond(200, params.buys);
|
||||||
|
controller.onSubmit();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
const importData = controller.import;
|
||||||
|
|
||||||
|
expect(importData.buys.length).toEqual(2);
|
||||||
|
|
||||||
|
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
|
||||||
|
expect(controller.$state.go).toHaveBeenCalledWith('entry.card.buy.index');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
vn-entry-buy-import {
|
||||||
|
.vn-table > tbody td:nth-child(1) {
|
||||||
|
width: 250px
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
<div fixed-bottom-right>
|
||||||
|
<vn-vertical style="align-items: center;">
|
||||||
|
<a ui-sref="entry.card.buy.import"
|
||||||
|
vn-bind="+"
|
||||||
|
vn-acl="buyer"
|
||||||
|
vn-acl-action="remove">
|
||||||
|
<vn-button class="round md vn-mb-sm"
|
||||||
|
icon="publish"
|
||||||
|
vn-tooltip="Import buys"
|
||||||
|
tooltip-position="left">
|
||||||
|
</vn-button>
|
||||||
|
</a>
|
||||||
|
</vn-vertical>
|
||||||
|
</div>
|
|
@ -1,7 +1,7 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../../module';
|
||||||
import Section from 'salix/components/section';
|
import Section from 'salix/components/section';
|
||||||
|
|
||||||
ngModule.vnComponent('vnEntryBuy', {
|
ngModule.vnComponent('vnEntryBuyIndex', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
controller: Section,
|
controller: Section,
|
||||||
bindings: {
|
bindings: {
|
|
@ -0,0 +1 @@
|
||||||
|
Buy: Lineas de entrada
|
|
@ -1 +1,5 @@
|
||||||
Buy: Lineas de entrada
|
reference: Referencia
|
||||||
|
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
|
|
@ -13,4 +13,6 @@ import './card';
|
||||||
import './note';
|
import './note';
|
||||||
import './summary';
|
import './summary';
|
||||||
import './log';
|
import './log';
|
||||||
import './buy';
|
import './buy/index';
|
||||||
|
import './buy/import';
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
<vn-tbody>
|
<vn-tbody>
|
||||||
<a ng-repeat="buy in $ctrl.buys"
|
<a ng-repeat="buy in $ctrl.buys"
|
||||||
class="clickable vn-tr search-result"
|
class="clickable vn-tr search-result"
|
||||||
ui-sref="entry.card.buy({id: {{::buy.entryFk}}})">
|
ui-sref="entry.card.buy.index({id: {{::buy.entryFk}}})">
|
||||||
<vn-td shrink>
|
<vn-td shrink>
|
||||||
<vn-check
|
<vn-check
|
||||||
ng-model="buy.checked"
|
ng-model="buy.checked"
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
],
|
],
|
||||||
"card": [
|
"card": [
|
||||||
{"state": "entry.card.basicData", "icon": "settings"},
|
{"state": "entry.card.basicData", "icon": "settings"},
|
||||||
{"state": "entry.card.buy", "icon": "icon-lines"},
|
{"state": "entry.card.buy.index", "icon": "icon-lines"},
|
||||||
{"state": "entry.card.observation", "icon": "insert_drive_file"},
|
{"state": "entry.card.observation", "icon": "insert_drive_file"},
|
||||||
{"state": "entry.card.log", "icon": "history"}
|
{"state": "entry.card.log", "icon": "history"}
|
||||||
]
|
]
|
||||||
|
@ -56,7 +56,8 @@
|
||||||
"description": "Summary",
|
"description": "Summary",
|
||||||
"params": {
|
"params": {
|
||||||
"entry": "$ctrl.entry"
|
"entry": "$ctrl.entry"
|
||||||
}
|
},
|
||||||
|
"acl": ["buyer", "administrative"]
|
||||||
}, {
|
}, {
|
||||||
"url": "/basic-data",
|
"url": "/basic-data",
|
||||||
"state": "entry.card.basicData",
|
"state": "entry.card.basicData",
|
||||||
|
@ -64,7 +65,8 @@
|
||||||
"description": "Basic data",
|
"description": "Basic data",
|
||||||
"params": {
|
"params": {
|
||||||
"entry": "$ctrl.entry"
|
"entry": "$ctrl.entry"
|
||||||
}
|
},
|
||||||
|
"acl": ["buyer", "administrative"]
|
||||||
},{
|
},{
|
||||||
"url": "/observation",
|
"url": "/observation",
|
||||||
"state": "entry.card.observation",
|
"state": "entry.card.observation",
|
||||||
|
@ -72,20 +74,40 @@
|
||||||
"description": "Notes",
|
"description": "Notes",
|
||||||
"params": {
|
"params": {
|
||||||
"entry": "$ctrl.entry"
|
"entry": "$ctrl.entry"
|
||||||
}
|
},
|
||||||
|
"acl": ["buyer", "administrative"]
|
||||||
},{
|
},{
|
||||||
"url" : "/log",
|
"url" : "/log",
|
||||||
"state": "entry.card.log",
|
"state": "entry.card.log",
|
||||||
"component": "vn-entry-log",
|
"component": "vn-entry-log",
|
||||||
"description": "Log"
|
"description": "Log",
|
||||||
}, {
|
"acl": ["buyer", "administrative"]
|
||||||
|
},
|
||||||
|
{
|
||||||
"url": "/buy",
|
"url": "/buy",
|
||||||
"state": "entry.card.buy",
|
"state": "entry.card.buy",
|
||||||
"component": "vn-entry-buy",
|
"abstract": true,
|
||||||
|
"component": "ui-view"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url" : "/index",
|
||||||
|
"state": "entry.card.buy.index",
|
||||||
|
"component": "vn-entry-buy-index",
|
||||||
"description": "Buy",
|
"description": "Buy",
|
||||||
"params": {
|
"params": {
|
||||||
"entry": "$ctrl.entry"
|
"entry": "$ctrl.entry"
|
||||||
}
|
},
|
||||||
|
"acl": ["buyer", "administrative"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url" : "/import",
|
||||||
|
"state": "entry.card.buy.import",
|
||||||
|
"component": "vn-entry-buy-import",
|
||||||
|
"description": "Import buys",
|
||||||
|
"params": {
|
||||||
|
"entry": "$ctrl.entry"
|
||||||
|
},
|
||||||
|
"acl": ["buyer"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
Loading…
Reference in New Issue