Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 6451_insomnia
gitea/salix/pipeline/head There was a failure building this commit
Details
gitea/salix/pipeline/head There was a failure building this commit
Details
This commit is contained in:
commit
ba2fde3a00
|
@ -5,6 +5,13 @@ 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).
|
||||
|
||||
## [2406.01] - 2024-02-08
|
||||
|
||||
### Added
|
||||
### Changed
|
||||
### Fixed
|
||||
|
||||
|
||||
## [2404.01] - 2024-01-25
|
||||
|
||||
### Added
|
||||
|
|
|
@ -35,10 +35,17 @@ module.exports = Self => {
|
|||
let html = `<strong>Motivo</strong>:<br/>${reason}<br/>`;
|
||||
html += `<strong>Usuario</strong>:<br/>${ctx.req.accessToken.userId} ${emailUser.email}<br/>`;
|
||||
|
||||
delete additionalData.backError.config.headers.Authorization;
|
||||
const httpRequest = JSON.parse(additionalData?.httpRequest);
|
||||
|
||||
if (httpRequest)
|
||||
delete httpRequest.config.headers.Authorization;
|
||||
additionalData.httpRequest = httpRequest;
|
||||
|
||||
for (const data in additionalData)
|
||||
html += `<strong>${data}</strong>:<br/>${tryParse(additionalData[data])}<br/>`;
|
||||
|
||||
const subjectReason = JSON.parse(additionalData?.httpRequest)?.data?.error;
|
||||
const subjectReason = httpRequest?.data?.error;
|
||||
smtp.send({
|
||||
to: `${config.app.reportEmail}, ${emailUser.email}`,
|
||||
subject:
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
const {ParameterizedSQL} = require('loopback-connector');
|
||||
const {buildFilter, mergeFilters} = require('vn-loopback/util/filter');
|
||||
// const {models} = require('vn-loopback/server/server');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('filter', {
|
||||
description:
|
||||
'Find all postcodes of the model matched by postcode, town, province or country.',
|
||||
accessType: 'READ',
|
||||
returns: {
|
||||
type: ['object'],
|
||||
root: true,
|
||||
},
|
||||
accepts: [
|
||||
{
|
||||
arg: 'filter',
|
||||
type: 'object',
|
||||
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||
http: {source: 'query'}
|
||||
},
|
||||
{
|
||||
arg: 'search',
|
||||
type: 'string',
|
||||
description: 'Value to filter',
|
||||
http: {source: 'query'}
|
||||
},
|
||||
],
|
||||
http: {
|
||||
path: `/filter`,
|
||||
verb: 'GET',
|
||||
},
|
||||
});
|
||||
Self.filter = async(ctx, filter, options) => {
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const conn = Self.dataSource.connector;
|
||||
const where = buildFilter(ctx.args, (param, value) => {
|
||||
switch (param) {
|
||||
case 'search':
|
||||
return {or: [
|
||||
{'pc.code': {like: `%${value}%`}},
|
||||
{'t.name': {like: `%${value}%`}},
|
||||
{'p.name': {like: `%${value}%`}},
|
||||
{'c.country': {like: `%${value}%`}}
|
||||
]
|
||||
};
|
||||
}
|
||||
}) ?? {};
|
||||
|
||||
filter = mergeFilters(ctx.args?.filter ?? {}, {where});
|
||||
|
||||
const stmts = [];
|
||||
let stmt;
|
||||
stmt = new ParameterizedSQL(`
|
||||
SELECT
|
||||
pc.code,
|
||||
t.name as town,
|
||||
p.name as province,
|
||||
c.country
|
||||
FROM
|
||||
postCode pc
|
||||
JOIN town t on t.id = pc.townFk
|
||||
JOIN province p on p.id = t.provinceFk
|
||||
JOIN country c on c.id = p.countryFk
|
||||
`);
|
||||
|
||||
stmt.merge(conn.makeSuffix(filter));
|
||||
const itemsIndex = stmts.push(stmt) - 1;
|
||||
|
||||
const sql = ParameterizedSQL.join(stmts, ';');
|
||||
const result = await conn.executeStmt(sql, myOptions);
|
||||
return itemsIndex === 0 ? result : result[itemsIndex];
|
||||
};
|
||||
};
|
|
@ -0,0 +1,103 @@
|
|||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('Postcode filter()', () => {
|
||||
it('should retrieve with no filter', async() => {
|
||||
const tx = await models.Postcode.beginTransaction({});
|
||||
const options = {transaction: tx};
|
||||
|
||||
try {
|
||||
const ctx = {
|
||||
args: {
|
||||
|
||||
},
|
||||
};
|
||||
const results = await models.Postcode.filter(ctx, options);
|
||||
|
||||
expect(results.length).toBeGreaterThan(0);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should retrieve with filter as postcode', async() => {
|
||||
const tx = await models.Postcode.beginTransaction({});
|
||||
const options = {transaction: tx};
|
||||
|
||||
try {
|
||||
const ctx = {
|
||||
args: {
|
||||
search: 46,
|
||||
},
|
||||
};
|
||||
const results = await models.Postcode.filter(ctx, options);
|
||||
|
||||
expect(results.length).toEqual(4);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should retrieve with filter as city', async() => {
|
||||
const tx = await models.Postcode.beginTransaction({});
|
||||
const options = {transaction: tx};
|
||||
|
||||
try {
|
||||
const ctx = {
|
||||
args: {
|
||||
search: 'Alz',
|
||||
},
|
||||
};
|
||||
const results = await models.Postcode.filter(ctx, options);
|
||||
|
||||
expect(results.length).toEqual(1);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should retrieve with filter as province', async() => {
|
||||
const tx = await models.Postcode.beginTransaction({});
|
||||
const options = {transaction: tx};
|
||||
|
||||
try {
|
||||
const ctx = {
|
||||
args: {
|
||||
search: 'one',
|
||||
},
|
||||
};
|
||||
const results = await models.Postcode.filter(ctx, options);
|
||||
|
||||
expect(results.length).toEqual(4);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should retrieve with filter as country', async() => {
|
||||
const tx = await models.Postcode.beginTransaction({});
|
||||
const options = {transaction: tx};
|
||||
|
||||
try {
|
||||
const ctx = {
|
||||
args: {
|
||||
search: 'Ec',
|
||||
},
|
||||
};
|
||||
const results = await models.Postcode.filter(ctx, options);
|
||||
|
||||
expect(results.length).toEqual(1);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
|
@ -20,7 +20,7 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.internationalExpedition = async expeditionFk => {
|
||||
Self.internationalExpedition = async (expeditionFk) => {
|
||||
const models = Self.app.models;
|
||||
|
||||
const viaexpressConfig = await models.ViaexpressConfig.findOne({
|
||||
|
|
|
@ -20,11 +20,11 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.renderer = async expeditionFk => {
|
||||
Self.renderer = async (expeditionFk) => {
|
||||
const models = Self.app.models;
|
||||
|
||||
const viaexpressConfig = await models.ViaexpressConfig.findOne({
|
||||
fields: ['client', 'user', 'password', 'defaultWeight', 'deliveryType']
|
||||
fields: ['client', 'user', 'password', 'defaultWeight', 'deliveryType', 'agencyModeFk']
|
||||
});
|
||||
|
||||
const expedition = await models.Expedition.findOne({
|
||||
|
@ -34,7 +34,7 @@ module.exports = Self => {
|
|||
{
|
||||
relation: 'ticket',
|
||||
scope: {
|
||||
fields: ['shipped', 'addressFk', 'clientFk', 'companyFk'],
|
||||
fields: ['shipped', 'addressFk', 'clientFk', 'companyFk', 'agencyModeFk'],
|
||||
include: [
|
||||
{
|
||||
relation: 'client',
|
||||
|
@ -102,7 +102,6 @@ module.exports = Self => {
|
|||
}
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
]
|
||||
});
|
||||
|
@ -110,13 +109,15 @@ module.exports = Self => {
|
|||
const ticket = expedition.ticket();
|
||||
const sender = ticket.company().client();
|
||||
const shipped = ticket.shipped.toISOString();
|
||||
const isInterdia = (ticket.agencyModeFk === viaexpressConfig.agencyModeFk)
|
||||
const data = {
|
||||
viaexpressConfig,
|
||||
sender,
|
||||
senderAddress: sender.defaultAddress(),
|
||||
client: ticket.client(),
|
||||
address: ticket.address(),
|
||||
shipped
|
||||
shipped,
|
||||
isInterdia
|
||||
};
|
||||
|
||||
const template = fs.readFileSync(__dirname + '/template.ejs', 'utf-8');
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<Asegurado>0</Asegurado>
|
||||
<Imprimir>0</Imprimir>
|
||||
<ConDevolucionAlbaran>0</ConDevolucionAlbaran>
|
||||
<Intradia>0</Intradia>
|
||||
<Intradia><%= isInterdia %></Intradia>
|
||||
<Observaciones></Observaciones>
|
||||
<AlbaranRemitente></AlbaranRemitente>
|
||||
<Modo>0</Modo>
|
||||
|
|
|
@ -17,10 +17,6 @@
|
|||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"code": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
let UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
module.exports = Self => {
|
||||
require('../methods/postcode/filter.js')(Self);
|
||||
Self.rewriteDbError(function(err) {
|
||||
if (err.code === 'ER_DUP_ENTRY')
|
||||
return new UserError(`This postcode already exists`);
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
},
|
||||
"deliveryType": {
|
||||
"type": "string"
|
||||
},
|
||||
"agencyModeFk": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -258,18 +258,20 @@ module.exports = function(Self) {
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,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 = [
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM mariadb:10.7.7
|
||||
FROM mariadb:10.11.6
|
||||
|
||||
ENV MYSQL_ROOT_PASSWORD root
|
||||
ENV TZ Europe/Madrid
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
DELETE FROM `account`.`signInLog` where owner <> FALSE
|
|
@ -0,0 +1,2 @@
|
|||
DELETE FROM `account`.`signInLog` where owner <> FALSE;
|
||||
|
|
@ -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 ;
|
File diff suppressed because it is too large
Load Diff
|
@ -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 ;
|
|
@ -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;
|
||||
|
|
@ -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');
|
|
@ -0,0 +1,589 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`sale_calculateComponent`(vSelf INT, vOption VARCHAR(25))
|
||||
proc: BEGIN
|
||||
/**
|
||||
* Crea tabla temporal para vn.sale_recalcComponent() para recalcular los componentes
|
||||
*
|
||||
* @param vSelf Id de la venta
|
||||
* @param vOption indica en que componente pone el descuadre, NULL en casos habituales
|
||||
*/
|
||||
CREATE OR REPLACE TEMPORARY TABLE tmp.recalculateSales
|
||||
SELECT s.id
|
||||
FROM sale s
|
||||
WHERE s.id = vSelf;
|
||||
|
||||
CALL sale_recalcComponent(vOption);
|
||||
|
||||
DROP TEMPORARY TABLE tmp.recalculateSales;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`sale_checkNoComponents`(vCreatedFrom DATETIME, vCreatedTo DATETIME)
|
||||
BEGIN
|
||||
/**
|
||||
* Comprueba que las ventas creadas entre un rango de fechas tienen componentes
|
||||
*
|
||||
* @param vCreatedFrom inicio del rango
|
||||
* @param vCreatedTo fin del rango
|
||||
*/
|
||||
DECLARE v_done BOOL DEFAULT FALSE;
|
||||
DECLARE vSaleFk INTEGER;
|
||||
DECLARE vTicketFk INTEGER;
|
||||
DECLARE vConcept VARCHAR(50);
|
||||
DECLARE vCur CURSOR FOR
|
||||
SELECT s.id
|
||||
FROM sale s
|
||||
JOIN ticket t ON t.id = s.ticketFk
|
||||
JOIN item i ON i.id = s.itemFk
|
||||
JOIN itemType tp ON tp.id = i.typeFk
|
||||
JOIN itemCategory ic ON ic.id = tp.categoryFk
|
||||
LEFT JOIN tmp.coste c ON c.id = s.id
|
||||
WHERE s.created >= vCreatedFrom AND s.created <= vCreatedTo
|
||||
AND c.id IS NULL
|
||||
AND t.agencyModeFk IS NOT NULL
|
||||
AND t.isDeleted IS FALSE
|
||||
AND t.warehouseFk = 60
|
||||
AND ic.merchandise != FALSE
|
||||
GROUP BY s.id;
|
||||
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND
|
||||
SET v_done = TRUE;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.coste;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.coste;
|
||||
CREATE TEMPORARY TABLE tmp.coste
|
||||
(PRIMARY KEY (id)) ENGINE = MEMORY
|
||||
SELECT s.id
|
||||
FROM sale s
|
||||
JOIN item i ON i.id = s.itemFk
|
||||
JOIN itemType tp ON tp.id = i.typeFk
|
||||
JOIN itemCategory ic ON ic.id = tp.categoryFk
|
||||
JOIN saleComponent sc ON sc.saleFk = s.id
|
||||
JOIN component c ON c.id = sc.componentFk
|
||||
JOIN componentType ct ON ct.id = c.typeFk AND ct.id = 6
|
||||
WHERE s.created >= vCreatedFrom
|
||||
AND ic.merchandise != FALSE;
|
||||
|
||||
OPEN vCur;
|
||||
|
||||
l: LOOP
|
||||
SET v_done = FALSE;
|
||||
FETCH vCur INTO vSaleFk;
|
||||
|
||||
IF v_done THEN
|
||||
LEAVE l;
|
||||
END IF;
|
||||
|
||||
SELECT ticketFk, concept
|
||||
INTO vTicketFk, vConcept
|
||||
FROM sale
|
||||
WHERE id = vSaleFk;
|
||||
|
||||
CALL sale_calculateComponent(vSaleFk, 'renewPrices');
|
||||
END LOOP;
|
||||
|
||||
CLOSE vCur;
|
||||
DROP TEMPORARY TABLE tmp.coste;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`sale_recalcComponent`(vOption VARCHAR(25))
|
||||
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, 'renewPrices', 'imbalance');
|
||||
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`.`ticketCalculateClon`(IN vTicketNew INT, vTicketOld INT)
|
||||
BEGIN
|
||||
/*
|
||||
* Recalcula los componentes un ticket clonado,
|
||||
* las lineas a precio cero fuerza para que tengan precio, el resto lo respeta
|
||||
* @param vTicketNew nuevo ticket clonado
|
||||
* @param vTicketOld icket original, a partir del qual se clonara el nuevo
|
||||
*/
|
||||
|
||||
REPLACE INTO orderTicket(orderFk,ticketFk)
|
||||
SELECT orderFk, vTicketNew
|
||||
FROM orderTicket
|
||||
WHERE ticketFk = vTicketOld;
|
||||
|
||||
-- Bionizamos lineas con Preu = 0
|
||||
CREATE OR REPLACE TEMPORARY TABLE tmp.recalculateSales
|
||||
(PRIMARY KEY (id)) ENGINE = MEMORY
|
||||
SELECT id
|
||||
FROM sale
|
||||
WHERE ticketFk = vTicketNew AND price = 0;
|
||||
|
||||
CALL sale_recalcComponent('renewPrices');
|
||||
|
||||
-- Bionizamos lineas con Preu > 0
|
||||
CREATE OR REPLACE TEMPORARY TABLE tmp.recalculateSales
|
||||
(PRIMARY KEY (id)) ENGINE = MEMORY
|
||||
SELECT id
|
||||
FROM sale
|
||||
WHERE ticketFk = vTicketNew AND price > 0;
|
||||
|
||||
CALL sale_recalcComponent('imbalance');
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.recalculateSales;
|
||||
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticketComponentUpdate`(
|
||||
vTicketFk INT,
|
||||
vClientFk INT,
|
||||
vAgencyModeFk INT,
|
||||
vAddressFk INT,
|
||||
vWarehouseFk TINYINT,
|
||||
vCompanyFk SMALLINT,
|
||||
vShipped DATETIME,
|
||||
vLanded DATE,
|
||||
vIsDeleted BOOLEAN,
|
||||
vHasToBeUnrouted BOOLEAN,
|
||||
vOption VARCHAR(25))
|
||||
BEGIN
|
||||
|
||||
DECLARE EXIT HANDLER FOR SQLEXCEPTION
|
||||
BEGIN
|
||||
ROLLBACK;
|
||||
RESIGNAL;
|
||||
END;
|
||||
|
||||
START TRANSACTION;
|
||||
|
||||
IF (SELECT addressFk FROM ticket WHERE id = vTicketFk) <> vAddressFk THEN
|
||||
|
||||
UPDATE ticket t
|
||||
JOIN address a ON a.id = vAddressFk
|
||||
SET t.nickname = a.nickname
|
||||
WHERE t.id = vTicketFk;
|
||||
END IF;
|
||||
|
||||
UPDATE ticket t
|
||||
SET
|
||||
t.clientFk = vClientFk,
|
||||
t.agencyModeFk = vAgencyModeFk,
|
||||
t.addressFk = vAddressFk,
|
||||
t.warehouseFk = vWarehouseFk,
|
||||
t.companyFk = vCompanyFk,
|
||||
t.landed = vLanded,
|
||||
t.shipped = vShipped,
|
||||
t.isDeleted = vIsDeleted
|
||||
WHERE
|
||||
t.id = vTicketFk;
|
||||
|
||||
IF vHasToBeUnrouted THEN
|
||||
UPDATE ticket t SET t.routeFk = NULL
|
||||
WHERE t.id = vTicketFk;
|
||||
END IF;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.sale;
|
||||
CREATE TEMPORARY TABLE tmp.sale
|
||||
(PRIMARY KEY (saleFk))
|
||||
ENGINE = MEMORY
|
||||
SELECT id AS saleFk, vWarehouseFk warehouseFk
|
||||
FROM sale s WHERE s.ticketFk = vTicketFk;
|
||||
|
||||
CALL ticketComponentUpdateSale (vOption);
|
||||
|
||||
DROP TEMPORARY TABLE tmp.sale;
|
||||
COMMIT;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticketComponentUpdateSale`(vCode VARCHAR(25))
|
||||
BEGIN
|
||||
/**
|
||||
* A partir de la tabla tmp.sale, crea los Movimientos_componentes
|
||||
* y modifica el campo Preu de la tabla Movimientos
|
||||
*
|
||||
* @param i_option integer tipo de actualizacion
|
||||
* @param table tmp.sale tabla memory con el campo saleFk, warehouseFk
|
||||
**/
|
||||
DECLARE vComponentFk INT;
|
||||
|
||||
IF vCode <> 'renewPrices' THEN
|
||||
SELECT id INTO vComponentFk FROM component WHERE `code` = vCode;
|
||||
END IF;
|
||||
|
||||
DELETE sc.*
|
||||
FROM tmp.sale tmps
|
||||
JOIN saleComponent sc ON sc.saleFk = tmps.saleFk
|
||||
JOIN `component` c ON c.id = sc.componentFk
|
||||
WHERE c.isRenewable;
|
||||
|
||||
REPLACE INTO saleComponent(saleFk, componentFk, value)
|
||||
SELECT s.id, tc.componentFk, tc.cost
|
||||
FROM sale s
|
||||
JOIN tmp.sale tmps ON tmps.saleFk = s.id
|
||||
JOIN tmp.ticketComponent tc ON tc.itemFk = s.itemFk AND tc.warehouseFk = tmps.warehouseFk
|
||||
LEFT JOIN saleComponent sc ON sc.saleFk = s.id
|
||||
AND sc.componentFk = tc.componentFk
|
||||
LEFT JOIN `component` c ON c.id = tc.componentFk
|
||||
WHERE IF(sc.componentFk IS NULL AND NOT c.isRenewable, FALSE, TRUE);
|
||||
|
||||
-- Añadir componente venta por paquete
|
||||
REPLACE INTO saleComponent(saleFk, componentFk, value)
|
||||
SELECT t.id, t.componentFk, t.cost
|
||||
FROM (
|
||||
SELECT s.id, tc.componentFk, tc.cost, MOD(s.quantity, b.packing) as resto
|
||||
FROM vn.sale s
|
||||
JOIN tmp.sale tmps ON tmps.saleFk = s.id
|
||||
JOIN cache.last_buy lb ON lb.item_id = s.itemFk AND tmps.warehouseFk = lb.warehouse_id
|
||||
JOIN vn.buy b ON b.id = buy_id
|
||||
JOIN tmp.ticketComponent tc ON tc.itemFk = s.itemFk AND tc.warehouseFk = tmps.warehouseFk
|
||||
JOIN `component` c ON c.id = tc.componentFk AND c.code = 'salePerPackage'
|
||||
LEFT JOIN (
|
||||
SELECT s.id
|
||||
FROM vn.sale s
|
||||
JOIN tmp.sale tmps ON tmps.saleFk = s.id
|
||||
JOIN tmp.ticketComponent tc ON tc.itemFk = s.itemFk AND tc.warehouseFk = tmps.warehouseFk
|
||||
JOIN saleComponent sc ON sc.saleFk = s.id AND sc.componentFk = tc.componentFk
|
||||
JOIN `component` c ON c.id = sc.componentFk AND c.code = 'lastUnitsDiscount'
|
||||
) tp ON tp.id = s.id
|
||||
WHERE tp.id IS NULL
|
||||
HAVING resto <> 0) t;
|
||||
|
||||
IF vCode <> 'renewPrices' THEN
|
||||
REPLACE INTO saleComponent(saleFk, componentFk, value)
|
||||
SELECT s.id, vComponentFk, ROUND((s.price * (100 - s.discount) / 100) - SUM(sc.value), 3) dif
|
||||
FROM sale s
|
||||
JOIN tmp.sale tmps ON tmps.saleFk = s.id
|
||||
LEFT JOIN saleComponent sc ON sc.saleFk = s.id
|
||||
WHERE sc.saleFk <> vComponentFk
|
||||
GROUP BY s.id
|
||||
HAVING dif <> 0;
|
||||
ELSE
|
||||
UPDATE sale s
|
||||
JOIN item i on i.id = s.itemFk
|
||||
JOIN itemType it on it.id = i.typeFk
|
||||
JOIN (SELECT SUM(sc.value) sumValue, sc.saleFk
|
||||
FROM saleComponent sc
|
||||
JOIN tmp.sale tmps ON tmps.saleFk = sc.saleFk
|
||||
GROUP BY sc.saleFk) sc ON sc.saleFk = s.id
|
||||
SET s.price = sumValue / ((100 - s.discount) / 100)
|
||||
WHERE it.code != 'PRT' ;
|
||||
|
||||
REPLACE INTO saleComponent(saleFk, componentFk, value)
|
||||
SELECT s.id, 21, ROUND((s.price * (100 - s.discount) / 100) - SUM(value), 3) saleValue
|
||||
FROM sale s
|
||||
JOIN tmp.sale tmps ON tmps.saleFk = s.id
|
||||
LEFT JOIN saleComponent sc ON sc.saleFk = s.id
|
||||
WHERE sc.componentFk != 21
|
||||
GROUP BY s.id
|
||||
HAVING ROUND(saleValue, 4) <> 0;
|
||||
END IF;
|
||||
|
||||
UPDATE sale s
|
||||
JOIN (
|
||||
SELECT SUM(sc.value) sumValue, sc.saleFk
|
||||
FROM saleComponent sc
|
||||
JOIN tmp.sale tmps ON tmps.saleFk = sc.saleFk
|
||||
JOIN `component` c ON c.id = sc.componentFk
|
||||
JOIN componentType ct on ct.id = c.typeFk AND ct.isBase
|
||||
GROUP BY sc.saleFk) sc ON sc.saleFk = s.id
|
||||
SET s.priceFixed = sumValue, s.isPriceFixed = 1;
|
||||
|
||||
DELETE sc.*
|
||||
FROM saleComponent sc
|
||||
JOIN tmp.sale tmps ON tmps.saleFk = sc.saleFk
|
||||
JOIN sale s on s.id = sc.saleFk
|
||||
JOIN item i ON i.id = s.itemFk
|
||||
JOIN itemType it ON it.id = i.typeFk
|
||||
WHERE it.code = 'PRT';
|
||||
|
||||
INSERT INTO saleComponent(saleFk, componentFk, value)
|
||||
SELECT s.id, 15, s.price
|
||||
FROM sale s
|
||||
JOIN tmp.sale tmps ON tmps.saleFk = s.id
|
||||
JOIN item i ON i.id = s.itemFK
|
||||
JOIN itemType it ON it.id = i.typeFk
|
||||
WHERE it.code = 'PRT' AND s.price > 0;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_checkNoComponents`(vShippedFrom DATETIME, vShippedTo DATETIME)
|
||||
BEGIN
|
||||
|
||||
/**
|
||||
* Comprueba que los tickets entre un rango de fechas tienen componentes
|
||||
* y recalcula sus componentes
|
||||
*
|
||||
* @param vShippedFrom rango inicial de fecha
|
||||
* @param vShippedTo rango final de fecha
|
||||
*/
|
||||
|
||||
CREATE OR REPLACE TEMPORARY TABLE tmp.coste
|
||||
(primary key (id)) ENGINE = MEMORY
|
||||
SELECT s.id
|
||||
FROM ticket t
|
||||
JOIN sale s ON s.ticketFk = t.id
|
||||
JOIN item i ON i.id = s.itemFk
|
||||
JOIN itemType tp ON tp.id = i.typeFk
|
||||
JOIN itemCategory ic ON ic.id = tp.categoryFk
|
||||
JOIN saleComponent sc ON sc.saleFk = s.id
|
||||
JOIN component c ON c.id = sc.componentFk
|
||||
JOIN componentType ct ON ct.id = c.typeFk AND ct.id = 1
|
||||
WHERE t.shipped BETWEEN vShippedFrom AND vShippedTo
|
||||
AND ic.merchandise;
|
||||
|
||||
CREATE OR REPLACE TEMPORARY TABLE tmp.recalculateSales
|
||||
(primary key (id)) ENGINE = MEMORY
|
||||
SELECT DISTINCT s.id
|
||||
FROM ticket t
|
||||
JOIN sale s ON s.ticketFk = t.id
|
||||
JOIN item i ON i.id = s.itemFk
|
||||
JOIN itemType tp ON tp.id = i.typeFk
|
||||
JOIN itemCategory ic ON ic.id = tp.categoryFk
|
||||
LEFT JOIN tmp.coste c ON c.id = s.id
|
||||
WHERE t.shipped >= vShippedFrom AND t.shipped <= vShippedTo
|
||||
AND c.id IS NULL
|
||||
AND ic.merchandise;
|
||||
|
||||
CALL sale_recalcComponent('renewPrices');
|
||||
|
||||
DROP TEMPORARY TABLE tmp.recalculateSales;
|
||||
DROP TEMPORARY TABLE tmp.coste;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_componentMakeUpdate`(
|
||||
vTicketFk INT,
|
||||
vClientFk INT,
|
||||
vNickname VARCHAR(50),
|
||||
vAgencyModeFk INT,
|
||||
vAddressFk INT,
|
||||
vZoneFk INT,
|
||||
vWarehouseFk INT,
|
||||
vCompanyFk INT,
|
||||
vShipped DATETIME,
|
||||
vLanded DATE,
|
||||
vIsDeleted BOOLEAN,
|
||||
vHasToBeUnrouted BOOLEAN,
|
||||
vOption VARCHAR(25))
|
||||
BEGIN
|
||||
|
||||
/**
|
||||
* Modifica en el ticket los campos que se le pasan por parámetro
|
||||
* y cambia sus componentes
|
||||
*
|
||||
* @param vTicketFk Id del ticket a modificar
|
||||
* @param vClientFk nuevo cliente
|
||||
* @param vNickname nuevo alias
|
||||
* @param vAgencyModeFk nueva agencia
|
||||
* @param vAddressFk nuevo consignatario
|
||||
* @param vZoneFk nueva zona
|
||||
* @param vWarehouseFk nuevo almacen
|
||||
* @param vCompanyFk nueva empresa
|
||||
* @param vShipped nueva fecha del envio de mercancia
|
||||
* @param vLanded nueva fecha de recepcion de mercancia
|
||||
* @param vIsDeleted si se borra el ticket
|
||||
* @param vHasToBeUnrouted si se le elimina la ruta al ticket
|
||||
* @param vOption opcion para el case del proc ticketComponentUpdateSale
|
||||
*/
|
||||
|
||||
DECLARE vPrice DECIMAL(10,2);
|
||||
DECLARE vBonus DECIMAL(10,2);
|
||||
|
||||
CALL ticket_componentPreview (vTicketFk, vLanded, vAddressFk, vZoneFk, vWarehouseFk);
|
||||
|
||||
IF (SELECT addressFk FROM ticket WHERE id = vTicketFk) <> vAddressFk THEN
|
||||
|
||||
UPDATE ticket t
|
||||
JOIN address a ON a.id = vAddressFk
|
||||
SET t.nickname = a.nickname
|
||||
WHERE t.id = vTicketFk;
|
||||
|
||||
END IF;
|
||||
|
||||
CALL zone_getShipped(vLanded, vAddressFk, vAgencyModeFk, TRUE);
|
||||
|
||||
SELECT zoneFk, price, bonus INTO vZoneFk, vPrice, vBonus
|
||||
FROM tmp.zoneGetShipped
|
||||
WHERE shipped BETWEEN DATE(vShipped) AND util.dayEnd(vShipped) AND warehouseFk = vWarehouseFk LIMIT 1;
|
||||
|
||||
UPDATE ticket t
|
||||
SET
|
||||
t.clientFk = vClientFk,
|
||||
t.nickname = vNickname,
|
||||
t.agencyModeFk = vAgencyModeFk,
|
||||
t.addressFk = vAddressFk,
|
||||
t.zoneFk = vZoneFk,
|
||||
t.zonePrice = vPrice,
|
||||
t.zoneBonus = vBonus,
|
||||
t.warehouseFk = vWarehouseFk,
|
||||
t.companyFk = vCompanyFk,
|
||||
t.landed = vLanded,
|
||||
t.shipped = vShipped,
|
||||
t.isDeleted = vIsDeleted
|
||||
WHERE
|
||||
t.id = vTicketFk;
|
||||
|
||||
IF vHasToBeUnrouted THEN
|
||||
UPDATE ticket t SET t.routeFk = NULL
|
||||
WHERE t.id = vTicketFk;
|
||||
END IF;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.sale;
|
||||
CREATE TEMPORARY TABLE tmp.sale
|
||||
(PRIMARY KEY (saleFk))
|
||||
ENGINE = MEMORY
|
||||
SELECT id AS saleFk, vWarehouseFk warehouseFk
|
||||
FROM sale s WHERE s.ticketFk = vTicketFk;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.ticketComponent;
|
||||
CREATE TEMPORARY TABLE tmp.ticketComponent
|
||||
SELECT * FROM tmp.ticketComponentPreview;
|
||||
|
||||
CALL ticketComponentUpdateSale (vOption);
|
||||
|
||||
DROP TEMPORARY TABLE tmp.sale;
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.ticketComponent;
|
||||
|
||||
DROP TEMPORARY TABLE tmp.zoneGetShipped, tmp.ticketComponentPreview;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_recalcComponents`(vSelf INT, vOption VARCHAR(25))
|
||||
proc: BEGIN
|
||||
|
||||
/**
|
||||
* Crea tabla temporal para sale_recalcComponent() para recalcular los componentes
|
||||
*
|
||||
* @param vSelf Id del ticket
|
||||
* @param vOption si no se quiere forzar llamar con NULL
|
||||
*/
|
||||
|
||||
CREATE OR REPLACE TEMPORARY TABLE tmp.recalculateSales
|
||||
SELECT s.id
|
||||
FROM sale s
|
||||
WHERE s.ticketFk = vSelf;
|
||||
|
||||
CALL sale_recalcComponent(vOption);
|
||||
|
||||
DROP TEMPORARY TABLE tmp.recalculateSales;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
TRUNCATE TABLE `vn`.`ticketUpdateAction`;
|
||||
INSERT INTO `vn`.`ticketUpdateAction` (id, description, code) VALUES(1, 'Cambiar los precios en el ticket', 'renewPrice');
|
||||
INSERT INTO `vn`.`ticketUpdateAction` (id, description, code) VALUES(2, 'Convertir en maná', 'mana');
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE `vn`.`viaexpressConfig` ADD agencyModeFk int DEFAULT NULL NULL COMMENT 'Indica el agencyMode que es interdia';
|
||||
ALTER TABLE `vn`.`viaexpressConfig` ADD CONSTRAINT viaexpressConfig_agencyMode_Fk FOREIGN KEY (agencyModeFK) REFERENCES vn.agencyMode(id) ON DELETE RESTRICT ON UPDATE RESTRICT;
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE `vn`.`item` DROP COLUMN stars;
|
|
@ -0,0 +1,4 @@
|
|||
REVOKE UPDATE ON TABLE `vn`.`item` FROM `employee`;
|
||||
|
||||
|
||||
GRANT UPDATE(id, equivalent, stems, minPrice, isToPrint, family, box, category, doPhoto, image, inkFk, intrastatFk, hasMinPrice, created, comment, typeFk, generic, producerFk, description, density, relevancy, expenseFk, isActive, subName, tag5, value5, tag6, value6, tag7, value7, tag8, value8, tag9, value9, tag10, value10, minimum, upToDown, supplyResponseFk, hasKgPrice, isFloramondo, isFragile, numberOfItemsPerCask, embalageCode, quality, stemMultiplier, itemPackingTypeFk, packingOut, genericFk, packingShelve, isLaid, lastUsed, weightByPiece, weightByPiece, editorFk, recycledPlastic, nonRecycledPlastic, minQuantity) ON TABLE `vn`.`item` TO `employee`;
|
|
@ -0,0 +1,12 @@
|
|||
ALTER TABLE `vn`.`company` MODIFY COLUMN `supplierAccountFk` mediumint(8) unsigned DEFAULT NULL NULL COMMENT 'Cuenta por defecto para ingresos desde este pais';
|
||||
|
||||
|
||||
ALTER TABLE `vn`.`supplierAccount`
|
||||
ADD COLUMN `countryFk` mediumint(8) unsigned DEFAULT NULL,
|
||||
ADD CONSTRAINT `fk_supplierAccount_country`
|
||||
FOREIGN KEY (`countryFk`) REFERENCES `country` (`id`) ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE `vn`.`supplierAccount`
|
||||
ADD UNIQUE KEY `uk_supplier_country` (`supplierFk`, `countryFk`);
|
||||
|
||||
|
|
@ -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 ;
|
|
@ -1,4 +1,4 @@
|
|||
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) VALUES
|
||||
INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId) VALUES
|
||||
('VnRole','*','READ','ALLOW','ROLE','employee'),
|
||||
('VnRole','*','WRITE','ALLOW','ROLE','it');
|
||||
|
|
@ -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;
|
|
@ -0,0 +1,9 @@
|
|||
UPDATE `vn`.`supplierAccount` sa
|
||||
JOIN `vn`.`country` c ON sa.countryFk = c.id AND c.code = 'FR'
|
||||
SET countryFk = c.id
|
||||
WHERE iban = 'FR7630003012690002801121597';
|
||||
|
||||
UPDATE `vn`.`supplierAccount` sa
|
||||
JOIN `vn`.`country` c ON sa.countryFk = c.id AND c.code = 'PT'
|
||||
SET countryFk = c.id
|
||||
WHERE iban = 'PT50001000005813059150168';
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE `vn`.`invoiceOutConfig`
|
||||
ADD IF NOT EXISTS refLen TINYINT UNSIGNED DEFAULT 5 NOT NULL COMMENT 'Invoice reference identifier length';
|
|
@ -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 ;
|
|
@ -600,6 +600,9 @@ 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'),
|
||||
|
@ -623,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),
|
||||
|
@ -929,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
|
||||
|
@ -2911,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
|
||||
|
@ -3011,6 +3010,15 @@ INSERT INTO `vn`.`invoiceCorrectionType` (`id`, `description`)
|
|||
(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),
|
||||
|
@ -3022,16 +3030,16 @@ INSERT INTO `vn`.`docuwareTablet` (`tablet`,`description`)
|
|||
('Tablet1','Jarvis tablet'),
|
||||
('Tablet2','Avengers tablet');
|
||||
|
||||
INSERT INTO `vn`.`sms` (`id`, `senderFk`, `sender`, `destination`, `message`, `statusCode`, `status`, `created`)
|
||||
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`)
|
||||
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);
|
||||
(14, 1101, 4, 27);
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -27,11 +27,8 @@ describe('Item Edit basic data path', () => {
|
|||
|
||||
it(`should edit the item basic data and confirm the item data was edited`, async() => {
|
||||
const values = {
|
||||
name: 'Rose of Purity',
|
||||
longName: 'RS Rose of Purity',
|
||||
type: 'Anthurium',
|
||||
intrastat: 'Coral y materiales similares',
|
||||
origin: 'Spain',
|
||||
relevancy: 1,
|
||||
generic: 'Pallet',
|
||||
isActive: false,
|
||||
|
|
|
@ -225,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('7');
|
||||
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
|
||||
await page.waitToClick(selectors.ticketDescriptor.moreMenuDeleteTicket);
|
||||
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||
|
|
|
@ -69,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}}",
|
||||
|
@ -203,5 +202,6 @@
|
|||
"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}}",
|
||||
"Incorrect pin": "Incorrect pin."
|
||||
}
|
||||
"Incorrect pin": "Incorrect pin.",
|
||||
"The notification subscription of this worker cant be modified": "The notification subscription of this worker cant be modified"
|
||||
}
|
|
@ -136,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",
|
||||
|
@ -332,6 +331,7 @@
|
|||
"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}}",
|
||||
"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",
|
||||
|
|
|
@ -270,8 +270,8 @@ class VnMySQL extends MySQL {
|
|||
|
||||
isLoggable(model) {
|
||||
const Model = this.getModelDefinition(model).model;
|
||||
const settings = Model.definition.settings;
|
||||
return settings.base && settings.base === 'Loggable';
|
||||
const {settings} = Model.definition;
|
||||
return settings?.mixins?.Loggable;
|
||||
}
|
||||
|
||||
invokeMethod(method, args, model, ctx, opts, cb) {
|
||||
|
@ -291,7 +291,7 @@ class VnMySQL extends MySQL {
|
|||
}
|
||||
|
||||
try {
|
||||
const userId = opts.httpCtx && opts.httpCtx.active.accessToken.userId;
|
||||
const userId = opts.httpCtx && opts.httpCtx.active?.accessToken?.userId;
|
||||
if (userId) {
|
||||
const user = await Model.app.models.VnUser.findById(userId, {fields: ['name']}, opts);
|
||||
await this.executeP(`CALL account.myUser_loginWithName(?)`, [user.name], opts);
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
const SalixError = require('../../util/salixError');
|
||||
const UserError = require('../../util/user-error');
|
||||
const logToConsole = require('strong-error-handler/lib/logger');
|
||||
|
||||
module.exports = function() {
|
||||
return function(err, req, res, next) {
|
||||
// Thrown user errors
|
||||
if (err instanceof UserError) {
|
||||
if (err instanceof SalixError) {
|
||||
err.message = req.__(err.message, ...err.translateArgs);
|
||||
return next(err);
|
||||
}
|
||||
|
@ -13,7 +14,7 @@ module.exports = function() {
|
|||
if (err.statusCode == 422) {
|
||||
try {
|
||||
let code;
|
||||
let messages = err.details.messages;
|
||||
let {messages} = err.details;
|
||||
for (code in messages) break;
|
||||
err.message = req.__(messages[code][0]);
|
||||
return next(err);
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
module.exports = class ForbiddenError extends Error {
|
||||
const SalixError = require('./salixError');
|
||||
module.exports = class ForbiddenError extends SalixError {
|
||||
constructor(message, code, ...translateArgs) {
|
||||
super(message);
|
||||
this.name = 'ForbiddenError';
|
||||
this.name = ForbiddenError.name;
|
||||
this.statusCode = 403;
|
||||
this.code = code;
|
||||
this.translateArgs = translateArgs;
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
module.exports = class SalixError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
}
|
||||
};
|
|
@ -4,10 +4,11 @@
|
|||
* the final user, so they cannot contain sensitive data and must
|
||||
* be understandable by people who do not have a technical profile.
|
||||
*/
|
||||
module.exports = class UserError extends Error {
|
||||
const SalixError = require('./salixError');
|
||||
module.exports = class UserError extends SalixError {
|
||||
constructor(message, code, ...translateArgs) {
|
||||
super(message);
|
||||
this.name = 'UserError';
|
||||
this.name = UserError.name;
|
||||
this.statusCode = 400;
|
||||
this.code = code;
|
||||
this.translateArgs = translateArgs;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -120,7 +120,7 @@ module.exports = Self => {
|
|||
observationTypeFk: obsevationType.id
|
||||
}, myOptions);
|
||||
|
||||
await models.TicketTracking.create({
|
||||
await models.Ticket.state(ctx, {
|
||||
ticketFk: newRefundTicket.id,
|
||||
stateFk: state.id,
|
||||
userFk: worker.id
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -27,4 +27,4 @@
|
|||
label="Cancel">
|
||||
</vn-button>
|
||||
</vn-button-bar>
|
||||
</form>
|
||||
</form>
|
||||
|
|
|
@ -42,14 +42,15 @@
|
|||
translate-attr="{title: 'Set as default'}">
|
||||
</vn-icon-button>
|
||||
</vn-none>
|
||||
<vn-one
|
||||
<vn-one
|
||||
style="overflow: hidden; min-width: 14em;">
|
||||
<div class="ellipsize"><b>{{::address.nickname}} - #{{::address.id}}</b></div>
|
||||
<div class="ellipsize" name="street">{{::address.street}}</div>
|
||||
<div class="ellipsize">
|
||||
<span ng-show="::address.postalCode">{{::address.postalCode}} -</span>
|
||||
<span ng-show="::address.city">{{::address.city}},</span>
|
||||
{{::address.province.name}}
|
||||
<span ng-show="::address.postalCode">{{::address.postalCode}} -</span>
|
||||
<span ng-show="::address.city">{{::address.city}},</span>
|
||||
<span ng-show="::address.province.name">{{::address.province.name}},</span>
|
||||
{{::address.province.country.country}}
|
||||
</div>
|
||||
<div class="ellipsize">
|
||||
{{::address.phone}}<span ng-if="::address.mobile">, </span>
|
||||
|
@ -72,7 +73,7 @@
|
|||
class="vn-hide-narrow vn-px-md border-solid-left"
|
||||
style="height: 6em; overflow: auto;">
|
||||
<vn-one ng-repeat="observation in address.observations track by $index" ng-class="{'vn-pt-sm': $index}">
|
||||
<b>{{::observation.observationType.description}}:</b>
|
||||
<b>{{::observation.observationType.description}}:</b>
|
||||
<span>{{::observation.description}}</span>
|
||||
</vn-one>
|
||||
</vn-vertical>
|
||||
|
|
|
@ -33,7 +33,13 @@ class Controller extends Section {
|
|||
}, {
|
||||
relation: 'province',
|
||||
scope: {
|
||||
fields: ['id', 'name']
|
||||
fields: ['id', 'name', 'countryFk'],
|
||||
include: {
|
||||
relation: 'country',
|
||||
scope: {
|
||||
fields: ['id', 'country']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -78,7 +78,7 @@ 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) {
|
||||
|
|
|
@ -18,22 +18,6 @@
|
|||
</vn-crud-model>
|
||||
<form name="form" ng-submit="watcher.submit()" ng-cloak class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
label="Name"
|
||||
ng-model="$ctrl.item.name"
|
||||
vn-name="name"
|
||||
rule
|
||||
vn-focus>
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
label="Full name"
|
||||
ng-model="$ctrl.item.longName"
|
||||
vn-name="longName"
|
||||
rule
|
||||
info="Full name calculates based on tags 1-3. Is not recommended to change it manually">
|
||||
</vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete
|
||||
url="ItemTypes"
|
||||
|
@ -52,6 +36,34 @@
|
|||
</div>
|
||||
</tpl-item>
|
||||
</vn-autocomplete>
|
||||
<vn-textfield
|
||||
label="Reference"
|
||||
ng-model="$ctrl.item.comment"
|
||||
vn-name="comment"
|
||||
rule>
|
||||
</vn-textfield>
|
||||
<vn-input-number
|
||||
min="0"
|
||||
label="Relevancy"
|
||||
ng-model="$ctrl.item.relevancy"
|
||||
vn-name="relevancy"
|
||||
rule>
|
||||
</vn-input-number>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-input-number
|
||||
min="0"
|
||||
label="stems"
|
||||
ng-model="$ctrl.item.stems"
|
||||
vn-name="stems"
|
||||
rule>
|
||||
</vn-input-number>
|
||||
<vn-input-number
|
||||
min="0"
|
||||
label="Multiplier"
|
||||
ng-model="$ctrl.item.stemMultiplier"
|
||||
vn-name="stemMultiplier">
|
||||
</vn-input-number>
|
||||
<vn-autocomplete
|
||||
label="Generic"
|
||||
url="Items/withName"
|
||||
|
@ -105,63 +117,10 @@
|
|||
url="Expenses"
|
||||
label="Expense"
|
||||
ng-model="$ctrl.item.expenseFk"
|
||||
vn-name="expense"
|
||||
vn-name="expence"
|
||||
initial-data="$ctrl.item.expense">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete
|
||||
data="originsData"
|
||||
label="Origin"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
ng-model="$ctrl.item.originFk"
|
||||
vn-name="origin"
|
||||
initial-data="$ctrl.item.origin">
|
||||
</vn-autocomplete>
|
||||
<vn-input-number
|
||||
min="0"
|
||||
label="Size"
|
||||
ng-model="$ctrl.item.size"
|
||||
vn-name="size"
|
||||
rule>
|
||||
</vn-input-number>
|
||||
<vn-textfield
|
||||
label="Reference"
|
||||
ng-model="$ctrl.item.comment"
|
||||
vn-name="comment"
|
||||
rule>
|
||||
</vn-textfield>
|
||||
<vn-input-number
|
||||
min="0"
|
||||
label="Relevancy"
|
||||
ng-model="$ctrl.item.relevancy"
|
||||
vn-name="relevancy"
|
||||
rule>
|
||||
</vn-input-number>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-input-number
|
||||
min="0"
|
||||
label="stems"
|
||||
ng-model="$ctrl.item.stems"
|
||||
vn-name="stems"
|
||||
rule>
|
||||
</vn-input-number>
|
||||
<vn-input-number
|
||||
min="0"
|
||||
label="Multiplier"
|
||||
ng-model="$ctrl.item.stemMultiplier"
|
||||
vn-name="stemMultiplier">
|
||||
</vn-input-number>
|
||||
<vn-input-number
|
||||
min="1"
|
||||
label="Minimum sales quantity"
|
||||
ng-model="$ctrl.item.minQuantity"
|
||||
vn-name="minQuantity"
|
||||
rule>
|
||||
</vn-input-number>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-input-number
|
||||
min="0"
|
||||
|
@ -192,14 +151,6 @@
|
|||
rule>
|
||||
</vn-input-number>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textarea
|
||||
label="Description"
|
||||
ng-model="$ctrl.item.description"
|
||||
vn-name="description"
|
||||
rule>
|
||||
</vn-textarea>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-check
|
||||
label="Active"
|
||||
|
@ -224,6 +175,14 @@
|
|||
info="This item does need a photo">
|
||||
</vn-check>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textarea
|
||||
label="Description"
|
||||
ng-model="$ctrl.item.description"
|
||||
vn-name="description"
|
||||
rule>
|
||||
</vn-textarea>
|
||||
</vn-horizontal>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -37,11 +37,6 @@
|
|||
value="{{::item.value7}}">
|
||||
</vn-label-value>
|
||||
</div>
|
||||
<vn-horizontal>
|
||||
<vn-one>
|
||||
<vn-rating ng-if="::item.stars"
|
||||
ng-model="::item.stars"/>
|
||||
</vn-one>
|
||||
<vn-horizontal
|
||||
class="text-right text-caption alert vn-mr-xs"
|
||||
ng-if="::item.minQuantity">
|
||||
|
@ -54,7 +49,6 @@
|
|||
</vn-one>
|
||||
{{::item.minQuantity}}
|
||||
</vn-horizontal>
|
||||
</vn-horizontal>
|
||||
<div class="footer">
|
||||
<div class="price">
|
||||
<vn-one>
|
||||
|
|
|
@ -16,19 +16,4 @@ columns:
|
|||
vehicleFk: vehicle
|
||||
agencyModeFk: agency
|
||||
routeFk: route
|
||||
zoneFk: zone
|
||||
name: name
|
||||
beachFk: beach
|
||||
ticketPacked: tickets packed
|
||||
ticketFree: tickets free
|
||||
ticketProduction: tickets production
|
||||
packages: packages
|
||||
note: note
|
||||
dated: dated
|
||||
dockFk: dock
|
||||
priority: priority
|
||||
etd: etd
|
||||
expeditionTruckFk: truck
|
||||
m3boxes: m3 boxes
|
||||
bufferFk: buffer
|
||||
isPickingAllowed: is picking allowed
|
||||
zoneFk: zone
|
|
@ -16,19 +16,4 @@ columns:
|
|||
vehicleFk: vehículo
|
||||
agencyModeFk: agencia
|
||||
routeFk: ruta
|
||||
zoneFk: zona
|
||||
name: nombre
|
||||
beachFk: playa
|
||||
ticketPacked: tickets encajados
|
||||
ticketFree: tickets libres
|
||||
ticketProduction: tickets producción
|
||||
packages: paquetes
|
||||
note: nota
|
||||
dated: fecha
|
||||
dockFk: muelle
|
||||
priority: prioridad
|
||||
etd: etd
|
||||
expeditionTruckFk: camión
|
||||
m3boxes: m3 cajas
|
||||
bufferFk: buffer
|
||||
isPickingAllowed: está permitido recoger
|
||||
zoneFk: zona
|
|
@ -0,0 +1,19 @@
|
|||
name: routesMonitor
|
||||
columns:
|
||||
routeFk: route
|
||||
name: name
|
||||
beachFk: beach
|
||||
ticketPacked: tickets packed
|
||||
ticketFree: tickets free
|
||||
ticketProduction: tickets production
|
||||
packages: packages
|
||||
note: note
|
||||
dated: dated
|
||||
dockFk: dock
|
||||
m3: m3
|
||||
priority: priority
|
||||
etd: etd
|
||||
expeditionTruckFk: truck
|
||||
m3boxes: m3 boxes
|
||||
bufferFk: buffer
|
||||
isPickingAllowed: is picking allowed
|
|
@ -0,0 +1,19 @@
|
|||
name: monitorRutas
|
||||
columns:
|
||||
routeFk: ruta
|
||||
name: nombre
|
||||
beachFk: playa
|
||||
ticketPacked: tickets encajados
|
||||
ticketFree: tickets libres
|
||||
ticketProduction: tickets producción
|
||||
packages: paquetes
|
||||
note: nota
|
||||
dated: fecha
|
||||
dockFk: muelle
|
||||
m3: m3
|
||||
priority: prioridad
|
||||
etd: etd
|
||||
expeditionTruckFk: camión
|
||||
m3boxes: m3 cajas
|
||||
bufferFk: buffer
|
||||
isPickingAllowed: está permitido recoger
|
|
@ -22,5 +22,8 @@
|
|||
},
|
||||
"Vehicle": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"RoutesMonitor": {
|
||||
"dataSource": "vn"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"name": "RoutesMonitor",
|
||||
"base": "VnModel",
|
||||
"mixins": {
|
||||
"Loggable": true
|
||||
},
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "routesMonitor"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"routeFk": {
|
||||
"type": "number",
|
||||
"id": true,
|
||||
"description": "Identifier"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"beachFk": {
|
||||
"type": "number"
|
||||
},
|
||||
"ticketPacked": {
|
||||
"type": "number"
|
||||
},
|
||||
"ticketFree": {
|
||||
"type": "number"
|
||||
},
|
||||
"ticketProduction": {
|
||||
"type": "number"
|
||||
},
|
||||
"packages": {
|
||||
"type": "number"
|
||||
},
|
||||
"note": {
|
||||
"type": "string"
|
||||
},
|
||||
"dated": {
|
||||
"type": "date"
|
||||
},
|
||||
"dockFk": {
|
||||
"type": "number"
|
||||
},
|
||||
"m3": {
|
||||
"type": "number"
|
||||
},
|
||||
"priority": {
|
||||
"type": "number"
|
||||
},
|
||||
"expeditionTruckFk": {
|
||||
"type": "number"
|
||||
},
|
||||
"m3boxes": {
|
||||
"type": "number"
|
||||
},
|
||||
"bufferFk": {
|
||||
"type": "number"
|
||||
},
|
||||
"isPickingAllowed": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,9 @@
|
|||
},
|
||||
"beneficiary": {
|
||||
"type": "string"
|
||||
},
|
||||
"supplierFk": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module.exports = Self => {
|
||||
Self.clone = async(ctx, salesIds, servicesIds, withWarehouse, group, negative, options) => {
|
||||
Self.clone = async(ctx, salesIds, servicesIds, withWarehouse, negative, options) => {
|
||||
const models = Self.app.models;
|
||||
const myOptions = {};
|
||||
let tx;
|
||||
|
@ -14,22 +14,33 @@ module.exports = Self => {
|
|||
}
|
||||
|
||||
try {
|
||||
const salesFilter = {
|
||||
where: {id: {inq: salesIds}},
|
||||
include: {
|
||||
relation: 'components',
|
||||
scope: {
|
||||
fields: ['saleFk', 'componentFk', 'value']
|
||||
let sales;
|
||||
let services;
|
||||
|
||||
if (salesIds && salesIds.length) {
|
||||
sales = await models.Sale.find({
|
||||
where: {id: {inq: salesIds}},
|
||||
include: {
|
||||
relation: 'components',
|
||||
scope: {
|
||||
fields: ['saleFk', 'componentFk', 'value']
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const sales = await models.Sale.find(salesFilter, myOptions);
|
||||
let ticketsIds = [...new Set(sales.map(sale => sale.ticketFk))];
|
||||
}, myOptions);
|
||||
}
|
||||
|
||||
if (servicesIds && servicesIds.length) {
|
||||
services = await models.TicketService.find({
|
||||
where: {id: {inq: servicesIds}}
|
||||
}, myOptions);
|
||||
}
|
||||
|
||||
let ticketsIds = sales ?
|
||||
[...new Set(sales.map(sale => sale.ticketFk))] :
|
||||
[...new Set(services.map(service => service.ticketFk))];
|
||||
|
||||
const mappedTickets = new Map();
|
||||
|
||||
if (group) ticketsIds = [ticketsIds[0]];
|
||||
|
||||
for (let ticketId of ticketsIds) {
|
||||
const newTicket = await createTicket(
|
||||
ctx,
|
||||
|
@ -41,32 +52,28 @@ module.exports = Self => {
|
|||
newTickets.push(newTicket);
|
||||
mappedTickets.set(ticketId, newTicket.id);
|
||||
}
|
||||
if (sales) {
|
||||
for (const sale of sales) {
|
||||
const newTicketId = mappedTickets.get(sale.ticketFk);
|
||||
|
||||
for (const sale of sales) {
|
||||
const newTicketId = mappedTickets.get(sale.ticketFk);
|
||||
const createdSale = await models.Sale.create({
|
||||
ticketFk: newTicketId,
|
||||
itemFk: sale.itemFk,
|
||||
quantity: negative ? - sale.quantity : sale.quantity,
|
||||
concept: sale.concept,
|
||||
price: sale.price,
|
||||
discount: sale.discount,
|
||||
}, myOptions);
|
||||
|
||||
const createdSale = await models.Sale.create({
|
||||
ticketFk: newTicketId,
|
||||
itemFk: sale.itemFk,
|
||||
quantity: negative ? - sale.quantity : sale.quantity,
|
||||
concept: sale.concept,
|
||||
price: sale.price,
|
||||
discount: sale.discount,
|
||||
}, myOptions);
|
||||
const components = sale.components();
|
||||
for (const component of components)
|
||||
component.saleFk = createdSale.id;
|
||||
|
||||
const components = sale.components();
|
||||
for (const component of components)
|
||||
component.saleFk = createdSale.id;
|
||||
|
||||
await models.SaleComponent.create(components, myOptions);
|
||||
await models.SaleComponent.create(components, myOptions);
|
||||
}
|
||||
}
|
||||
|
||||
if (servicesIds && servicesIds.length) {
|
||||
const servicesFilter = {
|
||||
where: {id: {inq: servicesIds}}
|
||||
};
|
||||
const services = await models.TicketService.find(servicesFilter, myOptions);
|
||||
|
||||
if (services) {
|
||||
for (const service of services) {
|
||||
const newTicketId = mappedTickets.get(service.ticketFk);
|
||||
|
||||
|
@ -109,7 +116,10 @@ module.exports = Self => {
|
|||
|
||||
const newTicket = await models.Ticket.new(ctx, myOptions);
|
||||
|
||||
if (negative) {
|
||||
const ticketRefund = await models.TicketRefund.findOne({
|
||||
where: {refundTicketFk: ticketId}
|
||||
}, myOptions);
|
||||
if (negative && (withWarehouse || !ticketRefund?.id)) {
|
||||
await models.TicketRefund.create({
|
||||
originalTicketFk: ticketId,
|
||||
refundTicketFk: newTicket.id
|
||||
|
|
|
@ -6,7 +6,6 @@ module.exports = Self => {
|
|||
{
|
||||
arg: 'salesIds',
|
||||
type: ['number'],
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'servicesIds',
|
||||
|
@ -47,7 +46,6 @@ module.exports = Self => {
|
|||
salesIds,
|
||||
servicesIds,
|
||||
withWarehouse,
|
||||
false,
|
||||
true,
|
||||
myOptions
|
||||
);
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
describe('Ticket cloning - clone function', () => {
|
||||
let ctx;
|
||||
let options;
|
||||
let tx;
|
||||
|
||||
beforeEach(async() => {
|
||||
ctx = {
|
||||
req: {
|
||||
accessToken: {userId: 9},
|
||||
headers: {origin: 'http://localhost'}
|
||||
},
|
||||
args: {}
|
||||
};
|
||||
|
||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||
active: ctx.req
|
||||
});
|
||||
|
||||
options = {transaction: tx};
|
||||
tx = await models.Sale.beginTransaction({});
|
||||
options.transaction = tx;
|
||||
});
|
||||
|
||||
afterEach(async() => {
|
||||
await tx.rollback();
|
||||
});
|
||||
|
||||
it('should create new tickets with cloned sales with warehouse', async() => {
|
||||
const salesIds = [1, 2, 3];
|
||||
const servicesIds = [];
|
||||
const withWarehouse = true;
|
||||
const negative = false;
|
||||
const newTickets = await models.Sale.clone(ctx, salesIds, servicesIds, withWarehouse, negative, options);
|
||||
|
||||
expect(newTickets).toBeDefined();
|
||||
expect(newTickets.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should handle negative quantities correctly', async() => {
|
||||
const negative = true;
|
||||
const salesIds = [7, 8];
|
||||
const servicesIds = [];
|
||||
const newTickets = await models.Sale.clone(ctx, salesIds, servicesIds, false, negative, options);
|
||||
|
||||
for (const ticket of newTickets) {
|
||||
const sales = await models.Sale.find({where: {ticketFk: ticket.id}}, options);
|
||||
sales.forEach(sale => {
|
||||
expect(sale.quantity).toBeLessThan(0);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should create new components and services for cloned tickets', async() => {
|
||||
const servicesIds = [2];
|
||||
const salesIds = [5];
|
||||
const newTickets = await models.Sale.clone(ctx, salesIds, servicesIds, false, false, options);
|
||||
|
||||
for (const ticket of newTickets) {
|
||||
const sale = await models.Sale.findOne({where: {ticketFk: ticket.id}}, options);
|
||||
const components = await models.SaleComponent.find({where: {saleFk: sale.id}}, options);
|
||||
const services = await models.TicketService.find({where: {ticketFk: ticket.id}}, options);
|
||||
|
||||
expect(components.length).toBeGreaterThan(0);
|
||||
expect(services.length).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
});
|
|
@ -44,24 +44,7 @@ describe('Sale refund()', () => {
|
|||
|
||||
const tickets = await models.Sale.refund(ctx, salesIds, servicesIds, withWarehouse, options);
|
||||
|
||||
const refundedTicket = await models.Ticket.findOne({
|
||||
where: {
|
||||
id: tickets[0].id
|
||||
},
|
||||
include: [
|
||||
{
|
||||
relation: 'ticketSales',
|
||||
scope: {
|
||||
include: {
|
||||
relation: 'components'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'ticketServices',
|
||||
}
|
||||
]
|
||||
}, options);
|
||||
const refundedTicket = await getTicketRefund(tickets[0].id, options);
|
||||
const ticketsAfter = await models.Ticket.find({}, options);
|
||||
const salesLength = refundedTicket.ticketSales().length;
|
||||
const componentsLength = refundedTicket.ticketSales()[0].components().length;
|
||||
|
@ -77,4 +60,42 @@ describe('Sale refund()', () => {
|
|||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should create a ticket without sales', async() => {
|
||||
const servicesIds = [4];
|
||||
const tx = await models.Sale.beginTransaction({});
|
||||
const options = {transaction: tx};
|
||||
try {
|
||||
const tickets = await models.Sale.refund(ctx, null, servicesIds, withWarehouse, options);
|
||||
const refundedTicket = await getTicketRefund(tickets[0].id, options);
|
||||
|
||||
expect(refundedTicket).toBeDefined();
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
async function getTicketRefund(id, options) {
|
||||
return models.Ticket.findOne({
|
||||
where: {
|
||||
id
|
||||
},
|
||||
include: [
|
||||
{
|
||||
relation: 'ticketSales',
|
||||
scope: {
|
||||
include: {
|
||||
relation: 'components'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'ticketServices',
|
||||
}
|
||||
]
|
||||
}, options);
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ module.exports = Self => {
|
|||
for (const id of ticketIds) {
|
||||
const promise = await models.Ticket.state(ctx, {
|
||||
stateFk: state.id,
|
||||
workerFk: worker.id,
|
||||
userFk: worker.id,
|
||||
ticketFk: id
|
||||
}, myOptions);
|
||||
promises.push(promise);
|
||||
|
|
|
@ -74,8 +74,8 @@ module.exports = Self => {
|
|||
},
|
||||
{
|
||||
arg: 'option',
|
||||
type: 'number',
|
||||
description: 'Action id'
|
||||
type: 'string',
|
||||
description: 'Action code'
|
||||
},
|
||||
{
|
||||
arg: 'isWithoutNegatives',
|
||||
|
|
|
@ -84,7 +84,7 @@ module.exports = Self => {
|
|||
arg: 'filter',
|
||||
type: 'object',
|
||||
description: `Filter defining where, order, offset, and limit - must be a JSON-encoded string`
|
||||
}
|
||||
},
|
||||
],
|
||||
returns: {
|
||||
type: ['object'],
|
||||
|
|
|
@ -130,7 +130,17 @@ module.exports = Self => {
|
|||
await models.TicketDms.create({ticketFk: ticketId, dmsFk: dms[0].id}, myOptions);
|
||||
const ticket = await models.Ticket.findById(ticketId, null, myOptions);
|
||||
await ticket.updateAttribute('isSigned', true, myOptions);
|
||||
await Self.rawSql(`CALL vn.ticket_setState(?, ?)`, [ticketId, 'DELIVERED'], myOptions);
|
||||
|
||||
const deliveryState = await models.State.find({
|
||||
where: {
|
||||
code: 'DELIVERED'
|
||||
}
|
||||
}, options);
|
||||
|
||||
await models.Ticket.state(ctx, {
|
||||
ticketFk: ticketId,
|
||||
stateFk: deliveryState.id
|
||||
}, myOptions);
|
||||
}
|
||||
|
||||
if (tx) await tx.commit();
|
||||
|
|
|
@ -60,7 +60,7 @@ describe('ticket componentUpdate()', () => {
|
|||
shipped: today,
|
||||
landed: tomorrow,
|
||||
isDeleted: false,
|
||||
option: 1,
|
||||
option: 'renewPrices',
|
||||
isWithoutNegatives: false
|
||||
};
|
||||
|
||||
|
@ -110,7 +110,7 @@ describe('ticket componentUpdate()', () => {
|
|||
shipped: today,
|
||||
landed: tomorrow,
|
||||
isDeleted: false,
|
||||
option: 1,
|
||||
option: 'renewPrices',
|
||||
isWithoutNegatives: false
|
||||
};
|
||||
|
||||
|
@ -176,7 +176,7 @@ describe('ticket componentUpdate()', () => {
|
|||
shipped: newDate,
|
||||
landed: tomorrow,
|
||||
isDeleted: false,
|
||||
option: 1,
|
||||
option: 'renewPrices',
|
||||
isWithoutNegatives: true
|
||||
};
|
||||
|
||||
|
@ -235,7 +235,7 @@ describe('ticket componentUpdate()', () => {
|
|||
shipped: newDate,
|
||||
landed: tomorrow,
|
||||
isDeleted: false,
|
||||
option: 1,
|
||||
option: 'renewPrices',
|
||||
isWithoutNegatives: false,
|
||||
keepPrice: true
|
||||
};
|
||||
|
@ -288,7 +288,7 @@ describe('ticket componentUpdate()', () => {
|
|||
shipped: newDate,
|
||||
landed: tomorrow,
|
||||
isDeleted: false,
|
||||
option: 1,
|
||||
option: 'renewPrices',
|
||||
isWithoutNegatives: false,
|
||||
keepPrice: false
|
||||
};
|
||||
|
|
|
@ -3,7 +3,6 @@ const LoopBackContext = require('loopback-context');
|
|||
|
||||
describe('ticket setDeleted()', () => {
|
||||
const userId = 1106;
|
||||
const employeeUser = 1110;
|
||||
const activeCtx = {
|
||||
accessToken: {userId: userId},
|
||||
};
|
||||
|
@ -118,7 +117,7 @@ describe('ticket setDeleted()', () => {
|
|||
return value;
|
||||
};
|
||||
|
||||
const ticketId = 12;
|
||||
const ticketId = 7;
|
||||
await models.Ticket.setDeleted(ctx, ticketId, options);
|
||||
|
||||
await tx.rollback();
|
||||
|
|
|
@ -45,9 +45,8 @@ describe('ticket state()', () => {
|
|||
const options = {transaction: tx};
|
||||
|
||||
activeCtx.accessToken.userId = salesPersonId;
|
||||
const params = {ticketFk: 2, stateFk: 3};
|
||||
|
||||
await models.Ticket.state(ctx, params, options);
|
||||
await models.Ticket.state(ctx, {ticketFk: 2, stateFk: 3}, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
|
@ -67,9 +66,8 @@ describe('ticket state()', () => {
|
|||
const options = {transaction: tx};
|
||||
|
||||
activeCtx.accessToken.userId = employeeId;
|
||||
const params = {ticketFk: 11, stateFk: 13};
|
||||
|
||||
await models.Ticket.state(ctx, params, options);
|
||||
await models.Ticket.state(ctx, {ticketFk: 11, stateFk: 13}, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
|
|
|
@ -7,7 +7,6 @@ module.exports = Self => {
|
|||
accepts: [
|
||||
{
|
||||
arg: 'data',
|
||||
description: 'Model instance data',
|
||||
type: 'Object',
|
||||
required: true,
|
||||
http: {source: 'body'}
|
||||
|
@ -37,25 +36,21 @@ module.exports = Self => {
|
|||
}
|
||||
|
||||
try {
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
|
||||
if (!params.stateFk && !params.code)
|
||||
throw new UserError('State cannot be blank');
|
||||
|
||||
if (params.code) {
|
||||
const state = await models.State.findOne({
|
||||
where: {code: params.code},
|
||||
fields: ['id']
|
||||
}, myOptions);
|
||||
|
||||
params.stateFk = state.id;
|
||||
if (params.stateFk) {
|
||||
const {code} = await models.State.findById(params.stateFk, {fields: ['code']}, myOptions);
|
||||
params.code = code;
|
||||
} else {
|
||||
const {id} = await models.State.findOne({where: {code: params.code}}, myOptions);
|
||||
params.stateFk = id;
|
||||
}
|
||||
|
||||
if (!params.userFk) {
|
||||
const worker = await models.Worker.findOne({
|
||||
where: {id: userId}
|
||||
where: {id: ctx.req.accessToken.userId}
|
||||
}, myOptions);
|
||||
|
||||
params.userFk = worker.id;
|
||||
}
|
||||
|
||||
|
@ -63,17 +58,21 @@ module.exports = Self => {
|
|||
fields: ['stateFk']
|
||||
}, myOptions);
|
||||
|
||||
let oldStateAllowed;
|
||||
if (ticketState)
|
||||
oldStateAllowed = await models.State.isEditable(ctx, ticketState.stateFk, myOptions);
|
||||
const oldStateAllowed = ticketState && await models.State.isEditable(ctx, ticketState.stateFk, myOptions);
|
||||
const newStateAllowed = await models.State.isEditable(ctx, params.stateFk, myOptions);
|
||||
|
||||
const isAllowed = (!ticketState || oldStateAllowed == true) && newStateAllowed == true;
|
||||
|
||||
if (!isAllowed)
|
||||
if ((ticketState && !oldStateAllowed) || !newStateAllowed)
|
||||
throw new UserError(`You don't have enough privileges`, 'ACCESS_DENIED');
|
||||
|
||||
const ticketTracking = await models.TicketTracking.create(params, myOptions);
|
||||
await Self.rawSql(`CALL vn.ticket_setState(?, ?)`, [params.ticketFk, params.code], myOptions);
|
||||
|
||||
const ticketTracking = await models.TicketTracking.findOne({
|
||||
where: {ticketFk: params.ticketFk},
|
||||
order: 'id DESC',
|
||||
limit: 1
|
||||
}, myOptions);
|
||||
|
||||
await ticketTracking.updateAttribute('userFk', params.userFk, myOptions);
|
||||
|
||||
if (tx) await tx.commit();
|
||||
|
||||
|
|
|
@ -132,7 +132,7 @@ describe('sale model ', () => {
|
|||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(9));
|
||||
|
||||
const tx = await models.Sale.beginTransaction({});
|
||||
const saleId = 13;
|
||||
const saleId = 32;
|
||||
const newQuantity = -10;
|
||||
|
||||
try {
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
"user": {
|
||||
"type": "belongsTo",
|
||||
"model": "VnUser",
|
||||
"foreignKey": "workerFk"
|
||||
"foreignKey": "userFk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
<vn-radio
|
||||
ng-model="$ctrl.ticket.option"
|
||||
label="{{::action.description}}"
|
||||
val={{::action.id}}>
|
||||
val={{::action.code}}>
|
||||
</vn-radio>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -25,12 +25,7 @@ class Controller extends Component {
|
|||
|
||||
loadDefaultTicketAction() {
|
||||
const isSalesAssistant = this.aclService.hasAny(['salesAssistant']);
|
||||
const defaultOption = isSalesAssistant ? 'turnInMana' : 'changePrice';
|
||||
const filter = {where: {code: defaultOption}};
|
||||
|
||||
this.$http.get(`TicketUpdateActions`, {filter}).then(response => {
|
||||
return this.ticket.option = response.data[0].id;
|
||||
});
|
||||
this.ticket.option = isSalesAssistant ? 'mana' : 'buyerDiscount';
|
||||
}
|
||||
|
||||
onStepChange() {
|
||||
|
@ -112,7 +107,7 @@ class Controller extends Component {
|
|||
shipped: this.ticket.shipped,
|
||||
landed: this.ticket.landed,
|
||||
isDeleted: this.ticket.isDeleted,
|
||||
option: parseInt(this.ticket.option),
|
||||
option: this.ticket.option,
|
||||
isWithoutNegatives: this.ticket.withoutNegatives,
|
||||
withWarningAccept: this.ticket.withWarningAccept,
|
||||
keepPrice: false
|
||||
|
|
|
@ -61,6 +61,9 @@
|
|||
<th field="liters">
|
||||
<span translate>Liters</span>
|
||||
</th>
|
||||
<th field="totalWithVat">
|
||||
<span translate>Import</span>
|
||||
</th>
|
||||
<th shrink field="lines">
|
||||
<span translate>Available Lines</span>
|
||||
</th>
|
||||
|
@ -76,6 +79,7 @@
|
|||
<th shrink field="futureState">
|
||||
<span translate>State</span>
|
||||
</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -148,6 +152,13 @@
|
|||
</span>
|
||||
</td>
|
||||
<td>{{::ticket.liters}}</td>
|
||||
<td>
|
||||
<span
|
||||
class="chip {{$ctrl.totalPriceColor(ticket.totalWithVat)}}"
|
||||
title="{{$ctrl.totalPriceTitle(ticket.totalWithVat) | translate}}">
|
||||
{{::(ticket.totalWithVat ? ticket.totalWithVat : 0) | currency: 'EUR': 2}}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{::ticket.lines}}</td>
|
||||
<td separator>
|
||||
<span
|
||||
|
|
|
@ -21,6 +21,9 @@ export default class Controller extends Section {
|
|||
{
|
||||
field: 'futureShipped',
|
||||
searchable: false
|
||||
}, {
|
||||
field: 'totalWithVat',
|
||||
searchable: false
|
||||
},
|
||||
{
|
||||
field: 'state',
|
||||
|
@ -130,6 +133,17 @@ export default class Controller extends Section {
|
|||
this.vnApp.showSuccess(this.$t('Success'));
|
||||
});
|
||||
}
|
||||
totalPriceColor(totalWithVat) {
|
||||
return this.isLessThan50(totalWithVat) ? 'warning' : '';
|
||||
}
|
||||
|
||||
totalPriceTitle(totalWithVat) {
|
||||
return this.isLessThan50(totalWithVat) ? 'Less than 50€' : '';
|
||||
}
|
||||
|
||||
isLessThan50(totalWithVat) {
|
||||
return (parseInt(totalWithVat) > 0 && parseInt(totalWithVat) < 50);
|
||||
}
|
||||
|
||||
exprBuilder(param, value) {
|
||||
switch (param) {
|
||||
|
@ -145,6 +159,8 @@ export default class Controller extends Section {
|
|||
return {'ipt': {like: `%${value}%`}};
|
||||
case 'futureIpt':
|
||||
return {'futureIpt': {like: `%${value}%`}};
|
||||
case 'totalWithVat':
|
||||
return {'totalWithVat': value};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</vn-autocomplete>
|
||||
<vn-worker-autocomplete
|
||||
vn-one
|
||||
ng-model="$ctrl.workerFk">
|
||||
ng-model="$ctrl.userFk">
|
||||
</vn-worker-autocomplete>
|
||||
</vn-horizontal>
|
||||
</vn-card>
|
||||
|
|
|
@ -17,19 +17,19 @@ class Controller extends Section {
|
|||
set stateFk(value) {
|
||||
this.params.stateFk = value;
|
||||
this.isPickerDesignedState = this.getIsPickerDesignedState(value);
|
||||
this.workerFk = window.localStorage.currentUserWorkerId;
|
||||
this.userFk = window.localStorage.currentUserWorkerId;
|
||||
}
|
||||
|
||||
get stateFk() {
|
||||
return this.params.stateFk;
|
||||
}
|
||||
|
||||
set workerFk(value) {
|
||||
this.params.workerFk = value;
|
||||
set userFk(value) {
|
||||
this.params.userFk = value;
|
||||
}
|
||||
|
||||
get workerFk() {
|
||||
return this.params.workerFk;
|
||||
get userFk() {
|
||||
return this.params.userFk;
|
||||
}
|
||||
|
||||
getPickerDesignedState() {
|
||||
|
|
|
@ -29,11 +29,11 @@ describe('Ticket', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('workerFk setter', () => {
|
||||
it('should set params.workerFk', () => {
|
||||
controller.workerFk = 1;
|
||||
describe('userFk setter', () => {
|
||||
it('should set params.userFk', () => {
|
||||
controller.userFk = 1;
|
||||
|
||||
expect(controller.params.workerFk).toEqual(1);
|
||||
expect(controller.params.userFk).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
<vn-tr ng-repeat="tracking in trackings">
|
||||
<vn-td>{{::tracking.state.name}}</vn-td>
|
||||
<vn-td expand>
|
||||
<span
|
||||
ng-class="{'link': tracking.user.id}"
|
||||
ng-click="workerDescriptor.show($event, tracking.user.id)">
|
||||
<span
|
||||
ng-class="{'link': tracking.user.worker}"
|
||||
ng-click="tracking.user.worker && workerDescriptor.show($event, tracking.user.worker.id)">
|
||||
{{::tracking.user.name || 'System' | translate}}
|
||||
</span>
|
||||
</vn-td>
|
||||
|
|
|
@ -9,7 +9,13 @@ class Controller extends Section {
|
|||
{
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['name']
|
||||
fields: ['id', 'name'],
|
||||
include: {
|
||||
relation: 'worker',
|
||||
scope: {
|
||||
fields: ['id']
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
relation: 'state',
|
||||
|
|
|
@ -3,7 +3,7 @@ const UserError = require('vn-loopback/util/user-error');
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('deleteTimeEntry', {
|
||||
description: 'Deletes a manual time entry for a worker if the user role is above the worker',
|
||||
accessType: 'READ',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
|
|
|
@ -5,7 +5,7 @@ module.exports = Self => {
|
|||
accepts: [
|
||||
{
|
||||
arg: 'workerFk',
|
||||
type: 'int',
|
||||
type: 'number',
|
||||
required: true,
|
||||
},
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ const UserError = require('vn-loopback/util/user-error');
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('updateTimeEntry', {
|
||||
description: 'Updates a time entry for a worker if the user role is above the worker',
|
||||
accessType: 'READ',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
module.exports = Self => {
|
||||
const validateTin = require('vn-loopback/util/validateTin');
|
||||
require('../methods/worker/filter')(Self);
|
||||
require('../methods/worker/mySubordinates')(Self);
|
||||
require('../methods/worker/isSubordinate')(Self);
|
||||
|
@ -23,4 +24,21 @@ module.exports = Self => {
|
|||
Self.validatesUniquenessOf('locker', {
|
||||
message: 'This locker has already been assigned'
|
||||
});
|
||||
|
||||
Self.validateAsync('fi', tinIsValid, {
|
||||
message: 'Invalid TIN'
|
||||
});
|
||||
|
||||
async function tinIsValid(err, done) {
|
||||
const filter = {
|
||||
fields: ['code'],
|
||||
where: {id: this.countryFk}
|
||||
};
|
||||
const country = await Self.app.models.Country.findOne(filter);
|
||||
const code = country ? country.code.toLowerCase() : null;
|
||||
|
||||
if (!this.fi || !validateTin(this.fi, code))
|
||||
err();
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -61,7 +61,7 @@ module.exports = Self => {
|
|||
|
||||
for (ticket of ticketList) {
|
||||
if (ticket.ticketState().alertLevel == 0) {
|
||||
promises.push(models.TicketTracking.create({
|
||||
promises.push(models.Ticket.state(ctx, {
|
||||
ticketFk: ticket.id,
|
||||
stateFk: fixingState.id,
|
||||
userFk: worker.id
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "salix-back",
|
||||
"version": "24.04.01",
|
||||
"version": "24.06.01",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "salix-back",
|
||||
"version": "24.04.01",
|
||||
"version": "24.06.01",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"axios": "^1.2.2",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "salix-back",
|
||||
"version": "24.04.01",
|
||||
"version": "24.06.01",
|
||||
"author": "Verdnatura Levante SL",
|
||||
"description": "Salix backend",
|
||||
"license": "GPL-3.0",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue