Merge branch '2113-item_data_add_instrastat' of verdnatura/salix into dev
gitea/salix/dev This commit looks good Details

This commit is contained in:
Joan Sanchez 2020-02-18 06:32:17 +00:00 committed by Gitea
commit 61b3ce184c
15 changed files with 291 additions and 85 deletions

View File

@ -0,0 +1,2 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES ('Intrastat', '*', '*', 'ALLOW', 'ROLE', 'buyer');

View File

@ -684,8 +684,14 @@ INSERT INTO `vn`.`taxCode`(`id`, `dated`, `code`, `taxTypeFk`, `rate`, `equaliza
INSERT INTO `vn`.`taxClass`(`id`, `description`, `code`)
VALUES
(1, 'Reduced VAT','R'),
(1, 'Reduced VAT', 'R'),
(2, 'General VAT', 'G');
INSERT INTO `vn`.`taxClassCode`(`taxClassFk`, `effectived`, `taxCodeFk`)
VALUES
(1, CURDATE(), 1),
(1, CURDATE(), 21),
(2, CURDATE(), 2);
INSERT INTO `vn`.`intrastat`(`id`, `description`, `taxClassFk`, `taxCodeFk`)
VALUES

View File

@ -262,6 +262,10 @@ export default {
longName: 'vn-textfield[ng-model="$ctrl.item.longName"]',
isActiveCheckbox: 'vn-check[label="Active"]',
priceInKgCheckbox: 'vn-check[label="Price in kg"]',
newIntrastatButton: 'vn-item-basic-data vn-icon-button[vn-tooltip="New intrastat"] > button',
newIntrastatId: '.vn-dialog.shown vn-input-number[ng-model="$ctrl.newIntrastat.intrastatId"]',
newIntrastatDescription: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.newIntrastat.description"]',
acceptIntrastatButton: '.vn-dialog.shown button[response="accept"]',
submitBasicDataButton: `button[type=submit]`
},
itemTags: {

View File

@ -39,6 +39,26 @@ describe('Item Edit basic data path', () => {
expect(result).toEqual('Data saved!');
}, 20000);
it(`should create a new intrastat`, async() => {
await page.waitToClick(selectors.itemBasicData.newIntrastatButton);
await page.write(selectors.itemBasicData.newIntrastatId, '588420239');
await page.write(selectors.itemBasicData.newIntrastatDescription, 'Tropical Flowers');
await page.waitToClick(selectors.itemBasicData.acceptIntrastatButton);
await page.waitForTextInField(selectors.itemBasicData.intrastat, 'Tropical Flowers');
let newcode = await page.waitToGetProperty(selectors.itemBasicData.intrastat, 'value');
expect(newcode).toEqual('588420239 Tropical Flowers');
});
it(`should save with the new intrastat`, async() => {
await page.waitFor(250);
await page.waitForTextInField(selectors.itemBasicData.intrastat, 'Tropical Flowers');
await page.waitToClick(selectors.itemBasicData.submitBasicDataButton);
const result = await page.waitForLastSnackbar();
expect(result).toEqual('Data saved!');
});
it(`should confirm the item name was edited`, async() => {
await page.reloadSection('item.card.basicData');
const result = await page.waitToGetProperty(selectors.itemBasicData.name, 'value');
@ -53,11 +73,11 @@ describe('Item Edit basic data path', () => {
expect(result).toEqual('Anthurium');
});
it(`should confirm the item intrastad was edited`, async() => {
it(`should confirm the item intrastat was edited`, async() => {
const result = await page
.waitToGetProperty(selectors.itemBasicData.intrastat, 'value');
expect(result).toEqual('5080000 Coral y materiales similares');
expect(result).toEqual('588420239 Tropical Flowers');
});
it(`should confirm the item relevancy was edited`, async() => {

View File

@ -0,0 +1,61 @@
let UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethod('createIntrastat', {
description: 'Creates a new item intrastat',
accessType: 'WRITE',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'The item id',
http: {source: 'path'}
},
{
arg: 'intrastatId',
type: 'number',
required: true
},
{
arg: 'description',
type: 'string',
required: true
}],
returns: {
type: 'boolean',
root: true
},
http: {
path: `/:id/createIntrastat`,
verb: 'PATCH'
}
});
Self.createIntrastat = async(id, intrastatId, description) => {
const models = Self.app.models;
const country = await models.Country.findOne({
where: {code: 'ES'}
});
const itemTaxCountry = await models.ItemTaxCountry.findOne({
where: {
itemFk: id,
countryFk: country.id
},
order: 'effectived DESC'
});
const taxClassCode = await models.TaxClassCode.findOne({
where: {
taxClassFk: itemTaxCountry.taxClassFk
},
order: 'effectived DESC'
});
return models.Intrastat.create({
id: intrastatId,
description: description,
taxClassFk: itemTaxCountry.taxClassFk,
taxCodeFk: taxClassCode.taxCodeFk
});
};
};

View File

@ -0,0 +1,22 @@
const app = require('vn-loopback/server/server');
describe('createIntrastat()', () => {
let newIntrastat;
afterAll(async done => {
await app.models.Intrastat.destroyById(newIntrastat.id);
done();
});
it('should create a new intrastat', async() => {
const intrastatId = 588420239;
const description = 'Tropical Flowers';
const itemId = 9;
newIntrastat = await app.models.Item.createIntrastat(itemId, intrastatId, description);
expect(newIntrastat.description).toEqual(description);
expect(newIntrastat.taxClassFk).toEqual(1);
expect(newIntrastat.taxCodeFk).toEqual(21);
});
});

View File

@ -62,6 +62,9 @@
"TaxClass": {
"dataSource": "vn"
},
"TaxClassCode": {
"dataSource": "vn"
},
"TaxCode": {
"dataSource": "vn"
},

View File

@ -12,6 +12,7 @@ module.exports = Self => {
require('../methods/item/getVisibleAvailable')(Self);
require('../methods/item/new')(Self);
require('../methods/item/getWasteDetail')(Self);
require('../methods/item/createIntrastat')(Self);
Self.validatesPresenceOf('originFk', {message: 'Cannot be blank'});

View File

@ -1,13 +1,13 @@
{
"name": "Item",
"base": "Loggable",
"log": {
"model":"ItemLog",
"base": "Loggable",
"log": {
"model": "ItemLog",
"showField": "id"
},
},
"options": {
"mysql": {
"table": "item"
"table": "item"
}
},
"properties": {
@ -125,55 +125,55 @@
}
},
"relations": {
"itemType": {
"type": "belongsTo",
"model": "ItemType",
"foreignKey": "typeFk"
},
"ink": {
"type": "belongsTo",
"model": "Ink",
"foreignKey": "inkFk"
},
"origin": {
"type": "belongsTo",
"model": "Origin",
"foreignKey": "originFk"
},
"producer": {
"type": "belongsTo",
"model": "Producer",
"foreignKey": "producerFk"
},
"intrastat": {
"type": "belongsTo",
"model": "Intrastat",
"foreignKey": "intrastatFk"
},
"expense": {
"type": "belongsTo",
"model": "Expense",
"foreignKey": "expenseFk"
},
"tags": {
"type": "hasMany",
"model": "ItemTag",
"foreignKey": "itemFk"
},
"itemBarcode": {
"type": "hasMany",
"model": "ItemBarcode",
"foreignKey": "itemFk"
},
"taxes": {
"type": "hasMany",
"model": "ItemTaxCountry",
"foreignKey": "itemFk"
},
"itemNiche": {
"type": "hasMany",
"model": "ItemNiche",
"foreignKey": "itemFk"
}
"itemType": {
"type": "belongsTo",
"model": "ItemType",
"foreignKey": "typeFk"
},
"ink": {
"type": "belongsTo",
"model": "Ink",
"foreignKey": "inkFk"
},
"origin": {
"type": "belongsTo",
"model": "Origin",
"foreignKey": "originFk"
},
"producer": {
"type": "belongsTo",
"model": "Producer",
"foreignKey": "producerFk"
},
"intrastat": {
"type": "belongsTo",
"model": "Intrastat",
"foreignKey": "intrastatFk"
},
"expense": {
"type": "belongsTo",
"model": "Expense",
"foreignKey": "expenseFk"
},
"tags": {
"type": "hasMany",
"model": "ItemTag",
"foreignKey": "itemFk"
},
"itemBarcode": {
"type": "hasMany",
"model": "ItemBarcode",
"foreignKey": "itemFk"
},
"taxes": {
"type": "hasMany",
"model": "ItemTaxCountry",
"foreignKey": "itemFk"
},
"itemNiche": {
"type": "hasMany",
"model": "ItemNiche",
"foreignKey": "itemFk"
}
}
}
}

View File

@ -0,0 +1,46 @@
{
"name": "TaxClassCode",
"base": "VnModel",
"options": {
"mysql": {
"table": "taxClassCode"
}
},
"properties": {
"taxClassFk": {
"type": "number",
"required": true,
"id": 1
},
"taxCodeFk": {
"type": "number",
"required": true,
"id": 2
},
"effectived": {
"type": "date",
"required": true,
"id": 3
}
},
"relations": {
"taxClass": {
"type": "belongsTo",
"model": "TaxClass",
"foreignKey": "taxClassFk"
},
"taxCode": {
"type": "belongsTo",
"model": "TaxCode",
"foreignKey": "taxCodeFk"
}
},
"acls": [
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
]
}

View File

@ -1,3 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`vnItemBasicData Component vnItemBasicData $onChanges() should pass the data to the watcher 1`] = `"the current value of an item"`;

View File

@ -55,6 +55,13 @@
<div style="width: 6em; text-align: right; padding-right: 1em;">{{::id}}</div>
<div>{{::description}}</div>
</tpl-item>
<append>
<vn-icon-button
icon="add_circle"
vn-tooltip="New intrastat"
ng-click="$ctrl.showIntrastat($event)">
</vn-icon-button>
</append>
</vn-autocomplete>
<vn-autocomplete vn-one
url="Expenses"
@ -69,7 +76,7 @@
value-field="id"
ng-model="$ctrl.item.originFk"
initial-data="$ctrl.item.origin">
</vn-autocomplete>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
@ -123,7 +130,7 @@
</vn-check>
<vn-textarea
vn-one
label="description"
label="Description"
ng-model="$ctrl.item.description"
rule>
</vn-textarea>
@ -134,3 +141,30 @@
<vn-button label="Undo changes" ng-if="$ctrl.$scope.form.$dirty" ng-click="watcher.loadOriginalData()"></vn-button>
</vn-button-bar>
</form>
<!-- Create custom agent dialog -->
<vn-dialog class="edit"
vn-id="intrastat"
on-accept="$ctrl.onIntrastatAccept()">
<tpl-body>
<h5 class="vn-py-sm" translate>New intrastat</h5>
<vn-horizontal>
<vn-input-number vn-one vn-focus
label="Identifier"
ng-model="$ctrl.newIntrastat.intrastatId"
required="true">
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one
label="Description"
ng-model="$ctrl.newIntrastat.description"
required="true">
</vn-textfield>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Create</button>
</tpl-buttons>
</vn-dialog>

View File

@ -1,20 +1,23 @@
import ngModule from '../module';
import Component from 'core/lib/component';
class Controller extends Component {
showIntrastat(event) {
if (event.defaultPrevented) return;
event.preventDefault();
class Controller {
constructor($scope, $timeout) {
this.$scope = $scope;
this.$timeout = $timeout;
this.newIntrastat = {
taxClassFk: this.item.taxClassFk
};
this.$.intrastat.show();
}
$onChanges(data) {
this.$timeout(() => {
this.$scope.watcher.data = data.item.currentValue;
});
onIntrastatAccept() {
const query = `Items/${this.$params.id}/createIntrastat`;
return this.$http.patch(query, this.newIntrastat)
.then(res => this.item.intrastatFk = res.data.id);
}
}
Controller.$inject = ['$scope', '$timeout'];
ngModule.component('vnItemBasicData', {
template: require('./index.html'),
bindings: {

View File

@ -2,26 +2,31 @@ import './index.js';
describe('vnItemBasicData', () => {
describe('Component vnItemBasicData', () => {
let $httpBackend;
let $scope;
let controller;
let $timeout;
let $element;
beforeEach(ngModule('item'));
beforeEach(angular.mock.inject(($componentController, $rootScope, _$timeout_) => {
$timeout = _$timeout_;
beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
controller = $componentController('vnItemBasicData', {$scope, $timeout});
controller.$scope.watcher = {};
$element = angular.element('<vn-item-basic-data></vn-item-basic-data>');
controller = $componentController('vnItemBasicData', {$element, $scope});
controller.$.watcher = {};
controller.$params.id = 1;
controller.item = {id: 1, name: 'Rainbow Coral'};
}));
describe('$onChanges()', () => {
describe('onIntrastatAccept()', () => {
it('should pass the data to the watcher', () => {
const data = {item: {currentValue: 'the current value of an item'}};
controller.$onChanges(data);
$timeout.flush();
const newIntrastatId = 20;
$httpBackend.expect('PATCH', 'Items/1/createIntrastat').respond({id: 20});
controller.onIntrastatAccept();
$httpBackend.flush();
expect(controller.$scope.watcher.data).toMatchSnapshot();
expect(controller.item.intrastatFk).toEqual(newIntrastatId);
});
});
});

View File

@ -5,4 +5,6 @@ Full name calculates based on tags 1-3. Is not recommended to change it manually
No se recomienda cambiarlo manualmente
Is active: Activo
Expense: Gasto
Price in kg: Precio en kg
Price in kg: Precio en kg
New intrastat: Nuevo intrastat
Identifier: Identificador