diff --git a/db/changes/10390-constitution/00-item_getBalance.sql b/db/changes/10390-constitution/00-item_getBalance.sql index 90e3ee2bb2..54f612eaea 100644 --- a/db/changes/10390-constitution/00-item_getBalance.sql +++ b/db/changes/10390-constitution/00-item_getBalance.sql @@ -2,8 +2,7 @@ DROP PROCEDURE IF EXISTS `vn`.`item_getBalance`; DELIMITER $$ $$ -CREATE - definer = root@`%` procedure `vn`.`item_getBalance`(IN vItemId int, IN vWarehouse int) +CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`item_getBalance`(IN vItemId int, IN vWarehouse int) BEGIN DECLARE vDateInventory DATETIME; DECLARE vCurdate DATE DEFAULT CURDATE(); @@ -116,7 +115,7 @@ BEGIN s.id, st.`order`, ct.code, - cl.id + cb.claimFk FROM sale s JOIN ticket t ON t.id = s.ticketFk LEFT JOIN ticketState ts ON ts.ticket = t.id @@ -132,6 +131,7 @@ BEGIN LEFT JOIN state stPrep ON stPrep.`code` = 'PREPARED' LEFT JOIN saleTracking stk ON stk.saleFk = s.id AND stk.stateFk = stPrep.id LEFT JOIN claim cl ON cl.ticketFk = t.id + LEFT JOIN claimBeginning cb ON cl.id = cb.claimFk AND s.id = cb.saleFk WHERE t.shipped >= vDateInventory AND s.itemFk = vItemId AND vWarehouse =t.warehouseFk @@ -141,4 +141,3 @@ BEGIN END; $$ DELIMITER ; - diff --git a/db/changes/10390-constitution/00-payMethod.sql b/db/changes/10390-constitution/00-payMethod.sql new file mode 100644 index 0000000000..6e196af954 --- /dev/null +++ b/db/changes/10390-constitution/00-payMethod.sql @@ -0,0 +1,2 @@ +ALTER TABLE vn.payMethod CHANGE ibanRequiredForClients isIbanRequiredForClients tinyint(3) DEFAULT 0 NULL; +ALTER TABLE vn.payMethod CHANGE ibanRequiredForSuppliers isIbanRequiredForSuppliers tinyint(3) DEFAULT 0 NULL; diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index b460561358..9220da4efe 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -217,7 +217,7 @@ UPDATE `vn`.`agencyMode` SET `web` = 1, `reportMail` = 'no-reply@gothamcity.com' UPDATE `vn`.`agencyMode` SET `code` = 'refund' WHERE `id` = 23; -INSERT INTO `vn`.`payMethod`(`id`,`code`, `name`, `graceDays`, `outstandingDebt`, `ibanRequiredForClients`, `ibanRequiredForSuppliers`) +INSERT INTO `vn`.`payMethod`(`id`,`code`, `name`, `graceDays`, `outstandingDebt`, `isIbanRequiredForClients`, `isIbanRequiredForSuppliers`) VALUES (1, NULL, 'PayMethod one', 0, 001, 0, 0), (2, NULL, 'PayMethod two', 10, 001, 0, 0), @@ -1882,6 +1882,7 @@ INSERT INTO `postgresql`.`calendar_state` (`calendar_state_id`, `type`, `rgb`, ` INSERT INTO `postgresql`.`calendar_employee` (`business_id`, `calendar_state_id`, `date`) VALUES + (1, 6, IF(MONTH(CURDATE()) = 12 AND DAY(CURDATE()) > 10, DATE_ADD(CURDATE(), INTERVAL -10 DAY), DATE_ADD(CURDATE(), INTERVAL 10 DAY))), (1106, 1, IF(MONTH(CURDATE()) = 12 AND DAY(CURDATE()) > 10, DATE_ADD(CURDATE(), INTERVAL -10 DAY), DATE_ADD(CURDATE(), INTERVAL 10 DAY))), (1106, 1, IF(MONTH(CURDATE()) = 12 AND DAY(CURDATE()) > 10, DATE_ADD(CURDATE(), INTERVAL -11 DAY), DATE_ADD(CURDATE(), INTERVAL 11 DAY))), (1106, 1, IF(MONTH(CURDATE()) = 12 AND DAY(CURDATE()) > 10, DATE_ADD(CURDATE(), INTERVAL -12 DAY), DATE_ADD(CURDATE(), INTERVAL 12 DAY))), diff --git a/db/export-data.sh b/db/export-data.sh index 96adc7ab68..f696208123 100755 --- a/db/export-data.sh +++ b/db/export-data.sh @@ -47,19 +47,20 @@ TABLES=( cplusSubjectOp cplusTaxBreak cplusTrascendency472 - pgc - time claimResponsible claimReason claimRedelivery claimResult - ticketUpdateAction - state - sample - department component componentType continent + department + itemPackingType + pgc + sample + state + ticketUpdateAction + time volumeConfig ) dump_tables ${TABLES[@]} diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 4a5448d34c..a54d2169fb 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -193,6 +193,7 @@ "Client assignment has changed": "He cambiado el comercial ~*\"<{{previousWorkerName}}>\"*~ por *\"<{{currentWorkerName}}>\"* del cliente [{{clientName}} ({{clientId}})]({{{url}}})", "None": "Ninguno", "The contract was not active during the selected date": "El contrato no estaba activo durante la fecha seleccionada", + "Cannot add more than one '1/2 day vacation'": "No puedes añadir más de un 'Vacaciones 1/2 dia'", "This document already exists on this ticket": "Este documento ya existe en el ticket", "Some of the selected tickets are not billable": "Algunos de los tickets seleccionados no son facturables", "You can't invoice tickets from multiple clients": "No puedes facturar tickets de multiples clientes", @@ -212,5 +213,6 @@ "You don't have enough privileges to set this credit amount": "No tienes suficientes privilegios para establecer esta cantidad de crédito", "You can't change the credit set to zero from a manager": "No puedes cambiar el cŕedito establecido a cero por un gerente", "The PDF document does not exists": "El documento PDF no existe. Prueba a regenerarlo desde la opción 'Regenerar PDF factura'", - "The type of business must be filled in basic data": "El tipo de negocio debe estar rellenado en datos básicos" + "The type of business must be filled in basic data": "El tipo de negocio debe estar rellenado en datos básicos", + "You can't create a claim from a ticket delivered more than seven days ago": "No puedes crear una reclamación de un ticket entregado hace más de siete días" } \ No newline at end of file diff --git a/modules/claim/back/methods/claim/createFromSales.js b/modules/claim/back/methods/claim/createFromSales.js index f22aabbf36..cdbce865b4 100644 --- a/modules/claim/back/methods/claim/createFromSales.js +++ b/modules/claim/back/methods/claim/createFromSales.js @@ -57,8 +57,14 @@ module.exports = Self => { } }, myOptions); + const landedPlusWeek = new Date(ticket.landed); + landedPlusWeek.setDate(landedPlusWeek.getDate() + 7); + const isClaimable = landedPlusWeek >= new Date(); + if (ticket.isDeleted) throw new UserError(`You can't create a claim for a removed ticket`); + if (!isClaimable) + throw new UserError(`You can't create a claim from a ticket delivered more than seven days ago`); const newClaim = await Self.create({ ticketFk: ticketId, diff --git a/modules/claim/back/methods/claim/specs/createFromSales.spec.js b/modules/claim/back/methods/claim/specs/createFromSales.spec.js index 849ccf8f51..8a48b86131 100644 --- a/modules/claim/back/methods/claim/specs/createFromSales.spec.js +++ b/modules/claim/back/methods/claim/specs/createFromSales.spec.js @@ -1,31 +1,40 @@ -const app = require('vn-loopback/server/server'); +const models = require('vn-loopback/server/server').models; +const LoopBackContext = require('loopback-context'); describe('Claim createFromSales()', () => { - const ticketId = 2; + const ticketId = 16; const newSale = [{ id: 3, instance: 0, quantity: 10 }]; - const ctx = { - req: { - accessToken: {userId: 1}, - headers: {origin: 'localhost:5000'}, - __: () => {} - } + const activeCtx = { + accessToken: {userId: 1}, + headers: {origin: 'localhost:5000'}, + __: () => {} }; + const ctx = { + req: activeCtx + }; + + beforeEach(() => { + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + }); + it('should create a new claim', async() => { - const tx = await app.models.Claim.beginTransaction({}); + const tx = await models.Claim.beginTransaction({}); try { const options = {transaction: tx}; - const claim = await app.models.Claim.createFromSales(ctx, ticketId, newSale, options); + const claim = await models.Claim.createFromSales(ctx, ticketId, newSale, options); expect(claim.ticketFk).toEqual(ticketId); - let claimBeginning = await app.models.ClaimBeginning.findOne({where: {claimFk: claim.id}}, options); + let claimBeginning = await models.ClaimBeginning.findOne({where: {claimFk: claim.id}}, options); expect(claimBeginning.saleFk).toEqual(newSale[0].id); expect(claimBeginning.quantity).toEqual(newSale[0].quantity); @@ -37,17 +46,42 @@ describe('Claim createFromSales()', () => { } }); - it('should not be able to create a claim if exists that sale', async() => { - const tx = await app.models.Claim.beginTransaction({}); + it('should not be able to create a claim for a ticket delivered more than seven days ago', async() => { + const tx = await models.Claim.beginTransaction({}); let error; try { const options = {transaction: tx}; - await app.models.Claim.createFromSales(ctx, ticketId, newSale, options); + const todayMinusEightDays = new Date(); + todayMinusEightDays.setDate(todayMinusEightDays.getDate() - 8); - await app.models.Claim.createFromSales(ctx, ticketId, newSale, options); + const ticket = await models.Ticket.findById(ticketId, options); + await ticket.updateAttribute('landed', todayMinusEightDays, options); + + await models.Claim.createFromSales(ctx, ticketId, newSale, options); + + await tx.rollback(); + } catch (e) { + error = e; + await tx.rollback(); + } + + expect(error.toString()).toContain(`You can't create a claim from a ticket delivered more than seven days ago`); + }); + + it('should not be able to create a claim if exists that sale', async() => { + const tx = await models.Claim.beginTransaction({}); + + let error; + + try { + const options = {transaction: tx}; + + await models.Claim.createFromSales(ctx, ticketId, newSale, options); + + await models.Claim.createFromSales(ctx, ticketId, newSale, options); await tx.rollback(); } catch (e) { diff --git a/modules/client/back/models/client.js b/modules/client/back/models/client.js index f3591750da..b4961771d9 100644 --- a/modules/client/back/models/client.js +++ b/modules/client/back/models/client.js @@ -138,7 +138,8 @@ module.exports = Self => { function hasIban(err, done) { Self.app.models.PayMethod.findById(this.payMethodFk, (_, instance) => { - if (instance && instance.ibanRequiredForClients && !this.iban) + const isMissingIban = instance && instance.isIbanRequiredForClients && !this.iban; + if (isMissingIban) err(); done(); }); diff --git a/modules/client/back/models/pay-method.json b/modules/client/back/models/pay-method.json index 0666be7a5b..4080a09538 100644 --- a/modules/client/back/models/pay-method.json +++ b/modules/client/back/models/pay-method.json @@ -25,10 +25,10 @@ "outstandingDebt": { "type": "Number" }, - "ibanRequiredForClients": { + "isIbanRequiredForClients": { "type": "boolean" }, - "ibanRequiredForSuppliers": { + "isIbanRequiredForSuppliers": { "type": "boolean" } } diff --git a/modules/client/front/billing-data/index.html b/modules/client/front/billing-data/index.html index 8c7c6cfe9d..06d7d7d73a 100644 --- a/modules/client/front/billing-data/index.html +++ b/modules/client/front/billing-data/index.html @@ -19,7 +19,7 @@ vn-acl="salesAssistant" ng-model="$ctrl.client.payMethodFk" data="paymethods" - fields="['ibanRequiredForClients']" + fields="['isIbanRequiredForClients']" initial-data="$ctrl.client.payMethod"> { case 'isActive': case 'typeFk': case 'isFloramondo': - param = `i.${param}`; - return {[param]: value}; + return {[`i.${param}`]: value}; case 'multiplier': return {'i.stemMultiplier': value}; case 'categoryFk': diff --git a/modules/item/back/methods/item/specs/getBalance.spec.js b/modules/item/back/methods/item/specs/getBalance.spec.js index 5143a346fd..779516b198 100644 --- a/modules/item/back/methods/item/specs/getBalance.spec.js +++ b/modules/item/back/methods/item/specs/getBalance.spec.js @@ -36,4 +36,36 @@ describe('item getBalance()', () => { throw e; } }); + + it('should show the claimFk only on the claimed item', async() => { + const tx = await models.Item.beginTransaction({}); + const options = {transaction: tx}; + + try { + const firstFilter = { + where: { + itemFk: 1, + warehouseFk: 1 + } + }; + + const secondFilter = { + where: { + itemFk: 2, + warehouseFk: 1 + } + }; + + const firstItemBalance = await models.Item.getBalance(firstFilter, options); + const secondItemBalance = await models.Item.getBalance(secondFilter, options); + + expect(firstItemBalance[9].claimFk).toEqual(null); + expect(secondItemBalance[5].claimFk).toEqual(2); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); }); diff --git a/modules/item/back/models/item-packing-type.json b/modules/item/back/models/item-packing-type.json new file mode 100644 index 0000000000..d77c37dd86 --- /dev/null +++ b/modules/item/back/models/item-packing-type.json @@ -0,0 +1,26 @@ +{ + "name": "ItemPackingType", + "base": "VnModel", + "options": { + "mysql": { + "table": "itemPackingType" + } + }, + "properties": { + "code": { + "type": "string", + "id": true + }, + "description": { + "type": "string" + } + }, + "acls": [ + { + "accessType": "READ", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" + } + ] +} \ No newline at end of file diff --git a/modules/supplier/back/models/supplier.js b/modules/supplier/back/models/supplier.js index 2d1c6ac282..1ac6e3bd28 100644 --- a/modules/supplier/back/models/supplier.js +++ b/modules/supplier/back/models/supplier.js @@ -79,8 +79,9 @@ module.exports = Self => { const payMethod = await Self.app.models.PayMethod.findById(this.payMethodFk); const supplierAccount = await Self.app.models.SupplierAccount.findOne({where: {supplierFk: this.id}}); const hasIban = supplierAccount && supplierAccount.iban; + const isMissingIban = payMethod && payMethod.isIbanRequiredForSuppliers && !hasIban; - if (payMethod && payMethod.ibanRequiredForSuppliers && !hasIban) + if (isMissingIban) err(); done(); diff --git a/modules/supplier/front/billing-data/index.html b/modules/supplier/front/billing-data/index.html index c1c57e1357..238760c1a9 100644 --- a/modules/supplier/front/billing-data/index.html +++ b/modules/supplier/front/billing-data/index.html @@ -24,7 +24,7 @@ vn-acl="salesAssistant" ng-model="$ctrl.supplier.payMethodFk" data="paymethods" - fields="['ibanRequiredForSuppliers']" + fields="['isIbanRequiredForSuppliers']" initial-data="$ctrl.supplier.payMethod"> + ng-click="$ctrl.createClaim()" + ng-if="$ctrl.isClaimable"> Add claim = new Date(); + } + return false; + } + getSaleTotal(sale) { if (sale.quantity == null || sale.price == null) return null; diff --git a/modules/ticket/front/sale/index.spec.js b/modules/ticket/front/sale/index.spec.js index cee1fe5b40..d543c1b816 100644 --- a/modules/ticket/front/sale/index.spec.js +++ b/modules/ticket/front/sale/index.spec.js @@ -15,7 +15,8 @@ describe('Ticket', () => { const ticket = { id: 1, clientFk: 1101, - shipped: 1, + shipped: new Date(), + landed: new Date(), created: new Date(), client: {salesPersonFk: 1}, address: {mobile: 111111111} @@ -74,6 +75,25 @@ describe('Ticket', () => { }); }); + describe('isClaimable() getter', () => { + it('should return true for a ticket delivered less than seven days ago', () => { + const result = controller.isClaimable; + + expect(result).toEqual(true); + }); + + it('should return false for a ticket delivered more than seven days ago', () => { + const ticket = controller.ticket; + const landedMinusEightDays = new Date(ticket.landed); + landedMinusEightDays.setDate(landedMinusEightDays.getDate() - 8); + ticket.landed = landedMinusEightDays; + + const result = controller.isClaimable; + + expect(result).toEqual(false); + }); + }); + describe('getSaleTotal()', () => { it('should return the sale total amount', () => { const sale = { diff --git a/modules/worker/back/methods/worker/createAbsence.js b/modules/worker/back/methods/worker/createAbsence.js index b276cf1f7c..89830197d4 100644 --- a/modules/worker/back/methods/worker/createAbsence.js +++ b/modules/worker/back/methods/worker/createAbsence.js @@ -65,6 +65,23 @@ module.exports = Self => { if (args.dated < labour.started || (labour.ended != null && args.dated > labour.ended)) throw new UserError(`The contract was not active during the selected date`); + const result = await Self.rawSql( + `SELECT COUNT(*) halfHolidayCounter + FROM vn.calendar c + JOIN postgresql.business b ON b.business_id = c.businessFk + JOIN postgresql.profile p ON p.profile_id = b.client_id + JOIN vn.person pe ON pe.id = p.person_id + WHERE c.dayOffTypeFk = 6 + AND pe.workerFk = ? + AND c.dated BETWEEN util.firstDayOfYear(CURDATE()) + AND LAST_DAY(DATE_ADD(NOW(), INTERVAL 12-MONTH(NOW()) MONTH))`, [args.id]); + + const hasHalfHoliday = result[0].halfHolidayCounter > 0; + const isHalfHoliday = args.absenceTypeId == 6; + + if (isHalfHoliday && hasHalfHoliday) + throw new UserError(`Cannot add more than one '1/2 day vacation'`); + const absence = await models.Calendar.create({ businessFk: labour.businessFk, dayOffTypeFk: args.absenceTypeId, diff --git a/modules/worker/back/methods/worker/specs/createAbsence.spec.js b/modules/worker/back/methods/worker/specs/createAbsence.spec.js index f2c00e8040..0d6ebfc807 100644 --- a/modules/worker/back/methods/worker/specs/createAbsence.spec.js +++ b/modules/worker/back/methods/worker/specs/createAbsence.spec.js @@ -74,4 +74,32 @@ describe('Worker createAbsence()', () => { throw e; } }); + + it(`should throw an error when adding a "Half holiday" absence if there's already one`, async() => { + const ctx = { + req: {accessToken: {userId: 19}}, + args: { + id: 1, + businessFk: 1, + absenceTypeId: 6, + dated: new Date() + } + }; + + const tx = await app.models.Calendar.beginTransaction({}); + + let error; + try { + const options = {transaction: tx}; + + await app.models.Worker.createAbsence(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toEqual(`Cannot add more than one '1/2 day vacation'`); + }); });