diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 1a87b5bf0..aed129805 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -1038,6 +1038,17 @@ export default { booked: 'vn-invoice-in-basic-data vn-date-picker[ng-model="$ctrl.invoiceIn.booked"]', currency: 'vn-invoice-in-basic-data vn-autocomplete[ng-model="$ctrl.invoiceIn.currencyFk"]', company: 'vn-invoice-in-basic-data vn-autocomplete[ng-model="$ctrl.invoiceIn.companyFk"]', + dms: 'vn-invoice-in-basic-data vn-textfield[ng-model="$ctrl.invoiceIn.dmsFk"]', + download: 'vn-invoice-in-basic-data vn-textfield[ng-model="$ctrl.invoiceIn.dmsFk"] > div.container > div.prepend > prepend > vn-icon-button', + edit: 'vn-invoice-in-basic-data vn-textfield[ng-model="$ctrl.invoiceIn.dmsFk"] > div.container > div.append > append > vn-icon-button[icon="edit"]', + create: 'vn-invoice-in-basic-data vn-textfield[ng-model="$ctrl.invoiceIn.dmsFk"] > div.container > div.append > append > vn-icon-button[icon="add_circle"]', + reference: 'vn-textfield[ng-model="$ctrl.dms.reference"]', + companyId: 'vn-autocomplete[ng-model="$ctrl.dms.companyId"]', + warehouseId: 'vn-autocomplete[ng-model="$ctrl.dms.warehouseId"]', + dmsTypeId: 'vn-autocomplete[ng-model="$ctrl.dms.dmsTypeId"]', + description: 'vn-textarea[ng-model="$ctrl.dms.description"]', + inputFile: 'vn-input-file[ng-model="$ctrl.dms.files"]', + confirm: 'button[response="accept"]', save: 'vn-invoice-in-basic-data button[type=submit]' }, invoiceInTax: { diff --git a/e2e/paths/09-invoice-in/03_basic_data.spec.js b/e2e/paths/09-invoice-in/03_basic_data.spec.js index 0c3f914e4..0a28ed191 100644 --- a/e2e/paths/09-invoice-in/03_basic_data.spec.js +++ b/e2e/paths/09-invoice-in/03_basic_data.spec.js @@ -4,6 +4,7 @@ import getBrowser from '../../helpers/puppeteer'; describe('InvoiceIn basic data path', () => { let browser; let page; + let newDms; beforeAll(async() => { browser = await getBrowser(); @@ -24,6 +25,8 @@ describe('InvoiceIn basic data path', () => { await page.autocompleteSearch(selectors.invoiceInBasicData.supplier, 'Verdnatura'); await page.clearInput(selectors.invoiceInBasicData.supplierRef); await page.write(selectors.invoiceInBasicData.supplierRef, '9999'); + await page.clearInput(selectors.invoiceInBasicData.dms); + await page.write(selectors.invoiceInBasicData.dms, '2'); await page.pickDate(selectors.invoiceInBasicData.bookEntried, now); await page.pickDate(selectors.invoiceInBasicData.booked, now); await page.autocompleteSearch(selectors.invoiceInBasicData.currency, 'USD'); @@ -61,4 +64,141 @@ describe('InvoiceIn basic data path', () => { expect(result).toEqual('ORN'); }); + + it(`should confirm the invoiceIn dms was edited`, async() => { + const result = await page + .waitToGetProperty(selectors.invoiceInBasicData.dms, 'value'); + + expect(result).toEqual('2'); + }); + + it(`should create a new invoiceIn dms and save the changes`, async() => { + await page.clearInput(selectors.invoiceInBasicData.dms); + await page.waitToClick(selectors.invoiceInBasicData.create); + + await page.clearInput(selectors.invoiceInBasicData.reference); + await page.write(selectors.invoiceInBasicData.reference, 'New Dms'); + + await page.waitToClick(selectors.invoiceInBasicData.confirm); + let message = await page.waitForSnackbar(); + + expect(message.text).toContain('The company can\'t be empty'); + + await page.clearInput(selectors.invoiceInBasicData.companyId); + await page.autocompleteSearch(selectors.invoiceInBasicData.companyId, 'VNL'); + + await page.waitToClick(selectors.invoiceInBasicData.confirm); + message = await page.waitForSnackbar(); + + expect(message.text).toContain('The warehouse can\'t be empty'); + + await page.clearInput(selectors.invoiceInBasicData.warehouseId); + await page.autocompleteSearch(selectors.invoiceInBasicData.warehouseId, 'Warehouse One'); + + await page.waitToClick(selectors.invoiceInBasicData.confirm); + message = await page.waitForSnackbar(); + + expect(message.text).toContain('The DMS Type can\'t be empty'); + + await page.clearInput(selectors.invoiceInBasicData.dmsTypeId); + await page.autocompleteSearch(selectors.invoiceInBasicData.dmsTypeId, 'Ticket'); + + await page.waitToClick(selectors.invoiceInBasicData.confirm); + message = await page.waitForSnackbar(); + + expect(message.text).toContain('The description can\'t be empty'); + + await page.waitToClick(selectors.invoiceInBasicData.description); + await page.write(selectors.invoiceInBasicData.description, 'Dms without edition.'); + + await page.waitToClick(selectors.invoiceInBasicData.confirm); + message = await page.waitForSnackbar(); + + expect(message.text).toContain('The files can\'t be empty'); + + let currentDir = process.cwd(); + let filePath = `${currentDir}/e2e/assets/thermograph.jpeg`; + + const [fileChooser] = await Promise.all([ + page.waitForFileChooser(), + page.waitToClick(selectors.invoiceInBasicData.inputFile) + ]); + await fileChooser.accept([filePath]); + + await page.waitToClick(selectors.invoiceInBasicData.confirm); + message = await page.waitForSnackbar(); + + expect(message.text).toContain('Data saved!'); + + newDms = await page + .waitToGetProperty(selectors.invoiceInBasicData.dms, 'value'); + }); + + it(`should confirm the invoiceIn was edited with the new dms`, async() => { + await page.reloadSection('invoiceIn.card.basicData'); + const result = await page + .waitToGetProperty(selectors.invoiceInBasicData.dms, 'value'); + + expect(result).toEqual(newDms); + }); + + it(`should edit the invoiceIn`, async() => { + await page.waitToClick(selectors.invoiceInBasicData.edit); + + await page.clearInput(selectors.invoiceInBasicData.reference); + await page.write(selectors.invoiceInBasicData.reference, 'Dms Edited'); + await page.clearInput(selectors.invoiceInBasicData.companyId); + await page.autocompleteSearch(selectors.invoiceInBasicData.companyId, 'CCs'); + await page.clearInput(selectors.invoiceInBasicData.warehouseId); + await page.autocompleteSearch(selectors.invoiceInBasicData.warehouseId, 'Algemesi'); + await page.clearInput(selectors.invoiceInBasicData.dmsTypeId); + await page.autocompleteSearch(selectors.invoiceInBasicData.dmsTypeId, 'Basura'); + await page.waitToClick(selectors.invoiceInBasicData.description); + await page.write(selectors.invoiceInBasicData.description, ' Nevermind, now is edited.'); + + await page.waitToClick(selectors.invoiceInBasicData.confirm); + let message = await page.waitForSnackbar(); + + expect(message.text).toContain('Data saved!'); + }); + + it(`should confirm the new dms has been edited`, async() => { + await page.reloadSection('invoiceIn.card.basicData'); + await page.waitToClick(selectors.invoiceInBasicData.edit); + + const reference = await page + .waitToGetProperty(selectors.invoiceInBasicData.reference, 'value'); + const companyId = await page + .waitToGetProperty(selectors.invoiceInBasicData.companyId, 'value'); + const warehouseId = await page + .waitToGetProperty(selectors.invoiceInBasicData.warehouseId, 'value'); + const dmsTypeId = await page + .waitToGetProperty(selectors.invoiceInBasicData.dmsTypeId, 'value'); + const description = await page + .waitToGetProperty(selectors.invoiceInBasicData.description, 'value'); + + expect(reference).toEqual('Dms Edited'); + expect(companyId).toEqual('CCs'); + expect(warehouseId).toEqual('Algemesi'); + expect(dmsTypeId).toEqual('Basura'); + expect(description).toEqual('Dms without edition. Nevermind, now is edited.'); + + await page.waitToClick(selectors.invoiceInBasicData.confirm); + }); + + it(`should disable edit and download if dms doesn't exists, and set back the original dms`, async() => { + await page.clearInput(selectors.invoiceInBasicData.dms); + await page.write(selectors.invoiceInBasicData.dms, '9999'); + + await page.waitForSelector(`${selectors.invoiceInBasicData.download}.disabled`); + await page.waitForSelector(`${selectors.invoiceInBasicData.edit}.disabled`); + + await page.clearInput(selectors.invoiceInBasicData.dms); + await page.write(selectors.invoiceInBasicData.dms, '1'); + + await page.waitToClick(selectors.invoiceInBasicData.save); + const message = await page.waitForSnackbar(); + + expect(message.text).toContain('Data saved!'); + }); }); diff --git a/modules/invoiceIn/front/basic-data/index.html b/modules/invoiceIn/front/basic-data/index.html index be89e502c..a22abbb33 100644 --- a/modules/invoiceIn/front/basic-data/index.html +++ b/modules/invoiceIn/front/basic-data/index.html @@ -5,6 +5,24 @@ form="form" save="patch"> + + + + + +
@@ -31,14 +49,14 @@ @@ -57,17 +75,47 @@ {{id}} - {{name}} + + + + + + + + + + + + @@ -104,4 +152,164 @@ ng-click="watcher.loadOriginalData()"> - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/invoiceIn/front/basic-data/index.js b/modules/invoiceIn/front/basic-data/index.js index 6a39f35cd..8747fc4f2 100644 --- a/modules/invoiceIn/front/basic-data/index.js +++ b/modules/invoiceIn/front/basic-data/index.js @@ -1,9 +1,181 @@ import ngModule from '../module'; import Section from 'salix/components/section'; +import UserError from 'core/lib/user-error'; + +class Controller extends Section { + constructor($element, $, vnFile) { + super($element, $, vnFile); + this.dms = { + files: [], + hasFile: false, + hasFileAttached: false + }; + this.vnFile = vnFile; + this.getAllowedContentTypes(); + this._editDownloadDisabled = false; + } + + get contentTypesInfo() { + return this.$t('ContentTypesInfo', { + allowedContentTypes: this.allowedContentTypes + }); + } + + get editDownloadDisabled() { + return this._editDownloadDisabled; + } + + async checkFileExists(dmsId) { + if (!dmsId) return; + let filter = { + fields: ['id'] + }; + await this.$http.get(`Dms/${dmsId}`, {filter}) + .then(() => this._editDownloadDisabled = false) + .catch(() => this._editDownloadDisabled = true); + } + + async getFile(dmsId) { + const path = `Dms/${dmsId}`; + await this.$http.get(path).then(res => { + const dms = res.data && res.data; + this.dms = { + dmsId: dms.id, + reference: dms.reference, + warehouseId: dms.warehouseFk, + companyId: dms.companyFk, + dmsTypeId: dms.dmsTypeFk, + description: dms.description, + hasFile: dms.hasFile, + hasFileAttached: false, + files: [] + }; + }); + } + + getAllowedContentTypes() { + this.$http.get('DmsContainers/allowedContentTypes').then(res => { + if (res.data.length > 0) { + const contentTypes = res.data.join(', '); + this.allowedContentTypes = contentTypes; + } + }); + } + + openEditDialog(dmsId) { + this.getFile(dmsId).then(() => this.$.dmsEditDialog.show()); + } + + openCreateDialog() { + this.dms = { + reference: null, + warehouseId: null, + companyId: null, + dmsTypeId: null, + description: null, + hasFile: true, + hasFileAttached: true, + files: null + }; + this.$.dmsCreateDialog.show(); + } + + downloadFile(dmsId) { + this.vnFile.download(`api/dms/${dmsId}/downloadFile`); + } + + onFileChange(files) { + let hasFileAttached = false; + if (files.length > 0) + hasFileAttached = true; + + this.$.$applyAsync(() => { + this.dms.hasFileAttached = hasFileAttached; + }); + } + + onEdit() { + if (!this.dms.companyId) + throw new UserError(`The company can't be empty`); + if (!this.dms.warehouseId) + throw new UserError(`The warehouse can't be empty`); + if (!this.dms.dmsTypeId) + throw new UserError(`The DMS Type can't be empty`); + if (!this.dms.description) + throw new UserError(`The description can't be empty`); + + const query = `dms/${this.dms.dmsId}/updateFile`; + const options = { + method: 'POST', + url: query, + params: this.dms, + headers: { + 'Content-Type': undefined + }, + transformRequest: files => { + const formData = new FormData(); + + for (let i = 0; i < files.length; i++) + formData.append(files[i].name, files[i]); + + return formData; + }, + data: this.dms.files + }; + + this.$http(options).then(res => { + if (res) { + this.vnApp.showSuccess(this.$t('Data saved!')); + if (res.data.length > 0) this.invoiceIn.dmsFk = res.data[0].id; + } + }); + } + + onCreate() { + if (!this.dms.companyId) + throw new UserError(`The company can't be empty`); + if (!this.dms.warehouseId) + throw new UserError(`The warehouse can't be empty`); + if (!this.dms.dmsTypeId) + throw new UserError(`The DMS Type can't be empty`); + if (!this.dms.description) + throw new UserError(`The description can't be empty`); + if (!this.dms.files) + throw new UserError(`The files can't be empty`); + + const query = `Dms/uploadFile`; + const options = { + method: 'POST', + url: query, + params: this.dms, + headers: { + 'Content-Type': undefined + }, + transformRequest: files => { + const formData = new FormData(); + + for (let i = 0; i < files.length; i++) + formData.append(files[i].name, files[i]); + + return formData; + }, + data: this.dms.files + }; + + this.$http(options).then(res => { + if (res) { + this.vnApp.showSuccess(this.$t('Data saved!')); + if (res.data.length > 0) this.invoiceIn.dmsFk = res.data[0].id; + } + }); + } +} + +Controller.$inject = ['$element', '$scope', 'vnFile']; ngModule.vnComponent('vnInvoiceInBasicData', { template: require('./index.html'), - controller: Section, + controller: Controller, bindings: { invoiceIn: '<' } diff --git a/modules/invoiceIn/front/basic-data/index.spec.js b/modules/invoiceIn/front/basic-data/index.spec.js new file mode 100644 index 000000000..09aa08293 --- /dev/null +++ b/modules/invoiceIn/front/basic-data/index.spec.js @@ -0,0 +1,102 @@ +import './index.js'; +import watcher from 'core/mocks/watcher'; + +describe('InvoiceIn', () => { + describe('Component vnInvoiceInBasicData', () => { + let controller; + let $scope; + let $httpBackend; + let $httpParamSerializer; + + beforeEach(ngModule('invoiceIn')); + + beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => { + $scope = $rootScope.$new(); + $httpBackend = _$httpBackend_; + $httpParamSerializer = _$httpParamSerializer_; + const $element = angular.element(''); + controller = $componentController('vnInvoiceInBasicData', {$element, $scope}); + controller.$.watcher = watcher; + $httpBackend.expect('GET', `DmsContainers/allowedContentTypes`).respond({}); + })); + + describe('onFileChange()', () => { + it('should set dms hasFileAttached property to true if has any files', () => { + const files = [{id: 1, name: 'MyFile'}]; + controller.onFileChange(files); + + $scope.$apply(); + + expect(controller.dms.hasFileAttached).toBeTruthy(); + }); + }); + + describe('checkFileExists()', () => { + it(`should return false if a file exists`, () => { + const fileIdExists = 1; + controller.checkFileExists(fileIdExists); + + expect(controller.editDownloadDisabled).toBe(false); + }); + }); + + describe('onEdit()', () => { + it(`should perform a POST query to edit the dms properties`, () => { + jest.spyOn(controller.vnApp, 'showSuccess'); + + const dms = { + dmsId: 1, + reference: 'Ref1', + warehouseId: 1, + companyId: 442, + dmsTypeId: 20, + description: 'This is a description', + files: [] + }; + + controller.dms = dms; + const serializedParams = $httpParamSerializer(controller.dms); + const query = `dms/${controller.dms.dmsId}/updateFile?${serializedParams}`; + + $httpBackend.expectPOST(query).respond({}); + controller.onEdit(); + $httpBackend.flush(); + + expect(controller.vnApp.showSuccess).toHaveBeenCalled(); + }); + }); + + describe('onCreate()', () => { + it(`should perform a POST query to create a new dms`, () => { + jest.spyOn(controller.vnApp, 'showSuccess'); + + const dms = { + reference: 'Ref1', + warehouseId: 1, + companyId: 442, + dmsTypeId: 20, + description: 'This is a description', + files: [{ + lastModified: 1668673957761, + lastModifiedDate: new Date(), + name: 'file-example.png', + size: 19653, + type: 'image/png', + webkitRelativePath: '' + }] + }; + + controller.dms = dms; + const serializedParams = $httpParamSerializer(controller.dms); + const query = `Dms/uploadFile?${serializedParams}`; + + $httpBackend.expectPOST(query).respond({}); + controller.onCreate(); + $httpBackend.flush(); + + expect(controller.vnApp.showSuccess).toHaveBeenCalled(); + }); + }); + }); +}); + diff --git a/modules/invoiceIn/front/basic-data/locale/en.yml b/modules/invoiceIn/front/basic-data/locale/en.yml new file mode 100644 index 000000000..19f4dc8c2 --- /dev/null +++ b/modules/invoiceIn/front/basic-data/locale/en.yml @@ -0,0 +1 @@ +ContentTypesInfo: Allowed file types {{allowedContentTypes}} diff --git a/modules/invoiceIn/front/basic-data/locale/es.yml b/modules/invoiceIn/front/basic-data/locale/es.yml new file mode 100644 index 000000000..e2e494fa5 --- /dev/null +++ b/modules/invoiceIn/front/basic-data/locale/es.yml @@ -0,0 +1,15 @@ +Upload file: Subir fichero +Edit file: Editar fichero +Upload: Subir +Document: Documento +ContentTypesInfo: "Tipos de archivo permitidos: {{allowedContentTypes}}" +Generate identifier for original file: Generar identificador para archivo original +File management: GestiĆ³n documental +Hard copy: Copia +This file will be deleted: Este fichero va a ser borrado +Are you sure?: Estas seguro? +File deleted: Fichero eliminado +Remove file: Eliminar fichero +Download file: Descargar fichero +Edit document: Editar documento +Create document: Crear documento