diff --git a/db/changes/10160-postValentineDay/00-ACL.sql b/db/changes/10160-postValentineDay/00-ACL.sql new file mode 100644 index 000000000..5b6301e3d --- /dev/null +++ b/db/changes/10160-postValentineDay/00-ACL.sql @@ -0,0 +1,2 @@ +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) + VALUES ('Intrastat', '*', '*', 'ALLOW', 'ROLE', 'buyer'); diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index f3dd36976..d56176713 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -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 diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 63d40f9d5..1510ff893 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -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: { diff --git a/e2e/paths/04-item/02_basic_data.spec.js b/e2e/paths/04-item/02_basic_data.spec.js index 2c8a8a7a1..64827ed9b 100644 --- a/e2e/paths/04-item/02_basic_data.spec.js +++ b/e2e/paths/04-item/02_basic_data.spec.js @@ -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() => { diff --git a/modules/item/back/methods/item/createIntrastat.js b/modules/item/back/methods/item/createIntrastat.js new file mode 100644 index 000000000..1a88d16e2 --- /dev/null +++ b/modules/item/back/methods/item/createIntrastat.js @@ -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 + }); + }; +}; diff --git a/modules/item/back/methods/item/specs/createIntrastat.spec.js b/modules/item/back/methods/item/specs/createIntrastat.spec.js new file mode 100644 index 000000000..fb10de858 --- /dev/null +++ b/modules/item/back/methods/item/specs/createIntrastat.spec.js @@ -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); + }); +}); diff --git a/modules/item/back/model-config.json b/modules/item/back/model-config.json index db8eed9d5..d8ec5914a 100644 --- a/modules/item/back/model-config.json +++ b/modules/item/back/model-config.json @@ -62,6 +62,9 @@ "TaxClass": { "dataSource": "vn" }, + "TaxClassCode": { + "dataSource": "vn" + }, "TaxCode": { "dataSource": "vn" }, diff --git a/modules/item/back/models/item.js b/modules/item/back/models/item.js index 6c221e94d..01061ce99 100644 --- a/modules/item/back/models/item.js +++ b/modules/item/back/models/item.js @@ -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'}); diff --git a/modules/item/back/models/item.json b/modules/item/back/models/item.json index d8d1cb64d..dbaa3a409 100644 --- a/modules/item/back/models/item.json +++ b/modules/item/back/models/item.json @@ -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" + } } - } \ No newline at end of file +} \ No newline at end of file diff --git a/modules/item/back/models/tax-class-code.json b/modules/item/back/models/tax-class-code.json new file mode 100644 index 000000000..ef8c529d9 --- /dev/null +++ b/modules/item/back/models/tax-class-code.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/modules/item/front/basic-data/__snapshots__/index.spec.js.snap b/modules/item/front/basic-data/__snapshots__/index.spec.js.snap deleted file mode 100644 index 92219bb33..000000000 --- a/modules/item/front/basic-data/__snapshots__/index.spec.js.snap +++ /dev/null @@ -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"`; diff --git a/modules/item/front/basic-data/index.html b/modules/item/front/basic-data/index.html index bd0cec86d..3cd879945 100644 --- a/modules/item/front/basic-data/index.html +++ b/modules/item/front/basic-data/index.html @@ -55,6 +55,13 @@
{{::id}}
{{::description}}
+ + + + - + @@ -134,3 +141,30 @@ + + + + +
New intrastat
+ + + + + + + + +
+ + + + +
\ No newline at end of file diff --git a/modules/item/front/basic-data/index.js b/modules/item/front/basic-data/index.js index 123aa59cd..33a60b32d 100644 --- a/modules/item/front/basic-data/index.js +++ b/modules/item/front/basic-data/index.js @@ -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: { diff --git a/modules/item/front/basic-data/index.spec.js b/modules/item/front/basic-data/index.spec.js index e7578b54c..178fac278 100644 --- a/modules/item/front/basic-data/index.spec.js +++ b/modules/item/front/basic-data/index.spec.js @@ -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(''); + 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); }); }); }); diff --git a/modules/item/front/basic-data/locale/es.yml b/modules/item/front/basic-data/locale/es.yml index 67780557a..07e681770 100644 --- a/modules/item/front/basic-data/locale/es.yml +++ b/modules/item/front/basic-data/locale/es.yml @@ -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 \ No newline at end of file +Price in kg: Precio en kg +New intrastat: Nuevo intrastat +Identifier: Identificador \ No newline at end of file