8144-devToTest_2444 #3137
|
@ -632,14 +632,21 @@ INSERT INTO vn.invoiceOutConfig
|
|||
SET id = 1,
|
||||
parallelism = 8;
|
||||
|
||||
INSERT INTO `vn`.`invoiceOutSerial` (`code`, `description`, `isTaxed`, `taxAreaFk`, `isCEE`, `type`)
|
||||
INSERT INTO `vn`.`invoiceOutSerial`
|
||||
(`code`,`description`, `isTaxed`, `taxAreaFk`, `isCEE`, `type`)
|
||||
VALUES
|
||||
('A', 'Global nacional', 1, 'NATIONAL', 0, 'global'),
|
||||
('T', 'Española rapida', 1, 'NATIONAL', 0, 'quick'),
|
||||
('V', 'Intracomunitaria global', 0, 'CEE', 1, 'global'),
|
||||
('M', 'Múltiple nacional', 1, 'NATIONAL', 0, 'multiple'),
|
||||
('R', 'Rectificativa', 1, 'NATIONAL', 0, NULL),
|
||||
('E', 'Exportación rápida', 0, 'WORLD', 0, 'quick');
|
||||
('A', 'Global nacional', 1, 'NATIONAL', 0, 'global'),
|
||||
('T', 'Española rapida', 1, 'NATIONAL', 0, 'quick'),
|
||||
('V', 'Intracomunitaria global', 0, 'CEE', 1, 'global'),
|
||||
('M', 'Múltiple nacional', 1, 'NATIONAL', 0, 'multiple'),
|
||||
('R', 'Rectificativa', 1, 'NATIONAL', 0, NULL),
|
||||
('E', 'Exportación rápida', 0, 'WORLD', 0, 'quick'),
|
||||
('H', 'Intracomunitaria rápida', 0, 'CEE', 1, 'quick'),
|
||||
('P', 'Factura simplificada', 1, 'NATIONAL', 0, NULL),
|
||||
('PE', 'COOPERATIE FLORAHOLLAND UA', 0, 'CEE', 1, NULL),
|
||||
('S', 'Simplificada', 1, 'NATIONAL', 0, NULL),
|
||||
('X', 'Exportación global', 0, 'WORLD', 0, 'global'),
|
||||
('N', 'Múltiple Intracomunitaria', 0, 'CEE', 1, 'multiple');
|
||||
|
||||
INSERT INTO `vn`.`invoiceOut`(`id`, `serial`, `amount`, `issued`,`clientFk`, `created`, `companyFk`, `dued`, `booked`, `bankFk`, `hasPdf`)
|
||||
VALUES
|
||||
|
@ -2911,7 +2918,8 @@ INSERT INTO `util`.`notification` (`id`, `name`, `description`)
|
|||
(6, 'book-entry-deleted', 'accounting entries deleted'),
|
||||
(7, 'zone-included','An email to notify zoneCollisions'),
|
||||
(8, 'backup-printer-selected','A backup printer has been selected'),
|
||||
(9, 'mrw-deadline','The MRW deadline has passed');
|
||||
(9, 'mrw-deadline','The MRW deadline has passed'),
|
||||
(10,'invoice-ticket-closure','Tickets not invoiced during the nightly closure ticket process');
|
||||
|
||||
TRUNCATE `util`.`notificationAcl`;
|
||||
INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`)
|
||||
|
|
|
@ -350,6 +350,7 @@
|
|||
"Cmr file does not exist": "El archivo del cmr no existe",
|
||||
"You are not allowed to modify the alias": "No estás autorizado a modificar el alias",
|
||||
"The address of the customer must have information about Incoterms and Customs Agent": "El consignatario del cliente debe tener informado Incoterms y Agente de aduanas",
|
||||
"No invoice series found for these parameters": "No se encontró una serie para estos parámetros",
|
||||
"The line could not be marked": "La linea no puede ser marcada",
|
||||
"Through this procedure, it is not possible to modify the password of users with verified email": "Mediante este procedimiento, no es posible modificar la contraseña de usuarios con correo verificado",
|
||||
"They're not your subordinate": "No es tu subordinado/a.",
|
||||
|
|
|
@ -350,6 +350,7 @@
|
|||
"Cmr file does not exist": "Le fichier cmr n'existe pas",
|
||||
"You are not allowed to modify the alias": "Vous n'êtes pas autorisé à modifier l'alias",
|
||||
"The address of the customer must have information about Incoterms and Customs Agent": "L'adresse du client doit contenir des informations sur les Incoterms et l'agent des douanes",
|
||||
"No invoice series found for these parameters": "Aucune série de facture trouvée pour ces paramètres",
|
||||
"The line could not be marked": "La ligne ne peut pas être marquée",
|
||||
"This password can only be changed by the user themselves": "Ce mot de passe ne peut être modifié que par l'utilisateur lui-même",
|
||||
"They're not your subordinate": "Ce n'est pas votre subordonné.",
|
||||
|
|
|
@ -350,6 +350,7 @@
|
|||
"Cmr file does not exist": "O arquivo CMR não existe",
|
||||
"You are not allowed to modify the alias": "Você não tem permissão para modificar o alias",
|
||||
"The address of the customer must have information about Incoterms and Customs Agent": "O endereço do cliente deve ter informações sobre Incoterms e Agente Aduaneiro",
|
||||
"No invoice series found for these parameters": "Nenhuma série de fatura encontrada para esses parâmetros",
|
||||
"The line could not be marked": "A linha não pôde ser marcada",
|
||||
"This password can only be changed by the user themselves": "Esta senha só pode ser alterada pelo próprio usuário",
|
||||
"They're not your subordinate": "Eles não são seus subordinados.",
|
||||
|
|
|
@ -37,7 +37,7 @@ module.exports = Self => {
|
|||
const tickets = await models.Ticket.find({
|
||||
where: {refFk: invoiceOut.ref}
|
||||
}, myOptions);
|
||||
|
||||
|
||||
const [bookEntry] = await models.Xdiario.find({
|
||||
where: {
|
||||
SERIE: invoiceOut.ref[0],
|
||||
|
@ -55,13 +55,13 @@ module.exports = Self => {
|
|||
if (bookEntry) {
|
||||
if (bookEntry.enlazadoSage) {
|
||||
const params = {
|
||||
bookEntry: bookEntry.ASIEN,
|
||||
bookEntry: bookEntry.ASIEN,
|
||||
invoiceOutRef: invoiceOut.ref
|
||||
}
|
||||
};
|
||||
await Self.rawSql(`SELECT util.notification_send('book-entry-deleted', ?, NULL)`,
|
||||
[JSON.stringify(params)],
|
||||
myOptions);
|
||||
};
|
||||
}
|
||||
|
||||
await models.Xdiario.destroyAll({
|
||||
ASIEN: bookEntry.ASIEN
|
||||
|
|
|
@ -79,6 +79,8 @@ module.exports = Self => {
|
|||
type
|
||||
],
|
||||
myOptions);
|
||||
if (!serial)
|
||||
throw new UserError('No invoice series found for these parameters');
|
||||
|
||||
const invoiceOutSerial = await Self.app.models.InvoiceOutSerial.findById(serial);
|
||||
if (invoiceOutSerial?.taxAreaFk == 'WORLD') {
|
||||
|
|
|
@ -31,6 +31,6 @@ module.exports = Self => {
|
|||
|
||||
const usesMana = departments.find(department => department.id == workerDepartment.departmentFk);
|
||||
|
||||
return usesMana ? true : false;
|
||||
return !!usesMana;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
@ -58,12 +63,12 @@ module.exports = Self => {
|
|||
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(
|
||||
|
@ -133,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
|
||||
|
@ -150,7 +155,10 @@ module.exports = Self => {
|
|||
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'
|
||||
|
|
|
@ -19,9 +19,14 @@ module.exports = async function(ctx, Self, tickets, options) {
|
|||
const failedtickets = [];
|
||||
for (const ticket of tickets) {
|
||||
try {
|
||||
await Self.rawSql(`CALL util.debugAdd('invoicingTicket', ?)`, [ticket.id], {userId});
|
||||
await Self.rawSql(`CALL util.debugAdd('invoicingTicket', ?)`, [ticket.id], myOptions);
|
||||
|
||||
await Self.app.models.InvoiceOut.getSerial(ticket.clientFk, ticket.companyFk, ticket.addressFk, 'quick');
|
||||
await Self.app.models.InvoiceOut.getSerial(
|
||||
ticket.clientFk,
|
||||
ticket.companyFk,
|
||||
ticket.addressFk,
|
||||
'quick',
|
||||
myOptions);
|
||||
await Self.rawSql(
|
||||
`CALL vn.ticket_closeByTicket(?)`,
|
||||
[ticket.id],
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue