Merge branch '2113-item_data_add_instrastat' of verdnatura/salix into dev
gitea/salix/dev This commit looks good
Details
gitea/salix/dev This commit looks good
Details
This commit is contained in:
commit
61b3ce184c
|
@ -0,0 +1,2 @@
|
||||||
|
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
|
VALUES ('Intrastat', '*', '*', 'ALLOW', 'ROLE', 'buyer');
|
|
@ -684,8 +684,14 @@ INSERT INTO `vn`.`taxCode`(`id`, `dated`, `code`, `taxTypeFk`, `rate`, `equaliza
|
||||||
|
|
||||||
INSERT INTO `vn`.`taxClass`(`id`, `description`, `code`)
|
INSERT INTO `vn`.`taxClass`(`id`, `description`, `code`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'Reduced VAT','R'),
|
(1, 'Reduced VAT', 'R'),
|
||||||
(2, 'General VAT', 'G');
|
(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`)
|
INSERT INTO `vn`.`intrastat`(`id`, `description`, `taxClassFk`, `taxCodeFk`)
|
||||||
VALUES
|
VALUES
|
||||||
|
|
|
@ -262,6 +262,10 @@ export default {
|
||||||
longName: 'vn-textfield[ng-model="$ctrl.item.longName"]',
|
longName: 'vn-textfield[ng-model="$ctrl.item.longName"]',
|
||||||
isActiveCheckbox: 'vn-check[label="Active"]',
|
isActiveCheckbox: 'vn-check[label="Active"]',
|
||||||
priceInKgCheckbox: 'vn-check[label="Price in kg"]',
|
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]`
|
submitBasicDataButton: `button[type=submit]`
|
||||||
},
|
},
|
||||||
itemTags: {
|
itemTags: {
|
||||||
|
|
|
@ -39,6 +39,26 @@ describe('Item Edit basic data path', () => {
|
||||||
expect(result).toEqual('Data saved!');
|
expect(result).toEqual('Data saved!');
|
||||||
}, 20000);
|
}, 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() => {
|
it(`should confirm the item name was edited`, async() => {
|
||||||
await page.reloadSection('item.card.basicData');
|
await page.reloadSection('item.card.basicData');
|
||||||
const result = await page.waitToGetProperty(selectors.itemBasicData.name, 'value');
|
const result = await page.waitToGetProperty(selectors.itemBasicData.name, 'value');
|
||||||
|
@ -53,11 +73,11 @@ describe('Item Edit basic data path', () => {
|
||||||
expect(result).toEqual('Anthurium');
|
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
|
const result = await page
|
||||||
.waitToGetProperty(selectors.itemBasicData.intrastat, 'value');
|
.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() => {
|
it(`should confirm the item relevancy was edited`, async() => {
|
||||||
|
|
|
@ -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
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -62,6 +62,9 @@
|
||||||
"TaxClass": {
|
"TaxClass": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"TaxClassCode": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"TaxCode": {
|
"TaxCode": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,6 +12,7 @@ module.exports = Self => {
|
||||||
require('../methods/item/getVisibleAvailable')(Self);
|
require('../methods/item/getVisibleAvailable')(Self);
|
||||||
require('../methods/item/new')(Self);
|
require('../methods/item/new')(Self);
|
||||||
require('../methods/item/getWasteDetail')(Self);
|
require('../methods/item/getWasteDetail')(Self);
|
||||||
|
require('../methods/item/createIntrastat')(Self);
|
||||||
|
|
||||||
Self.validatesPresenceOf('originFk', {message: 'Cannot be blank'});
|
Self.validatesPresenceOf('originFk', {message: 'Cannot be blank'});
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
{
|
{
|
||||||
"name": "Item",
|
"name": "Item",
|
||||||
"base": "Loggable",
|
"base": "Loggable",
|
||||||
"log": {
|
"log": {
|
||||||
"model":"ItemLog",
|
"model": "ItemLog",
|
||||||
"showField": "id"
|
"showField": "id"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "item"
|
"table": "item"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -125,55 +125,55 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
"itemType": {
|
"itemType": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "ItemType",
|
"model": "ItemType",
|
||||||
"foreignKey": "typeFk"
|
"foreignKey": "typeFk"
|
||||||
},
|
},
|
||||||
"ink": {
|
"ink": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Ink",
|
"model": "Ink",
|
||||||
"foreignKey": "inkFk"
|
"foreignKey": "inkFk"
|
||||||
},
|
},
|
||||||
"origin": {
|
"origin": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Origin",
|
"model": "Origin",
|
||||||
"foreignKey": "originFk"
|
"foreignKey": "originFk"
|
||||||
},
|
},
|
||||||
"producer": {
|
"producer": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Producer",
|
"model": "Producer",
|
||||||
"foreignKey": "producerFk"
|
"foreignKey": "producerFk"
|
||||||
},
|
},
|
||||||
"intrastat": {
|
"intrastat": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Intrastat",
|
"model": "Intrastat",
|
||||||
"foreignKey": "intrastatFk"
|
"foreignKey": "intrastatFk"
|
||||||
},
|
},
|
||||||
"expense": {
|
"expense": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Expense",
|
"model": "Expense",
|
||||||
"foreignKey": "expenseFk"
|
"foreignKey": "expenseFk"
|
||||||
},
|
},
|
||||||
"tags": {
|
"tags": {
|
||||||
"type": "hasMany",
|
"type": "hasMany",
|
||||||
"model": "ItemTag",
|
"model": "ItemTag",
|
||||||
"foreignKey": "itemFk"
|
"foreignKey": "itemFk"
|
||||||
},
|
},
|
||||||
"itemBarcode": {
|
"itemBarcode": {
|
||||||
"type": "hasMany",
|
"type": "hasMany",
|
||||||
"model": "ItemBarcode",
|
"model": "ItemBarcode",
|
||||||
"foreignKey": "itemFk"
|
"foreignKey": "itemFk"
|
||||||
},
|
},
|
||||||
"taxes": {
|
"taxes": {
|
||||||
"type": "hasMany",
|
"type": "hasMany",
|
||||||
"model": "ItemTaxCountry",
|
"model": "ItemTaxCountry",
|
||||||
"foreignKey": "itemFk"
|
"foreignKey": "itemFk"
|
||||||
},
|
},
|
||||||
"itemNiche": {
|
"itemNiche": {
|
||||||
"type": "hasMany",
|
"type": "hasMany",
|
||||||
"model": "ItemNiche",
|
"model": "ItemNiche",
|
||||||
"foreignKey": "itemFk"
|
"foreignKey": "itemFk"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -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"`;
|
|
|
@ -55,6 +55,13 @@
|
||||||
<div style="width: 6em; text-align: right; padding-right: 1em;">{{::id}}</div>
|
<div style="width: 6em; text-align: right; padding-right: 1em;">{{::id}}</div>
|
||||||
<div>{{::description}}</div>
|
<div>{{::description}}</div>
|
||||||
</tpl-item>
|
</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-autocomplete vn-one
|
<vn-autocomplete vn-one
|
||||||
url="Expenses"
|
url="Expenses"
|
||||||
|
@ -69,7 +76,7 @@
|
||||||
value-field="id"
|
value-field="id"
|
||||||
ng-model="$ctrl.item.originFk"
|
ng-model="$ctrl.item.originFk"
|
||||||
initial-data="$ctrl.item.origin">
|
initial-data="$ctrl.item.origin">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
|
@ -123,7 +130,7 @@
|
||||||
</vn-check>
|
</vn-check>
|
||||||
<vn-textarea
|
<vn-textarea
|
||||||
vn-one
|
vn-one
|
||||||
label="description"
|
label="Description"
|
||||||
ng-model="$ctrl.item.description"
|
ng-model="$ctrl.item.description"
|
||||||
rule>
|
rule>
|
||||||
</vn-textarea>
|
</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 label="Undo changes" ng-if="$ctrl.$scope.form.$dirty" ng-click="watcher.loadOriginalData()"></vn-button>
|
||||||
</vn-button-bar>
|
</vn-button-bar>
|
||||||
</form>
|
</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>
|
|
@ -1,20 +1,23 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
|
import Component from 'core/lib/component';
|
||||||
|
class Controller extends Component {
|
||||||
|
showIntrastat(event) {
|
||||||
|
if (event.defaultPrevented) return;
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
class Controller {
|
this.newIntrastat = {
|
||||||
constructor($scope, $timeout) {
|
taxClassFk: this.item.taxClassFk
|
||||||
this.$scope = $scope;
|
};
|
||||||
this.$timeout = $timeout;
|
this.$.intrastat.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
$onChanges(data) {
|
onIntrastatAccept() {
|
||||||
this.$timeout(() => {
|
const query = `Items/${this.$params.id}/createIntrastat`;
|
||||||
this.$scope.watcher.data = data.item.currentValue;
|
return this.$http.patch(query, this.newIntrastat)
|
||||||
});
|
.then(res => this.item.intrastatFk = res.data.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$scope', '$timeout'];
|
|
||||||
|
|
||||||
ngModule.component('vnItemBasicData', {
|
ngModule.component('vnItemBasicData', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
bindings: {
|
bindings: {
|
||||||
|
|
|
@ -2,26 +2,31 @@ import './index.js';
|
||||||
|
|
||||||
describe('vnItemBasicData', () => {
|
describe('vnItemBasicData', () => {
|
||||||
describe('Component vnItemBasicData', () => {
|
describe('Component vnItemBasicData', () => {
|
||||||
|
let $httpBackend;
|
||||||
let $scope;
|
let $scope;
|
||||||
let controller;
|
let controller;
|
||||||
let $timeout;
|
let $element;
|
||||||
|
|
||||||
beforeEach(ngModule('item'));
|
beforeEach(ngModule('item'));
|
||||||
|
|
||||||
beforeEach(angular.mock.inject(($componentController, $rootScope, _$timeout_) => {
|
beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_) => {
|
||||||
$timeout = _$timeout_;
|
$httpBackend = _$httpBackend_;
|
||||||
$scope = $rootScope.$new();
|
$scope = $rootScope.$new();
|
||||||
controller = $componentController('vnItemBasicData', {$scope, $timeout});
|
$element = angular.element('<vn-item-basic-data></vn-item-basic-data>');
|
||||||
controller.$scope.watcher = {};
|
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', () => {
|
it('should pass the data to the watcher', () => {
|
||||||
const data = {item: {currentValue: 'the current value of an item'}};
|
const newIntrastatId = 20;
|
||||||
controller.$onChanges(data);
|
$httpBackend.expect('PATCH', 'Items/1/createIntrastat').respond({id: 20});
|
||||||
$timeout.flush();
|
controller.onIntrastatAccept();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
expect(controller.$scope.watcher.data).toMatchSnapshot();
|
expect(controller.item.intrastatFk).toEqual(newIntrastatId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,4 +5,6 @@ Full name calculates based on tags 1-3. Is not recommended to change it manually
|
||||||
No se recomienda cambiarlo manualmente
|
No se recomienda cambiarlo manualmente
|
||||||
Is active: Activo
|
Is active: Activo
|
||||||
Expense: Gasto
|
Expense: Gasto
|
||||||
Price in kg: Precio en kg
|
Price in kg: Precio en kg
|
||||||
|
New intrastat: Nuevo intrastat
|
||||||
|
Identifier: Identificador
|
Loading…
Reference in New Issue