{
+ Self.remoteMethod('getWithPackaging', {
+ description: 'Returns the list of suppliers with an entry of type packaging',
+ accessType: 'READ',
+ returns: {
+ type: 'object',
+ root: true
+ },
+ http: {
+ path: `/getWithPackaging`,
+ verb: 'GET'
+ },
+ nolimit: true
+ });
+ Self.getWithPackaging = async options => {
+ const models = Self.app.models;
+ const myOptions = {};
+ const oneYearAgo = new Date();
+ oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ const entries = await models.Entry.find({
+ where: {
+ typeFk: 'packaging',
+ created: {gte: oneYearAgo}
+ },
+ include: {
+ relation: 'supplier',
+ scope: {
+ fields: ['id', 'name']
+ }
+ },
+ fields: {supplierFk: true}
+ }, myOptions);
+
+ const result = entries.map(item => ({
+ id: item.supplier().id,
+ name: item.supplier().name
+ }));
+ return Array.from(new Map(result.map(entry => [entry.id, entry])).values());
+ };
+};
+
diff --git a/modules/supplier/back/methods/supplier/specs/getWithPackaging.spec.js b/modules/supplier/back/methods/supplier/specs/getWithPackaging.spec.js
new file mode 100644
index 000000000..bd30d7437
--- /dev/null
+++ b/modules/supplier/back/methods/supplier/specs/getWithPackaging.spec.js
@@ -0,0 +1,33 @@
+const {models} = require('vn-loopback/server/server');
+
+describe('Supplier getWithPackaging()', () => {
+ it('should return a list of suppliers with an entry of type packaging', async() => {
+ const typeFk = 'packaging';
+
+ const tx = await models.Supplier.beginTransaction({});
+ const myOptions = {transaction: tx};
+
+ try {
+ const entry = await models.Entry.findOne(
+ {
+ where: {
+ id: 1
+ },
+ myOptions
+ });
+
+ await entry.updateAttributes({
+ typeFk: typeFk,
+ created: new Date()
+ });
+
+ const result = await models.Supplier.getWithPackaging(myOptions);
+
+ expect(result.length).toEqual(1);
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+});
diff --git a/modules/supplier/back/models/supplier.js b/modules/supplier/back/models/supplier.js
index 2d3ffef3e..7e6908d57 100644
--- a/modules/supplier/back/models/supplier.js
+++ b/modules/supplier/back/models/supplier.js
@@ -12,6 +12,7 @@ module.exports = Self => {
require('../methods/supplier/campaignMetricsEmail')(Self);
require('../methods/supplier/newSupplier')(Self);
require('../methods/supplier/getItemsPackaging')(Self);
+ require('../methods/supplier/getWithPackaging')(Self);
Self.validatesPresenceOf('name', {
message: 'The social name cannot be empty'
diff --git a/modules/ticket/back/methods/expedition-state/filter.js b/modules/ticket/back/methods/expedition-state/filter.js
index 1483780f7..3a4e7a87c 100644
--- a/modules/ticket/back/methods/expedition-state/filter.js
+++ b/modules/ticket/back/methods/expedition-state/filter.js
@@ -29,7 +29,7 @@ module.exports = Self => {
Object.assign(myOptions, options);
const stmt = new ParameterizedSQL(
- `SELECT es.created, u.name, u.id workerFk, est.description state
+ `SELECT es.created, u.name, u.id workerFk, est.description state, es.isScanned
FROM vn.expeditionState es
JOIN vn.expeditionStateType est ON est.id = es.typeFk
JOIN account.user u ON u.id = es.userFk
diff --git a/modules/ticket/back/methods/sale/usesMana.js b/modules/ticket/back/methods/sale/usesMana.js
index 31beb3a4c..b4768d80a 100644
--- a/modules/ticket/back/methods/sale/usesMana.js
+++ b/modules/ticket/back/methods/sale/usesMana.js
@@ -31,6 +31,6 @@ module.exports = Self => {
const usesMana = departments.find(department => department.id == workerDepartment.departmentFk);
- return usesMana ? true : false;
+ return !!usesMana;
};
};
diff --git a/modules/ticket/back/methods/ticket-request/filter.js b/modules/ticket/back/methods/ticket-request/filter.js
index 5364cef9a..53f90b98f 100644
--- a/modules/ticket/back/methods/ticket-request/filter.js
+++ b/modules/ticket/back/methods/ticket-request/filter.js
@@ -59,6 +59,11 @@ module.exports = Self => {
arg: 'state',
type: 'string',
description: `Search request by request state`
+ },
+ {
+ arg: 'myTeam',
+ type: 'boolean',
+ description: `Team partners`
}
],
returns: {
@@ -75,6 +80,8 @@ module.exports = Self => {
const conn = Self.dataSource.connector;
const userId = ctx.req.accessToken.userId;
const myOptions = {};
+ const models = Self.app.models;
+ const args = ctx.args;
if (typeof options == 'object')
Object.assign(myOptions, options);
@@ -82,6 +89,21 @@ module.exports = Self => {
if (ctx.args.mine)
ctx.args.attenderFk = userId;
+ const teamMembersId = [];
+ if (args.myTeam != null) {
+ const worker = await models.Worker.findById(userId, {
+ include: {
+ relation: 'collegues'
+ }
+ }, myOptions);
+ const collegues = worker.collegues() || [];
+ for (let collegue of collegues)
+ teamMembersId.push(collegue.collegueFk);
+
+ if (teamMembersId.length == 0)
+ teamMembersId.push(userId);
+ }
+
let where = buildFilter(ctx.args, (param, value) => {
switch (param) {
case 'search':
@@ -113,6 +135,11 @@ module.exports = Self => {
return {'w.id': value};
case 'salesPersonFk':
return {'c.salesPersonFk': value};
+ case 'myTeam':
+ if (value)
+ return {'tr.requesterFk': {inq: teamMembersId}};
+ else
+ return {'tr.requesterFk': {nin: teamMembersId}};
}
});
diff --git a/modules/ticket/back/methods/ticket/closeAll.js b/modules/ticket/back/methods/ticket/closeAll.js
index 143c0a3f0..71122808c 100644
--- a/modules/ticket/back/methods/ticket/closeAll.js
+++ b/modules/ticket/back/methods/ticket/closeAll.js
@@ -1,11 +1,17 @@
-const UserError = require('vn-loopback/util/user-error');
const closure = require('./closure');
module.exports = Self => {
Self.remoteMethodCtx('closeAll', {
description: 'Makes the closure process from all warehouses',
accessType: 'WRITE',
- accepts: [],
+ accepts: [
+ {
+ arg: 'options',
+ type: 'object',
+ http: {source: 'body'},
+ description: 'Optional parameters, including transaction.'
+ }
+ ],
returns: {
type: 'object',
root: true
@@ -16,21 +22,20 @@ module.exports = Self => {
}
});
- Self.closeAll = async ctx => {
+ Self.closeAll = async(ctx, options) => {
+ const userId = ctx.req.accessToken.userId;
+ const myOptions = {userId};
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ let tx;
+ // IMPORTANT: Due to its high cost in production, wrapping this process in a transaction may cause timeouts.
+
const toDate = Date.vnNew();
toDate.setHours(0, 0, 0, 0);
toDate.setDate(toDate.getDate() - 1);
- const todayMinDate = Date.vnNew();
- todayMinDate.setHours(0, 0, 0, 0);
-
- const todayMaxDate = Date.vnNew();
- todayMaxDate.setHours(23, 59, 59, 59);
-
- // Prevent closure for current day
- if (toDate >= todayMinDate && toDate <= todayMaxDate)
- throw new UserError('You cannot close tickets for today');
-
const tickets = await Self.rawSql(`
SELECT t.id,
t.clientFk,
@@ -41,7 +46,7 @@ module.exports = Self => {
c.salesPersonFk,
c.isToBeMailed,
c.hasToInvoice,
- co.hasDailyInvoice,
+ c.hasDailyInvoice,
eu.email salesPersonEmail,
t.addressFk
FROM ticket t
@@ -53,16 +58,17 @@ module.exports = Self => {
JOIN province p ON p.id = c.provinceFk
JOIN country co ON co.id = p.countryFk
LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk
+ JOIN ticketConfig tc ON TRUE
WHERE (al.code = 'PACKED' OR (am.code = 'refund' AND al.code <> 'delivered'))
- AND DATE(t.shipped) BETWEEN ? - INTERVAL 2 DAY AND util.dayEnd(?)
+ AND t.shipped BETWEEN ? - INTERVAL tc.closureDaysAgo DAY AND util.dayEnd(?)
AND t.refFk IS NULL
GROUP BY t.id
- `, [toDate, toDate]);
+ `, [toDate, toDate], myOptions);
const ticketIds = tickets.map(ticket => ticket.id);
await Self.rawSql(`
INSERT INTO util.debug (variable, value)
VALUES ('nightInvoicing', ?)
- `, [ticketIds.join(',')]);
+ `, [ticketIds.join(',')], myOptions);
await Self.rawSql(`
WITH ticketNotInvoiceable AS(
@@ -108,6 +114,7 @@ module.exports = Self => {
JOIN alertLevel al ON al.id = ts.alertLevel
JOIN client c ON c.id = t.clientFk
JOIN province p ON p.id = c.provinceFk
+ JOIN ticketConfig tc ON TRUE
LEFT JOIN autonomy a ON a.id = p.autonomyFk
JOIN country co ON co.id = p.countryFk
LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk
@@ -116,9 +123,9 @@ module.exports = Self => {
LEFT JOIN vn.invoiceOutSerial ios ON ios.taxAreaFk = 'WORLD'
AND ios.code = invoiceSerial(t.clientFk, t.companyFk, 'multiple')
WHERE (al.code = 'PACKED' OR (am.code = 'refund' AND al.code <> 'delivered'))
- AND DATE(t.shipped) BETWEEN ? - INTERVAL 2 DAY AND util.dayEnd(?)
+ AND t.shipped BETWEEN ? - INTERVAL tc.closureDaysAgo DAY AND util.dayEnd(?)
AND t.refFk IS NULL
- AND IFNULL(a.hasDailyInvoice, co.hasDailyInvoice)
+ AND c.hasDailyInvoice
GROUP BY ticketFk
HAVING hasErrorToInvoice
OR hasErrorTaxDataChecked
@@ -131,9 +138,9 @@ module.exports = Self => {
) SELECT IF(errors = '{"tickets": null}',
'No errors',
util.notification_send('invoice-ticket-closure', errors, NULL))
- FROM ticketNotInvoiceable`, [toDate, toDate]);
+ FROM ticketNotInvoiceable`, [toDate, toDate], myOptions);
- await closure(ctx, Self, tickets);
+ await closure(ctx, Self, tickets, myOptions);
await Self.rawSql(`
UPDATE ticket t
@@ -141,13 +148,17 @@ module.exports = Self => {
JOIN alertLevel al ON al.id = ts.alertLevel
JOIN agencyMode am ON am.id = t.agencyModeFk
JOIN deliveryMethod dm ON dm.id = am.deliveryMethodFk
+ JOIN ticketConfig tc ON TRUE
LEFT JOIN ticketObservation tob ON tob.ticketFk = t.id
SET t.routeFk = NULL
- WHERE DATE(t.shipped) BETWEEN ? - INTERVAL 2 DAY AND util.dayEnd(?)
+ WHERE t.shipped BETWEEN ? - INTERVAL tc.closureDaysAgo DAY AND util.dayEnd(?)
AND al.code NOT IN ('DELIVERED', 'PACKED')
AND NOT t.packages
AND tob.id IS NULL
- AND t.routeFk`, [toDate, toDate], {userId: ctx.req.accessToken.userId});
+ AND t.routeFk`, [toDate, toDate], myOptions);
+
+ if (tx)
+ await tx.commit();
return {
message: 'Success'
diff --git a/modules/ticket/back/methods/ticket/closeByTicket.js b/modules/ticket/back/methods/ticket/closeByTicket.js
index 40fe048a5..8a21267b6 100644
--- a/modules/ticket/back/methods/ticket/closeByTicket.js
+++ b/modules/ticket/back/methods/ticket/closeByTicket.js
@@ -50,7 +50,7 @@ module.exports = Self => {
c.salesPersonFk,
c.isToBeMailed,
c.hasToInvoice,
- co.hasDailyInvoice,
+ c.hasDailyInvoice,
eu.email salesPersonEmail,
t.addressFk
FROM expedition e
@@ -58,8 +58,6 @@ module.exports = Self => {
JOIN ticketState ts ON ts.ticketFk = t.id
JOIN alertLevel al ON al.id = ts.alertLevel
JOIN client c ON c.id = t.clientFk
- JOIN province p ON p.id = c.provinceFk
- JOIN country co ON co.id = p.countryFk
LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk
WHERE al.code = 'PACKED'
AND t.id = ?
diff --git a/modules/ticket/back/methods/ticket/closure.js b/modules/ticket/back/methods/ticket/closure.js
index a75596bac..e4cb49007 100644
--- a/modules/ticket/back/methods/ticket/closure.js
+++ b/modules/ticket/back/methods/ticket/closure.js
@@ -12,17 +12,21 @@ module.exports = async function(ctx, Self, tickets, options) {
Object.assign(myOptions, options);
let tx;
- if (!myOptions.transaction) {
- tx = await Self.beginTransaction({});
- myOptions.transaction = tx;
- }
+ // IMPORTANT: Due to its high cost in production, wrapping this process in a transaction may cause timeouts.
if (tickets.length == 0) return;
const failedtickets = [];
for (const ticket of tickets) {
try {
- await Self.app.models.InvoiceOut.getSerial(ticket.clientFk, ticket.companyFk, ticket.addressFk, 'quick');
+ await Self.rawSql(`CALL util.debugAdd('invoicingTicket', ?)`, [ticket.id], myOptions);
+
+ await Self.app.models.InvoiceOut.getSerial(
+ ticket.clientFk,
+ ticket.companyFk,
+ ticket.addressFk,
+ 'quick',
+ myOptions);
await Self.rawSql(
`CALL vn.ticket_closeByTicket(?)`,
[ticket.id],
@@ -149,6 +153,11 @@ module.exports = async function(ctx, Self, tickets, options) {
myOptions);
}
} catch (error) {
+ await Self.rawSql(`
+ INSERT INTO util.debug (variable, value)
+ VALUES ('invoicingTicketError', ?)
+ `, [ticket.id + ' - ' + error]);
+
if (error.responseCode == 450) {
await invalidEmail(ticket);
continue;
diff --git a/modules/ticket/back/methods/ticket/filter.js b/modules/ticket/back/methods/ticket/filter.js
index 06781c3c8..d3e53c702 100644
--- a/modules/ticket/back/methods/ticket/filter.js
+++ b/modules/ticket/back/methods/ticket/filter.js
@@ -228,52 +228,57 @@ module.exports = Self => {
stmt = new ParameterizedSQL(`
CREATE OR REPLACE TEMPORARY TABLE tmp.filter
(INDEX (id))
- ENGINE = MEMORY
+ ENGINE = InnoDB
SELECT t.id,
- t.shipped,
- CAST(DATE(t.shipped) AS CHAR) shippedDate,
- HOUR(t.shipped) shippedHour,
- t.nickname,
- t.refFk,
- t.routeFk,
- t.warehouseFk,
- t.clientFk,
- t.totalWithoutVat,
- t.totalWithVat,
- io.id invoiceOutId,
- a.provinceFk,
- p.name province,
- w.name warehouse,
- am.name agencyMode,
- am.id agencyModeFk,
- st.name state,
- st.classColor,
- wk.lastName salesPerson,
- ts.stateFk stateFk,
- ts.alertLevel alertLevel,
- ts.code alertLevelCode,
- u.name userName,
- c.salesPersonFk,
- z.hour zoneLanding,
- HOUR(z.hour) zoneHour,
- MINUTE(z.hour) zoneMinute,
- z.name zoneName,
- z.id zoneFk,
- CAST(z.hour AS CHAR) hour,
- a.nickname addressNickname
- FROM ticket t
- LEFT JOIN invoiceOut io ON t.refFk = io.ref
- LEFT JOIN zone z ON z.id = t.zoneFk
- LEFT JOIN address a ON a.id = t.addressFk
- LEFT JOIN province p ON p.id = a.provinceFk
- LEFT JOIN warehouse w ON w.id = t.warehouseFk
- LEFT JOIN agencyMode am ON am.id = t.agencyModeFk
- LEFT JOIN ticketState ts ON ts.ticketFk = t.id
- LEFT JOIN state st ON st.id = ts.stateFk
- LEFT JOIN client c ON c.id = t.clientFk
- LEFT JOIN worker wk ON wk.id = c.salesPersonFk
- LEFT JOIN account.user u ON u.id = wk.id
- LEFT JOIN route r ON r.id = t.routeFk
+ t.shipped,
+ CAST(DATE(t.shipped) AS CHAR) shippedDate,
+ HOUR(t.shipped) shippedHour,
+ t.nickname,
+ t.refFk,
+ t.routeFk,
+ t.warehouseFk,
+ t.clientFk,
+ t.totalWithoutVat,
+ t.totalWithVat,
+ io.id invoiceOutId,
+ a.provinceFk,
+ p.name province,
+ w.name warehouse,
+ am.name agencyMode,
+ am.id agencyModeFk,
+ st.name state,
+ st.classColor,
+ wk.lastName salesPerson,
+ ts.stateFk stateFk,
+ ts.alertLevel alertLevel,
+ ts.code alertLevelCode,
+ u.name userName,
+ c.salesPersonFk,
+ z.hour zoneLanding,
+ HOUR(z.hour) zoneHour,
+ MINUTE(z.hour) zoneMinute,
+ z.name zoneName,
+ z.id zoneFk,
+ CAST(z.hour AS CHAR) hour,
+ a.nickname addressNickname,
+ (SELECT GROUP_CONCAT(DISTINCT i2.itemPackingTypeFk ORDER BY i2.itemPackingTypeFk SEPARATOR ',')
+ FROM sale s2
+ JOIN item i2 ON i2.id = s2.itemFk
+ WHERE s2.ticketFk = t.id
+ ) AS packing
+ FROM ticket t
+ LEFT JOIN invoiceOut io ON t.refFk = io.ref
+ LEFT JOIN zone z ON z.id = t.zoneFk
+ LEFT JOIN address a ON a.id = t.addressFk
+ LEFT JOIN province p ON p.id = a.provinceFk
+ LEFT JOIN warehouse w ON w.id = t.warehouseFk
+ LEFT JOIN agencyMode am ON am.id = t.agencyModeFk
+ LEFT JOIN ticketState ts ON ts.ticketFk = t.id
+ LEFT JOIN state st ON st.id = ts.stateFk
+ LEFT JOIN client c ON c.id = t.clientFk
+ LEFT JOIN worker wk ON wk.id = c.salesPersonFk
+ LEFT JOIN account.user u ON u.id = wk.id
+ LEFT JOIN route r ON r.id = t.routeFk
`);
if (args.orderFk) {
@@ -292,6 +297,7 @@ module.exports = Self => {
}
stmt.merge(conn.makeWhere(filter.where));
+
stmts.push(stmt);
stmt = new ParameterizedSQL(`
diff --git a/modules/ticket/back/methods/ticket/getTicketsAdvance.js b/modules/ticket/back/methods/ticket/getTicketsAdvance.js
index 1bd5f83de..41f3ee79a 100644
--- a/modules/ticket/back/methods/ticket/getTicketsAdvance.js
+++ b/modules/ticket/back/methods/ticket/getTicketsAdvance.js
@@ -50,6 +50,11 @@ module.exports = Self => {
type: 'boolean',
description: 'True when lines and stock of origin are equal'
},
+ {
+ arg: 'departmentFk',
+ type: 'number',
+ description: 'Department identifier'
+ },
{
arg: 'filter',
type: 'object',
@@ -96,6 +101,8 @@ module.exports = Self => {
};
case 'isFullMovable':
return {'f.isFullMovable': value};
+ case 'departmentFk':
+ return {'f.departmentFk': value};
}
});
diff --git a/modules/ticket/back/methods/ticket/setDeleted.js b/modules/ticket/back/methods/ticket/setDeleted.js
index a684e1cbc..fcab95ee2 100644
--- a/modules/ticket/back/methods/ticket/setDeleted.js
+++ b/modules/ticket/back/methods/ticket/setDeleted.js
@@ -49,9 +49,12 @@ module.exports = Self => {
where: {originalTicketFk: id}
}, myOptions);
+ const hasRefund = !!ticketRefunds?.length;
+
const allDeleted = ticketRefunds.every(refund => refund.refundTicket().isDeleted);
- if (ticketRefunds?.length && !allDeleted) {
+ if (!hasRefund) await models.TicketRefund.destroyAll({refundTicketFk: id}, myOptions);
+ if (hasRefund && !allDeleted) {
const notDeleted = [];
for (const refund of ticketRefunds)
if (!refund.refundTicket().isDeleted) notDeleted.push(refund.refundTicket().id);
diff --git a/modules/ticket/back/methods/ticket/specs/closeAll.spec.js b/modules/ticket/back/methods/ticket/specs/closeAll.spec.js
new file mode 100644
index 000000000..f01541eec
--- /dev/null
+++ b/modules/ticket/back/methods/ticket/specs/closeAll.spec.js
@@ -0,0 +1,54 @@
+const models = require('vn-loopback/server/server').models;
+const LoopBackContext = require('loopback-context');
+
+describe('Ticket Closure - closeAll function', () => {
+ let ctx = {
+ req: {
+ getLocale: () => 'es',
+ accessToken: {userId: 1106},
+ headers: {origin: 'http://localhost'},
+ __: value => value,
+ },
+ args: {}
+ };
+ let options;
+ let tx;
+ let originalVnNew;
+
+ beforeEach(async() => {
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({active: ctx.req});
+
+ tx = await models.Ticket.beginTransaction({});
+ options = {transaction: tx};
+ originalVnNew = Date.vnNew;
+ spyOn(Date, 'vnNew').and.callFake(() => {
+ const mockDate = originalVnNew();
+ mockDate.setDate(mockDate.getDate() + 1);
+ return mockDate;
+ });
+ });
+
+ afterEach(async() => {
+ if (tx)
+ await tx.rollback();
+ });
+
+ it('should set routeFk to NULL when conditions are met', async() => {
+ const ticketsBefore = await models.Ticket.find({
+ where: {
+ routeFk: {neq: null}
+ }
+ }, options);
+
+ await models.Ticket.closeAll(ctx, options);
+
+ const ticketsAfter = await models.Ticket.find({
+ where: {
+ id: {inq: ticketsBefore.map(ticket => ticket.id)},
+ routeFk: {neq: null}
+ }
+ }, options);
+
+ expect(ticketsBefore.length).toBeGreaterThan(ticketsAfter.length);
+ });
+});
diff --git a/modules/ticket/back/methods/ticket/specs/closure.spec.js b/modules/ticket/back/methods/ticket/specs/closure.spec.js
index 303c38233..cafe178cb 100644
--- a/modules/ticket/back/methods/ticket/specs/closure.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/closure.spec.js
@@ -50,9 +50,9 @@ describe('Ticket closure functionality', () => {
expect(ticketStateBefore.code).not.toBe(ticketStateAfter.code);
- const ticketAfter = await models.TicketState.findById(ticketId, null, options);
+ const ticketAfter = await models.Ticket.findById(ticketId, null, options);
- expect(ticketAfter.refFk).toBeUndefined();
+ expect(ticketAfter.refFk).toBeNull();
});
it('should send Incoterms authorization email on first order', async() => {
diff --git a/modules/ticket/back/methods/ticket/specs/getTicketsAdvance.spec.js b/modules/ticket/back/methods/ticket/specs/getTicketsAdvance.spec.js
index 488cd1fc2..a941013cd 100644
--- a/modules/ticket/back/methods/ticket/specs/getTicketsAdvance.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/getTicketsAdvance.spec.js
@@ -6,6 +6,9 @@ describe('TicketFuture getTicketsAdvance()', () => {
today.setHours(0, 0, 0, 0);
let tomorrow = Date.vnNew();
tomorrow.setDate(today.getDate() + 1);
+ const salesDeptId = 43;
+ const spain1DeptId = 95;
+ beforeAll.mockLoopBackContext();
it('should return the tickets passing the required data', async() => {
const tx = await models.Ticket.beginTransaction({});
@@ -129,4 +132,39 @@ describe('TicketFuture getTicketsAdvance()', () => {
throw e;
}
});
+
+ it('should return the tickets matching the right department', async() => {
+ const tx = await models.Ticket.beginTransaction({});
+
+ try {
+ const options = {transaction: tx};
+ ctx.args = {
+ dateFuture: tomorrow,
+ dateToAdvance: today,
+ warehouseFk: 1,
+ };
+
+ await models.Ticket.updateAll({id: {inq: [12, 31]}}, {clientFk: 1}, options);
+ const client = await models.Client.findById(1, null, options);
+ await client.updateAttribute('salesPersonFk', 1, options);
+ const business = await models.Business.findById(1, null, options);
+ await business.updateAttributes({departmentFk: spain1DeptId}, options);
+
+ const saleTickets = await models.Ticket.getTicketsAdvance(ctx, options);
+ const filteredSaleTickets = await models.Ticket.getTicketsAdvance(
+ {args: {...ctx.args, departmentFk: spain1DeptId}},
+ options);
+
+ expect(saleTickets.length).toBeGreaterThan(filteredSaleTickets.length);
+ expect(saleTickets.some(ticket => ticket.departmentFk === salesDeptId)).toBeTrue();
+ expect(saleTickets.some(ticket => ticket.departmentFk === spain1DeptId)).toBeTrue();
+
+ expect(filteredSaleTickets.some(ticket => ticket.departmentFk === salesDeptId)).toBeFalse();
+ expect(filteredSaleTickets.some(ticket => ticket.departmentFk === spain1DeptId)).toBeTrue();
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
});
diff --git a/modules/ticket/back/methods/ticket/specs/setDeleted.spec.js b/modules/ticket/back/methods/ticket/specs/setDeleted.spec.js
index 782c31c02..b70c94cee 100644
--- a/modules/ticket/back/methods/ticket/specs/setDeleted.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/setDeleted.spec.js
@@ -113,5 +113,27 @@ describe('ticket setDeleted()', () => {
expect(error.message).not.toContain('Tickets with associated refunds');
});
+
+ it('should delete the refund - original ticket relation', async() => {
+ const tx = await models.Ticket.beginTransaction({});
+ try {
+ const options = {transaction: tx};
+
+ const ticketId = 24;
+ const refundTicket = await models.TicketRefund.findOne({where: {refundTicketFk: ticketId}}, options);
+
+ expect(refundTicket).toBeTruthy();
+
+ await models.Ticket.setDeleted(ctx, ticketId, options);
+ const removedRefundTicket = await models.TicketRefund.findOne({
+ where: {refundTicketFk: ticketId}},
+ options);
+
+ expect(removedRefundTicket).toBeNull();
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ }
+ });
});
});
diff --git a/modules/ticket/back/models/expedition.json b/modules/ticket/back/models/expedition.json
index 2dcca1e87..f3f912ec3 100644
--- a/modules/ticket/back/models/expedition.json
+++ b/modules/ticket/back/models/expedition.json
@@ -1,63 +1,66 @@
{
- "name": "Expedition",
- "base": "VnModel",
- "mixins": {
- "Loggable": true
+ "name": "Expedition",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
+ "options": {
+ "mysql": {
+ "table": "expedition"
+ }
+ },
+ "properties": {
+ "id": {
+ "id": true,
+ "type": "number",
+ "description": "Identifier"
},
- "options": {
- "mysql": {
- "table": "expedition"
- }
+ "freightItemFk": {
+ "type": "number"
},
- "properties": {
- "id": {
- "id": true,
- "type": "number",
- "description": "Identifier"
- },
- "freightItemFk": {
- "type": "number"
- },
- "created": {
- "type": "date"
- },
- "counter": {
- "type": "number"
- },
- "externalId": {
- "type": "string"
- }
+ "created": {
+ "type": "date"
},
- "relations": {
- "ticket": {
- "type": "belongsTo",
- "model": "Ticket",
- "foreignKey": "ticketFk"
- },
- "agencyMode": {
- "type": "belongsTo",
- "model": "AgencyMode",
- "foreignKey": "agencyModeFk"
- },
- "worker": {
- "type": "belongsTo",
- "model": "Worker",
- "foreignKey": "workerFk"
- },
- "packages": {
- "type": "hasMany",
- "model": "TicketPackaging",
- "foreignKey": "ticketFk"
- },
- "freightItem": {
- "type": "belongsTo",
- "model": "Item",
- "foreignKey": "freightItemFk"
- },
- "packaging": {
- "type": "belongsTo",
- "model": "Package",
- "foreignKey": "packagingFk"
- }
+ "counter": {
+ "type": "number"
+ },
+ "externalId": {
+ "type": "string"
+ },
+ "stateTypeFk": {
+ "type": "number"
+ }
+ },
+ "relations": {
+ "ticket": {
+ "type": "belongsTo",
+ "model": "Ticket",
+ "foreignKey": "ticketFk"
+ },
+ "agencyMode": {
+ "type": "belongsTo",
+ "model": "AgencyMode",
+ "foreignKey": "agencyModeFk"
+ },
+ "worker": {
+ "type": "belongsTo",
+ "model": "Worker",
+ "foreignKey": "workerFk"
+ },
+ "packages": {
+ "type": "hasMany",
+ "model": "TicketPackaging",
+ "foreignKey": "ticketFk"
+ },
+ "freightItem": {
+ "type": "belongsTo",
+ "model": "Item",
+ "foreignKey": "freightItemFk"
+ },
+ "packaging": {
+ "type": "belongsTo",
+ "model": "Package",
+ "foreignKey": "packagingFk"
}
}
+}
\ No newline at end of file
diff --git a/modules/worker/back/methods/device/handle-user.js b/modules/worker/back/methods/device/handle-user.js
index 55302c1cb..abafffd55 100644
--- a/modules/worker/back/methods/device/handle-user.js
+++ b/modules/worker/back/methods/device/handle-user.js
@@ -87,7 +87,7 @@ module.exports = Self => {
{
relation: 'sector',
scope: {
- fields: ['warehouseFk', 'description'],
+ fields: ['warehouseFk', 'description', 'isOnReservationMode'],
}
}, {
relation: 'printer',
diff --git a/modules/worker/back/methods/worker/new.js b/modules/worker/back/methods/worker/new.js
index bb43fba99..7da8a8da2 100644
--- a/modules/worker/back/methods/worker/new.js
+++ b/modules/worker/back/methods/worker/new.js
@@ -217,6 +217,7 @@ module.exports = Self => {
const code = e.code;
const message = e.sqlMessage;
+ if (e.message && e.message.includes('Invalid email')) throw new UserError('Invalid email');
if (e.message && e.message.includes(`Email already exists`)) throw new UserError(`This personal mail already exists`);
if (code === 'ER_DUP_ENTRY' && message.includes(`CodigoTrabajador_UNIQUE`)) throw new UserError(`This worker code already exists`);
if (code === 'ER_DUP_ENTRY' && message.includes(`PRIMARY`)) throw new UserError(`This worker already exists`);
diff --git a/modules/worker/back/models/operator.json b/modules/worker/back/models/operator.json
index d4832bccf..b75bf6732 100644
--- a/modules/worker/back/models/operator.json
+++ b/modules/worker/back/models/operator.json
@@ -24,13 +24,28 @@
"warehouseFk": {
"type": "number"
},
+ "sectorFk": {
+ "type": "number"
+ },
"labelerFk": {
"type": "number"
},
"isOnReservationMode": {
"type": "boolean",
"required": true
- }
+ },
+ "machineFk": {
+ "type": "number"
+ },
+ "linesLimit": {
+ "type": "number"
+ },
+ "volumeLimit": {
+ "type": "number"
+ },
+ "sizeLimit": {
+ "type": "number"
+ }
},
"relations": {
"sector": {
@@ -53,6 +68,11 @@
"model": "ItemPackingType",
"foreignKey": "itemPackingTypeFk",
"primaryKey": "code"
- }
+ },
+ "machine": {
+ "type": "belongsTo",
+ "model": "Machine",
+ "foreignKey": "machineFk"
+ }
}
-}
\ No newline at end of file
+}
diff --git a/modules/worker/back/models/worker.json b/modules/worker/back/models/worker.json
index b896e775b..c334c0d05 100644
--- a/modules/worker/back/models/worker.json
+++ b/modules/worker/back/models/worker.json
@@ -253,7 +253,18 @@
"relation": "client"
},
{
- "relation": "sip"
+ "relation": "sip",
+ "scope": {
+ "include": {
+ "relation": "queueMember",
+ "scope": {
+ "fields": [
+ "queue",
+ "extension"
+ ]
+ }
+ }
+ }
}
]
},
diff --git a/modules/zone/back/methods/zone/deleteZone.js b/modules/zone/back/methods/zone/deleteZone.js
index a75302703..ffe23c9b9 100644
--- a/modules/zone/back/methods/zone/deleteZone.js
+++ b/modules/zone/back/methods/zone/deleteZone.js
@@ -1,3 +1,4 @@
+const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('deleteZone', {
description: 'Delete a zone',
@@ -49,26 +50,12 @@ module.exports = Self => {
}
}
};
- const promises = [];
const ticketList = await models.Ticket.find(filter, myOptions);
- const fixingState = await models.State.findOne({where: {code: 'FIXING'}}, myOptions);
- const worker = await models.Worker.findOne({
- where: {id: userId}
- }, myOptions);
- await models.Ticket.rawSql('UPDATE ticket SET zoneFk = NULL WHERE zoneFk = ?', [id], myOptions);
+ if (ticketList.length > 0)
+ throw new UserError('There are tickets for this area, delete them first');
- for (ticket of ticketList) {
- if (ticket.ticketState().alertLevel == 0) {
- promises.push(models.Ticket.state(ctx, {
- ticketFk: ticket.id,
- stateFk: fixingState.id,
- userFk: worker.id
- }, myOptions));
- }
- }
- await Promise.all(promises);
await models.Zone.destroyById(id, myOptions);
if (tx) await tx.commit();
diff --git a/modules/zone/back/methods/zone/specs/deleteZone.spec.js b/modules/zone/back/methods/zone/specs/deleteZone.spec.js
index 08dafd181..aef7fd290 100644
--- a/modules/zone/back/methods/zone/specs/deleteZone.spec.js
+++ b/modules/zone/back/methods/zone/specs/deleteZone.spec.js
@@ -8,9 +8,9 @@ describe('zone deletezone()', () => {
__: value => value
};
const ctx = {req: activeCtx};
- const zoneId = 9;
+ const zoneId = 4;
+ const zoneId2 = 3;
let ticketIDs;
- let originalTicketStates;
beforeAll(async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
@@ -35,18 +35,8 @@ describe('zone deletezone()', () => {
await models.Zone.deleteZone(ctx, zoneId, options);
const updatedZone = await models.Zone.findById(zoneId, null, options);
- const anUpdatedTicket = await models.Ticket.findById(ticketIDs[0], null, options);
-
- const updatedTicketStates = await models.TicketState.find({
- where: {
- ticketFk: {inq: ticketIDs},
- code: 'FIXING'
- }
- }, options);
expect(updatedZone).toBeNull();
- expect(anUpdatedTicket.zoneFk).toBeNull();
- expect(updatedTicketStates.length).toBeGreaterThan(originalTicketStates.length);
await tx.rollback();
} catch (e) {
@@ -54,4 +44,20 @@ describe('zone deletezone()', () => {
throw e;
}
});
+
+ it('should not delete the zone if it has tickets', async() => {
+ const tx = await models.Zone.beginTransaction({});
+
+ let error;
+ try {
+ const options = {transaction: tx};
+ await models.Zone.deleteZone(ctx, zoneId2, options);
+ await tx.rollback();
+ } catch (e) {
+ error = e.message;
+ await tx.rollback();
+ }
+
+ expect(error).toEqual('There are tickets for this area, delete them first');
+ });
});
diff --git a/package.json b/package.json
index 32c1f21d8..767ec231e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "salix-back",
- "version": "24.42.0",
+ "version": "24.44.0",
"author": "Verdnatura Levante SL",
"description": "Salix backend",
"license": "GPL-3.0",
diff --git a/print/templates/reports/delivery-note/delivery-note.html b/print/templates/reports/delivery-note/delivery-note.html
index c1701e084..89bc07488 100644
--- a/print/templates/reports/delivery-note/delivery-note.html
+++ b/print/templates/reports/delivery-note/delivery-note.html
@@ -58,6 +58,7 @@
{{$t('reference')}} |
{{$t('quantity')}} |
{{$t('concept')}} |
+ {{$t('producer')}} |
{{$t('price')}} |
{{$t('discount')}} |
{{$t('vat')}} |
@@ -69,6 +70,7 @@
{{sale.itemFk}} |
{{sale.quantity}} |
{{sale.concept}} |
+ {{sale.subName}} |
{{sale.price | currency('EUR', $i18n.locale)}} |
{{(sale.discount / 100) | percentage}} |
{{sale.vatType}} |
@@ -81,7 +83,6 @@
{{sale.tag5}} {{sale.value5}}
{{sale.tag6}} {{sale.value6}}
{{sale.tag7}} {{sale.value7}}
- {{$t('producer')}} {{ sale.subName }}