diff --git a/.vscode/settings.json b/.vscode/settings.json
index 899dfc7884..03479d27ac 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -3,7 +3,7 @@
// Carácter predeterminado de final de línea.
"files.eol": "\n",
"editor.codeActionsOnSave": {
- "source.fixAll.eslint": true
+ "source.fixAll.eslint": "explicit"
},
"search.useIgnoreFiles": false,
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
@@ -11,8 +11,12 @@
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
+ "[json]": {
+ "editor.defaultFormatter": "vscode.json-language-features"
+ },
"cSpell.words": [
"salix",
- "fdescribe"
+ "fdescribe",
+ "Loggable"
]
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d8d040fd67..69e93a309b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,23 +5,40 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## [2350.01] - 2023-12-14
+## [2404.01] - 2024-01-25
### Added
### Changed
### Fixed
+## [2402.01] - 2024-01-11
+
+### Added
+### Changed
+### Fixed
+
+## [2400.01] - 2024-01-04
+
+### Added
+### Changed
+### Fixed
+
+## [2350.01] - 2023-12-14
+
+### Características Añadidas 🆕
+- **Tickets → Expediciones:** Añadido soporte para Viaexpress
+
+
## [2348.01] - 2023-11-30
-### Added
-- (Ticket -> Adelantar) Permite mover lineas sin generar negativos
-- (Ticket -> Adelantar) Permite modificar la fecha de los tickets
-- (Trabajadores -> Notificaciones) Nueva sección (lilium)
+### Características Añadidas 🆕
+- **Tickets → Adelantar:** Permite mover lineas sin generar negativos
+- **Tickets → Adelantar:** Permite modificar la fecha de los tickets
+- **Trabajadores → Notificaciones:** Nueva sección (lilium)
-### Changed
-### Fixed
-- (Ticket -> RocketChat) Arreglada detección de cambios
+### Correcciones 🛠️
+- **Tickets → RocketChat:** Arreglada detección de cambios
## [2346.01] - 2023-11-16
diff --git a/back/methods/dms/removeFile.js b/back/methods/dms/removeFile.js
index a9ff368834..dc55b4d385 100644
--- a/back/methods/dms/removeFile.js
+++ b/back/methods/dms/removeFile.js
@@ -22,8 +22,8 @@ module.exports = Self => {
Self.removeFile = async(ctx, id, options) => {
const models = Self.app.models;
- let tx;
const myOptions = {};
+ let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
diff --git a/back/methods/docuware/specs/upload.spec.js b/back/methods/docuware/specs/upload.spec.js
index 3b8c55a504..866499b665 100644
--- a/back/methods/docuware/specs/upload.spec.js
+++ b/back/methods/docuware/specs/upload.spec.js
@@ -24,15 +24,40 @@ describe('docuware upload()', () => {
});
it('should try upload file', async() => {
+ const tx = await models.Docuware.beginTransaction({});
spyOn(ticketModel, 'deliveryNotePdf').and.returnValue(new Promise(resolve => resolve({})));
let error;
try {
- await models.Docuware.upload(ctx, ticketIds, fileCabinetName);
+ const options = {transaction: tx};
+ const user = await models.UserConfig.findById(userId, null, options);
+ await user.updateAttribute('tabletFk', 'Tablet1', options);
+ await models.Docuware.upload(ctx, ticketIds, fileCabinetName, options);
+
+ await tx.rollback();
} catch (e) {
- error = e.message;
+ error = e;
+ await tx.rollback();
}
- expect(error).toEqual('Action not allowed on the test environment');
+ expect(error.message).toEqual('Action not allowed on the test environment');
+ });
+
+ it('should throw error when not have tablet assigned', async() => {
+ const tx = await models.Docuware.beginTransaction({});
+ spyOn(ticketModel, 'deliveryNotePdf').and.returnValue(new Promise(resolve => resolve({})));
+
+ let error;
+ try {
+ const options = {transaction: tx};
+ await models.Docuware.upload(ctx, ticketIds, fileCabinetName, options);
+
+ await tx.rollback();
+ } catch (e) {
+ error = e;
+ await tx.rollback();
+ }
+
+ expect(error.message).toEqual('This user does not have an assigned tablet');
});
});
diff --git a/back/methods/docuware/upload.js b/back/methods/docuware/upload.js
index 7055bf8d5c..27be72295e 100644
--- a/back/methods/docuware/upload.js
+++ b/back/methods/docuware/upload.js
@@ -29,12 +29,24 @@ module.exports = Self => {
}
});
- Self.upload = async function(ctx, ticketIds, fileCabinet) {
+ Self.upload = async function(ctx, ticketIds, fileCabinet, options) {
delete ctx.args.ticketIds;
const models = Self.app.models;
const action = 'store';
- const options = await Self.getOptions();
+ const myOptions = {};
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ const userConfig = await models.UserConfig.findById(ctx.req.accessToken.userId, {
+ fields: ['tabletFk']
+ }, myOptions);
+
+ if (!userConfig?.tabletFk)
+ throw new UserError('This user does not have an assigned tablet');
+
+ const docuwareOptions = await Self.getOptions();
const fileCabinetId = await Self.getFileCabinet(fileCabinet);
const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId);
@@ -45,7 +57,7 @@ module.exports = Self => {
const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, {
id,
type: 'deliveryNote'
- });
+ }, myOptions);
// get ticket data
const ticket = await models.Ticket.findById(id, {
include: [{
@@ -54,7 +66,7 @@ module.exports = Self => {
fields: ['id', 'name', 'fi']
}
}]
- });
+ }, myOptions);
// upload file
const templateJson = {
@@ -102,7 +114,7 @@ module.exports = Self => {
{
'FieldName': 'FILTRO_TABLET',
'ItemElementName': 'string',
- 'Item': 'Tablet1',
+ 'Item': userConfig.tabletFk,
}
]
};
@@ -116,11 +128,11 @@ module.exports = Self => {
const deleteJson = {
'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}]
};
- const deleteUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/Fields`;
- await axios.put(deleteUri, deleteJson, options.headers);
+ const deleteUri = `${docuwareOptions.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/Fields`;
+ await axios.put(deleteUri, deleteJson, docuwareOptions.headers);
}
- const uploadUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents?StoreDialogId=${dialogId}`;
+ const uploadUri = `${docuwareOptions.url}/FileCabinets/${fileCabinetId}/Documents?StoreDialogId=${dialogId}`;
const FormData = require('form-data');
const data = new FormData();
@@ -130,7 +142,7 @@ module.exports = Self => {
headers: {
'Content-Type': 'multipart/form-data',
'X-File-ModifiedDate': Date.vnNew(),
- 'Cookie': options.headers.headers.Cookie,
+ 'Cookie': docuwareOptions.headers.headers.Cookie,
...data.getHeaders()
},
};
@@ -141,11 +153,11 @@ module.exports = Self => {
const $t = ctx.req.__;
const message = $t('Failed to upload delivery note', {id});
if (uploaded.length)
- await models.TicketTracking.setDelivered(ctx, uploaded);
+ await models.TicketTracking.setDelivered(ctx, uploaded, myOptions);
throw new UserError(message);
}
uploaded.push(id);
}
- return models.TicketTracking.setDelivered(ctx, ticketIds);
+ return models.TicketTracking.setDelivered(ctx, ticketIds, myOptions);
};
};
diff --git a/back/methods/vn-user/privileges.js b/back/methods/vn-user/privileges.js
index 08cfaaae88..9f936c29ba 100644
--- a/back/methods/vn-user/privileges.js
+++ b/back/methods/vn-user/privileges.js
@@ -68,7 +68,7 @@ module.exports = Self => {
userToUpdate.hasGrant = hasGrant;
if (roleFk) {
- const role = await models.Role.findById(roleFk, {fields: ['name']}, myOptions);
+ const role = await models.VnRole.findById(roleFk, {fields: ['name']}, myOptions);
const hasRole = await Self.hasRole(userId, role.name, myOptions);
if (!hasRole)
diff --git a/back/methods/vn-user/renew-token.js b/back/methods/vn-user/renew-token.js
index 9850267d6b..d00085d8a9 100644
--- a/back/methods/vn-user/renew-token.js
+++ b/back/methods/vn-user/renew-token.js
@@ -1,4 +1,4 @@
-const UserError = require('vn-loopback/util/user-error');
+const {models} = require('vn-loopback/server/server');
module.exports = Self => {
Self.remoteMethodCtx('renewToken', {
@@ -16,20 +16,31 @@ module.exports = Self => {
});
Self.renewToken = async function(ctx) {
- const models = Self.app.models;
- const token = ctx.req.accessToken;
+ const {accessToken: token} = ctx.req;
- const now = new Date();
+ // Check if current token is valid
+
+ const {renewPeriod, courtesyTime} = await models.AccessTokenConfig.findOne({
+ fields: ['renewPeriod', 'courtesyTime']
+ });
+ const now = Date.now();
const differenceMilliseconds = now - token.created;
const differenceSeconds = Math.floor(differenceMilliseconds / 1000);
+ const isNotExceeded = differenceSeconds < renewPeriod - courtesyTime;
+ if (isNotExceeded)
+ return token;
- const fields = ['renewPeriod', 'courtesyTime'];
- const accessTokenConfig = await models.AccessTokenConfig.findOne({fields});
+ // Schedule to remove current token
+ setTimeout(async() => {
+ try {
+ await Self.logout(token.id);
+ } catch (err) {
+ // eslint-disable-next-line no-console
+ console.error(err);
+ }
+ }, courtesyTime * 1000);
- if (differenceSeconds < accessTokenConfig.renewPeriod - accessTokenConfig.courtesyTime)
- throw new UserError(`The renew period has not been exceeded`, 'periodNotExceeded');
-
- await Self.logout(token.id);
+ // Create new accessToken
const user = await Self.findById(token.userId);
const accessToken = await user.createAccessToken();
diff --git a/back/methods/vn-user/sign-in.js b/back/methods/vn-user/sign-in.js
index 9c2d568f4f..782046641e 100644
--- a/back/methods/vn-user/sign-in.js
+++ b/back/methods/vn-user/sign-in.js
@@ -49,13 +49,7 @@ module.exports = Self => {
if (vnUser.twoFactor)
throw new ForbiddenError(null, 'REQUIRES_2FA');
}
- const validateLogin = await Self.validateLogin(user, password);
- await Self.app.models.SignInLog.create({
- token: validateLogin.token,
- userFk: vnUser.id,
- ip: ctx.req.ip
- });
- return validateLogin;
+ return Self.validateLogin(user, password, ctx);
};
Self.passExpired = async vnUser => {
diff --git a/back/methods/vn-user/specs/privileges.spec.js b/back/methods/vn-user/specs/privileges.spec.js
index 3d25eecf97..04d9c09ff3 100644
--- a/back/methods/vn-user/specs/privileges.spec.js
+++ b/back/methods/vn-user/specs/privileges.spec.js
@@ -70,7 +70,7 @@ describe('VnUser privileges()', () => {
const tx = await models.VnUser.beginTransaction({});
const options = {transaction: tx};
- const agency = await models.Role.findOne({
+ const agency = await models.VnRole.findOne({
where: {
name: 'agency'
}
diff --git a/back/methods/vn-user/specs/renew-token.spec.js b/back/methods/vn-user/specs/renew-token.spec.js
new file mode 100644
index 0000000000..8d9bbf11ce
--- /dev/null
+++ b/back/methods/vn-user/specs/renew-token.spec.js
@@ -0,0 +1,50 @@
+const {models} = require('vn-loopback/server/server');
+describe('Renew Token', () => {
+ const startingTime = Date.now();
+ let ctx = null;
+ beforeAll(async() => {
+ const unAuthCtx = {
+ req: {
+ headers: {},
+ connection: {
+ remoteAddress: '127.0.0.1'
+ },
+ getLocale: () => 'en'
+ },
+ args: {}
+ };
+ let login = await models.VnUser.signIn(unAuthCtx, 'salesAssistant', 'nightmare');
+ let accessToken = await models.AccessToken.findById(login.token);
+ ctx = {req: {accessToken: accessToken}};
+ });
+
+ beforeEach(() => {
+ jasmine.clock().install();
+ jasmine.clock().mockDate(new Date(startingTime));
+ });
+
+ afterEach(() => {
+ jasmine.clock().uninstall();
+ });
+
+ it('should renew token', async() => {
+ const mockDate = new Date(startingTime + 26600000);
+ jasmine.clock().mockDate(mockDate);
+ const {id} = await models.VnUser.renewToken(ctx);
+
+ expect(id).not.toEqual(ctx.req.accessToken.id);
+ });
+
+ it('NOT should renew', async() => {
+ let error;
+ let response;
+ try {
+ response = await models.VnUser.renewToken(ctx);
+ } catch (e) {
+ error = e;
+ }
+
+ expect(error).toBeUndefined();
+ expect(response.id).toEqual(ctx.req.accessToken.id);
+ });
+});
diff --git a/back/methods/vn-user/specs/sign-in.spec.js b/back/methods/vn-user/specs/sign-in.spec.js
index ac2dfe2b23..a14dd301ef 100644
--- a/back/methods/vn-user/specs/sign-in.spec.js
+++ b/back/methods/vn-user/specs/sign-in.spec.js
@@ -2,7 +2,7 @@ const {models} = require('vn-loopback/server/server');
describe('VnUser Sign-in()', () => {
const employeeId = 1;
- const unauthCtx = {
+ const unAuthCtx = {
req: {
headers: {},
connection: {
@@ -15,20 +15,18 @@ describe('VnUser Sign-in()', () => {
const {VnUser, AccessToken, SignInLog} = models;
describe('when credentials are correct', () => {
it('should return the token if user uses email', async() => {
- let login = await VnUser.signIn(unauthCtx, 'salesAssistant@mydomain.com', 'nightmare');
+ let login = await VnUser.signIn(unAuthCtx, 'salesAssistant@mydomain.com', 'nightmare');
let accessToken = await AccessToken.findById(login.token);
let ctx = {req: {accessToken: accessToken}};
let signInLog = await SignInLog.find({where: {token: accessToken.id}});
- expect(signInLog.length).toEqual(1);
- expect(signInLog[0].userFk).toEqual(accessToken.userId);
- expect(login.token).toBeDefined();
+ expect(signInLog.length).toEqual(0);
await VnUser.logout(ctx.req.accessToken.id);
});
it('should return the token', async() => {
- let login = await VnUser.signIn(unauthCtx, 'salesAssistant', 'nightmare');
+ let login = await VnUser.signIn(unAuthCtx, 'salesAssistant', 'nightmare');
let accessToken = await AccessToken.findById(login.token);
let ctx = {req: {accessToken: accessToken}};
@@ -38,7 +36,7 @@ describe('VnUser Sign-in()', () => {
});
it('should return the token if the user doesnt exist but the client does', async() => {
- let login = await VnUser.signIn(unauthCtx, 'PetterParker', 'nightmare');
+ let login = await VnUser.signIn(unAuthCtx, 'PetterParker', 'nightmare');
let accessToken = await AccessToken.findById(login.token);
let ctx = {req: {accessToken: accessToken}};
@@ -53,7 +51,7 @@ describe('VnUser Sign-in()', () => {
let error;
try {
- await VnUser.signIn(unauthCtx, 'IDontExist', 'TotallyWrongPassword');
+ await VnUser.signIn(unAuthCtx, 'IDontExist', 'TotallyWrongPassword');
} catch (e) {
error = e;
}
@@ -74,7 +72,7 @@ describe('VnUser Sign-in()', () => {
const options = {transaction: tx};
await employee.updateAttribute('twoFactor', 'email', options);
- await VnUser.signIn(unauthCtx, 'employee', 'nightmare', options);
+ await VnUser.signIn(unAuthCtx, 'employee', 'nightmare', options);
await tx.rollback();
} catch (e) {
await tx.rollback();
@@ -99,7 +97,7 @@ describe('VnUser Sign-in()', () => {
const options = {transaction: tx};
await employee.updateAttribute('passExpired', yesterday, options);
- await VnUser.signIn(unauthCtx, 'employee', 'nightmare', options);
+ await VnUser.signIn(unAuthCtx, 'employee', 'nightmare', options);
await tx.rollback();
} catch (e) {
await tx.rollback();
diff --git a/back/methods/vn-user/validate-token.js b/back/methods/vn-user/validate-token.js
deleted file mode 100644
index 7bccfe0b1d..0000000000
--- a/back/methods/vn-user/validate-token.js
+++ /dev/null
@@ -1,17 +0,0 @@
-module.exports = Self => {
- Self.remoteMethod('validateToken', {
- description: 'Validates the current logged user token',
- returns: {
- type: 'Boolean',
- root: true
- },
- http: {
- path: `/validateToken`,
- verb: 'GET'
- }
- });
-
- Self.validateToken = async function() {
- return true;
- };
-};
diff --git a/back/model-config.json b/back/model-config.json
index ebc0e321b0..27a94498cf 100644
--- a/back/model-config.json
+++ b/back/model-config.json
@@ -139,9 +139,6 @@
"Warehouse": {
"dataSource": "vn"
},
- "VnUser": {
- "dataSource": "vn"
- },
"OsTicket": {
"dataSource": "osticket"
},
@@ -156,6 +153,12 @@
},
"ViaexpressConfig": {
"dataSource": "vn"
+ },
+ "VnUser": {
+ "dataSource": "vn"
+ },
+ "VnRole": {
+ "dataSource": "vn"
}
}
diff --git a/back/models/dms-type.json b/back/models/dms-type.json
index de3d564b43..d3e96a986d 100644
--- a/back/models/dms-type.json
+++ b/back/models/dms-type.json
@@ -17,10 +17,6 @@
"type": "string",
"required": true
},
- "path": {
- "type": "string",
- "required": true
- },
"code": {
"type": "string",
"required": true
@@ -29,12 +25,12 @@
"relations": {
"readRole": {
"type": "belongsTo",
- "model": "Role",
+ "model": "VnRole",
"foreignKey": "readRoleFk"
},
"writeRole": {
"type": "belongsTo",
- "model": "Role",
+ "model": "VnRole",
"foreignKey": "writeRoleFk"
}
},
diff --git a/back/models/docuwareTablet.json b/back/models/docuwareTablet.json
new file mode 100644
index 0000000000..dbbf62f56e
--- /dev/null
+++ b/back/models/docuwareTablet.json
@@ -0,0 +1,17 @@
+{
+ "name": "docuwareTablet",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "docuwareTablet"
+ }
+ },
+ "properties": {
+ "tablet": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ }
+ }
+}
diff --git a/back/models/image-collection.json b/back/models/image-collection.json
index 186ab02084..ae0e0adcdd 100644
--- a/back/models/image-collection.json
+++ b/back/models/image-collection.json
@@ -46,12 +46,12 @@
},
"readRole": {
"type": "belongsTo",
- "model": "Role",
+ "model": "VnRole",
"foreignKey": "readRoleFk"
},
"writeRole": {
"type": "belongsTo",
- "model": "Role",
+ "model": "VnRole",
"foreignKey": "writeRoleFk"
}
},
@@ -64,4 +64,3 @@
}
]
}
-
\ No newline at end of file
diff --git a/back/models/notificationAcl.json b/back/models/notificationAcl.json
index a201879610..9ab85530f7 100644
--- a/back/models/notificationAcl.json
+++ b/back/models/notificationAcl.json
@@ -24,8 +24,8 @@
},
"role": {
"type": "belongsTo",
- "model": "Role",
+ "model": "VnRole",
"foreignKey": "roleFk"
}
}
-}
\ No newline at end of file
+}
diff --git a/back/models/specs/mailAliasAccount.spec.js b/back/models/specs/mailAliasAccount.spec.js
new file mode 100644
index 0000000000..c13cc7ae84
--- /dev/null
+++ b/back/models/specs/mailAliasAccount.spec.js
@@ -0,0 +1,73 @@
+const models = require('vn-loopback/server/server').models;
+
+describe('loopback model MailAliasAccount', () => {
+ it('should fail to add a mail Alias if the worker doesnt have ACLs', async() => {
+ const tx = await models.MailAliasAccount.beginTransaction({});
+ let error;
+
+ try {
+ const options = {transaction: tx, accessToken: {userId: 57}};
+ await models.MailAliasAccount.create({mailAlias: 2, account: 5}, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toEqual('The alias cant be modified');
+ });
+
+ it('should add a mail Alias', async() => {
+ const tx = await models.MailAliasAccount.beginTransaction({});
+ let error;
+
+ try {
+ const options = {transaction: tx, accessToken: {userId: 9}};
+ await models.MailAliasAccount.create({mailAlias: 2, account: 5}, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error).toBeUndefined();
+ });
+
+ it('should add a mail Alias of an inherit role', async() => {
+ const tx = await models.MailAliasAccount.beginTransaction({});
+ let error;
+
+ try {
+ const options = {transaction: tx, accessToken: {userId: 9}};
+ await models.MailAliasAccount.create({mailAlias: 3, account: 5}, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error).toBeUndefined();
+ });
+
+ it('should delete a mail Alias', async() => {
+ const tx = await models.MailAliasAccount.beginTransaction({});
+ let error;
+
+ try {
+ const options = {transaction: tx, accessToken: {userId: 1}};
+ const mailAclId = 2;
+ await models.MailAliasAccount.destroyAll({id: mailAclId}, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error).toBeUndefined();
+ });
+});
+
diff --git a/back/models/user-config.json b/back/models/user-config.json
index 52125dc012..5c5df1b9ef 100644
--- a/back/models/user-config.json
+++ b/back/models/user-config.json
@@ -26,6 +26,9 @@
},
"darkMode": {
"type": "boolean"
+ },
+ "tabletFk": {
+ "type": "string"
}
},
"relations": {
@@ -43,6 +46,11 @@
"type": "belongsTo",
"model": "VnUser",
"foreignKey": "userFk"
- }
+ },
+ "Tablet": {
+ "type": "belongsTo",
+ "model": "docuwareTablet",
+ "foreignKey": "tabletFk"
+ }
}
}
diff --git a/back/models/vn-role.json b/back/models/vn-role.json
new file mode 100644
index 0000000000..c7d7e172b8
--- /dev/null
+++ b/back/models/vn-role.json
@@ -0,0 +1,13 @@
+{
+ "name": "VnRole",
+ "base": "Role",
+ "validateUpsert": true,
+ "options": {
+ "mysql": {
+ "table": "account.role"
+ }
+ },
+ "mixins": {
+ "Loggable": true
+ }
+}
diff --git a/back/models/vn-user.js b/back/models/vn-user.js
index 719e96cbf7..39e7008caf 100644
--- a/back/models/vn-user.js
+++ b/back/models/vn-user.js
@@ -10,7 +10,6 @@ module.exports = function(Self) {
require('../methods/vn-user/sign-in')(Self);
require('../methods/vn-user/acl')(Self);
require('../methods/vn-user/recover-password')(Self);
- require('../methods/vn-user/validate-token')(Self);
require('../methods/vn-user/privileges')(Self);
require('../methods/vn-user/validate-auth')(Self);
require('../methods/vn-user/renew-token')(Self);
@@ -124,20 +123,43 @@ module.exports = function(Self) {
return email.send();
});
- Self.signInValidate = (user, userToken) => {
+
+ /**
+ * Sign-in validate
+ * @param {String} user The user
+ * @param {Object} userToken Options
+ * @param {Object} token accessToken
+ * @param {Object} ctx context
+ */
+ Self.signInValidate = async(user, userToken, token, ctx) => {
const [[key, value]] = Object.entries(Self.userUses(user));
- if (userToken[key].toLowerCase().trim() !== value.toLowerCase().trim()) {
- console.error('ERROR!!! - Signin with other user', userToken, user);
- throw new UserError('Try again');
+ const isOwner = Self.rawSql(`SELECT ? = ? `, [userToken[key], value]);
+ if (!isOwner) {
+ await Self.app.models.SignInLog.create({
+ userName: user,
+ token: token.id,
+ userFk: userToken.id,
+ ip: ctx.req.ip,
+ owner: isOwner
+ });
+ throw new UserError('Try again');
}
};
- Self.validateLogin = async function(user, password) {
+ /**
+ * Validate login params
+ * @param {String} user The user
+ * @param {String} password
+ * @param {Object} ctx context
+ */
+ Self.validateLogin = async function(user, password, ctx) {
const loginInfo = Object.assign({password}, Self.userUses(user));
const token = await Self.login(loginInfo, 'user');
const userToken = await token.user.get();
- Self.signInValidate(user, userToken);
+
+ if (ctx)
+ await Self.signInValidate(user, userToken, token, ctx);
try {
await Self.app.models.Account.sync(userToken.name, password);
@@ -187,8 +209,8 @@ module.exports = function(Self) {
};
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls =
- Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls
- .filter(acl => acl.property != 'changePassword');
+ Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls
+ .filter(acl => acl.property != 'changePassword');
Self.userSecurity = async(ctx, userId, options) => {
const models = Self.app.models;
@@ -226,26 +248,30 @@ module.exports = function(Self) {
const env = process.env.NODE_ENV;
const liliumUrl = await Self.app.models.Url.findOne({
- where: {and: [
- {appName: 'lilium'},
- {environment: env}
- ]}
+ where: {
+ and: [
+ {appName: 'lilium'},
+ {environment: env}
+ ]
+ }
});
class Mailer {
async send(verifyOptions, cb) {
- const url = new URL(verifyOptions.verifyHref);
- if (process.env.NODE_ENV) url.port = '';
+ try {
+ const url = new URL(verifyOptions.verifyHref);
+ if (process.env.NODE_ENV) url.port = '';
- const params = {
- url: url.href,
- recipient: verifyOptions.to
- };
+ const email = new Email('email-verify', {
+ url: url.href,
+ recipient: verifyOptions.to
+ });
+ await email.send();
- const email = new Email('email-verify', params);
- email.send();
-
- cb(null, verifyOptions.to);
+ cb(null, verifyOptions.to);
+ } catch (err) {
+ cb(err);
+ }
}
}
diff --git a/back/models/vn-user.json b/back/models/vn-user.json
index 0f6daff5ac..6396036433 100644
--- a/back/models/vn-user.json
+++ b/back/models/vn-user.json
@@ -7,6 +7,9 @@
"table": "account.user"
}
},
+ "mixins": {
+ "Loggable": true
+ },
"resetPasswordTokenTTL": "604800",
"properties": {
"id": {
@@ -63,7 +66,7 @@
"relations": {
"role": {
"type": "belongsTo",
- "model": "Role",
+ "model": "VnRole",
"foreignKey": "roleFk"
},
"roles": {
@@ -95,34 +98,30 @@
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
- },
- {
- "property": "recoverPassword",
- "accessType": "EXECUTE",
- "principalType": "ROLE",
- "principalId": "$everyone",
- "permission": "ALLOW"
- },
- {
- "property": "validateToken",
- "accessType": "EXECUTE",
- "principalType": "ROLE",
- "principalId": "$authenticated",
- "permission": "ALLOW"
- },
- {
- "property": "validateAuth",
+ }, {
+ "property": "recoverPassword",
"accessType": "EXECUTE",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
- },
- {
+ }, {
+ "property": "validateAuth",
+ "accessType": "EXECUTE",
+ "principalType": "ROLE",
+ "principalId": "$everyone",
+ "permission": "ALLOW"
+ }, {
"property": "privileges",
"accessType": "*",
"principalType": "ROLE",
"principalId": "$authenticated",
"permission": "ALLOW"
+ }, {
+ "property": "renewToken",
+ "accessType": "WRITE",
+ "principalType": "ROLE",
+ "principalId": "$authenticated",
+ "permission": "ALLOW"
}
],
"scopes": {
diff --git a/back/tests.js b/back/tests.js
index 97e548d334..2678f6744b 100644
--- a/back/tests.js
+++ b/back/tests.js
@@ -7,6 +7,10 @@ process.on('warning', warning => {
console.log(warning.stack);
});
+process.on('SIGUSR2', async() => {
+ if (container) await container.rm();
+});
+
process.on('exit', async function() {
if (container) await container.rm();
});
@@ -55,8 +59,8 @@ async function test() {
const JunitReporter = require('jasmine-reporters');
jasmine.addReporter(new JunitReporter.JUnitXmlReporter());
- jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000;
jasmine.exitOnCompletion = true;
+ jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 900000;
}
const backSpecs = [
diff --git a/db/Dockerfile b/db/Dockerfile
index 8eeed35e59..0020e89502 100644
--- a/db/Dockerfile
+++ b/db/Dockerfile
@@ -1,4 +1,4 @@
-FROM mariadb:10.7.7
+FROM mariadb:10.11.6
ENV MYSQL_ROOT_PASSWORD root
ENV TZ Europe/Madrid
diff --git a/db/changes/234601/00-updateCourtesyTime.sql b/db/changes/234601/00-updateCourtesyTime.sql
new file mode 100644
index 0000000000..4751b2e032
--- /dev/null
+++ b/db/changes/234601/00-updateCourtesyTime.sql
@@ -0,0 +1,4 @@
+-- Auto-generated SQL script #202311061003
+UPDATE salix.accessTokenConfig
+ SET courtesyTime=60
+ WHERE id=1;
diff --git a/db/changes/234604/00-createSignInLogTable.sql b/db/changes/234802/00-createSignInLogTable.sql
similarity index 91%
rename from db/changes/234604/00-createSignInLogTable.sql
rename to db/changes/234802/00-createSignInLogTable.sql
index 5253481357..942f651c97 100644
--- a/db/changes/234604/00-createSignInLogTable.sql
+++ b/db/changes/234802/00-createSignInLogTable.sql
@@ -1,5 +1,4 @@
-
--
-- Table structure for table `signInLog`
-- Description: log to debug cross-login error
@@ -13,7 +12,9 @@ CREATE TABLE `account`.`signInLog` (
`token` varchar(255) NOT NULL ,
`userFk` int(10) unsigned DEFAULT NULL,
`creationDate` timestamp NULL DEFAULT current_timestamp(),
+ `userName` varchar(30) NOT NULL,
`ip` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
+ `owner` tinyint(1) DEFAULT 1,
KEY `userFk` (`userFk`),
CONSTRAINT `signInLog_ibfk_1` FOREIGN KEY (`userFk`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
);
diff --git a/db/changes/235001/00-updateACL_Role_VnRole.sql b/db/changes/235001/00-updateACL_Role_VnRole.sql
new file mode 100644
index 0000000000..b08a44138e
--- /dev/null
+++ b/db/changes/235001/00-updateACL_Role_VnRole.sql
@@ -0,0 +1,6 @@
+INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) VALUES
+ ('VnRole','*','READ','ALLOW','ROLE','employee'),
+ ('VnRole','*','WRITE','ALLOW','ROLE','it');
+
+DELETE FROM`salix`.`ACL` WHERE model='Role';
+
diff --git a/db/changes/240001/00-alterTable.sql b/db/changes/240001/00-alterTable.sql
new file mode 100644
index 0000000000..b6974b715a
--- /dev/null
+++ b/db/changes/240001/00-alterTable.sql
@@ -0,0 +1 @@
+ALTER TABLE `vn`.`ticketTracking` CHANGE `workerFk` `userFk` int(10) unsigned DEFAULT NULL NULL;
\ No newline at end of file
diff --git a/db/changes/240001/00-clientCreditLimitToRoleCreditLimit.sql b/db/changes/240001/00-clientCreditLimitToRoleCreditLimit.sql
new file mode 100644
index 0000000000..2bc0f830dd
--- /dev/null
+++ b/db/changes/240001/00-clientCreditLimitToRoleCreditLimit.sql
@@ -0,0 +1,4 @@
+RENAME TABLE `vn`.`clientCreditLimit` TO `vn`.`roleCreditLimit`;
+ALTER TABLE `vn`.`roleCreditLimit` DROP FOREIGN KEY `clientCreditLimit_FK`;
+ALTER TABLE `vn`.`roleCreditLimit` ADD CONSTRAINT `roleCreditLimit_FK` FOREIGN KEY (`roleFk`) REFERENCES `account`.`role`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
+
diff --git a/db/changes/240001/01-procedures.sql b/db/changes/240001/01-procedures.sql
new file mode 100644
index 0000000000..3777708d57
--- /dev/null
+++ b/db/changes/240001/01-procedures.sql
@@ -0,0 +1,1129 @@
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `hedera`.`order_confirmWithUser`(vSelf INT, vUserId INT)
+BEGIN
+/**
+ * Confirms an order, creating each of its tickets on the corresponding
+ * date, store and user.
+ *
+ * @param vSelf The order identifier
+ * @param vUser The user identifier
+ */
+ DECLARE vOk BOOL;
+ DECLARE vDone BOOL DEFAULT FALSE;
+ DECLARE vWarehouse INT;
+ DECLARE vShipment DATE;
+ DECLARE vTicket INT;
+ DECLARE vNotes VARCHAR(255);
+ DECLARE vItem INT;
+ DECLARE vConcept VARCHAR(30);
+ DECLARE vAmount INT;
+ DECLARE vPrice DECIMAL(10,2);
+ DECLARE vSale INT;
+ DECLARE vRate INT;
+ DECLARE vRowId INT;
+ DECLARE vPriceFixed DECIMAL(10,2);
+ DECLARE vDelivery DATE;
+ DECLARE vAddress INT;
+ DECLARE vIsConfirmed BOOL;
+ DECLARE vClientId INT;
+ DECLARE vCompanyId INT;
+ DECLARE vAgencyModeId INT;
+ DECLARE TICKET_FREE INT DEFAULT 2;
+ DECLARE vCalc INT;
+ DECLARE vIsLogifloraItem BOOL;
+ DECLARE vOldQuantity INT;
+ DECLARE vNewQuantity INT;
+ DECLARE vIsTaxDataChecked BOOL;
+
+ DECLARE cDates CURSOR FOR
+ SELECT zgs.shipped, r.warehouse_id
+ FROM `order` o
+ JOIN order_row r ON r.order_id = o.id
+ LEFT JOIN tmp.zoneGetShipped zgs ON zgs.warehouseFk = r.warehouse_id
+ WHERE o.id = vSelf AND r.amount != 0
+ GROUP BY r.warehouse_id;
+
+ DECLARE cRows CURSOR FOR
+ SELECT r.id, r.item_id, i.name, r.amount, r.price, r.rate, i.isFloramondo
+ FROM order_row r
+ JOIN vn.item i ON i.id = r.item_id
+ WHERE r.amount != 0
+ AND r.warehouse_id = vWarehouse
+ AND r.order_id = vSelf
+ ORDER BY r.rate DESC;
+
+ DECLARE CONTINUE HANDLER FOR NOT FOUND
+ SET vDone = TRUE;
+
+ DECLARE EXIT HANDLER FOR SQLEXCEPTION
+ BEGIN
+ ROLLBACK;
+ RESIGNAL;
+ END;
+
+ -- Carga los datos del pedido
+ SELECT o.date_send, o.address_id, o.note, a.clientFk,
+ o.company_id, o.agency_id, c.isTaxDataChecked
+ INTO vDelivery, vAddress, vNotes, vClientId,
+ vCompanyId, vAgencyModeId, vIsTaxDataChecked
+ FROM hedera.`order` o
+ JOIN vn.address a ON a.id = o.address_id
+ JOIN vn.client c ON c.id = a.clientFk
+ WHERE o.id = vSelf;
+
+ -- Verifica si el cliente tiene los datos comprobados
+ IF NOT vIsTaxDataChecked THEN
+ CALL util.throw ('clientNotVerified');
+ END IF;
+
+ -- Carga las fechas de salida de cada almacen
+ CALL vn.zone_getShipped (vDelivery, vAddress, vAgencyModeId, FALSE);
+
+ -- Trabajador que realiza la accion
+ IF vUserId IS NULL THEN
+ SELECT employeeFk INTO vUserId FROM orderConfig;
+ END IF;
+
+ START TRANSACTION;
+
+ CALL order_checkEditable(vSelf);
+
+ -- Check order is not empty
+
+ SELECT COUNT(*) > 0 INTO vOk
+ FROM order_row WHERE order_id = vSelf AND amount > 0;
+
+ IF NOT vOk THEN
+ CALL util.throw ('ORDER_EMPTY');
+ END IF;
+
+ -- Crea los tickets del pedido
+
+ OPEN cDates;
+
+ lDates:
+ LOOP
+ SET vTicket = NULL;
+ SET vDone = FALSE;
+ FETCH cDates INTO vShipment, vWarehouse;
+
+ IF vDone THEN
+ LEAVE lDates;
+ END IF;
+
+ -- Busca un ticket existente que coincida con los parametros
+ WITH tPrevia AS
+ (SELECT DISTINCT s.ticketFk
+ FROM vn.sale s
+ JOIN vn.saleGroupDetail sgd ON sgd.saleFk = s.id
+ JOIN vn.ticket t ON t.id = s.ticketFk
+ WHERE t.shipped BETWEEN vShipment AND util.dayend(vShipment)
+ )
+ SELECT t.id INTO vTicket
+ FROM vn.ticket t
+ LEFT JOIN tPrevia tp ON tp.ticketFk = t.id
+ LEFT JOIN vn.ticketState tls on tls.ticket = t.id
+ JOIN hedera.`order` o
+ ON o.address_id = t.addressFk
+ AND vWarehouse = t.warehouseFk
+ AND o.date_send = t.landed
+ AND DATE(t.shipped) = vShipment
+ WHERE o.id = vSelf
+ AND t.refFk IS NULL
+ AND tp.ticketFk IS NULL
+ AND IFNULL(tls.alertLevel,0) = 0
+ LIMIT 1;
+
+ -- Crea el ticket en el caso de no existir uno adecuado
+ IF vTicket IS NULL
+ THEN
+
+ SET vShipment = IFNULL(vShipment, util.VN_CURDATE());
+
+ CALL vn.ticket_add(
+ vClientId,
+ vShipment,
+ vWarehouse,
+ vCompanyId,
+ vAddress,
+ vAgencyModeId,
+ NULL,
+ vDelivery,
+ vUserId,
+ TRUE,
+ vTicket
+ );
+ ELSE
+ INSERT INTO vn.ticketTracking
+ SET ticketFk = vTicket,
+ userFk = vUserId,
+ stateFk = TICKET_FREE;
+ END IF;
+
+ INSERT IGNORE INTO vn.orderTicket
+ SET orderFk = vSelf,
+ ticketFk = vTicket;
+
+ -- Añade las notas
+
+ IF vNotes IS NOT NULL AND vNotes != ''
+ THEN
+ INSERT INTO vn.ticketObservation SET
+ ticketFk = vTicket,
+ observationTypeFk = 4 /* salesperson */,
+ `description` = vNotes
+ ON DUPLICATE KEY UPDATE
+ `description` = CONCAT(VALUES(`description`),'. ', `description`);
+ END IF;
+
+ -- Añade los movimientos y sus componentes
+
+ OPEN cRows;
+
+ lRows: LOOP
+ SET vDone = FALSE;
+ FETCH cRows INTO vRowId, vItem, vConcept, vAmount, vPrice, vRate, vIsLogifloraItem;
+
+ IF vDone THEN
+ LEAVE lRows;
+ END IF;
+
+ SET vSale = NULL;
+
+ SELECT s.id, s.quantity INTO vSale, vOldQuantity
+ FROM vn.sale s
+ WHERE ticketFk = vTicket
+ AND price = vPrice
+ AND itemFk = vItem
+ AND discount = 0
+ LIMIT 1;
+
+ IF vSale THEN
+ UPDATE vn.sale
+ SET quantity = quantity + vAmount,
+ originalQuantity = quantity
+ WHERE id = vSale;
+
+ SELECT s.quantity INTO vNewQuantity
+ FROM vn.sale s
+ WHERE id = vSale;
+ ELSE
+ -- Obtiene el coste
+ SELECT SUM(rc.`price`) valueSum INTO vPriceFixed
+ FROM orderRowComponent rc
+ JOIN vn.component c ON c.id = rc.componentFk
+ JOIN vn.componentType ct ON ct.id = c.typeFk AND ct.isBase
+ WHERE rc.rowFk = vRowId;
+
+ INSERT INTO vn.sale
+ SET itemFk = vItem,
+ ticketFk = vTicket,
+ concept = vConcept,
+ quantity = vAmount,
+ price = vPrice,
+ priceFixed = vPriceFixed,
+ isPriceFixed = TRUE;
+
+ SET vSale = LAST_INSERT_ID();
+
+ INSERT INTO vn.saleComponent
+ (saleFk, componentFk, `value`)
+ SELECT vSale, rc.componentFk, rc.price
+ FROM orderRowComponent rc
+ JOIN vn.component c ON c.id = rc.componentFk
+ WHERE rc.rowFk = vRowId
+ GROUP BY vSale, rc.componentFk;
+ END IF;
+
+ UPDATE order_row SET Id_Movimiento = vSale
+ WHERE id = vRowId;
+
+ -- Inserta en putOrder si la compra es de Floramondo
+ IF vIsLogifloraItem THEN
+ CALL cache.availableNoRaids_refresh(vCalc,FALSE,vWarehouse,vShipment);
+
+ SET @available := 0;
+
+ SELECT GREATEST(0,available) INTO @available
+ FROM cache.availableNoRaids
+ WHERE calc_id = vCalc
+ AND item_id = vItem;
+
+ UPDATE cache.availableNoRaids
+ SET available = GREATEST(0,available - vAmount)
+ WHERE item_id = vItem
+ AND calc_id = vCalc;
+
+ INSERT INTO edi.putOrder (
+ deliveryInformationID,
+ supplyResponseId,
+ quantity ,
+ EndUserPartyId,
+ EndUserPartyGLN,
+ FHAdminNumber,
+ saleFk
+ )
+ SELECT di.ID,
+ i.supplyResponseFk,
+ CEIL((vAmount - @available)/ sr.NumberOfItemsPerCask),
+ o.address_id ,
+ vClientId,
+ IFNULL(ca.fhAdminNumber, fhc.defaultAdminNumber),
+ vSale
+ FROM edi.deliveryInformation di
+ JOIN vn.item i ON i.supplyResponseFk = di.supplyResponseID
+ JOIN edi.supplyResponse sr ON sr.ID = i.supplyResponseFk
+ LEFT JOIN edi.clientFHAdminNumber ca ON ca.clientFk = vClientId
+ JOIN edi.floraHollandConfig fhc
+ JOIN hedera.`order` o ON o.id = vSelf
+ WHERE i.id = vItem
+ AND di.LatestOrderDateTime > util.VN_NOW()
+ AND vAmount > @available
+ LIMIT 1;
+ END IF;
+ END LOOP;
+
+ CLOSE cRows;
+ END LOOP;
+
+ CLOSE cDates;
+
+ UPDATE `order` SET confirmed = TRUE, confirm_date = util.VN_NOW()
+ WHERE id = vSelf;
+
+ COMMIT;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`invoiceOut_new`(
+ vSerial VARCHAR(255),
+ vInvoiceDate DATE,
+ vTaxArea VARCHAR(25),
+ OUT vNewInvoiceId INT)
+BEGIN
+/**
+ * Creación de facturas emitidas.
+ * requiere previamente tabla tmp.ticketToInvoice(id).
+ *
+ * @param vSerial serie a la cual se hace la factura
+ * @param vInvoiceDate fecha de la factura
+ * @param vTaxArea tipo de iva en relacion a la empresa y al cliente
+ * @param vNewInvoiceId id de la factura que se acaba de generar
+ * @return vNewInvoiceId
+ */
+ DECLARE vIsAnySaleToInvoice BOOL;
+ DECLARE vIsAnyServiceToInvoice BOOL;
+ DECLARE vNewRef VARCHAR(255);
+ DECLARE vWorker INT DEFAULT account.myUser_getId();
+ DECLARE vCompanyFk INT;
+ DECLARE vInterCompanyFk INT;
+ DECLARE vClientFk INT;
+ DECLARE vCplusStandardInvoiceTypeFk INT DEFAULT 1;
+ DECLARE vCplusCorrectingInvoiceTypeFk INT DEFAULT 6;
+ DECLARE vCplusSimplifiedInvoiceTypeFk INT DEFAULT 2;
+ DECLARE vCorrectingSerial VARCHAR(1) DEFAULT 'R';
+ DECLARE vSimplifiedSerial VARCHAR(1) DEFAULT 'S';
+ DECLARE vNewInvoiceInFk INT;
+ DECLARE vIsInterCompany BOOL DEFAULT FALSE;
+ DECLARE vIsCEESerial BOOL DEFAULT FALSE;
+ DECLARE vIsCorrectInvoiceDate BOOL;
+ DECLARE vMaxShipped DATE;
+ DECLARE vDone BOOL;
+ DECLARE vTicketFk INT;
+ DECLARE vCursor CURSOR FOR
+ SELECT id
+ FROM tmp.ticketToInvoice;
+
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
+
+ SET vInvoiceDate = IFNULL(vInvoiceDate, util.VN_CURDATE());
+
+ SELECT t.clientFk,
+ t.companyFk,
+ MAX(DATE(t.shipped)),
+ DATE(vInvoiceDate) >= invoiceOut_getMaxIssued(
+ vSerial,
+ t.companyFk,
+ YEAR(vInvoiceDate))
+ INTO vClientFk,
+ vCompanyFk,
+ vMaxShipped,
+ vIsCorrectInvoiceDate
+ FROM tmp.ticketToInvoice tt
+ JOIN ticket t ON t.id = tt.id;
+
+ IF(vMaxShipped > vInvoiceDate) THEN
+ CALL util.throw("Invoice date can't be less than max date");
+ END IF;
+
+ IF NOT vIsCorrectInvoiceDate THEN
+ CALL util.throw('Exists an invoice with a previous date');
+ END IF;
+
+ -- Eliminem de tmp.ticketToInvoice els tickets que no han de ser facturats
+ DELETE ti.*
+ FROM tmp.ticketToInvoice ti
+ JOIN ticket t ON t.id = ti.id
+ JOIN sale s ON s.ticketFk = t.id
+ JOIN item i ON i.id = s.itemFk
+ JOIN supplier su ON su.id = t.companyFk
+ JOIN client c ON c.id = t.clientFk
+ LEFT JOIN itemTaxCountry itc ON itc.itemFk = i.id AND itc.countryFk = su.countryFk
+ WHERE (YEAR(t.shipped) < 2001 AND t.isDeleted)
+ OR c.isTaxDataChecked = FALSE
+ OR t.isDeleted
+ OR c.hasToInvoice = FALSE
+ OR itc.id IS NULL;
+
+ SELECT SUM(s.quantity * s.price * (100 - s.discount)/100) <> 0
+ INTO vIsAnySaleToInvoice
+ FROM tmp.ticketToInvoice t
+ JOIN sale s ON s.ticketFk = t.id;
+
+ SELECT COUNT(*) > 0 INTO vIsAnyServiceToInvoice
+ FROM tmp.ticketToInvoice t
+ JOIN ticketService ts ON ts.ticketFk = t.id;
+
+ IF (vIsAnySaleToInvoice OR vIsAnyServiceToInvoice)
+ AND (vCorrectingSerial = vSerial OR NOT hasAnyNegativeBase())
+ THEN
+
+ -- el trigger añade el siguiente Id_Factura correspondiente a la vSerial
+ INSERT INTO invoiceOut(
+ ref,
+ serial,
+ issued,
+ clientFk,
+ dued,
+ companyFk,
+ siiTypeInvoiceOutFk
+ )
+ SELECT
+ 1,
+ vSerial,
+ vInvoiceDate,
+ vClientFk,
+ getDueDate(vInvoiceDate, dueDay),
+ vCompanyFk,
+ IF(vSerial = vCorrectingSerial,
+ vCplusCorrectingInvoiceTypeFk,
+ IF(vSerial = vSimplifiedSerial,
+ vCplusSimplifiedInvoiceTypeFk,
+ vCplusStandardInvoiceTypeFk))
+ FROM client
+ WHERE id = vClientFk;
+
+ SET vNewInvoiceId = LAST_INSERT_ID();
+
+ SELECT `ref`
+ INTO vNewRef
+ FROM invoiceOut
+ WHERE id = vNewInvoiceId;
+
+ OPEN vCursor;
+ l: LOOP
+ SET vDone = FALSE;
+ FETCH vCursor INTO vTicketFk;
+
+ IF vDone THEN
+ LEAVE l;
+ END IF;
+
+ CALL ticket_recalc(vTicketFk, vTaxArea);
+
+ END LOOP;
+ CLOSE vCursor;
+
+ UPDATE ticket t
+ JOIN tmp.ticketToInvoice ti ON ti.id = t.id
+ SET t.refFk = vNewRef;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.updateInter;
+ CREATE TEMPORARY TABLE tmp.updateInter ENGINE = MEMORY
+ SELECT s.id, ti.id ticket_id, vWorker Id_Trabajador
+ FROM tmp.ticketToInvoice ti
+ LEFT JOIN ticketState ts ON ti.id = ts.ticket
+ JOIN state s
+ WHERE IFNULL(ts.alertLevel, 0) < 3 and s.`code` = getAlert3State(ti.id);
+
+ INSERT INTO ticketTracking(stateFk, ticketFk, userFk)
+ SELECT * FROM tmp.updateInter;
+
+ CALL invoiceExpenseMake(vNewInvoiceId);
+ CALL invoiceTaxMake(vNewInvoiceId, vTaxArea);
+
+ UPDATE invoiceOut io
+ JOIN (
+ SELECT SUM(amount) total
+ FROM invoiceOutExpense
+ WHERE invoiceOutFk = vNewInvoiceId
+ ) base
+ JOIN (
+ SELECT SUM(vat) total
+ FROM invoiceOutTax
+ WHERE invoiceOutFk = vNewInvoiceId
+ ) vat
+ SET io.amount = base.total + vat.total
+ WHERE io.id = vNewInvoiceId;
+
+ DROP TEMPORARY TABLE tmp.updateInter;
+
+ SELECT COUNT(*), id
+ INTO vIsInterCompany, vInterCompanyFk
+ FROM company
+ WHERE clientFk = vClientFk;
+
+ IF (vIsInterCompany) THEN
+
+ INSERT INTO invoiceIn(supplierFk, supplierRef, issued, companyFk)
+ SELECT vCompanyFk, vNewRef, vInvoiceDate, vInterCompanyFk;
+
+ SET vNewInvoiceInFk = LAST_INSERT_ID();
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.ticket;
+ CREATE TEMPORARY TABLE tmp.ticket
+ (KEY (ticketFk))
+ ENGINE = MEMORY
+ SELECT id ticketFk
+ FROM tmp.ticketToInvoice;
+
+ CALL `ticket_getTax`('NATIONAL');
+
+ SET @vTaxableBaseServices := 0.00;
+ SET @vTaxCodeGeneral := NULL;
+
+ INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenseFk, taxTypeSageFk, transactionTypeSageFk)
+ SELECT vNewInvoiceInFk,
+ @vTaxableBaseServices,
+ sub.expenseFk,
+ sub.taxTypeSageFk,
+ sub.transactionTypeSageFk
+ FROM (
+ SELECT @vTaxableBaseServices := SUM(tst.taxableBase) taxableBase,
+ i.expenseFk,
+ i.taxTypeSageFk,
+ i.transactionTypeSageFk,
+ @vTaxCodeGeneral := i.taxClassCodeFk
+ FROM tmp.ticketServiceTax tst
+ JOIN invoiceOutTaxConfig i ON i.taxClassCodeFk = tst.code
+ WHERE i.isService
+ HAVING taxableBase
+ ) sub;
+
+ INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenseFk, taxTypeSageFk, transactionTypeSageFk)
+ SELECT vNewInvoiceInFk,
+ SUM(tt.taxableBase) - IF(tt.code = @vTaxCodeGeneral,
+ @vTaxableBaseServices, 0) taxableBase,
+ i.expenseFk,
+ i.taxTypeSageFk ,
+ i.transactionTypeSageFk
+ FROM tmp.ticketTax tt
+ JOIN invoiceOutTaxConfig i ON i.taxClassCodeFk = tt.code
+ WHERE !i.isService
+ GROUP BY tt.pgcFk
+ HAVING taxableBase
+ ORDER BY tt.priority;
+
+ CALL invoiceInDueDay_calculate(vNewInvoiceInFk);
+
+ SELECT COUNT(*) INTO vIsCEESerial
+ FROM invoiceOutSerial
+ WHERE code = vSerial;
+
+ IF vIsCEESerial THEN
+
+ INSERT INTO invoiceInIntrastat (
+ invoiceInFk,
+ intrastatFk,
+ amount,
+ stems,
+ countryFk,
+ net)
+ SELECT
+ vNewInvoiceInFk,
+ i.intrastatFk,
+ SUM(CAST((s.quantity * s.price * (100 - s.discount) / 100 ) AS DECIMAL(10, 2))),
+ SUM(CAST(IFNULL(i.stems, 1) * s.quantity AS DECIMAL(10, 2))),
+ su.countryFk,
+ CAST(SUM(IFNULL(i.stems, 1)
+ * s.quantity
+ * IF(ic.grams, ic.grams, IFNULL(i.weightByPiece, 0)) / 1000) AS DECIMAL(10, 2))
+ FROM sale s
+ JOIN ticket t ON s.ticketFk = t.id
+ JOIN supplier su ON su.id = t.companyFk
+ JOIN item i ON i.id = s.itemFk
+ LEFT JOIN itemCost ic ON ic.itemFk = i.id AND ic.warehouseFk = t.warehouseFk
+ WHERE t.refFk = vNewRef
+ GROUP BY i.intrastatFk;
+
+ END IF;
+ DROP TEMPORARY TABLE tmp.ticket;
+ DROP TEMPORARY TABLE tmp.ticketAmount;
+ DROP TEMPORARY TABLE tmp.ticketTax;
+ DROP TEMPORARY TABLE tmp.ticketServiceTax;
+ END IF;
+ END IF;
+ DROP TEMPORARY TABLE `tmp`.`ticketToInvoice`;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`productionError_add`()
+BEGIN
+ DECLARE vDatedFrom DATETIME;
+ DECLARE vDatedTo DATETIME;
+/**
+ * Rellena la tabla vn.productionError con estadisticas de encajadores, revisores y sacadores. Se ejecuta en el nightTask
+ */
+ SELECT util.VN_CURDATE() - INTERVAL 1 DAY, util.dayend(util.VN_CURDATE() - INTERVAL 1 DAY) INTO vDatedFrom, vDatedTo;
+ CALL timeControl_calculateAll(vDatedFrom, vDatedTo);
+
+ -- Rellena la tabla tmp.errorsByClaim con encajadores, revisores y sacadores
+ CREATE OR REPLACE TEMPORARY TABLE tmp.errorsByClaim
+ ENGINE = MEMORY
+ SELECT COUNT(c.ticketFk) errors,
+ cd.workerFk
+ FROM claimDevelopment cd
+ JOIN claim c ON cd.claimFk = c.id
+ JOIN ticket t ON c.ticketFk = t.id
+ JOIN claimResponsible cr ON cd.claimResponsibleFk = cr.id
+ WHERE t.shipped BETWEEN vDatedFrom AND vDatedTo
+ AND cr.code IN ('pic', 'chk', 'pck')
+ GROUP BY cd.workerFk;
+
+ -- Genera la tabla tmp.volume con encajadores, sacadores y revisores
+ CREATE OR REPLACE TEMPORARY TABLE tmp.volume
+ ENGINE = MEMORY
+ SELECT SUM(w.volume) volume,
+ w.workerFk
+ FROM bs.workerProductivity w
+ WHERE w.dated BETWEEN vDatedFrom AND vDatedTo
+ GROUP BY w.workerFk;
+
+ -- Rellena la tabla tmp.errorsByChecker con fallos de revisores
+ CREATE OR REPLACE TEMPORARY TABLE tmp.errorsByChecker
+ ENGINE = MEMORY
+ SELECT st.workerFk,
+ COUNT(t.id) errors
+ FROM saleMistake sm
+ JOIN saleTracking st ON sm.saleFk = st.saleFk
+ JOIN `state` s2 ON s2.id = st.stateFk
+ JOIN sale s ON s.id = sm.saleFk
+ JOIN ticket t on t.id = s.ticketFk
+ WHERE (t.shipped BETWEEN vDatedFrom AND vDatedTo)
+ AND s2.code IN ('OK','PREVIOUS_PREPARATION','PREPARED','CHECKED')
+ GROUP BY st.workerFk;
+
+ -- Rellena la tabla tmp.expeditionErrors con fallos de expediciones
+ CREATE OR REPLACE TEMPORARY TABLE tmp.expeditionErrors
+ ENGINE = MEMORY
+ SELECT COUNT(t.id) errors,
+ e.workerFk
+ FROM vn.expeditionMistake pm
+ JOIN vn.expedition e ON e.id = pm.expeditionFk
+ JOIN vn.ticket t ON t.id = e.ticketFk
+ WHERE t.shipped BETWEEN vDatedFrom AND vDatedTo
+ GROUP BY e.workerFk;
+
+ -- Genera la tabla tmp.total para sacadores y revisores
+ CREATE OR REPLACE TEMPORARY TABLE tmp.total
+ ENGINE = MEMORY
+ SELECT st.workerFk,
+ COUNT(DISTINCT t.id) ticketCount,
+ COUNT(s.id) lineCount
+ FROM saleTracking st
+ JOIN `state` s2 ON s2.id = st.stateFk
+ JOIN sale s ON s.id = st.saleFk
+ JOIN ticket t ON s.ticketFk = t.id
+ WHERE (t.shipped BETWEEN vDatedFrom AND vDatedTo)
+ AND s2.code IN ('OK','PREVIOUS_PREPARATION','PREPARED','CHECKED')
+ GROUP BY st.workerFk;
+
+ -- Rellena la tabla vn.productionError con sacadores
+ INSERT INTO productionError(userFk,
+ firstname,
+ lastname,
+ rol,
+ ticketNumber,
+ lineNumber,
+ error,
+ volume,
+ hourStart,
+ hourEnd,
+ hourWorked,
+ dated)
+ SELECT w.id,
+ w.firstName,
+ w.lastName,
+ "Sacadores",
+ t.ticketCount totalTickets,
+ t.lineCount,
+ IFNULL(ec.errors,0) + IFNULL(ec2.errors,0) errors,
+ v.volume volume,
+ SUBSTRING(tc.tableTimed, 1, 5) hourStart,
+ SUBSTRING(tc.tableTimed, LENGTH(tc.tableTimed)-4, 5) hourEnd,
+ IFNULL(CAST(tc.timeWorkDecimal AS DECIMAL (10,2)) , 0) hourWorked,
+ vDatedFrom dated
+ FROM tmp.total t
+ LEFT JOIN worker w ON w.id = t.workerFk
+ LEFT JOIN tmp.timeControlCalculate tc ON tc.userFk = t.workerFk
+ LEFT JOIN tmp.errorsByClaim ec ON ec.workerFk = t.workerFk
+ LEFT JOIN tmp.volume v ON v.workerFk = t.workerFk
+ LEFT JOIN tmp.errorsByChecker ec2 ON ec2.workerFk = t.workerFk
+ JOIN (SELECT DISTINCT w.id -- Verificamos que son sacadores
+ FROM vn.collection c
+ JOIN vn.state s ON s.id = c.stateFk
+ JOIN vn.train tn ON tn.id = c.trainFk
+ JOIN vn.worker w ON w.id = c.workerFk
+ WHERE c.created BETWEEN vDatedFrom AND vDatedTo) sub ON sub.id = w.id
+ GROUP BY w.id;
+
+ CREATE OR REPLACE TEMPORARY TABLE itemPickerErrors -- Errores de los sacadores, derivadores de los revisadores
+ ENGINE = MEMORY
+ SELECT COUNT(c.ticketFk) errors,
+ tt.userFk
+ FROM claimDevelopment cd
+ JOIN claim c ON cd.claimFk = c.id
+ JOIN ticket t ON c.ticketFk = t.id
+ JOIN claimResponsible cr ON cd.claimResponsibleFk = cr.id
+ JOIN ticketTracking tt ON tt.ticketFk = t.id
+ JOIN `state` s ON s.id = tt.stateFk
+ WHERE t.shipped BETWEEN vDatedFrom AND vDatedTo
+ AND cr.code = 'chk'
+ AND s.code = 'ON_PREPARATION'
+ GROUP BY tt.userFk;
+
+ UPDATE productionError ep
+ JOIN itemPickerErrors ipe ON ipe.workerFk = ep.userFk
+ SET ep.error = ep.error + ipe.errors
+ WHERE vDatedFrom = ep.dated AND ep.rol = 'Sacadores';
+
+ DROP TEMPORARY TABLE itemPickerErrors;
+
+ -- Rellena la tabla vn.productionError con revisores
+ CALL productionError_addCheckerPackager(vDatedFrom, vDatedTo, "Revisadores");
+
+ -- Genera la tabla tmp.total para encajadores
+ CREATE OR REPLACE TEMPORARY TABLE tmp.total
+ ENGINE = MEMORY
+ SELECT e.workerFk,
+ COUNT(DISTINCT t.id) ticketCount,
+ COUNT(s.id) lineCount
+ FROM expedition e
+ JOIN ticket t ON e.ticketFk = t.id
+ JOIN sale s ON s.ticketFk = t.id
+ WHERE t.shipped BETWEEN vDatedFrom AND vDatedTo
+ GROUP BY e.workerFk;
+
+ -- Rellena la tabla vn.productionError con encajadores
+ CALL productionError_addCheckerPackager(vDatedFrom, vDatedTo, "Encajadores");
+
+ DROP TEMPORARY TABLE tmp.errorsByClaim,
+ tmp.volume,
+ tmp.errorsByChecker,
+ tmp.expeditionErrors,
+ tmp.total;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`sectorProductivity_add`()
+BEGIN
+ DECLARE vDatedFrom DATETIME;
+ DECLARE vDatedTo DATETIME;
+
+ SELECT DATE_SUB(util.VN_CURDATE(),INTERVAL 1 DAY), CONCAT(DATE_SUB(util.VN_CURDATE(),INTERVAL 1 DAY),' 23:59:59') INTO vDatedFrom, vDatedTo;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.timeControlCalculate;
+ DROP TEMPORARY TABLE IF EXISTS tmp.errorsByChecker;
+ DROP TEMPORARY TABLE IF EXISTS tmp.previousErrors;
+
+ CALL timeControl_calculateAll(vDatedFrom, vDatedTo);
+
+ CREATE TEMPORARY TABLE tmp.errorsByChecker
+ ENGINE = MEMORY
+ SELECT sc.userFk workerFk, COUNT(DISTINCT s.ticketFk) errorsByChecker
+ FROM saleMistake sm
+ JOIN vn.saleGroupDetail sgd on sgd.saleFk = sm.saleFk
+ JOIN vn.sectorCollectionSaleGroup scsg on scsg.saleGroupFk = sgd.saleGroupFk
+ JOIN vn.sectorCollection sc on sc.id = scsg.sectorCollectionFk
+ JOIN sale s ON s.id = sm.saleFk
+ JOIN ticket t on t.id = s.ticketFk
+ WHERE (t.shipped BETWEEN vDatedFrom AND vDatedTo)
+ GROUP BY sc.userFk ;
+
+ CREATE TEMPORARY TABLE tmp.previousErrors -- Errores de previa, derivadores de los revisadores (por reclamación)
+ ENGINE = MEMORY
+ SELECT tt.userFk, COUNT(c.ticketFk) errorsByClaim
+ FROM claimDevelopment cd
+ JOIN claim c ON cd.claimFk = c.id
+ JOIN ticket t ON c.ticketFk = t.id
+ JOIN claimResponsible cr ON cd.claimResponsibleFk = cr.id
+ JOIN ticketTracking tt ON tt.ticketFk = t.id
+ JOIN `state` s ON s.id = tt.stateFk
+ WHERE t.shipped BETWEEN vDatedFrom AND vDatedTo AND cr.description = 'Revisadores' AND s.code = 'OK PREVIOUS'
+ GROUP BY cd.workerFk;
+
+ DELETE FROM sectorProductivity
+ WHERE dated = vDatedFrom
+ AND sector IN ('Algemesi Artificial','Algemesi Complementos');
+
+ INSERT INTO sectorProductivity(workerFk, firstName, lastName, sector, ticketCount, saleCount, error, volume, hourWorked, dated)
+ SELECT w.id workerFk,
+ w.firstName,
+ w.lastName,
+ se.description sector,
+ COUNT(DISTINCT s.ticketFk) ticketCount,
+ COUNT(sgd.id) saleCount,
+ IFNULL(ec2.errorsByChecker,0) + IFNULL(pe.errorsByClaim, 0) errors,
+ wp.volume,
+ IFNULL(CAST(tc.timeWorkDecimal AS DECIMAL (10,2)) , 0) AS hourWorked,
+ DATE(vDatedFrom) dated
+ FROM vn.saleGroupDetail sgd
+ JOIN vn.saleGroup sg on sg.id = sgd.saleGroupFk
+ JOIN vn.sectorCollectionSaleGroup scsg on scsg.saleGroupFk = sgd.saleGroupFk
+ JOIN vn.sectorCollection sc on sc.id = scsg.sectorCollectionFk
+ join vn.sector se on se.id = sc.sectorFk
+ JOIN vn.worker w ON w.id = sc.userFk
+ LEFT JOIN vn.sale s ON s.id = sgd.saleFk
+ LEFT JOIN tmp.timeControlCalculate tc ON tc.userFk = w.id
+ LEFT JOIN bs.workerProductivity wp ON wp.workerFk = w.id
+ LEFT JOIN `state` s2 ON s2.id = wp.stateFk AND s2.code = 'OK PREVIOUS'
+ LEFT JOIN tmp.errorsByChecker ec2 ON ec2.workerFk = w.id
+ LEFT JOIN tmp.previousErrors pe ON pe.workerFk = w.id
+ WHERE DATE(sc.created) = vDatedFrom
+ AND wp.dated = vDatedFrom
+ GROUP BY w.id;
+
+ DROP TEMPORARY TABLE tmp.timeControlCalculate;
+ DROP TEMPORARY TABLE tmp.errorsByChecker;
+ DROP TEMPORARY TABLE tmp.previousErrors;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticketStateUpdate`(vTicketFk INT, vStateCode VARCHAR(45))
+BEGIN
+
+ /*
+ * @deprecated:utilizar ticket_setState
+ */
+
+ DECLARE vAlertLevel INT;
+
+ SELECT s.alertLevel INTO vAlertLevel
+ FROM vn.state s
+ JOIN vn.ticketState ts ON ts.stateFk = s.id
+ WHERE ts.ticketFk = vTicketFk;
+
+ IF !(vStateCode = 'ON_CHECKING' AND vAlertLevel > 1) THEN
+
+ INSERT INTO ticketTracking(stateFk, ticketFk, userFk)
+ SELECT id, vTicketFk, account.myUser_getId()
+ FROM vn.state
+ WHERE `code` = vStateCode collate utf8_unicode_ci;
+
+ END IF;
+
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_Clone`(vOriginalTicket INT, OUT vNewTicket INT)
+BEGIN
+/**
+ * Clona el contenido de un ticket en otro
+ *
+ * @param vOriginalTicket ticket Original
+ * @param vNewTicket ticket creado
+ */
+ DECLARE vStateFk INT;
+
+ INSERT INTO ticket (
+ clientFk,
+ shipped,
+ addressFk,
+ agencyModeFk,
+ nickname,
+ warehouseFk,
+ companyFk,
+ landed,
+ zoneFk,
+ zonePrice,
+ zoneBonus,
+ routeFk,
+ priority,
+ hasPriority,
+ clonedFrom
+ )
+ SELECT
+ clientFk,
+ shipped,
+ addressFk,
+ agencyModeFk,
+ nickname,
+ warehouseFk,
+ companyFk,
+ landed,
+ zoneFk,
+ zonePrice,
+ zoneBonus,
+ routeFk,
+ priority,
+ hasPriority,
+ vOriginalTicket
+ FROM ticket
+ WHERE id = vOriginalTicket;
+
+ SET vNewTicket = LAST_INSERT_ID();
+
+ INSERT INTO ticketObservation(ticketFk, observationTypeFk, description)
+ SELECT vNewTicket, observationTypeFk, description
+ FROM ticketObservation
+ WHERE ticketFk = vOriginalTicket;
+
+ INSERT INTO ticketTracking(ticketFk, stateFk, userFk, created)
+ SELECT vNewTicket, stateFk, userFk, created
+ FROM ticketTracking
+ WHERE ticketFk = vOriginalTicket
+ ORDER BY created;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_add`(
+ vClientId INT
+ ,vShipped DATE
+ ,vWarehouseFk INT
+ ,vCompanyFk INT
+ ,vAddressFk INT
+ ,vAgencyModeFk INT
+ ,vRouteFk INT
+ ,vlanded DATE
+ ,vUserId INT
+ ,vIsRequiredZone INT
+ ,OUT vNewTicket INT)
+BEGIN
+/**
+* Crea un ticket,
+* ¡¡NO se debe llamar directamente, llamar a salix que hace comprobaciones previas!!
+*
+* @param vClientId id del cliente
+* @param vShipped dia preparacion
+* @param vWarehouseFk id del warehouse
+* @param vCompanyFk id la empresa
+* @param vAddressFk id del consignatario
+* @param vAgencyModeFk id de la agencia
+* @param vRouteFk id de la ruta | NULL
+* @param vlanded dia llegada
+* @param vUserId que crea el ticket
+* @param vIsRequiredZone Indica si tiene que tener zona valida para ser creado
+* @return vNewTicket id del ticket creado
+*/
+ DECLARE vZoneFk INT;
+ DECLARE vPrice DECIMAL(10,2);
+ DECLARE vBonus DECIMAL(10,2);
+ DECLARE vIsActive BOOL;
+
+ IF vClientId IS NULL THEN
+ CALL util.throw ('CLIENT_NOT_ESPECIFIED');
+ END IF;
+
+ SELECT isActive INTO vIsActive
+ FROM vn.client
+ WHERE id = vClientId;
+
+ IF NOT vIsActive THEN
+ CALL util.throw ('CLIENT_NOT_ACTIVE');
+ END IF;
+
+ IF NOT vAddressFk OR vAddressFk IS NULL THEN
+ SELECT id INTO vAddressFk
+ FROM address
+ WHERE clientFk = vClientId
+ AND isDefaultAddress;
+ END IF;
+
+ IF vAgencyModeFk IS NOT NULL THEN
+ CALL vn.zone_getShipped (vlanded, vAddressFk, vAgencyModeFk, TRUE);
+
+ SELECT zoneFk, price, bonus
+ INTO vZoneFk, vPrice, vBonus
+ FROM tmp.zoneGetShipped
+ WHERE shipped = vShipped
+ AND warehouseFk = vWarehouseFk
+ LIMIT 1;
+
+ IF (vZoneFk IS NULL OR vZoneFk = 0) AND vIsRequiredZone THEN
+ CALL util.throw ('NOT_ZONE_WITH_THIS_PARAMETERS');
+ END IF;
+ END IF;
+
+ INSERT INTO ticket (
+ clientFk,
+ shipped,
+ addressFk,
+ agencyModeFk,
+ nickname,
+ warehouseFk,
+ routeFk,
+ companyFk,
+ landed,
+ zoneFk,
+ zonePrice,
+ zoneBonus
+ )
+ SELECT vClientId,
+ vShipped,
+ a.id,
+ vAgencyModeFk,
+ a.nickname,
+ vWarehouseFk,
+ IF(vRouteFk,vRouteFk,NULL),
+ vCompanyFk,
+ vlanded,
+ vZoneFk,
+ vPrice,
+ vBonus
+ FROM address a
+ JOIN agencyMode am ON am.id = a.agencyModeFk
+ WHERE a.id = vAddressFk;
+
+ SET vNewTicket = LAST_INSERT_ID();
+
+ INSERT INTO ticketObservation(ticketFk, observationTypeFk, description)
+ SELECT vNewTicket, ao.observationTypeFk, ao.description
+ FROM addressObservation ao
+ JOIN address a ON a.id = ao.addressFk
+ WHERE a.id = vAddressFk;
+
+ IF (SELECT COUNT(*)
+ FROM bs.clientNewBorn cnb
+ WHERE cnb.clientFk = vClientId
+ AND NOT cnb.isRookie) = 0 THEN
+
+ CALL vn.ticketObservation_addNewBorn(vNewTicket);
+ END IF;
+
+ IF (SELECT ct.isCreatedAsServed FROM vn.clientType ct JOIN vn.client c ON c.typeFk = ct.code WHERE c.id = vClientId ) <> FALSE THEN
+ INSERT INTO ticketTracking(stateFk, ticketFk, userFk)
+ SELECT id, vNewTicket, account.myUser_getId()
+ FROM state
+ WHERE `code` = 'DELIVERED';
+ END IF;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_setNextState`(vSelf INT)
+BEGIN
+/**
+ * Cambia el estado del ticket al siguiente estado según la tabla state
+ *
+ * @param vSelf id dle ticket
+ */
+ DECLARE vStateFk INT;
+ DECLARE vNewStateFk INT;
+
+ SELECT stateFk INTO vStateFk
+ FROM ticketState
+ WHERE ticketFk = vSelf;
+
+ SELECT nextStateFk INTO vNewStateFk
+ FROM state
+ WHERE id = vStateFk;
+
+ INSERT INTO ticketTracking(stateFk, ticketFk, userFk)
+ VALUES (vNewStateFk, vSelf, account.myUser_getId());
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_setPreviousState`(vTicketFk INT)
+BEGIN
+ DECLARE vControlFk INT;
+
+ SELECT MAX(id) INTO vControlFk
+ FROM ticketTracking
+ WHERE ticketFk = vTicketFk;
+
+ IF (SELECT s.code
+ FROM vn.state s
+ JOIN ticketTracking tt ON tt.stateFk = s.id
+ WHERE tt.id = vControlFk)
+ = 'PREVIOUS_PREPARATION' THEN
+ SELECT id
+ INTO vControlFk
+ FROM ticketTracking tt
+ JOIN vn.state s ON tt.stateFk = s.id
+ WHERE ticketFk = vTicketFk
+ AND id < vControlFk
+ AND s.code != 'PREVIOUS_PREPARATION'
+ ORDER BY id DESC
+ LIMIT 1;
+
+ INSERT INTO ticketTracking(stateFk, ticketFk, userFk)
+ SELECT s.nextStateFk, tt.ticketFk, account.myUser_getId()
+ FROM ticketTracking tt
+ JOIN vn.state s ON tt.stateFk = s.id
+ WHERE id = vControlFk;
+ END IF;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_setState`(
+ vSelf INT,
+ vStateCode VARCHAR(255) COLLATE utf8_general_ci
+)
+BEGIN
+/**
+ * Modifica el estado de un ticket si se cumplen las condiciones necesarias.
+ *
+ * @param vSelf el id del ticket
+ * @param vStateCode estado a modificar del ticket
+ */
+ DECLARE vticketAlertLevel INT;
+ DECLARE vTicketStateCode VARCHAR(255);
+ DECLARE vCanChangeState BOOL;
+ DECLARE vPackedAlertLevel INT;
+ DECLARE vZoneFk INT;
+
+ SELECT s.alertLevel, s.`code`, t.zoneFk
+ INTO vticketAlertLevel, vTicketStateCode, vZoneFk
+ FROM state s
+ JOIN ticketTracking tt ON tt.stateFk = s.id
+ JOIN ticket t ON t.id = tt.ticketFk
+ WHERE tt.ticketFk = vSelf
+ ORDER BY tt.created DESC
+ LIMIT 1;
+
+ SELECT id INTO vPackedAlertLevel FROM alertLevel WHERE code = 'PACKED';
+
+ IF vStateCode = 'OK' AND vZoneFk IS NULL THEN
+ CALL util.throw('ASSIGN_ZONE_FIRST');
+ END IF;
+
+ SET vCanChangeState = (
+ vStateCode <> 'ON_CHECKING' OR
+ vticketAlertLevel < vPackedAlertLevel
+ )AND NOT (
+ vTicketStateCode IN ('CHECKED', 'CHECKING')
+ AND vStateCode IN ('PREPARED', 'ON_PREPARATION')
+ );
+
+ IF vCanChangeState THEN
+ INSERT INTO ticketTracking (stateFk, ticketFk, userFk)
+ SELECT id, vSelf, account.myUser_getId()
+ FROM state
+ WHERE `code` = vStateCode COLLATE utf8_unicode_ci;
+
+ IF vStateCode = 'PACKED' THEN
+ CALL ticket_doCmr(vSelf);
+ END IF;
+ ELSE
+ CALL util.throw('INCORRECT_TICKET_STATE');
+ END IF;
+END$$
+DELIMITER ;
diff --git a/db/changes/240001/02-views.sql b/db/changes/240001/02-views.sql
new file mode 100644
index 0000000000..86a1049a79
--- /dev/null
+++ b/db/changes/240001/02-views.sql
@@ -0,0 +1,56 @@
+CREATE SCHEMA IF NOT EXISTS `vn2008`;
+USE `vn`;
+
+CREATE OR REPLACE DEFINER=`root`@`localhost`
+ SQL SECURITY DEFINER
+ VIEW `ticketState`
+AS SELECT `tt`.`created` AS `updated`,
+ `tt`.`stateFk` AS `stateFk`,
+ `tt`.`userFk` AS `workerFk`,
+ `tls`.`ticketFk` AS `ticketFk`,
+ `s`.`id` AS `state`,
+ `s`.`order` AS `productionOrder`,
+ `s`.`alertLevel` AS `alertLevel`,
+ `s`.`code` AS `code`,
+ `tls`.`ticketFk` AS `ticket`,
+ `tt`.`userFk` AS `worker`,
+ `s`.`isPreviousPreparable` AS `isPreviousPreparable`,
+ `s`.`isPicked` AS `isPicked`
+FROM (
+ (
+ `ticketLastState` `tls`
+ JOIN `ticketTracking` `tt` ON(`tt`.`id` = `tls`.`ticketTrackingFk`)
+ )
+ JOIN `state` `s` ON(`s`.`id` = `tt`.`stateFk`)
+ );
+
+CREATE OR REPLACE DEFINER=`root`@`localhost`
+ SQL SECURITY DEFINER
+ VIEW `vn2008`.`v_inter`
+AS SELECT `tt`.`id` AS `inter_id`,
+ `tt`.`stateFk` AS `state_id`,
+ `tt`.`notes` AS `nota`,
+ `tt`.`created` AS `odbc_date`,
+ `tt`.`ticketFk` AS `Id_Ticket`,
+ `tt`.`userFk` AS `Id_Trabajador`,
+ `tt`.`supervisorFk` AS `Id_supervisor`
+FROM `vn`.`ticketTracking` `tt`;
+
+CREATE OR REPLACE DEFINER=`root`@`localhost`
+ SQL SECURITY DEFINER
+ VIEW `ticketStateToday`
+AS SELECT
+ `ts`.`ticket` AS `ticket`,
+ `ts`.`state` AS `state`,
+ `ts`.`productionOrder` AS `productionOrder`,
+ `ts`.`alertLevel` AS `alertLevel`,
+ `ts`.`worker` AS `worker`,
+ `ts`.`code` AS `code`,
+ `ts`.`updated` AS `updated`,
+ `ts`.`isPicked` AS `isPicked`
+FROM
+ (`ticketState` `ts`
+JOIN `ticket` `t` ON
+ (`t`.`id` = `ts`.`ticket`))
+WHERE
+ `t`.`shipped` BETWEEN `util`.`VN_CURDATE`() AND `MIDNIGHT`(`util`.`VN_CURDATE`());
diff --git a/db/changes/240002/00-silexToSalix.sql b/db/changes/240002/00-silexToSalix.sql
new file mode 100644
index 0000000000..bad430ac24
--- /dev/null
+++ b/db/changes/240002/00-silexToSalix.sql
@@ -0,0 +1,46 @@
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`delivery_beforeInsert`
+ BEFORE INSERT ON `delivery`
+ FOR EACH ROW
+BEGIN
+
+ IF (NEW.longitude IS NOT NULL AND NEW.latitude IS NOT NULL AND NEW.ticketFK IS NOT NULL)
+ THEN
+ UPDATE address
+ SET longitude = NEW.longitude,
+ latitude = NEW.latitude
+ WHERE id IN (
+ SELECT addressFK
+ FROM ticket
+ WHERE id = NEW.ticketFk
+ );
+ END IF;
+
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`delivery_beforeUpdate`
+ BEFORE UPDATE ON `delivery`
+ FOR EACH ROW
+BEGIN
+
+IF (NEW.longitude IS NOT NULL AND NEW.latitude IS NOT NULL AND NEW.ticketFK IS NOT NULL)
+ THEN
+ UPDATE address
+ SET longitude = NEW.longitude,
+ latitude = NEW.latitude
+ WHERE id IN (
+ SELECT addressFK
+ FROM ticket
+ WHERE id = NEW.ticketFk
+ );
+ END IF;
+
+END$$
+DELIMITER ;
+
+
+ALTER TABLE `vn`.`address` MODIFY COLUMN longitude decimal(11,7) DEFAULT NULL NULL COMMENT 'Indica la última longitud proporcionada por tabla delivery';
+ALTER TABLE `vn`.`address` MODIFY COLUMN latitude decimal(11,7) DEFAULT NULL NULL COMMENT 'Indica la última latitud proporcionada por tabla delivery';
diff --git a/db/changes/240201/.gitkeep b/db/changes/240201/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/db/changes/240201/00-aclsMails.sql b/db/changes/240201/00-aclsMails.sql
new file mode 100644
index 0000000000..5cfea40301
--- /dev/null
+++ b/db/changes/240201/00-aclsMails.sql
@@ -0,0 +1,8 @@
+-- Definición de la tabla mailAliasACL
+
+CREATE OR REPLACE TABLE `account`.`mailAliasAcl` (
+ `mailAliasFk` int(10) unsigned NOT NULL,
+ `roleFk` int(10) unsigned NOT NULL,
+ FOREIGN KEY (`mailAliasFk`) REFERENCES `account`.`mailAlias` (`id`),
+ FOREIGN KEY (`roleFk`) REFERENCES `account`.`role` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
diff --git a/db/changes/240201/00-fixInvoiceCorrectionConstraintsName.sql b/db/changes/240201/00-fixInvoiceCorrectionConstraintsName.sql
new file mode 100644
index 0000000000..426afea900
--- /dev/null
+++ b/db/changes/240201/00-fixInvoiceCorrectionConstraintsName.sql
@@ -0,0 +1,7 @@
+ALTER TABLE `vn`.`invoiceCorrection` DROP FOREIGN KEY `cplusInvoiceTyoeFk`;
+ALTER TABLE `vn`.`invoiceCorrection` DROP FOREIGN KEY `invoiceCorrectionType_Fk33`;
+ALTER TABLE `vn`.`invoiceCorrection` DROP FOREIGN KEY `invoiceCorrection_ibfk_1`;
+
+ALTER TABLE `vn`.`invoiceCorrection` ADD CONSTRAINT `siiTypeInvoiceOut_FK` FOREIGN KEY (`siiTypeInvoiceOutFk`) REFERENCES `vn`.`siiTypeInvoiceOut`(id) ON UPDATE CASCADE;
+ALTER TABLE `vn`.`invoiceCorrection` ADD CONSTRAINT `invoiceCorrectionType_FK` FOREIGN KEY (`invoiceCorrectionTypeFk`) REFERENCES `vn`.`invoiceCorrectionType`(id) ON UPDATE CASCADE;
+ALTER TABLE `vn`.`invoiceCorrection` ADD CONSTRAINT `cplusRectificationType_FK` FOREIGN KEY (`cplusRectificationTypeFk`) REFERENCES `vn`.`cplusRectificationType`(id) ON UPDATE CASCADE;
diff --git a/db/changes/240201/00-getTaxBases.sql b/db/changes/240201/00-getTaxBases.sql
new file mode 100644
index 0000000000..8bd1b745ac
--- /dev/null
+++ b/db/changes/240201/00-getTaxBases.sql
@@ -0,0 +1,33 @@
+DELIMITER $$
+$$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`getTaxBases`()
+BEGIN
+/**
+* Calcula y devuelve en número de bases imponibles postivas y negativas
+* Requiere la tabla temporal tmp.ticketToInvoice(id)
+*
+* returns tmp.taxBases
+*/
+
+ CREATE OR REPLACE TEMPORARY TABLE tmp.ticket
+ (KEY (ticketFk))
+ ENGINE = MEMORY
+ SELECT id ticketFk
+ FROM tmp.ticketToInvoice;
+
+ CALL ticket_getTax(NULL);
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.taxBases;
+ CREATE TEMPORARY TABLE tmp.taxBases
+ ENGINE = MEMORY
+ SELECT
+ SUM(taxableBase > 0) as positive,
+ SUM(taxableBase < 0) as negative
+ FROM(
+ SELECT SUM(taxableBase) taxableBase
+ FROM tmp.ticketTax
+ GROUP BY pgcFk
+ ) t;
+
+END$$
+DELIMITER ;
diff --git a/db/changes/240201/00-tabletDocuware.sql b/db/changes/240201/00-tabletDocuware.sql
new file mode 100644
index 0000000000..ffa0226b33
--- /dev/null
+++ b/db/changes/240201/00-tabletDocuware.sql
@@ -0,0 +1,10 @@
+-- vn.docuwareTablet definition
+
+CREATE TABLE `vn`.`docuwareTablet` (
+ `tablet` varchar(100) NOT NULL PRIMARY KEY,
+ `description` varchar(255) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
+
+ALTER TABLE `vn`.`userConfig`
+ADD COLUMN tabletFk varchar(100) DEFAULT NULL,
+ADD FOREIGN KEY (tabletFk) REFERENCES `vn`.`docuwareTablet`(tablet);
diff --git a/db/changes/240201/00-ticketSmsToClientSms.sql b/db/changes/240201/00-ticketSmsToClientSms.sql
new file mode 100644
index 0000000000..cd3cf7dd3a
--- /dev/null
+++ b/db/changes/240201/00-ticketSmsToClientSms.sql
@@ -0,0 +1,9 @@
+ALTER TABLE `vn`.`clientSms` ADD `ticketFk` int(11) NULL;
+ALTER TABLE `vn`.`clientSms` ADD CONSTRAINT `clientSms_FK_2` FOREIGN KEY (`ticketFk`) REFERENCES `vn`.`ticket`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
+
+INSERT INTO`vn`.`clientSms` (`clientFk`, `smsFk`, `ticketFk`)
+ SELECT `t`.`clientFk`, `s`.`smsFk`, `s`.`ticketFk`
+ FROM `vn`.`clientSms` `s`
+ JOIN `vn`.`ticket` `t` ON `t`.`id` = `s`.`ticketFk`;
+
+RENAME TABLE `vn`.`ticketSms` TO `vn`.`ticketSms__`;
diff --git a/db/changes/240201/00-timecontrol.sql b/db/changes/240201/00-timecontrol.sql
new file mode 100644
index 0000000000..c3ddf5d96c
--- /dev/null
+++ b/db/changes/240201/00-timecontrol.sql
@@ -0,0 +1,17 @@
+DELETE FROM `salix`.`ACL`
+ WHERE model = 'VnUser'
+ AND property = 'renewToken';
+
+INSERT INTO `account`.`role` (name, description)
+ VALUES ('timeControl','Tablet para fichar');
+
+INSERT INTO `account`.`roleInherit` (role, inheritsFrom)
+ VALUES (127, 11);
+
+INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
+ VALUES
+ ('WorkerTimeControl', 'login', 'READ', 'ALLOW', 'ROLE', 'timeControl'),
+ ('WorkerTimeControl', 'getClockIn', 'READ', 'ALLOW', 'ROLE', 'timeControl'),
+ ('WorkerTimeControl', 'clockIn', 'WRITE', 'ALLOW', 'ROLE', 'timeControl');
+
+CALL `account`.`role_sync`();
diff --git a/db/changes/240201/00-truncateWhereSignInLog.sql b/db/changes/240201/00-truncateWhereSignInLog.sql
new file mode 100644
index 0000000000..db5d8e25dd
--- /dev/null
+++ b/db/changes/240201/00-truncateWhereSignInLog.sql
@@ -0,0 +1,2 @@
+DELETE FROM `account`.`signInLog` where owner <> FALSE;
+
diff --git a/db/changes/240201/01-functions.sql b/db/changes/240201/01-functions.sql
new file mode 100644
index 0000000000..7bbe1f442c
--- /dev/null
+++ b/db/changes/240201/01-functions.sql
@@ -0,0 +1,81 @@
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `vn`.`ticketPositionInPath`(vTicketId INT)
+ RETURNS varchar(10) CHARSET utf8mb3 COLLATE utf8mb3_general_ci
+ DETERMINISTIC
+BEGIN
+
+ DECLARE vRestTicketsMaxOrder INT;
+ DECLARE vRestTicketsMinOrder INT;
+ DECLARE vRestTicketsPacking INT;
+ DECLARE vMyProductionOrder INT;
+ DECLARE vPosition VARCHAR(10) DEFAULT 'MID';
+ DECLARE vMyPath INT;
+ DECLARE vMyWarehouse INT;
+ DECLARE PACKING_ORDER INT;
+ DECLARE vExpeditionsCount INT;
+ DECLARE vIsValenciaPath BOOLEAN DEFAULT FALSE;
+
+
+
+SELECT `order`
+ INTO PACKING_ORDER
+ FROM state
+ WHERE code = 'PACKING';
+
+SELECT t.routeFk, t.warehouseFk, IFNULL(ts.productionOrder,0)
+ INTO vMyPath, vMyWarehouse, vMyProductionOrder
+ FROM ticket t
+ LEFT JOIN ticketState ts on ts.ticketFk = t.id
+ WHERE t.id = vTicketId;
+
+SELECT (ag.`name` = 'VN_VALENCIA')
+ INTO vIsValenciaPath
+ FROM vn2008.Rutas r
+ JOIN vn2008.Agencias a on a.Id_Agencia = r.Id_Agencia
+ JOIN vn2008.agency ag on ag.agency_id = a.agency_id
+ WHERE r.Id_Ruta = vMyPath;
+
+IF vIsValenciaPath THEN -- Rutas Valencia
+
+ SELECT COUNT(*)
+ INTO vExpeditionsCount
+ FROM expedition e
+ JOIN ticket t ON t.id = e.ticketFk
+ WHERE t.routeFk = vMyPath;
+
+ SELECT MAX(ts.productionOrder), MIN(ts.productionOrder)
+ INTO vRestTicketsMaxOrder, vRestTicketsMinOrder
+ FROM ticket t
+ LEFT JOIN ticketState ts on t.id = ts.ticketFk
+ WHERE t.routeFk = vMyPath
+ AND t.warehouseFk = vMyWarehouse
+ AND t.id != vTicketid;
+
+ SELECT COUNT(*)
+ INTO vRestTicketsPacking
+ FROM ticket t
+ LEFT JOIN ticketState ts on t.id = ts.ticketFk
+ WHERE ts.productionOrder = PACKING_ORDER
+ AND t.routeFk = vMyPath
+ AND t.warehouseFk = vMyWarehouse
+ AND t.id != vTicketid;
+
+ IF vExpeditionsCount = 1 THEN
+ SET vPosition = 'FIRST';
+ ELSEIF vRestTicketsMinOrder > PACKING_ORDER THEN
+ SET vPosition = 'LAST';
+ ELSEIF vRestTicketsPacking THEN
+ SET vPosition = 'SHARED';
+ ELSE
+ SET vPosition = 'MID';
+ END IF;
+
+ELSE
+ SET vPosition = 'MID';
+
+END IF;
+
+RETURN vPosition;
+
+END$$
+DELIMITER ;
diff --git a/db/changes/240201/01-newHasAnyPositiveBase.sql b/db/changes/240201/01-newHasAnyPositiveBase.sql
new file mode 100644
index 0000000000..c4edfaed0e
--- /dev/null
+++ b/db/changes/240201/01-newHasAnyPositiveBase.sql
@@ -0,0 +1,30 @@
+DELIMITER $$
+$$
+CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `vn`.`hasAnyPositiveBase`() RETURNS tinyint(1)
+ DETERMINISTIC
+BEGIN
+
+/**
+* Calcula si existe alguna base imponible positiva
+* Requiere la tabla temporal tmp.ticketToInvoice(id) para getTaxBases()
+*
+* returns BOOLEAN
+*/
+
+ DECLARE hasAnyPositiveBase BOOLEAN;
+
+ CALL getTaxBases();
+
+ SELECT positive INTO hasAnyPositiveBase
+ FROM tmp.taxBases
+ LIMIT 1;
+
+ DROP TEMPORARY TABLE
+ tmp.ticketTax,
+ tmp.ticket,
+ tmp.taxBases;
+
+ RETURN hasAnyPositiveBase;
+
+END$$
+DELIMITER ;
diff --git a/db/changes/240201/01-procedures.sql b/db/changes/240201/01-procedures.sql
new file mode 100644
index 0000000000..ab52dbd1b4
--- /dev/null
+++ b/db/changes/240201/01-procedures.sql
@@ -0,0 +1,1788 @@
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `hedera`.`item_getVisible`(
+ vWarehouse TINYINT,
+ vDate DATE,
+ vType INT,
+ vPrefix VARCHAR(255))
+BEGIN
+
+/**
+ * Gets visible items of the specified type at specified date.
+ *
+ * @param vWarehouse The warehouse id
+ * @param vDate The visible date
+ * @param vType The type id
+ * @param vPrefix The article prefix to filter or %NULL for all
+ * @return tmp.itemVisible Visible items
+ */
+ DECLARE vPrefixLen SMALLINT;
+ DECLARE vFilter VARCHAR(255) DEFAULT NULL;
+ DECLARE vDateInv DATE DEFAULT vn.getInventoryDate();
+ DECLARE EXIT HANDLER FOR 1114
+ BEGIN
+ GET DIAGNOSTICS CONDITION 1
+ @message = MESSAGE_TEXT;
+ CALL vn.mail_insert(
+ 'cau@verdnatura.es',
+ NULL,
+ CONCAT('hedera.item_getVisible error: ', @message),
+ CONCAT(
+ 'warehouse: ', IFNULL(vWarehouse, ''),
+ ', Fecha:', IFNULL(vDate, ''),
+ ', tipo: ', IFNULL(vType,''),
+ ', prefijo: ', IFNULL(vPrefix,'')));
+ RESIGNAL;
+ END;
+ SET vPrefixLen = IFNULL(LENGTH(vPrefix), 0) + 1;
+
+ IF vPrefixLen > 1 THEN
+ SET vFilter = CONCAT(vPrefix, '%');
+ END IF;
+
+ DROP TEMPORARY TABLE IF EXISTS `filter`;
+ CREATE TEMPORARY TABLE `filter`
+ (INDEX (itemFk))
+ ENGINE = MEMORY
+ SELECT id itemFk FROM vn.item
+ WHERE typeFk = vType
+ AND (vFilter IS NULL OR `name` LIKE vFilter);
+
+ DROP TEMPORARY TABLE IF EXISTS currentStock;
+ CREATE TEMPORARY TABLE currentStock
+ (INDEX (itemFk))
+ ENGINE = MEMORY
+ SELECT itemFk, SUM(quantity) quantity
+ FROM (
+ SELECT b.itemFk, b.quantity
+ FROM vn.buy b
+ JOIN vn.entry e ON e.id = b.entryFk
+ JOIN vn.travel t ON t.id = e.travelFk
+ WHERE t.landed BETWEEN vDateInv AND vDate
+ AND t.warehouseInFk = vWarehouse
+ AND NOT e.isRaid
+ UNION ALL
+ SELECT b.itemFk, -b.quantity
+ FROM vn.buy b
+ JOIN vn.entry e ON e.id = b.entryFk
+ JOIN vn.travel t ON t.id = e.travelFk
+ WHERE t.shipped BETWEEN vDateInv AND util.VN_CURDATE()
+ AND t.warehouseOutFk = vWarehouse
+ AND NOT e.isRaid
+ AND t.isDelivered
+ UNION ALL
+ SELECT m.itemFk, -m.quantity
+ FROM vn.sale m
+ JOIN vn.ticket t ON t.id = m.ticketFk
+ JOIN vn.ticketState s ON s.ticketFk = t.id
+ WHERE t.shipped BETWEEN vDateInv AND util.VN_CURDATE()
+ AND t.warehouseFk = vWarehouse
+ AND s.alertLevel = 3
+ ) t
+ GROUP BY itemFk
+ HAVING quantity > 0;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp;
+ CREATE TEMPORARY TABLE tmp
+ (INDEX (itemFk))
+ ENGINE = MEMORY
+ SELECT *
+ FROM (
+ SELECT b.itemFk, b.packagingFk, b.packing
+ FROM vn.buy b
+ JOIN vn.entry e ON e.id = b.entryFk
+ JOIN vn.travel t ON t.id = e.travelFk
+ WHERE t.landed BETWEEN vDateInv AND vDate
+ AND NOT b.isIgnored
+ AND b.price2 >= 0
+ AND b.packagingFk IS NOT NULL
+ ORDER BY t.warehouseInFk = vWarehouse DESC, t.landed DESC
+ LIMIT 10000000000000000000
+ ) t GROUP BY itemFk;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.itemVisible;
+ CREATE TEMPORARY TABLE tmp.itemVisible
+ ENGINE = MEMORY
+ SELECT i.id Id_Article,
+ SUBSTRING(i.`name`, vPrefixLen) Article,
+ t.packing, p.id Id_Cubo,
+ IF(p.depth > 0, p.depth, 0) depth, p.width, p.height,
+ CEIL(s.quantity / t.packing) etiquetas
+ FROM vn.item i
+ JOIN `filter` f ON f.itemFk = i.id
+ JOIN currentStock s ON s.itemFk = i.id
+ LEFT JOIN tmp t ON t.itemFk = i.id
+ LEFT JOIN vn.packaging p ON p.id = t.packagingFk
+ WHERE CEIL(s.quantity / t.packing) > 0
+ -- FIXME: Column Cubos.box not included in view vn.packaging
+ /* AND p.box */ ;
+
+ DROP TEMPORARY TABLE
+ `filter`,
+ currentStock,
+ tmp;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `hedera`.`order_confirmWithUser`(vSelf INT, vUserId INT)
+BEGIN
+/**
+ * Confirms an order, creating each of its tickets on the corresponding
+ * date, store and user.
+ *
+ * @param vSelf The order identifier
+ * @param vUser The user identifier
+ */
+ DECLARE vOk BOOL;
+ DECLARE vDone BOOL DEFAULT FALSE;
+ DECLARE vWarehouse INT;
+ DECLARE vShipment DATE;
+ DECLARE vTicket INT;
+ DECLARE vNotes VARCHAR(255);
+ DECLARE vItem INT;
+ DECLARE vConcept VARCHAR(30);
+ DECLARE vAmount INT;
+ DECLARE vPrice DECIMAL(10,2);
+ DECLARE vSale INT;
+ DECLARE vRate INT;
+ DECLARE vRowId INT;
+ DECLARE vPriceFixed DECIMAL(10,2);
+ DECLARE vDelivery DATE;
+ DECLARE vAddress INT;
+ DECLARE vIsConfirmed BOOL;
+ DECLARE vClientId INT;
+ DECLARE vCompanyId INT;
+ DECLARE vAgencyModeId INT;
+ DECLARE TICKET_FREE INT DEFAULT 2;
+ DECLARE vCalc INT;
+ DECLARE vIsLogifloraItem BOOL;
+ DECLARE vOldQuantity INT;
+ DECLARE vNewQuantity INT;
+ DECLARE vIsTaxDataChecked BOOL;
+
+ DECLARE cDates CURSOR FOR
+ SELECT zgs.shipped, r.warehouse_id
+ FROM `order` o
+ JOIN order_row r ON r.order_id = o.id
+ LEFT JOIN tmp.zoneGetShipped zgs ON zgs.warehouseFk = r.warehouse_id
+ WHERE o.id = vSelf AND r.amount != 0
+ GROUP BY r.warehouse_id;
+
+ DECLARE cRows CURSOR FOR
+ SELECT r.id, r.item_id, i.name, r.amount, r.price, r.rate, i.isFloramondo
+ FROM order_row r
+ JOIN vn.item i ON i.id = r.item_id
+ WHERE r.amount != 0
+ AND r.warehouse_id = vWarehouse
+ AND r.order_id = vSelf
+ ORDER BY r.rate DESC;
+
+ DECLARE CONTINUE HANDLER FOR NOT FOUND
+ SET vDone = TRUE;
+
+ DECLARE EXIT HANDLER FOR SQLEXCEPTION
+ BEGIN
+ ROLLBACK;
+ RESIGNAL;
+ END;
+
+ -- Carga los datos del pedido
+ SELECT o.date_send, o.address_id, o.note, a.clientFk,
+ o.company_id, o.agency_id, c.isTaxDataChecked
+ INTO vDelivery, vAddress, vNotes, vClientId,
+ vCompanyId, vAgencyModeId, vIsTaxDataChecked
+ FROM hedera.`order` o
+ JOIN vn.address a ON a.id = o.address_id
+ JOIN vn.client c ON c.id = a.clientFk
+ WHERE o.id = vSelf;
+
+ -- Verifica si el cliente tiene los datos comprobados
+ IF NOT vIsTaxDataChecked THEN
+ CALL util.throw ('clientNotVerified');
+ END IF;
+
+ -- Carga las fechas de salida de cada almacen
+ CALL vn.zone_getShipped (vDelivery, vAddress, vAgencyModeId, FALSE);
+
+ -- Trabajador que realiza la accion
+ IF vUserId IS NULL THEN
+ SELECT employeeFk INTO vUserId FROM orderConfig;
+ END IF;
+
+ START TRANSACTION;
+
+ CALL order_checkEditable(vSelf);
+
+ -- Check order is not empty
+
+ SELECT COUNT(*) > 0 INTO vOk
+ FROM order_row WHERE order_id = vSelf AND amount > 0;
+
+ IF NOT vOk THEN
+ CALL util.throw ('ORDER_EMPTY');
+ END IF;
+
+ -- Crea los tickets del pedido
+
+ OPEN cDates;
+
+ lDates:
+ LOOP
+ SET vTicket = NULL;
+ SET vDone = FALSE;
+ FETCH cDates INTO vShipment, vWarehouse;
+
+ IF vDone THEN
+ LEAVE lDates;
+ END IF;
+
+ -- Busca un ticket existente que coincida con los parametros
+ WITH tPrevia AS
+ (SELECT DISTINCT s.ticketFk
+ FROM vn.sale s
+ JOIN vn.saleGroupDetail sgd ON sgd.saleFk = s.id
+ JOIN vn.ticket t ON t.id = s.ticketFk
+ WHERE t.shipped BETWEEN vShipment AND util.dayend(vShipment)
+ )
+ SELECT t.id INTO vTicket
+ FROM vn.ticket t
+ LEFT JOIN tPrevia tp ON tp.ticketFk = t.id
+ LEFT JOIN vn.ticketState tls on tls.ticketFk = t.id
+ JOIN hedera.`order` o
+ ON o.address_id = t.addressFk
+ AND vWarehouse = t.warehouseFk
+ AND o.date_send = t.landed
+ AND DATE(t.shipped) = vShipment
+ WHERE o.id = vSelf
+ AND t.refFk IS NULL
+ AND tp.ticketFk IS NULL
+ AND IFNULL(tls.alertLevel,0) = 0
+ LIMIT 1;
+
+ -- Crea el ticket en el caso de no existir uno adecuado
+ IF vTicket IS NULL
+ THEN
+
+ SET vShipment = IFNULL(vShipment, util.VN_CURDATE());
+
+ CALL vn.ticket_add(
+ vClientId,
+ vShipment,
+ vWarehouse,
+ vCompanyId,
+ vAddress,
+ vAgencyModeId,
+ NULL,
+ vDelivery,
+ vUserId,
+ TRUE,
+ vTicket
+ );
+ ELSE
+ INSERT INTO vn.ticketTracking
+ SET ticketFk = vTicket,
+ workerFk = vUserId,
+ stateFk = TICKET_FREE;
+ END IF;
+
+ INSERT IGNORE INTO vn.orderTicket
+ SET orderFk = vSelf,
+ ticketFk = vTicket;
+
+ -- Añade las notas
+
+ IF vNotes IS NOT NULL AND vNotes != ''
+ THEN
+ INSERT INTO vn.ticketObservation SET
+ ticketFk = vTicket,
+ observationTypeFk = 4 /* salesperson */,
+ `description` = vNotes
+ ON DUPLICATE KEY UPDATE
+ `description` = CONCAT(VALUES(`description`),'. ', `description`);
+ END IF;
+
+ -- Añade los movimientos y sus componentes
+
+ OPEN cRows;
+
+ lRows: LOOP
+ SET vDone = FALSE;
+ FETCH cRows INTO vRowId, vItem, vConcept, vAmount, vPrice, vRate, vIsLogifloraItem;
+
+ IF vDone THEN
+ LEAVE lRows;
+ END IF;
+
+ SET vSale = NULL;
+
+ SELECT s.id, s.quantity INTO vSale, vOldQuantity
+ FROM vn.sale s
+ WHERE ticketFk = vTicket
+ AND price = vPrice
+ AND itemFk = vItem
+ AND discount = 0
+ LIMIT 1;
+
+ IF vSale THEN
+ UPDATE vn.sale
+ SET quantity = quantity + vAmount,
+ originalQuantity = quantity
+ WHERE id = vSale;
+
+ SELECT s.quantity INTO vNewQuantity
+ FROM vn.sale s
+ WHERE id = vSale;
+ ELSE
+ -- Obtiene el coste
+ SELECT SUM(rc.`price`) valueSum INTO vPriceFixed
+ FROM orderRowComponent rc
+ JOIN vn.component c ON c.id = rc.componentFk
+ JOIN vn.componentType ct ON ct.id = c.typeFk AND ct.isBase
+ WHERE rc.rowFk = vRowId;
+
+ INSERT INTO vn.sale
+ SET itemFk = vItem,
+ ticketFk = vTicket,
+ concept = vConcept,
+ quantity = vAmount,
+ price = vPrice,
+ priceFixed = vPriceFixed,
+ isPriceFixed = TRUE;
+
+ SET vSale = LAST_INSERT_ID();
+
+ INSERT INTO vn.saleComponent
+ (saleFk, componentFk, `value`)
+ SELECT vSale, rc.componentFk, rc.price
+ FROM orderRowComponent rc
+ JOIN vn.component c ON c.id = rc.componentFk
+ WHERE rc.rowFk = vRowId
+ GROUP BY vSale, rc.componentFk;
+ END IF;
+
+ UPDATE order_row SET Id_Movimiento = vSale
+ WHERE id = vRowId;
+
+ -- Inserta en putOrder si la compra es de Floramondo
+ IF vIsLogifloraItem THEN
+ CALL cache.availableNoRaids_refresh(vCalc,FALSE,vWarehouse,vShipment);
+
+ SET @available := 0;
+
+ SELECT GREATEST(0,available) INTO @available
+ FROM cache.availableNoRaids
+ WHERE calc_id = vCalc
+ AND item_id = vItem;
+
+ UPDATE cache.availableNoRaids
+ SET available = GREATEST(0,available - vAmount)
+ WHERE item_id = vItem
+ AND calc_id = vCalc;
+
+ INSERT INTO edi.putOrder (
+ deliveryInformationID,
+ supplyResponseId,
+ quantity ,
+ EndUserPartyId,
+ EndUserPartyGLN,
+ FHAdminNumber,
+ saleFk
+ )
+ SELECT di.ID,
+ i.supplyResponseFk,
+ CEIL((vAmount - @available)/ sr.NumberOfItemsPerCask),
+ o.address_id ,
+ vClientId,
+ IFNULL(ca.fhAdminNumber, fhc.defaultAdminNumber),
+ vSale
+ FROM edi.deliveryInformation di
+ JOIN vn.item i ON i.supplyResponseFk = di.supplyResponseID
+ JOIN edi.supplyResponse sr ON sr.ID = i.supplyResponseFk
+ LEFT JOIN edi.clientFHAdminNumber ca ON ca.clientFk = vClientId
+ JOIN edi.floraHollandConfig fhc
+ JOIN hedera.`order` o ON o.id = vSelf
+ WHERE i.id = vItem
+ AND di.LatestOrderDateTime > util.VN_NOW()
+ AND vAmount > @available
+ LIMIT 1;
+ END IF;
+ END LOOP;
+
+ CLOSE cRows;
+ END LOOP;
+
+ CLOSE cDates;
+
+ UPDATE `order` SET confirmed = TRUE, confirm_date = util.VN_NOW()
+ WHERE id = vSelf;
+
+ COMMIT;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`invoiceOut_new`(
+ vSerial VARCHAR(255),
+ vInvoiceDate DATE,
+ vTaxArea VARCHAR(25),
+ OUT vNewInvoiceId INT)
+BEGIN
+/**
+ * Creación de facturas emitidas.
+ * requiere previamente tabla tmp.ticketToInvoice(id).
+ *
+ * @param vSerial serie a la cual se hace la factura
+ * @param vInvoiceDate fecha de la factura
+ * @param vTaxArea tipo de iva en relacion a la empresa y al cliente
+ * @param vNewInvoiceId id de la factura que se acaba de generar
+ * @return vNewInvoiceId
+ */
+ DECLARE vIsAnySaleToInvoice BOOL;
+ DECLARE vIsAnyServiceToInvoice BOOL;
+ DECLARE vNewRef VARCHAR(255);
+ DECLARE vWorker INT DEFAULT account.myUser_getId();
+ DECLARE vCompanyFk INT;
+ DECLARE vInterCompanyFk INT;
+ DECLARE vClientFk INT;
+ DECLARE vCplusStandardInvoiceTypeFk INT DEFAULT 1;
+ DECLARE vCplusCorrectingInvoiceTypeFk INT DEFAULT 6;
+ DECLARE vCplusSimplifiedInvoiceTypeFk INT DEFAULT 2;
+ DECLARE vCorrectingSerial VARCHAR(1) DEFAULT 'R';
+ DECLARE vSimplifiedSerial VARCHAR(1) DEFAULT 'S';
+ DECLARE vNewInvoiceInFk INT;
+ DECLARE vIsInterCompany BOOL DEFAULT FALSE;
+ DECLARE vIsCEESerial BOOL DEFAULT FALSE;
+ DECLARE vIsCorrectInvoiceDate BOOL;
+ DECLARE vMaxShipped DATE;
+ DECLARE vDone BOOL;
+ DECLARE vTicketFk INT;
+ DECLARE vCursor CURSOR FOR
+ SELECT id
+ FROM tmp.ticketToInvoice;
+
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
+
+ SET vInvoiceDate = IFNULL(vInvoiceDate, util.VN_CURDATE());
+
+ SELECT t.clientFk,
+ t.companyFk,
+ MAX(DATE(t.shipped)),
+ DATE(vInvoiceDate) >= invoiceOut_getMaxIssued(
+ vSerial,
+ t.companyFk,
+ YEAR(vInvoiceDate))
+ INTO vClientFk,
+ vCompanyFk,
+ vMaxShipped,
+ vIsCorrectInvoiceDate
+ FROM tmp.ticketToInvoice tt
+ JOIN ticket t ON t.id = tt.id;
+
+ IF(vMaxShipped > vInvoiceDate) THEN
+ CALL util.throw("Invoice date can't be less than max date");
+ END IF;
+
+ IF NOT vIsCorrectInvoiceDate THEN
+ CALL util.throw('Exists an invoice with a previous date');
+ END IF;
+
+ -- Eliminem de tmp.ticketToInvoice els tickets que no han de ser facturats
+ DELETE ti.*
+ FROM tmp.ticketToInvoice ti
+ JOIN ticket t ON t.id = ti.id
+ JOIN sale s ON s.ticketFk = t.id
+ JOIN item i ON i.id = s.itemFk
+ JOIN supplier su ON su.id = t.companyFk
+ JOIN client c ON c.id = t.clientFk
+ LEFT JOIN itemTaxCountry itc ON itc.itemFk = i.id AND itc.countryFk = su.countryFk
+ WHERE (YEAR(t.shipped) < 2001 AND t.isDeleted)
+ OR c.isTaxDataChecked = FALSE
+ OR t.isDeleted
+ OR c.hasToInvoice = FALSE
+ OR itc.id IS NULL;
+
+ SELECT SUM(s.quantity * s.price * (100 - s.discount)/100) <> 0
+ INTO vIsAnySaleToInvoice
+ FROM tmp.ticketToInvoice t
+ JOIN sale s ON s.ticketFk = t.id;
+
+ SELECT COUNT(*) > 0 INTO vIsAnyServiceToInvoice
+ FROM tmp.ticketToInvoice t
+ JOIN ticketService ts ON ts.ticketFk = t.id;
+
+ IF (vIsAnySaleToInvoice OR vIsAnyServiceToInvoice)
+ AND (vCorrectingSerial = vSerial OR NOT hasAnyNegativeBase())
+ THEN
+
+ -- el trigger añade el siguiente Id_Factura correspondiente a la vSerial
+ INSERT INTO invoiceOut(
+ ref,
+ serial,
+ issued,
+ clientFk,
+ dued,
+ companyFk,
+ siiTypeInvoiceOutFk
+ )
+ SELECT
+ 1,
+ vSerial,
+ vInvoiceDate,
+ vClientFk,
+ getDueDate(vInvoiceDate, dueDay),
+ vCompanyFk,
+ IF(vSerial = vCorrectingSerial,
+ vCplusCorrectingInvoiceTypeFk,
+ IF(vSerial = vSimplifiedSerial,
+ vCplusSimplifiedInvoiceTypeFk,
+ vCplusStandardInvoiceTypeFk))
+ FROM client
+ WHERE id = vClientFk;
+
+ SET vNewInvoiceId = LAST_INSERT_ID();
+
+ SELECT `ref`
+ INTO vNewRef
+ FROM invoiceOut
+ WHERE id = vNewInvoiceId;
+
+ OPEN vCursor;
+ l: LOOP
+ SET vDone = FALSE;
+ FETCH vCursor INTO vTicketFk;
+
+ IF vDone THEN
+ LEAVE l;
+ END IF;
+
+ CALL ticket_recalc(vTicketFk, vTaxArea);
+
+ END LOOP;
+ CLOSE vCursor;
+
+ UPDATE ticket t
+ JOIN tmp.ticketToInvoice ti ON ti.id = t.id
+ SET t.refFk = vNewRef;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.updateInter;
+ CREATE TEMPORARY TABLE tmp.updateInter ENGINE = MEMORY
+ SELECT s.id,ti.id ticket_id,vWorker Id_Trabajador
+ FROM tmp.ticketToInvoice ti
+ LEFT JOIN ticketState ts ON ti.id = ts.ticketFk
+ JOIN state s
+ WHERE IFNULL(ts.alertLevel,0) < 3 and s.`code` = getAlert3State(ti.id);
+
+ INSERT INTO ticketTracking(stateFk,ticketFk,userFk)
+ SELECT * FROM tmp.updateInter;
+
+ CALL invoiceExpenseMake(vNewInvoiceId);
+ CALL invoiceTaxMake(vNewInvoiceId,vTaxArea);
+
+ UPDATE invoiceOut io
+ JOIN (
+ SELECT SUM(amount) total
+ FROM invoiceOutExpense
+ WHERE invoiceOutFk = vNewInvoiceId
+ ) base
+ JOIN (
+ SELECT SUM(vat) total
+ FROM invoiceOutTax
+ WHERE invoiceOutFk = vNewInvoiceId
+ ) vat
+ SET io.amount = base.total + vat.total
+ WHERE io.id = vNewInvoiceId;
+
+ DROP TEMPORARY TABLE tmp.updateInter;
+
+ SELECT COUNT(*), id
+ INTO vIsInterCompany, vInterCompanyFk
+ FROM company
+ WHERE clientFk = vClientFk;
+
+ IF (vIsInterCompany) THEN
+
+ INSERT INTO invoiceIn(supplierFk, supplierRef, issued, companyFk)
+ SELECT vCompanyFk, vNewRef, vInvoiceDate, vInterCompanyFk;
+
+ SET vNewInvoiceInFk = LAST_INSERT_ID();
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.ticket;
+ CREATE TEMPORARY TABLE tmp.ticket
+ (KEY (ticketFk))
+ ENGINE = MEMORY
+ SELECT id ticketFk
+ FROM tmp.ticketToInvoice;
+
+ CALL `ticket_getTax`('NATIONAL');
+
+ SET @vTaxableBaseServices := 0.00;
+ SET @vTaxCodeGeneral := NULL;
+
+ INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenseFk, taxTypeSageFk, transactionTypeSageFk)
+ SELECT vNewInvoiceInFk,
+ @vTaxableBaseServices,
+ sub.expenseFk,
+ sub.taxTypeSageFk,
+ sub.transactionTypeSageFk
+ FROM (
+ SELECT @vTaxableBaseServices := SUM(tst.taxableBase) taxableBase,
+ i.expenseFk,
+ i.taxTypeSageFk,
+ i.transactionTypeSageFk,
+ @vTaxCodeGeneral := i.taxClassCodeFk
+ FROM tmp.ticketServiceTax tst
+ JOIN invoiceOutTaxConfig i ON i.taxClassCodeFk = tst.code
+ WHERE i.isService
+ HAVING taxableBase
+ ) sub;
+
+ INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenseFk, taxTypeSageFk, transactionTypeSageFk)
+ SELECT vNewInvoiceInFk,
+ SUM(tt.taxableBase) - IF(tt.code = @vTaxCodeGeneral,
+ @vTaxableBaseServices, 0) taxableBase,
+ i.expenseFk,
+ i.taxTypeSageFk ,
+ i.transactionTypeSageFk
+ FROM tmp.ticketTax tt
+ JOIN invoiceOutTaxConfig i ON i.taxClassCodeFk = tt.code
+ WHERE !i.isService
+ GROUP BY tt.pgcFk
+ HAVING taxableBase
+ ORDER BY tt.priority;
+
+ CALL invoiceInDueDay_calculate(vNewInvoiceInFk);
+
+ SELECT COUNT(*) INTO vIsCEESerial
+ FROM invoiceOutSerial
+ WHERE code = vSerial;
+
+ IF vIsCEESerial THEN
+
+ INSERT INTO invoiceInIntrastat (
+ invoiceInFk,
+ intrastatFk,
+ amount,
+ stems,
+ countryFk,
+ net)
+ SELECT
+ vNewInvoiceInFk,
+ i.intrastatFk,
+ SUM(CAST((s.quantity * s.price * (100 - s.discount) / 100 ) AS DECIMAL(10, 2))),
+ SUM(CAST(IFNULL(i.stems, 1) * s.quantity AS DECIMAL(10, 2))),
+ su.countryFk,
+ CAST(SUM(IFNULL(i.stems, 1)
+ * s.quantity
+ * IF(ic.grams, ic.grams, IFNULL(i.weightByPiece, 0)) / 1000) AS DECIMAL(10, 2))
+ FROM sale s
+ JOIN ticket t ON s.ticketFk = t.id
+ JOIN supplier su ON su.id = t.companyFk
+ JOIN item i ON i.id = s.itemFk
+ LEFT JOIN itemCost ic ON ic.itemFk = i.id AND ic.warehouseFk = t.warehouseFk
+ WHERE t.refFk = vNewRef
+ GROUP BY i.intrastatFk;
+
+ END IF;
+ DROP TEMPORARY TABLE tmp.ticket;
+ DROP TEMPORARY TABLE tmp.ticketAmount;
+ DROP TEMPORARY TABLE tmp.ticketTax;
+ DROP TEMPORARY TABLE tmp.ticketServiceTax;
+ END IF;
+ END IF;
+ DROP TEMPORARY TABLE `tmp`.`ticketToInvoice`;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`itemShelvingRadar`(vSectorFk INT)
+proc:BEGIN
+
+ DECLARE vCalcVisibleFk INT;
+ DECLARE vCalcAvailableFk INT;
+ DECLARE hasFatherSector BOOLEAN;
+ DECLARE vBuyerFk INT DEFAULT 0;
+ DECLARE vWarehouseFk INT DEFAULT 0;
+ DECLARE vSonSectorFk INT;
+ DECLARE vWorkerFk INT;
+
+ SELECT s.workerFk
+ INTO vWorkerFk
+ FROM vn.sector s
+ WHERE s.id = vSectorFk;
+
+ SELECT w.id, s.warehouseFk INTO vBuyerFk, vWarehouseFk
+ FROM vn.worker w
+ JOIN vn.sector s ON s.code = w.code
+ WHERE s.id = vSectorFk;
+
+ SELECT s.id INTO vSectorFk
+ FROM vn.sector s
+ WHERE s.warehouseFk = vWarehouseFk
+ AND s.isMain;
+
+ SELECT COUNT(*) INTO hasFatherSector
+ FROM vn.sector
+ WHERE sonFk = vSectorFk;
+
+ SELECT warehouseFk, sonFk INTO vWarehouseFk, vSonSectorFk
+ FROM vn.sector
+ WHERE id = vSectorFk;
+
+ CALL cache.visible_refresh(vCalcVisibleFk, TRUE, vWarehouseFk);
+ CALL cache.available_refresh(vCalcAvailableFk, FALSE, vWarehouseFk, util.VN_CURDATE());
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.itemShelvingRadar;
+
+ IF hasFatherSector THEN
+ CREATE TEMPORARY TABLE tmp.itemShelvingRadar
+ (PRIMARY KEY (itemFk))
+ ENGINE = MEMORY
+ SELECT *
+ FROM (
+ SELECT iss.itemFk,
+ i.longName,
+ i.size,
+ i.subName producer,
+ IFNULL(a.available,0) available,
+ SUM(IF(s.sonFk = vSectorFk, IFNULL(iss.visible,0), 0)) upstairs,
+ SUM(IF(iss.sectorFk = vSectorFk, IFNULL(iss.visible,0), 0)) downstairs,
+ IF(it.isPackaging, NULL, IFNULL(v.visible,0)) as visible,
+ vSectorFk sectorFk
+ FROM vn.itemShelvingStock iss
+ JOIN vn.sector s ON s.id = iss.sectorFk
+ JOIN vn.item i on i.id = iss.itemFk
+ JOIN vn.itemType it ON it.id = i.typeFk AND vBuyerFk IN (0,it.workerFk)
+ LEFT JOIN cache.available a ON a.item_id = iss.itemFk AND a.calc_id = vCalcAvailableFk
+ LEFT JOIN cache.visible v ON v.item_id = iss.itemFk AND v.calc_id = vCalcVisibleFk
+ WHERE vSectorFk IN (iss.sectorFk, s.sonFk)
+ GROUP BY iss.itemFk
+
+ UNION ALL
+
+ SELECT v.item_id,
+ i.longName,
+ i.size,
+ i.subName producer,
+ IFNULL(a.available,0) as available,
+ 0 upstairs,
+ 0 downstairs,
+ IF(it.isPackaging, NULL, v.visible) visible,
+ vSectorFk as sectorFk
+ FROM cache.visible v
+ JOIN vn.item i on i.id = v.item_id
+ JOIN vn.itemType it ON it.id = i.typeFk AND vBuyerFk IN (0,it.workerFk)
+ LEFT JOIN vn.itemShelvingStock iss ON iss.itemFk = v.item_id AND iss.warehouseFk = vWarehouseFk
+ LEFT JOIN cache.available a ON a.item_id = v.item_id AND a.calc_id = vCalcAvailableFk
+ WHERE v.calc_id = vCalcVisibleFk
+ AND iss.itemFk IS NULL
+ AND it.isInventory
+ ) sub GROUP BY itemFk;
+
+ SELECT ishr.*,
+ CAST(visible - upstairs - downstairs AS DECIMAL(10,0)) AS nicho,
+ CAST(downstairs - IFNULL(notPickedYed,0) AS DECIMAL(10,0)) as pendiente
+ FROM tmp.itemShelvingRadar ishr
+ JOIN vn.item i ON i.id = ishr.itemFk
+ LEFT JOIN (SELECT s.itemFk, sum(s.quantity) as notPickedYed
+ FROM vn.ticket t
+ JOIN vn.ticketStateToday tst ON tst.ticketFk = t.id
+ JOIN vn.sale s ON s.ticketFk = t.id
+ WHERE t.warehouseFk = vWarehouseFk
+ AND tst.alertLevel = 0
+ GROUP BY s.itemFk
+ ) sub ON sub.itemFk = ishr.itemFk
+ ORDER BY i.typeFk, i.longName;
+ ELSE
+ CREATE TEMPORARY TABLE tmp.itemShelvingRadar
+ (PRIMARY KEY (itemFk))
+ ENGINE = MEMORY
+ SELECT iss.itemFk,
+ 0 `hour`,
+ 0 `minute`,
+ '--' itemPlacementCode,
+ i.longName,
+ i.size,
+ i.subName producer,
+ i.upToDown,
+ IFNULL(a.available,0) available,
+ IFNULL(v.visible - iss.visible,0) dayEndVisible,
+ IFNULL(v.visible - iss.visible,0) firstNegative,
+ IFNULL(v.visible - iss.visible,0) itemPlacementVisible,
+ IFNULL(i.minimum * b.packing,0) itemPlacementSize,
+ ips.onTheWay,
+ iss.visible itemShelvingStock,
+ IFNULL(v.visible,0) visible,
+ b.isPickedOff,
+ iss.sectorFk
+ FROM vn.itemShelvingStock iss
+ JOIN vn.item i on i.id = iss.itemFk
+ LEFT JOIN cache.last_buy lb ON lb.item_id = iss.itemFk AND lb.warehouse_id = vWarehouseFk
+ LEFT JOIN vn.buy b ON b.id = lb.buy_id
+ LEFT JOIN cache.available a ON a.item_id = iss.itemFk AND a.calc_id = vCalcAvailableFk
+ LEFT JOIN cache.visible v ON v.item_id = iss.itemFk AND v.calc_id = vCalcVisibleFk
+ LEFT JOIN (SELECT itemFk, sum(saldo) as onTheWay
+ FROM vn.itemPlacementSupplyList
+ WHERE saldo > 0
+ GROUP BY itemFk
+ ) ips ON ips.itemFk = i.id
+ WHERE IFNULL(iss.sectorFk,0) IN (0, vSectorFk)
+ OR iss.sectorFk = vSectorFk;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.itemOutTime;
+ CREATE TEMPORARY TABLE tmp.itemOutTime
+ SELECT *,SUM(amount) quantity
+ FROM
+ (SELECT item_id itemFk,
+ amount,
+ IF(HOUR(t.shipped), HOUR(t.shipped), HOUR(z.`hour`)) as hours,
+ IF(MINUTE(t.shipped), MINUTE(t.shipped), MINUTE(z.`hour`)) as minutes
+ FROM vn2008.item_out io
+ JOIN tmp.itemShelvingRadar isr ON isr.itemFk = io.item_id
+ JOIN vn.ticket t on t.id= io.ticketFk
+ JOIN vn.ticketState ts on ts.ticketFk = io.ticketFk
+ JOIN vn.state s ON s.id = ts.stateFk
+ LEFT JOIN vn.zone z ON z.id = t.zoneFk
+ LEFT JOIN (SELECT DISTINCT saleFk
+ FROM vn.saleTracking st
+ WHERE st.created > util.VN_CURDATE()
+ AND st.isChecked
+ ) stPrevious ON `stPrevious`.`saleFk` = io.saleFk
+ WHERE t.warehouseFk = vWarehouseFk
+ AND s.isPicked = 0
+ AND NOT io.Reservado
+ AND stPrevious.saleFk IS NULL
+ AND io.dat >= util.VN_CURDATE()
+ AND io.dat < util.VN_CURDATE() + INTERVAL 1 DAY
+ ) sub
+ GROUP BY itemFk, hours, minutes;
+
+ INSERT INTO tmp.itemShelvingRadar (itemFk)
+ SELECT itemFk FROM tmp.itemOutTime
+ ON DUPLICATE KEY UPDATE dayEndVisible = dayEndVisible + quantity,
+ firstNegative = if (firstNegative < 0, firstNegative, firstNegative + quantity),
+ `hour` = ifnull(if (firstNegative > 0 , `hour`, hours),0),
+ `minute` = ifnull(if (firstNegative > 0, `minute`, minutes),0);
+
+ UPDATE tmp.itemShelvingRadar isr
+ JOIN (SELECT s.itemFk, sum(s.quantity) amount
+ FROM sale s
+ JOIN ticket t ON t.id = s.ticketFk
+ JOIN ticketLastState tls ON tls.ticketFk = t.id
+ WHERE t.shipped BETWEEN util.VN_CURDATE() AND util.dayend(util.VN_CURDATE())
+ AND tls.name = 'Prep Camara'
+ GROUP BY s.itemFk) sub ON sub.itemFk = isr.itemFk
+ SET isr.dayEndVisible = dayEndVisible + sub.amount,
+ firstNegative = firstNegative + sub.amount;
+
+ SELECT * FROM tmp.itemShelvingRadar;
+ END IF;
+
+ DROP TEMPORARY TABLE tmp.itemShelvingRadar;
+
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`item_getBalance`(
+ vItemFk INT,
+ vWarehouseFk INT,
+ vDate DATETIME
+)
+BEGIN
+/**
+ * @vItemFk item a buscar
+ * @vWarehouseFk almacen donde buscar
+ * @vDate Si la fecha es null, muestra el histórico desde el inventario.
+ * Si la fecha no es null, muestra histórico desde la fecha pasada.
+ */
+ DECLARE vDateInventory DATETIME;
+
+ IF vDate IS NULL THEN
+ SELECT inventoried INTO vDateInventory
+ FROM config;
+ ELSE
+ SELECT mockUtcTime INTO vDateInventory
+ FROM util.config;
+ END IF;
+
+ CREATE OR REPLACE TEMPORARY TABLE tItemDiary(
+ shipped DATE,
+ `in` INT(11),
+ `out` INT(11),
+ alertLevel INT(11),
+ stateName VARCHAR(20),
+ `name` VARCHAR(50),
+ reference VARCHAR(50),
+ origin INT(11),
+ clientFk INT(11),
+ isPicked INT(11),
+ isTicket TINYINT(1),
+ lineFk INT(11),
+ `order` TINYINT(3) UNSIGNED,
+ clientType VARCHAR(20),
+ claimFk INT(10) UNSIGNED,
+ inventorySupplierFk INT(10)
+ );
+
+ INSERT INTO tItemDiary
+ SELECT tr.landed shipped,
+ b.quantity `in`,
+ NULL `out`,
+ st.alertLevel ,
+ st.name stateName,
+ s.name `name`,
+ e.invoiceNumber reference,
+ e.id origin,
+ s.id clientFk,
+ IF(st.`code` = 'DELIVERED', TRUE, FALSE) isPicked,
+ FALSE isTicket,
+ b.id lineFk,
+ NULL `order`,
+ NULL clientType,
+ NULL claimFk,
+ ec.inventorySupplierFk
+ FROM buy b
+ JOIN entry e ON e.id = b.entryFk
+ JOIN travel tr ON tr.id = e.travelFk
+ JOIN supplier s ON s.id = e.supplierFk
+ JOIN state st ON st.`code` = IF( tr.landed < util.VN_CURDATE()
+ OR (util.VN_CURDATE() AND tr.isReceived),
+ 'DELIVERED',
+ 'FREE')
+ JOIN entryConfig ec
+ WHERE tr.landed >= vDateInventory
+ AND vWarehouseFk = tr.warehouseInFk
+ AND (s.id <> ec.inventorySupplierFk OR vDate IS NULL)
+ AND b.itemFk = vItemFk
+ AND e.isExcludedFromAvailable = FALSE
+ AND e.isRaid = FALSE
+ UNION ALL
+ SELECT tr.shipped,
+ NULL,
+ b.quantity,
+ st.alertLevel,
+ st.name,
+ s.name,
+ e.invoiceNumber,
+ e.id,
+ s.id,
+ IF(st.`code` = 'DELIVERED' , TRUE, FALSE),
+ FALSE,
+ b.id,
+ NULL,
+ NULL,
+ NULL,
+ ec.inventorySupplierFk
+ FROM buy b
+ JOIN entry e ON e.id = b.entryFk
+ JOIN travel tr ON tr.id = e.travelFk
+ JOIN warehouse w ON w.id = tr.warehouseOutFk
+ JOIN supplier s ON s.id = e.supplierFk
+ JOIN state st ON st.`code` = IF(tr.shipped < util.VN_CURDATE()
+ OR (tr.shipped = util.VN_CURDATE() AND tr.isReceived),
+ 'DELIVERED',
+ 'FREE')
+ JOIN entryConfig ec
+ WHERE tr.shipped >= vDateInventory
+ AND vWarehouseFk = tr.warehouseOutFk
+ AND (s.id <> ec.inventorySupplierFk OR vDate IS NULL)
+ AND b.itemFk = vItemFk
+ AND e.isExcludedFromAvailable = FALSE
+ AND w.isFeedStock = FALSE
+ AND e.isRaid = FALSE
+ UNION ALL
+ SELECT DATE(t.shipped),
+ NULL,
+ s.quantity,
+ st2.alertLevel,
+ st2.name,
+ t.nickname,
+ t.refFk,
+ t.id,
+ t.clientFk,
+ stk.id,
+ TRUE,
+ s.id,
+ st.`order`,
+ ct.`code`,
+ cb.claimFk,
+ NULL
+ FROM sale s
+ JOIN ticket t ON t.id = s.ticketFk
+ LEFT JOIN ticketState ts ON ts.ticketFk = t.id
+ LEFT JOIN state st ON st.`code` = ts.`code`
+ JOIN client c ON c.id = t.clientFk
+ JOIN clientType ct ON ct.id = c.clientTypeFk
+ JOIN state st2 ON st2.`code` = IF(t.shipped < util.VN_CURDATE(),
+ 'DELIVERED',
+ IF (t.shipped > util.dayEnd(util.VN_CURDATE()),
+ 'FREE',
+ IFNULL(ts.code, 'FREE')))
+ LEFT JOIN state stPrep ON stPrep.`code` = 'PREPARED'
+ LEFT JOIN saleTracking stk ON stk.saleFk = s.id
+ AND stk.stateFk = stPrep.id
+ LEFT JOIN claimBeginning cb ON s.id = cb.saleFk
+ WHERE t.shipped >= vDateInventory
+ AND s.itemFk = vItemFk
+ AND vWarehouseFk =t.warehouseFk
+ ORDER BY shipped,
+ (inventorySupplierFk = clientFk) DESC,
+ alertLevel DESC,
+ isTicket,
+ `order` DESC,
+ isPicked DESC,
+ `in` DESC,
+ `out` DESC;
+
+ IF vDate IS NULL THEN
+
+ SET @a := 0;
+ SET @currentLineFk := 0;
+ SET @shipped := '';
+
+ SELECT DATE(@shipped:= shipped) shipped,
+ alertLevel,
+ stateName,
+ origin,
+ reference,
+ clientFk,
+ name,
+ `in` invalue,
+ `out`,
+ @a := @a + IFNULL(`in`, 0) - IFNULL(`out`, 0) balance,
+ @currentLineFk := IF (@shipped < util.VN_CURDATE()
+ OR (@shipped = util.VN_CURDATE() AND (isPicked OR a.`code` >= 'ON_PREPARATION')),
+ lineFk,
+ @currentLineFk) lastPreparedLineFk,
+ isTicket,
+ lineFk,
+ isPicked,
+ clientType,
+ claimFk
+ FROM tItemDiary
+ LEFT JOIN alertLevel a ON a.id = tItemDiary.alertLevel;
+
+ ELSE
+ SELECT SUM(`in`) - SUM(`out`) INTO @a
+ FROM tItemDiary
+ WHERE shipped < vDate;
+
+ SELECT vDate shipped,
+ 0 alertLevel,
+ 0 stateName,
+ 0 origin,
+ '' reference,
+ 0 clientFk,
+ 'Inventario calculado',
+ @a invalue,
+ NULL `out`,
+ @a balance,
+ 0 lastPreparedLineFk,
+ 0 isTicket,
+ 0 lineFk,
+ 0 isPicked,
+ 0 clientType,
+ 0 claimFk
+ UNION ALL
+ SELECT shipped,
+ alertlevel,
+ stateName,
+ origin,
+ reference,
+ clientFk,
+ name, `in`,
+ `out`,
+ @a := @a + IFNULL(`in`, 0) - IFNULL(`out`, 0),
+ 0,
+ isTicket,
+ lineFk,
+ isPicked,
+ clientType,
+ claimFk
+ FROM tItemDiary
+ WHERE shipped >= vDate;
+ END IF;
+
+ DROP TEMPORARY TABLE tItemDiary;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`productionControl`(
+ vWarehouseFk INT,
+ vScopeDays INT
+)
+proc: BEGIN
+/**
+ * Devuelve un listado de tickets con parámetros relativos a la producción de los días en rango.
+ *
+ * @param vWarehouseFk Identificador de warehouse
+ * @param vScopeDays Número de días desde hoy en adelante que entran en el cálculo.
+ *
+ * @return Table tmp.productionBuffer
+ */
+ DECLARE vEndingDate DATETIME;
+ DECLARE vIsTodayRelative BOOLEAN;
+
+ SELECT util.dayEnd(util.VN_CURDATE()) + INTERVAL LEAST(vScopeDays, maxProductionScopeDays) DAY
+ INTO vEndingDate
+ FROM productionConfig;
+
+ SELECT isTodayRelative INTO vIsTodayRelative
+ FROM worker
+ WHERE id = getUser(); -- Cambiar por account.myUser_getId(), falta dar permisos
+
+ CALL prepareTicketList(util.yesterday(), vEndingDate);
+
+ CREATE OR REPLACE TEMPORARY TABLE tmp.ticket
+ SELECT * FROM tmp.productionTicket;
+
+ CALL prepareClientList();
+
+ CREATE OR REPLACE TEMPORARY TABLE tmp.sale_getProblems
+ (INDEX (ticketFk)) ENGINE = MEMORY
+ SELECT tt.ticketFk, tt.clientFk, t.warehouseFk, t.shipped
+ FROM tmp.productionTicket tt
+ JOIN ticket t ON t.id = tt.ticketFk;
+
+ CALL ticket_getProblems(vIsTodayRelative);
+
+ CREATE OR REPLACE TEMPORARY TABLE tmp.productionBuffer
+ (PRIMARY KEY(ticketFk), previaParking VARCHAR(255))
+ ENGINE = MEMORY
+ SELECT tt.ticketFk,
+ tt.clientFk,
+ t.warehouseFk,
+ t.nickname,
+ t.packages,
+ IF(HOUR(t.shipped), HOUR(t.shipped), COALESCE(HOUR(zc.hour),HOUR(z.hour))) HH,
+ COALESCE(HOUR(zc.hour), HOUR(z.hour)) Departure,
+ COALESCE(MINUTE(t.shipped), MINUTE(zc.hour), MINUTE(z.hour)) mm,
+ t.routeFk,
+ IF(dm.code = 'DELIVERY', z.`id`, 0) zona,
+ t.nickname addressNickname,
+ a.postalCode,
+ a.city,
+ p.name province,
+ CONCAT(z.`name`,' ',IFNULL(RIGHT(t.routeFk,3),'')) agency,
+ am.id agencyModeFk,
+ 0 `lines`,
+ CAST( 0 AS DECIMAL(5,2)) m3,
+ CAST( 0 AS DECIMAL(5,2)) preparationRate,
+ "" problem,
+ IFNULL(tls.state,2) state,
+ w.code workerCode,
+ DATE(t.shipped) shipped,
+ wk.code salesPersonCode,
+ p.id provinceFk,
+ tls.productionOrder,
+ IFNULL(tls.alertLevel, 0) alertLevel,
+ t.isBoxed palletized,
+ IF(rm.isPickingAllowed, rm.bufferFk, NULL) ubicacion,
+ tlu.lastUpdated,
+ IFNULL(st.graphCategory, 0) graphCategory,
+ pk.code parking,
+ 0 H,
+ 0 V,
+ 0 N,
+ st.isOk,
+ ag.isOwn,
+ rm.bufferFk
+ FROM tmp.productionTicket tt
+ JOIN ticket t ON tt.ticketFk = t.id
+ LEFT JOIN ticketStateToday tst ON tst.ticket = t.id
+ LEFT JOIN state st ON st.id = tst.state
+ LEFT JOIN client c ON c.id = t.clientFk
+ LEFT JOIN worker wk ON wk.id = c.salesPersonFk
+ JOIN address a ON a.id = t.addressFk
+ LEFT JOIN province p ON p.id = a.provinceFk
+ JOIN agencyMode am ON am.id = t.agencyModeFk
+ JOIN deliveryMethod dm ON dm.id = am.deliveryMethodFk
+ JOIN agency ag ON ag.id = am.agencyFk
+ LEFT JOIN ticketState tls ON tls.ticketFk = tt.ticketFk
+ LEFT JOIN ticketLastUpdated tlu ON tlu.ticketFk = tt.ticketFk
+ LEFT JOIN worker w ON w.id = tls.userFk
+ LEFT JOIN routesMonitor rm ON rm.routeFk = t.routeFk
+ LEFT JOIN `zone` z ON z.id = t.zoneFk
+ LEFT JOIN zoneClosure zc ON zc.zoneFk = t.zoneFk
+ AND DATE(t.shipped) = zc.dated
+ LEFT JOIN ticketParking tp ON tp.ticketFk = t.id
+ LEFT JOIN parking pk ON pk.id = tp.parkingFk
+ WHERE t.warehouseFk = vWarehouseFk
+ AND dm.code IN ('AGENCY', 'DELIVERY', 'PICKUP');
+
+ UPDATE tmp.productionBuffer pb
+ JOIN (
+ SELECT pb.ticketFk, GROUP_CONCAT(p.code) previaParking
+ FROM tmp.productionBuffer pb
+ JOIN sale s ON s.ticketFk = pb.ticketFk
+ JOIN saleGroupDetail sgd ON sgd.saleFk = s.id
+ JOIN saleGroup sg ON sg.id = sgd.saleGroupFk
+ JOIN parking p ON p.id = sg.parkingFk
+ GROUP BY pb.ticketFk
+ ) t ON t.ticketFk = pb.ticketFk
+ SET pb.previaParking = t.previaParking;
+
+ -- Problemas por ticket
+ ALTER TABLE tmp.productionBuffer
+ CHANGE COLUMN `problem` `problem` VARCHAR(255),
+ ADD COLUMN `collectionH` INT,
+ ADD COLUMN `collectionV` INT,
+ ADD COLUMN `collectionN` INT;
+
+ UPDATE tmp.productionBuffer pb
+ JOIN tmp.ticket_problems tp ON tp.ticketFk = pb.ticketFk
+ SET pb.problem = TRIM(CAST(CONCAT( IFNULL(tp.itemShortage, ''),
+ IFNULL(tp.itemDelay, ''),
+ IFNULL(tp.itemLost, ''),
+ IF(tp.isFreezed, ' CONGELADO',''),
+ IF(tp.hasHighRisk, ' RIESGO',''),
+ IF(tp.hasTicketRequest, ' COD 100',''),
+ IF(tp.isTaxDataChecked, '',' FICHA INCOMPLETA'),
+ IF(tp.hasComponentLack, ' COMPONENTES', ''),
+ IF(HOUR(util.VN_NOW()) < pb.HH AND tp.isTooLittle, ' PEQUEÑO', '')
+ ) AS char(255)));
+
+ -- Clientes Nuevos o Recuperados
+ UPDATE tmp.productionBuffer pb
+ LEFT JOIN bs.clientNewBorn cnb ON cnb.clientFk = pb.clientFk
+ JOIN productionConfig pc
+ SET pb.problem = TRIM(CAST(CONCAT('NUEVO ', pb.problem) AS CHAR(255)))
+ WHERE (cnb.clientFk IS NULL OR cnb.isRookie)
+ AND pc.rookieDays;
+
+ -- Líneas y volumen por ticket
+ UPDATE tmp.productionBuffer pb
+ JOIN (
+ SELECT tt.ticketFk,
+ COUNT(*) `lines`,
+ SUM(sv.volume) m3,
+ IFNULL(SUM(IF(sv.isPicked, sv.volume, 0)) / SUM(sv.volume), 0) rate
+ FROM tmp.productionTicket tt
+ JOIN saleVolume sv ON sv.ticketFk = tt.ticketFk
+ GROUP BY tt.ticketFk
+ ) m ON m.ticketFk = pb.ticketFk
+ SET pb.`lines` = m.`lines`,
+ pb.m3 = m.m3,
+ pb.preparationRate = m.rate;
+
+ DELETE FROM tmp.productionBuffer
+ WHERE NOT `lines`;
+
+ -- Lineas por linea de encajado
+ UPDATE tmp.productionBuffer pb
+ JOIN (
+ SELECT ticketFk,
+ SUM(sub.H) H,
+ SUM(sub.V) V,
+ SUM(sub.N) N
+ FROM (
+ SELECT t.ticketFk,
+ SUM(i.itemPackingTypeFk = 'H') H,
+ SUM(i.itemPackingTypeFk = 'V') V,
+ SUM(i.itemPackingTypeFk IS NULL) N
+ FROM tmp.productionTicket t
+ JOIN sale s ON s.ticketFk = t.ticketFk
+ JOIN item i ON i.id = s.itemFk
+ GROUP BY t.ticketFk, i.itemPackingTypeFk
+ ) sub
+ GROUP BY ticketFk
+ ) sub2 ON sub2.ticketFk = pb.ticketFk
+ SET pb.H = sub2.H,
+ pb.V = sub2.V,
+ pb.N = sub2.N;
+
+ -- Colecciones segun tipo de encajado
+ UPDATE tmp.productionBuffer pb
+ JOIN ticketCollection tc ON pb.ticketFk = tc.ticketFk
+ SET pb.collectionH = IF(pb.H, tc.collectionFk, NULL),
+ pb.collectionV = IF(pb.V, tc.collectionFk, NULL),
+ pb.collectionN = IF(pb.N, tc.collectionFk, NULL);
+
+ -- Previa pendiente
+ ALTER TABLE tmp.productionBuffer
+ ADD previousWithoutParking BOOL DEFAULT FALSE;
+
+ CREATE OR REPLACE TEMPORARY TABLE tmp.ticketWithPrevia
+ (ticketFk INT PRIMARY KEY,
+ salesCount INT DEFAULT 0,
+ salesInParkingCount INT DEFAULT 0)
+ ENGINE = MEMORY;
+
+ -- Insertamos todos los tickets que tienen productos parkineados
+ -- en sectores de previa, segun el sector
+ CREATE OR REPLACE TEMPORARY TABLE tItemShelvingStock
+ (PRIMARY KEY(itemFk, sectorFk))
+ ENGINE = MEMORY
+ SELECT ish.itemFk,
+ p.sectorFk,
+ sc.isPreviousPrepared,
+ sc.itemPackingTypeFk
+ FROM itemShelving ish
+ JOIN shelving sh ON sh.code = ish.shelvingFk
+ JOIN parking p ON p.id = sh.parkingFk
+ JOIN sector sc ON sc.id = p.sectorFk
+ WHERE p.sectorFk
+ AND ish.visible
+ GROUP BY ish.itemFk, p.sectorFk;
+
+ INSERT INTO tmp.ticketWithPrevia(ticketFk, salesCount)
+ SELECT pb.ticketFk, COUNT(DISTINCT s.id)
+ FROM tmp.productionBuffer pb
+ JOIN sale s ON s.ticketFk = pb.ticketFk
+ JOIN tItemShelvingStock iss ON iss.itemFk = s.itemFk
+ JOIN sector sc ON sc.id = iss.sectorFk
+ JOIN item i ON i.id = iss.itemFk
+ WHERE iss.isPreviousPrepared
+ AND (sc.itemPackingTypeFk IS NULL
+ OR (i.itemPackingTypeFk IS NULL AND NOT pb.V)
+ OR sc.itemPackingTypeFk = i.itemPackingTypeFk)
+ AND s.quantity > 0
+ GROUP BY pb.ticketFk;
+
+ -- Se calcula la cantidad de productos que estan ya preparados porque su saleGroup está aparcado
+ UPDATE tmp.ticketWithPrevia twp
+ JOIN (
+ SELECT pb.ticketFk, COUNT(DISTINCT s.id) salesInParkingCount
+ FROM tmp.productionBuffer pb
+ JOIN sale s ON s.ticketFk = pb.ticketFk
+ JOIN saleGroupDetail sgd ON sgd.saleFk = s.id
+ JOIN saleGroup sg ON sg.id = sgd.saleGroupFk
+ WHERE sg.parkingFk IS NOT NULL
+ AND s.quantity > 0
+ GROUP BY pb.ticketFk
+ ) sub ON twp.ticketFk = sub.ticketFk
+ SET twp.salesInParkingCount = sub.salesInParkingCount;
+
+ -- Marcamos como pendientes aquellos que no coinciden las cantidades
+ UPDATE tmp.productionBuffer pb
+ JOIN tmp.ticketWithPrevia twp ON twp.ticketFk = pb.ticketFk
+ SET pb.previousWithoutParking = TRUE
+ WHERE twp.salesCount > twp.salesInParkingCount;
+
+ DROP TEMPORARY TABLE
+ tmp.productionTicket,
+ tmp.ticket,
+ tmp.risk,
+ tmp.ticket_problems,
+ tmp.ticketWithPrevia,
+ tItemShelvingStock;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`expedition_beforeInsert`
+ BEFORE INSERT ON `expedition`
+ FOR EACH ROW
+BEGIN
+ DECLARE intcounter INT;
+ DECLARE vShipFk INT;
+
+ SET NEW.editorFk = account.myUser_getId();
+
+ IF NEW.freightItemFk IS NOT NULL THEN
+
+ UPDATE ticket SET packages = nz(packages) + 1 WHERE id = NEW.ticketFk;
+
+ SELECT IFNULL(MAX(counter),0) +1 INTO intcounter
+ FROM expedition e
+ INNER JOIN ticket t1 ON e.ticketFk = t1.id
+ LEFT JOIN ticketState ts ON ts.ticketFk = t1.id
+ INNER JOIN ticket t2 ON t2.addressFk = t1.addressFk AND DATE(t2.shipped) = DATE(t1.shipped)
+ AND t1.warehouseFk = t2.warehouseFk
+ WHERE t2.id = NEW.ticketFk AND ts.alertLevel < 3 AND t1.companyFk = t2.companyFk
+ AND t1.agencyModeFk = t2.agencyModeFk;
+
+ SET NEW.`counter` = intcounter;
+ END IF;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`sale_recalcComponent`(vOption INT)
+proc: BEGIN
+/**
+ * Este procedimiento recalcula los componentes de un conjunto de sales,
+ * eliminando los componentes existentes e insertandolos de nuevo
+ *
+ * @param vOption si no se quiere forzar llamar con NULL
+ * @table tmp.recalculateSales (id)
+ */
+ DECLARE vShipped DATE;
+ DECLARE vWarehouseFk SMALLINT;
+ DECLARE vAgencyModeFk INT;
+ DECLARE vAddressFk INT;
+ DECLARE vTicketFk INT;
+ DECLARE vLanded DATE;
+ DECLARE vIsEditable BOOLEAN;
+ DECLARE vZoneFk INTEGER;
+ DECLARE vDone BOOL DEFAULT FALSE;
+
+ DECLARE vCur CURSOR FOR
+ SELECT DISTINCT s.ticketFk
+ FROM tmp.recalculateSales rs
+ JOIN vn.sale s ON s.id = rs.id;
+
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
+
+ OPEN vCur;
+
+ l: LOOP
+ SET vDone = FALSE;
+ FETCH vCur INTO vTicketFk;
+
+ IF vDone THEN
+ LEAVE l;
+ END IF;
+
+ SELECT (hasToRecalcPrice OR ts.alertLevel IS NULL) AND t.refFk IS NULL,
+ t.zoneFk,
+ t.warehouseFk,
+ t.shipped,
+ t.addressFk,
+ t.agencyModeFk,
+ t.landed
+ INTO vIsEditable,
+ vZoneFk,
+ vWarehouseFk,
+ vShipped,
+ vAddressFk,
+ vAgencyModeFk,
+ vLanded
+ FROM ticket t
+ LEFT JOIN ticketState ts ON t.id = ts.ticketFk
+ LEFT JOIN alertLevel al ON al.id = ts.alertLevel
+ WHERE t.id = vTicketFk;
+
+ CALL zone_getLanded(vShipped, vAddressFk, vAgencyModeFk, vWarehouseFk, TRUE);
+
+ IF NOT EXISTS (SELECT TRUE FROM tmp.zoneGetLanded LIMIT 1) THEN
+ CALL util.throw(CONCAT('There is no zone for these parameters ', vTicketFk));
+ END IF;
+
+ IF vLanded IS NULL OR vZoneFk IS NULL THEN
+
+ UPDATE ticket t
+ SET t.landed = (SELECT landed FROM tmp.zoneGetLanded LIMIT 1)
+ WHERE t.id = vTicketFk AND t.landed IS NULL;
+
+ IF vZoneFk IS NULL THEN
+ SELECT zoneFk INTO vZoneFk FROM tmp.zoneGetLanded LIMIT 1;
+ UPDATE ticket t
+ SET t.zoneFk = vZoneFk
+ WHERE t.id = vTicketFk AND t.zoneFk IS NULL;
+ END IF;
+
+ END IF;
+
+ DROP TEMPORARY TABLE tmp.zoneGetLanded;
+
+ -- rellena la tabla buyUltimate con la ultima compra
+ CALL buyUltimate (vWarehouseFk, vShipped);
+
+ CREATE OR REPLACE TEMPORARY TABLE tmp.sale
+ (PRIMARY KEY (saleFk)) ENGINE = MEMORY
+ SELECT s.id saleFk, vWarehouseFk warehouseFk
+ FROM sale s
+ JOIN tmp.recalculateSales rs ON s.id = rs.id
+ WHERE s.ticketFk = vTicketFk;
+
+ CREATE OR REPLACE TEMPORARY TABLE tmp.ticketLot
+ SELECT vWarehouseFk warehouseFk, NULL available, s.itemFk, bu.buyFk, vZoneFk zoneFk
+ FROM sale s
+ JOIN tmp.recalculateSales rs ON s.id = rs.id
+ LEFT JOIN tmp.buyUltimate bu ON bu.itemFk = s.itemFk
+ WHERE s.ticketFk = vTicketFk
+ GROUP BY s.itemFk;
+
+ CALL catalog_componentPrepare();
+ CALL catalog_componentCalculate(vZoneFk, vAddressFk, vShipped, vWarehouseFk);
+
+ IF vOption IS NULL THEN
+ SET vOption = IF(vIsEditable, 1, 6);
+ END IF;
+
+ CALL ticketComponentUpdateSale(vOption);
+ CALL catalog_componentPurge();
+
+ DROP TEMPORARY TABLE tmp.buyUltimate;
+ DROP TEMPORARY TABLE tmp.sale;
+
+ END LOOP;
+ CLOSE vCur;
+
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`invoiceOut_new`(
+ vSerial VARCHAR(255),
+ vInvoiceDate DATE,
+ vTaxArea VARCHAR(25),
+ OUT vNewInvoiceId INT)
+BEGIN
+/**
+ * Creación de facturas emitidas.
+ * requiere previamente tabla tmp.ticketToInvoice(id).
+ *
+ * @param vSerial serie a la cual se hace la factura
+ * @param vInvoiceDate fecha de la factura
+ * @param vTaxArea tipo de iva en relacion a la empresa y al cliente
+ * @param vNewInvoiceId id de la factura que se acaba de generar
+ * @return vNewInvoiceId
+ */
+ DECLARE vIsAnySaleToInvoice BOOL;
+ DECLARE vIsAnyServiceToInvoice BOOL;
+ DECLARE vNewRef VARCHAR(255);
+ DECLARE vWorker INT DEFAULT account.myUser_getId();
+ DECLARE vCompanyFk INT;
+ DECLARE vInterCompanyFk INT;
+ DECLARE vClientFk INT;
+ DECLARE vCplusStandardInvoiceTypeFk INT DEFAULT 1;
+ DECLARE vCplusCorrectingInvoiceTypeFk INT DEFAULT 6;
+ DECLARE vCplusSimplifiedInvoiceTypeFk INT DEFAULT 2;
+ DECLARE vCorrectingSerial VARCHAR(1) DEFAULT 'R';
+ DECLARE vSimplifiedSerial VARCHAR(1) DEFAULT 'S';
+ DECLARE vNewInvoiceInFk INT;
+ DECLARE vIsInterCompany BOOL DEFAULT FALSE;
+ DECLARE vIsCEESerial BOOL DEFAULT FALSE;
+ DECLARE vIsCorrectInvoiceDate BOOL;
+ DECLARE vMaxShipped DATE;
+ DECLARE vDone BOOL;
+ DECLARE vTicketFk INT;
+ DECLARE vCursor CURSOR FOR
+ SELECT id
+ FROM tmp.ticketToInvoice;
+
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
+
+ SET vInvoiceDate = IFNULL(vInvoiceDate, util.VN_CURDATE());
+
+ SELECT t.clientFk,
+ t.companyFk,
+ MAX(DATE(t.shipped)),
+ DATE(vInvoiceDate) >= invoiceOut_getMaxIssued(
+ vSerial,
+ t.companyFk,
+ YEAR(vInvoiceDate))
+ INTO vClientFk,
+ vCompanyFk,
+ vMaxShipped,
+ vIsCorrectInvoiceDate
+ FROM tmp.ticketToInvoice tt
+ JOIN ticket t ON t.id = tt.id;
+
+ IF(vMaxShipped > vInvoiceDate) THEN
+ CALL util.throw("Invoice date can't be less than max date");
+ END IF;
+
+ IF NOT vIsCorrectInvoiceDate THEN
+ CALL util.throw('Exists an invoice with a previous date');
+ END IF;
+
+ -- Eliminem de tmp.ticketToInvoice els tickets que no han de ser facturats
+ DELETE ti.*
+ FROM tmp.ticketToInvoice ti
+ JOIN ticket t ON t.id = ti.id
+ JOIN sale s ON s.ticketFk = t.id
+ JOIN item i ON i.id = s.itemFk
+ JOIN supplier su ON su.id = t.companyFk
+ JOIN client c ON c.id = t.clientFk
+ LEFT JOIN itemTaxCountry itc ON itc.itemFk = i.id AND itc.countryFk = su.countryFk
+ WHERE (YEAR(t.shipped) < 2001 AND t.isDeleted)
+ OR c.isTaxDataChecked = FALSE
+ OR t.isDeleted
+ OR c.hasToInvoice = FALSE
+ OR itc.id IS NULL;
+
+ SELECT SUM(s.quantity * s.price * (100 - s.discount)/100) <> 0
+ INTO vIsAnySaleToInvoice
+ FROM tmp.ticketToInvoice t
+ JOIN sale s ON s.ticketFk = t.id;
+
+ SELECT COUNT(*) > 0 INTO vIsAnyServiceToInvoice
+ FROM tmp.ticketToInvoice t
+ JOIN ticketService ts ON ts.ticketFk = t.id;
+
+ IF (vIsAnySaleToInvoice OR vIsAnyServiceToInvoice)
+ AND (vCorrectingSerial = vSerial OR NOT hasAnyNegativeBase())
+ THEN
+
+ -- el trigger añade el siguiente Id_Factura correspondiente a la vSerial
+ INSERT INTO invoiceOut(
+ ref,
+ serial,
+ issued,
+ clientFk,
+ dued,
+ companyFk,
+ siiTypeInvoiceOutFk
+ )
+ SELECT
+ 1,
+ vSerial,
+ vInvoiceDate,
+ vClientFk,
+ getDueDate(vInvoiceDate, dueDay),
+ vCompanyFk,
+ IF(vSerial = vCorrectingSerial,
+ vCplusCorrectingInvoiceTypeFk,
+ IF(vSerial = vSimplifiedSerial,
+ vCplusSimplifiedInvoiceTypeFk,
+ vCplusStandardInvoiceTypeFk))
+ FROM client
+ WHERE id = vClientFk;
+
+ SET vNewInvoiceId = LAST_INSERT_ID();
+
+ SELECT `ref`
+ INTO vNewRef
+ FROM invoiceOut
+ WHERE id = vNewInvoiceId;
+
+ OPEN vCursor;
+ l: LOOP
+ SET vDone = FALSE;
+ FETCH vCursor INTO vTicketFk;
+
+ IF vDone THEN
+ LEAVE l;
+ END IF;
+
+ CALL ticket_recalc(vTicketFk, vTaxArea);
+
+ END LOOP;
+ CLOSE vCursor;
+
+ UPDATE ticket t
+ JOIN tmp.ticketToInvoice ti ON ti.id = t.id
+ SET t.refFk = vNewRef;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.updateInter;
+ CREATE TEMPORARY TABLE tmp.updateInter ENGINE = MEMORY
+ SELECT s.id,ti.id ticket_id,vWorker Id_Trabajador
+ FROM tmp.ticketToInvoice ti
+ LEFT JOIN ticketState ts ON ti.id = ts.ticketFk
+ JOIN state s
+ WHERE IFNULL(ts.alertLevel,0) < 3 and s.`code` = getAlert3State(ti.id);
+
+ INSERT INTO ticketTracking(stateFk,ticketFk,userFk)
+ SELECT * FROM tmp.updateInter;
+
+ CALL invoiceExpenseMake(vNewInvoiceId);
+ CALL invoiceTaxMake(vNewInvoiceId,vTaxArea);
+
+ UPDATE invoiceOut io
+ JOIN (
+ SELECT SUM(amount) total
+ FROM invoiceOutExpense
+ WHERE invoiceOutFk = vNewInvoiceId
+ ) base
+ JOIN (
+ SELECT SUM(vat) total
+ FROM invoiceOutTax
+ WHERE invoiceOutFk = vNewInvoiceId
+ ) vat
+ SET io.amount = base.total + vat.total
+ WHERE io.id = vNewInvoiceId;
+
+ DROP TEMPORARY TABLE tmp.updateInter;
+
+ SELECT COUNT(*), id
+ INTO vIsInterCompany, vInterCompanyFk
+ FROM company
+ WHERE clientFk = vClientFk;
+
+ IF (vIsInterCompany) THEN
+
+ INSERT INTO invoiceIn(supplierFk, supplierRef, issued, companyFk)
+ SELECT vCompanyFk, vNewRef, vInvoiceDate, vInterCompanyFk;
+
+ SET vNewInvoiceInFk = LAST_INSERT_ID();
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.ticket;
+ CREATE TEMPORARY TABLE tmp.ticket
+ (KEY (ticketFk))
+ ENGINE = MEMORY
+ SELECT id ticketFk
+ FROM tmp.ticketToInvoice;
+
+ CALL `ticket_getTax`('NATIONAL');
+
+ SET @vTaxableBaseServices := 0.00;
+ SET @vTaxCodeGeneral := NULL;
+
+ INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenseFk, taxTypeSageFk, transactionTypeSageFk)
+ SELECT vNewInvoiceInFk,
+ @vTaxableBaseServices,
+ sub.expenseFk,
+ sub.taxTypeSageFk,
+ sub.transactionTypeSageFk
+ FROM (
+ SELECT @vTaxableBaseServices := SUM(tst.taxableBase) taxableBase,
+ i.expenseFk,
+ i.taxTypeSageFk,
+ i.transactionTypeSageFk,
+ @vTaxCodeGeneral := i.taxClassCodeFk
+ FROM tmp.ticketServiceTax tst
+ JOIN invoiceOutTaxConfig i ON i.taxClassCodeFk = tst.code
+ WHERE i.isService
+ HAVING taxableBase
+ ) sub;
+
+ INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenseFk, taxTypeSageFk, transactionTypeSageFk)
+ SELECT vNewInvoiceInFk,
+ SUM(tt.taxableBase) - IF(tt.code = @vTaxCodeGeneral,
+ @vTaxableBaseServices, 0) taxableBase,
+ i.expenseFk,
+ i.taxTypeSageFk ,
+ i.transactionTypeSageFk
+ FROM tmp.ticketTax tt
+ JOIN invoiceOutTaxConfig i ON i.taxClassCodeFk = tt.code
+ WHERE !i.isService
+ GROUP BY tt.pgcFk
+ HAVING taxableBase
+ ORDER BY tt.priority;
+
+ CALL invoiceInDueDay_calculate(vNewInvoiceInFk);
+
+ SELECT COUNT(*) INTO vIsCEESerial
+ FROM invoiceOutSerial
+ WHERE code = vSerial;
+
+ IF vIsCEESerial THEN
+
+ INSERT INTO invoiceInIntrastat (
+ invoiceInFk,
+ intrastatFk,
+ amount,
+ stems,
+ countryFk,
+ net)
+ SELECT
+ vNewInvoiceInFk,
+ i.intrastatFk,
+ SUM(CAST((s.quantity * s.price * (100 - s.discount) / 100 ) AS DECIMAL(10, 2))),
+ SUM(CAST(IFNULL(i.stems, 1) * s.quantity AS DECIMAL(10, 2))),
+ su.countryFk,
+ CAST(SUM(IFNULL(i.stems, 1)
+ * s.quantity
+ * IF(ic.grams, ic.grams, IFNULL(i.weightByPiece, 0)) / 1000) AS DECIMAL(10, 2))
+ FROM sale s
+ JOIN ticket t ON s.ticketFk = t.id
+ JOIN supplier su ON su.id = t.companyFk
+ JOIN item i ON i.id = s.itemFk
+ LEFT JOIN itemCost ic ON ic.itemFk = i.id AND ic.warehouseFk = t.warehouseFk
+ WHERE t.refFk = vNewRef
+ GROUP BY i.intrastatFk;
+
+ END IF;
+ DROP TEMPORARY TABLE tmp.ticket;
+ DROP TEMPORARY TABLE tmp.ticketAmount;
+ DROP TEMPORARY TABLE tmp.ticketTax;
+ DROP TEMPORARY TABLE tmp.ticketServiceTax;
+ END IF;
+ END IF;
+ DROP TEMPORARY TABLE `tmp`.`ticketToInvoice`;
+END$$
+DELIMITER ;
diff --git a/db/changes/240201/01-refactorHasAnyNegativeBase.sql b/db/changes/240201/01-refactorHasAnyNegativeBase.sql
new file mode 100644
index 0000000000..a3eb2d9c7e
--- /dev/null
+++ b/db/changes/240201/01-refactorHasAnyNegativeBase.sql
@@ -0,0 +1,32 @@
+DELIMITER $$
+$$
+CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `vn`.`hasAnyNegativeBase`() RETURNS tinyint(1)
+ DETERMINISTIC
+BEGIN
+
+/**
+* Calcula si existe alguna base imponible negativa
+* Requiere la tabla temporal tmp.ticketToInvoice(id) para getTaxBases()
+*
+* returns BOOLEAN
+*/
+
+ DECLARE hasAnyNegativeBase BOOLEAN;
+
+ CALL getTaxBases();
+
+ SELECT negative INTO hasAnyNegativeBase
+ FROM tmp.taxBases
+ LIMIT 1;
+
+ DROP TEMPORARY TABLE
+ tmp.ticketTax,
+ tmp.ticket,
+ tmp.taxBases;
+
+ RETURN hasAnyNegativeBase;
+
+END$$
+DELIMITER ;
+
+
diff --git a/db/changes/240201/01-triggers.sql b/db/changes/240201/01-triggers.sql
new file mode 100644
index 0000000000..a7fa029b46
--- /dev/null
+++ b/db/changes/240201/01-triggers.sql
@@ -0,0 +1,27 @@
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`expedition_beforeInsert`
+ BEFORE INSERT ON `expedition`
+ FOR EACH ROW
+BEGIN
+ DECLARE intcounter INT;
+ DECLARE vShipFk INT;
+
+ SET NEW.editorFk = account.myUser_getId();
+
+ IF NEW.freightItemFk IS NOT NULL THEN
+
+ UPDATE ticket SET packages = nz(packages) + 1 WHERE id = NEW.ticketFk;
+
+ SELECT IFNULL(MAX(counter),0) +1 INTO intcounter
+ FROM expedition e
+ INNER JOIN ticket t1 ON e.ticketFk = t1.id
+ LEFT JOIN ticketState ts ON ts.ticketFk = t1.id
+ INNER JOIN ticket t2 ON t2.addressFk = t1.addressFk AND DATE(t2.shipped) = DATE(t1.shipped)
+ AND t1.warehouseFk = t2.warehouseFk
+ WHERE t2.id = NEW.ticketFk AND ts.alertLevel < 3 AND t1.companyFk = t2.companyFk
+ AND t1.agencyModeFk = t2.agencyModeFk;
+
+ SET NEW.`counter` = intcounter;
+ END IF;
+END$$
+DELIMITER ;
diff --git a/db/changes/240201/01-views.sql b/db/changes/240201/01-views.sql
new file mode 100644
index 0000000000..30b08b7764
--- /dev/null
+++ b/db/changes/240201/01-views.sql
@@ -0,0 +1,58 @@
+CREATE OR REPLACE DEFINER=`root`@`localhost`
+ SQL SECURITY DEFINER
+ VIEW `vn`.`expeditionRoute_freeTickets` AS
+SELECT
+ `t`.`routeFk` AS `routeFk`,
+ `tss`.`ticketFk` AS `ticket`,
+ `s`.`name` AS `code`,
+ `w`.`name` AS `almacen`,
+ `tss`.`updated` AS `updated`,
+ `p`.`code` AS `parkingCode`
+ FROM `vn`.`ticketState` `tss`
+ JOIN `vn`.`ticket` `t` ON `t`.`id` = `tss`.`ticketFk`
+ JOIN `vn`.`warehouse` `w` ON `w`.`id` = `t`.`warehouseFk`
+ JOIN `vn`.`state` `s` ON `s`.`id` = `tss`.`state`
+ LEFT JOIN `vn`.`ticketParking` `tp` ON `tp`.`ticketFk` = `t`.`id`
+ LEFT JOIN `vn`.`parking` `p` ON `p`.`id` = `tp`.`parkingFk`
+ WHERE IFNULL(`t`.`packages`, 0) = 0;
+
+CREATE OR REPLACE DEFINER=`root`@`localhost`
+ SQL SECURITY DEFINER
+ VIEW `vn`.`ticketState`
+AS SELECT `tt`.`created` AS `updated`,
+ `tt`.`stateFk` AS `stateFk`,
+ `tt`.`userFk` AS `userFk`,
+ `tls`.`ticketFk` AS `ticketFk`,
+ `s`.`id` AS `state`,
+ `s`.`order` AS `productionOrder`,
+ `s`.`alertLevel` AS `alertLevel`,
+ `s`.`code` AS `code`,
+ `s`.`isPreviousPreparable` AS `isPreviousPreparable`,
+ `s`.`isPicked` AS `isPicked`
+FROM (
+ (
+ `vn`.`ticketLastState` `tls`
+ JOIN `vn`.`ticketTracking` `tt` ON(`tt`.`id` = `tls`.`ticketTrackingFk`)
+ )
+ JOIN `vn`.`state` `s` ON(`s`.`id` = `tt`.`stateFk`)
+ );
+
+CREATE OR REPLACE DEFINER=`root`@`localhost`
+ SQL SECURITY DEFINER
+ VIEW `vn`.`ticketStateToday`
+AS
+SELECT
+ `ts`.`ticketFk` AS `ticket`,
+ `ts`.`state` AS `state`,
+ `ts`.`productionOrder` AS `productionOrder`,
+ `ts`.`alertLevel` AS `alertLevel`,
+ `ts`.`userFk` AS `worker`,
+ `ts`.`code` AS `code`,
+ `ts`.`updated` AS `updated`,
+ `ts`.`isPicked` AS `isPicked`
+FROM
+ `vn`.`ticketState` `ts`
+ JOIN `vn`.`ticket` `t` ON `t`.`id` = `ts`.`ticketFk`
+WHERE
+ `t`.`shipped` BETWEEN `util`.`VN_CURDATE`() AND `util`.`VN_CURDATE`() + INTERVAL 1 DAY;
+
diff --git a/db/changes/240202/.gitkeep b/db/changes/240202/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/db/changes/240202/00-aclWorkerTimeControl.sql b/db/changes/240202/00-aclWorkerTimeControl.sql
new file mode 100644
index 0000000000..5ccb3131dd
--- /dev/null
+++ b/db/changes/240202/00-aclWorkerTimeControl.sql
@@ -0,0 +1,14 @@
+DELETE FROM salix.ACL
+ WHERE model = 'WorkerTimeControl'
+ AND property IN ('*','addTime');
+
+INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
+ VALUES
+ ('WorkerTimeControl', 'addTimeEntry', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
+ ('WorkerTimeControl', 'deleteTimeEntry', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
+ ('WorkerTimeControl', 'updateTimeEntry', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
+ ('WorkerTimeControl', 'sendMail', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
+ ('WorkerTimeControl', 'updateWorkerTimeControlMail', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
+ ('WorkerTimeControl', 'weeklyHourRecordEmail', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
+ ('WorkerTimeControl', 'getMailStates', 'READ', 'ALLOW', 'ROLE', 'employee'),
+ ('WorkerTimeControl', 'resendWeeklyHourEmail', 'WRITE', 'ALLOW', 'ROLE', 'employee');
diff --git a/db/changes/240401/.gitkeep b/db/changes/240401/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/db/changes/240401/00-removeStars.sql b/db/changes/240401/00-removeStars.sql
new file mode 100644
index 0000000000..ecc1664c82
--- /dev/null
+++ b/db/changes/240401/00-removeStars.sql
@@ -0,0 +1 @@
+ALTER TABLE `vn`.`item` DROP COLUMN stars;
diff --git a/db/changes/240401/00-ticket_canbePostponed.sql b/db/changes/240401/00-ticket_canbePostponed.sql
new file mode 100644
index 0000000000..e0fbb99cfb
--- /dev/null
+++ b/db/changes/240401/00-ticket_canbePostponed.sql
@@ -0,0 +1,74 @@
+DELIMITER $$
+$$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_canbePostponed`(vOriginDated DATE, vFutureDated DATE, vWarehouseFk INT)
+BEGIN
+/**
+ * Devuelve un listado de tickets susceptibles de fusionarse con otros tickets en el futuro
+ *
+ * @param vOriginDated Fecha en cuestión
+ * @param vFutureDated Fecha en el futuro a sondear
+ * @param vWarehouseFk Identificador de vn.warehouse
+ */
+ CREATE OR REPLACE TEMPORARY TABLE tmp.filter
+ (INDEX (id))
+ SELECT sv.ticketFk id,
+ sub2.id futureId,
+ GROUP_CONCAT(DISTINCT i.itemPackingTypeFk ORDER BY i.itemPackingTypeFk) ipt,
+ CAST(sum(litros) AS DECIMAL(10,0)) liters,
+ CAST(count(*) AS DECIMAL(10,0)) `lines`,
+ st.name state,
+ sub2.iptd futureIpt,
+ sub2.state futureState,
+ t.clientFk,
+ t.warehouseFk,
+ ts.alertLevel,
+ t.shipped,
+ t.totalWithVat,
+ sub2.shipped futureShipped,
+ t.workerFk,
+ st.code stateCode,
+ sub2.code futureStateCode,
+ st.classColor,
+ sub2.classColor futureClassColor
+ FROM vn.saleVolume sv
+ JOIN vn.sale s ON s.id = sv.saleFk
+ JOIN vn.item i ON i.id = s.itemFk
+ JOIN vn.ticket t ON t.id = sv.ticketFk
+ JOIN vn.address a ON a.id = t.addressFk
+ JOIN vn.province p ON p.id = a.provinceFk
+ JOIN vn.country c ON c.id = p.countryFk
+ JOIN vn.ticketState ts ON ts.ticketFk = t.id
+ JOIN vn.state st ON st.id = ts.stateFk
+ JOIN vn.alertLevel al ON al.id = ts.alertLevel
+ LEFT JOIN vn.ticketParking tp ON tp.ticketFk = t.id
+ LEFT JOIN (
+ SELECT *
+ FROM (
+ SELECT
+ t.addressFk,
+ t.id,
+ t.shipped,
+ st.name state,
+ st.code,
+ st.classColor,
+ GROUP_CONCAT(DISTINCT i.itemPackingTypeFk ORDER BY i.itemPackingTypeFk) iptd
+ FROM vn.ticket t
+ JOIN vn.ticketState ts ON ts.ticketFk = t.id
+ JOIN vn.state st ON st.id = ts.stateFk
+ JOIN vn.sale s ON s.ticketFk = t.id
+ JOIN vn.item i ON i.id = s.itemFk
+ WHERE t.shipped BETWEEN vFutureDated
+ AND util.dayend(vFutureDated)
+ AND t.warehouseFk = vWarehouseFk
+ GROUP BY t.id
+ ) sub
+ GROUP BY sub.addressFk
+ ) sub2 ON sub2.addressFk = t.addressFk AND t.id != sub2.id
+ WHERE t.shipped BETWEEN vOriginDated AND util.dayend(vOriginDated)
+ AND t.warehouseFk = vWarehouseFk
+ AND al.code = 'FREE'
+ AND tp.ticketFk IS NULL
+ GROUP BY sv.ticketFk
+ HAVING futureId;
+END$$
+DELIMITER ;
diff --git a/db/changes/240401/01-saleGroupDetailDefaultSize.sql b/db/changes/240401/01-saleGroupDetailDefaultSize.sql
new file mode 100644
index 0000000000..12f17c5e71
--- /dev/null
+++ b/db/changes/240401/01-saleGroupDetailDefaultSize.sql
@@ -0,0 +1,2 @@
+ALTER TABLE vn.productionConfig ADD itemPreviousDefaultSize int NULL COMMENT 'Altura por defecto para los artículos de previa';
+UPDATE IGNORE vn.productionConfig SET itemPreviousDefaultSize = 40 WHERE id = 1;
diff --git a/db/changes/240401/02-invoiceOutConfig_refLen.sql b/db/changes/240401/02-invoiceOutConfig_refLen.sql
new file mode 100644
index 0000000000..8b91889f24
--- /dev/null
+++ b/db/changes/240401/02-invoiceOutConfig_refLen.sql
@@ -0,0 +1,2 @@
+ALTER TABLE vn.invoiceOutConfig
+ ADD IF NOT EXISTS refLen TINYINT UNSIGNED DEFAULT 5 NOT NULL COMMENT 'Invoice reference identifier length';
diff --git a/db/changes/240401/03-fixInvoiceOutBeforeInsert.sql b/db/changes/240401/03-fixInvoiceOutBeforeInsert.sql
new file mode 100644
index 0000000000..0081c88031
--- /dev/null
+++ b/db/changes/240401/03-fixInvoiceOutBeforeInsert.sql
@@ -0,0 +1,59 @@
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`invoiceOut_beforeInsert`
+ BEFORE INSERT ON `invoiceOut`
+ FOR EACH ROW
+BEGIN
+/**
+ * Generates the next reference for the invoice serial. There cannot be gaps
+ * between identifiers of the same serial!
+ *
+ * Reference format:
+ * {0} Invoice serial
+ * {1} The company code
+ * {2-3} Last two digits of issue year
+ * {4-$} Autoincrement identifier
+ */
+ DECLARE vRef INT DEFAULT 0;
+ DECLARE vRefLen INT;
+ DECLARE vRefPrefix VARCHAR(255);
+ DECLARE vLastRef VARCHAR(255);
+ DECLARE vCompanyCode INT;
+ DECLARE vYearLen INT DEFAULT 2;
+ DECLARE vPrefixLen INT;
+
+ SELECT companyCode INTO vCompanyCode
+ FROM company
+ WHERE id = NEW.companyFk;
+
+ IF vCompanyCode IS NULL THEN
+ CALL util.throw('companyCodeNotDefined');
+ END IF;
+
+ SELECT MAX(i.ref) INTO vLastRef
+ FROM invoiceOut i
+ WHERE i.serial = NEW.serial
+ AND i.issued BETWEEN util.firstDayOfYear(NEW.issued) AND util.lastDayOfYear(NEW.issued)
+ AND i.companyFk = NEW.companyFk;
+
+ IF vLastRef IS NOT NULL THEN
+ SET vPrefixLen = LENGTH(NEW.serial) + LENGTH(vCompanyCode) + vYearLen;
+ SET vRefLen = LENGTH(vLastRef) - vPrefixLen;
+ SET vRefPrefix = LEFT(vLastRef, vPrefixLen);
+ SET vRef = RIGHT(vLastRef, vRefLen);
+ ELSE
+ SELECT refLen INTO vRefLen FROM invoiceOutConfig;
+ SET vRefPrefix = CONCAT(
+ NEW.serial,
+ vCompanyCode,
+ RIGHT(YEAR(NEW.issued), vYearLen)
+ );
+ END IF;
+
+ SET vRef = vRef + 1;
+ IF LENGTH(vRef) > vRefLen THEN
+ CALL util.throw('refIdLenExceeded');
+ END IF;
+
+ SET NEW.ref = CONCAT(vRefPrefix, LPAD(vRef, vRefLen, '0'));
+END$$
+DELIMITER ;
diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index 93b7b796f1..b243692bb2 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -1,3 +1,7 @@
+CREATE ROLE 'salix';
+GRANT 'salix' TO 'root'@'%';
+SET DEFAULT ROLE 'salix' FOR 'root'@'%';
+
CREATE SCHEMA IF NOT EXISTS `vn2008`;
CREATE SCHEMA IF NOT EXISTS `tmp`;
@@ -362,7 +366,7 @@ INSERT INTO `vn`.`contactChannel`(`id`, `name`)
INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city`,`postcode`,`phone`,`mobile`,`isRelevant`,`email`,`iban`,`dueDay`,`accountingAccount`,`isEqualizated`,`provinceFk`,`hasToInvoice`,`credit`,`countryFk`,`isActive`,`gestdocFk`,`quality`,`payMethodFk`,`created`,`isToBeMailed`,`contactChannelFk`,`hasSepaVnl`,`hasCoreVnl`,`hasCoreVnh`,`riskCalculated`,`clientTypeFk`, `hasToInvoiceByAddress`,`isTaxDataChecked`,`isFreezed`,`creditInsurance`,`isCreatedAsServed`,`hasInvoiceSimplified`,`salesPersonFk`,`isVies`,`eypbc`, `businessTypeFk`,`typeFk`)
VALUES
- (1101, 'Bruce Wayne', '84612325V', 'BATMAN', 'Alfred', '1007 MOUNTAIN DRIVE, GOTHAM', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceWayne@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist','loses'),
+ (1101, 'Bruce Wayne', '84612325V', 'BATMAN', 'Alfred', '1007 MOUNTAIN DRIVE, GOTHAM', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceWayne@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist','normal'),
(1102, 'Petter Parker', '87945234L', 'SPIDER MAN', 'Aunt May', '20 INGRAM STREET, QUEENS, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'PetterParker@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist','normal'),
(1103, 'Clark Kent', '06815934E', 'SUPER MAN', 'lois lane', '344 CLINTON STREET, APARTAMENT 3-D', 'Gotham', 46460, 1111111111, 222222222, 1, 'ClarkKent@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 0, 19, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist','normal'),
(1104, 'Tony Stark', '06089160W', 'IRON MAN', 'Pepper Potts', '10880 MALIBU POINT, 90265', 'Gotham', 46460, 1111111111, 222222222, 1, 'TonyStark@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist','normal'),
@@ -372,8 +376,8 @@ INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city
(1108, 'Charles Xavier', '22641921P', 'PROFESSOR X', 'Beast', '3800 VICTORY PKWY, CINCINNATI, OH 45207, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'CharlesXavier@mydomain.com', NULL, 0, 1234567890, 0, 5, 1, 300, 13, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 19, 0, 1, 'florist','normal'),
(1109, 'Bruce Banner', '16104829E', 'HULK', 'Black widow', 'SOMEWHERE IN NEW YORK', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceBanner@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 9, 0, 1, 'florist','normal'),
(1110, 'Jessica Jones', '58282869H', 'JESSICA JONES', 'Luke Cage', 'NYCC 2015 POSTER', 'Gotham', 46460, 1111111111, 222222222, 1, 'JessicaJones@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, NULL, 0, 1, 'florist','normal'),
- (1111, 'Missing', NULL, 'MISSING MAN', 'Anton', 'THE SPACE, UNIVERSE FAR AWAY', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others','normal'),
- (1112, 'Trash', NULL, 'GARBAGE MAN', 'Unknown name', 'NEW YORK CITY, UNDERGROUND', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others','normal');
+ (1111, 'Missing', NULL, 'MISSING MAN', 'Anton', 'THE SPACE, UNIVERSE FAR AWAY', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others','loses'),
+ (1112, 'Trash', NULL, 'GARBAGE MAN', 'Unknown name', 'NEW YORK CITY, UNDERGROUND', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others','loses');
INSERT INTO `vn`.`client`(`id`, `name`, `fi`, `socialName`, `contact`, `street`, `city`, `postcode`, `isRelevant`, `email`, `iban`,`dueDay`,`accountingAccount`, `isEqualizated`, `provinceFk`, `hasToInvoice`, `credit`, `countryFk`, `isActive`, `gestdocFk`, `quality`, `payMethodFk`,`created`, `isTaxDataChecked`)
SELECT id, name, CONCAT(RPAD(CONCAT(id,9),8,id),'A'), CONCAT(name, 'Social'), CONCAT(name, 'Contact'), CONCAT(name, 'Street'), 'GOTHAM', 46460, 1, CONCAT(name,'@mydomain.com'), NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5, util.VN_CURDATE(), 1
@@ -489,7 +493,7 @@ INSERT INTO `vn`.`clientCredit`(`clientFk`, `workerFk`, `amount`, `created`)
(1104, 9, 90 , util.VN_CURDATE()),
(1105, 9, 90 , util.VN_CURDATE());
-INSERT INTO `vn`.`clientCreditLimit`(`id`, `maxAmount`, `roleFk`)
+INSERT INTO `vn`.`roleCreditLimit`(`id`, `maxAmount`, `roleFk`)
VALUES
(1, 9999999, 20),
(2, 10000, 21),
@@ -596,20 +600,24 @@ INSERT INTO `vn`.`taxArea` (`code`, `claveOperacionFactura`, `CodigoTransaccion`
('NATIONAL', 0, 1),
('WORLD', 2, 15);
+INSERT INTO vn.invoiceOutConfig
+ SET parallelism = 8;
+
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, 'quick'),
- ('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, 'quick'),
+ ('R', 'Rectificativa', 1, 'NATIONAL', 0, NULL),
+ ('E', 'Exportación rápida', 0, 'WORLD', 0, 'quick');
INSERT INTO `vn`.`invoiceOut`(`id`, `serial`, `amount`, `issued`,`clientFk`, `created`, `companyFk`, `dued`, `booked`, `bankFk`, `hasPdf`)
VALUES
(1, 'T', 1026.24, util.VN_CURDATE(), 1101, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
(2, 'T', 121.36, util.VN_CURDATE(), 1102, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
(3, 'T', 8.88, util.VN_CURDATE(), 1103, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
- (4, 'T', 8.88, util.VN_CURDATE(), 1103, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
+ (4, 'T', 8.88, util.VN_CURDATE(), 1104, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
(5, 'A', 8.88, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1103, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 442, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1, 0);
UPDATE `vn`.`invoiceOut` SET ref = 'T1111111' WHERE id = 1;
@@ -618,9 +626,6 @@ UPDATE `vn`.`invoiceOut` SET ref = 'T3333333' WHERE id = 3;
UPDATE `vn`.`invoiceOut` SET ref = 'T4444444' WHERE id = 4;
UPDATE `vn`.`invoiceOut` SET ref = 'A1111111' WHERE id = 5;
-INSERT INTO vn.invoiceOutConfig
- SET parallelism = 8;
-
INSERT INTO `vn`.`invoiceOutTax` (`invoiceOutFk`, `taxableBase`, `vat`, `pgcFk`)
VALUES
(1, 895.76, 89.58, 4722000010),
@@ -715,7 +720,7 @@ INSERT INTO `vn`.`route`(`id`, `time`, `workerFk`, `created`, `vehicleFk`, `agen
INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `zonePrice`, `zoneBonus`, `created`, `weight`)
VALUES
(1 , 3, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 121, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1),
- (2 , 1, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 2),
+ (2 , 1, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 1, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 2),
(3 , 1, 7, 1, 6, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), NULL),
(4 , 3, 2, 1, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 9, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), NULL),
(5 , 3, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), NULL),
@@ -767,7 +772,7 @@ INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `des
-- FIX for state hours on local, inter_afterInsert
-- UPDATE vncontrol.inter SET odbc_date = DATE_ADD(util.VN_CURDATE(), INTERVAL -10 SECOND);
-INSERT INTO `vn`.`ticketTracking`(`ticketFk`, `stateFk`, `workerFk`, `created`)
+INSERT INTO `vn`.`ticketTracking`(`ticketFk`, `stateFk`, `userFk`, `created`)
VALUES
(1, 16, 5 , DATE_ADD(util.VN_NOW(), INTERVAL -1 MONTH)),
(2, 16, 5 , DATE_ADD(util.VN_NOW(), INTERVAL -1 MONTH)),
@@ -811,6 +816,7 @@ INSERT INTO `vn`.`config`(`id`, `mdbServer`, `fakeEmail`, `defaultersMaxAmount`,
VALUES
(1, 'beta-server', 'nightmare@mydomain.com', '200', DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH));
+
INSERT INTO `vn`.`greugeType`(`id`, `name`, `code`)
VALUES
(1, 'Diff', 'diff'),
@@ -923,25 +929,25 @@ INSERT INTO `vn`.`itemFamily`(`code`, `description`)
('VT', 'Sales');
INSERT INTO `vn`.`item`(`id`, `typeFk`, `stems`, `originFk`, `description`, `producerFk`, `intrastatFk`, `expenseFk`,
- `comment`, `relevancy`, `image`, `subName`, `minPrice`, `stars`, `family`, `isFloramondo`, `genericFk`, `itemPackingTypeFk`, `hasMinPrice`, `packingShelve`, `weightByPiece`)
+ `comment`, `relevancy`, `image`, `subName`, `minPrice`, `family`, `isFloramondo`, `genericFk`, `itemPackingTypeFk`, `hasMinPrice`, `packingShelve`, `weightByPiece`)
VALUES
- (1, 2, 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '1', NULL, 0, 1, 'EMB', 0, NULL, 'V', 0, 15,3),
- (2, 2, 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '2', NULL, 0, 2, 'VT', 0, NULL, 'H', 0, 10,2),
- (3, 1, 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '3', NULL, 0, 5, 'VT', 0, NULL, NULL, 0, 5,5),
- (4, 1, 1, 1, 'Increases block', 1, 05080000, 4751000000, NULL, 0, '4', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL,NULL),
- (5, 3, 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '5', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL,NULL),
- (6, 5, 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '6', NULL, 0, 4, 'VT', 0, NULL, NULL, 0, NULL,NULL),
- (7, 5, 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '7', NULL, 0, 4, 'VT', 0, NULL, NULL, 0, NULL,NULL),
- (8, 2, 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '8', NULL, 0, 5, 'VT', 0, NULL, NULL, 0, NULL,NULL),
- (9, 2, 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '9', NULL, 0, 4, 'VT', 1, NULL, NULL, 0, NULL,NULL),
- (10, 1, 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '10', NULL, 0, 4, 'VT', 0, NULL, NULL, 0, NULL,NULL),
- (11, 1, 1, 1, NULL, 1, 05080000, 4751000000, NULL, 0, '11', NULL, 0, 4, 'VT', 0, NULL, NULL, 0, NULL,NULL),
- (12, 3, 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '12', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL,NULL),
- (13, 5, 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '13', NULL, 1, 2, 'VT', 1, NULL, NULL, 1, NULL,NULL),
- (14, 5, 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 4, 'VT', 1, NULL, NULL, 0, NULL,NULL),
- (15, 4, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'EMB', 0, NULL, NULL, 0, NULL,NULL),
- (16, 6, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'EMB', 0, NULL, NULL, 0, NULL,NULL),
- (71, 6, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'VT', 0, NULL, NULL, 0, NULL,NULL);
+ (1, 2, 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '1', NULL, 0, 'EMB', 0, NULL, 'V', 0, 15,3),
+ (2, 2, 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '2', NULL, 0, 'VT', 0, NULL, 'H', 0, 10,2),
+ (3, 1, 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '3', NULL, 0, 'VT', 0, NULL, NULL, 0, 5,5),
+ (4, 1, 1, 1, 'Increases block', 1, 05080000, 4751000000, NULL, 0, '4', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL,NULL),
+ (5, 3, 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '5', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL,NULL),
+ (6, 5, 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '6', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL,NULL),
+ (7, 5, 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '7', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL,NULL),
+ (8, 2, 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '8', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL,NULL),
+ (9, 2, 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '9', NULL, 0, 'VT', 1, NULL, NULL, 0, NULL,NULL),
+ (10, 1, 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '10', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL,NULL),
+ (11, 1, 1, 1, NULL, 1, 05080000, 4751000000, NULL, 0, '11', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL,NULL),
+ (12, 3, 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '12', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL,NULL),
+ (13, 5, 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '13', NULL, 1, 'VT', 1, NULL, NULL, 1, NULL,NULL),
+ (14, 5, 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 'VT', 1, NULL, NULL, 0, NULL,NULL),
+ (15, 4, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 'EMB', 0, NULL, NULL, 0, NULL,NULL),
+ (16, 6, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 'EMB', 0, NULL, NULL, 0, NULL,NULL),
+ (71, 6, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL,NULL);
-- Update the taxClass after insert of the items
UPDATE `vn`.`itemTaxCountry` SET `taxClassFk` = 2
@@ -2343,9 +2349,11 @@ INSERT INTO `vn`.`zoneEvent`(`zoneFk`, `type`, `weekDays`)
(8, 'indefinitely', 'mon,tue,wed,thu,fri,sat,sun'),
(10, 'indefinitely', 'mon,tue,wed,thu,fri,sat,sun');
-INSERT INTO `vn`.`zoneEvent`(`zoneFk`, `type`, `started`, `ended`)
+INSERT INTO `vn`.`zoneEvent`(`zoneFk`, `type`, `started`, `ended`, `weekDays`)
VALUES
- (9, 'range', DATE_ADD(util.VN_CURDATE(), INTERVAL -1 YEAR), DATE_ADD(util.VN_CURDATE(), INTERVAL +1 YEAR));
+ (9, 'range', DATE_ADD(util.VN_CURDATE(), INTERVAL -1 YEAR), DATE_ADD(util.VN_CURDATE(), INTERVAL +1 YEAR), 'mon'),
+ (9, 'range', util.VN_CURDATE(), NULL, 'tue'),
+ (9, 'range', NULL, util.VN_CURDATE(), 'wed');
INSERT INTO `vn`.`workerTimeControl`(`userFk`, `timed`, `manual`, `direction`, `isSendMail`)
VALUES
@@ -2903,8 +2911,7 @@ INSERT INTO `vn`.`workerConfig` (`id`, `businessUpdated`, `roleFk`, `payMethodFk
INSERT INTO `vn`.`ticketRefund`(`refundTicketFk`, `originalTicketFk`)
VALUES
- (1, 12),
- (8, 10);
+ (24, 7);
INSERT INTO `vn`.`deviceProductionModels` (`code`)
VALUES
@@ -2960,9 +2967,9 @@ INSERT INTO `vn`.`wagonTypeTray` (`id`, `typeFk`, `height`, `colorFk`)
(2, 1, 50, 2),
(3, 1, 0, 3);
-INSERT INTO `salix`.`accessTokenConfig` (`id`, `renewPeriod`, `renewInterval`)
+INSERT INTO `salix`.`accessTokenConfig` (`id`, `renewPeriod`, `courtesyTime`, `renewInterval`)
VALUES
- (1, 21600, 300);
+ (1, 21600, 60, 300);
INSERT INTO `vn`.`travelConfig` (`id`, `warehouseInFk`, `warehouseOutFk`, `agencyFk`, `companyFk`)
VALUES
@@ -3002,3 +3009,37 @@ INSERT INTO `vn`.`invoiceCorrectionType` (`id`, `description`)
(1, 'Error in VAT calculation'),
(2, 'Error in sales details'),
(3, 'Error in customer data');
+
+UPDATE `vn`.`client`
+ SET fi='65004204V'
+ WHERE id=1;
+
+UPDATE `vn`.`worker`
+ SET fi='59328808D'
+ WHERE id=1106;
+
+
+INSERT INTO `account`.`mailAliasAcl` (`mailAliasFk`, `roleFk`)
+ VALUES
+ (1, 1),
+ (2, 9),
+ (3, 15);
+
+INSERT INTO `vn`.`docuwareTablet` (`tablet`,`description`)
+ VALUES
+ ('Tablet1','Jarvis tablet'),
+ ('Tablet2','Avengers tablet');
+
+INSERT INTO `vn`.`sms` (`id`, `senderFk`, `sender`, `destination`, `message`, `statusCode`, `status`, `created`)
+ VALUES (1, 66, '111111111', '0001111111111', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', 0, 'OK', util.VN_CURDATE()),
+ (2, 66, '222222222', '0002222222222', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', 0, 'PENDING', util.VN_CURDATE()),
+ (3, 66, '333333333', '0003333333333', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', 0, 'ERROR', util.VN_CURDATE()),
+ (4, 66, '444444444', '0004444444444', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', 0, 'OK', util.VN_CURDATE());
+
+INSERT INTO `vn`.`clientSms` (`id`, `clientFk`, `smsFk`, `ticketFk`)
+ VALUES(1, 1103, 1, NULL),
+ (2, 1103, 2, NULL),
+ (3, 1103, 3, 32),
+ (4, 1103, 4, 32),
+ (13, 1101, 1, NULL),
+ (14, 1101, 4, 27);
diff --git a/db/dump/structure.sql b/db/dump/structure.sql
index 1db4252f48..694f745efa 100644
--- a/db/dump/structure.sql
+++ b/db/dump/structure.sql
@@ -26391,6 +26391,7 @@ CREATE TABLE `cplusCorrectingType` (
) ENGINE=InnoDBDEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
+
--
-- Table structure for table `cplusRectificationType`
--
diff --git a/db/tests/vn/ticket_componentMakeUpdate.spec.js b/db/tests/vn/ticket_componentMakeUpdate.spec.js
deleted file mode 100644
index a059f1060c..0000000000
--- a/db/tests/vn/ticket_componentMakeUpdate.spec.js
+++ /dev/null
@@ -1,123 +0,0 @@
-const app = require('vn-loopback/server/server');
-const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
-
-// 2277 solucionar problema al testear procedimiento con start transaction / rollback
-xdescribe('ticket_componentMakeUpdate()', () => {
- it('should recalculate the ticket components without make modifications', async() => {
- let stmts = [];
- let stmt;
-
- let params = {
- ticketId: 11,
- clientId: 1102,
- agencyModeId: 2,
- addressId: 122,
- zoneId: 3,
- warehouseId: 1,
- companyId: 442,
- isDeleted: 0,
- hasToBeUnrouted: 0,
- componentOption: 1
- };
-
- stmts.push('START TRANSACTION');
-
- stmt = new ParameterizedSQL('SELECT * FROM vn.ticket WHERE id = ?', [
- params.ticketId
- ]);
- stmts.push(stmt);
-
- let originalTicketIndex = stmts.push(stmt) - 1;
-
- stmt = new ParameterizedSQL('CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, DATE_ADD(CURDATE(), INTERVAL +1 DAY), DATE_ADD(CURDATE(), INTERVAL +1 DAY), ?, ?, ?)', [
- params.ticketId,
- params.clientId,
- params.agencyModeId,
- params.addressId,
- params.zoneId,
- params.warehouseId,
- params.companyId,
- params.isDeleted,
- params.hasToBeUnrouted,
- params.componentOption
- ]);
- stmts.push(stmt);
-
- stmt = new ParameterizedSQL('SELECT * FROM vn.ticket WHERE id = ?', [
- params.ticketId
- ]);
- stmts.push(stmt);
-
- let updatedTicketIndex = stmts.push(stmt) - 1;
-
- stmts.push('ROLLBACK');
-
- let sql = ParameterizedSQL.join(stmts, ';');
- let result = await app.models.Ticket.rawStmt(sql);
-
- let originalTicketData = result[originalTicketIndex];
- let updatedTicketData = result[updatedTicketIndex];
-
- expect(originalTicketData[0].isDeleted).toEqual(updatedTicketData[0].isDeleted);
- expect(originalTicketData[0].routeFk).toEqual(updatedTicketData[0].routeFk);
- });
-
- it('should delete and unroute a ticket and recalculate the components', async() => {
- let stmts = [];
- let stmt;
-
- let params = {
- ticketId: 11,
- clientId: 1102,
- agencyModeId: 2,
- addressId: 122,
- zoneId: 3,
- warehouseId: 1,
- companyId: 442,
- isDeleted: 1,
- hasToBeUnrouted: 1,
- componentOption: 1
- };
-
- stmts.push('START TRANSACTION');
-
- stmt = new ParameterizedSQL('SELECT * FROM vn.ticket WHERE id = ?', [
- params.ticketId
- ]);
- stmts.push(stmt);
-
- let originalTicketIndex = stmts.push(stmt) - 1;
-
- stmt = new ParameterizedSQL('CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, DATE_ADD(CURDATE(), INTERVAL +1 DAY), DATE_ADD(CURDATE(), INTERVAL +1 DAY), ?, ?, ?)', [
- params.ticketId,
- params.clientId,
- params.agencyModeId,
- params.addressId,
- params.zoneId,
- params.warehouseId,
- params.companyId,
- params.isDeleted,
- params.hasToBeUnrouted,
- params.componentOption
- ]);
- stmts.push(stmt);
-
- stmt = new ParameterizedSQL('SELECT * FROM vn.ticket WHERE id = ?', [
- params.ticketId
- ]);
- stmts.push(stmt);
-
- let updatedTicketIndex = stmts.push(stmt) - 1;
-
- stmts.push('ROLLBACK');
-
- let sql = ParameterizedSQL.join(stmts, ';');
- let result = await app.models.Ticket.rawStmt(sql);
-
- let originalTicketData = result[originalTicketIndex];
- let updatedTicketData = result[updatedTicketIndex];
-
- expect(originalTicketData[0].isDeleted).not.toEqual(updatedTicketData[0].isDeleted);
- expect(originalTicketData[0].routeFk).not.toEqual(updatedTicketData[0].routeFk);
- });
-});
diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js
index acd13800b5..dba430e663 100644
--- a/e2e/helpers/selectors.js
+++ b/e2e/helpers/selectors.js
@@ -735,7 +735,7 @@ export default {
},
createStateView: {
state: 'vn-autocomplete[ng-model="$ctrl.stateFk"]',
- worker: 'vn-worker-autocomplete[ng-model="$ctrl.workerFk"]',
+ worker: 'vn-worker-autocomplete[ng-model="$ctrl.userFk"]',
saveStateButton: `button[type=submit]`
},
claimsIndex: {
diff --git a/e2e/paths/03-worker/04_time_control.spec.js b/e2e/paths/03-worker/04_time_control.spec.js
index 5f64aa6ce5..c6589d2e3a 100644
--- a/e2e/paths/03-worker/04_time_control.spec.js
+++ b/e2e/paths/03-worker/04_time_control.spec.js
@@ -56,63 +56,6 @@ describe('Worker time control path', () => {
expect(result).toContain(month);
});
- it(`should return error when insert 'out' of first entry`, async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.dialogTimeInput, eightAm);
- await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'out');
- await page.respondToDialog('accept');
- const message = await page.waitForSnackbar();
-
- expect(message.text).toBeDefined();
- });
-
- it(`should insert 'in' monday`, async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.dialogTimeInput, eightAm);
- await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'in');
- await page.respondToDialog('accept');
- const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfMonday, 'innerText');
-
- expect(result).toEqual(eightAm);
- });
-
- it(`should insert 'out' monday`, async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.dialogTimeInput, fourPm);
- await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'out');
- await page.respondToDialog('accept');
- const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfMonday, 'innerText');
-
- expect(result).toEqual(fourPm);
- });
-
- it(`should check Hank Pym worked 8:20 hours`, async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- await page.waitForTextInElement(selectors.workerTimeControl.mondayWorkedHours, '08:20 h.');
- await page.waitForTextInElement(selectors.workerTimeControl.weekWorkedHours, '08:20 h.');
- });
-
- it('should remove first entry of monday', async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- await page.waitForTextInElement(selectors.workerTimeControl.firstEntryOfMonday, eightAm);
- await page.waitForTextInElement(selectors.workerTimeControl.secondEntryOfMonday, fourPm);
- await page.waitToClick(selectors.workerTimeControl.firstEntryOfMondayDelete);
- await page.respondToDialog('accept');
- const message = await page.waitForSnackbar();
-
- expect(message.text).toContain('Entry removed');
- });
-
- it(`should be the 'out' the first entry of monday`, async() => {
- pending('https://redmine.verdnatura.es/issues/4707');
- const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfMonday, 'innerText');
-
- expect(result).toEqual(fourPm);
- });
-
it('should change week of month', async() => {
await page.click(selectors.workerTimeControl.thrirdWeekDay);
const result = await page.getProperty(selectors.workerTimeControl.mondayWorkedHours, 'innerText');
diff --git a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
index 5e82306cc6..9095eee0a3 100644
--- a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
+++ b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
@@ -188,17 +188,6 @@ describe('Ticket Edit sale path', () => {
expect(result).toContain('22.50');
});
- it('should check in the history that logs has been added', async() => {
- pending('https://redmine.verdnatura.es/issues/5455');
- await page.reload({waitUntil: ['networkidle0', 'domcontentloaded']});
- await page.waitToClick(selectors.ticketSales.firstSaleHistoryButton);
- await page.waitForSelector(selectors.ticketSales.firstSaleHistory);
- const result = await page.countElement(selectors.ticketSales.firstSaleHistory);
-
- expect(result).toBeGreaterThan(0);
- await page.waitToClick(selectors.ticketSales.closeHistory);
- });
-
it('should recalculate price of sales', async() => {
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
await page.waitToClick(selectors.ticketSales.secondSaleCheckbox);
@@ -236,7 +225,7 @@ describe('Ticket Edit sale path', () => {
});
it('should show error trying to delete a ticket with a refund', async() => {
- await page.accessToSearchResult('16');
+ await page.accessToSearchResult('6');
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuDeleteTicket);
await page.waitToClick(selectors.globalItems.acceptButton);
diff --git a/e2e/paths/05-ticket/11_diary.spec.js b/e2e/paths/05-ticket/11_diary.spec.js
deleted file mode 100644
index e4c63855af..0000000000
--- a/e2e/paths/05-ticket/11_diary.spec.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import selectors from '../../helpers/selectors.js';
-import getBrowser from '../../helpers/puppeteer';
-
-// #2221 Local MySQL8 crashes when rest method Items/getBalance is called
-xdescribe('Ticket diary path', () => {
- let page;
-
- beforeAll(async() => {
- page = (await getBrowser()).page;
- await page.loginAndModule('employee', 'ticket');
- });
-
- afterAll(async() => {
- await page.browser().close();
- });
-
- it(`should navigate to item diary from ticket sale and check the lines`, async() => {
- await page.accessToSearchResult('1');
- await page.waitToClick(selectors.ticketSummary.firstSaleItemId);
- await page.waitToClick(selectors.ticketSummary.popoverDiaryButton);
- await page.waitForState('item.card.diary');
-
- const secondIdClass = await page.getClassName(selectors.itemDiary.secondTicketId);
- const fourthBalanceClass = await page.getClassName(selectors.itemDiary.fourthBalance);
- const firstBalanceClass = await page.getClassName(selectors.itemDiary.firstBalance);
-
- expect(secondIdClass).toContain('message');
- expect(fourthBalanceClass).toContain('message');
- expect(firstBalanceClass).toContain('balance');
- });
-});
diff --git a/e2e/paths/05-ticket/18_index_payout.spec.js b/e2e/paths/05-ticket/18_index_payout.spec.js
index 7e5201d11a..9c55184243 100644
--- a/e2e/paths/05-ticket/18_index_payout.spec.js
+++ b/e2e/paths/05-ticket/18_index_payout.spec.js
@@ -35,7 +35,7 @@ describe('Ticket index payout path', () => {
await page.waitToClick(selectors.ticketsIndex.openAdvancedSearchButton);
await page.write(selectors.ticketsIndex.advancedSearchClient, '1101');
await page.keyboard.press('Enter');
- await page.waitForNumberOfElements(selectors.ticketsIndex.anySearchResult, 9);
+ await page.waitForNumberOfElements(selectors.ticketsIndex.anySearchResult, 10);
await page.waitToClick(selectors.ticketsIndex.firstTicketCheckbox);
await page.waitToClick(selectors.ticketsIndex.secondTicketCheckbox);
diff --git a/e2e/paths/06-claim/02_detail.spec.js b/e2e/paths/06-claim/02_detail.spec.js
deleted file mode 100644
index eb4ac5d710..0000000000
--- a/e2e/paths/06-claim/02_detail.spec.js
+++ /dev/null
@@ -1,114 +0,0 @@
-import selectors from '../../helpers/selectors.js';
-import getBrowser from '../../helpers/puppeteer.js';
-
-// #1528 e2e claim/detail
-xdescribe('Claim detail', () => {
- let browser;
- let page;
-
- beforeAll(async() => {
- browser = await getBrowser();
- page = browser.page;
- await page.loginAndModule('salesPerson', 'claim');
- await page.accessToSearchResult('1');
- await page.accessToSection('claim.card.detail');
- });
-
- afterAll(async() => {
- await browser.close();
- });
-
- it('should add the first claimable item from ticket to the claim', async() => {
- await page.waitToClick(selectors.claimDetail.addItemButton);
- await page.waitToClick(selectors.claimDetail.firstClaimableSaleFromTicket);
- const message = await page.waitForSnackbar();
-
- expect(message.text).toContain('Data saved!');
- });
-
- it('should confirm the claim contains now two items', async() => {
- const result = await page.countElement(selectors.claimDetail.claimDetailLine);
-
- expect(result).toEqual(2);
- });
-
- it('should edit de first item claimed quantity', async() => {
- await page.clearInput(selectors.claimDetail.firstItemQuantityInput); // selector deleted, find new upon fixes
- await page.write(selectors.claimDetail.firstItemQuantityInput, '4'); // selector deleted, find new upon fixes
- await page.keyboard.press('Enter');
- const message = await page.waitForSnackbar();
-
- expect(message.text).toContain('Data saved!');
- });
-
- it('should confirm the first item quantity, and the claimed total were correctly edited', async() => {
- const claimedQuantity = page
- .waitToGetProperty(selectors.claimDetail.firstItemQuantityInput, 'value'); // selector deleted, find new upon fixes
-
- const totalClaimed = page
- .waitToGetProperty(selectors.claimDetail.totalClaimed, 'innerText');
-
- expect(claimedQuantity).toEqual('4');
- expect(totalClaimed).toContain('€47.62');
- });
-
- it('should login as salesAssistant and navigate to the claim.detail section', async() => {
- await page.loginAndModule('salesAssistant', 'claim');
- await page.accessToSearchResult('1');
- await page.accessToSection('claim.card.detail');
- let url = await page.expectURL('/detail'); // replace with waitForState
-
- expect(url).toBe(true);
- });
-
- it('should edit de second item claimed discount', async() => {
- await page.waitToClick(selectors.claimDetail.secondItemDiscount);
- await page.write(selectors.claimDetail.discount, '100');
- await page.keyboard.press('Enter');
- const message = await page.waitForSnackbar();
-
- expect(message.text).toContain('Data saved!');
- });
-
- it('should check the mana is the expected one', async() => {
- await page.waitToClick(selectors.claimDetail.secondItemDiscount);
- const result = await page.waitToGetProperty(selectors.claimDetail.discoutPopoverMana, 'innerText');
-
- expect(result).toContain('MANÁ: €106');
- });
-
- it('should delete the second item from the claim', async() => {
- await page.waitToClick(selectors.claimDetail.secondItemDeleteButton);
- const message = await page.waitForSnackbar();
-
- expect(message.text).toContain('Data saved!');
- });
-
- it('should confirm the claim contains now one item', async() => {
- const result = await page.countElement(selectors.claimDetail.claimDetailLine);
-
- expect(result).toEqual(1);
- });
-
- it('should add the deleted ticket from to the claim', async() => {
- await page.waitToClick(selectors.claimDetail.addItemButton);
- await page.waitToClick(selectors.claimDetail.firstClaimableSaleFromTicket);
- const message = await page.waitForSnackbar();
-
- expect(message.text).toContain('Data saved!');
- });
-
- it(`should have been redirected to the next section in claims`, async() => {
- let url = await page.expectURL('development'); // replace with waitForState
-
- expect(url).toBe(true);
- });
-
- it('should navigate back to claim.detail to confirm the claim contains now two items', async() => {
- await page.accessToSection('claim.card.detail');
- await page.waitForSelector(selectors.claimDetail.claimDetailLine);
- const result = await page.countElement(selectors.claimDetail.claimDetailLine);
-
- expect(result).toEqual(2);
- });
-});
diff --git a/e2e/paths/08-route/03_create_and_clone.spec.js b/e2e/paths/08-route/03_create_and_clone.spec.js
index 0b8da98b46..31c0cfc18b 100644
--- a/e2e/paths/08-route/03_create_and_clone.spec.js
+++ b/e2e/paths/08-route/03_create_and_clone.spec.js
@@ -26,7 +26,7 @@ describe('Route create path', () => {
await page.waitToClick(selectors.createRouteView.submitButton);
const message = await page.waitForSnackbar();
- expect(message.text).toContain('Access denied');
+ expect(message.text).toContain('Access Denied');
});
});
diff --git a/e2e/paths/09-invoice-out/01_summary.spec.js b/e2e/paths/09-invoice-out/01_summary.spec.js
index 728f0130a9..09ac66ffce 100644
--- a/e2e/paths/09-invoice-out/01_summary.spec.js
+++ b/e2e/paths/09-invoice-out/01_summary.spec.js
@@ -28,7 +28,6 @@ describe('InvoiceOut summary path', () => {
it('should contain the tax breakdown', async() => {
const firstTax = await page.waitToGetProperty(selectors.invoiceOutSummary.taxOne, 'innerText');
-
const secondTax = await page.waitToGetProperty(selectors.invoiceOutSummary.taxTwo, 'innerText');
expect(firstTax).toContain('10%');
@@ -37,10 +36,9 @@ describe('InvoiceOut summary path', () => {
it('should contain the tickets info', async() => {
const firstTicket = await page.waitToGetProperty(selectors.invoiceOutSummary.ticketOne, 'innerText');
-
const secondTicket = await page.waitToGetProperty(selectors.invoiceOutSummary.ticketTwo, 'innerText');
expect(firstTicket).toContain('Bat cave');
- expect(secondTicket).toContain('Stark tower');
+ expect(secondTicket).toContain('Bat cave');
});
});
diff --git a/e2e/paths/10-travel/02_basic_data_and_log.spec.js b/e2e/paths/10-travel/02_basic_data_and_log.spec.js
index 0079e80235..e6c601d7dc 100644
--- a/e2e/paths/10-travel/02_basic_data_and_log.spec.js
+++ b/e2e/paths/10-travel/02_basic_data_and_log.spec.js
@@ -105,17 +105,4 @@ describe('Travel basic data path', () => {
it(`should check the received checkbox was saved even tho it doesn't make sense`, async() => {
await page.waitForClassPresent(selectors.travelBasicData.received, 'checked');
});
-
- it('should navigate to the travel logs', async() => {
- pending('https://redmine.verdnatura.es/issues/5455');
- await page.accessToSection('travel.card.log');
- await page.waitForState('travel.card.log');
- });
-
- it('should check the 1st log contains details from the changes made', async() => {
- pending('https://redmine.verdnatura.es/issues/5455');
- const result = await page.waitToGetProperty(selectors.travelLog.firstLogFirstTD, 'innerText');
-
- expect(result).toContain('new reference!');
- });
});
diff --git a/front/core/components/treeview/index.spec.js b/front/core/components/treeview/index.spec.js
index 9277f3ee46..1979d517f4 100644
--- a/front/core/components/treeview/index.spec.js
+++ b/front/core/components/treeview/index.spec.js
@@ -33,18 +33,6 @@ describe('Component vnTreeview', () => {
$element.remove();
});
- // See how to test DOM element in Jest
- xdescribe('undrop()', () => {
- it(`should reset all drop events and properties`, () => {
- controller.dropping = angular.element(``);
- jest.spyOn(controller.dropping.classList, 'remove');
-
- controller.undrop();
-
- expect(controller.dropping).toBeNull();
- });
- });
-
describe('dragOver()', () => {
it(`should set the dragClientY property`, () => {
const event = new Event('dragover');
diff --git a/front/core/directives/rule.js b/front/core/directives/rule.js
index f65efe176c..34781c2aac 100644
--- a/front/core/directives/rule.js
+++ b/front/core/directives/rule.js
@@ -23,7 +23,6 @@ export function directive($translate, $window) {
let rule = $attrs.rule.split('.');
let modelName = rule.shift();
let fieldName = rule.shift();
-
let split = $attrs.ngModel.split('.');
if (!fieldName) fieldName = split.pop() || null;
if (!modelName) modelName = firstUpper(split.pop() || '');
diff --git a/front/core/locale/es.yml b/front/core/locale/es.yml
index 96c34d98ca..17e955ff52 100644
--- a/front/core/locale/es.yml
+++ b/front/core/locale/es.yml
@@ -68,3 +68,4 @@ Load more results: Cargar más resultados
Send cau: Enviar cau
By sending this ticket, all the data related to the error, the section, the user, etc., are already sent.: Al enviar este cau ya se envían todos los datos relacionados con el error, la sección, el usuario, etc
ExplainReason: Explique el motivo por el que no deberia aparecer este fallo
+You already have the mailAlias: Ya tienes este alias de correo
diff --git a/front/core/services/locale/en.yml b/front/core/services/locale/en.yml
index 2be73e696c..8eb8a6f0dd 100644
--- a/front/core/services/locale/en.yml
+++ b/front/core/services/locale/en.yml
@@ -3,4 +3,4 @@ Could not contact the server: Could not contact the server, make sure you have a
Please enter your username: Please enter your username
It seems that the server has fall down: It seems that the server has fall down, wait a few minutes and try again
Session has expired: Your session has expired, please login again
-Access denied: Access denied
\ No newline at end of file
+Access Denied: Access Denied
\ No newline at end of file
diff --git a/front/core/services/locale/es.yml b/front/core/services/locale/es.yml
index e9811e38f0..2c43ca3b2a 100644
--- a/front/core/services/locale/es.yml
+++ b/front/core/services/locale/es.yml
@@ -3,5 +3,5 @@ Could not contact the server: No se ha podido contactar con el servidor, asegura
Please enter your username: Por favor introduce tu nombre de usuario
It seems that the server has fall down: Parece que el servidor se ha caído, espera unos minutos e inténtalo de nuevo
Session has expired: Tu sesión ha expirado, por favor vuelve a iniciar sesión
-Access denied: Acción no permitida
+Access Denied: Acción no permitida
Direction not found: Dirección no encontrada
diff --git a/front/core/services/token.js b/front/core/services/token.js
index 426fe2b731..c8cb4f6bb6 100644
--- a/front/core/services/token.js
+++ b/front/core/services/token.js
@@ -82,7 +82,7 @@ export default class Token {
if (!data) return;
this.renewPeriod = data.renewPeriod;
this.stopRenewer();
- this.inservalId = setInterval(() => this.checkValidity(), data.renewInterval * 1000);
+ this.intervalId = setInterval(() => this.checkValidity(), data.renewInterval * 1000);
});
}
@@ -103,17 +103,13 @@ export default class Token {
const token = res.data;
this.set(token.id, now, token.ttl, this.remember);
})
- .catch(res => {
- if (res.data?.error?.code !== 'periodNotExceeded')
- throw res;
- })
.finally(() => {
this.checking = false;
});
}
stopRenewer() {
- clearInterval(this.inservalId);
+ clearInterval(this.intervalId);
}
}
Token.$inject = ['vnInterceptor', '$http', '$rootScope'];
diff --git a/front/salix/locale/es.yml b/front/salix/locale/es.yml
index bed41c63b4..044d0d043b 100644
--- a/front/salix/locale/es.yml
+++ b/front/salix/locale/es.yml
@@ -18,6 +18,7 @@ Show summary: Mostrar vista previa
What is new: Novedades de la versión
Settings: Ajustes
There is a new version, click here to reload: Hay una nueva versión, pulse aquí para recargar
+This ticket is locked: Este ticket está bloqueado
# Actions
diff --git a/front/salix/module.js b/front/salix/module.js
index f8fa0faf0b..0ce855308f 100644
--- a/front/salix/module.js
+++ b/front/salix/module.js
@@ -120,7 +120,7 @@ function $exceptionHandler(vnApp, $window, $state, $injector) {
messageT = 'Invalid login';
break;
case 403:
- messageT = 'Access denied';
+ messageT = exception.data?.error?.message || 'Access Denied';
break;
case 502:
messageT = 'It seems that the server has fall down';
diff --git a/loopback/common/mixins/loggable.js b/loopback/common/mixins/loggable.js
new file mode 100644
index 0000000000..760fdf60a0
--- /dev/null
+++ b/loopback/common/mixins/loggable.js
@@ -0,0 +1,12 @@
+const LoopBackContext = require('loopback-context');
+async function handleObserve(ctx) {
+ ctx.options.httpCtx = LoopBackContext.getCurrentContext();
+}
+module.exports = function(Self) {
+ let Mixin = {
+ 'before save': handleObserve,
+ 'before delete': handleObserve,
+ };
+ for (const [listener, handler] of Object.entries(Mixin))
+ Self.observe(listener, handler);
+};
diff --git a/loopback/common/models/loggable.js b/loopback/common/models/loggable.js
deleted file mode 100644
index 360c845661..0000000000
--- a/loopback/common/models/loggable.js
+++ /dev/null
@@ -1,15 +0,0 @@
-const LoopBackContext = require('loopback-context');
-
-module.exports = function(Self) {
- Self.setup = function() {
- Self.super_.setup.call(this);
- };
-
- Self.observe('before save', async function(ctx) {
- ctx.options.httpCtx = LoopBackContext.getCurrentContext();
- });
-
- Self.observe('before delete', async function(ctx) {
- ctx.options.httpCtx = LoopBackContext.getCurrentContext();
- });
-};
diff --git a/loopback/common/models/loggable.json b/loopback/common/models/loggable.json
deleted file mode 100644
index 9101532a34..0000000000
--- a/loopback/common/models/loggable.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "Loggable",
- "base": "VnModel",
- "validateUpsert": true
-}
diff --git a/loopback/locale/en.json b/loopback/locale/en.json
index 7d5b5ed47f..568916bef4 100644
--- a/loopback/locale/en.json
+++ b/loopback/locale/en.json
@@ -45,6 +45,7 @@
"Extension format is invalid": "Extension format is invalid",
"NO_ZONE_FOR_THIS_PARAMETERS": "NO_ZONE_FOR_THIS_PARAMETERS",
"This client can't be invoiced": "This client can't be invoiced",
+ "You must provide the correction information to generate a corrective invoice": "You must provide the correction information to generate a corrective invoice",
"The introduced hour already exists": "The introduced hour already exists",
"Invalid parameters to create a new ticket": "Invalid parameters to create a new ticket",
"Concept cannot be blank": "Concept cannot be blank",
@@ -68,8 +69,7 @@
"Sent units from ticket": "I sent *{{quantity}}* units of [{{concept}} ({{itemId}})]({{{itemUrl}}}) to *\"{{nickname}}\"* coming from ticket id [{{ticketId}}]({{{ticketUrl}}})",
"Change quantity": "{{concept}} change of {{oldQuantity}} to {{newQuantity}}",
"Claim will be picked": "The product from the claim [({{claimId}})]({{{claimUrl}}}) from the client *{{clientName}}* will be picked",
- "Claim state has changed to incomplete": "The state of the claim [({{claimId}})]({{{claimUrl}}}) from client *{{clientName}}* has changed to *incomplete*",
- "Claim state has changed to canceled": "The state of the claim [({{claimId}})]({{{claimUrl}}}) from client *{{clientName}}* has changed to *canceled*",
+ "Claim state has changed to": "The state of the claim [({{claimId}})]({{{claimUrl}}}) from client *{{clientName}}* has changed to *{{newState}}*",
"Customs agent is required for a non UEE member": "Customs agent is required for a non UEE member",
"Incoterms is required for a non UEE member": "Incoterms is required for a non UEE member",
"Client checked as validated despite of duplication": "Client checked as validated despite of duplication from client id {{clientId}}",
@@ -178,12 +178,13 @@
"The renew period has not been exceeded": "The renew period has not been exceeded",
"You can not use the same password": "You can not use the same password",
"Valid priorities": "Valid priorities: %d",
- "Negative basis of tickets": "Negative basis of tickets: {{ticketsIds}}",
+ "hasAnyNegativeBase": "Negative basis of tickets: {{ticketsIds}}",
+ "hasAnyPositiveBase": "Positive basis of tickets: {{ticketsIds}}",
"This ticket cannot be left empty.": "This ticket cannot be left empty. %s",
"Social name should be uppercase": "Social name should be uppercase",
"Street should be uppercase": "Street should be uppercase",
"You don't have enough privileges.": "You don't have enough privileges.",
- "This ticket is locked.": "This ticket is locked.",
+ "This ticket is locked": "This ticket is locked",
"This ticket is not editable.": "This ticket is not editable.",
"The ticket doesn't exist.": "The ticket doesn't exist.",
"The sales do not exists": "The sales do not exists",
@@ -200,5 +201,7 @@
"Try again": "Try again",
"keepPrice": "keepPrice",
"Cannot past travels with entries": "Cannot past travels with entries",
- "It was not able to remove the next expeditions:": "It was not able to remove the next expeditions: {{expeditions}}"
+ "It was not able to remove the next expeditions:": "It was not able to remove the next expeditions: {{expeditions}}",
+ "Incorrect pin": "Incorrect pin.",
+ "The notification subscription of this worker cant be modified": "The notification subscription of this worker cant be modified"
}
\ No newline at end of file
diff --git a/loopback/locale/es.json b/loopback/locale/es.json
index 01384efb42..5555ef8b05 100644
--- a/loopback/locale/es.json
+++ b/loopback/locale/es.json
@@ -72,6 +72,7 @@
"The secret can't be blank": "La contraseña no puede estar en blanco",
"We weren't able to send this SMS": "No hemos podido enviar el SMS",
"This client can't be invoiced": "Este cliente no puede ser facturado",
+ "You must provide the correction information to generate a corrective invoice": "Debes informar la información de corrección para generar una factura rectificativa",
"This ticket can't be invoiced": "Este ticket no puede ser facturado",
"You cannot add or modify services to an invoiced ticket": "No puedes añadir o modificar servicios a un ticket facturado",
"This ticket can not be modified": "Este ticket no puede ser modificado",
@@ -135,8 +136,7 @@
"Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} ({{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [{{ticketId}}]({{{ticketUrl}}})",
"Change quantity": "{{concept}} cambia de {{oldQuantity}} a {{newQuantity}}",
"Claim will be picked": "Se recogerá el género de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}*",
- "Claim state has changed to incomplete": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *incompleta*",
- "Claim state has changed to canceled": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *anulado*",
+ "Claim state has changed to": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *{{newState}}*",
"Client checked as validated despite of duplication": "Cliente comprobado a pesar de que existe el cliente id {{clientId}}",
"ORDER_ROW_UNAVAILABLE": "No hay disponibilidad de este producto",
"Distance must be lesser than 1000": "La distancia debe ser inferior a 1000",
@@ -305,14 +305,15 @@
"Mail not sent": "Se ha producido un fallo al enviar la factura al cliente [{{clientId}}]({{{clientUrl}}}), por favor revisa la dirección de correo electrónico",
"The renew period has not been exceeded": "El periodo de renovación no ha sido superado",
"Valid priorities": "Prioridades válidas: %d",
- "Negative basis of tickets": "Base negativa para los tickets: {{ticketsIds}}",
+ "hasAnyNegativeBase": "Base negativa para los tickets: {{ticketsIds}}",
+ "hasAnyPositiveBase": "Base positivas para los tickets: {{ticketsIds}}",
"You cannot assign an alias that you are not assigned to": "No puede asignar un alias que no tenga asignado",
"This ticket cannot be left empty.": "Este ticket no se puede dejar vacío. %s",
"The company has not informed the supplier account for bank transfers": "La empresa no tiene informado la cuenta de proveedor para transferencias bancarias",
"You cannot assign/remove an alias that you are not assigned to": "No puede asignar/eliminar un alias que no tenga asignado",
"This invoice has a linked vehicle.": "Esta factura tiene un vehiculo vinculado",
"You don't have enough privileges.": "No tienes suficientes permisos.",
- "This ticket is locked.": "Este ticket está bloqueado.",
+ "This ticket is locked": "Este ticket está bloqueado.",
"This ticket is not editable.": "Este ticket no es editable.",
"The ticket doesn't exist.": "No existe el ticket.",
"Social name should be uppercase": "La razón social debe ir en mayúscula",
@@ -329,5 +330,11 @@
"The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mínima",
"quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mínima",
"Cannot past travels with entries": "No se pueden pasar envíos con entradas",
- "It was not able to remove the next expeditions:": "No se pudo eliminar las siguientes expediciones: {{expeditions}}"
-}
\ No newline at end of file
+ "It was not able to remove the next expeditions:": "No se pudo eliminar las siguientes expediciones: {{expeditions}}",
+ "This claim has been updated": "La reclamación con Id: {{claimId}}, ha sido actualizada",
+ "This user does not have an assigned tablet": "Este usuario no tiene tablet asignada",
+ "Incorrect pin": "Pin incorrecto.",
+ "You already have the mailAlias": "Ya tienes este alias de correo",
+ "The alias cant be modified": "Este alias de correo no puede ser modificado",
+ "No tickets to invoice": "No hay tickets para facturar"
+}
diff --git a/loopback/server/boot/date.js b/loopback/server/boot/date.js
index 8107455625..d592dc416b 100644
--- a/loopback/server/boot/date.js
+++ b/loopback/server/boot/date.js
@@ -1,6 +1,5 @@
module.exports = () => {
- Date.vnUTC = () => {
- const env = process.env.NODE_ENV;
+ Date.vnUTC = (env = process.env.NODE_ENV) => {
if (!env || env === 'development')
return new Date(Date.UTC(2001, 0, 1, 11));
diff --git a/loopback/server/middleware.json b/loopback/server/middleware.json
index 31a2f113b5..cfc6932175 100644
--- a/loopback/server/middleware.json
+++ b/loopback/server/middleware.json
@@ -39,7 +39,7 @@
"./middleware/salix-version": {}
},
"parse": {
- "body-parser#json":{}
+ "body-parser#json":{}
},
"routes": {
"loopback#rest": {
diff --git a/loopback/server/model-config.json b/loopback/server/model-config.json
index 33ef3797d6..56b5360e84 100644
--- a/loopback/server/model-config.json
+++ b/loopback/server/model-config.json
@@ -25,20 +25,19 @@
"FieldAcl": {
"dataSource": "vn"
},
- "Role": {
- "dataSource": "vn",
- "options": {
- "mysql": {
- "table": "salix.Role"
- }
- }
- },
"RoleMapping": {
"dataSource": "vn",
"options": {
"mysql": {
"table": "salix.RoleMapping"
}
+ },
+ "relations": {
+ "role": {
+ "type": "belongsTo",
+ "model": "VnRole",
+ "foreignKey": "roleId"
+ }
}
},
"Schema": {
diff --git a/modules/account/back/methods/account/specs/change-password.spec.js b/modules/account/back/methods/account/specs/change-password.spec.js
index 2fa3010afb..c799602128 100644
--- a/modules/account/back/methods/account/specs/change-password.spec.js
+++ b/modules/account/back/methods/account/specs/change-password.spec.js
@@ -2,7 +2,7 @@ const {models} = require('vn-loopback/server/server');
describe('account changePassword()', () => {
const userId = 70;
- const unauthCtx = {
+ const unAuthCtx = {
req: {
headers: {},
connection: {
@@ -79,7 +79,7 @@ describe('account changePassword()', () => {
passExpired: yesterday
}
, options);
- await models.VnUser.signIn(unauthCtx, 'trainee', 'nightmare', options);
+ await models.VnUser.signIn(unAuthCtx, 'trainee', 'nightmare', options);
} catch (e) {
if (e.message != 'Pass expired')
throw e;
diff --git a/modules/account/back/methods/account/sync.js b/modules/account/back/methods/account/sync.js
index 0eab0ef63a..1026c50208 100644
--- a/modules/account/back/methods/account/sync.js
+++ b/modules/account/back/methods/account/sync.js
@@ -26,24 +26,46 @@ module.exports = Self => {
});
Self.sync = async function(userName, password, force, options) {
+ const models = Self.app.models;
const myOptions = {};
-
+ let tx;
+
if (typeof options == 'object')
Object.assign(myOptions, options);
- const models = Self.app.models;
- const user = await models.VnUser.findOne({
- fields: ['id', 'password'],
- where: {name: userName}
- }, myOptions);
+ if (!myOptions.transaction) {
+ tx = await Self.beginTransaction({});
+ myOptions.transaction = tx;
+ };
- if (user && password && !await user.hasPassword(password))
- throw new ForbiddenError('Wrong password');
+ try {
+ const user = await models.VnUser.findOne({
+ fields: ['id', 'password'],
+ where: {name: userName}
+ }, myOptions);
- const isSync = !await models.UserSync.exists(userName, myOptions);
+ if (user && password && !await user.hasPassword(password))
+ throw new ForbiddenError('Wrong password');
- if (!force && isSync && user) return;
- await models.AccountConfig.syncUser(userName, password);
- await models.UserSync.destroyById(userName, myOptions);
+ const isSync = !await models.UserSync.exists(userName, myOptions);
+
+ if (!force && isSync && user) {
+ if (tx) await tx.rollback();
+ return;
+ }
+
+ await Self.rawSql(`
+ SELECT id
+ FROM account.user
+ WHERE id = ?
+ FOR UPDATE`, [user.id], myOptions);
+
+ await models.AccountConfig.syncUser(userName, password);
+ await models.UserSync.destroyById(userName, myOptions);
+ if (tx) await tx.commit();
+ } catch (err) {
+ if (tx) await tx.rollback();
+ throw err;
+ }
};
};
diff --git a/modules/account/back/model-config.json b/modules/account/back/model-config.json
index b4bd6dbafd..0cd43d0cec 100644
--- a/modules/account/back/model-config.json
+++ b/modules/account/back/model-config.json
@@ -14,6 +14,9 @@
"MailAliasAccount": {
"dataSource": "vn"
},
+ "MailAliasAcl": {
+ "dataSource": "vn"
+ },
"MailConfig": {
"dataSource": "vn"
},
diff --git a/modules/account/back/models/account.json b/modules/account/back/models/account.json
index 3c22521cbe..6c27846966 100644
--- a/modules/account/back/models/account.json
+++ b/modules/account/back/models/account.json
@@ -1,49 +1,49 @@
{
- "name": "Account",
- "base": "VnModel",
- "options": {
- "mysql": {
- "table": "account.account"
- }
- },
- "properties": {
- "id": {
- "id": true
- }
- },
- "relations": {
- "user": {
- "type": "belongsTo",
- "model": "VnUser",
- "foreignKey": "id"
- },
- "aliases": {
- "type": "hasMany",
- "model": "MailAliasAccount",
- "foreignKey": "account"
- }
- },
- "acls": [
- {
- "property": "login",
- "accessType": "EXECUTE",
- "principalType": "ROLE",
- "principalId": "$everyone",
- "permission": "ALLOW"
+ "name": "Account",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "account.account"
+ }
+ },
+ "properties": {
+ "id": {
+ "id": true
+ }
+ },
+ "relations": {
+ "user": {
+ "type": "belongsTo",
+ "model": "VnUser",
+ "foreignKey": "id"
},
- {
+ "aliases": {
+ "type": "hasMany",
+ "model": "MailAliasAccount",
+ "foreignKey": "account"
+ }
+ },
+ "acls": [
+ {
+ "property": "login",
+ "accessType": "EXECUTE",
+ "principalType": "ROLE",
+ "principalId": "$everyone",
+ "permission": "ALLOW"
+ },
+ {
"property": "logout",
- "accessType": "EXECUTE",
- "principalType": "ROLE",
- "principalId": "$authenticated",
- "permission": "ALLOW"
- },
- {
+ "accessType": "EXECUTE",
+ "principalType": "ROLE",
+ "principalId": "$authenticated",
+ "permission": "ALLOW"
+ },
+ {
"property": "changePassword",
- "accessType": "EXECUTE",
- "principalType": "ROLE",
- "principalId": "$everyone",
- "permission": "ALLOW"
- }
- ]
+ "accessType": "EXECUTE",
+ "principalType": "ROLE",
+ "principalId": "$everyone",
+ "permission": "ALLOW"
+ }
+ ]
}
diff --git a/modules/account/back/models/ldap-config.js b/modules/account/back/models/ldap-config.js
index b557d243c8..89f0add486 100644
--- a/modules/account/back/models/ldap-config.js
+++ b/modules/account/back/models/ldap-config.js
@@ -239,7 +239,7 @@ module.exports = Self => {
// Prepare data
- let roles = await $.Role.find({
+ let roles = await $.VnRole.find({
fields: ['id', 'name', 'description']
});
let roleRoles = await $.RoleRole.find({
diff --git a/modules/account/back/models/mail-alias-account.js b/modules/account/back/models/mail-alias-account.js
index 6f5213f24b..5eb5614081 100644
--- a/modules/account/back/models/mail-alias-account.js
+++ b/modules/account/back/models/mail-alias-account.js
@@ -2,54 +2,44 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
+ Self.rewriteDbError(function(err) {
+ if (err.code === 'ER_DUP_ENTRY')
+ return new UserError(`You already have the mailAlias`);
+ return err;
+ });
+
Self.observe('before save', async ctx => {
const changes = ctx.currentInstance || ctx.instance;
- await Self.hasGrant(ctx, changes.mailAlias);
+ await checkModifyPermission(ctx, changes.mailAlias);
});
Self.observe('before delete', async ctx => {
const mailAliasAccount = await Self.findById(ctx.where.id);
- await Self.hasGrant(ctx, mailAliasAccount.mailAlias);
+ await checkModifyPermission(ctx, mailAliasAccount.mailAlias);
});
- /**
- * Checks if current user has
- * grant to add/remove alias
- *
- * @param {Object} ctx - Request context
- * @param {Interger} mailAlias - mailAlias id
- * @return {Boolean} True for user with grant
- */
- Self.hasGrant = async function(ctx, mailAlias) {
+ async function checkModifyPermission(ctx, mailAliasFk) {
+ const userId = ctx.options.accessToken.userId;
const models = Self.app.models;
- const accessToken = {req: {accessToken: ctx.options.accessToken}};
- const userId = accessToken.req.accessToken.userId;
- const canEditAlias = await models.ACL.checkAccessAcl(accessToken, 'MailAliasAccount', 'canEditAlias', 'WRITE');
- if (canEditAlias) return true;
+ const roles = await models.RoleMapping.find({
+ fields: ['roleId'],
+ where: {principalId: userId}
+ });
- const user = await models.VnUser.findById(userId, {fields: ['hasGrant']});
- if (!user.hasGrant)
- throw new UserError(`You don't have grant privilege`);
-
- const account = await models.Account.findById(userId, {
- fields: ['id'],
- include: {
- relation: 'aliases',
- scope: {
- fields: ['mailAlias']
- }
+ const availableMailAlias = await models.MailAliasAcl.findOne({
+ fields: ['mailAliasFk'],
+ include: {relation: 'mailAlias'},
+ where: {
+ roleFk: {
+ inq: roles.map(role => role.roleId),
+ },
+ mailAliasFk
}
});
- const aliases = account.aliases().map(alias => alias.mailAlias);
-
- const hasAlias = aliases.includes(mailAlias);
- if (!hasAlias)
- throw new UserError(`You cannot assign/remove an alias that you are not assigned to`);
-
- return true;
- };
+ if (!availableMailAlias) throw new UserError('The alias cant be modified');
+ }
};
diff --git a/modules/account/back/models/mail-alias-acl.json b/modules/account/back/models/mail-alias-acl.json
new file mode 100644
index 0000000000..014b95d144
--- /dev/null
+++ b/modules/account/back/models/mail-alias-acl.json
@@ -0,0 +1,31 @@
+{
+ "name": "MailAliasAcl",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "account.mailAliasAcl"
+ }
+ },
+ "properties": {
+ "mailAliasFk": {
+ "id": true,
+ "type": "number"
+ },
+ "roleFk": {
+ "id": true,
+ "type": "number"
+ }
+ },
+ "relations": {
+ "mailAlias": {
+ "type": "belongsTo",
+ "model": "MailAlias",
+ "foreignKey": "mailAliasFk"
+ },
+ "role": {
+ "type": "belongsTo",
+ "model": "Role",
+ "foreignKey": "roleFk"
+ }
+ }
+}
diff --git a/modules/account/back/models/role-inherit.json b/modules/account/back/models/role-inherit.json
index 4b69ffdc26..a89f47b77a 100644
--- a/modules/account/back/models/role-inherit.json
+++ b/modules/account/back/models/role-inherit.json
@@ -15,12 +15,12 @@
"relations": {
"owner": {
"type": "belongsTo",
- "model": "Role",
+ "model": "VnRole",
"foreignKey": "role"
},
"inherits": {
"type": "belongsTo",
- "model": "Role",
+ "model": "VnRole",
"foreignKey": "inheritsFrom"
}
}
diff --git a/modules/account/back/models/role-role.json b/modules/account/back/models/role-role.json
index 77df7a9204..e59351c598 100644
--- a/modules/account/back/models/role-role.json
+++ b/modules/account/back/models/role-role.json
@@ -14,12 +14,12 @@
"relations": {
"owner": {
"type": "belongsTo",
- "model": "Role",
+ "model": "VnRole",
"foreignKey": "role"
},
"inherits": {
"type": "belongsTo",
- "model": "Role",
+ "model": "VnRole",
"foreignKey": "inheritsFrom"
}
}
diff --git a/modules/account/back/models/samba-config.js b/modules/account/back/models/samba-config.js
index f5672ca215..927510a291 100644
--- a/modules/account/back/models/samba-config.js
+++ b/modules/account/back/models/samba-config.js
@@ -7,7 +7,8 @@ const execFile = require('child_process').execFile;
* https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/useraccountcontrol-manipulate-account-properties
*/
const UserAccountControlFlags = {
- ACCOUNTDISABLE: 2
+ ACCOUNTDISABLE: 0x2,
+ DONT_EXPIRE_PASSWD: 0x10000
};
module.exports = Self => {
@@ -118,7 +119,8 @@ module.exports = Self => {
}
entry = {
- userAccountControl: sambaUser.userAccountControl
+ userAccountControl: (sambaUser.userAccountControl
+ | UserAccountControlFlags.DONT_EXPIRE_PASSWD)
& ~UserAccountControlFlags.ACCOUNTDISABLE,
uidNumber: info.uidNumber,
accountExpires: 0,
diff --git a/modules/account/back/models/sign_in-log.json b/modules/account/back/models/sign_in-log.json
index c5c014e60a..8656e92dc2 100644
--- a/modules/account/back/models/sign_in-log.json
+++ b/modules/account/back/models/sign_in-log.json
@@ -25,7 +25,15 @@
"type": "number"
},
"ip": {
- "type": "string"
+ "type": "string"
+ },
+ "userName": {
+ "type": "string"
+ },
+ "owner": {
+ "type": "boolean",
+ "required": true,
+ "default": true
}
},
"relations": {
diff --git a/modules/account/front/acl/create/index.html b/modules/account/front/acl/create/index.html
index 7f4fa9e462..14332f737a 100644
--- a/modules/account/front/acl/create/index.html
+++ b/modules/account/front/acl/create/index.html
@@ -15,7 +15,7 @@
@@ -32,7 +32,7 @@
diff --git a/modules/account/front/acl/search-panel/index.html b/modules/account/front/acl/search-panel/index.html
index b83b9c255c..a3efab440d 100644
--- a/modules/account/front/acl/search-panel/index.html
+++ b/modules/account/front/acl/search-panel/index.html
@@ -4,7 +4,7 @@
-
\ No newline at end of file
+
diff --git a/modules/account/front/create/index.html b/modules/account/front/create/index.html
index acc07d3468..70a5188857 100644
--- a/modules/account/front/create/index.html
+++ b/modules/account/front/create/index.html
@@ -30,7 +30,7 @@
+ url="VnRoles">
diff --git a/modules/account/front/role/basic-data/index.html b/modules/account/front/role/basic-data/index.html
index 7499271864..846f8b455e 100644
--- a/modules/account/front/role/basic-data/index.html
+++ b/modules/account/front/role/basic-data/index.html
@@ -1,25 +1,27 @@
\ No newline at end of file
+
diff --git a/modules/account/front/role/card/index.js b/modules/account/front/role/card/index.js
index 6f888211de..3c7c758efc 100644
--- a/modules/account/front/role/card/index.js
+++ b/modules/account/front/role/card/index.js
@@ -3,7 +3,7 @@ import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard {
reload() {
- this.$http.get(`Roles/${this.$params.id}`)
+ this.$http.get(`VnRoles/${this.$params.id}`)
.then(res => this.role = res.data);
}
}
diff --git a/modules/account/front/role/card/index.spec.js b/modules/account/front/role/card/index.spec.js
index f39840e5fa..f02c08f285 100644
--- a/modules/account/front/role/card/index.spec.js
+++ b/modules/account/front/role/card/index.spec.js
@@ -1,6 +1,6 @@
import './index';
-describe('component vnRoleCard', () => {
+fdescribe('component vnRoleCard', () => {
let controller;
let $httpBackend;
@@ -15,7 +15,7 @@ describe('component vnRoleCard', () => {
it('should reload the controller data', () => {
controller.$params.id = 1;
- $httpBackend.expectGET('Roles/1').respond('foo');
+ $httpBackend.expectGET('VnRoles/1').respond('foo');
controller.reload();
$httpBackend.flush();
diff --git a/modules/account/front/role/create/index.html b/modules/account/front/role/create/index.html
index 02900d5808..77d6fc2c1f 100644
--- a/modules/account/front/role/create/index.html
+++ b/modules/account/front/role/create/index.html
@@ -1,6 +1,6 @@
@@ -12,15 +12,15 @@
+ rule="VnRole.description">
diff --git a/modules/account/front/role/descriptor/index.html b/modules/account/front/role/descriptor/index.html
index 4cd4ac8229..d8bf4857a0 100644
--- a/modules/account/front/role/descriptor/index.html
+++ b/modules/account/front/role/descriptor/index.html
@@ -24,4 +24,4 @@
on-accept="$ctrl.onDelete()"
question="Are you sure you want to continue?"
message="Role will be removed">
-
\ No newline at end of file
+
diff --git a/modules/account/front/role/descriptor/index.js b/modules/account/front/role/descriptor/index.js
index a1b5781336..17b585cb79 100644
--- a/modules/account/front/role/descriptor/index.js
+++ b/modules/account/front/role/descriptor/index.js
@@ -11,7 +11,7 @@ class Controller extends Descriptor {
}
onDelete() {
- return this.$http.delete(`Roles/${this.id}`)
+ return this.$http.delete(`VnRoles/${this.id}`)
.then(() => this.$state.go('account.role'))
.then(() => this.vnApp.showSuccess(this.$t('Role removed')));
}
diff --git a/modules/account/front/role/descriptor/index.spec.js b/modules/account/front/role/descriptor/index.spec.js
index e2761c6396..eafb96727e 100644
--- a/modules/account/front/role/descriptor/index.spec.js
+++ b/modules/account/front/role/descriptor/index.spec.js
@@ -1,6 +1,6 @@
import './index';
-describe('component vnRoleDescriptor', () => {
+fdescribe('component vnRoleDescriptor', () => {
let controller;
let $httpBackend;
@@ -18,7 +18,7 @@ describe('component vnRoleDescriptor', () => {
controller.$state.go = jest.fn();
jest.spyOn(controller.vnApp, 'showSuccess');
- $httpBackend.expectDELETE('Roles/1').respond();
+ $httpBackend.expectDELETE('VnRoles/1').respond();
controller.onDelete();
$httpBackend.flush();
diff --git a/modules/account/front/role/main/index.html b/modules/account/front/role/main/index.html
index 9d7e6e053a..cfef28e57a 100644
--- a/modules/account/front/role/main/index.html
+++ b/modules/account/front/role/main/index.html
@@ -1,6 +1,6 @@
@@ -15,4 +15,4 @@
-
\ No newline at end of file
+
diff --git a/modules/account/front/role/subroles/index.html b/modules/account/front/role/subroles/index.html
index bc554b9f93..eba1002b0e 100644
--- a/modules/account/front/role/subroles/index.html
+++ b/modules/account/front/role/subroles/index.html
@@ -33,14 +33,14 @@
ng-click="$ctrl.onAddClick()"
fixed-bottom-right>
-
@@ -49,7 +49,7 @@
- this.$.summary = res.data);
}
diff --git a/modules/account/front/search-panel/index.html b/modules/account/front/search-panel/index.html
index f80b537aa4..a539d96576 100644
--- a/modules/account/front/search-panel/index.html
+++ b/modules/account/front/search-panel/index.html
@@ -19,7 +19,7 @@
vn-one
label="Role"
ng-model="filter.roleFk"
- url="Roles"
+ url="VnRoles"
value-field="id"
show-field="name">
@@ -28,4 +28,4 @@
-
\ No newline at end of file
+
diff --git a/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.js b/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.js
index be3baccd73..393c3b10d1 100644
--- a/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.js
+++ b/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.js
@@ -120,10 +120,10 @@ module.exports = Self => {
observationTypeFk: obsevationType.id
}, myOptions);
- await models.TicketTracking.create({
+ await models.Ticket.state(ctx, {
ticketFk: newRefundTicket.id,
stateFk: state.id,
- workerFk: worker.id
+ userFk: worker.id
}, myOptions);
const salesToRefund = await models.ClaimBeginning.find(salesFilter, myOptions);
diff --git a/modules/claim/back/methods/claim-dms/removeFile.js b/modules/claim/back/methods/claim-dms/removeFile.js
index edc714235f..28e78c9d70 100644
--- a/modules/claim/back/methods/claim-dms/removeFile.js
+++ b/modules/claim/back/methods/claim-dms/removeFile.js
@@ -1,3 +1,5 @@
+const UserError = require('vn-loopback/util/user-error');
+
module.exports = Self => {
Self.remoteMethodCtx('removeFile', {
description: 'Removes a claim document',
@@ -19,8 +21,8 @@ module.exports = Self => {
});
Self.removeFile = async(ctx, id, options) => {
- let tx;
const myOptions = {};
+ let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
@@ -31,19 +33,18 @@ module.exports = Self => {
}
try {
- const models = Self.app.models;
- const targetClaimDms = await models.ClaimDms.findById(id, null, myOptions);
- const targetDms = await models.Dms.findById(targetClaimDms.dmsFk, null, myOptions);
- const trashDmsType = await models.DmsType.findOne({where: {code: 'trash'}}, myOptions);
+ const claimDms = await Self.findById(id, null, myOptions);
- await models.Dms.removeFile(ctx, targetClaimDms.dmsFk, myOptions);
- await targetClaimDms.destroy(myOptions);
+ const targetDms = await Self.app.models.Dms.removeFile(ctx, claimDms.dmsFk, myOptions);
- await targetDms.updateAttribute('dmsTypeFk', trashDmsType.id, myOptions);
+ if (!targetDms || ! claimDms)
+ throw new UserError('Try again');
+
+ const claimDmsDestroyed = await claimDms.destroy(myOptions);
if (tx) await tx.commit();
- return targetDms;
+ return claimDmsDestroyed;
} catch (e) {
if (tx) await tx.rollback();
throw e;
diff --git a/modules/claim/back/methods/claim/specs/updateClaim.spec.js b/modules/claim/back/methods/claim/specs/updateClaim.spec.js
index 85ada869a9..e2d5fcfebf 100644
--- a/modules/claim/back/methods/claim/specs/updateClaim.spec.js
+++ b/modules/claim/back/methods/claim/specs/updateClaim.spec.js
@@ -1,8 +1,9 @@
const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
-
+const i18n = require('i18n');
describe('Update Claim', () => {
let url;
+ let claimStatesMap = {};
beforeAll(async() => {
url = await app.models.Url.getUrl();
const activeCtx = {
@@ -16,6 +17,8 @@ describe('Update Claim', () => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
+ const claimStates = await app.models.ClaimState.find();
+ claimStatesMap = claimStates.reduce((acc, state) => ({...acc, [state.code]: state.id}), {});
});
const newDate = Date.vnNew();
const originalData = {
@@ -62,6 +65,123 @@ describe('Update Claim', () => {
expect(error.message).toEqual(`You don't have enough privileges to change that field`);
});
+ it(`should success to update the claimState to 'pending' and send a rocket message`, async() => {
+ const tx = await app.models.Claim.beginTransaction({});
+
+ try {
+ const options = {transaction: tx};
+
+ const newClaim = await app.models.Claim.create(originalData, options);
+
+ const chatModel = app.models.Chat;
+ spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
+
+ const pendingState = claimStatesMap.pending;
+ const claimManagerId = 72;
+ const ctx = {
+ req: {
+ accessToken: {userId: claimManagerId},
+ headers: {origin: url}
+ },
+ args: {
+ observation: 'valid observation',
+ claimStateFk: pendingState,
+ hasToPickUp: false
+ }
+ };
+ ctx.req.__ = i18n.__;
+ await app.models.Claim.updateClaim(ctx, newClaim.id, options);
+
+ let updatedClaim = await app.models.Claim.findById(newClaim.id, null, options);
+
+ expect(updatedClaim.observation).toEqual(ctx.args.observation);
+ expect(chatModel.sendCheckingPresence).toHaveBeenCalled();
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ it(`should success to update the claimState to 'managed' and send a rocket message`, async() => {
+ const tx = await app.models.Claim.beginTransaction({});
+
+ try {
+ const options = {transaction: tx};
+
+ const newClaim = await app.models.Claim.create(originalData, options);
+
+ const chatModel = app.models.Chat;
+ spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
+
+ const managedState = claimStatesMap.managed;
+ const claimManagerId = 72;
+ const ctx = {
+ req: {
+ accessToken: {userId: claimManagerId},
+ headers: {origin: url}
+ },
+ args: {
+ observation: 'valid observation',
+ claimStateFk: managedState,
+ hasToPickUp: false
+ }
+ };
+ ctx.req.__ = i18n.__;
+ await app.models.Claim.updateClaim(ctx, newClaim.id, options);
+
+ let updatedClaim = await app.models.Claim.findById(newClaim.id, null, options);
+
+ expect(updatedClaim.observation).toEqual(ctx.args.observation);
+ expect(chatModel.sendCheckingPresence).toHaveBeenCalled();
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ it(`should success to update the claimState to 'resolved' and send a rocket message`, async() => {
+ const tx = await app.models.Claim.beginTransaction({});
+
+ try {
+ const options = {transaction: tx};
+
+ const newClaim = await app.models.Claim.create(originalData, options);
+
+ const chatModel = app.models.Chat;
+ spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
+
+ const resolvedState = claimStatesMap.resolved;
+ const claimManagerId = 72;
+ const ctx = {
+ req: {
+ accessToken: {userId: claimManagerId},
+ headers: {origin: url}
+ },
+ args: {
+ observation: 'valid observation',
+ claimStateFk: resolvedState,
+ hasToPickUp: false
+ }
+ };
+ ctx.req.__ = i18n.__;
+ await app.models.Claim.updateClaim(ctx, newClaim.id, options);
+
+ let updatedClaim = await app.models.Claim.findById(newClaim.id, null, options);
+
+ expect(updatedClaim.observation).toEqual(ctx.args.observation);
+ expect(chatModel.sendCheckingPresence).toHaveBeenCalled();
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
it(`should success to update the claimState to 'canceled' and send a rocket message`, async() => {
const tx = await app.models.Claim.beginTransaction({});
@@ -73,7 +193,7 @@ describe('Update Claim', () => {
const chatModel = app.models.Chat;
spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
- const canceledState = 4;
+ const canceledState = claimStatesMap.canceled;
const claimManagerId = 72;
const ctx = {
req: {
@@ -86,9 +206,7 @@ describe('Update Claim', () => {
hasToPickUp: false
}
};
- ctx.req.__ = (value, params) => {
- return params.nickname;
- };
+ ctx.req.__ = i18n.__;
await app.models.Claim.updateClaim(ctx, newClaim.id, options);
let updatedClaim = await app.models.Claim.findById(newClaim.id, null, options);
@@ -127,9 +245,7 @@ describe('Update Claim', () => {
hasToPickUp: false
}
};
- ctx.req.__ = (value, params) => {
- return params.nickname;
- };
+ ctx.req.__ = i18n.__;
await app.models.Claim.updateClaim(ctx, newClaim.id, options);
let updatedClaim = await app.models.Claim.findById(newClaim.id, null, options);
@@ -168,9 +284,7 @@ describe('Update Claim', () => {
hasToPickUp: true
}
};
- ctx.req.__ = (value, params) => {
- return params.nickname;
- };
+ ctx.req.__ = i18n.__;
await app.models.Claim.updateClaim(ctx, newClaim.id, options);
let updatedClaim = await app.models.Claim.findById(newClaim.id, null, options);
diff --git a/modules/claim/back/methods/claim/updateClaim.js b/modules/claim/back/methods/claim/updateClaim.js
index d99528413c..68fff7846e 100644
--- a/modules/claim/back/methods/claim/updateClaim.js
+++ b/modules/claim/back/methods/claim/updateClaim.js
@@ -96,12 +96,9 @@ module.exports = Self => {
// When claimState has been changed
if (args.claimStateFk) {
const newState = await models.ClaimState.findById(args.claimStateFk, null, myOptions);
- if (newState.hasToNotify) {
- if (newState.code == 'incomplete')
- await notifyStateChange(ctx, salesPerson.id, claim, newState.code);
- if (newState.code == 'canceled')
- await notifyStateChange(ctx, claim.workerFk, claim, newState.code);
- }
+ await notifyStateChange(ctx, salesPerson.id, claim, newState.code);
+ if (newState.code == 'canceled')
+ await notifyStateChange(ctx, claim.workerFk, claim, newState.code);
}
if (tx) await tx.commit();
@@ -113,15 +110,16 @@ module.exports = Self => {
}
};
- async function notifyStateChange(ctx, workerId, claim, state) {
+ async function notifyStateChange(ctx, workerId, claim, newState) {
const models = Self.app.models;
const url = await models.Url.getUrl();
const $t = ctx.req.__; // $translate
- const message = $t(`Claim state has changed to ${state}`, {
+ const message = $t(`Claim state has changed to`, {
claimId: claim.id,
clientName: claim.client().name,
- claimUrl: `${url}claim/${claim.id}/summary`
+ claimUrl: `${url}claim/${claim.id}/summary`,
+ newState
});
await models.Chat.sendCheckingPresence(ctx, workerId, message);
}
diff --git a/modules/claim/back/methods/claim/uploadFile.js b/modules/claim/back/methods/claim/uploadFile.js
index 3d0737cf81..4fd041456a 100644
--- a/modules/claim/back/methods/claim/uploadFile.js
+++ b/modules/claim/back/methods/claim/uploadFile.js
@@ -1,6 +1,3 @@
-const UserError = require('vn-loopback/util/user-error');
-const fs = require('fs-extra');
-const path = require('path');
module.exports = Self => {
Self.remoteMethodCtx('uploadFile', {
@@ -57,96 +54,33 @@ module.exports = Self => {
});
Self.uploadFile = async(ctx, id, options) => {
- const tx = await Self.beginTransaction({});
+ const {Dms, ClaimDms} = Self.app.models;
const myOptions = {};
+ let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
- if (!myOptions.transaction)
+ if (!myOptions.transaction) {
+ tx = await Self.beginTransaction({});
myOptions.transaction = tx;
+ }
- const models = Self.app.models;
- const promises = [];
- const TempContainer = models.TempContainer;
- const ClaimContainer = models.ClaimContainer;
- const fileOptions = {};
- const args = ctx.args;
-
- let srcFile;
try {
- const hasWriteRole = await models.DmsType.hasWriteRole(ctx, args.dmsTypeId, myOptions);
- if (!hasWriteRole)
- throw new UserError(`You don't have enough privileges`);
+ const uploadedFiles = await Dms.uploadFile(ctx, myOptions);
- // Upload file to temporary path
- const tempContainer = await TempContainer.container('dms');
- const uploaded = await TempContainer.upload(tempContainer.name, ctx.req, ctx.result, fileOptions);
- const files = Object.values(uploaded.files).map(file => {
- return file[0];
- });
-
- const addedDms = [];
- for (const uploadedFile of files) {
- const newDms = await createDms(ctx, uploadedFile, myOptions);
- const pathHash = ClaimContainer.getHash(newDms.id);
-
- const file = await TempContainer.getFile(tempContainer.name, uploadedFile.name);
- srcFile = path.join(file.client.root, file.container, file.name);
-
- const claimContainer = await ClaimContainer.container(pathHash);
- const dstFile = path.join(claimContainer.client.root, pathHash, newDms.file);
-
- await fs.move(srcFile, dstFile, {
- overwrite: true
- });
-
- addedDms.push(newDms);
- }
-
- addedDms.forEach(dms => {
- const newClaimDms = models.ClaimDms.create({
- claimFk: id,
- dmsFk: dms.id
- }, myOptions);
-
- promises.push(newClaimDms);
- });
- const resolvedPromises = await Promise.all(promises);
+ const promises = uploadedFiles.map(dms => ClaimDms.create({
+ claimFk: id,
+ dmsFk: dms.id
+ }, myOptions));
+ await Promise.all(promises);
if (tx) await tx.commit();
- return resolvedPromises;
+ return uploadedFiles;
} catch (e) {
if (tx) await tx.rollback();
-
- if (fs.existsSync(srcFile))
- await fs.unlink(srcFile);
-
throw e;
}
};
-
- async function createDms(ctx, file, myOptions) {
- const models = Self.app.models;
- const myUserId = ctx.req.accessToken.userId;
- const args = ctx.args;
-
- const newDms = await models.Dms.create({
- workerFk: myUserId,
- dmsTypeFk: args.dmsTypeId,
- companyFk: args.companyId,
- warehouseFk: args.warehouseId,
- reference: args.reference,
- description: args.description,
- contentType: file.type,
- hasFile: args.hasFile
- }, myOptions);
-
- let fileName = file.name;
- const extension = models.DmsContainer.getFileExtension(fileName);
- fileName = `${newDms.id}.${extension}`;
-
- return newDms.updateAttribute('file', fileName, myOptions);
- }
};
diff --git a/modules/claim/back/models/claim-beginning.json b/modules/claim/back/models/claim-beginning.json
index d355881e85..d224586da4 100644
--- a/modules/claim/back/models/claim-beginning.json
+++ b/modules/claim/back/models/claim-beginning.json
@@ -1,6 +1,9 @@
{
"name": "ClaimBeginning",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "claimBeginning"
diff --git a/modules/claim/back/models/claim-development.json b/modules/claim/back/models/claim-development.json
index b0f352f50e..732955660c 100644
--- a/modules/claim/back/models/claim-development.json
+++ b/modules/claim/back/models/claim-development.json
@@ -1,6 +1,9 @@
{
"name": "ClaimDevelopment",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "claimDevelopment"
diff --git a/modules/claim/back/models/claim-dms.json b/modules/claim/back/models/claim-dms.json
index 26c90fd690..ed12c925b0 100644
--- a/modules/claim/back/models/claim-dms.json
+++ b/modules/claim/back/models/claim-dms.json
@@ -1,6 +1,9 @@
{
"name": "ClaimDms",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "claimDms"
diff --git a/modules/claim/back/models/claim-end.json b/modules/claim/back/models/claim-end.json
index 9f12ff93a0..ef5477f50e 100644
--- a/modules/claim/back/models/claim-end.json
+++ b/modules/claim/back/models/claim-end.json
@@ -1,6 +1,9 @@
{
"name": "ClaimEnd",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "claimEnd"
diff --git a/modules/claim/back/models/claim-observation.json b/modules/claim/back/models/claim-observation.json
index 2d418b76e9..1e4cb6a0f6 100644
--- a/modules/claim/back/models/claim-observation.json
+++ b/modules/claim/back/models/claim-observation.json
@@ -1,6 +1,9 @@
{
"name": "ClaimObservation",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "claimObservation"
diff --git a/modules/claim/back/models/claim-state.json b/modules/claim/back/models/claim-state.json
index f5bde4168c..c50fdebdfc 100644
--- a/modules/claim/back/models/claim-state.json
+++ b/modules/claim/back/models/claim-state.json
@@ -1,6 +1,9 @@
{
"name": "ClaimState",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "claimState"
@@ -32,7 +35,7 @@
"relations": {
"writeRole": {
"type": "belongsTo",
- "model": "Role",
+ "model": "VnRole",
"foreignKey": "roleFk"
}
},
diff --git a/modules/claim/back/models/claim.json b/modules/claim/back/models/claim.json
index a7db1f3e1f..b85b9e073c 100644
--- a/modules/claim/back/models/claim.json
+++ b/modules/claim/back/models/claim.json
@@ -1,6 +1,9 @@
{
"name": "Claim",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "claim"
diff --git a/modules/claim/front/note/create/index.html b/modules/claim/front/note/create/index.html
index 304a8c0046..8a882a4f5b 100644
--- a/modules/claim/front/note/create/index.html
+++ b/modules/claim/front/note/create/index.html
@@ -27,4 +27,4 @@
label="Cancel">
-
\ No newline at end of file
+
diff --git a/modules/client/back/methods/client-dms/removeFile.js b/modules/client/back/methods/client-dms/removeFile.js
index 786a329289..bc9a0f7194 100644
--- a/modules/client/back/methods/client-dms/removeFile.js
+++ b/modules/client/back/methods/client-dms/removeFile.js
@@ -19,9 +19,8 @@ module.exports = Self => {
});
Self.removeFile = async(ctx, id, options) => {
- const models = Self.app.models;
- let tx;
const myOptions = {};
+ let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
@@ -34,13 +33,16 @@ module.exports = Self => {
try {
const clientDms = await Self.findById(id, null, myOptions);
- await models.Dms.removeFile(ctx, clientDms.dmsFk, myOptions);
+ const targetDms = await Self.app.models.Dms.removeFile(ctx, clientDms.dmsFk, myOptions);
- const destroyedClient = await clientDms.destroy(myOptions);
+ if (!targetDms || !clientDms)
+ throw new UserError('Try again');
+
+ const clientDmsDestroyed = await clientDms.destroy(myOptions);
if (tx) await tx.commit();
- return destroyedClient;
+ return clientDmsDestroyed;
} catch (e) {
if (tx) await tx.rollback();
throw e;
diff --git a/modules/client/back/methods/client/canBeInvoiced.js b/modules/client/back/methods/client/canBeInvoiced.js
index 843e9549fc..cdb8655005 100644
--- a/modules/client/back/methods/client/canBeInvoiced.js
+++ b/modules/client/back/methods/client/canBeInvoiced.js
@@ -1,7 +1,7 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = function(Self) {
- Self.remoteMethodCtx('canBeInvoiced', {
+ Self.remoteMethod('canBeInvoiced', {
description: 'Change property isEqualizated in all client addresses',
accessType: 'READ',
accepts: [
diff --git a/modules/client/back/methods/client/filter.js b/modules/client/back/methods/client/filter.js
index 47d5f6d2f5..f805c4be95 100644
--- a/modules/client/back/methods/client/filter.js
+++ b/modules/client/back/methods/client/filter.js
@@ -107,17 +107,29 @@ module.exports = Self => {
return {or: [
{'c.phone': {like: `%${value}%`}},
{'c.mobile': {like: `%${value}%`}},
+ {'a.phone': {like: `%${value}%`}},
]};
case 'zoneFk':
- param = 'a.postalCode';
- return {[param]: {inq: postalCode}};
+ return {'a.postalCode': {inq: postalCode}};
+ case 'city':
+ return {or: [
+ {'c.city': {like: `%${value}%`}},
+ {'a.city': {like: `%${value}%`}}
+ ]};
+ case 'postcode':
+ return {or: [
+ {'c.postcode': value},
+ {'a.postalCode': value}
+ ]};
+ case 'provinceFk':
+ return {or: [
+ {'p.id': value},
+ {'a.provinceFk': value}
+ ]};
case 'name':
case 'salesPersonFk':
case 'fi':
case 'socialName':
- case 'city':
- case 'postcode':
- case 'provinceFk':
case 'email':
param = `c.${param}`;
return {[param]: {like: `%${value}%`}};
@@ -134,24 +146,29 @@ module.exports = Self => {
c.fi,
c.socialName,
c.phone,
+ a.phone,
c.mobile,
c.city,
+ a.city,
c.postcode,
+ a.postalCode,
c.email,
c.isActive,
c.isFreezed,
- p.id AS provinceFk,
+ p.id AS provinceClientFk,
+ a.provinceFk AS provinceAddressFk,
p.name AS province,
u.id AS salesPersonFk,
u.name AS salesPerson
FROM client c
LEFT JOIN account.user u ON u.id = c.salesPersonFk
LEFT JOIN province p ON p.id = c.provinceFk
- JOIN vn.address a ON a.clientFk = c.id
+ JOIN address a ON a.clientFk = c.id
`
);
stmt.merge(conn.makeWhere(filter.where));
+ stmt.merge('GROUP BY c.id');
stmt.merge(conn.makePagination(filter));
const clientsIndex = stmts.push(stmt) - 1;
diff --git a/modules/client/back/methods/client/specs/consumption.spec.js b/modules/client/back/methods/client/specs/consumption.spec.js
index 47a495d799..85dbb74229 100644
--- a/modules/client/back/methods/client/specs/consumption.spec.js
+++ b/modules/client/back/methods/client/specs/consumption.spec.js
@@ -16,7 +16,7 @@ describe('client consumption() filter', () => {
};
const result = await models.Client.consumption(ctx, filter, options);
- expect(result.length).toEqual(10);
+ expect(result.length).toEqual(11);
await tx.rollback();
} catch (e) {
@@ -49,7 +49,7 @@ describe('client consumption() filter', () => {
const thirdRow = result[2];
expect(result.length).toEqual(3);
- expect(firstRow.quantity).toEqual(10);
+ expect(firstRow.quantity).toEqual(11);
expect(secondRow.quantity).toEqual(15);
expect(thirdRow.quantity).toEqual(20);
diff --git a/modules/client/back/methods/client/uploadFile.js b/modules/client/back/methods/client/uploadFile.js
index 99ede27c67..1a59659557 100644
--- a/modules/client/back/methods/client/uploadFile.js
+++ b/modules/client/back/methods/client/uploadFile.js
@@ -55,9 +55,9 @@ module.exports = Self => {
});
Self.uploadFile = async(ctx, id, options) => {
- const models = Self.app.models;
- let tx;
+ const {Dms, ClientDms} = Self.app.models;
const myOptions = {};
+ let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
@@ -67,23 +67,20 @@ module.exports = Self => {
myOptions.transaction = tx;
}
- const promises = [];
-
try {
- const uploadedFiles = await models.Dms.uploadFile(ctx, myOptions);
- uploadedFiles.forEach(dms => {
- const newClientDms = models.ClientDms.create({
+ const uploadedFiles = await Dms.uploadFile(ctx, myOptions);
+ const promises = uploadedFiles.map(dms =>
+ ClientDms.create({
clientFk: id,
dmsFk: dms.id
- }, myOptions);
+ }, myOptions)
- promises.push(newClientDms);
- });
- const resolvedPromises = await Promise.all(promises);
+ );
+ await Promise.all(promises);
if (tx) await tx.commit();
- return resolvedPromises;
+ return uploadedFiles;
} catch (e) {
if (tx) await tx.rollback();
throw e;
diff --git a/modules/client/back/model-config.json b/modules/client/back/model-config.json
index 0cc5df9a2f..fc1254dd80 100644
--- a/modules/client/back/model-config.json
+++ b/modules/client/back/model-config.json
@@ -29,7 +29,7 @@
"ClientCredit": {
"dataSource": "vn"
},
- "ClientCreditLimit": {
+ "RoleCreditLimit": {
"dataSource": "vn"
},
"ClientConsumptionQueue": {
@@ -113,9 +113,6 @@
"SageTransactionType": {
"dataSource": "vn"
},
- "TicketSms": {
- "dataSource": "vn"
- },
"TpvError": {
"dataSource": "vn"
},
diff --git a/modules/client/back/models/address.json b/modules/client/back/models/address.json
index 5f962677d8..e8bf8d8a06 100644
--- a/modules/client/back/models/address.json
+++ b/modules/client/back/models/address.json
@@ -1,7 +1,10 @@
{
"name": "Address",
"description": "Client addresses",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "address"
diff --git a/modules/client/back/models/client-contact.json b/modules/client/back/models/client-contact.json
index 3f71ab79e6..55cc9d4369 100644
--- a/modules/client/back/models/client-contact.json
+++ b/modules/client/back/models/client-contact.json
@@ -1,7 +1,10 @@
{
"name": "ClientContact",
"description": "Client phone contacts",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "clientContact"
diff --git a/modules/client/back/models/client-dms.json b/modules/client/back/models/client-dms.json
index 14b19498e3..6dbcd0140c 100644
--- a/modules/client/back/models/client-dms.json
+++ b/modules/client/back/models/client-dms.json
@@ -1,6 +1,9 @@
{
"name": "ClientDms",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "clientDms"
diff --git a/modules/client/back/models/client-informa.json b/modules/client/back/models/client-informa.json
index 0c652484e9..5e536faffb 100644
--- a/modules/client/back/models/client-informa.json
+++ b/modules/client/back/models/client-informa.json
@@ -1,6 +1,9 @@
{
"name": "ClientInforma",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"log": {
"model":"ClientLog",
"relation": "client",
diff --git a/modules/client/back/models/client-observation.json b/modules/client/back/models/client-observation.json
index 95d00d374f..b204ebeb4a 100644
--- a/modules/client/back/models/client-observation.json
+++ b/modules/client/back/models/client-observation.json
@@ -1,7 +1,10 @@
{
"name": "ClientObservation",
"description": "Client notes",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "clientObservation"
diff --git a/modules/client/back/models/client-sample.json b/modules/client/back/models/client-sample.json
index a32f308ab1..4cd55d9dfd 100644
--- a/modules/client/back/models/client-sample.json
+++ b/modules/client/back/models/client-sample.json
@@ -1,6 +1,9 @@
{
"name": "ClientSample",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "clientSample"
diff --git a/modules/client/back/models/client-sms.json b/modules/client/back/models/client-sms.json
index b2244ebbb0..df1b587378 100644
--- a/modules/client/back/models/client-sms.json
+++ b/modules/client/back/models/client-sms.json
@@ -21,6 +21,11 @@
"type": "belongsTo",
"model": "Sms",
"foreignKey": "smsFk"
+ },
+ "ticket": {
+ "type": "belongsTo",
+ "model": "Ticket",
+ "foreignKey": "ticketFk"
}
}
}
diff --git a/modules/client/back/models/client.js b/modules/client/back/models/client.js
index 72b7027798..a9e14effa6 100644
--- a/modules/client/back/models/client.js
+++ b/modules/client/back/models/client.js
@@ -463,7 +463,7 @@ module.exports = Self => {
throw new UserError(`You can't change the credit set to zero from a financialBoss`);
}
- const creditLimits = await models.ClientCreditLimit.find({
+ const creditLimits = await models.RoleCreditLimit.find({
fields: ['roleFk'],
where: {
maxAmount: {gte: changes.credit}
diff --git a/modules/client/back/models/client.json b/modules/client/back/models/client.json
index f32915bb5c..bfde05162a 100644
--- a/modules/client/back/models/client.json
+++ b/modules/client/back/models/client.json
@@ -1,6 +1,9 @@
{
"name": "Client",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "client"
diff --git a/modules/client/back/models/greuge.json b/modules/client/back/models/greuge.json
index 884cbd34ed..f57744f8a0 100644
--- a/modules/client/back/models/greuge.json
+++ b/modules/client/back/models/greuge.json
@@ -1,6 +1,9 @@
{
"name": "Greuge",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "greuge"
diff --git a/modules/client/back/models/recovery.json b/modules/client/back/models/recovery.json
index 5ea89197da..89ec544945 100644
--- a/modules/client/back/models/recovery.json
+++ b/modules/client/back/models/recovery.json
@@ -1,6 +1,9 @@
{
"name": "Recovery",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "recovery"
diff --git a/modules/client/back/models/client-credit-limit.json b/modules/client/back/models/role-credit-limit.json
similarity index 79%
rename from modules/client/back/models/client-credit-limit.json
rename to modules/client/back/models/role-credit-limit.json
index 740f0cf534..e36180dfc5 100644
--- a/modules/client/back/models/client-credit-limit.json
+++ b/modules/client/back/models/role-credit-limit.json
@@ -1,9 +1,9 @@
{
- "name": "ClientCreditLimit",
+ "name": "RoleCreditLimit",
"base": "VnModel",
"options": {
"mysql": {
- "table": "clientCreditLimit"
+ "table": "roleCreditLimit"
}
},
"properties": {
@@ -19,8 +19,8 @@
"relations": {
"role": {
"type": "belongsTo",
- "model": "Role",
+ "model": "VnRole",
"foreignKey": "roleFk"
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/client/back/models/ticket-sms.json b/modules/client/back/models/ticket-sms.json
deleted file mode 100644
index 03f592f51e..0000000000
--- a/modules/client/back/models/ticket-sms.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "name": "TicketSms",
- "base": "VnModel",
- "options": {
- "mysql": {
- "table": "ticketSms"
- }
- },
- "properties": {
- "smsFk": {
- "type": "number",
- "id": true,
- "description": "Identifier"
- }
- },
- "relations": {
- "ticket": {
- "type": "belongsTo",
- "model": "Ticket",
- "foreignKey": "ticketFk"
- },
- "sms": {
- "type": "belongsTo",
- "model": "Sms",
- "foreignKey": "smsFk"
- }
- }
-}
diff --git a/modules/client/front/basic-data/index.html b/modules/client/front/basic-data/index.html
index e48b39fdc0..acab99d915 100644
--- a/modules/client/front/basic-data/index.html
+++ b/modules/client/front/basic-data/index.html
@@ -62,7 +62,7 @@
diff --git a/modules/client/front/sms/index.html b/modules/client/front/sms/index.html
index e2bc0785e3..7fb3b870e0 100644
--- a/modules/client/front/sms/index.html
+++ b/modules/client/front/sms/index.html
@@ -1,40 +1,2 @@
-
-
-
-
-
-
-
- Sender
- Destination
- Message
- Status
- Created
-
-
-
-
-
-
- {{::clientSms.sms.sender.name}}
-
-
- {{::clientSms.sms.destination}}
- {{::clientSms.sms.message}}
- {{::clientSms.sms.status}}
- {{::clientSms.sms.created | date:'dd/MM/yyyy HH:mm'}}
-
-
-
-
-
-
-
+
+
diff --git a/modules/client/front/sms/index.js b/modules/client/front/sms/index.js
index 6ad64282e7..8fa130248c 100644
--- a/modules/client/front/sms/index.js
+++ b/modules/client/front/sms/index.js
@@ -1,32 +1,14 @@
import ngModule from '../module';
import Section from 'salix/components/section';
-export default class Controller extends Section {
+class Controller extends Section {
constructor($element, $) {
super($element, $);
+ }
- this.filter = {
- fields: ['id', 'smsFk'],
- include: {
- relation: 'sms',
- scope: {
- fields: [
- 'senderFk',
- 'sender',
- 'destination',
- 'message',
- 'statusCode',
- 'status',
- 'created'],
- include: {
- relation: 'sender',
- scope: {
- fields: ['name']
- }
- }
- }
- }
- };
+ async $onInit() {
+ this.$state.go('client.card.summary', {id: this.$params.id});
+ window.location.href = await this.vnApp.getUrl(`Customer/${this.$params.id}/sms`);
}
}
diff --git a/modules/entry/back/models/buy.json b/modules/entry/back/models/buy.json
index 30379eaf68..fa804f4d80 100644
--- a/modules/entry/back/models/buy.json
+++ b/modules/entry/back/models/buy.json
@@ -1,6 +1,9 @@
{
"name": "Buy",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "buy"
diff --git a/modules/entry/back/models/entry-observation.json b/modules/entry/back/models/entry-observation.json
index cdf0c5e6ed..6a1592037a 100644
--- a/modules/entry/back/models/entry-observation.json
+++ b/modules/entry/back/models/entry-observation.json
@@ -1,6 +1,9 @@
{
"name": "EntryObservation",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "entryObservation"
diff --git a/modules/entry/back/models/entry.json b/modules/entry/back/models/entry.json
index a7508b4e8a..0f3e389b6f 100644
--- a/modules/entry/back/models/entry.json
+++ b/modules/entry/back/models/entry.json
@@ -1,6 +1,9 @@
{
"name": "Entry",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "entry"
diff --git a/modules/invoiceIn/back/methods/invoice-in/summary.js b/modules/invoiceIn/back/methods/invoice-in/summary.js
index 0e55eeaace..fe198b2b40 100644
--- a/modules/invoiceIn/back/methods/invoice-in/summary.js
+++ b/modules/invoiceIn/back/methods/invoice-in/summary.js
@@ -112,7 +112,7 @@ module.exports = Self => {
{
relation: 'taxTypeSage',
scope: {
- fields: ['vat']
+ fields: ['vat', 'rate']
}
}]
}
diff --git a/modules/invoiceIn/back/models/invoice-in-tax.json b/modules/invoiceIn/back/models/invoice-in-tax.json
index 5bfbbe2a8d..53b5548b63 100644
--- a/modules/invoiceIn/back/models/invoice-in-tax.json
+++ b/modules/invoiceIn/back/models/invoice-in-tax.json
@@ -1,6 +1,9 @@
{
"name": "InvoiceInTax",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "invoiceInTax"
diff --git a/modules/invoiceIn/back/models/invoice-in.json b/modules/invoiceIn/back/models/invoice-in.json
index 5be55c8510..59c179e768 100644
--- a/modules/invoiceIn/back/models/invoice-in.json
+++ b/modules/invoiceIn/back/models/invoice-in.json
@@ -1,6 +1,9 @@
{
"name": "InvoiceIn",
- "base": "Loggable",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
"options": {
"mysql": {
"table": "invoiceIn"
diff --git a/modules/invoiceOut/back/methods/invoiceOut/createManualInvoice.js b/modules/invoiceOut/back/methods/invoiceOut/createManualInvoice.js
index 18e6903d68..043dfbead2 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/createManualInvoice.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/createManualInvoice.js
@@ -85,7 +85,7 @@ module.exports = Self => {
throw new UserError(`A ticket with an amount of zero can't be invoiced`);
// Validates ticket nagative base
- const hasNegativeBase = await getNegativeBase(ticketId, myOptions);
+ const hasNegativeBase = await getNegativeBase(maxShipped, clientId, companyId, myOptions);
if (hasNegativeBase && company.code == 'VNL')
throw new UserError(`A ticket with a negative base can't be invoiced`);
} else {
@@ -162,10 +162,13 @@ module.exports = Self => {
return result.invoiceable;
}
- async function getNegativeBase(ticketId, options) {
+ async function getNegativeBase(maxShipped, clientId, companyId, options) {
const models = Self.app.models;
- const query = 'SELECT vn.hasSomeNegativeBase(?) AS base';
- const [result] = await models.InvoiceOut.rawSql(query, [ticketId], options);
+ await models.InvoiceOut.rawSql('CALL invoiceOut_exportationFromClient(?,?,?)',
+ [maxShipped, clientId, companyId], options
+ );
+ const query = 'SELECT vn.hasAnyNegativeBase() AS base';
+ const [result] = await models.InvoiceOut.rawSql(query, [], options);
return result.base;
}
diff --git a/modules/invoiceOut/back/methods/invoiceOut/invoiceClient.js b/modules/invoiceOut/back/methods/invoiceOut/invoiceClient.js
index fa22dab1e0..530b02353c 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/invoiceClient.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/invoiceClient.js
@@ -80,6 +80,7 @@ module.exports = Self => {
invoiceType,
args.companyFk,
args.invoiceDate,
+ null,
options
);
diff --git a/modules/invoiceOut/back/methods/invoiceOut/negativeBases.js b/modules/invoiceOut/back/methods/invoiceOut/negativeBases.js
index ae9c404afd..96c7893160 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/negativeBases.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/negativeBases.js
@@ -90,7 +90,7 @@ module.exports = Self => {
AND t.refFk IS NULL
AND c.typeFk IN ('normal','trust')
GROUP BY t.clientFk, negativeBase.taxableBase
- HAVING amount <> 0`, [args.from, args.to]));
+ HAVING amount < 0`, [args.from, args.to]));
stmt = new ParameterizedSQL(`
SELECT f.*
diff --git a/modules/invoiceOut/back/methods/invoiceOut/negativeBasesCsv.js b/modules/invoiceOut/back/methods/invoiceOut/negativeBasesCsv.js
index d70a8fce52..87e9a67eae 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/negativeBasesCsv.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/negativeBasesCsv.js
@@ -10,13 +10,17 @@ module.exports = Self => {
type: 'date',
description: 'From date',
required: true
- },
- {
+ }, {
arg: 'to',
type: 'date',
description: 'To date',
required: true
- }],
+ }, {
+ arg: 'filter',
+ type: 'object',
+ description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string'
+ },
+ ],
returns: [
{
arg: 'body',
diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js
index 800a4ea835..11575999ac 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js
@@ -2,7 +2,7 @@
const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
-describe('InvoiceOut tranferInvoice()', () => {
+describe('InvoiceOut transferInvoice()', () => {
const activeCtx = {
accessToken: {userId: 5},
http: {
@@ -23,20 +23,29 @@ describe('InvoiceOut tranferInvoice()', () => {
const tx = await models.InvoiceOut.beginTransaction({});
const options = {transaction: tx};
const args = {
- id: '1',
- ref: 'T4444444',
+ id: '4',
+ refFk: 'T4444444',
newClientFk: 1,
- cplusRectificationId: 1,
- siiTypeInvoiceOutId: 1,
- invoiceCorrectionTypeId: 1
+ cplusRectificationTypeFk: 1,
+ siiTypeInvoiceOutFk: 1,
+ invoiceCorrectionTypeFk: 1
};
ctx.args = args;
try {
+ const {clientFk: oldClient} = await models.InvoiceOut.findById(args.id, {fields: ['clientFk']});
+ const invoicesBefore = await models.InvoiceOut.find({}, options);
const result = await models.InvoiceOut.transferInvoice(
ctx,
options);
+ const invoicesAfter = await models.InvoiceOut.find({}, options);
+ const rectificativeInvoice = invoicesAfter[invoicesAfter.length - 2];
+ const newInvoice = invoicesAfter[invoicesAfter.length - 1];
expect(result).toBeDefined();
+ expect(invoicesAfter.length - invoicesBefore.length).toEqual(2);
+ expect(rectificativeInvoice.clientFk).toEqual(oldClient);
+ expect(newInvoice.clientFk).toEqual(args.newClientFk);
+
await tx.rollback();
} catch (e) {
await tx.rollback();
@@ -49,20 +58,44 @@ describe('InvoiceOut tranferInvoice()', () => {
const options = {transaction: tx};
const args = {
id: '1',
- ref: 'T1111111',
+ refFk: 'T1111111',
newClientFk: 1101,
- cplusRectificationId: 1,
- siiTypeInvoiceOutId: 1,
- invoiceCorrectionTypeId: 1
+ cplusRectificationTypeFk: 1,
+ siiTypeInvoiceOutFk: 1,
+ invoiceCorrectionTypeFk: 1
};
ctx.args = args;
try {
await models.InvoiceOut.transferInvoice(
ctx,
options);
+ await tx.rollback();
} catch (e) {
expect(e.message).toBe(`Select a different client`);
await tx.rollback();
}
});
+
+ it('should throw an UserError when it is refund', async() => {
+ const tx = await models.InvoiceOut.beginTransaction({});
+ const options = {transaction: tx};
+ const args = {
+ id: '1',
+ refFk: 'T1111111',
+ newClientFk: 1102,
+ cplusRectificationTypeFk: 1,
+ siiTypeInvoiceOutFk: 1,
+ invoiceCorrectionTypeFk: 1
+ };
+ ctx.args = args;
+ try {
+ await models.InvoiceOut.transferInvoice(
+ ctx,
+ options);
+ await tx.rollback();
+ } catch (e) {
+ expect(e.message).toContain(`This ticket is already a refund`);
+ await tx.rollback();
+ }
+ });
});
diff --git a/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js b/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js
index dde535c998..782eaf6d81 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js
@@ -12,7 +12,7 @@ module.exports = Self => {
description: 'Issued invoice id'
},
{
- arg: 'ref',
+ arg: 'refFk',
type: 'string',
required: true
},
@@ -22,17 +22,17 @@ module.exports = Self => {
required: true
},
{
- arg: 'cplusRectificationId',
+ arg: 'cplusRectificationTypeFk',
type: 'number',
required: true
},
{
- arg: 'siiTypeInvoiceOutId',
+ arg: 'siiTypeInvoiceOutFk',
type: 'number',
required: true
},
{
- arg: 'invoiceCorrectionTypeId',
+ arg: 'invoiceCorrectionTypeFk',
type: 'number',
required: true
},
@@ -50,14 +50,14 @@ module.exports = Self => {
Self.transferInvoice = async(ctx, options) => {
const models = Self.app.models;
const myOptions = {userId: ctx.req.accessToken.userId};
- const args = ctx.args;
+ const {id, refFk, newClientFk, cplusRectificationTypeFk, siiTypeInvoiceOutFk, invoiceCorrectionTypeFk} = ctx.args;
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
- const {clientFk} = await models.InvoiceOut.findById(args.id);
+ const {clientFk} = await models.InvoiceOut.findById(id);
- if (clientFk == args.newClientFk)
+ if (clientFk == newClientFk)
throw new UserError(`Select a different client`);
if (!myOptions.transaction) {
@@ -65,10 +65,10 @@ module.exports = Self => {
myOptions.transaction = tx;
}
try {
- const filterRef = {where: {refFk: args.ref}};
+ const filterRef = {where: {refFk: refFk}};
const tickets = await models.Ticket.find(filterRef, myOptions);
const ticketsIds = tickets.map(ticket => ticket.id);
- await models.Ticket.refund(ctx, ticketsIds, null, myOptions);
+ const refundTickets = await models.Ticket.refund(ctx, ticketsIds, null, myOptions);
const filterTicket = {where: {ticketFk: {inq: ticketsIds}}};
@@ -78,24 +78,20 @@ module.exports = Self => {
const sales = await models.Sale.find(filterTicket, myOptions);
const salesIds = sales.map(sale => sale.id);
- const clonedTickets = await models.Sale.clone(ctx, salesIds, servicesIds, null, false, false, myOptions);
+ const clonedTickets = await models.Sale.clone(ctx, salesIds, servicesIds, null, false, myOptions);
const clonedTicketIds = [];
for (const clonedTicket of clonedTickets) {
- await clonedTicket.updateAttribute('clientFk', args.newClientFk, myOptions);
+ await clonedTicket.updateAttribute('clientFk', newClientFk, myOptions);
clonedTicketIds.push(clonedTicket.id);
}
- const invoiceIds = await models.Ticket.invoiceTickets(ctx, clonedTicketIds, myOptions);
- const [invoiceId] = invoiceIds;
+ const invoiceCorrection =
+ {correctedFk: id, cplusRectificationTypeFk, siiTypeInvoiceOutFk, invoiceCorrectionTypeFk};
+ const refundTicketIds = refundTickets.map(ticket => ticket.id);
- await models.InvoiceCorrection.create({
- correctingFk: invoiceId,
- correctedFk: args.id,
- cplusRectificationTypeFk: args.cplusRectificationId,
- siiTypeInvoiceOutFk: args.siiTypeInvoiceOutId,
- invoiceCorrectionTypeFk: args.invoiceCorrectionTypeId
- }, myOptions);
+ await models.Ticket.invoiceTickets(ctx, refundTicketIds, invoiceCorrection, myOptions);
+ const [invoiceId] = await models.Ticket.invoiceTickets(ctx, clonedTicketIds, null, myOptions);
if (tx) {
await tx.commit();
diff --git a/modules/invoiceOut/back/models/invoice-correction.json b/modules/invoiceOut/back/models/invoice-correction.json
index 43e4f07ef5..58f6f63b72 100644
--- a/modules/invoiceOut/back/models/invoice-correction.json
+++ b/modules/invoiceOut/back/models/invoice-correction.json
@@ -16,13 +16,43 @@
"type": "number"
},
"cplusRectificationTypeFk": {
- "type": "number"
+ "type": "number",
+ "required": true
},
"siiTypeInvoiceOutFk": {
- "type": "number"
+ "type": "number",
+ "required": true
},
"invoiceCorrectionTypeFk": {
- "type": "number"
+ "type": "number",
+ "required": true
+ },
+ "relations": {
+ "correcting": {
+ "type": "belongsTo",
+ "model": "InvoiceOut",
+ "foreignKey": "correctingFk"
+ },
+ "corrected": {
+ "type": "belongsTo",
+ "model": "InvoiceOut",
+ "foreignKey": "correctedFk"
+ },
+ "cplusRectificationType": {
+ "type": "belongsTo",
+ "model": "cplusRectificationType",
+ "foreignKey": "cplusRectificationTypeFk"
+ },
+ "siiTypeInvoiceOut": {
+ "type": "belongsTo",
+ "model": "siiTypeInvoiceOut",
+ "foreignKey": "siiTypeInvoiceOutFk"
+ },
+ "invoiceCorrectionType": {
+ "type": "belongsTo",
+ "model": "invoiceCorrectionType",
+ "foreignKey": "invoiceCorrectionTypeFk"
+ }
}
}
}
diff --git a/modules/invoiceOut/back/models/sii-type-invoice-out.json b/modules/invoiceOut/back/models/sii-type-invoice-out.json
index 17b3126178..58d50a12c5 100644
--- a/modules/invoiceOut/back/models/sii-type-invoice-out.json
+++ b/modules/invoiceOut/back/models/sii-type-invoice-out.json
@@ -12,6 +12,9 @@
"type": "number",
"description": "Identifier"
},
+ "code": {
+ "type": "string"
+ },
"description": {
"type": "string"
}
diff --git a/modules/invoiceOut/front/descriptor-menu/index.html b/modules/invoiceOut/front/descriptor-menu/index.html
index 0052f0c037..435db36124 100644
--- a/modules/invoiceOut/front/descriptor-menu/index.html
+++ b/modules/invoiceOut/front/descriptor-menu/index.html
@@ -7,7 +7,8 @@
+ data="siiTypeInvoiceOuts"
+ where="{code: {like: 'R%'}}">
+
+ transferInvoice
+
-
-
-
- #{{id}} - {{::name}}
-
-
-
-
- {{::description}}
-
-
-
-
-
-
-
-
-
-
+
+
+
+ #{{id}} - {{::name}}
+
+
+
+
+ {{::description}}
+
+
+
+
+
+
+ {{::code}} - {{::description}}
+
+
+
+
+
+
diff --git a/modules/invoiceOut/front/descriptor-menu/index.js b/modules/invoiceOut/front/descriptor-menu/index.js
index d3862a753c..2c28599e7c 100644
--- a/modules/invoiceOut/front/descriptor-menu/index.js
+++ b/modules/invoiceOut/front/descriptor-menu/index.js
@@ -129,15 +129,15 @@ class Controller extends Section {
transferInvoice() {
const params = {
id: this.invoiceOut.id,
- ref: this.invoiceOut.ref,
- newClientFk: this.invoiceOut.client.id,
- cplusRectificationId: this.cplusRectificationType,
- siiTypeInvoiceOutId: this.siiTypeInvoiceOut,
- invoiceCorrectionTypeId: this.invoiceCorrectionType
+ refFk: this.invoiceOut.ref,
+ newClientFk: this.clientId,
+ cplusRectificationTypeFk: this.cplusRectificationType,
+ siiTypeInvoiceOutFk: this.siiTypeInvoiceOut,
+ invoiceCorrectionTypeFk: this.invoiceCorrectionType
};
this.$http.post(`InvoiceOuts/transferInvoice`, params).then(res => {
const invoiceId = res.data;
- this.vnApp.showSuccess(this.$t('Invoice trasfered!'));
+ this.vnApp.showSuccess(this.$t('Transferred invoice'));
this.$state.go('invoiceOut.card.summary', {id: invoiceId});
});
}
diff --git a/modules/invoiceOut/front/descriptor-menu/locale/es.yml b/modules/invoiceOut/front/descriptor-menu/locale/es.yml
index 0f74b5fece..aaeefd9cc4 100644
--- a/modules/invoiceOut/front/descriptor-menu/locale/es.yml
+++ b/modules/invoiceOut/front/descriptor-menu/locale/es.yml
@@ -22,4 +22,5 @@ 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
+Rectificative type: Tipo rectificativa
+Transferred invoice: Factura transferida
diff --git a/modules/invoiceOut/front/index/manual/index.html b/modules/invoiceOut/front/index/manual/index.html
index c3362a319d..5872911e46 100644
--- a/modules/invoiceOut/front/index/manual/index.html
+++ b/modules/invoiceOut/front/index/manual/index.html
@@ -6,6 +6,7 @@
auto-load="true"
url="InvoiceOutSerials"
data="invoiceOutSerials"
+ where="{code: {neq: 'R'}}"
order="code">
{
const result = await models.SalesMonitor.salesFilter(ctx, filter, options);
const firstRow = result[0];
- expect(result.length).toEqual(15);
+ expect(result.length).toEqual(12);
expect(firstRow.alertLevel).not.toEqual(0);
await tx.rollback();
diff --git a/modules/order/back/methods/order/catalogFilter.js b/modules/order/back/methods/order/catalogFilter.js
index 722f3e0966..dc0e2f60b3 100644
--- a/modules/order/back/methods/order/catalogFilter.js
+++ b/modules/order/back/methods/order/catalogFilter.js
@@ -50,7 +50,7 @@ module.exports = Self => {
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.item');
stmt = new ParameterizedSQL(
- `CREATE TEMPORARY TABLE tmp.item
+ `CREATE TEMPORARY TABLE tmp.item
(PRIMARY KEY (itemFk)) ENGINE = MEMORY
SELECT DISTINCT
i.id AS itemFk,
@@ -112,7 +112,6 @@ module.exports = Self => {
i.value7,
i.tag8,
i.value8,
- i.stars,
tci.price,
tci.available,
w.lastName,
@@ -131,7 +130,7 @@ module.exports = Self => {
if (orderBy.isTag) {
stmt.merge({
sql: `
- LEFT JOIN vn.itemTag itg
+ LEFT JOIN vn.itemTag itg
LEFT JOIN vn.tag t ON t.id = itg.tagFk
ON itg.itemFk = tci.itemFk AND itg.tagFk = ?`,
params: [orderBy.field],
@@ -140,7 +139,7 @@ module.exports = Self => {
const way = orderBy.way == 'DESC' ? 'DESC' : 'ASC';
const tag = await Self.app.models.Tag.findById(orderBy.field, null, myOptions);
const orderSql = `
- ORDER BY
+ ORDER BY
itg.value IS NULL,
${tag.isQuantitative ? 'CAST(itg.value AS SIGNED)' : 'itg.value'}
${way}`;
@@ -158,7 +157,7 @@ module.exports = Self => {
// Apply item prices
const pricesIndex = stmts.push(
- `SELECT
+ `SELECT
tcp.itemFk,
tcp.grouping,
tcp.price,
diff --git a/modules/order/front/catalog-view/index.html b/modules/order/front/catalog-view/index.html
index 5d60211ed7..081ce05a02 100644
--- a/modules/order/front/catalog-view/index.html
+++ b/modules/order/front/catalog-view/index.html
@@ -37,11 +37,6 @@
value="{{::item.value7}}">
-
-
-
-
@@ -54,7 +49,6 @@
{{::item.minQuantity}}
-