diff --git a/CHANGELOG.md b/CHANGELOG.md index e89f394b0..1be03b733 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [2316.01] - 2023-05-04 ### Added -- +- (Usuarios -> Histórico) Nueva sección +- (Roles -> Histórico) Nueva sección ### Changed - (Artículo -> Precio fijado) Modificado el buscador superior por uno lateral diff --git a/db/changes/230601/00-acl_receiptEmail.sql b/db/changes/230601/00-acl_receiptEmail.sql new file mode 100644 index 000000000..2de8adf50 --- /dev/null +++ b/db/changes/230601/00-acl_receiptEmail.sql @@ -0,0 +1,3 @@ +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) + VALUES + ('Receipt', 'receiptEmail', '*', 'ALLOW', 'ROLE', 'salesAssistant'); diff --git a/db/changes/231402/00-negativeBases.sql b/db/changes/231402/00-negativeBases.sql new file mode 100644 index 000000000..f7d6cdfb0 --- /dev/null +++ b/db/changes/231402/00-negativeBases.sql @@ -0,0 +1,3 @@ +UPDATE `salix`.`ACL` + SET model = 'InvoiceOut' + WHERE property IN ('negativeBases', 'negativeBasesCsv'); diff --git a/db/changes/231601/00-User.sql b/db/changes/231601/00-User.sql deleted file mode 100644 index 6c80d2c2d..000000000 --- a/db/changes/231601/00-User.sql +++ /dev/null @@ -1,21 +0,0 @@ -create or replace definer = root@localhost view User as -select `account`.`user`.`id` AS `id`, - `account`.`user`.`realm` AS `realm`, - `account`.`user`.`name` AS `name`, - `account`.`user`.`nickname` AS `nickname`, - `account`.`user`.`bcryptPassword` AS `password`, - `account`.`user`.`role` AS `role`, - `account`.`user`.`active` AS `active`, - `account`.`user`.`email` AS `email`, - `account`.`user`.`emailVerified` AS `emailVerified`, - `account`.`user`.`verificationToken` AS `verificationToken`, - `account`.`user`.`lang` AS `lang`, - `account`.`user`.`lastPassChange` AS `lastPassChange`, - `account`.`user`.`created` AS `created`, - `account`.`user`.`updated` AS `updated`, - `account`.`user`.`image` AS `image`, - `account`.`user`.`recoverPass` AS `recoverPass`, - `account`.`user`.`sync` AS `sync`, - `account`.`user`.`hasGrant` AS `hasGrant` -from `account`.`user`; - diff --git a/db/changes/231601/00-deleteProcs_refund.sql b/db/changes/231601/00-deleteProcs_refund.sql new file mode 100644 index 000000000..8bf8982f4 --- /dev/null +++ b/db/changes/231601/00-deleteProcs_refund.sql @@ -0,0 +1,2 @@ +DROP PROCEDURE `vn`.`refund`; +DROP PROCEDURE `vn`.`ticket_doRefund`; diff --git a/db/changes/231601/00-newCompanyI18n.sql b/db/changes/231601/00-newCompanyI18n.sql new file mode 100644 index 000000000..948b9cb08 --- /dev/null +++ b/db/changes/231601/00-newCompanyI18n.sql @@ -0,0 +1,9 @@ +-- vn.companyI18n definition +CREATE TABLE `vn`.`companyI18n` ( + `companyFk` smallint(5) unsigned NOT NULL, + `lang` char(2) CHARACTER SET utf8mb3 NOT NULL, + `footnotes` longtext COLLATE utf8mb3_unicode_ci DEFAULT NULL, + PRIMARY KEY (`companyFk`,`lang`), + CONSTRAINT `companyI18n_FK` FOREIGN KEY (`companyFk`) REFERENCES `company` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + diff --git a/db/changes/231601/00-newTableWeb.sql b/db/changes/231601/00-newTableWeb.sql new file mode 100644 index 000000000..1a2402956 --- /dev/null +++ b/db/changes/231601/00-newTableWeb.sql @@ -0,0 +1 @@ +ALTER TABLE `vn`.`company` ADD `web` varchar(100) NULL; \ No newline at end of file diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 4dc7dcafc..15ccece35 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -546,7 +546,8 @@ INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif` VALUES (1, 'Plants SL', 'Plants nick', 4100000001, 1, '06089160W', 0, util.VN_CURDATE(), 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, 4, 1, 1, 18, 'flowerPlants', 1, '400664487V'), (2, 'Farmer King', 'The farmer', 4000020002, 1, '87945234L', 0, util.VN_CURDATE(), 1, 'supplier address 2', 'GOTHAM', 2, 43022, 1, 2, 10, 93, 2, 8, 18, 'animals', 1, '400664487V'), - (442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, '06815934E', 0, util.VN_CURDATE(), 1, 'supplier address 3', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V'); + (442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, '06815934E', 0, util.VN_CURDATE(), 1, 'supplier address 3', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V'), + (1381, 'Ornamentales', 'Ornamentales', 7185000440, 1, '03815934E', 0, util.VN_CURDATE(), 1, 'supplier address 4', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V'); INSERT INTO `vn`.`supplierContact`(`id`, `supplierFk`, `phone`, `mobile`, `email`, `observation`, `name`) VALUES @@ -2789,7 +2790,7 @@ INSERT INTO `vn`.`profileType` (`id`, `name`) INSERT INTO `salix`.`url` (`appName`, `environment`, `url`) VALUES - ('lilium', 'dev', 'http://localhost:8080/#/'), + ('lilium', 'dev', 'http://localhost:9000/#/'), ('salix', 'dev', 'http://localhost:5000/#!/'); INSERT INTO `vn`.`report` (`id`, `name`, `paperSizeFk`, `method`) diff --git a/db/export-data.sh b/db/export-data.sh index bdf8049e0..11358e64c 100755 --- a/db/export-data.sh +++ b/db/export-data.sh @@ -68,6 +68,7 @@ TABLES=( time volumeConfig workCenter + companyI18n ) dump_tables ${TABLES[@]} diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 46dea0ddc..1740db7ca 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -740,6 +740,7 @@ export default { anyDocument: 'vn-ticket-dms-index > vn-data-viewer vn-tbody vn-tr' }, ticketFuture: { + searchResult: 'vn-ticket-future tbody tr', openAdvancedSearchButton: 'vn-searchbar .append vn-icon[icon="arrow_drop_down"]', originDated: 'vn-date-picker[label="Origin date"]', futureDated: 'vn-date-picker[label="Destination date"]', @@ -755,7 +756,6 @@ export default { problems: 'vn-check[label="With problems"]', tableButtonSearch: 'vn-button[vn-tooltip="Search"]', moveButton: 'vn-button[vn-tooltip="Future tickets"]', - acceptButton: '.vn-confirm.shown button[response="accept"]', firstCheck: 'tbody > tr:nth-child(1) > td > vn-check', multiCheck: 'vn-multi-check', tableId: 'vn-textfield[name="id"]', diff --git a/e2e/paths/01-salix/03_smartTable_searchBar_integrations.spec.js b/e2e/paths/01-salix/03_smartTable_searchBar_integrations.spec.js index 25735e50a..f9844d5f7 100644 --- a/e2e/paths/01-salix/03_smartTable_searchBar_integrations.spec.js +++ b/e2e/paths/01-salix/03_smartTable_searchBar_integrations.spec.js @@ -81,9 +81,7 @@ describe('SmartTable SearchBar integration', () => { await page.accessToSection('item.fixedPrice'); await page.keyboard.press('Enter'); - const result = await page.waitToGetProperty(selectors.itemFixedPrice.firstItemID, 'value'); - - expect(result).toEqual('1'); + await page.waitForTextInField(selectors.itemFixedPrice.firstItemID, '1'); }); it('should order by last id, reload page and have same order', async() => { @@ -91,9 +89,7 @@ describe('SmartTable SearchBar integration', () => { await page.reload({ waitUntil: 'networkidle2' }); - const result = await page.waitToGetProperty(selectors.itemFixedPrice.firstItemID, 'value'); - - expect(result).toEqual('13'); + await page.waitForTextInField(selectors.itemFixedPrice.firstItemID, '13'); }); }); }); diff --git a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js index 323646d29..1b3204046 100644 --- a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js +++ b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js @@ -246,6 +246,7 @@ describe('Ticket Edit sale path', () => { it('should select the third sale and create a claim of it', async() => { await page.accessToSearchResult('16'); await page.accessToSection('ticket.card.sale'); + await page.waitToClick(selectors.ticketSales.firstSaleCheckbox); await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox); await page.waitToClick(selectors.ticketSales.moreMenu); await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim); diff --git a/e2e/paths/05-ticket/21_future.spec.js b/e2e/paths/05-ticket/21_future.spec.js index 626056958..783b0c9b2 100644 --- a/e2e/paths/05-ticket/21_future.spec.js +++ b/e2e/paths/05-ticket/21_future.spec.js @@ -126,10 +126,11 @@ describe('Ticket Future path', () => { }); it('should check the three last tickets and move to the future', async() => { + await page.waitForNumberOfElements(selectors.ticketFuture.searchResult, 4); await page.waitToClick(selectors.ticketFuture.multiCheck); await page.waitToClick(selectors.ticketFuture.firstCheck); await page.waitToClick(selectors.ticketFuture.moveButton); - await page.waitToClick(selectors.ticketFuture.acceptButton); + await page.waitToClick(selectors.globalItems.acceptButton); const message = await page.waitForSnackbar(); expect(message.text).toContain('Tickets moved successfully!'); diff --git a/e2e/paths/09-invoice-in/05_serial.spec.js b/e2e/paths/09-invoice-in/05_serial.spec.js index 3aa94f48c..8be5660da 100644 --- a/e2e/paths/09-invoice-in/05_serial.spec.js +++ b/e2e/paths/09-invoice-in/05_serial.spec.js @@ -35,7 +35,7 @@ describe('InvoiceIn serial path', () => { }); it('should go to index and check if the search-panel has the correct params', async() => { - await page.click(selectors.invoiceInSerial.goToIndex); + await page.waitToClick(selectors.invoiceInSerial.goToIndex); const params = await page.$$(selectors.invoiceInIndex.topbarSearchParams); const serial = await params[0].getProperty('title'); const isBooked = await params[1].getProperty('title'); diff --git a/e2e/paths/09-invoice-in/05_negative_bases.spec.js b/e2e/paths/09-invoice-out/05_negative_bases.spec.js similarity index 70% rename from e2e/paths/09-invoice-in/05_negative_bases.spec.js rename to e2e/paths/09-invoice-out/05_negative_bases.spec.js index 4c9fe651f..43ced2115 100644 --- a/e2e/paths/09-invoice-in/05_negative_bases.spec.js +++ b/e2e/paths/09-invoice-out/05_negative_bases.spec.js @@ -1,6 +1,6 @@ import getBrowser from '../../helpers/puppeteer'; -describe('InvoiceIn negative bases path', () => { +describe('InvoiceOut negative bases path', () => { let browser; let page; const httpRequests = []; @@ -9,11 +9,11 @@ describe('InvoiceIn negative bases path', () => { browser = await getBrowser(); page = browser.page; page.on('request', req => { - if (req.url().includes(`InvoiceIns/negativeBases`)) + if (req.url().includes(`InvoiceOuts/negativeBases`)) httpRequests.push(req.url()); }); - await page.loginAndModule('administrative', 'invoiceIn'); - await page.accessToSection('invoiceIn.negative-bases'); + await page.loginAndModule('administrative', 'invoiceOut'); + await page.accessToSection('invoiceOut.negative-bases'); }); afterAll(async() => { diff --git a/loopback/locale/en.json b/loopback/locale/en.json index ff51452dd..ae0da8170 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -156,6 +156,19 @@ "Component cost not set": "Componente coste no está estabecido", "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2", "Description cannot be blank": "Description cannot be blank", + "company": "Company", + "country": "Country", + "clientId": "Id client", + "clientSocialName": "Client", + "amount": "Amount", + "taxableBase": "Taxable base", + "ticketFk": "Id ticket", + "isActive": "Active", + "hasToInvoice": "Invoice", + "isTaxDataChecked": "Data checked", + "comercialId": "Id Comercial", + "comercialName": "Comercial", "Added observation": "Added observation", - "Comment added to client": "Comment added to client" -} + "Comment added to client": "Comment added to client", + "This ticket is already a refund": "This ticket is already a refund" +} \ No newline at end of file diff --git a/loopback/locale/es.json b/loopback/locale/es.json index d52eacff6..3ef3c4a22 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -275,7 +275,19 @@ "Collection does not exist": "La colección no existe", "Cannot obtain exclusive lock": "No se puede obtener un bloqueo exclusivo", "Insert a date range": "Inserte un rango de fechas", - "Added observation": "{{user}} añadió esta observacion: {{text}}", - "Comment added to client": "Observación añadida al cliente {{clientFk}}", - "Cannot create a new claimBeginning from a different ticket": "No se puede crear una línea de reclamación de un ticket diferente al origen" + "Added observation": "{{user}} añadió esta observacion: {{text}}", + "Comment added to client": "Observación añadida al cliente {{clientFk}}", + "Cannot create a new claimBeginning from a different ticket": "No se puede crear una línea de reclamación de un ticket diferente al origen", + "company": "Compañía", + "country": "País", + "clientId": "Id cliente", + "clientSocialName": "Cliente", + "amount": "Importe", + "taxableBase": "Base", + "ticketFk": "Id ticket", + "isActive": "Activo", + "hasToInvoice": "Facturar", + "isTaxDataChecked": "Datos comprobados", + "comercialId": "Id comercial", + "comercialName": "Comercial" } diff --git a/modules/account/front/main/index.html b/modules/account/front/main/index.html index dd3489e9f..5872a328d 100644 --- a/modules/account/front/main/index.html +++ b/modules/account/front/main/index.html @@ -6,6 +6,7 @@ - \ No newline at end of file + diff --git a/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.js b/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.js index f0686ffa6..83043f012 100644 --- a/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.js +++ b/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.js @@ -109,6 +109,11 @@ module.exports = Self => { zoneFk: zone.id }, myOptions); + await models.TicketRefund.create({ + refundTicketFk: newRefundTicket.id, + originalTicketFk: claim.ticket().id + }, myOptions); + await saveObservation({ description: `Reclama ticket: ${claim.ticketFk}`, ticketFk: newRefundTicket.id, diff --git a/modules/claim/front/action/index.html b/modules/claim/front/action/index.html index 81b14d3a7..9da51b8de 100644 --- a/modules/claim/front/action/index.html +++ b/modules/claim/front/action/index.html @@ -16,7 +16,7 @@ value="{{$ctrl.claimedTotal | currency: 'EUR':2}}"> - + - + label="Change destination" + disabled="$ctrl.checked.length == 0" + ng-click="changeDestination.show()"> + + + - - - + diff --git a/modules/claim/front/descriptor/index.js b/modules/claim/front/descriptor/index.js index 0dddadbe1..5e9ea5140 100644 --- a/modules/claim/front/descriptor/index.js +++ b/modules/claim/front/descriptor/index.js @@ -17,6 +17,10 @@ class Controller extends Descriptor { } sendPickupOrder() { + if (!this.claim.client.email) { + this.vnApp.showError(this.$t('The client does not have an email')); + return; + } return this.vnEmail.send(`Claims/${this.claim.id}/claim-pickup-email`, { recipient: this.claim.client.email, recipientId: this.claim.clientFk diff --git a/modules/claim/front/locale/es.yml b/modules/claim/front/locale/es.yml index 419e62f56..f6dac2b83 100644 --- a/modules/claim/front/locale/es.yml +++ b/modules/claim/front/locale/es.yml @@ -20,3 +20,4 @@ Photos: Fotos Go to the claim: Ir a la reclamación Sale tracking: Líneas preparadas Ticket tracking: Estados del ticket +The client does not have an email: El cliente no tiene email diff --git a/modules/claim/front/photos/index.html b/modules/claim/front/photos/index.html index 9e00ee02f..8b1378917 100644 --- a/modules/claim/front/photos/index.html +++ b/modules/claim/front/photos/index.html @@ -1,48 +1 @@ - - - -
-
-
Drag & Drop photos here...
-
-
-
-
- -
- - -
-
-
- - - - + diff --git a/modules/claim/front/photos/index.js b/modules/claim/front/photos/index.js index 62e439a91..c9fada9a4 100644 --- a/modules/claim/front/photos/index.js +++ b/modules/claim/front/photos/index.js @@ -1,105 +1,17 @@ import ngModule from '../module'; import Section from 'salix/components/section'; -import './style.scss'; class Controller extends Section { - constructor($element, $, vnFile) { + constructor($element, $) { super($element, $); - this.vnFile = vnFile; - this.filter = { - include: [ - { - relation: 'dms' - } - ] - }; } - deleteDms(index) { - const dmsFk = this.photos[index].dmsFk; - return this.$http.post(`ClaimDms/${dmsFk}/removeFile`) - .then(() => { - this.$.model.remove(index); - this.vnApp.showSuccess(this.$t('File deleted')); - }); - } - - onDrop($event) { - const files = $event.dataTransfer.files; - this.setDefaultParams().then(() => { - this.dms.files = files; - this.create(); - }); - } - - setDefaultParams() { - const filter = { - where: {code: 'claim'} - }; - return this.$http.get('DmsTypes/findOne', {filter}).then(res => { - const dmsTypeId = res.data && res.data.id; - const companyId = this.vnConfig.companyFk; - const warehouseId = this.vnConfig.warehouseFk; - this.dms = { - hasFile: false, - hasFileAttached: false, - reference: this.claim.id, - warehouseId: warehouseId, - companyId: companyId, - dmsTypeId: dmsTypeId, - description: this.$t('FileDescription', { - claimId: this.claim.id, - clientId: this.claim.client.id, - clientName: this.claim.client.name - }).toUpperCase() - }; - }); - } - - openUploadDialog() { - const element = document.createElement('input'); - element.setAttribute('type', 'file'); - element.setAttribute('multiple', true); - element.click(); - - element.addEventListener('change', () => - this.setDefaultParams().then(() => { - this.dms.files = element.files; - this.create(); - }) - ); - } - - create() { - const query = `claims/${this.claim.id}/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(() => { - this.vnApp.showSuccess(this.$t('File uploaded!')); - this.$.model.refresh(); - }); - } - - getImagePath(dmsId) { - return this.vnFile.getPath(`/api/Claims/${dmsId}/downloadFile`); + async $onInit() { + const url = await this.vnApp.getUrl(`claim/${this.$params.id}/photos`); + window.location.href = url; } } -Controller.$inject = ['$element', '$scope', 'vnFile']; - ngModule.vnComponent('vnClaimPhotos', { template: require('./index.html'), controller: Controller, diff --git a/modules/claim/front/photos/index.spec.js b/modules/claim/front/photos/index.spec.js deleted file mode 100644 index 84df48b44..000000000 --- a/modules/claim/front/photos/index.spec.js +++ /dev/null @@ -1,70 +0,0 @@ -import './index'; -import crudModel from 'core/mocks/crud-model'; - -describe('Claim', () => { - describe('Component vnClaimPhotos', () => { - let $scope; - let $httpBackend; - let controller; - - beforeEach(ngModule('claim')); - - beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => { - $httpBackend = _$httpBackend_; - $scope = $rootScope.$new(); - controller = $componentController('vnClaimPhotos', {$element: null, $scope}); - controller.$.model = crudModel; - controller.claim = { - id: 1, - client: {id: 1101, name: 'Bruce Wayne'} - }; - })); - - describe('deleteDms()', () => { - it('should make an HTTP Post query', () => { - jest.spyOn(controller.vnApp, 'showSuccess'); - jest.spyOn(controller.$.model, 'remove'); - - const dmsId = 1; - const dmsIndex = 0; - controller.photos = [{dmsFk: 1}]; - - $httpBackend.expectPOST(`ClaimDms/${dmsId}/removeFile`).respond(); - controller.deleteDms(dmsIndex); - $httpBackend.flush(); - - expect(controller.$.model.remove).toHaveBeenCalledWith(dmsIndex); - expect(controller.vnApp.showSuccess).toHaveBeenCalled(); - }); - }); - - describe('setDefaultParams()', () => { - it('should make an HTTP GET query, then set all dms properties', () => { - $httpBackend.expectRoute('GET', `DmsTypes/findOne`).respond({}); - controller.setDefaultParams(); - $httpBackend.flush(); - - expect(controller.dms).toBeDefined(); - }); - }); - - describe('create()', () => { - it('should make an HTTP Post query, then refresh the model data', () => { - const claimId = 1; - const dmsIndex = 0; - jest.spyOn(controller.vnApp, 'showSuccess'); - jest.spyOn(controller.$.model, 'refresh'); - controller.photos = [{dmsFk: 1}]; - controller.dmsIndex = dmsIndex; - controller.dms = {files: []}; - - $httpBackend.expectPOST(`claims/${claimId}/uploadFile`).respond({}); - controller.create(); - $httpBackend.flush(); - - expect(controller.$.model.refresh).toHaveBeenCalled(); - expect(controller.vnApp.showSuccess).toHaveBeenCalled(); - }); - }); - }); -}); diff --git a/modules/claim/front/photos/locale/es.yml b/modules/claim/front/photos/locale/es.yml deleted file mode 100644 index d2ee9ffbd..000000000 --- a/modules/claim/front/photos/locale/es.yml +++ /dev/null @@ -1,5 +0,0 @@ -Are you sure you want to continue?: ¿Seguro que quieres continuar? -Drag & Drop photos here...: Arrastra y suelta fotos aquí... -File deleted: Archivo eliminado -File uploaded!: Archivo subido! -Select file: Seleccionar fichero \ No newline at end of file diff --git a/modules/claim/front/photos/style.scss b/modules/claim/front/photos/style.scss deleted file mode 100644 index 101cb0da2..000000000 --- a/modules/claim/front/photos/style.scss +++ /dev/null @@ -1,47 +0,0 @@ -@import "./variables"; - -vn-claim-photos { - height: 100%; - - .drop-zone { - color: $color-font-secondary; - box-sizing: border-box; - border-radius: 8px; - text-align: center; - min-height: 100%; - - .empty-rows { - padding: 80px $spacing-md; - font-size: 1.375rem - } - - vn-icon { - font-size: 3rem - } - } - - .photo-list { - padding: $spacing-md; - min-height: 100%; - - .photo { - width: 512px; - height: 288px; - } - } - - .video { - width: 100%; - height: 100%; - object-fit: cover; - cursor: pointer; - box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), - 0 3px 1px -2px rgba(0,0,0,.2), - 0 1px 5px 0 rgba(0,0,0,.12); - border: 2px solid transparent; - - } - .video:hover { - border: 2px solid $color-primary - } -} diff --git a/modules/client/back/methods/client/getCard.js b/modules/client/back/methods/client/getCard.js index a2365ee25..414cbe058 100644 --- a/modules/client/back/methods/client/getCard.js +++ b/modules/client/back/methods/client/getCard.js @@ -76,7 +76,7 @@ module.exports = function(Self) { const date = Date.vnNew(); date.setHours(0, 0, 0, 0); - const query = `SELECT vn.clientGetDebt(?, ?) AS debt`; + const query = `SELECT vn.client_getDebt(?, ?) AS debt`; const data = await Self.rawSql(query, [id, date], myOptions); client.debt = data[0].debt; diff --git a/modules/client/back/methods/client/getDebt.js b/modules/client/back/methods/client/getDebt.js index 5f8a8c569..859746083 100644 --- a/modules/client/back/methods/client/getDebt.js +++ b/modules/client/back/methods/client/getDebt.js @@ -27,7 +27,7 @@ module.exports = Self => { const date = Date.vnNew(); date.setHours(0, 0, 0, 0); - const query = `SELECT vn.clientGetDebt(?, ?) AS debt`; + const query = `SELECT vn.client_getDebt(?, ?) AS debt`; const [debt] = await Self.rawSql(query, [clientFk, date], myOptions); return debt; diff --git a/modules/client/back/methods/receipt/receiptEmail.js b/modules/client/back/methods/receipt/receiptEmail.js new file mode 100644 index 000000000..cd529eece --- /dev/null +++ b/modules/client/back/methods/receipt/receiptEmail.js @@ -0,0 +1,57 @@ +const {Email} = require('vn-print'); + +module.exports = Self => { + Self.remoteMethodCtx('receiptEmail', { + description: 'Returns the receipt pdf', + accepts: [ + { + arg: 'id', + type: 'number', + required: true, + description: 'The claim id', + http: {source: 'path'} + }, + { + arg: 'recipient', + type: 'string', + description: 'The recipient email', + required: true, + } + ], + returns: [ + { + arg: 'body', + type: 'file', + root: true + }, { + arg: 'Content-Type', + type: 'String', + http: {target: 'header'} + }, { + arg: 'Content-Disposition', + type: 'String', + http: {target: 'header'} + } + ], + http: { + path: '/:id/receipt-email', + verb: 'POST' + } + }); + + Self.receiptEmail = async(ctx, id) => { + const args = Object.assign({}, ctx.args); + const params = { + recipient: args.recipient, + lang: ctx.req.getLocale() + }; + + delete args.ctx; + for (const param in args) + params[param] = args[param]; + + const email = new Email('receipt', params); + + return email.send(); + }; +}; diff --git a/modules/client/back/methods/receipt/receiptPdf.js b/modules/client/back/methods/receipt/receiptPdf.js index 433f386db..6e49de22a 100644 --- a/modules/client/back/methods/receipt/receiptPdf.js +++ b/modules/client/back/methods/receipt/receiptPdf.js @@ -1,12 +1,12 @@ module.exports = Self => { Self.remoteMethodCtx('receiptPdf', { - description: 'Returns the receipt pdf', + description: 'Send the receipt pdf to client', accepts: [ { arg: 'id', type: 'number', required: true, - description: 'The claim id', + description: 'The receipt id', http: {source: 'path'} }, { diff --git a/modules/client/back/models/receipt.js b/modules/client/back/models/receipt.js index 3118cc239..feb8ca053 100644 --- a/modules/client/back/models/receipt.js +++ b/modules/client/back/models/receipt.js @@ -5,6 +5,7 @@ module.exports = function(Self) { require('../methods/receipt/balanceCompensationEmail')(Self); require('../methods/receipt/balanceCompensationPdf')(Self); require('../methods/receipt/receiptPdf')(Self); + require('../methods/receipt/receiptEmail')(Self); Self.validateBinded('amountPaid', isNotZero, { message: 'Amount cannot be zero', diff --git a/modules/client/front/balance/create/index.html b/modules/client/front/balance/create/index.html index b76c09356..524b2b2ef 100644 --- a/modules/client/front/balance/create/index.html +++ b/modules/client/front/balance/create/index.html @@ -84,6 +84,10 @@ label="View receipt" ng-model="$ctrl.viewReceipt"> + + diff --git a/modules/client/front/balance/create/index.js b/modules/client/front/balance/create/index.js index cc9e1ab5a..9113d7605 100644 --- a/modules/client/front/balance/create/index.js +++ b/modules/client/front/balance/create/index.js @@ -2,9 +2,10 @@ import ngModule from '../../module'; import Dialog from 'core/components/dialog'; class Controller extends Dialog { - constructor($element, $, $transclude, vnReport) { + constructor($element, $, $transclude, vnReport, vnEmail) { super($element, $, $transclude); this.vnReport = vnReport; + this.vnEmail = vnEmail; this.receipt = {}; } @@ -23,6 +24,18 @@ class Controller extends Dialog { set clientFk(value) { this.receipt.clientFk = value; + + const filter = { + fields: ['email'], + where: { + id: value + } + }; + + this.$http.get(`Clients/findOne`, {filter}) + .then(res => { + this.receipt.email = res.data.email; + }); } get clientFk() { @@ -154,10 +167,13 @@ class Controller extends Dialog { return super.responseHandler(response); const exceededAmount = this.receipt.amountPaid > this.maxAmount; - - if (this.bankSelection.accountingType.code == 'cash' && exceededAmount) + const isCash = this.bankSelection.accountingType.code == 'cash'; + if (isCash && exceededAmount) return this.vnApp.showError(this.$t('Amount exceeded', {maxAmount: this.maxAmount})); + if (isCash && this.sendEmail && !this.receipt.email) + return this.vnApp.showError(this.$t('There is no assigned email for this client')); + let receiptId; return this.$http.post(`Clients/${this.clientFk}/createReceipt`, this.receipt) .then(res => { @@ -165,6 +181,13 @@ class Controller extends Dialog { super.responseHandler(response); }) .then(() => this.vnApp.showSuccess(this.$t('Data saved!'))) + .then(() => { + if (!this.sendEmail || !isCash) return; + const params = { + recipient: this.receipt.email + }; + this.vnEmail.send(`Receipts/${receiptId}/receipt-email`, params); + }) .then(() => { if (this.viewReceipt) this.vnReport.show(`Receipts/${receiptId}/receipt-pdf`); @@ -178,7 +201,7 @@ class Controller extends Dialog { } } -Controller.$inject = ['$element', '$scope', '$transclude', 'vnReport']; +Controller.$inject = ['$element', '$scope', '$transclude', 'vnReport', 'vnEmail']; ngModule.vnComponent('vnClientBalanceCreate', { slotTemplate: require('./index.html'), diff --git a/modules/entry/front/latest-buys/index.html b/modules/entry/front/latest-buys/index.html index 727b19220..a2cf62da2 100644 --- a/modules/entry/front/latest-buys/index.html +++ b/modules/entry/front/latest-buys/index.html @@ -192,19 +192,19 @@ {{::buy.entryFk}} - - - - + + + + - - - + + + diff --git a/modules/invoiceIn/back/methods/invoice-in/negativeBasesCsv.js b/modules/invoiceIn/back/methods/invoice-in/negativeBasesCsv.js deleted file mode 100644 index 963151b7d..000000000 --- a/modules/invoiceIn/back/methods/invoice-in/negativeBasesCsv.js +++ /dev/null @@ -1,53 +0,0 @@ -const {toCSV} = require('vn-loopback/util/csv'); - -module.exports = Self => { - Self.remoteMethodCtx('negativeBasesCsv', { - description: 'Returns the negative bases as .csv', - accessType: 'READ', - accepts: [{ - arg: 'negativeBases', - type: ['object'], - required: true - }, - { - arg: 'from', - type: 'date', - description: 'From date' - }, - { - arg: 'to', - type: 'date', - description: 'To date' - }], - returns: [ - { - arg: 'body', - type: 'file', - root: true - }, { - arg: 'Content-Type', - type: 'String', - http: {target: 'header'} - }, { - arg: 'Content-Disposition', - type: 'String', - http: {target: 'header'} - } - ], - http: { - path: '/negativeBasesCsv', - verb: 'GET' - } - }); - - Self.negativeBasesCsv = async ctx => { - const args = ctx.args; - const content = toCSV(args.negativeBases); - - return [ - content, - 'text/csv', - `attachment; filename="negative-bases-${new Date(args.from).toLocaleDateString()}-${new Date(args.to).toLocaleDateString()}.csv"` - ]; - }; -}; diff --git a/modules/invoiceIn/back/models/invoice-in.js b/modules/invoiceIn/back/models/invoice-in.js index 167f2ac34..51905ccb8 100644 --- a/modules/invoiceIn/back/models/invoice-in.js +++ b/modules/invoiceIn/back/models/invoice-in.js @@ -7,6 +7,4 @@ module.exports = Self => { require('../methods/invoice-in/invoiceInPdf')(Self); require('../methods/invoice-in/invoiceInEmail')(Self); require('../methods/invoice-in/getSerial')(Self); - require('../methods/invoice-in/negativeBases')(Self); - require('../methods/invoice-in/negativeBasesCsv')(Self); }; diff --git a/modules/invoiceIn/front/index.js b/modules/invoiceIn/front/index.js index c0374e996..e257cfee3 100644 --- a/modules/invoiceIn/front/index.js +++ b/modules/invoiceIn/front/index.js @@ -15,4 +15,3 @@ import './create'; import './log'; import './serial'; import './serial-search-panel'; -import './negative-bases'; diff --git a/modules/invoiceIn/front/negative-bases/locale/es.yml b/modules/invoiceIn/front/negative-bases/locale/es.yml deleted file mode 100644 index 9095eee22..000000000 --- a/modules/invoiceIn/front/negative-bases/locale/es.yml +++ /dev/null @@ -1,14 +0,0 @@ -Has To Invoice: Facturar -Download as CSV: Descargar como CSV -company: Compañía -country: País -clientId: Id Cliente -clientSocialName: Cliente -amount: Importe -taxableBase: Base -ticketFk: Id Ticket -isActive: Activo -hasToInvoice: Facturar -isTaxDataChecked: Datos comprobados -comercialId: Id Comercial -comercialName: Comercial diff --git a/modules/invoiceIn/front/routes.json b/modules/invoiceIn/front/routes.json index 40d061d1b..6f4d3d1a3 100644 --- a/modules/invoiceIn/front/routes.json +++ b/modules/invoiceIn/front/routes.json @@ -10,8 +10,7 @@ "menus": { "main": [ { "state": "invoiceIn.index", "icon": "icon-invoice-in"}, - { "state": "invoiceIn.serial", "icon": "icon-invoice-in"}, - { "state": "invoiceIn.negative-bases", "icon": "icon-ticket"} + { "state": "invoiceIn.serial", "icon": "icon-invoice-in"} ], "card": [ { @@ -53,15 +52,6 @@ "administrative" ] }, - { - "url": "/negative-bases", - "state": "invoiceIn.negative-bases", - "component": "vn-negative-bases", - "description": "Negative bases", - "acl": [ - "administrative" - ] - }, { "url": "/serial", "state": "invoiceIn.serial", diff --git a/modules/invoiceIn/back/methods/invoice-in/negativeBases.js b/modules/invoiceOut/back/methods/invoiceOut/negativeBases.js similarity index 93% rename from modules/invoiceIn/back/methods/invoice-in/negativeBases.js rename to modules/invoiceOut/back/methods/invoiceOut/negativeBases.js index 4c9a8984b..ae9c404af 100644 --- a/modules/invoiceIn/back/methods/invoice-in/negativeBases.js +++ b/modules/invoiceOut/back/methods/invoiceOut/negativeBases.js @@ -96,16 +96,18 @@ module.exports = Self => { SELECT f.* FROM tmp.filter f`); - stmt.merge(conn.makeWhere(args.filter.where)); - stmt.merge(conn.makeOrderBy(args.filter.order)); - stmt.merge(conn.makeLimit(args.filter)); + if (args.filter) { + stmt.merge(conn.makeWhere(args.filter.where)); + stmt.merge(conn.makeOrderBy(args.filter.order)); + stmt.merge(conn.makeLimit(args.filter)); + } const negativeBasesIndex = stmts.push(stmt) - 1; stmts.push(`DROP TEMPORARY TABLE tmp.filter, tmp.ticket, tmp.ticketTax, tmp.ticketAmount`); const sql = ParameterizedSQL.join(stmts, ';'); - const result = await conn.executeStmt(sql, myOptions); + const result = await conn.executeStmt(sql); return negativeBasesIndex === 0 ? result : result[negativeBasesIndex]; }; diff --git a/modules/invoiceOut/back/methods/invoiceOut/negativeBasesCsv.js b/modules/invoiceOut/back/methods/invoiceOut/negativeBasesCsv.js new file mode 100644 index 000000000..d70a8fce5 --- /dev/null +++ b/modules/invoiceOut/back/methods/invoiceOut/negativeBasesCsv.js @@ -0,0 +1,68 @@ +const {toCSV} = require('vn-loopback/util/csv'); + +module.exports = Self => { + Self.remoteMethodCtx('negativeBasesCsv', { + description: 'Returns the negative bases as .csv', + accessType: 'READ', + accepts: [ + { + arg: 'from', + type: 'date', + description: 'From date', + required: true + }, + { + arg: 'to', + type: 'date', + description: 'To date', + required: true + }], + returns: [ + { + arg: 'body', + type: 'file', + root: true + }, { + arg: 'Content-Type', + type: 'String', + http: {target: 'header'} + }, { + arg: 'Content-Disposition', + type: 'String', + http: {target: 'header'} + } + ], + http: { + path: '/negativeBasesCsv', + verb: 'GET' + } + }); + + Self.negativeBasesCsv = async(ctx, options) => { + const $t = ctx.req.__; // $translate + const args = ctx.args; + const myOptions = {}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + const negativeBases = await Self.app.models.InvoiceOut.negativeBases(ctx, myOptions); + const locatedFields = []; + negativeBases.forEach(element => { + locatedFields.push(Object.keys(element).map(key => { + return {newName: $t(key), value: element[key]}; + }).filter(item => item !== null) + .reduce((result, item) => { + result[item.newName] = item.value; + return result; + }, {})); + }); + const content = toCSV(locatedFields); + + return [ + content, + 'text/csv', + `attachment; filename="negative-bases-${new Date(args.from).toLocaleDateString()}-${new Date(args.to).toLocaleDateString()}.csv"` + ]; + }; +}; diff --git a/modules/invoiceOut/back/methods/invoiceOut/refund.js b/modules/invoiceOut/back/methods/invoiceOut/refund.js index ba1fdfedd..ad480dc7d 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/refund.js +++ b/modules/invoiceOut/back/methods/invoiceOut/refund.js @@ -35,7 +35,7 @@ module.exports = Self => { const tickets = await models.Ticket.find(filter, myOptions); const ticketsIds = tickets.map(ticket => ticket.id); - const refundedTickets = await models.Ticket.refund(ticketsIds, true, myOptions); + const refundedTickets = await models.Ticket.refund(ticketsIds, myOptions); if (tx) await tx.commit(); diff --git a/modules/invoiceIn/back/methods/invoice-in/specs/negativeBases.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/negativeBases.spec.js similarity index 71% rename from modules/invoiceIn/back/methods/invoice-in/specs/negativeBases.spec.js rename to modules/invoiceOut/back/methods/invoiceOut/specs/negativeBases.spec.js index a5c6e3102..1164e894b 100644 --- a/modules/invoiceIn/back/methods/invoice-in/specs/negativeBases.spec.js +++ b/modules/invoiceOut/back/methods/invoiceOut/specs/negativeBases.spec.js @@ -1,19 +1,18 @@ const models = require('vn-loopback/server/server').models; -describe('invoiceIn negativeBases()', () => { +describe('invoiceOut negativeBases()', () => { it('should return all negative bases in a date range', async() => { - const tx = await models.InvoiceIn.beginTransaction({}); + const tx = await models.InvoiceOut.beginTransaction({}); const options = {transaction: tx}; const ctx = { args: { from: new Date().setMonth(new Date().getMonth() - 12), - to: new Date(), - filter: {} + to: new Date() } }; try { - const result = await models.InvoiceIn.negativeBases(ctx, options); + const result = await models.InvoiceOut.negativeBases(ctx, options); expect(result.length).toBeGreaterThan(0); @@ -26,7 +25,7 @@ describe('invoiceIn negativeBases()', () => { it('should throw an error if a date range is not in args', async() => { let error; - const tx = await models.InvoiceIn.beginTransaction({}); + const tx = await models.InvoiceOut.beginTransaction({}); const options = {transaction: tx}; const ctx = { args: { @@ -35,7 +34,7 @@ describe('invoiceIn negativeBases()', () => { }; try { - await models.InvoiceIn.negativeBases(ctx, options); + await models.InvoiceOut.negativeBases(ctx, options); await tx.rollback(); } catch (e) { error = e; diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/refund.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/refund.spec.js index c5a1ac603..35f2b4023 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/specs/refund.spec.js +++ b/modules/invoiceOut/back/methods/invoiceOut/specs/refund.spec.js @@ -17,7 +17,7 @@ describe('InvoiceOut refund()', () => { try { const result = await models.InvoiceOut.refund('T1111111', options); - expect(result.length).toEqual(1); + expect(result).toBeDefined(); await tx.rollback(); } catch (e) { diff --git a/modules/invoiceOut/back/models/invoice-out.js b/modules/invoiceOut/back/models/invoice-out.js index 6205abe7b..5fcef7744 100644 --- a/modules/invoiceOut/back/models/invoice-out.js +++ b/modules/invoiceOut/back/models/invoice-out.js @@ -17,4 +17,6 @@ module.exports = Self => { require('../methods/invoiceOut/invoiceCsvEmail')(Self); require('../methods/invoiceOut/invoiceOutPdf')(Self); require('../methods/invoiceOut/getInvoiceDate')(Self); + require('../methods/invoiceOut/negativeBases')(Self); + require('../methods/invoiceOut/negativeBasesCsv')(Self); }; diff --git a/modules/invoiceOut/front/descriptor-menu/index.js b/modules/invoiceOut/front/descriptor-menu/index.js index 456939119..57ea653a8 100644 --- a/modules/invoiceOut/front/descriptor-menu/index.js +++ b/modules/invoiceOut/front/descriptor-menu/index.js @@ -118,8 +118,11 @@ class Controller extends Section { const query = 'InvoiceOuts/refund'; const params = {ref: this.invoiceOut.ref}; this.$http.post(query, params).then(res => { - const ticketIds = res.data.map(ticket => ticket.id).join(', '); - this.vnApp.showSuccess(this.$t('The following refund tickets have been created', {ticketIds})); + const refundTicket = res.data; + this.vnApp.showSuccess(this.$t('The following refund ticket have been created', { + ticketId: refundTicket.id + })); + this.$state.go('ticket.card.sale', {id: refundTicket.id}); }); } } diff --git a/modules/invoiceOut/front/index.js b/modules/invoiceOut/front/index.js index f7cebc0d0..723e3be5a 100644 --- a/modules/invoiceOut/front/index.js +++ b/modules/invoiceOut/front/index.js @@ -10,3 +10,4 @@ import './descriptor-popover'; import './descriptor-menu'; import './index/manual'; import './global-invoicing'; +import './negative-bases'; diff --git a/modules/invoiceIn/front/negative-bases/index.html b/modules/invoiceOut/front/negative-bases/index.html similarity index 96% rename from modules/invoiceIn/front/negative-bases/index.html rename to modules/invoiceOut/front/negative-bases/index.html index 5da8e7aad..26f67c7d4 100644 --- a/modules/invoiceIn/front/negative-bases/index.html +++ b/modules/invoiceOut/front/negative-bases/index.html @@ -1,6 +1,6 @@ @@ -43,7 +43,7 @@ Country
{{::buy.buyingValue | currency: 'EUR':2}}{{::buy.freightValue | currency: 'EUR':2}}{{::buy.comissionValue | currency: 'EUR':2}}{{::buy.packageValue | currency: 'EUR':2}}{{::buy.buyingValue | currency: 'EUR':3}}{{::buy.freightValue | currency: 'EUR':3}}{{::buy.comissionValue | currency: 'EUR':3}}{{::buy.packageValue | currency: 'EUR':3}} {{::buy.price2 | currency: 'EUR':2}}{{::buy.price3 | currency: 'EUR':2}}{{::buy.minPrice | currency: 'EUR':2}}{{::buy.price2 | currency: 'EUR':3}}{{::buy.price3 | currency: 'EUR':3}}{{::buy.minPrice | currency: 'EUR':3}} {{::buy.ektFk | dashIfEmpty}} {{::buy.weight}} {{::buy.packageFk}} - Id Client + Client id Client @@ -55,7 +55,7 @@ Base - Id Ticket + Ticket id Active diff --git a/modules/invoiceIn/front/negative-bases/index.js b/modules/invoiceOut/front/negative-bases/index.js similarity index 79% rename from modules/invoiceIn/front/negative-bases/index.js rename to modules/invoiceOut/front/negative-bases/index.js index 0f6f04692..1a838507c 100644 --- a/modules/invoiceIn/front/negative-bases/index.js +++ b/modules/invoiceOut/front/negative-bases/index.js @@ -58,18 +58,7 @@ export default class Controller extends Section { } downloadCSV() { - const data = []; - this.$.model._orgData.forEach(element => { - data.push(Object.keys(element).map(key => { - return {newName: this.$t(key), value: element[key]}; - }).filter(item => item !== null) - .reduce((result, item) => { - result[item.newName] = item.value; - return result; - }, {})); - }); - this.vnReport.show('InvoiceIns/negativeBasesCsv', { - negativeBases: data, + this.vnReport.show('InvoiceOuts/negativeBasesCsv', { from: this.params.from, to: this.params.to }); diff --git a/modules/invoiceOut/front/negative-bases/locale/es.yml b/modules/invoiceOut/front/negative-bases/locale/es.yml new file mode 100644 index 000000000..dd3432592 --- /dev/null +++ b/modules/invoiceOut/front/negative-bases/locale/es.yml @@ -0,0 +1,2 @@ +Has To Invoice: Facturar +Download as CSV: Descargar como CSV diff --git a/modules/invoiceIn/front/negative-bases/style.scss b/modules/invoiceOut/front/negative-bases/style.scss similarity index 100% rename from modules/invoiceIn/front/negative-bases/style.scss rename to modules/invoiceOut/front/negative-bases/style.scss diff --git a/modules/invoiceOut/front/routes.json b/modules/invoiceOut/front/routes.json index c396a5334..f7f589b01 100644 --- a/modules/invoiceOut/front/routes.json +++ b/modules/invoiceOut/front/routes.json @@ -7,8 +7,8 @@ "menus": { "main": [ {"state": "invoiceOut.index", "icon": "icon-invoice-out"}, - {"state": "invoiceOut.global-invoicing", "icon": "contact_support"} - + {"state": "invoiceOut.global-invoicing", "icon": "contact_support"}, + { "state": "invoiceOut.negative-bases", "icon": "icon-ticket"} ] }, "routes": [ @@ -46,6 +46,15 @@ "state": "invoiceOut.card", "abstract": true, "component": "vn-invoice-out-card" + }, + { + "url": "/negative-bases", + "state": "invoiceOut.negative-bases", + "component": "vn-negative-bases", + "description": "Negative bases", + "acl": [ + "administrative" + ] } ] } diff --git a/modules/item/front/request-search-panel/index.html b/modules/item/front/request-search-panel/index.html index a431d4fd6..dfafb02f3 100644 --- a/modules/item/front/request-search-panel/index.html +++ b/modules/item/front/request-search-panel/index.html @@ -42,7 +42,7 @@ { ENGINE = MEMORY SELECT DISTINCT clientFk FROM tmp.filter`); - stmt = new ParameterizedSQL('CALL clientGetDebt(?)', [args.to]); + stmt = new ParameterizedSQL('CALL client_getDebt(?)', [args.to]); stmts.push(stmt); stmts.push('DROP TEMPORARY TABLE tmp.clientGetDebt'); diff --git a/modules/supplier/back/methods/supplier/specs/filter.spec.js b/modules/supplier/back/methods/supplier/specs/filter.spec.js index 2620bb687..8f8ff1346 100644 --- a/modules/supplier/back/methods/supplier/specs/filter.spec.js +++ b/modules/supplier/back/methods/supplier/specs/filter.spec.js @@ -23,6 +23,6 @@ describe('Supplier filter()', () => { let result = await app.models.Supplier.filter(ctx); - expect(result.length).toEqual(2); + expect(result.length).toEqual(3); }); }); diff --git a/modules/ticket/back/methods/boxing/getVideoList.js b/modules/ticket/back/methods/boxing/getVideoList.js index 3d45a720d..ba989936e 100644 --- a/modules/ticket/back/methods/boxing/getVideoList.js +++ b/modules/ticket/back/methods/boxing/getVideoList.js @@ -53,7 +53,7 @@ module.exports = Self => { let start = new Date(expedition.created); let end = new Date(start.getTime() + (packingSiteConfig.avgBoxingTime * 1000)); - if (from && to) { + if (from != undefined && to != undefined) { start.setHours(from, 0, 0); end.setHours(to, 0, 0); } diff --git a/modules/ticket/back/methods/sale/refund.js b/modules/ticket/back/methods/sale/refund.js index 7b63fd66e..af58a6286 100644 --- a/modules/ticket/back/methods/sale/refund.js +++ b/modules/ticket/back/methods/sale/refund.js @@ -11,11 +11,6 @@ module.exports = Self => { { arg: 'servicesIds', type: ['number'] - }, - { - arg: 'createSingleTicket', - type: 'boolean', - required: false } ], returns: { @@ -28,7 +23,7 @@ module.exports = Self => { } }); - Self.refund = async(salesIds, servicesIds, createSingleTicket = false, options) => { + Self.refund = async(salesIds, servicesIds, options) => { const models = Self.app.models; const myOptions = {}; let tx; @@ -67,40 +62,14 @@ module.exports = Self => { const sales = await models.Sale.find(salesFilter, myOptions); const ticketsIds = [...new Set(sales.map(sale => sale.ticketFk))]; - const refundTickets = []; - const mappedTickets = new Map(); const now = Date.vnNew(); - const [firstTicketId] = ticketsIds; - if (createSingleTicket) { - await createTicketRefund( - firstTicketId, - refundTickets, - mappedTickets, - now, - refundAgencyMode, - refoundZoneId, - myOptions - ); - } else { - for (let ticketId of ticketsIds) { - await createTicketRefund( - ticketId, - refundTickets, - mappedTickets, - now, - refundAgencyMode, - refoundZoneId, - myOptions - ); - } - } + + const refundTicket = await createTicketRefund(firstTicketId, now, refundAgencyMode, refoundZoneId, myOptions); for (const sale of sales) { - const refundTicketId = await getTicketRefundId(createSingleTicket, sale.ticketFk, refundTickets, mappedTickets); - const createdSale = await models.Sale.create({ - ticketFk: refundTicketId, + ticketFk: refundTicket.id, itemFk: sale.itemFk, quantity: - sale.quantity, concept: sale.concept, @@ -120,16 +89,13 @@ module.exports = Self => { where: {id: {inq: servicesIds}} }; const services = await models.TicketService.find(servicesFilter, myOptions); - for (const service of services) { - const refundTicketId = await getTicketRefundId(createSingleTicket, service.ticketFk, refundTickets, mappedTickets); - await models.TicketService.create({ description: service.description, quantity: - service.quantity, price: service.price, taxClassFk: service.taxClassFk, - ticketFk: refundTicketId, + ticketFk: refundTicket.id, ticketServiceTypeFk: service.ticketServiceTypeFk, }, myOptions); } @@ -137,22 +103,14 @@ module.exports = Self => { if (tx) await tx.commit(); - return refundTickets; + return refundTicket; } catch (e) { if (tx) await tx.rollback(); throw e; } }; - async function createTicketRefund( - ticketId, - refundTickets, - mappedTickets, - now, - refundAgencyMode, - refoundZoneId, - myOptions - ) { + async function createTicketRefund(ticketId, now, refundAgencyMode, refoundZoneId, myOptions) { const models = Self.app.models; const filter = {include: {relation: 'address'}}; @@ -170,20 +128,11 @@ module.exports = Self => { zoneFk: refoundZoneId }, myOptions); - refundTickets.push(refundTicket); - - mappedTickets.set(ticketId, refundTicket.id); - await models.TicketRefund.create({ refundTicketFk: refundTicket.id, originalTicketFk: ticket.id, }, myOptions); - } - async function getTicketRefundId(createSingleTicket, ticketId, refundTickets, mappedTickets) { - if (createSingleTicket) { - const [firstRefundTicket] = refundTickets; - return firstRefundTicket.id; - } else return mappedTickets.get(ticketId); + return refundTicket; } }; diff --git a/modules/ticket/back/methods/sale/specs/refund.spec.js b/modules/ticket/back/methods/sale/specs/refund.spec.js index 403b4b477..83b3755e2 100644 --- a/modules/ticket/back/methods/sale/specs/refund.spec.js +++ b/modules/ticket/back/methods/sale/specs/refund.spec.js @@ -22,9 +22,9 @@ describe('Sale refund()', () => { try { const options = {transaction: tx}; - const response = await models.Sale.refund(salesIds, servicesIds, false, options); + const refundedTicket = await models.Sale.refund(salesIds, servicesIds, options); - expect(response.length).toBeGreaterThanOrEqual(1); + expect(refundedTicket).toBeDefined(); await tx.rollback(); } catch (e) { @@ -33,23 +33,18 @@ describe('Sale refund()', () => { } }); - it('should create a ticket for each unique ticketFk in the sales', async() => { + it('should create one ticket for each unique ticketFk in the sales', async() => { const tx = await models.Sale.beginTransaction({}); const salesIds = [6, 7]; try { const options = {transaction: tx}; - const createSingleTicket = false; - const tickets = await models.Sale.refund(salesIds, servicesIds, createSingleTicket, options); + const ticket = await models.Sale.refund(salesIds, servicesIds, options); - const ticketsIds = tickets.map(ticket => ticket.id); - - const refundedTickets = await models.Ticket.find({ + const refundedTicket = await models.Ticket.findOne({ where: { - id: { - inq: ticketsIds - } + id: ticket.id }, include: [ { @@ -66,16 +61,12 @@ describe('Sale refund()', () => { ] }, options); - const firstRefoundedTicket = refundedTickets[0]; - const secondRefoundedTicket = refundedTickets[1]; - const salesLength = firstRefoundedTicket.ticketSales().length; - const componentsLength = firstRefoundedTicket.ticketSales()[0].components().length; - const servicesLength = secondRefoundedTicket.ticketServices().length; + const salesLength = refundedTicket.ticketSales().length; + const componentsLength = refundedTicket.ticketSales()[0].components().length; - expect(refundedTickets.length).toEqual(2); - expect(salesLength).toEqual(1); + expect(refundedTicket).toBeDefined(); + expect(salesLength).toEqual(2); expect(componentsLength).toEqual(4); - expect(servicesLength).toBeGreaterThanOrEqual(1); await tx.rollback(); } catch (e) { diff --git a/modules/ticket/back/methods/ticket/refund.js b/modules/ticket/back/methods/ticket/refund.js index 1f0021316..91f48cfd6 100644 --- a/modules/ticket/back/methods/ticket/refund.js +++ b/modules/ticket/back/methods/ticket/refund.js @@ -7,11 +7,6 @@ module.exports = Self => { arg: 'ticketsIds', type: ['number'], required: true - }, - { - arg: 'createSingleTicket', - type: 'boolean', - required: false } ], returns: { @@ -24,7 +19,7 @@ module.exports = Self => { } }); - Self.refund = async(ticketsIds, createSingleTicket = false, options) => { + Self.refund = async(ticketsIds, options) => { const models = Self.app.models; const myOptions = {}; let tx; @@ -46,7 +41,7 @@ module.exports = Self => { const services = await models.TicketService.find(filter, myOptions); const servicesIds = services.map(service => service.id); - const refundedTickets = await models.Sale.refund(salesIds, servicesIds, createSingleTicket, myOptions); + const refundedTickets = await models.Sale.refund(salesIds, servicesIds, myOptions); if (tx) await tx.commit(); diff --git a/modules/ticket/front/descriptor-menu/index.js b/modules/ticket/front/descriptor-menu/index.js index ff029db78..e32363f09 100644 --- a/modules/ticket/front/descriptor-menu/index.js +++ b/modules/ticket/front/descriptor-menu/index.js @@ -300,7 +300,7 @@ class Controller extends Section { const params = {ticketsIds: [this.id]}; const query = 'Tickets/refund'; return this.$http.post(query, params).then(res => { - const [refundTicket] = res.data; + const refundTicket = res.data; this.vnApp.showSuccess(this.$t('The following refund ticket have been created', { ticketId: refundTicket.id })); @@ -326,8 +326,13 @@ class Controller extends Section { return this.$http.post(`Docuwares/${this.id}/upload`, {fileCabinet: 'deliveryNote'}) .then(() => { - this.vnApp.showSuccess(this.$t('PDF sent!')); + this.$.balanceCreate.amountPaid = this.ticket.totalWithVat; + this.$.balanceCreate.clientFk = this.ticket.clientFk; + this.$.balanceCreate.description = 'Albaran: '; + this.$.balanceCreate.description += this.ticket.id; + this.$.balanceCreate.show(); + this.vnApp.showSuccess(this.$t('PDF sent!')); }); } } diff --git a/modules/ticket/front/descriptor-menu/index.spec.js b/modules/ticket/front/descriptor-menu/index.spec.js index babc22038..48998325a 100644 --- a/modules/ticket/front/descriptor-menu/index.spec.js +++ b/modules/ticket/front/descriptor-menu/index.spec.js @@ -250,7 +250,7 @@ describe('Ticket Component vnTicketDescriptorMenu', () => { const params = { ticketsIds: [16] }; - $httpBackend.expectPOST('Tickets/refund', params).respond([{id: 99}]); + $httpBackend.expectPOST('Tickets/refund', params).respond({id: 99}); controller.refund(); $httpBackend.flush(); diff --git a/modules/ticket/front/sale/index.js b/modules/ticket/front/sale/index.js index 20739b619..24b077476 100644 --- a/modules/ticket/front/sale/index.js +++ b/modules/ticket/front/sale/index.js @@ -516,7 +516,7 @@ class Controller extends Section { const params = {salesIds: salesIds}; const query = 'Sales/refund'; this.$http.post(query, params).then(res => { - const [refundTicket] = res.data; + const refundTicket = res.data; this.vnApp.showSuccess(this.$t('The following refund ticket have been created', { ticketId: refundTicket.id })); diff --git a/modules/ticket/front/sale/index.spec.js b/modules/ticket/front/sale/index.spec.js index c35ed3d9a..5fb3b3df3 100644 --- a/modules/ticket/front/sale/index.spec.js +++ b/modules/ticket/front/sale/index.spec.js @@ -726,8 +726,7 @@ describe('Ticket', () => { salesIds: [1, 4], }; const refundTicket = {id: 99}; - const createdTickets = [refundTicket]; - $httpBackend.expect('POST', 'Sales/refund', params).respond(200, createdTickets); + $httpBackend.expect('POST', 'Sales/refund', params).respond(200, refundTicket); controller.createRefund(); $httpBackend.flush(); diff --git a/print/core/components/report-footer/assets/css/style.css b/print/core/components/report-footer/assets/css/style.css index 9727e6f8f..b260c1b9f 100644 --- a/print/core/components/report-footer/assets/css/style.css +++ b/print/core/components/report-footer/assets/css/style.css @@ -4,6 +4,7 @@ margin-right: 2cm; font-size: 10px; color: #555; + width: 100%; zoom: 0.65 } diff --git a/print/core/components/report-footer/locale/en.yml b/print/core/components/report-footer/locale/en.yml index 3899f8b98..8ca14b4d7 100644 --- a/print/core/components/report-footer/locale/en.yml +++ b/print/core/components/report-footer/locale/en.yml @@ -1,8 +1 @@ -numPages: Page of -law: - privacy: 'In compliance with the provisions of Organic Law 15/1999, on the - Protection of Personal Data, we inform you that the personal data you provide - will be included in automated files of VERDNATURA LEVANTE SL, being able at all - times to exercise the rights of access, rectification, cancellation and opposition, - communicating it in writing to the registered office of the entity. - The purpose of the file is administrative management, accounting, and billing.' +numPages: Page of \ No newline at end of file diff --git a/print/core/components/report-footer/locale/es.yml b/print/core/components/report-footer/locale/es.yml index 985c1e17a..5ac6544ad 100644 --- a/print/core/components/report-footer/locale/es.yml +++ b/print/core/components/report-footer/locale/es.yml @@ -1,8 +1 @@ -numPages: Página de -law: - privacy: En cumplimiento de lo dispuesto en la Ley Orgánica 15/1999, de Protección - de Datos de Carácter Personal, le comunicamos que los datos personales que facilite - se incluirán en ficheros automatizados de VERDNATURA LEVANTE S.L., pudiendo en - todo momento ejercitar los derechos de acceso, rectificación, cancelación y oposición, - comunicándolo por escrito al domicilio social de la entidad. La finalidad del - fichero es la gestión administrativa, contabilidad, y facturación. +numPages: Página de \ No newline at end of file diff --git a/print/core/components/report-footer/locale/fr.yml b/print/core/components/report-footer/locale/fr.yml index 861ee5684..6fb644b2c 100644 --- a/print/core/components/report-footer/locale/fr.yml +++ b/print/core/components/report-footer/locale/fr.yml @@ -1,8 +1 @@ numPages: Page de -law: - privacy: Conformément aux dispositions de la loi organique 15/1999 sur la protection - des données personnelles, nous vous informons que les données personnelles que - vous fournissez seront incluses dans des dossiers. VERDNATURA LEVANTE S.L., vous - pouvez à tout moment, exercer les droits d'accès, de rectification, d'annulation - et d'opposition, en communiquant par écrit au siège social de la société. Le dossier - a pour objet la gestion administrative, la comptabilité et la facturation. diff --git a/print/core/components/report-footer/locale/pt.yml b/print/core/components/report-footer/locale/pt.yml index 1c343bb4c..9354ba3e9 100644 --- a/print/core/components/report-footer/locale/pt.yml +++ b/print/core/components/report-footer/locale/pt.yml @@ -1,8 +1 @@ numPages: Página de -law: - privacy: Em cumprimento do disposto na lei Orgânica 15/1999, de Protecção de Dados - de Carácter Pessoal, comunicamos que os dados pessoais que facilite se incluirão - nos ficheiros automatizados de VERDNATURA LEVANTE S.L., podendo em todo momento - exercer os direitos de acesso, rectificação, cancelação e oposição, comunicando - por escrito ao domicílio social da entidade. A finalidade do ficheiro é a gestão - administrativa, contabilidade e facturação. diff --git a/print/core/components/report-footer/report-footer.html b/print/core/components/report-footer/report-footer.html index 1af8df4d4..67b5f3cd0 100644 --- a/print/core/components/report-footer/report-footer.html +++ b/print/core/components/report-footer/report-footer.html @@ -5,6 +5,11 @@
{{centerText}}
-

+

+

+ + diff --git a/print/core/components/report-footer/report-footer.js b/print/core/components/report-footer/report-footer.js index 1ba36b1d7..0eaab8ce4 100755 --- a/print/core/components/report-footer/report-footer.js +++ b/print/core/components/report-footer/report-footer.js @@ -1,4 +1,17 @@ +/* eslint-disable no-tabs */ +const db = require('../../database'); + module.exports = { name: 'report-footer', - props: ['leftText', 'centerText'] + async serverPrefetch() { + this.company = await db.findOne( + `SELECT + ci.footnotes + FROM companyI18n ci + JOIN company c ON c.id = ci.companyFk + WHERE c.code = ? AND ci.lang = (SELECT lang FROM account.user WHERE id = ?)`, + [this.companyCode, this.recipientId]); + }, + + props: ['leftText', 'companyCode', 'recipientId', 'centerText'] }; diff --git a/print/core/components/report-header/report-header.html b/print/core/components/report-header/report-header.html index 0479e5caf..22f2068e2 100644 --- a/print/core/components/report-header/report-header.html +++ b/print/core/components/report-header/report-header.html @@ -8,7 +8,7 @@ {{companyName}}. {{company.street}}. {{company.postCode}} {{company.city}}. ☎ {{companyPhone}} - · {{$t('company.contactData')}} + · {{company.web}} - {{company.email}}
CIF: {{fiscalAddress.nif}} {{fiscalAddress.register}}
diff --git a/print/core/components/report-header/report-header.js b/print/core/components/report-header/report-header.js index 50c3a1337..d85e2c836 100755 --- a/print/core/components/report-header/report-header.js +++ b/print/core/components/report-header/report-header.js @@ -43,7 +43,9 @@ module.exports = { s.postCode, s.city, s.phone, - cg.code AS groupName + cg.code AS groupName, + c.email, + c.web FROM company c JOIN companyGroup cg ON cg.id = c.companyGroupFk JOIN supplier s ON s.id = c.id diff --git a/print/templates/email/greuge-wrong/assets/css/import.js b/print/templates/email/greuge-wrong/assets/css/import.js new file mode 100644 index 000000000..89b2afaa5 --- /dev/null +++ b/print/templates/email/greuge-wrong/assets/css/import.js @@ -0,0 +1,11 @@ +const Stylesheet = require(`vn-print/core/stylesheet`); + +const path = require('path'); +const vnPrintPath = path.resolve('print'); + +module.exports = new Stylesheet([ + `${vnPrintPath}/common/css/spacing.css`, + `${vnPrintPath}/common/css/misc.css`, + `${vnPrintPath}/common/css/layout.css`, + `${vnPrintPath}/common/css/email.css`]) + .mergeStyles(); \ No newline at end of file diff --git a/print/templates/email/greuge-wrong/greuge-wrong.html b/print/templates/email/greuge-wrong/greuge-wrong.html new file mode 100644 index 000000000..336007122 --- /dev/null +++ b/print/templates/email/greuge-wrong/greuge-wrong.html @@ -0,0 +1,15 @@ + +
+
+

{{ $t('total') }}: {{tickets.length}}

+
+
+
+

{{ $t('ticketId') }}: {{ticket.ticketId}}

+

{{ $t('clientId') }}: {{ticket.clientId}}

+

{{ $t('description') }}: {{ticket.description}}

+

{{ $t('amount') }}: {{ticket.amount}} €

+
+
+
+
\ No newline at end of file diff --git a/print/templates/email/greuge-wrong/greuge-wrong.js b/print/templates/email/greuge-wrong/greuge-wrong.js new file mode 100644 index 000000000..2aa3d50a7 --- /dev/null +++ b/print/templates/email/greuge-wrong/greuge-wrong.js @@ -0,0 +1,36 @@ +const Component = require(`vn-print/core/component`); +const emailBody = new Component('email-body'); +const models = require('vn-loopback/server/server').models; + +module.exports = { + name: 'greuge-wrong', + async serverPrefetch() { + this.url = await this.salixUrl(); + + if (!this.url) + throw new Error('Something went wrong'); + }, + components: { + 'email-body': emailBody.build(), + }, + methods: { + async salixUrl() { + const salix = await models.Url.findOne({ + where: { + appName: 'salix', + environment: process.env.NODE_ENV || 'dev' + } + }); + return salix.url; + }, + clientGreugeUrl(clientId) { + return `${this.url}client/${clientId}/greuge/index` + }, + }, + props: { + tickets: { + type: Array, + required: true + }, + }, +}; \ No newline at end of file diff --git a/print/templates/email/greuge-wrong/locale/en.yml b/print/templates/email/greuge-wrong/locale/en.yml new file mode 100644 index 000000000..0ace99c36 --- /dev/null +++ b/print/templates/email/greuge-wrong/locale/en.yml @@ -0,0 +1,6 @@ +subject: Abnormal greuges have been created +total: Total number of abnormal greuges +ticketId: Ticket +clientId: Client +description: Description +amount: Amount \ No newline at end of file diff --git a/print/templates/email/greuge-wrong/locale/es.yml b/print/templates/email/greuge-wrong/locale/es.yml new file mode 100644 index 000000000..fd2397182 --- /dev/null +++ b/print/templates/email/greuge-wrong/locale/es.yml @@ -0,0 +1,6 @@ +subject: Se han creado greuges anormales +total: Número total de greuges anormales +ticketId: Ticket +clientId: Cliente +description: Descipción +amount: Importe \ No newline at end of file diff --git a/print/templates/email/receipt/assets/css/import.js b/print/templates/email/receipt/assets/css/import.js new file mode 100644 index 000000000..4b4bb7086 --- /dev/null +++ b/print/templates/email/receipt/assets/css/import.js @@ -0,0 +1,11 @@ +const Stylesheet = require(`vn-print/core/stylesheet`); + +const path = require('path'); +const vnPrintPath = path.resolve('print'); + +module.exports = new Stylesheet([ + `${vnPrintPath}/common/css/spacing.css`, + `${vnPrintPath}/common/css/misc.css`, + `${vnPrintPath}/common/css/layout.css`, + `${vnPrintPath}/common/css/email.css`]) + .mergeStyles(); diff --git a/print/templates/email/receipt/attachments.json b/print/templates/email/receipt/attachments.json new file mode 100644 index 000000000..9930596e0 --- /dev/null +++ b/print/templates/email/receipt/attachments.json @@ -0,0 +1,6 @@ +[ + { + "filename": "receipt.pdf", + "component": "receipt" + } +] diff --git a/print/templates/email/receipt/locale/es.yml b/print/templates/email/receipt/locale/es.yml new file mode 100644 index 000000000..95883afaa --- /dev/null +++ b/print/templates/email/receipt/locale/es.yml @@ -0,0 +1,5 @@ +subject: Recibo +title: Recibo +dear: Estimado cliente +description: Ya está disponible el recibo {0}.
+ Puedes descargarlo haciendo clic en el adjunto de este correo. diff --git a/print/templates/email/receipt/receipt.html b/print/templates/email/receipt/receipt.html new file mode 100644 index 000000000..734552014 --- /dev/null +++ b/print/templates/email/receipt/receipt.html @@ -0,0 +1,9 @@ + +
+
+

{{ $t('title') }}

+

{{$t('dear')}},

+

+
+
+
diff --git a/print/templates/email/receipt/receipt.js b/print/templates/email/receipt/receipt.js new file mode 100755 index 000000000..606534f4d --- /dev/null +++ b/print/templates/email/receipt/receipt.js @@ -0,0 +1,15 @@ +const Component = require(`vn-print/core/component`); +const emailBody = new Component('email-body'); + +module.exports = { + name: 'receipt', + components: { + 'email-body': emailBody.build(), + }, + props: { + id: { + type: Number, + required: true + } + } +}; diff --git a/print/templates/reports/invoice/invoice.html b/print/templates/reports/invoice/invoice.html index 8b0220e43..727621b2c 100644 --- a/print/templates/reports/invoice/invoice.html +++ b/print/templates/reports/invoice/invoice.html @@ -5,7 +5,7 @@ -
+
@@ -242,7 +242,7 @@
-
+
{{$t('observations')}}
@@ -266,7 +266,9 @@ v-bind:company-code="invoice.companyCode" v-bind:left-text="$t('invoiceRef', [invoice.ref])" v-bind:center-text="client.socialName" + v-bind:recipient-id="client.id" v-bind="$props" + > diff --git a/print/templates/reports/invoice/invoice.js b/print/templates/reports/invoice/invoice.js index 42988113f..1c9965d3b 100755 --- a/print/templates/reports/invoice/invoice.js +++ b/print/templates/reports/invoice/invoice.js @@ -11,8 +11,12 @@ module.exports = { this.client = await this.findOneFromDef('client', [this.reference]); this.taxes = await this.rawSqlFromDef(`taxes`, [this.reference]); this.hasIntrastat = await this.findValueFromDef(`hasIntrastat`, [this.reference]); - this.intrastat = await this.rawSqlFromDef(`intrastat`, - [this.reference, this.reference, this.reference, this.reference]); + this.intrastat = await this.rawSqlFromDef(`intrastat`, [ + this.reference, + this.reference, + this.reference, + this.reference + ]); this.rectified = await this.rawSqlFromDef(`rectified`, [this.reference]); this.hasIncoterms = await this.findValueFromDef(`hasIncoterms`, [this.reference]); diff --git a/print/templates/reports/invoice/sql/invoice.sql b/print/templates/reports/invoice/sql/invoice.sql index 0f12e4f53..303fa937f 100644 --- a/print/templates/reports/invoice/sql/invoice.sql +++ b/print/templates/reports/invoice/sql/invoice.sql @@ -11,7 +11,7 @@ FROM invoiceOut io JOIN client c ON c.id = io.clientFk JOIN payMethod pm ON pm.id = c.payMethodFk JOIN company cny ON cny.id = io.companyFk - JOIN supplierAccount sa ON sa.id = cny.supplierAccountFk + LEFT JOIN supplierAccount sa ON sa.id = cny.supplierAccountFk LEFT JOIN invoiceOutSerial ios ON ios.code = io.serial LEFT JOIN ticket t ON t.refFk = io.ref WHERE t.refFk = ? \ No newline at end of file