+
+ Transfer invoice to...
+
Confirm
+
+
+
+
+
+
+
+ #{{id}} - {{::name}}
+
+
+
+
+ {{::description}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/invoiceOut/front/descriptor-menu/index.js b/modules/invoiceOut/front/descriptor-menu/index.js
index 38c3c9434..7d2644158 100644
--- a/modules/invoiceOut/front/descriptor-menu/index.js
+++ b/modules/invoiceOut/front/descriptor-menu/index.js
@@ -125,6 +125,22 @@ class Controller extends Section {
this.$state.go('ticket.card.sale', {id: refundTicket.id});
});
}
+
+ transferInvoice() {
+ const params = {
+ id: this.invoiceOut.id,
+ ref: this.invoiceOut.ref,
+ newClientFk: this.invoiceOut.client.id,
+ cplusRectificationId: this.cplusRectificationType,
+ cplusInvoiceType477Id: this.cplusInvoiceType477,
+ invoiceCorrectionTypeId: this.invoiceCorrectionType
+ };
+ this.$http.post(`InvoiceOuts/transferInvoice`, params).then(res => {
+ const invoiceId = res.data;
+ this.vnApp.showSuccess(this.$t('Invoice trasfered!'));
+ this.$state.go('invoiceOut.card.summary', {id: invoiceId});
+ });
+ }
}
Controller.$inject = ['$element', '$scope', 'vnReport', 'vnEmail'];
diff --git a/modules/invoiceOut/front/descriptor-menu/locale/en.yml b/modules/invoiceOut/front/descriptor-menu/locale/en.yml
index d299155d7..8fad5f25e 100644
--- a/modules/invoiceOut/front/descriptor-menu/locale/en.yml
+++ b/modules/invoiceOut/front/descriptor-menu/locale/en.yml
@@ -1 +1,3 @@
The following refund tickets have been created: "The following refund tickets have been created: {{ticketIds}}"
+Transfer invoice to...: Transfer invoice to...
+Cplus Type: Cplus Type
\ No newline at end of file
diff --git a/modules/invoiceOut/front/descriptor-menu/locale/es.yml b/modules/invoiceOut/front/descriptor-menu/locale/es.yml
index 393efd58c..0f74b5fec 100644
--- a/modules/invoiceOut/front/descriptor-menu/locale/es.yml
+++ b/modules/invoiceOut/front/descriptor-menu/locale/es.yml
@@ -21,3 +21,5 @@ The invoice PDF document has been regenerated: El documento PDF de la factura ha
The email can't be empty: El correo no puede estar vacío
The following refund tickets have been created: "Se han creado los siguientes tickets de abono: {{ticketIds}}"
Refund...: Abono...
+Transfer invoice to...: Transferir factura a...
+Cplus Type: Cplus Tipo
diff --git a/modules/invoiceOut/front/descriptor-menu/style.scss b/modules/invoiceOut/front/descriptor-menu/style.scss
index b68301961..9e4cf4297 100644
--- a/modules/invoiceOut/front/descriptor-menu/style.scss
+++ b/modules/invoiceOut/front/descriptor-menu/style.scss
@@ -21,4 +21,10 @@ vn-invoice-out-descriptor-menu {
font-size: 1.75rem;
}
}
-}
\ No newline at end of file
+
+}
+@media screen and (min-width: 1000px) {
+ .transferInvoice {
+ min-width: $width-md;
+ }
+}
diff --git a/modules/item/front/request/index.html b/modules/item/front/request/index.html
index c505b3a09..03c8db8ec 100644
--- a/modules/item/front/request/index.html
+++ b/modules/item/front/request/index.html
@@ -26,6 +26,7 @@
Ticket ID
Shipped
Description
+ Requester
Requested
Price
Atender
@@ -51,6 +52,13 @@
{{::request.description}}
+
+
+ {{::request.requesterName}}
+
+
{{::request.quantity}}
{{::request.price | currency: 'EUR':2}}
@@ -78,8 +86,8 @@
-
{{request.itemDescription}}
@@ -106,13 +114,13 @@
-
-
-
@@ -141,24 +149,24 @@
ng-click="contextmenu.filterBySelection()">
Filter by selection
-
Exclude selection
-
Remove filter
-
Remove all filters
-
Copy value
-
\ No newline at end of file
+
diff --git a/modules/ticket/back/locale/sale-group/en.yml b/modules/ticket/back/locale/sale-group/en.yml
new file mode 100644
index 000000000..d88231647
--- /dev/null
+++ b/modules/ticket/back/locale/sale-group/en.yml
@@ -0,0 +1,10 @@
+name: saleGroup
+columns:
+ id: id
+ created: created
+ userFk: user
+ parkingFk: parking
+ sectorFk: sector
+ ticketFk: ticket
+ editorFk: editor
+
diff --git a/modules/ticket/back/locale/sale-group/es.yml b/modules/ticket/back/locale/sale-group/es.yml
new file mode 100644
index 000000000..9efbe7148
--- /dev/null
+++ b/modules/ticket/back/locale/sale-group/es.yml
@@ -0,0 +1,10 @@
+name: saleGroup
+columns:
+ id: id
+ created: creado
+ userFk: usuario
+ parkingFk: parking
+ sectorFk: sector
+ ticketFk: ticket
+ editorFk: editor
+
diff --git a/modules/ticket/back/methods/expedition-state/addExpeditionState.js b/modules/ticket/back/methods/expedition-state/addExpeditionState.js
index f1199f188..8eab1a838 100644
--- a/modules/ticket/back/methods/expedition-state/addExpeditionState.js
+++ b/modules/ticket/back/methods/expedition-state/addExpeditionState.js
@@ -1,7 +1,7 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
- Self.remoteMethod('addExpeditionState', {
+ Self.remoteMethodCtx('addExpeditionState', {
description: 'Update an expedition state',
accessType: 'WRITE',
accepts: [
@@ -12,18 +12,15 @@ module.exports = Self => {
description: 'Array of objects containing expeditionFk and stateCode'
}
],
- returns: {
- type: 'boolean',
- root: true
- },
http: {
path: `/addExpeditionState`,
verb: 'post'
}
});
- Self.addExpeditionState = async(expeditions, options) => {
+ Self.addExpeditionState = async(ctx, expeditions, options) => {
const models = Self.app.models;
+ const userId = ctx.req.accessToken.userId;
let tx;
const myOptions = {};
if (typeof options == 'object')
@@ -51,6 +48,7 @@ module.exports = Self => {
await models.ExpeditionState.create({
expeditionFk: expedition.expeditionFk,
typeFk,
+ userFk: userId,
}, myOptions);
}
diff --git a/modules/ticket/back/methods/expedition-state/specs/addExpeditionState.spec.js b/modules/ticket/back/methods/expedition-state/specs/addExpeditionState.spec.js
index 819a43a60..6c7739006 100644
--- a/modules/ticket/back/methods/expedition-state/specs/addExpeditionState.spec.js
+++ b/modules/ticket/back/methods/expedition-state/specs/addExpeditionState.spec.js
@@ -1,9 +1,9 @@
const models = require('vn-loopback/server/server').models;
describe('expeditionState addExpeditionState()', () => {
+ const ctx = {req: {accessToken: {userId: 9}}};
it('should update the expedition states', async() => {
const tx = await models.ExpeditionState.beginTransaction({});
-
try {
const options = {transaction: tx};
const payload = [
@@ -13,7 +13,7 @@ describe('expeditionState addExpeditionState()', () => {
},
];
- await models.ExpeditionState.addExpeditionState(payload, options);
+ await models.ExpeditionState.addExpeditionState(ctx, payload, options);
const expeditionState = await models.ExpeditionState.findOne({
where: {id: 5}
@@ -39,7 +39,7 @@ describe('expeditionState addExpeditionState()', () => {
stateCode: 'DUMMY'
}
];
- await models.ExpeditionState.addExpeditionState(payload, options);
+ await models.ExpeditionState.addExpeditionState(ctx, payload, options);
await tx.rollback();
} catch (e) {
@@ -62,7 +62,7 @@ describe('expeditionState addExpeditionState()', () => {
}
];
- await models.ExpeditionState.addExpeditionState(payload, options);
+ await models.ExpeditionState.addExpeditionState(ctx, payload, options);
await tx.rollback();
} catch (e) {
diff --git a/modules/ticket/back/methods/sale/clone.js b/modules/ticket/back/methods/sale/clone.js
new file mode 100644
index 000000000..a5ccb6de4
--- /dev/null
+++ b/modules/ticket/back/methods/sale/clone.js
@@ -0,0 +1,122 @@
+module.exports = Self => {
+ Self.clone = async(ctx, salesIds, servicesIds, withWarehouse, group, negative, options) => {
+ const models = Self.app.models;
+ const myOptions = {};
+ let tx;
+ const newTickets = [];
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ if (!myOptions.transaction) {
+ tx = await Self.beginTransaction({});
+ myOptions.transaction = tx;
+ }
+
+ try {
+ const salesFilter = {
+ where: {id: {inq: salesIds}},
+ include: {
+ relation: 'components',
+ scope: {
+ fields: ['saleFk', 'componentFk', 'value']
+ }
+ }
+ };
+ const sales = await models.Sale.find(salesFilter, myOptions);
+ let ticketsIds = [...new Set(sales.map(sale => sale.ticketFk))];
+
+ const mappedTickets = new Map();
+
+ if (group) ticketsIds = [ticketsIds[0]];
+
+ for (let ticketId of ticketsIds) {
+ const newTicket = await createTicket(
+ ctx,
+ ticketId,
+ withWarehouse,
+ negative,
+ myOptions
+ );
+ newTickets.push(newTicket);
+ mappedTickets.set(ticketId, newTicket.id);
+ }
+
+ for (const sale of sales) {
+ const newTicketId = mappedTickets.get(sale.ticketFk);
+
+ const createdSale = await models.Sale.create({
+ ticketFk: newTicketId,
+ itemFk: sale.itemFk,
+ quantity: negative ? - sale.quantity : sale.quantity,
+ concept: sale.concept,
+ price: sale.price,
+ discount: sale.discount,
+ }, myOptions);
+
+ const components = sale.components();
+ for (const component of components)
+ component.saleFk = createdSale.id;
+
+ await models.SaleComponent.create(components, myOptions);
+ }
+
+ if (servicesIds && servicesIds.length) {
+ const servicesFilter = {
+ where: {id: {inq: servicesIds}}
+ };
+ const services = await models.TicketService.find(servicesFilter, myOptions);
+
+ for (const service of services) {
+ const newTicketId = mappedTickets.get(service.ticketFk);
+
+ await models.TicketService.create({
+ description: service.description,
+ quantity: negative ? - service.quantity : service.quantity,
+ price: service.price,
+ taxClassFk: service.taxClassFk,
+ ticketFk: newTicketId,
+ ticketServiceTypeFk: service.ticketServiceTypeFk,
+ }, myOptions);
+ }
+ }
+
+ if (tx) await tx.commit();
+
+ return newTickets;
+ } catch (e) {
+ if (tx) await tx.rollback();
+ throw e;
+ }
+
+ async function createTicket(
+ ctx,
+ ticketId,
+ withWarehouse,
+ negative,
+ myOptions
+ ) {
+ const models = Self.app.models;
+ const now = Date.vnNew();
+
+ const ticket = await models.Ticket.findById(ticketId, null, myOptions);
+ ctx.args.clientId = ticket.clientFk;
+ ctx.args.shipped = now;
+ ctx.args.landed = now;
+ ctx.args.warehouseId = withWarehouse ? ticket.warehouseFk : null;
+ ctx.args.companyId = ticket.companyFk;
+ ctx.args.addressId = ticket.addressFk;
+
+ const newTicket = await models.Ticket.new(ctx, myOptions);
+
+ if (negative) {
+ await models.TicketRefund.create({
+ originalTicketFk: ticketId,
+ refundTicketFk: newTicket.id
+ }, myOptions);
+ }
+
+ return newTicket;
+ }
+ };
+};
diff --git a/modules/ticket/back/methods/sale/deleteSales.js b/modules/ticket/back/methods/sale/deleteSales.js
index 5d1463a66..0207815a9 100644
--- a/modules/ticket/back/methods/sale/deleteSales.js
+++ b/modules/ticket/back/methods/sale/deleteSales.js
@@ -1,5 +1,3 @@
-let UserError = require('vn-loopback/util/user-error');
-
module.exports = Self => {
Self.remoteMethodCtx('deleteSales', {
description: 'Deletes the selected sales',
@@ -70,11 +68,11 @@ module.exports = Self => {
const salesPerson = ticket.client().salesPersonUser();
if (salesPerson) {
- const origin = ctx.req.headers.origin;
+ const url = await Self.app.models.Url.getUrl();
const message = $t('Deleted sales from ticket', {
ticketId: ticketId,
- ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
+ ticketUrl: `${url}ticket/${ticketId}/sale`,
deletions: deletions
});
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
diff --git a/modules/ticket/back/methods/sale/refund.js b/modules/ticket/back/methods/sale/refund.js
index 03302550e..17b70f12b 100644
--- a/modules/ticket/back/methods/sale/refund.js
+++ b/modules/ticket/back/methods/sale/refund.js
@@ -5,7 +5,8 @@ module.exports = Self => {
accepts: [
{
arg: 'salesIds',
- type: ['number']
+ type: ['number'],
+ required: true
},
{
arg: 'servicesIds',
@@ -40,122 +41,23 @@ module.exports = Self => {
myOptions.transaction = tx;
}
- let refundTicket = null;
try {
- const refundAgencyMode = await models.AgencyMode.findOne({
- include: {
- relation: 'zones',
- scope: {
- limit: 1,
- field: ['id', 'name']
- }
- },
- where: {code: 'refund'}
- }, myOptions);
-
- const refoundZoneId = refundAgencyMode.zones()[0].id;
-
- if (salesIds.length) {
- const salesFilter = {
- where: {id: {inq: salesIds}},
- include: {
- relation: 'components',
- scope: {
- fields: ['saleFk', 'componentFk', 'value']
- }
- }
- };
- const sales = await models.Sale.find(salesFilter, myOptions);
- const ticketsIds = [...new Set(sales.map(sale => sale.ticketFk))];
-
- const now = Date.vnNew();
- const [firstTicketId] = ticketsIds;
-
- // eslint-disable-next-line max-len
- refundTicket = await createTicketRefund(firstTicketId, now, refundAgencyMode, refoundZoneId, withWarehouse, myOptions);
-
- for (const sale of sales) {
- const createdSale = await models.Sale.create({
- ticketFk: refundTicket.id,
- itemFk: sale.itemFk,
- quantity: - sale.quantity,
- concept: sale.concept,
- price: sale.price,
- discount: sale.discount,
- }, myOptions);
-
- const components = sale.components();
- for (const component of components)
- component.saleFk = createdSale.id;
-
- await models.SaleComponent.create(components, myOptions);
- }
- }
- if (!refundTicket) {
- const servicesFilter = {
- where: {id: {inq: servicesIds}}
- };
- const services = await models.TicketService.find(servicesFilter, myOptions);
- const firstTicketId = services[0].ticketFk;
-
- const now = Date.vnNew();
-
- // eslint-disable-next-line max-len
- refundTicket = await createTicketRefund(firstTicketId, now, refundAgencyMode, refoundZoneId, withWarehouse, myOptions);
- }
-
- if (servicesIds && servicesIds.length > 0) {
- const servicesFilter = {
- where: {id: {inq: servicesIds}}
- };
- const services = await models.TicketService.find(servicesFilter, myOptions);
- for (const service of services) {
- await models.TicketService.create({
- description: service.description,
- quantity: - service.quantity,
- price: service.price,
- taxClassFk: service.taxClassFk,
- ticketFk: refundTicket.id,
- ticketServiceTypeFk: service.ticketServiceTypeFk,
- }, myOptions);
- }
- }
-
- const query = `CALL vn.ticket_recalc(?, NULL)`;
- await Self.rawSql(query, [refundTicket.id], myOptions);
+ const refundsTicket = await models.Sale.clone(
+ ctx,
+ salesIds,
+ servicesIds,
+ withWarehouse,
+ false,
+ true,
+ myOptions
+ );
if (tx) await tx.commit();
- return refundTicket;
+ return refundsTicket[0];
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
-
- async function createTicketRefund(ticketId, now, refundAgencyMode, refoundZoneId, withWarehouse, myOptions) {
- const models = Self.app.models;
-
- const filter = {include: {relation: 'address'}};
- const ticket = await models.Ticket.findById(ticketId, filter, myOptions);
-
- const refundTicket = await models.Ticket.create({
- clientFk: ticket.clientFk,
- shipped: now,
- addressFk: ticket.address().id,
- agencyModeFk: refundAgencyMode.id,
- nickname: ticket.address().nickname,
- warehouseFk: withWarehouse ? ticket.warehouseFk : null,
- companyFk: ticket.companyFk,
- landed: now,
- zoneFk: refoundZoneId
- }, myOptions);
-
- await models.TicketRefund.create({
- refundTicketFk: refundTicket.id,
- originalTicketFk: ticket.id,
- }, myOptions);
-
- return refundTicket;
- }
};
diff --git a/modules/ticket/back/methods/sale/reserve.js b/modules/ticket/back/methods/sale/reserve.js
index 2dc368af6..36db791fc 100644
--- a/modules/ticket/back/methods/sale/reserve.js
+++ b/modules/ticket/back/methods/sale/reserve.js
@@ -1,6 +1,3 @@
-
-let UserError = require('vn-loopback/util/user-error');
-
module.exports = Self => {
Self.remoteMethodCtx('reserve', {
description: 'Change the state of a ticket',
@@ -65,7 +62,8 @@ module.exports = Self => {
promises.push(reservedSale);
- changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity}) ${$t('State')}: ${$t(oldState)} ➔ *${$t(newState)}*`;
+ changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity})
+ ${$t('State')}: ${$t(oldState)} ➔ *${$t(newState)}*`;
}
}
@@ -87,11 +85,11 @@ module.exports = Self => {
const salesPerson = ticket.client().salesPersonUser();
if (salesPerson) {
- const origin = ctx.req.headers.origin;
+ const url = await Self.app.models.Url.getUrl();
const message = $t('Changed sale reserved state', {
ticketId: ticketId,
- ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
+ ticketUrl: `${url}ticket/${ticketId}/sale`,
changes: changesMade
});
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
diff --git a/modules/ticket/back/methods/sale/specs/refund.spec.js b/modules/ticket/back/methods/sale/specs/refund.spec.js
index b81f7f84d..08eb1fabd 100644
--- a/modules/ticket/back/methods/sale/specs/refund.spec.js
+++ b/modules/ticket/back/methods/sale/specs/refund.spec.js
@@ -3,7 +3,7 @@ const LoopBackContext = require('loopback-context');
describe('Sale refund()', () => {
const userId = 5;
- const ctx = {req: {accessToken: userId}};
+ const ctx = {req: {accessToken: userId}, args: {}};
const activeCtx = {
accessToken: {userId},
};
@@ -40,6 +40,7 @@ describe('Sale refund()', () => {
try {
const options = {transaction: tx};
+ const ticketsBefore = await models.Ticket.find({}, options);
const ticket = await models.Sale.refund(ctx, salesIds, servicesIds, withWarehouse, options);
@@ -61,12 +62,13 @@ describe('Sale refund()', () => {
}
]
}, options);
-
+ const ticketsAfter = await models.Ticket.find({}, options);
const salesLength = refundedTicket.ticketSales().length;
const componentsLength = refundedTicket.ticketSales()[0].components().length;
expect(refundedTicket).toBeDefined();
- expect(salesLength).toEqual(2);
+ expect(salesLength).toEqual(1);
+ expect(ticketsBefore.length).toEqual(ticketsAfter.length - 2);
expect(componentsLength).toEqual(4);
await tx.rollback();
diff --git a/modules/ticket/back/methods/sale/specs/updateQuantity.spec.js b/modules/ticket/back/methods/sale/specs/updateQuantity.spec.js
deleted file mode 100644
index 0669711e7..000000000
--- a/modules/ticket/back/methods/sale/specs/updateQuantity.spec.js
+++ /dev/null
@@ -1,359 +0,0 @@
-/* eslint max-len: ["error", { "code": 150 }]*/
-
-const models = require('vn-loopback/server/server').models;
-const LoopBackContext = require('loopback-context');
-
-describe('sale updateQuantity()', () => {
- const ctx = {
- req: {
- accessToken: {userId: 9},
- headers: {origin: 'localhost:5000'},
- __: () => {}
- }
- };
- function getActiveCtx(userId) {
- return {
- active: {
- accessToken: {userId},
- http: {
- req: {
- headers: {origin: 'http://localhost'}
- }
- }
- }
- };
- }
-
- it('should add quantity if the quantity is greater than it should be and is role advanced', async() => {
- const saleId = 17;
- const buyerId = 35;
- const ctx = {
- req: {
- accessToken: {userId: buyerId},
- headers: {origin: 'localhost:5000'},
- __: () => {}
- }
- };
- const tx = await models.Sale.beginTransaction({});
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(buyerId));
- spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
- if (sqlStatement.includes('catalog_calcFromItem')) {
- sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
- SELECT 100 as available;`;
- params = null;
- }
- return models.Ticket.rawSql(sqlStatement, params, options);
- });
-
- try {
- const options = {transaction: tx};
-
- const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*');
-
- expect(isRoleAdvanced).toEqual(true);
-
- const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
-
- expect(originalLine.quantity).toEqual(30);
-
- const newQuantity = originalLine.quantity + 1;
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
-
- const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
-
- expect(modifiedLine.quantity).toEqual(newQuantity);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
- });
-
- it('should update the quantity of a given sale current line', async() => {
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(9));
-
- const tx = await models.Sale.beginTransaction({});
- const saleId = 25;
- const newQuantity = 4;
-
- try {
- const options = {transaction: tx};
-
- const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
-
- expect(originalLine.quantity).toEqual(20);
-
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
-
- const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
-
- expect(modifiedLine.quantity).toEqual(newQuantity);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
- });
-
- it('should throw an error if the quantity is negative and it is not a refund ticket', async() => {
- const ctx = {
- req: {
- accessToken: {userId: 1},
- headers: {origin: 'localhost:5000'},
- __: () => {}
- }
- };
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
-
- const saleId = 17;
- const newQuantity = -10;
-
- const tx = await models.Sale.beginTransaction({});
-
- let error;
- try {
- const options = {transaction: tx};
-
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error).toEqual(new Error('You can only add negative amounts in refund tickets'));
- });
-
- it('should update a negative quantity when is a ticket refund', async() => {
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(9));
-
- const tx = await models.Sale.beginTransaction({});
- const saleId = 13;
- const newQuantity = -10;
-
- try {
- const options = {transaction: tx};
-
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
-
- const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
-
- expect(modifiedLine.quantity).toEqual(newQuantity);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
- });
-
- it('should throw an error if the quantity is less than the minimum quantity of the item', async() => {
- const ctx = {
- req: {
- accessToken: {userId: 1},
- headers: {origin: 'localhost:5000'},
- __: () => {}
- }
- };
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
-
- const tx = await models.Sale.beginTransaction({});
- const itemId = 2;
- const saleId = 17;
- const minQuantity = 30;
- const newQuantity = minQuantity - 1;
-
- let error;
- try {
- const options = {transaction: tx};
-
- const item = await models.Item.findById(itemId, null, options);
- await item.updateAttribute('minQuantity', minQuantity, options);
- spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
- if (sqlStatement.includes('catalog_calcFromItem')) {
- sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
- SELECT 100 as available;`;
- params = null;
- }
- return models.Ticket.rawSql(sqlStatement, params, options);
- });
-
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error).toEqual(new Error('The amount cannot be less than the minimum'));
- });
-
- it('should change quantity if has minimum quantity and new quantity is equal than item available', async() => {
- const ctx = {
- req: {
- accessToken: {userId: 1},
- headers: {origin: 'localhost:5000'},
- __: () => {}
- }
- };
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
-
- const tx = await models.Sale.beginTransaction({});
- const itemId = 2;
- const saleId = 17;
- const minQuantity = 30;
- const newQuantity = minQuantity - 1;
-
- try {
- const options = {transaction: tx};
-
- const item = await models.Item.findById(itemId, null, options);
- await item.updateAttribute('minQuantity', minQuantity, options);
-
- spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
- if (sqlStatement.includes('catalog_calcFromItem')) {
- sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
- SELECT ${newQuantity} as available;`;
- params = null;
- }
- return models.Ticket.rawSql(sqlStatement, params, options);
- });
-
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
- });
-
- describe('newPrice', () => {
- it('should increase quantity if you have enough available and the new price is the same as the previous one', async() => {
- const ctx = {
- req: {
- accessToken: {userId: 1},
- headers: {origin: 'localhost:5000'},
- __: () => {}
- }
- };
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
-
- const tx = await models.Sale.beginTransaction({});
- const itemId = 2;
- const saleId = 17;
- const minQuantity = 30;
- const newQuantity = 31;
-
- try {
- const options = {transaction: tx};
-
- const item = await models.Item.findById(itemId, null, options);
- await item.updateAttribute('minQuantity', minQuantity, options);
- spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
- if (sqlStatement.includes('catalog_calcFromItem')) {
- sqlStatement = `
- CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available;
- CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 7.07 as price;`;
- params = null;
- }
- return models.Ticket.rawSql(sqlStatement, params, options);
- });
-
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
- });
-
- it('should increase quantity when the new price is lower than the previous one', async() => {
- const ctx = {
- req: {
- accessToken: {userId: 1},
- headers: {origin: 'localhost:5000'},
- __: () => {}
- }
- };
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
-
- const tx = await models.Sale.beginTransaction({});
- const itemId = 2;
- const saleId = 17;
- const minQuantity = 30;
- const newQuantity = 31;
-
- try {
- const options = {transaction: tx};
-
- const item = await models.Item.findById(itemId, null, options);
- await item.updateAttribute('minQuantity', minQuantity, options);
- spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
- if (sqlStatement.includes('catalog_calcFromItem')) {
- sqlStatement = `
- CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available;
- CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 1 as price;`;
- params = null;
- }
- return models.Ticket.rawSql(sqlStatement, params, options);
- });
-
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
- });
-
- it('should throw error when increase quantity and the new price is higher than the previous one', async() => {
- const ctx = {
- req: {
- accessToken: {userId: 1},
- headers: {origin: 'localhost:5000'},
- __: () => {}
- }
- };
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
-
- const tx = await models.Sale.beginTransaction({});
- const itemId = 2;
- const saleId = 17;
- const minQuantity = 30;
- const newQuantity = 31;
-
- let error;
- try {
- const options = {transaction: tx};
-
- const item = await models.Item.findById(itemId, null, options);
- await item.updateAttribute('minQuantity', minQuantity, options);
- spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
- if (sqlStatement.includes('catalog_calcFromItem')) {
- sqlStatement = `
- CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available;
- CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 100000 as price;`;
- params = null;
- }
- return models.Ticket.rawSql(sqlStatement, params, options);
- });
-
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error).toEqual(new Error('The price of the item changed'));
- });
- });
-});
diff --git a/modules/ticket/back/methods/sale/updatePrice.js b/modules/ticket/back/methods/sale/updatePrice.js
index 62e4ebd42..191fd09e3 100644
--- a/modules/ticket/back/methods/sale/updatePrice.js
+++ b/modules/ticket/back/methods/sale/updatePrice.js
@@ -1,5 +1,3 @@
-let UserError = require('vn-loopback/util/user-error');
-
module.exports = Self => {
Self.remoteMethodCtx('updatePrice', {
description: 'Changes the price of a sale',
@@ -100,7 +98,7 @@ module.exports = Self => {
const salesPerson = sale.ticket().client().salesPersonUser();
if (salesPerson) {
- const origin = ctx.req.headers.origin;
+ const url = await Self.app.models.Url.getUrl();
const message = $t('Changed sale price', {
ticketId: sale.ticket().id,
itemId: sale.itemFk,
@@ -108,8 +106,8 @@ module.exports = Self => {
quantity: sale.quantity,
oldPrice: oldPrice,
newPrice: newPrice,
- ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`,
- itemUrl: `${origin}/#!/item/${sale.itemFk}/summary`
+ ticketUrl: `${url}ticket/${sale.ticket().id}/sale`,
+ itemUrl: `${url}item/${sale.itemFk}/summary`
});
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
}
diff --git a/modules/ticket/back/methods/sale/updateQuantity.js b/modules/ticket/back/methods/sale/updateQuantity.js
index 55106f053..1a497da24 100644
--- a/modules/ticket/back/methods/sale/updateQuantity.js
+++ b/modules/ticket/back/methods/sale/updateQuantity.js
@@ -68,16 +68,17 @@ module.exports = Self => {
const salesPerson = sale.ticket().client().salesPersonUser();
if (salesPerson) {
- const origin = ctx.req.headers.origin;
+ const url = await Self.app.models.Url.getUrl();
const message = $t('Changed sale quantity', {
ticketId: sale.ticket().id,
itemId: sale.itemFk,
concept: sale.concept,
oldQuantity: oldQuantity,
newQuantity: newQuantity,
- ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`,
- itemUrl: `${origin}/#!/item/${sale.itemFk}/summary`
+ ticketUrl: `${url}ticket/${sale.ticket().id}/sale`,
+ itemUrl: `${url}item/${sale.itemFk}/summary`
});
+
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
}
diff --git a/modules/ticket/back/methods/ticket-request/confirm.js b/modules/ticket/back/methods/ticket-request/confirm.js
index 9cd7f4d68..00310f33c 100644
--- a/modules/ticket/back/methods/ticket-request/confirm.js
+++ b/modules/ticket/back/methods/ticket-request/confirm.js
@@ -84,7 +84,7 @@ module.exports = Self => {
const query = `CALL vn.sale_calculateComponent(?, NULL)`;
await Self.rawSql(query, [sale.id], myOptions);
- const origin = ctx.req.headers.origin;
+ const url = await Self.app.models.Url.getUrl();
const requesterId = request.requesterFk;
const message = $t('Bought units from buy request', {
@@ -92,8 +92,8 @@ module.exports = Self => {
concept: sale.concept,
itemId: sale.itemFk,
ticketId: sale.ticketFk,
- url: `${origin}/#!/ticket/${sale.ticketFk}/summary`,
- urlItem: `${origin}/#!/item/${sale.itemFk}/summary`
+ url: `${url}ticket/${sale.ticketFk}/summary`,
+ urlItem: `${url}item/${sale.itemFk}/summary`
});
await models.Chat.sendCheckingPresence(ctx, requesterId, message, myOptions);
diff --git a/modules/ticket/back/methods/ticket-request/deny.js b/modules/ticket/back/methods/ticket-request/deny.js
index 92f020083..44f1e48a1 100644
--- a/modules/ticket/back/methods/ticket-request/deny.js
+++ b/modules/ticket/back/methods/ticket-request/deny.js
@@ -50,12 +50,12 @@ module.exports = Self => {
const request = await Self.app.models.TicketRequest.findById(ctx.args.id, null, myOptions);
await request.updateAttributes(params, myOptions);
- const origin = ctx.req.headers.origin;
+ const url = await Self.app.models.Url.getUrl();
const requesterId = request.requesterFk;
const message = $t('Deny buy request', {
ticketId: request.ticketFk,
- url: `${origin}/#!/ticket/${request.ticketFk}/request/index`,
+ url: `${url}ticket/${request.ticketFk}/request/index`,
observation: params.response
});
diff --git a/modules/ticket/back/methods/ticket-request/filter.js b/modules/ticket/back/methods/ticket-request/filter.js
index f27ea5018..10aaf02e5 100644
--- a/modules/ticket/back/methods/ticket-request/filter.js
+++ b/modules/ticket/back/methods/ticket-request/filter.js
@@ -99,6 +99,8 @@ module.exports = Self => {
switch (value) {
case 'pending':
return {'tr.isOk': null};
+ case 'accepted':
+ return {'tr.isOk': 1};
default:
return {'tr.isOk': value};
}
@@ -122,8 +124,7 @@ module.exports = Self => {
filter = mergeFilters(filter, {where});
const stmt = new ParameterizedSQL(
- `SELECT
- tr.id,
+ `SELECT tr.id,
tr.ticketFk,
tr.quantity,
tr.price,
@@ -133,18 +134,19 @@ module.exports = Self => {
tr.saleFk,
tr.requesterFk,
tr.isOk,
- s.quantity AS saleQuantity,
+ s.quantity saleQuantity,
s.itemFk,
- i.name AS itemDescription,
+ i.name itemDescription,
t.shipped,
- DATE(t.shipped) AS shippedDate,
+ DATE(t.shipped) shippedDate,
t.nickname,
t.warehouseFk,
t.clientFk,
- w.name AS warehouse,
- u.nickname AS salesPersonNickname,
- ua.name AS attenderName,
- c.salesPersonFk
+ w.name warehouse,
+ u.nickname salesPersonNickname,
+ ua.name attenderName,
+ c.salesPersonFk,
+ ua2.name requesterName
FROM ticketRequest tr
LEFT JOIN ticketWeekly tw on tw.ticketFk = tr.ticketFk
LEFT JOIN ticket t ON t.id = tr.ticketFk
@@ -155,7 +157,8 @@ module.exports = Self => {
LEFT JOIN worker wk ON wk.id = c.salesPersonFk
LEFT JOIN account.user u ON u.id = wk.id
LEFT JOIN worker wka ON wka.id = tr.attenderFk
- LEFT JOIN account.user ua ON ua.id = wka.id`);
+ LEFT JOIN account.user ua ON ua.id = wka.id
+ LEFT JOIN account.user ua2 ON ua2.id = tr.requesterFk`);
stmt.merge(conn.makeSuffix(filter));
return conn.executeStmt(stmt, myOptions);
diff --git a/modules/ticket/back/methods/ticket-tracking/setDelivered.js b/modules/ticket/back/methods/ticket-tracking/setDelivered.js
index df482fd01..d3cdb192f 100644
--- a/modules/ticket/back/methods/ticket-tracking/setDelivered.js
+++ b/modules/ticket/back/methods/ticket-tracking/setDelivered.js
@@ -47,7 +47,7 @@ module.exports = Self => {
const promises = [];
for (const id of ticketIds) {
- const promise = models.TicketTracking.changeState(ctx, {
+ const promise = await models.Ticket.state(ctx, {
stateFk: state.id,
workerFk: worker.id,
ticketFk: id
diff --git a/modules/ticket/back/methods/ticket/addSale.js b/modules/ticket/back/methods/ticket/addSale.js
index 21fea1c81..826de6e12 100644
--- a/modules/ticket/back/methods/ticket/addSale.js
+++ b/modules/ticket/back/methods/ticket/addSale.js
@@ -1,5 +1,3 @@
-const UserError = require('vn-loopback/util/user-error');
-
module.exports = Self => {
Self.remoteMethodCtx('addSale', {
description: 'Inserts a new sale for the current ticket',
@@ -83,11 +81,11 @@ module.exports = Self => {
const salesPerson = ticket.client().salesPersonUser();
if (salesPerson) {
- const origin = ctx.req.headers.origin;
+ const url = await Self.app.models.Url.getUrl();
const message = $t('Added sale to ticket', {
ticketId: id,
- ticketUrl: `${origin}/#!/ticket/${id}/sale`,
+ ticketUrl: `${url}ticket/${id}/sale`,
addition: addition
});
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
diff --git a/modules/ticket/back/methods/ticket/componentUpdate.js b/modules/ticket/back/methods/ticket/componentUpdate.js
index 8aad8959b..f7c36f108 100644
--- a/modules/ticket/back/methods/ticket/componentUpdate.js
+++ b/modules/ticket/back/methods/ticket/componentUpdate.js
@@ -237,7 +237,7 @@ module.exports = Self => {
const salesPersonId = originalTicket.client().salesPersonFk;
if (salesPersonId) {
- const origin = ctx.req.headers.origin;
+ const url = await Self.app.models.Url.getUrl();
let changesMade = '';
for (let change in newProperties) {
@@ -249,7 +249,7 @@ module.exports = Self => {
const message = $t('Changed this data from the ticket', {
ticketId: args.id,
- ticketUrl: `${origin}/#!/ticket/${args.id}/sale`,
+ ticketUrl: `${url}ticket/${args.id}/sale`,
changes: changesMade
});
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
diff --git a/modules/ticket/back/methods/ticket/getTicketsAdvance.js b/modules/ticket/back/methods/ticket/getTicketsAdvance.js
index ec9314db2..ab40b9559 100644
--- a/modules/ticket/back/methods/ticket/getTicketsAdvance.js
+++ b/modules/ticket/back/methods/ticket/getTicketsAdvance.js
@@ -28,32 +28,27 @@ module.exports = Self => {
{
arg: 'ipt',
type: 'string',
- description: 'Origin Item Packaging Type',
- required: false
+ description: 'Origin Item Packaging Type'
},
{
arg: 'futureIpt',
type: 'string',
- description: 'Destination Item Packaging Type',
- required: false
+ description: 'Destination Item Packaging Type'
},
{
arg: 'id',
type: 'number',
- description: 'Origin id',
- required: false
+ description: 'Origin id'
},
{
arg: 'futureId',
type: 'number',
- description: 'Destination id',
- required: false
+ description: 'Destination id'
},
{
arg: 'isFullMovable',
type: 'boolean',
- description: 'True when lines and stock of origin are equal',
- required: false
+ description: 'True when lines and stock of origin are equal'
},
{
arg: 'filter',
diff --git a/modules/ticket/back/methods/ticket/invoiceTickets.js b/modules/ticket/back/methods/ticket/invoiceTickets.js
index ca1bf15fb..fa3ee93af 100644
--- a/modules/ticket/back/methods/ticket/invoiceTickets.js
+++ b/modules/ticket/back/methods/ticket/invoiceTickets.js
@@ -77,9 +77,10 @@ module.exports = function(Self) {
if (tx) await tx.rollback();
throw e;
}
-
- for (const invoiceId of invoicesIds)
- await models.InvoiceOut.makePdfAndNotify(ctx, invoiceId, null);
+ if (tx) {
+ for (const invoiceId of invoicesIds)
+ await models.InvoiceOut.makePdfAndNotify(ctx, invoiceId, null);
+ }
return invoicesIds;
};
diff --git a/modules/ticket/back/methods/ticket/merge.js b/modules/ticket/back/methods/ticket/merge.js
index 3c0da077f..1106cef06 100644
--- a/modules/ticket/back/methods/ticket/merge.js
+++ b/modules/ticket/back/methods/ticket/merge.js
@@ -25,7 +25,7 @@ module.exports = Self => {
Self.merge = async(ctx, tickets, options) => {
const httpRequest = ctx.req;
const $t = httpRequest.__;
- const origin = httpRequest.headers.origin;
+ const url = await Self.app.models.Url.getUrl();
const models = Self.app.models;
const myOptions = {};
let tx;
@@ -40,8 +40,8 @@ module.exports = Self => {
try {
for (let ticket of tickets) {
- const originFullPath = `${origin}/#!/ticket/${ticket.originId}/summary`;
- const destinationFullPath = `${origin}/#!/ticket/${ticket.destinationId}/summary`;
+ const originFullPath = `${url}ticket/${ticket.originId}/summary`;
+ const destinationFullPath = `${url}ticket/${ticket.destinationId}/summary`;
const message = $t('Ticket merged', {
originDated: dateUtil.toString(new Date(ticket.originShipped)),
destinationDated: dateUtil.toString(new Date(ticket.destinationShipped)),
diff --git a/modules/ticket/back/methods/ticket/new.js b/modules/ticket/back/methods/ticket/new.js
index 0f5c323ed..288d38d77 100644
--- a/modules/ticket/back/methods/ticket/new.js
+++ b/modules/ticket/back/methods/ticket/new.js
@@ -96,7 +96,7 @@ module.exports = Self => {
if (address.client().type().code === 'normal' && (!agencyMode || agencyMode.code != 'refund')) {
const canCreateTicket = await models.Client.canCreateTicket(args.clientId, myOptions);
if (!canCreateTicket)
- throw new UserError(`You can't create a ticket for a inactive client`);
+ throw new UserError(`You can't create a ticket for an inactive client`);
}
if (!args.shipped && args.landed) {
diff --git a/modules/ticket/back/methods/ticket/refund.js b/modules/ticket/back/methods/ticket/refund.js
index c99b6aa83..758384ae2 100644
--- a/modules/ticket/back/methods/ticket/refund.js
+++ b/modules/ticket/back/methods/ticket/refund.js
@@ -39,7 +39,6 @@ module.exports = Self => {
try {
const filter = {where: {ticketFk: {inq: ticketsIds}}};
-
const sales = await models.Sale.find(filter, myOptions);
const salesIds = sales.map(sale => sale.id);
diff --git a/modules/ticket/back/methods/ticket/restore.js b/modules/ticket/back/methods/ticket/restore.js
index 722c3294e..e268c3891 100644
--- a/modules/ticket/back/methods/ticket/restore.js
+++ b/modules/ticket/back/methods/ticket/restore.js
@@ -48,10 +48,10 @@ module.exports = Self => {
// Send notification to salesPerson
const salesPersonId = ticket.client().salesPersonFk;
if (salesPersonId) {
- const origin = ctx.req.headers.origin;
+ const url = await Self.app.models.Url.getUrl();
const message = $t(`I have restored the ticket id`, {
id: id,
- url: `${origin}/#!/ticket/${id}/summary`
+ url: `${url}ticket/${id}/summary`
});
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
}
diff --git a/modules/ticket/back/methods/ticket/setDeleted.js b/modules/ticket/back/methods/ticket/setDeleted.js
index fcd9972fe..9a9fd9056 100644
--- a/modules/ticket/back/methods/ticket/setDeleted.js
+++ b/modules/ticket/back/methods/ticket/setDeleted.js
@@ -119,10 +119,10 @@ module.exports = Self => {
// Send notification to salesPerson
const salesPersonUser = ticket.client().salesPersonUser();
if (salesPersonUser && sales.length) {
- const origin = ctx.req.headers.origin;
+ const url = await Self.app.models.Url.getUrl();
const message = $t(`I have deleted the ticket id`, {
id: id,
- url: `${origin}/#!/ticket/${id}/summary`
+ url: `${url}ticket/${id}/summary`
});
await models.Chat.send(ctx, `@${salesPersonUser.name}`, message);
}
@@ -146,7 +146,8 @@ module.exports = Self => {
JOIN vn.sectorCollection sc ON sc.id = scsg.sectorCollectionFk
JOIN vn.saleGroupDetail sgd ON sgd.saleGroupFk = sg.id
JOIN vn.sale s ON s.id = sgd.saleFk
- WHERE s.ticketFk = ?;`, [ticket.id], myOptions);
+ WHERE s.ticketFk = ?;`, [ticket.id], myOptions
+ );
if (tx) await tx.commit();
diff --git a/modules/ticket/back/methods/ticket/specs/new.spec.js b/modules/ticket/back/methods/ticket/specs/new.spec.js
index 0a2f93bc4..9aa073a7b 100644
--- a/modules/ticket/back/methods/ticket/specs/new.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/new.spec.js
@@ -30,7 +30,7 @@ describe('ticket new()', () => {
await tx.rollback();
}
- expect(error).toEqual(new UserError(`You can't create a ticket for a inactive client`));
+ expect(error).toEqual(new UserError(`You can't create a ticket for an inactive client`));
});
it('should throw an error if the address doesnt exist', async() => {
diff --git a/modules/ticket/back/methods/ticket-tracking/specs/changeState.spec.js b/modules/ticket/back/methods/ticket/specs/state.spec.js
similarity index 87%
rename from modules/ticket/back/methods/ticket-tracking/specs/changeState.spec.js
rename to modules/ticket/back/methods/ticket/specs/state.spec.js
index 175bc4e4b..9b5e80165 100644
--- a/modules/ticket/back/methods/ticket-tracking/specs/changeState.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/state.spec.js
@@ -1,7 +1,7 @@
const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
-describe('ticket changeState()', () => {
+describe('ticket state()', () => {
const salesPersonId = 18;
const employeeId = 1;
const productionId = 49;
@@ -47,7 +47,7 @@ describe('ticket changeState()', () => {
activeCtx.accessToken.userId = salesPersonId;
const params = {ticketFk: 2, stateFk: 3};
- await models.TicketTracking.changeState(ctx, params, options);
+ await models.Ticket.state(ctx, params, options);
await tx.rollback();
} catch (e) {
@@ -69,7 +69,7 @@ describe('ticket changeState()', () => {
activeCtx.accessToken.userId = employeeId;
const params = {ticketFk: 11, stateFk: 13};
- await models.TicketTracking.changeState(ctx, params, options);
+ await models.Ticket.state(ctx, params, options);
await tx.rollback();
} catch (e) {
@@ -80,7 +80,8 @@ describe('ticket changeState()', () => {
expect(error.code).toBe('ACCESS_DENIED');
});
- it('should be able to create a ticket tracking line for a not editable ticket if the user has the production role', async() => {
+ it('should be able to create a ticket tracking line for a not' +
+ ' editable ticket if the user has the production role', async() => {
const tx = await models.TicketTracking.beginTransaction({});
try {
@@ -91,7 +92,7 @@ describe('ticket changeState()', () => {
activeCtx.accessToken.userId = productionId;
const params = {ticketFk: ticket.id, stateFk: 3};
- const ticketTracking = await models.TicketTracking.changeState(ctx, params, options);
+ const ticketTracking = await models.Ticket.state(ctx, params, options);
expect(ticketTracking.__data.ticketFk).toBe(params.ticketFk);
expect(ticketTracking.__data.stateFk).toBe(params.stateFk);
@@ -105,7 +106,8 @@ describe('ticket changeState()', () => {
}
});
- it('should update the ticket tracking line when the user is salesperson, uses the state assigned and a valid worker id', async() => {
+ it('should update the ticket tracking line when the user is salesperson,' +
+ ' uses the state assigned and a valid worker id', async() => {
const tx = await models.TicketTracking.beginTransaction({});
try {
@@ -115,7 +117,7 @@ describe('ticket changeState()', () => {
const ctx = {req: {accessToken: {userId: 18}}};
const assignedState = await models.State.findOne({where: {code: 'PICKER_DESIGNED'}}, options);
const params = {ticketFk: ticket.id, stateFk: assignedState.id, workerFk: 1};
- const res = await models.TicketTracking.changeState(ctx, params, options);
+ const res = await models.Ticket.state(ctx, params, options);
expect(res.__data.ticketFk).toBe(params.ticketFk);
expect(res.__data.stateFk).toBe(params.stateFk);
diff --git a/modules/ticket/back/methods/ticket-tracking/changeState.js b/modules/ticket/back/methods/ticket/state.js
similarity index 94%
rename from modules/ticket/back/methods/ticket-tracking/changeState.js
rename to modules/ticket/back/methods/ticket/state.js
index dbef8762e..01bfbba20 100644
--- a/modules/ticket/back/methods/ticket-tracking/changeState.js
+++ b/modules/ticket/back/methods/ticket/state.js
@@ -1,7 +1,7 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
- Self.remoteMethodCtx('changeState', {
+ Self.remoteMethodCtx('state', {
description: 'Change the state of a ticket',
accessType: 'WRITE',
accepts: [
@@ -18,12 +18,12 @@ module.exports = Self => {
root: true
},
http: {
- path: `/changeState`,
+ path: `/state`,
verb: 'POST'
}
});
- Self.changeState = async(ctx, params, options) => {
+ Self.state = async(ctx, params, options) => {
const models = Self.app.models;
const myOptions = {};
let tx;
diff --git a/modules/ticket/back/methods/ticket/transferSales.js b/modules/ticket/back/methods/ticket/transferSales.js
index a2e92d524..54306510c 100644
--- a/modules/ticket/back/methods/ticket/transferSales.js
+++ b/modules/ticket/back/methods/ticket/transferSales.js
@@ -66,7 +66,7 @@ module.exports = Self => {
const ticket = await models.Ticket.findById(id);
const canCreateTicket = await models.Client.canCreateTicket(ticket.clientFk);
if (!canCreateTicket)
- throw new UserError(`You can't create a ticket for a inactive client`);
+ throw new UserError(`You can't create a ticket for an inactive client`);
ticketId = await cloneTicket(originalTicket, myOptions);
}
diff --git a/modules/ticket/back/methods/ticket/updateDiscount.js b/modules/ticket/back/methods/ticket/updateDiscount.js
index e092ee4ed..2e8bec27a 100644
--- a/modules/ticket/back/methods/ticket/updateDiscount.js
+++ b/modules/ticket/back/methods/ticket/updateDiscount.js
@@ -165,11 +165,10 @@ module.exports = Self => {
const salesPerson = ticket.client().salesPersonUser();
if (salesPerson) {
- const origin = ctx.req.headers.origin;
-
+ const url = await Self.app.models.Url.getUrl();
const message = $t('Changed sale discount', {
ticketId: id,
- ticketUrl: `${origin}/#!/ticket/${id}/sale`,
+ ticketUrl: `${url}ticket/${id}/sale`,
changes: changesMade
});
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
diff --git a/modules/ticket/back/models/expeditionScan.json b/modules/ticket/back/models/expeditionScan.json
index 1db2c1238..7b95eff1e 100644
--- a/modules/ticket/back/models/expeditionScan.json
+++ b/modules/ticket/back/models/expeditionScan.json
@@ -8,17 +8,16 @@
"properties": {
"id": {
"type": "number",
- "description": "Identifier"
+ "description": "Identifier",
+ "id": true
},
"expeditionFk": {
"type": "number",
- "description": "Identifier",
- "id": true
+ "description": "Identifier"
},
"palletFk": {
"type": "number",
- "description": "Identifier",
- "id": true
+ "description": "Identifier"
},
"scanned": {
"type": "date",
diff --git a/modules/ticket/back/models/sale.js b/modules/ticket/back/models/sale.js
index 4b7ed1043..3589eac4b 100644
--- a/modules/ticket/back/models/sale.js
+++ b/modules/ticket/back/models/sale.js
@@ -12,6 +12,7 @@ module.exports = Self => {
require('../methods/sale/refund')(Self);
require('../methods/sale/canEdit')(Self);
require('../methods/sale/usesMana')(Self);
+ require('../methods/sale/clone')(Self);
Self.validatesPresenceOf('concept', {
message: `Concept cannot be blank`
@@ -33,6 +34,7 @@ module.exports = Self => {
const itemId = changes?.itemFk || instance?.itemFk;
const oldQuantity = instance?.quantity ?? null;
const quantityAdded = newQuantity - oldQuantity;
+ const isReduction = oldQuantity && newQuantity <= oldQuantity;
const ticket = await models.Ticket.findById(
ticketId,
@@ -69,6 +71,8 @@ module.exports = Self => {
}, ctx.options);
if (item.family == 'EMB') return;
+ if (await models.ACL.checkAccessAcl(ctx, 'Sale', 'isInPreparing', '*')) return;
+
await models.Sale.rawSql(`CALL catalog_calcFromItem(?,?,?,?)`, [
ticket.landed,
ticket.addressFk,
@@ -78,16 +82,17 @@ module.exports = Self => {
ctx.options);
const [itemInfo] = await models.Sale.rawSql(`SELECT available FROM tmp.ticketCalculateItem`, null, ctx.options);
+ const available = itemInfo?.available;
- if (!itemInfo?.available || itemInfo.available < quantityAdded)
+ if ((!isReduction && !available) || available < quantityAdded)
throw new UserError(`This item is not available`);
if (await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*')) return;
- if (newQuantity < item.minQuantity && newQuantity != itemInfo?.available)
+ if (newQuantity < item.minQuantity && newQuantity != available)
throw new UserError('The amount cannot be less than the minimum');
- if (ctx.isNewInstance || newQuantity <= oldQuantity) return;
+ if (ctx.isNewInstance || isReduction) return;
const [saleGrouping] = await models.Sale.rawSql(`
SELECT t.price newPrice
diff --git a/modules/ticket/back/models/specs/sale.spec.js b/modules/ticket/back/models/specs/sale.spec.js
new file mode 100644
index 000000000..4af44c991
--- /dev/null
+++ b/modules/ticket/back/models/specs/sale.spec.js
@@ -0,0 +1,361 @@
+/* eslint max-len: ["error", { "code": 150 }]*/
+
+const models = require('vn-loopback/server/server').models;
+const LoopBackContext = require('loopback-context');
+
+describe('sale model ', () => {
+ const ctx = {
+ req: {
+ accessToken: {userId: 9},
+ headers: {origin: 'localhost:5000'},
+ __: () => {}
+ }
+ };
+ function getActiveCtx(userId) {
+ return {
+ active: {
+ accessToken: {userId},
+ http: {
+ req: {
+ headers: {origin: 'http://localhost'}
+ }
+ }
+ }
+ };
+ }
+
+ describe('quantity field ', () => {
+ it('should add quantity if the quantity is greater than it should be and is role advanced', async() => {
+ const saleId = 17;
+ const buyerId = 35;
+ const ctx = {
+ req: {
+ accessToken: {userId: buyerId},
+ headers: {origin: 'localhost:5000'},
+ __: () => {}
+ }
+ };
+ const tx = await models.Sale.beginTransaction({});
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(buyerId));
+ spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
+ if (sqlStatement.includes('catalog_calcFromItem')) {
+ sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
+ SELECT 100 as available;`;
+ params = null;
+ }
+ return models.Ticket.rawSql(sqlStatement, params, options);
+ });
+
+ try {
+ const options = {transaction: tx};
+
+ const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*');
+
+ expect(isRoleAdvanced).toEqual(true);
+
+ const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
+
+ expect(originalLine.quantity).toEqual(30);
+
+ const newQuantity = originalLine.quantity + 1;
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
+
+ const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
+
+ expect(modifiedLine.quantity).toEqual(newQuantity);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ it('should update the quantity of a given sale current line', async() => {
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(9));
+
+ const tx = await models.Sale.beginTransaction({});
+ const saleId = 25;
+ const newQuantity = 4;
+
+ try {
+ const options = {transaction: tx};
+
+ const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
+
+ expect(originalLine.quantity).toEqual(20);
+
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
+
+ const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
+
+ expect(modifiedLine.quantity).toEqual(newQuantity);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ it('should throw an error if the quantity is negative and it is not a refund ticket', async() => {
+ const ctx = {
+ req: {
+ accessToken: {userId: 1},
+ headers: {origin: 'localhost:5000'},
+ __: () => {}
+ }
+ };
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
+
+ const saleId = 17;
+ const newQuantity = -10;
+
+ const tx = await models.Sale.beginTransaction({});
+
+ let error;
+ try {
+ const options = {transaction: tx};
+
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error).toEqual(new Error('You can only add negative amounts in refund tickets'));
+ });
+
+ it('should update a negative quantity when is a ticket refund', async() => {
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(9));
+
+ const tx = await models.Sale.beginTransaction({});
+ const saleId = 13;
+ const newQuantity = -10;
+
+ try {
+ const options = {transaction: tx};
+
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
+
+ const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
+
+ expect(modifiedLine.quantity).toEqual(newQuantity);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ it('should throw an error if the quantity is less than the minimum quantity of the item', async() => {
+ const ctx = {
+ req: {
+ accessToken: {userId: 1},
+ headers: {origin: 'localhost:5000'},
+ __: () => {}
+ }
+ };
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
+
+ const tx = await models.Sale.beginTransaction({});
+ const itemId = 2;
+ const saleId = 17;
+ const minQuantity = 30;
+ const newQuantity = minQuantity - 1;
+
+ let error;
+ try {
+ const options = {transaction: tx};
+
+ const item = await models.Item.findById(itemId, null, options);
+ await item.updateAttribute('minQuantity', minQuantity, options);
+ spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
+ if (sqlStatement.includes('catalog_calcFromItem')) {
+ sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
+ SELECT 100 as available;`;
+ params = null;
+ }
+ return models.Ticket.rawSql(sqlStatement, params, options);
+ });
+
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error).toEqual(new Error('The amount cannot be less than the minimum'));
+ });
+
+ it('should change quantity if has minimum quantity and new quantity is equal than item available', async() => {
+ const ctx = {
+ req: {
+ accessToken: {userId: 1},
+ headers: {origin: 'localhost:5000'},
+ __: () => {}
+ }
+ };
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
+
+ const tx = await models.Sale.beginTransaction({});
+ const itemId = 2;
+ const saleId = 17;
+ const minQuantity = 30;
+ const newQuantity = minQuantity - 1;
+
+ try {
+ const options = {transaction: tx};
+
+ const item = await models.Item.findById(itemId, null, options);
+ await item.updateAttribute('minQuantity', minQuantity, options);
+
+ spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
+ if (sqlStatement.includes('catalog_calcFromItem')) {
+ sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
+ SELECT ${newQuantity} as available;`;
+ params = null;
+ }
+ return models.Ticket.rawSql(sqlStatement, params, options);
+ });
+
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ describe('newPrice', () => {
+ it('should increase quantity if you have enough available and the new price is the same as the previous one', async() => {
+ const ctx = {
+ req: {
+ accessToken: {userId: 1},
+ headers: {origin: 'localhost:5000'},
+ __: () => {}
+ }
+ };
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
+
+ const tx = await models.Sale.beginTransaction({});
+ const itemId = 2;
+ const saleId = 17;
+ const minQuantity = 30;
+ const newQuantity = 31;
+
+ try {
+ const options = {transaction: tx};
+
+ const item = await models.Item.findById(itemId, null, options);
+ await item.updateAttribute('minQuantity', minQuantity, options);
+ spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
+ if (sqlStatement.includes('catalog_calcFromItem')) {
+ sqlStatement = `
+ CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available;
+ CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 7.07 as price;`;
+ params = null;
+ }
+ return models.Ticket.rawSql(sqlStatement, params, options);
+ });
+
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ it('should increase quantity when the new price is lower than the previous one', async() => {
+ const ctx = {
+ req: {
+ accessToken: {userId: 1},
+ headers: {origin: 'localhost:5000'},
+ __: () => {}
+ }
+ };
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
+
+ const tx = await models.Sale.beginTransaction({});
+ const itemId = 2;
+ const saleId = 17;
+ const minQuantity = 30;
+ const newQuantity = 31;
+
+ try {
+ const options = {transaction: tx};
+
+ const item = await models.Item.findById(itemId, null, options);
+ await item.updateAttribute('minQuantity', minQuantity, options);
+ spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
+ if (sqlStatement.includes('catalog_calcFromItem')) {
+ sqlStatement = `
+ CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available;
+ CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 1 as price;`;
+ params = null;
+ }
+ return models.Ticket.rawSql(sqlStatement, params, options);
+ });
+
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ it('should throw error when increase quantity and the new price is higher than the previous one', async() => {
+ const ctx = {
+ req: {
+ accessToken: {userId: 1},
+ headers: {origin: 'localhost:5000'},
+ __: () => {}
+ }
+ };
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
+
+ const tx = await models.Sale.beginTransaction({});
+ const itemId = 2;
+ const saleId = 17;
+ const minQuantity = 30;
+ const newQuantity = 31;
+
+ let error;
+ try {
+ const options = {transaction: tx};
+
+ const item = await models.Item.findById(itemId, null, options);
+ await item.updateAttribute('minQuantity', minQuantity, options);
+ spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
+ if (sqlStatement.includes('catalog_calcFromItem')) {
+ sqlStatement = `
+ CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available;
+ CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 100000 as price;`;
+ params = null;
+ }
+ return models.Ticket.rawSql(sqlStatement, params, options);
+ });
+
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error).toEqual(new Error('The price of the item changed'));
+ });
+ });
+ });
+});
diff --git a/modules/ticket/back/models/ticket-tracking.js b/modules/ticket/back/models/ticket-tracking.js
index 2e6d3403e..92e046d3e 100644
--- a/modules/ticket/back/models/ticket-tracking.js
+++ b/modules/ticket/back/models/ticket-tracking.js
@@ -1,5 +1,4 @@
module.exports = function(Self) {
- require('../methods/ticket-tracking/changeState')(Self);
require('../methods/ticket-tracking/setDelivered')(Self);
Self.validatesPresenceOf('stateFk', {message: 'State cannot be blank'});
diff --git a/modules/ticket/back/models/ticket.js b/modules/ticket/back/models/ticket.js
index ea068ef8a..1930765fb 100644
--- a/modules/ticket/back/models/ticket.js
+++ b/modules/ticket/back/models/ticket.js
@@ -1,4 +1,4 @@
module.exports = Self => {
- // Methods
require('./ticket-methods')(Self);
+ require('../methods/ticket/state')(Self);
};
diff --git a/modules/ticket/front/advance/index.html b/modules/ticket/front/advance/index.html
index e6f16c965..a6cf8face 100644
--- a/modules/ticket/front/advance/index.html
+++ b/modules/ticket/front/advance/index.html
@@ -81,6 +81,9 @@
Liters
|
+
+ Zone
+ |
Not Movable
|
@@ -155,6 +158,7 @@
{{::ticket.futureLiters | dashIfEmpty}} |
+ {{::ticket.futureZoneName | dashIfEmpty}} |
{{::ticket.notMovableLines | dashIfEmpty}} |
{{::ticket.futureLines | dashIfEmpty}} |
diff --git a/modules/ticket/front/advance/index.js b/modules/ticket/front/advance/index.js
index 6f8a92ebe..389bcdf14 100644
--- a/modules/ticket/front/advance/index.js
+++ b/modules/ticket/front/advance/index.js
@@ -15,28 +15,22 @@ export default class Controller extends Section {
{
field: 'state',
searchable: false
- },
- {
+ }, {
field: 'futureState',
searchable: false
- },
- {
+ }, {
field: 'totalWithVat',
searchable: false
- },
- {
+ }, {
field: 'futureTotalWithVat',
searchable: false
- },
- {
+ }, {
field: 'shipped',
searchable: false
- },
- {
+ }, {
field: 'futureShipped',
searchable: false
- },
- {
+ }, {
field: 'ipt',
autocomplete: {
url: 'ItemPackingTypes',
@@ -44,8 +38,7 @@ export default class Controller extends Section {
showField: 'description',
valueField: 'code'
}
- },
- {
+ }, {
field: 'futureIpt',
autocomplete: {
url: 'ItemPackingTypes',
@@ -53,6 +46,11 @@ export default class Controller extends Section {
showField: 'description',
valueField: 'code'
}
+ }, {
+ field: 'futureZoneFk',
+ autocomplete: {
+ url: 'Zones',
+ }
},
]
};
@@ -158,27 +156,21 @@ export default class Controller extends Section {
exprBuilder(param, value) {
switch (param) {
case 'id':
- return {'id': value};
case 'futureId':
- return {'futureId': value};
case 'liters':
- return {'liters': value};
case 'futureLiters':
- return {'futureLiters': value};
case 'lines':
- return {'lines': value};
case 'futureLines':
- return {'futureLines': value};
+ case 'totalWithVat':
+ case 'futureTotalWithVat':
+ case 'futureZone':
+ case 'notMovableLines':
+ case 'futureZoneFk':
+ return {[param]: value};
case 'ipt':
return {'ipt': {like: `%${value}%`}};
case 'futureIpt':
return {'futureIpt': {like: `%${value}%`}};
- case 'totalWithVat':
- return {'totalWithVat': value};
- case 'futureTotalWithVat':
- return {'futureTotalWithVat': value};
- case 'notMovableLines':
- return {'notMovableLines': value};
}
}
}
diff --git a/modules/ticket/front/sale/index.html b/modules/ticket/front/sale/index.html
index 729822764..262395d16 100644
--- a/modules/ticket/front/sale/index.html
+++ b/modules/ticket/front/sale/index.html
@@ -15,7 +15,7 @@
+ on-change="$ctrl.state(value)">
{
+ return this.$http.post('Tickets/state', params).then(() => {
this.vnApp.showSuccess(this.$t('Data saved!'));
this.card.reload();
}).finally(() => this.resetChanges());
diff --git a/modules/ticket/front/sale/index.spec.js b/modules/ticket/front/sale/index.spec.js
index b36e78893..70781eb58 100644
--- a/modules/ticket/front/sale/index.spec.js
+++ b/modules/ticket/front/sale/index.spec.js
@@ -228,15 +228,16 @@ describe('Ticket', () => {
});
});
- describe('changeState()', () => {
- it('should make an HTTP post query, then call the showSuccess(), reload() and resetChanges() methods', () => {
+ describe('state()', () => {
+ it('should make an HTTP post query, then call the showSuccess(),' +
+ ' reload() and resetChanges() methods', () => {
jest.spyOn(controller.card, 'reload').mockReturnThis();
jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
jest.spyOn(controller, 'resetChanges').mockReturnThis();
const expectedParams = {ticketFk: 1, code: 'OK'};
- $httpBackend.expect('POST', `TicketTrackings/changeState`, expectedParams).respond(200);
- controller.changeState('OK');
+ $httpBackend.expect('POST', `Tickets/state`, expectedParams).respond(200);
+ controller.state('OK');
$httpBackend.flush();
expect(controller.card.reload).toHaveBeenCalledWith();
@@ -246,7 +247,8 @@ describe('Ticket', () => {
});
describe('removeSales()', () => {
- it('should make an HTTP post query, then call the showSuccess(), removeSelectedSales() and resetChanges() methods', () => {
+ it('should make an HTTP post query, then call the showSuccess(),' +
+ ' removeSelectedSales() and resetChanges() methods', () => {
jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
jest.spyOn(controller, 'removeSelectedSales').mockReturnThis();
jest.spyOn(controller, 'resetChanges').mockReturnThis();
@@ -352,7 +354,8 @@ describe('Ticket', () => {
});
describe('updatePrice()', () => {
- it('should make an HTTP POST query, update the sale price and then call to the resetChanges() method', () => {
+ it('should make an HTTP POST query, update the sale price ' +
+ 'and then call to the resetChanges() method', () => {
jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
jest.spyOn(controller, 'resetChanges').mockReturnThis();
@@ -418,7 +421,8 @@ describe('Ticket', () => {
expect(controller.$.editDiscount.hide).toHaveBeenCalledWith();
});
- it('should not call to the updateDiscount() method and then to the editDiscountDialog hide() method', () => {
+ it('should not call to the updateDiscount() method and then' +
+ ' to the editDiscountDialog hide() method', () => {
jest.spyOn(controller, 'updateDiscount').mockReturnThis();
const firstSelectedSale = controller.sales[0];
@@ -444,7 +448,8 @@ describe('Ticket', () => {
});
describe('updateDiscount()', () => {
- it('should make an HTTP POST query, update the sales discount and then call to the resetChanges() method', () => {
+ it('should make an HTTP POST query, update the sales discount ' +
+ 'and then call to the resetChanges() method', () => {
jest.spyOn(controller, 'resetChanges').mockReturnThis();
jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
diff --git a/modules/ticket/front/summary/index.html b/modules/ticket/front/summary/index.html
index 3c60352a7..025078d36 100644
--- a/modules/ticket/front/summary/index.html
+++ b/modules/ticket/front/summary/index.html
@@ -18,7 +18,7 @@
value-field="code"
fields="['id', 'name', 'alertLevel', 'code']"
url="States/editableStates"
- on-change="$ctrl.changeState(value)">
+ on-change="$ctrl.state(value)">
{{::sale.quantity}}
- {{::sale.item.name}}
+ {{::sale.concept}}
{{::sale.item.subName}}
diff --git a/modules/ticket/front/summary/index.js b/modules/ticket/front/summary/index.js
index 60394b5b3..9d161f63e 100644
--- a/modules/ticket/front/summary/index.js
+++ b/modules/ticket/front/summary/index.js
@@ -59,13 +59,12 @@ class Controller extends Summary {
this.$.invoiceOutDescriptor.show(event.target, this.summary.invoiceOut.id);
}
- changeState(value) {
+ state(value) {
const params = {
ticketFk: 'id' in this.ticket ? this.ticket.id : this.$params.id,
code: value
};
-
- this.$http.post(`TicketTrackings/changeState`, params)
+ this.$http.post(`Tickets/state`, params)
.then(() => {
if ('id' in this.$params) this.reload();
})
diff --git a/modules/ticket/front/summary/index.spec.js b/modules/ticket/front/summary/index.spec.js
index 599da73ae..6837bfd54 100644
--- a/modules/ticket/front/summary/index.spec.js
+++ b/modules/ticket/front/summary/index.spec.js
@@ -43,15 +43,15 @@ describe('Ticket', () => {
});
});
- describe('changeState()', () => {
+ describe('state()', () => {
it('should change the state', () => {
jest.spyOn(controller.vnApp, 'showSuccess');
const value = 'myTicketState';
let res = {id: 1, nickname: 'myNickname'};
$httpBackend.when('GET', `Tickets/1/summary`).respond(200, res);
- $httpBackend.expectPOST(`TicketTrackings/changeState`).respond(200, 'ok');
- controller.changeState(value);
+ $httpBackend.expectPOST(`Tickets/state`).respond(200, 'ok');
+ controller.state(value);
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
diff --git a/modules/ticket/front/tracking/edit/index.html b/modules/ticket/front/tracking/edit/index.html
index bff8e71b1..47f367007 100644
--- a/modules/ticket/front/tracking/edit/index.html
+++ b/modules/ticket/front/tracking/edit/index.html
@@ -1,4 +1,4 @@
-
+
{
+ this.$http.post(`Tickets/state`, this.params).then(() => {
this.$.watcher.updateOriginalData();
this.card.reload();
this.vnApp.showSuccess(this.$t('Data saved!'));
diff --git a/modules/ticket/front/tracking/edit/index.spec.js b/modules/ticket/front/tracking/edit/index.spec.js
index 1ba5912b5..9d9aa7983 100644
--- a/modules/ticket/front/tracking/edit/index.spec.js
+++ b/modules/ticket/front/tracking/edit/index.spec.js
@@ -61,7 +61,7 @@ describe('Ticket', () => {
jest.spyOn(controller.vnApp, 'showSuccess');
jest.spyOn(controller.$state, 'go');
- $httpBackend.expectPOST(`TicketTrackings/changeState`, controller.params).respond({});
+ $httpBackend.expectPOST(`Tickets/state`, controller.params).respond({});
controller.onSubmit();
$httpBackend.flush();
diff --git a/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js b/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js
index e90c849b7..42ec6290a 100644
--- a/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js
+++ b/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js
@@ -14,7 +14,6 @@ describe('workerTimeControl add/delete timeEntry()', () => {
const tuesday = 2;
const thursday = 4;
const friday = 5;
- const saturday = 6;
const sunday = 7;
const activeCtx = {
accessToken: {userId: 50},
@@ -200,15 +199,15 @@ describe('workerTimeControl add/delete timeEntry()', () => {
});
describe('WorkerTimeControl_clockIn calls', () => {
- it('should fail to add a time entry if the target user has an absence that day', async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
+ let workerId;
+ beforeEach(() => {
activeCtx.accessToken.userId = salesBossId;
- const workerId = hankPymId;
+ workerId = hankPymId;
+ });
+ it('should fail to add a time entry if the target user has an absence that day', async() => {
const date = Date.vnNew();
- date.setDate(date.getDate() - 16);
date.setHours(8, 0, 0);
- let error;
-
+ date.setDate(date.getDate() - 16);
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
try {
@@ -225,15 +224,12 @@ describe('workerTimeControl add/delete timeEntry()', () => {
});
it('should fail to add a time entry for a worker without an existing contract', async() => {
- activeCtx.accessToken.userId = salesBossId;
- const workerId = hankPymId;
const date = Date.vnNew();
date.setFullYear(date.getFullYear() - 2);
- let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
try {
- const options = {transaction: tx};
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
@@ -246,19 +242,39 @@ describe('workerTimeControl add/delete timeEntry()', () => {
expect(error.message).toBe(`No hay un contrato en vigor`);
});
+ it('should fail to add a time entry for a worker without an existing contract', async() => {
+ let date = Date.vnNew();
+ date.setDate(date.getDate() - 2);
+ let error;
+
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+ date.setHours(0, 0, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ try {
+ date.setHours(20,0, 1);
+ ctx.args = {timed: date, direction: 'out'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toBe(`Superado el tiempo máximo entre entrada y salida`);
+ });
+
describe('direction errors', () => {
+ let date = Date.vnNew();
+ date.setDate(date.getDate() - 1);
+ let error;
it('should throw an error when trying "in" direction twice', async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- activeCtx.accessToken.userId = salesBossId;
- const workerId = hankPymId;
-
- let date = Date.vnNew();
- date.setDate(date.getDate() - 21);
- date = weekDay(date, monday);
- let error;
-
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
+
date.setHours(8, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
@@ -278,21 +294,13 @@ describe('workerTimeControl add/delete timeEntry()', () => {
});
it('should throw an error when trying "in" direction after insert "in" and "middle"', async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- activeCtx.accessToken.userId = salesBossId;
- const workerId = hankPymId;
-
- let date = Date.vnNew();
- date.setDate(date.getDate() - 21);
- date = weekDay(date, monday);
- let error;
-
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
date.setHours(8, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
date.setHours(9, 0, 0);
ctx.args = {timed: date, direction: 'middle'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
@@ -312,15 +320,6 @@ describe('workerTimeControl add/delete timeEntry()', () => {
});
it('Should throw an error when trying "out" before closing a "middle" couple', async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- activeCtx.accessToken.userId = salesBossId;
- const workerId = hankPymId;
-
- let date = Date.vnNew();
- date.setDate(date.getDate() - 21);
- date = weekDay(date, monday);
- let error;
-
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
@@ -346,15 +345,6 @@ describe('workerTimeControl add/delete timeEntry()', () => {
});
it('should throw an error when trying "middle" after "out"', async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- activeCtx.accessToken.userId = salesBossId;
- const workerId = hankPymId;
-
- let date = Date.vnNew();
- date.setDate(date.getDate() - 21);
- date = weekDay(date, monday);
- let error;
-
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
@@ -380,15 +370,6 @@ describe('workerTimeControl add/delete timeEntry()', () => {
});
it('should throw an error when trying "out" direction twice', async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- activeCtx.accessToken.userId = salesBossId;
- const workerId = hankPymId;
-
- let date = Date.vnNew();
- date.setDate(date.getDate() - 21);
- date = weekDay(date, monday);
- let error;
-
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
@@ -415,14 +396,12 @@ describe('workerTimeControl add/delete timeEntry()', () => {
});
describe('12h rest', () => {
+ activeCtx.accessToken.userId = salesBossId;
+ const workerId = hankPymId;
it('should throw an error when the 12h rest is not fulfilled yet', async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
-
- activeCtx.accessToken.userId = salesBossId;
- const workerId = hankPymId;
let date = Date.vnNew();
- date.setDate(date.getDate() - 21);
+ date.setDate(date.getDate() - 2);
date = weekDay(date, monday);
let error;
@@ -448,16 +427,12 @@ describe('workerTimeControl add/delete timeEntry()', () => {
error = e;
}
- expect(error.message).toBe(`Descanso diario 12h.`);
+ expect(error.message).toBe(`Descanso diario`);
});
it('should not fail as the 12h rest is fulfilled', async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- activeCtx.accessToken.userId = salesBossId;
- const workerId = hankPymId;
-
let date = Date.vnNew();
- date.setDate(date.getDate() - 21);
+ date.setDate(date.getDate() - 2);
date = weekDay(date, monday);
let error;
@@ -488,13 +463,12 @@ describe('workerTimeControl add/delete timeEntry()', () => {
});
describe('for 3500kg drivers with enforced 9h rest', () => {
+ activeCtx.accessToken.userId = salesBossId;
+ const workerId = jessicaJonesId;
it('should throw an error when the 9h enforced rest is not fulfilled', async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- activeCtx.accessToken.userId = salesBossId;
- const workerId = jessicaJonesId;
let date = Date.vnNew();
- date.setDate(date.getDate() - 21);
+ date.setDate(date.getDate() - 2);
date = weekDay(date, monday);
let error;
@@ -520,16 +494,13 @@ describe('workerTimeControl add/delete timeEntry()', () => {
error = e;
}
- expect(error.message).toBe(`Descanso diario 9h.`);
+ expect(error.message).toBe(`Descanso diario`);
});
it('should not fail when the 9h enforced rest is fulfilled', async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- activeCtx.accessToken.userId = salesBossId;
- const workerId = jessicaJonesId;
-
+
let date = Date.vnNew();
- date.setDate(date.getDate() - 21);
+ date.setDate(date.getDate() - 2);
date = weekDay(date, monday);
let error;
@@ -559,14 +530,11 @@ describe('workerTimeControl add/delete timeEntry()', () => {
});
});
- describe('for 36h weekly rest', () => {
- it('should throw an error when the 36h weekly rest is not fulfilled', async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- activeCtx.accessToken.userId = salesBossId;
- const workerId = hankPymId;
-
+ describe('for 72h weekly rest', () => {
+
+ it('should throw an error when work 11 consecutive days', async() => {
let date = Date.vnNew();
- date.setMonth(date.getMonth() - 2);
+ date.setMonth(date.getMonth() - 1);
date.setDate(1);
let error;
@@ -576,66 +544,24 @@ describe('workerTimeControl add/delete timeEntry()', () => {
await populateWeek(date, monday, sunday, ctx, workerId, options);
date = nextWeek(date);
await populateWeek(date, monday, thursday, ctx, workerId, options);
- date = weekDay(date, friday);
- date.setHours(7, 59, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
try {
- date.setHours(8, 1, 0);
- ctx.args = {timed: date, direction: 'out'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error.message).toBe(`Descanso semanal 36h. / 72h.`);
- });
-
- it('should throw an error when the 36h weekly rest is not fulfilled again', async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- activeCtx.accessToken.userId = salesBossId;
- const workerId = hankPymId;
-
- let date = Date.vnNew();
- date.setMonth(date.getMonth() - 2);
- date.setDate(1);
- let error;
-
- const tx = await models.WorkerTimeControl.beginTransaction({});
- const options = {transaction: tx};
-
- await populateWeek(date, monday, sunday, ctx, workerId, options);
- date = nextWeek(date);
- await populateWeek(date, monday, thursday, ctx, workerId, options);
-
- try {
- date = weekDay(date, saturday);
- date.setHours(3, 59, 0);
+ date = weekDay(date, friday);
+ date.setHours(10, 0, 1);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
- expect(error.message).toBe(`Descanso semanal 36h. / 72h.`);
+ expect(error.message).toBe(`Descanso semanal`);
});
- });
- describe('for 72h weekly rest', () => {
- it('should throw when the 72h weekly rest is not fulfilled yet', async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- activeCtx.accessToken.userId = salesBossId;
- const workerId = hankPymId;
+ it('should throw an error when the 72h weekly rest is not fulfilled', async() => {
let date = Date.vnNew();
- date.setMonth(date.getMonth() - 2);
+ date.setMonth(date.getMonth() - 1);
date.setDate(1);
let error;
@@ -645,32 +571,263 @@ describe('workerTimeControl add/delete timeEntry()', () => {
await populateWeek(date, monday, sunday, ctx, workerId, options);
date = nextWeek(date);
await populateWeek(date, monday, thursday, ctx, workerId, options);
- date = nextWeek(date);
- await populateWeek(date, monday, friday, ctx, workerId, options);
- date = nextWeek(date);
- await populateWeek(date, monday, saturday, ctx, workerId, options);
- date = nextWeek(date);
- await populateWeek(date, monday, saturday, ctx, workerId, options);
- date = lastWeek(date);
try {
date = weekDay(date, sunday);
- date.setHours(8, 0, 0);
+ date.setHours(17, 59, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
- expect(error.message).toBe(`Descanso semanal 36h. / 72h.`);
+ expect(error.message).toBe(`Descanso semanal`);
+ });
+
+ it('should throw an error when the 72h weekly rest is fulfilled', async() => {
+
+ let date = Date.vnNew();
+ date.setMonth(date.getMonth() - 1);
+ date.setDate(1);
+ let error;
+
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ await populateWeek(date, monday, sunday, ctx, workerId, options);
+ date = nextWeek(date);
+ await populateWeek(date, monday, thursday, ctx, workerId, options);
+
+ try {
+ date = weekDay(date, sunday);
+ date.setHours(18, 00, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error).not.toBeDefined;
+ });
+ });
+
+ describe('WorkerTimeControl_calculate calls', () => {
+ let dated = Date.vnNew();
+ dated.setDate(dated.getDate() - 7);
+ dated = new Date(weekDay(dated, monday));
+ const end = new Date(dated);
+ end.setDate(end.getDate() + 1);
+
+ it(`should return today's worked 8 hours without break`, async() => {
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ try {
+ await populateWeek(dated, monday, monday, ctx, hankPymId, options);
+ const start = new Date(dated - 1);
+ start.setHours(0, 0, 0);
+ await models.WorkerTimeControl.rawSql('CALL vn.timeControl_calculateByUser(?, ?, ?)', [
+ hankPymId,
+ start,
+ end
+ ], options);
+
+ let [timeControlCalculateTable] = await models.WorkerTimeControl.rawSql('SELECT * FROM tmp.timeControlCalculate', null, options);
+
+ expect(timeControlCalculateTable.timeWorkSeconds).toEqual(28800);
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ it(`should return today's worked hours with 15min break and work time consecutive less than 5h`, async() => {
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ try {
+ await populateWeek(dated, monday, monday, ctx, hankPymId, options);
+ dated.setHours(14, 59, 0);
+ await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
+ dated.setHours(15, 14, 0);
+ await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
+
+ const start = new Date(dated - 1);
+ start.setHours(0, 0, 0);
+ await models.WorkerTimeControl.rawSql('CALL vn.timeControl_calculateByUser(?, ?, ?)', [
+ hankPymId,
+ start,
+ end
+ ], options);
+
+ let [timeControlCalculateTable] = await models.WorkerTimeControl.rawSql('SELECT * FROM tmp.timeControlCalculate', null, options);
+
+ expect(timeControlCalculateTable.timeWorkSeconds).toEqual(28800);
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ it(`should return today's worked hours with 15min break and work time consecutive greater than 5h`, async() => {
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ try {
+ await populateWeek(dated, monday, monday, ctx, hankPymId, options);
+ dated.setHours(15, 0, 0);
+ await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
+ dated.setHours(15, 15, 0);
+ await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
+
+ const start = new Date(dated - 1);
+ start.setHours(0, 0, 0);
+ await models.WorkerTimeControl.rawSql('CALL vn.timeControl_calculateByUser(?, ?, ?)', [
+ hankPymId,
+ start,
+ end
+ ], options);
+
+ let [timeControlCalculateTable] = await models.WorkerTimeControl.rawSql('SELECT * FROM tmp.timeControlCalculate', null, options);
+
+ expect(timeControlCalculateTable.timeWorkSeconds).toEqual(28800);
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ it(`should return today's worked hours with 25min break and work time consecutive less than 5h`, async() => {
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ try {
+ await populateWeek(dated, monday, monday, ctx, hankPymId, options);
+ dated.setHours(14, 59, 0);
+ await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
+ dated.setHours(15, 24, 0);
+ await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
+
+ const start = new Date(dated - 1);
+ start.setHours(0, 0, 0);
+ await models.WorkerTimeControl.rawSql('CALL vn.timeControl_calculateByUser(?, ?, ?)', [
+ hankPymId,
+ start,
+ end
+ ], options);
+
+ let [timeControlCalculateTable] = await models.WorkerTimeControl.rawSql('SELECT * FROM tmp.timeControlCalculate', null, options);
+
+ expect(timeControlCalculateTable.timeWorkSeconds).toEqual(28500);
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ it(`should return today's worked hours with 25min break and work time consecutive greater than 5h`, async() => {
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ try {
+ await populateWeek(dated, monday, monday, ctx, hankPymId, options);
+ dated.setHours(15, 0, 0);
+ await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
+ dated.setHours(15, 25, 0);
+ await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
+
+ const start = new Date(dated - 1);
+ start.setHours(0, 0, 0);
+ await models.WorkerTimeControl.rawSql('CALL vn.timeControl_calculateByUser(?, ?, ?)', [
+ hankPymId,
+ start,
+ end
+ ], options);
+
+ let [timeControlCalculateTable] = await models.WorkerTimeControl.rawSql('SELECT * FROM tmp.timeControlCalculate', null, options);
+
+ expect(timeControlCalculateTable.timeWorkSeconds).toEqual(28500);
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ it(`should return today's worked hours with 60min break and work time consecutive less than 5h`, async() => {
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ try {
+ await populateWeek(dated, monday, monday, ctx, hankPymId, options);
+ dated.setHours(14, 59, 0);
+ await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
+ dated.setHours(15, 59, 0);
+ await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
+
+ const start = new Date(dated - 1);
+ start.setHours(0, 0, 0);
+ await models.WorkerTimeControl.rawSql('CALL vn.timeControl_calculateByUser(?, ?, ?)', [
+ hankPymId,
+ start,
+ end
+ ], options);
+
+ let [timeControlCalculateTable] = await models.WorkerTimeControl.rawSql('SELECT * FROM tmp.timeControlCalculate', null, options);
+
+ expect(timeControlCalculateTable.timeWorkSeconds).toEqual(25200);
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ it(`should return today's worked hours with 60min break and work time consecutive greater than 5h`, async() => {
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ try {
+ await populateWeek(dated, monday, monday, ctx, hankPymId, options);
+ dated.setHours(15, 0, 0);
+ await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
+ dated.setHours(16, 0, 0);
+ await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
+
+ const start = new Date(dated - 1);
+ start.setHours(0, 0, 0);
+ await models.WorkerTimeControl.rawSql('CALL vn.timeControl_calculateByUser(?, ?, ?)', [
+ hankPymId,
+ start,
+ end
+ ], options);
+
+ let [timeControlCalculateTable] = await models.WorkerTimeControl.rawSql('SELECT * FROM tmp.timeControlCalculate', null, options);
+
+ expect(timeControlCalculateTable.timeWorkSeconds).toEqual(26400);
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
});
});
});
});
+async function addTimeEntry(ctx, dated, direction, userId, option) {
+ ctx.args = {timed: dated, direction};
+ await models.WorkerTimeControl.addTimeEntry(ctx, userId, option);
+}
+
function weekDay(date, dayToSet) {
const currentDay = date.getDay();
const distance = dayToSet - currentDay;
@@ -704,10 +861,10 @@ async function populateWeek(date, dayStart, dayEnd, ctx, workerId, options) {
dateEnd.setDate(dateStart.getDate() + dayEnd);
for (let i = dayStart; i <= dayEnd; i++) {
- dateStart.setHours(8, 0, 0);
+ dateStart.setHours(10, 0, 0);
ctx.args = {timed: dateStart, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
- dateStart.setHours(16, 0, 0);
+ dateStart.setHours(18, 0, 0);
ctx.args = {timed: dateStart, direction: 'out'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
dateStart.setDate(dateStart.getDate() + 1);
diff --git a/modules/worker/back/methods/worker/createAbsence.js b/modules/worker/back/methods/worker/createAbsence.js
index cb2cf8330..d628d0a2b 100644
--- a/modules/worker/back/methods/worker/createAbsence.js
+++ b/modules/worker/back/methods/worker/createAbsence.js
@@ -109,13 +109,13 @@ module.exports = Self => {
const absenceType = await models.AbsenceType.findById(args.absenceTypeId, null, myOptions);
const account = await models.VnUser.findById(userId, null, myOptions);
const subordinated = await models.VnUser.findById(id, null, myOptions);
- const origin = ctx.req.headers.origin;
+ const url = await Self.app.models.Url.getUrl();
const body = $t('Created absence', {
author: account.nickname,
employee: subordinated.nickname,
absenceType: absenceType.name,
dated: formatDate(args.dated),
- workerUrl: `${origin}/#!/worker/${id}/calendar`
+ workerUrl: `${url}worker/${id}/calendar`
});
await models.Mail.create({
subject: $t('Absence change notification on the labour calendar'),
diff --git a/modules/worker/back/methods/worker/deleteAbsence.js b/modules/worker/back/methods/worker/deleteAbsence.js
index c315f5178..b71d077a4 100644
--- a/modules/worker/back/methods/worker/deleteAbsence.js
+++ b/modules/worker/back/methods/worker/deleteAbsence.js
@@ -60,13 +60,13 @@ module.exports = Self => {
const absenceType = await models.AbsenceType.findById(absence.dayOffTypeFk, null, myOptions);
const account = await models.VnUser.findById(userId, null, myOptions);
const subordinated = await models.VnUser.findById(labour.workerFk, null, myOptions);
- const origin = ctx.req.headers.origin;
+ const url = await Self.app.models.Url.getUrl();
const body = $t('Deleted absence', {
author: account.nickname,
employee: subordinated.nickname,
absenceType: absenceType.name,
dated: formatDate(absence.dated),
- workerUrl: `${origin}/#!/worker/${id}/calendar`
+ workerUrl: `${url}worker/${id}/calendar`
});
await models.Mail.create({
subject: $t('Absence change notification on the labour calendar'),
diff --git a/modules/worker/back/methods/worker/new.js b/modules/worker/back/methods/worker/new.js
index 199a3be62..5316daf01 100644
--- a/modules/worker/back/methods/worker/new.js
+++ b/modules/worker/back/methods/worker/new.js
@@ -223,19 +223,16 @@ module.exports = Self => {
const user = await models.VnUser.findById(client.id, null, myOptions);
await user.updateAttribute('email', args.email, myOptions);
- await models.Worker.rawSql(
- 'CALL vn.workerCreate(?, ?, ?, ?, ?, ?, ?)',
- [
- args.firstName,
- args.lastNames,
- args.code,
- args.bossFk,
- client.id,
- args.fi,
- args.birth,
- ],
- myOptions
- );
+ await models.Worker.create({
+ id: client.id,
+ code: args.code,
+ firstName: args.firstName,
+ lastName: args.lastNames,
+ bossFk: args.bossFk,
+ fi: args.fi,
+ birth: args.birth,
+
+ }, myOptions);
if (tx) await tx.commit();
} catch (error) {
diff --git a/modules/worker/back/models/operator.json b/modules/worker/back/models/operator.json
index 2417078e1..6da3945fc 100644
--- a/modules/worker/back/models/operator.json
+++ b/modules/worker/back/models/operator.json
@@ -33,6 +33,16 @@
"type": "belongsTo",
"model": "Sector",
"foreignKey": "sectorFk"
+ },
+ "train": {
+ "type": "belongsTo",
+ "model": "Train",
+ "foreignKey": "trainFk"
+ },
+ "printer": {
+ "type": "belongsTo",
+ "model": "Printer",
+ "foreignKey": "labelerFk"
}
}
}
diff --git a/modules/worker/back/models/worker.json b/modules/worker/back/models/worker.json
index ea4aecf67..1a777fffe 100644
--- a/modules/worker/back/models/worker.json
+++ b/modules/worker/back/models/worker.json
@@ -48,6 +48,12 @@
"locker": {
"type" : "number"
},
+ "fi": {
+ "type" : "string"
+ },
+ "birth": {
+ "type" : "date"
+ },
"isF11Allowed": {
"type" : "boolean"
}
diff --git a/modules/worker/front/time-control/index.js b/modules/worker/front/time-control/index.js
index 9137d7839..0a955c586 100644
--- a/modules/worker/front/time-control/index.js
+++ b/modules/worker/front/time-control/index.js
@@ -111,9 +111,10 @@ class Controller extends Section {
dayIndex.setDate(dayIndex.getDate() + 1);
}
- if (!this.weekTotalHours) this.fetchHours();
- if (this.worker)
+ if (this.worker) {
+ this.fetchHours();
this.getWeekData();
+ }
}
set weekTotalHours(totalHours) {
@@ -403,7 +404,7 @@ class Controller extends Section {
});
}
- changeState(state, reason) {
+ state(state, reason) {
this.state = state;
this.reason = reason;
this.repaint();
diff --git a/package-lock.json b/package-lock.json
index c3f88bc2c..b66279ae3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "salix-back",
- "version": "23.42.01",
+ "version": "23.48.01",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "salix-back",
- "version": "23.42.01",
+ "version": "23.48.01",
"license": "GPL-3.0",
"dependencies": {
"axios": "^1.2.2",
diff --git a/package.json b/package.json
index 3320705f5..04fcb008b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "salix-back",
- "version": "23.42.01",
+ "version": "23.48.01",
"author": "Verdnatura Levante SL",
"description": "Salix backend",
"license": "GPL-3.0",
diff --git a/print/templates/reports/invoice/invoice.js b/print/templates/reports/invoice/invoice.js
index 1c9965d3b..b26472b08 100755
--- a/print/templates/reports/invoice/invoice.js
+++ b/print/templates/reports/invoice/invoice.js
@@ -7,6 +7,7 @@ module.exports = {
mixins: [vnReport],
async serverPrefetch() {
this.invoice = await this.findOneFromDef('invoice', [this.reference]);
+
this.checkMainEntity(this.invoice);
this.client = await this.findOneFromDef('client', [this.reference]);
this.taxes = await this.rawSqlFromDef(`taxes`, [this.reference]);
|