Merge branch 'dev' into 8244-floricode
gitea/salix/pipeline/pr-dev There was a failure building this commit Details

This commit is contained in:
Guillermo Bonet 2025-02-27 07:36:09 +01:00
commit f4842827a6
94 changed files with 2245 additions and 1472 deletions

View File

@ -13,6 +13,11 @@ module.exports = Self => {
type: 'string',
description: 'Origin model from insert'
},
{
arg: 'description',
type: 'string',
description: 'Action description'
},
],
http: {
@ -21,7 +26,7 @@ module.exports = Self => {
}
});
Self.add = async(ctx, code, model, options) => {
Self.add = async(ctx, code, model, description, options) => {
const userId = ctx.req.accessToken.userId;
const myOptions = {};
@ -29,8 +34,8 @@ module.exports = Self => {
Object.assign(myOptions, options);
return await Self.rawSql(`
INSERT INTO workerActivity (workerFk, workerActivityTypeFk, model)
SELECT ?, ?, ?
INSERT INTO workerActivity (workerFk, workerActivityTypeFk, model, description)
SELECT ?, ?, ?, ?
FROM workerTimeControlConfig wtcc
LEFT JOIN (
SELECT wa.workerFk,
@ -43,8 +48,8 @@ module.exports = Self => {
LIMIT 1
) sub ON TRUE
WHERE sub.workerFk IS NULL
OR sub.code <> ?
OR sub.code <> ?
OR TIMESTAMPDIFF(SECOND, sub.created, util.VN_NOW()) > wtcc.dayBreak;`
, [userId, code, model, userId, code], myOptions);
, [userId, code, model, description, userId, code], myOptions);
};
};

View File

@ -13,7 +13,7 @@ describe('workerActivity insert()', () => {
{'code': 'TEST', 'description': 'TEST'}, options
);
await models.WorkerActivity.add(ctx, 'TEST', 'APP', options);
await models.WorkerActivity.add(ctx, 'TEST', 'APP', 'description', options);
count = await models.WorkerActivity.count(
{'workerFK': 1106}, options

View File

@ -24,7 +24,7 @@
"relations": {
"agency": {
"type": "belongsTo",
"model": "WorkCenter",
"model": "Agency",
"foreignKey": "agencyFk"
},
"workCenter": {

View File

@ -195,7 +195,7 @@ INSERT INTO `vn`.`sectorType` (`id`, `code`)
INSERT INTO `vn`.`sector`(`id`, `description`, `warehouseFk`, `code`, `typeFk`)
VALUES
(1, 'First sector', 1, 'FIRST', 1),
(2, 'Second sector', 2, 'SECOND',1);
(2, 'Second sector', 6, 'SECOND',1);
INSERT INTO `vn`.`printer` (`id`, `name`, `path`, `isLabeler`, `sectorFk`, `ipAddress`)
VALUES
@ -730,7 +730,8 @@ INSERT INTO `vn`.`zoneWarehouse` (`id`, `zoneFk`, `warehouseFk`)
(10, 10, 3),
(11, 11, 5),
(12, 12, 4),
(13, 13, 5);
(13, 13, 5),
(14, 7, 4);
INSERT INTO `vn`.`zoneClosure` (`zoneFk`, `dated`, `hour`)
VALUES
@ -853,7 +854,8 @@ INSERT INTO `vn`.`ticketTracking`(`ticketFk`, `stateFk`, `userFk`, `created`)
INSERT INTO `vn`.`deliveryPoint` (`id`, `name`, `ubication`)
VALUES
(1, 'Gotham','1007 Mountain Drive, Gotham');
(1, 'Gotham','1007 Mountain Drive, Gotham'),
(6, 'Stark Tower','200 Park Avenue, Nueva York');
INSERT INTO `vn`.`vehicle`(`id`, `numberPlate`, `tradeMark`, `model`, `companyFk`, `warehouseFk`, `description`, `m3`, `isActive`, `deliveryPointFk`, `chassis`, `leasing`, `supplierFk`, `fuelTypeFk`, `bankPolicyFk`)
VALUES
@ -1302,9 +1304,10 @@ INSERT INTO `vn`.`train`(`id`, `name`)
INSERT INTO `vn`.`operator` (`workerFk`, `numberOfWagons`, `trainFk`, `itemPackingTypeFk`, `warehouseFk`, `sectorFk`, `labelerFk`)
VALUES
('1106', '1', '1', 'H', '1', '1', '1'),
('9', '2', '1', 'H', '1', '1', '1'),
('1107', '1', '1', 'V', '1', '1', '1');
(1106, '1', '1', 'H', '1', '1', '1'),
(9, '2', '1', 'H', '1', '1', '1'),
(1107, '1', '1', 'V', '1', '1', '1'),
(72, '1', '1', 'V', '1', '1', '1');
INSERT INTO `vn`.`collection`(`id`, `workerFk`, `stateFk`, `created`, `trainFk`)
VALUES
@ -1615,6 +1618,7 @@ INSERT INTO `bs`.`waste`(`buyerFk`, `year`, `week`, `itemFk`, `itemTypeFk`, `sal
(19, 100, 1, 50, 100, 4, 1, 1.500, 1.500, 0.000, 1, 1, 'grouping', NULL, 0.00, 99.6, 99.4, 0, 1, 0, NULL, 1, util.VN_CURDATE()),
(20, 100, 2, 5, 450, 3, 2, 1.000, 1.000, 0.000, 10, 10, NULL, NULL, 0.00, 7.30, 7.00, 0, 1, 0, NULL, 2.5, util.VN_CURDATE()),
(21, 100,72, 55, 500, 5, 3, 1.000, 1.000, 0.000, 1, 1, 'packing', NULL, 0.00, 78.3, 75.6, 0, 1, 0, 1, 3, util.VN_CURDATE()),
(22, 100, 4, 55, 0, 5, 0, 0, 0, 0.000, 1, 1, 'packing', NULL, 0.00, 78.3, 75.6, 0, 1, 0, 1, 3, util.VN_CURDATE()),
(10000002, 12,88, 50.0000, 5000, 4, 1, 1.500, 1.500, 0.000, 1, 1, 'grouping', NULL, 0.00, 99.60, 99.40, 0, 1, 0, 1.00, 1,util.VN_CURDATE() - INTERVAL 2 MONTH);
INSERT INTO `hedera`.`order`(`id`, `date_send`, `customer_id`, `delivery_method_id`, `agency_id`, `address_id`, `company_id`, `note`, `source_app`, `confirmed`,`total`, `date_make`, `first_row_stamp`, `confirm_date`)
@ -1957,11 +1961,11 @@ INSERT INTO `vn`.`claimBeginning`(`id`, `claimFk`, `saleFk`, `quantity`)
INSERT INTO `vn`.`claimDestination`(`id`, `description`, `addressFk`)
VALUES
(1, 'Bueno', NULL),
(2, 'Basura/Perd.', 12),
(1, 'Bueno', 11),
(2, 'Basura/Perd.', NULL),
(3, 'Confeccion', NULL),
(4, 'Reclam.PRAG', 12),
(5, 'Corregido', 11);
(4, 'Reclam.PRAG', NULL),
(5, 'Corregido', NULL);
INSERT INTO `vn`.`claimDevelopment`(`id`, `claimFk`, `claimResponsibleFk`, `workerFk`, `claimReasonFk`, `claimResultFk`, `claimRedeliveryFk`, `claimDestinationFk`)
VALUES
@ -1976,9 +1980,9 @@ INSERT INTO `vn`.`claimEnd`(`id`, `saleFk`, `claimFk`, `workerFk`, `claimDestina
(1, 31, 4, 21, 2),
(2, 32, 3, 21, 3);
INSERT INTO `vn`.`claimConfig`(`id`, `maxResponsibility`, `monthsToRefund`, `minShipped`,`daysToClaim`)
INSERT INTO `vn`.`claimConfig`(`id`, `maxResponsibility`, `monthsToRefund`, `minShipped`,`daysToClaim`, `pickupDeliveryFk`, `warehouseFk`)
VALUES
(1, 5, 4, '2016-10-01', 7);
(1, 5, 4, '2016-10-01', 7, 8, 4);
INSERT INTO `vn`.`claimRatio`(`clientFk`, `yearSale`, `claimAmount`, `claimingRate`, `priceIncreasing`, `packingRate`)
VALUES
@ -3063,9 +3067,10 @@ INSERT INTO `salix`.`url` (`appName`, `environment`, `url`)
('salix', 'development', 'http://localhost:5000/#!/'),
('docuware', 'development', 'http://docuware');
INSERT INTO `vn`.`report` (`id`, `name`, `paperSizeFk`, `method`)
INSERT INTO `vn`.`report` (`name`, `method`)
VALUES
(3, 'invoice', NULL, 'InvoiceOuts/{refFk}/invoice-out-pdf');
('invoice', 'InvoiceOuts/{refFk}/invoice-out-pdf'),
('LabelBuy', 'Entries/{id}/{labelType}/buy-label');
INSERT INTO `vn`.`payDemDetail` (`id`, `detail`)
VALUES
@ -4145,3 +4150,6 @@ INSERT IGNORE INTO vn.vehicleType (id, name)
INSERT INTO edi.tableMultiConfig (fileName, toTable, file, `method`, updated)
VALUES ('FG', 'genus', 'florecompc2', 'VBN/Genus', '2001-01-01');
INSERT INTO vn.addressWaste (addressFk, type)
VALUES (11, 'fault');

View File

@ -1,8 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` EVENT `stock`.`log_clean`
ON SCHEDULE EVERY 1 DAY
STARTS '2022-01-28 09:29:18.000'
ON COMPLETION PRESERVE
ENABLE
DO CALL log_clean$$
DELIMITER ;

View File

@ -1,8 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` EVENT `stock`.`log_syncNoWait`
ON SCHEDULE EVERY 5 SECOND
STARTS '2017-06-27 17:15:02.000'
ON COMPLETION NOT PRESERVE
DISABLE
DO CALL log_syncNoWait$$
DELIMITER ;

View File

@ -0,0 +1,8 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` EVENT `stock`.`stock_clean`
ON SCHEDULE EVERY 60 SECOND
STARTS '2025-01-01 00:00:00.000'
ON COMPLETION PRESERVE
ENABLE
DO CALL stock_clean$$
DELIMITER ;

View File

@ -0,0 +1,8 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` EVENT `stock`.`stock_sync`
ON SCHEDULE EVERY 5 SECOND
STARTS '2025-01-01 00:00:00.000'
ON COMPLETION PRESERVE
DISABLE
DO CALL stock_sync$$
DELIMITER ;

View File

@ -0,0 +1,91 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`buyLot_refresh`(
`vTable` ENUM('lot', 'entry', 'travel'),
`vId` INT)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
CREATE OR REPLACE TEMPORARY TABLE tBuyAlive
ENGINE = MEMORY
SELECT
t.id travelFk,
t.landed,
t.landingHour,
t.warehouseInFk,
t.isReceived,
t.isRaid,
e.id entryFk,
b.lotFk,
b.itemFk,
b.life,
b.quantity
FROM tLotStatus ls
JOIN vn.buy b ON b.lotFk = ls.lotFk
JOIN vn.entry e ON e.id = b.entryFk
JOIN vn.travel t ON t.id = e.travelFk
WHERE ls.isIncluded;
START TRANSACTION;
-- Delete excluded/deleted/dead lots
DELETE l FROM buyLot l
JOIN tLotStatus ls USING(lotFk)
WHERE NOT ls.isIncluded;
-- Delete undead lot picks
UPDATE buyOut o
JOIN buyPick p ON p.outFk = o.outFk
JOIN tLotStatus ls ON ls.lotFk = p.lotFk
SET o.isSync = FALSE,
o.lack = o.lack + p.quantity
WHERE ls.isExcluded OR ls.isIncluded;
DELETE p FROM buyPick p
JOIN tLotStatus ls USING(lotFk)
WHERE ls.isExcluded OR ls.isIncluded;
-- Update alive outs
INSERT INTO buyLot (
lotFk,
isSync,
isPicked,
warehouseFk,
itemFk,
dated,
expired,
quantity,
available
)
SELECT
lotFk,
FALSE,
isReceived,
warehouseInFk,
itemFk,
@dated := ADDTIME(landed, IFNULL(landingHour, '00:00:00')),
@dated + INTERVAL life DAY,
quantity,
NULL
FROM tBuyAlive
ON DUPLICATE KEY UPDATE
isSync = VALUES(isSync),
isPicked = VALUES(isPicked),
warehouseFk = VALUES(warehouseFk),
itemFk = VALUES(itemFk),
dated = VALUES(dated),
expired = VALUES(expired),
quantity = VALUES(quantity),
available = VALUES(available);
COMMIT;
DROP TEMPORARY TABLE tBuyAlive;
END$$
DELIMITER ;

View File

@ -0,0 +1,64 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`buyLot_requestQuantity`(
vSelf INT,
vRequested INT,
vDated DATETIME,
vOutFk INT,
OUT vSupplied INT)
BEGIN
/**
* Disassociates lot picks after the given date until the demanded quantity is
* satisfied.
*
* @param vSelf The buyLot reference
* @param vRequested The requested quantity
* @param vDate The starting date for the associated outs
* @param vOutFk The if of requesting out
* @param vSupplied The supplied quantity
*/
DECLARE vPickFk INT;
DECLARE vPickOutFk INT;
DECLARE vPickQuantity INT;
DECLARE vPickGranted INT;
DECLARE vDone BOOL;
DECLARE vPicks CURSOR FOR
SELECT p.id, o.outFk, p.quantity
FROM buyPick p
JOIN buyOut o USING(outFk)
WHERE p.lotFk = vSelf
AND (o.dated, o.outFk) > (vDated, vOutFk)
ORDER BY o.dated DESC, o.created DESC, o.outFk DESC;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET vDone = TRUE;
SET vSupplied = 0;
OPEN vPicks;
myLoop: LOOP
SET vDone = FALSE;
FETCH vPicks INTO vPickFk, vPickOutFk, vPickQuantity;
IF vDone THEN
LEAVE myLoop;
END IF;
SET vPickGranted = LEAST(vRequested - vSupplied, vPickQuantity);
SET vSupplied = vSupplied + vPickGranted;
CALL buyPick_remove(vPickFk, vPickGranted, vPickQuantity);
UPDATE buyOut
SET isSync = FALSE,
lack = lack + vPickGranted
WHERE outFk = vPickOutFk;
IF vSupplied >= vRequested THEN
LEAVE myLoop;
END IF;
END LOOP;
CLOSE vPicks;
END$$
DELIMITER ;

View File

@ -1,10 +1,10 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`inbound_sync`(vSelf INT)
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`buyLot_sync`(vSelf INT)
BEGIN
/**
* Associates a inbound with their possible outbounds, updating it's available.
* Associates a lot with their possible outs, updating it's available.
*
* @param vSelf The inbound identifier
* @param vSelf The lot id
*/
DECLARE vDated DATETIME;
DECLARE vExpired DATETIME;
@ -14,37 +14,37 @@ BEGIN
DECLARE vAvailable INT;
DECLARE vSupplied INT;
DECLARE vSuppliedFromRequest INT;
DECLARE vOutboundFk INT;
DECLARE vOutFk INT;
DECLARE vLack INT;
DECLARE vHasPicks BOOL;
DECLARE vDone BOOL;
DECLARE vOutbounds CURSOR FOR
SELECT id, lack, lack < quantity
FROM outbound
DECLARE vOuts CURSOR FOR
SELECT outFk, lack, lack < quantity
FROM buyOut
WHERE warehouseFk = vWarehouse
AND itemFk = vItem
AND dated >= vDated
AND (vExpired IS NULL OR dated < vExpired)
ORDER BY dated, created;
ORDER BY dated, created, outFk;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET vDone = TRUE;
SELECT warehouseFk, itemFk, available, quantity, expired, dated
INTO vWarehouse, vItem, vAvailable, vQuantity, vExpired, vDated
FROM inbound
WHERE id = vSelf;
FROM buyLot
WHERE lotFk = vSelf;
IF vAvailable IS NULL THEN
SET vAvailable = vQuantity;
END IF;
OPEN vOutbounds;
OPEN vOuts;
myLoop: LOOP
SET vDone = FALSE;
FETCH vOutbounds INTO vOutboundFk, vLack, vHasPicks;
FETCH vOuts INTO vOutFk, vLack, vHasPicks;
IF vDone THEN
LEAVE myLoop;
@ -54,19 +54,19 @@ BEGIN
IF vSupplied > 0 THEN
SET vAvailable = vAvailable - vSupplied;
UPDATE outbound
UPDATE buyOut
SET lack = lack - vSupplied
WHERE id = vOutboundFk;
WHERE outFk = vOutFk;
END IF;
IF vHasPicks AND vAvailable > 0 THEN
CALL outbound_requestQuantity(vOutboundFk, vAvailable, vDated, vSuppliedFromRequest);
CALL buyOut_requestQuantity(vOutFk, vAvailable, vDated, vSelf, vSuppliedFromRequest);
SET vSupplied = vSupplied + vSuppliedFromRequest;
SET vAvailable = vAvailable - vSuppliedFromRequest;
END IF;
IF vSupplied > 0 THEN
CALL inbound_addPick(vSelf, vOutboundFk, vSupplied);
CALL buyPick_add(vSelf, vOutFk, vSupplied);
END IF;
IF vAvailable <= 0 THEN
@ -74,11 +74,11 @@ BEGIN
END IF;
END LOOP;
CLOSE vOutbounds;
CLOSE vOuts;
UPDATE inbound
UPDATE buyLot
SET isSync = TRUE,
available = vAvailable
WHERE id = vSelf;
WHERE lotFk = vSelf;
END$$
DELIMITER ;

View File

@ -0,0 +1,95 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`buyOut_refresh`(
`vTable` VARCHAR(255),
`vId` INT,
`vSource` VARCHAR(255))
BEGIN
/**
* This procedure contains the common code used to refresh the out lot cache.
*
* @param vTable The id source table
* @param vId The lot id
* @param vSource The lot source
* @table tLotStatus Lots to modify
* @table tLotAlive Updated/Created alive lots data
*/
DECLARE vHasLots BOOL;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
IF vTable = 'lot' THEN
SELECT COUNT(*) > 0 INTO vHasLots FROM tLotStatus;
IF NOT vHasLots THEN
INSERT INTO tLotStatus
SET lotFk = vId,
isExcluded = TRUE,
isIncluded = FALSE;
END IF;
END IF;
START TRANSACTION;
-- Delete excluded/deleted/dead outs
DELETE o FROM buyOut o
JOIN tLotStatus ls ON ls.lotFk = o.outFk
WHERE NOT ls.isIncluded;
-- Delete undead out picks
UPDATE buyLot l
JOIN buyPick p ON p.lotFk = l.lotFk
JOIN tLotStatus ls ON ls.lotFk = p.outFk
SET l.isSync = FALSE,
l.available = l.available + p.quantity
WHERE ls.isExcluded OR ls.isIncluded;
DELETE p FROM buyPick p
JOIN tLotStatus ls ON ls.lotFk = p.outFk
WHERE ls.isExcluded OR ls.isIncluded;
-- Update alive outs
INSERT INTO buyOut (
outFk,
source,
isSync,
warehouseFk,
dated,
itemFk,
quantity,
lack,
created,
isPicked
)
SELECT
lotFk,
vSource,
FALSE,
warehouseFk,
dated,
itemFk,
quantity,
quantity,
created,
isPicked
FROM tLotAlive
ON DUPLICATE KEY UPDATE
source = VALUES(source),
warehouseFk = VALUES(warehouseFk),
dated = VALUES(dated),
itemFk = VALUES(itemFk),
quantity = VALUES(quantity),
lack = VALUES(lack),
created = VALUES(created),
isPicked = VALUES(isPicked),
isSync = VALUES(isSync);
COMMIT;
END$$
DELIMITER ;

View File

@ -0,0 +1,39 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`buyOut_refreshBuy`(
`vTable` VARCHAR(255),
`vId` INT)
BEGIN
CREATE OR REPLACE TEMPORARY TABLE tLotStatus
ENGINE = MEMORY
SELECT lotFk,
@isExcluded := b.quantity <= 0 isExcluded,
NOT @isExcluded AND b.isAlive isIncluded
FROM vn.buy b
JOIN vn.entry e ON e.id = b.entryFk
WHERE
(vTable = 'lot' AND b.lotFk = vId)
OR (vTable = 'entry' AND e.id = vId)
OR (vTable = 'travel' AND e.travelFk = vId);
CREATE OR REPLACE TEMPORARY TABLE tLotAlive
ENGINE = MEMORY
SELECT
ls.lotFk,
t.warehouseOutFk warehouseFk,
ADDTIME(t.shipped, IFNULL(shipmentHour, '00:00:00')) dated,
t.isDelivered isPicked,
b.itemFk,
b.quantity,
b.created
FROM tLotStatus ls
JOIN vn.buy b ON b.lotFk = ls.lotFk
JOIN vn.entry e ON e.id = b.entryFk
JOIN vn.travel t ON t.id = e.travelFk
WHERE ls.isIncluded;
CALL buyOut_refresh(vTable, vId, 'buy');
CALL buyLot_refresh(vTable, vId);
DROP TEMPORARY TABLE tLotStatus, tLotAlive;
END$$
DELIMITER ;

View File

@ -0,0 +1,36 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`buyOut_refreshOrder`(
`vTable` VARCHAR(255),
`vId` INT)
BEGIN
CREATE OR REPLACE TEMPORARY TABLE tLotStatus
ENGINE = MEMORY
SELECT lotFk,
@isExcluded := o.confirmed OR NOT isReserved OR r.amount <= 0 isExcluded,
NOT @isExcluded isIncluded
FROM hedera.orderRow r
JOIN hedera.`order` o ON o.id = r.orderFk
WHERE
(vTable = 'lot' AND r.lotFk = vId)
OR (vTable = 'order' AND o.id = vId);
CREATE OR REPLACE TEMPORARY TABLE tLotAlive
ENGINE = MEMORY
SELECT
ls.lotFk,
r.warehouseFk,
r.shipment dated,
r.itemFk,
r.amount quantity,
r.created,
FALSE isPicked
FROM tLotStatus ls
JOIN hedera.orderRow r ON r.lotFk = ls.lotFk
JOIN hedera.`order` o ON o.id = r.orderFk
WHERE ls.isIncluded;
CALL buyOut_refresh(vTable, vId, 'orderRow');
DROP TEMPORARY TABLE tLotStatus, tLotAlive;
END$$
DELIMITER ;

View File

@ -0,0 +1,42 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`buyOut_refreshSale`(
`vTable` VARCHAR(255),
`vId` INT)
BEGIN
DECLARE vAliveDate DATE;
SELECT util.VN_CURDATE() - INTERVAL saleLife MONTH
INTO vAliveDate
FROM config LIMIT 1;
CREATE OR REPLACE TEMPORARY TABLE tLotStatus
ENGINE = MEMORY
SELECT lotFk,
@isExcluded := s.quantity < 0 isExcluded,
NOT @isExcluded AND t.isAlive isIncluded
FROM vn.sale s
JOIN vn.ticket t ON t.id = s.ticketFk
WHERE
(vTable = 'lot' AND s.lotFk = vId)
OR (vTable = 'ticket' AND t.id = vId);
CREATE OR REPLACE TEMPORARY TABLE tLotAlive
ENGINE = MEMORY
SELECT
ls.lotFk,
t.warehouseFk,
t.shipped dated,
s.itemFk,
s.quantity,
s.created,
s.isPicked
FROM tLotStatus ls
JOIN vn.sale s ON s.lotFk = ls.lotFk
JOIN vn.ticket t ON t.id = s.ticketFk
WHERE ls.isIncluded;
CALL buyOut_refresh(vTable, vId, 'sale');
DROP TEMPORARY TABLE tLotStatus, tLotAlive;
END$$
DELIMITER ;

View File

@ -0,0 +1,64 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`buyOut_requestQuantity`(
vSelf INT,
vRequested INT,
vDated DATETIME,
vLotFk INT,
OUT vSupplied INT)
BEGIN
/**
* Disassociates out picks after the given date until the demanded quantity is
* satisfied.
*
* @param vSelf The buyOut reference
* @param vRequested The requested quantity
* @param vDate The starting date for the associated lots
* @param vLotFk The if of requesting lot
* @param vSupplied The supplied quantity
*/
DECLARE vPickFk INT;
DECLARE vPickLotFk INT;
DECLARE vPickQuantity INT;
DECLARE vPickGranted INT;
DECLARE vDone BOOL;
DECLARE vPicks CURSOR FOR
SELECT p.id, p.lotFk, p.quantity
FROM buyPick p
JOIN buyLot l USING(lotFk)
WHERE p.outFk = vSelf
AND (l.dated, p.lotFk) > (vDated, vLotFk)
ORDER BY l.dated, p.lotFk;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET vDone = TRUE;
SET vSupplied = 0;
OPEN vPicks;
myLoop: LOOP
SET vDone = FALSE;
FETCH vPicks INTO vPickFk, vPickLotFk, vPickQuantity;
IF vDone THEN
LEAVE myLoop;
END IF;
SET vPickGranted = LEAST(vRequested - vSupplied, vPickQuantity);
SET vSupplied = vSupplied + vPickGranted;
CALL buyPick_remove(vPickFk, vPickGranted, vPickQuantity);
UPDATE buyLot
SET isSync = FALSE,
available = available + vPickGranted
WHERE lotFk = vPickLotFk;
IF vSupplied >= vRequested THEN
LEAVE myLoop;
END IF;
END LOOP;
CLOSE vPicks;
END$$
DELIMITER ;

View File

@ -1,10 +1,10 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`outbound_sync`(vSelf INT)
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`buyOut_sync`(vSelf INT)
BEGIN
/**
* Attaches a outbound with available inbounds.
* Attaches an out with available lots.
*
* @param vSelf The outbound reference
* @param vSelf The buyOut reference
*/
DECLARE vDated DATETIME;
DECLARE vItem INT;
@ -12,33 +12,33 @@ BEGIN
DECLARE vLack INT;
DECLARE vSupplied INT;
DECLARE vSuppliedFromRequest INT;
DECLARE vInboundFk INT;
DECLARE vLotFk INT;
DECLARE vAvailable INT;
DECLARE vHasPicks BOOL;
DECLARE vDone BOOL;
DECLARE vInbounds CURSOR FOR
SELECT id, available, available < quantity
FROM inbound
DECLARE vBuyLots CURSOR FOR
SELECT lotFk, available, available < quantity
FROM buyLot
WHERE warehouseFk = vWarehouse
AND itemFk = vItem
AND dated <= vDated
AND (expired IS NULL OR expired > vDated)
ORDER BY dated;
ORDER BY dated, lotFk;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET vDone = TRUE;
SELECT warehouseFk, itemFk, dated, lack
INTO vWarehouse, vItem, vDated, vLack
FROM outbound
WHERE id = vSelf;
FROM buyOut
WHERE outFk = vSelf;
OPEN vInbounds;
OPEN vBuyLots;
myLoop: LOOP
SET vDone = FALSE;
FETCH vInbounds INTO vInboundFk, vAvailable, vHasPicks;
FETCH vBuyLots INTO vLotFk, vAvailable, vHasPicks;
IF vDone THEN
LEAVE myLoop;
@ -48,19 +48,19 @@ BEGIN
IF vSupplied > 0 THEN
SET vLack = vLack - vSupplied;
UPDATE inbound
UPDATE buyLot
SET available = available - vSupplied
WHERE id = vInboundFk;
WHERE lotFk = vLotFk;
END IF;
IF vHasPicks AND vLack > 0 THEN
CALL inbound_requestQuantity(vInboundFk, vLack, vDated, vSuppliedFromRequest);
CALL buyLot_requestQuantity(vLotFk, vLack, vDated, vSelf, vSuppliedFromRequest);
SET vSupplied = vSupplied + vSuppliedFromRequest;
SET vLack = vLack - vSuppliedFromRequest;
END IF;
IF vSupplied > 0 THEN
CALL inbound_addPick(vInboundFk, vSelf, vSupplied);
CALL buyPick_add(vLotFk, vSelf, vSupplied);
END IF;
IF vLack = 0 THEN
@ -68,11 +68,11 @@ BEGIN
END IF;
END LOOP;
CLOSE vInbounds;
CLOSE vBuyLots;
UPDATE outbound
UPDATE buyOut
SET isSync = TRUE,
lack = vLack
WHERE id = vSelf;
WHERE outFk = vSelf;
END$$
DELIMITER ;

View File

@ -0,0 +1,15 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`buyPick_add`(
vLotFk INT,
vOutFk INT,
vQuantity INT
)
BEGIN
INSERT INTO buyPick
SET lotFk = vLotFk,
outFk = vOutFk,
quantity = vQuantity
ON DUPLICATE KEY UPDATE
quantity = quantity + vQuantity;
END$$
DELIMITER ;

View File

@ -0,0 +1,17 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`buyPick_remove`(
vSelf INT,
vQuantity INT,
vTotalQuantity INT
)
BEGIN
IF vQuantity < vTotalQuantity THEN
UPDATE buyPick
SET quantity = quantity - vQuantity
WHERE id = vSelf;
ELSE
DELETE FROM buyPick
WHERE id = vSelf;
END IF;
END$$
DELIMITER ;

View File

@ -1,16 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`inbound_addPick`(
vSelf INT,
vOutboundFk INT,
vQuantity INT
)
BEGIN
INSERT INTO inboundPick
SET
inboundFk = vSelf,
outboundFk = vOutboundFk,
quantity = vQuantity
ON DUPLICATE KEY UPDATE
quantity = quantity + vQuantity;
END$$
DELIMITER ;

View File

@ -1,20 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`inbound_removePick`(
vSelf INT,
vOutboundFk INT,
vQuantity INT,
vTotalQuantity INT
)
BEGIN
IF vQuantity < vTotalQuantity THEN
UPDATE inboundPick
SET quantity = quantity - vQuantity
WHERE inboundFk = vSelf
AND outboundFk = vOutboundFk;
ELSE
DELETE FROM inboundPick
WHERE inboundFk = vSelf
AND outboundFk = vOutboundFk;
END IF;
END$$
DELIMITER ;

View File

@ -1,61 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`inbound_requestQuantity`(
vSelf INT,
vRequested INT,
vDated DATETIME,
OUT vSupplied INT)
BEGIN
/**
* Disassociates inbound picks after the given date until the
* demanded quantity is satisfied.
*
* @param vSelf The inbound reference
* @param vRequested The requested quantity
* @param vDate The starting date for the associated outbounds
* @param vSupplied The supplied quantity
*/
DECLARE vOutboundFk INT;
DECLARE vPickQuantity INT;
DECLARE vPickGranted INT;
DECLARE vDone BOOL;
DECLARE vPicks CURSOR FOR
SELECT p.outboundFk, p.quantity
FROM inboundPick p
JOIN outbound o ON o.id = p.outboundFk
WHERE p.inboundFk = vSelf
AND o.dated > vDated
ORDER BY o.dated DESC, o.created DESC;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET vDone = TRUE;
SET vSupplied = 0;
OPEN vPicks;
myLoop: LOOP
SET vDone = FALSE;
FETCH vPicks INTO vOutboundFk, vPickQuantity;
IF vDone THEN
LEAVE myLoop;
END IF;
SET vPickGranted = LEAST(vRequested - vSupplied, vPickQuantity);
SET vSupplied = vSupplied + vPickGranted;
CALL inbound_removePick(vSelf, vOutboundFk, vPickGranted, vPickQuantity);
UPDATE outbound
SET isSync = FALSE,
lack = lack + vPickGranted
WHERE id = vOutboundFk;
IF vSupplied >= vRequested THEN
LEAVE myLoop;
END IF;
END LOOP;
CLOSE vPicks;
END$$
DELIMITER ;

View File

@ -1,7 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`log_clean`()
BEGIN
DELETE FROM inbound WHERE dated = vn.getInventoryDate();
DELETE FROM outbound WHERE dated = vn.getInventoryDate();
END$$
DELIMITER ;

View File

@ -1,19 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`log_delete`(vTableName VARCHAR(255), vTableId INT)
proc: BEGIN
/**
* Processes orphan transactions.
*/
IF vTableName NOT IN ('buy', 'sale', 'orderRow') THEN
LEAVE proc;
END IF;
DELETE FROM inbound
WHERE tableName = vTableName COLLATE utf8_general_ci
AND tableId = vTableId;
DELETE FROM outbound
WHERE tableName = vTableName COLLATE utf8_general_ci
AND tableId = vTableId;
END$$
DELIMITER ;

View File

@ -1,33 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`log_refreshAll`()
BEGIN
/**
* Recalculates the entire cache. It takes a considerable time,
* please avoid calls to this procedure from commonly used operations.
*/
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
DO RELEASE_LOCK('stock.log_sync');
RESIGNAL;
END;
IF !GET_LOCK('stock.log_sync', 30) THEN
CALL util.throw('Lock timeout exceeded');
END IF;
TRUNCATE TABLE stock.`log`;
TRUNCATE TABLE stock.`inbound`;
TRUNCATE TABLE stock.`inboundPick`;
TRUNCATE TABLE stock.`outbound`;
TRUNCATE TABLE stock.`visible`;
CALL log_refreshSale(NULL, NULL);
CALL log_refreshBuy(NULL, NULL);
CALL log_refreshOrder(NULL, NULL);
UPDATE outbound SET isSync = TRUE;
CALL log_sync(TRUE);
DO RELEASE_LOCK('stock.log_sync');
END$$
DELIMITER ;

View File

@ -1,73 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`log_refreshBuy`(
`vTableName` VARCHAR(255),
`vTableId` INT)
BEGIN
DROP TEMPORARY TABLE IF EXISTS tValues;
CREATE TEMPORARY TABLE tValues
ENGINE = MEMORY
SELECT b.id buyFk,
e.id entryFk,
t.id travelFk,
b.itemFk,
t.isRaid,
ADDTIME(t.shipped,
IFNULL(t.shipmentHour, '00:00:00')) shipped,
t.warehouseOutFk,
t.isDelivered,
ADDTIME(t.landed,
IFNULL(t.landingHour, '00:00:00')) landed,
t.warehouseInFk,
t.isReceived,
tp.life,
ABS(b.quantity) quantity,
b.created,
b.quantity > 0 isIn,
t.shipped < vn.getInventoryDate() lessThanInventory
FROM vn.buy b
JOIN vn.entry e ON e.id = b.entryFk
JOIN vn.travel t ON t.id = e.travelFk
JOIN vn.item i ON i.id = b.itemFk
JOIN vn.itemType tp ON tp.id = i.typeFk
WHERE (
vTableId IS NULL
OR (vTableName = 'travel' AND t.id = vTableId)
OR (vTableName = 'entry' AND e.id = vTableId)
OR (vTableName = 'buy' AND b.id = vTableId)
)
AND t.landed >= vn.getInventoryDate()
AND b.quantity != 0;
REPLACE INTO inbound (
tableName, tableId, warehouseFk, dated,
itemFk, expired, quantity, isPicked
)
SELECT 'buy',
buyFk,
IF(isIn, warehouseInFk, warehouseOutFk),
@dated := IF(isIn, landed, shipped),
itemFk,
TIMESTAMPADD(DAY, life, @dated),
quantity,
IF(isIn, isReceived, isDelivered) AND NOT isRaid
FROM tValues
WHERE isIn OR !lessThanInventory;
REPLACE INTO outbound (
tableName, tableId, warehouseFk, dated,
itemFk, created, quantity, isPicked
)
SELECT 'buy',
buyFk,
IF(isIn, warehouseOutFk, warehouseInFk),
IF(isIn, shipped, landed),
itemFk,
created,
quantity,
IF(isIn, isDelivered, isReceived) AND NOT isRaid
FROM tValues
WHERE !isIn OR !lessThanInventory;
DROP TEMPORARY TABLE tValues;
END$$
DELIMITER ;

View File

@ -1,47 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`log_refreshOrder`(
`vTableName` VARCHAR(255),
`vTableId` INT)
BEGIN
DECLARE vExpireTime INT DEFAULT 20;
DECLARE vExpired DATETIME DEFAULT TIMESTAMPADD(MINUTE, -vExpireTime, util.VN_NOW());
DROP TEMPORARY TABLE IF EXISTS tValues;
CREATE TEMPORARY TABLE tValues
ENGINE = MEMORY
SELECT
r.id rowFk,
r.itemFk,
r.warehouseFk,
r.shipment shipped,
r.amount quantity,
r.created
FROM hedera.orderRow r
JOIN hedera.`order` o ON o.id = r.orderFk
WHERE (
vTableId IS NULL
OR (vTableName = 'order' AND o.id = vTableId)
OR (vTableName = 'orderRow' AND r.id = vTableId)
)
AND !o.confirmed
AND r.shipment >= vn.getInventoryDate()
AND r.created >= vExpired
AND r.amount != 0;
REPLACE INTO outbound (
tableName, tableId, warehouseFk, dated,
itemFk, created, expired, quantity
)
SELECT 'orderRow',
rowFk,
warehouseFk,
shipped,
itemFk,
created,
TIMESTAMPADD(MINUTE, vExpireTime, created),
quantity
FROM tValues;
DROP TEMPORARY TABLE tValues;
END$$
DELIMITER ;

View File

@ -1,66 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`log_refreshSale`(
`vTableName` VARCHAR(255),
`vTableId` INT)
BEGIN
DROP TEMPORARY TABLE IF EXISTS tValues;
CREATE TEMPORARY TABLE tValues
ENGINE = MEMORY
SELECT
m.id saleFk,
m.ticketFk,
m.itemFk,
t.warehouseFk,
t.shipped,
ABS(m.quantity) quantity,
m.created,
TIMESTAMPADD(DAY, tp.life, t.shipped) expired,
m.quantity < 0 isIn,
m.isPicked OR s.alertLevel > al.id isPicked
FROM vn.sale m
JOIN vn.ticket t ON t.id = m.ticketFk
JOIN vn.ticketState s ON s.ticketFk = t.id
JOIN vn.item i ON i.id = m.itemFk
JOIN vn.itemType tp ON tp.id = i.typeFk
JOIN vn.alertLevel al ON al.code = 'ON_PREPARATION'
WHERE (
vTableId IS NULL
OR (vTableName = 'ticket' AND t.id = vTableId)
OR (vTableName = 'sale' AND m.id = vTableId)
)
AND t.shipped >= vn.getInventoryDate()
AND m.quantity != 0;
REPLACE INTO inbound (
tableName, tableId, warehouseFk, dated,
itemFk, expired, quantity, isPicked
)
SELECT 'sale',
saleFk,
warehouseFk,
shipped,
itemFk,
expired,
quantity,
isPicked
FROM tValues
WHERE isIn;
REPLACE INTO outbound (
tableName, tableId, warehouseFk, dated,
itemFk, created, quantity, isPicked
)
SELECT 'sale',
saleFk,
warehouseFk,
shipped,
itemFk,
created,
quantity,
isPicked
FROM tValues
WHERE !isIn;
DROP TEMPORARY TABLE tValues;
END$$
DELIMITER ;

View File

@ -1,123 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`log_sync`(vSync BOOL)
proc: BEGIN
DECLARE vDone BOOL;
DECLARE vLogId INT;
DECLARE vHasPendingSync BOOL;
DECLARE vOperation VARCHAR(255);
DECLARE vTableName VARCHAR(255);
DECLARE vTableId VARCHAR(255);
DECLARE vInboundFk INT;
DECLARE vOutboundFk INT;
DECLARE cInbound CURSOR FOR
SELECT id FROM inbound
WHERE !isSync
ORDER BY dated;
DECLARE cOutbound CURSOR FOR
SELECT id FROM outbound
WHERE !isSync
ORDER BY dated;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET vDone = TRUE;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
-- Applies changes
opsLoop: LOOP
START TRANSACTION;
SET vDone = FALSE;
SELECT id, operation, tableName, tableId
INTO vLogId, vOperation, vTableName, vTableId
FROM `log`
ORDER BY id LIMIT 1
FOR UPDATE;
IF vDone THEN
COMMIT;
LEAVE opsLoop;
END IF;
CALL log_delete(vTableName, vTableId);
IF vOperation = 'insert' THEN
IF vTableName IN ('travel', 'entry', 'buy') THEN
CALL log_refreshBuy(vTableName, vTableId);
ELSEIF vTableName IN ('ticket', 'sale') THEN
CALL log_refreshSale(vTableName, vTableId);
ELSEIF vTableName IN ('order', 'orderRow') THEN
CALL log_refreshOrder(vTableName, vTableId);
END IF;
END IF;
DELETE FROM `log` WHERE id = vLogId;
SET vSync = TRUE;
COMMIT;
END LOOP;
IF !vSync THEN
LEAVE proc;
END IF;
-- Deletes expired outbounds
DELETE FROM outbound WHERE expired <= util.VN_NOW();
-- Attaches desync inbounds
REPEAT
OPEN cInbound;
SET vHasPendingSync = FALSE;
inboundLoop: LOOP
SET vDone = FALSE;
FETCH cInbound INTO vInboundFk;
IF vDone THEN
LEAVE inboundLoop;
END IF;
START TRANSACTION;
CALL inbound_sync(vInboundFk);
COMMIT;
SET vHasPendingSync = TRUE;
END LOOP;
CLOSE cInbound;
UNTIL !vHasPendingSync END REPEAT;
-- Attaches desync outbounds
REPEAT
OPEN cOutbound;
SET vHasPendingSync = FALSE;
outboundLoop: LOOP
SET vDone = FALSE;
FETCH cOutbound INTO vOutboundFk;
IF vDone THEN
LEAVE outboundLoop;
END IF;
START TRANSACTION;
CALL outbound_sync(vOutboundFk);
COMMIT;
SET vHasPendingSync = TRUE;
END LOOP;
CLOSE cOutbound;
UNTIL !vHasPendingSync END REPEAT;
END$$
DELIMITER ;

View File

@ -1,16 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`log_syncNoWait`()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
DO RELEASE_LOCK('stock.log_sync');
RESIGNAL;
END;
IF GET_LOCK('stock.log_sync', 0) THEN
CALL log_sync(FALSE);
END IF;
DO RELEASE_LOCK('stock.log_sync');
END$$
DELIMITER ;

View File

@ -1,61 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`outbound_requestQuantity`(
vSelf INT,
vRequested INT,
vDated DATETIME,
OUT vSupplied INT)
BEGIN
/**
* Disassociates outbound picks after the given date until the
* demanded quantity is satisfied.
*
* @param vSelf The outbound reference
* @param vRequested The requested quantity
* @param vDate The starting date for the associated inbounds
* @param vSupplied The supplied quantity
*/
DECLARE vInboundFk INT;
DECLARE vPickQuantity INT;
DECLARE vPickGranted INT;
DECLARE vDone BOOL;
DECLARE vPicks CURSOR FOR
SELECT p.inboundFk, p.quantity
FROM inboundPick p
JOIN inbound i ON i.id = p.inboundFk
WHERE p.outboundFk = vSelf
AND i.dated > vDated
ORDER BY i.dated DESC;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET vDone = TRUE;
SET vSupplied = 0;
OPEN vPicks;
myLoop: LOOP
SET vDone = FALSE;
FETCH vPicks INTO vInboundFk, vPickQuantity;
IF vDone THEN
LEAVE myLoop;
END IF;
SET vPickGranted = LEAST(vRequested - vSupplied, vPickQuantity);
SET vSupplied = vSupplied + vPickGranted;
CALL inbound_removePick(vInboundFk, vSelf, vPickGranted, vPickQuantity);
UPDATE inbound
SET isSync = FALSE,
available = available + vPickGranted
WHERE id = vInboundFk;
IF vSupplied >= vRequested THEN
LEAVE myLoop;
END IF;
END LOOP;
CLOSE vPicks;
END$$
DELIMITER ;

View File

@ -0,0 +1,32 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`stock_clean`()
BEGIN
/**
* Cleans current time dependent cache records.
*/
DECLARE vExpired DATETIME;
DECLARE vAliveDate DATE;
-- Expired order reserves
SELECT SUBTIME(util.VN_NOW(), reserveTime)
INTO vExpired
FROM hedera.orderConfig LIMIT 1;
UPDATE hedera.order
SET isReserved = FALSE
WHERE created < vExpired
AND isReserved;
-- Frozen old sales
SELECT util.VN_CURDATE() - INTERVAL saleLife DAY
INTO vAliveDate
FROM config LIMIT 1;
UPDATE vn.ticket
SET isAlive = FALSE
WHERE shipped < vAliveDate
AND isAlive;
END$$
DELIMITER ;

View File

@ -0,0 +1,87 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`stock_refreshAll`()
BEGIN
/**
* Recalculates the entire cache. It takes a considerable time,
* please avoid calls to this procedure from commonly used operations.
*/
DECLARE vDone BOOL;
DECLARE vId INT;
DECLARE vBuys CURSOR FOR
SELECT lotFk FROM vn.buy WHERE isAlive;
DECLARE vTickets CURSOR FOR
SELECT id FROM vn.ticket WHERE isAlive;
DECLARE vOrders CURSOR FOR
SELECT lotFk FROM hedera.orderRow WHERE isReserved;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET vDone = TRUE;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
DO RELEASE_LOCK('stock.stock_refreshAll');
RESIGNAL;
END;
IF NOT GET_LOCK('stock.stock_refreshAll', 30) THEN
CALL util.throw('Lock timeout exceeded');
END IF;
-- Prune cache
DELETE p FROM buyPick p JOIN buyLot l USING(lotFk);
TRUNCATE TABLE buyLot;
TRUNCATE TABLE buyOut;
-- Populate cache
OPEN vBuys;
buyLoop: LOOP
SET vDone = FALSE;
FETCH vBuys INTO vId;
IF vDone THEN
LEAVE buyLoop;
END IF;
CALL buyOut_refreshBuy('lot', vId);
END LOOP;
CLOSE vBuys;
OPEN vOrders;
orderLoop: LOOP
SET vDone = FALSE;
FETCH vOrders INTO vId;
IF vDone THEN
LEAVE orderLoop;
END IF;
CALL buyOut_refreshOrder('lot', vId);
END LOOP;
CLOSE vOrders;
OPEN vTickets;
saleLoop: LOOP
SET vDone = FALSE;
FETCH vTickets INTO vId;
IF vDone THEN
LEAVE saleLoop;
END IF;
CALL buyOut_refreshSale('ticket', vId);
END LOOP;
CLOSE vTickets;
-- Synchronize
UPDATE buyOut SET isSync = TRUE;
CALL stock_sync;
DO RELEASE_LOCK('stock.stock_refreshAll');
END$$
DELIMITER ;

View File

@ -0,0 +1,34 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`stock_sync`()
BEGIN
/**
* Synchronizes all out of sync items. It can be called in parallel
* since it generates a lock for each item (and warehouse) synchronization
* process, see stock_syncItem().
*/
DECLARE vDone BOOL;
DECLARE vWarehouseFk INT;
DECLARE vItemFk INT;
DECLARE vItems CURSOR FOR
SELECT itemFk, warehouseFk FROM buyLot WHERE NOT isSync
UNION
SELECT itemFk, warehouseFk FROM buyOut WHERE NOT isSync;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET vDone = TRUE;
OPEN vItems;
itemLoop: LOOP
SET vDone = FALSE;
FETCH vItems INTO vItemFk, vWarehouseFk;
IF vDone THEN
LEAVE itemLoop;
END IF;
CALL stock_syncItem(vItemFk, vWarehouseFk, 0);
END LOOP;
CLOSE vItems;
END$$
DELIMITER ;

View File

@ -0,0 +1,88 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`stock_syncItem`(
vItemFk INT,
vWarehouseFk INT,
vWait INT)
myProc: BEGIN
/**
* Synchronizes out of sync item. It generates a lock for each item and
* warehouse synchronization process.
*
* @param vItemFk The item id
* @param vWarehouseFk The item warehouse id
* @param vWait Maximum waiting time, see GET_LOCK()
*/
DECLARE vDone BOOL;
DECLARE vHasPendingSync BOOL;
DECLARE vLotFk INT;
DECLARE vOutFk INT;
DECLARE vLock VARCHAR(255);
DECLARE vLots CURSOR FOR
SELECT lotFk FROM buyLot
WHERE NOT isSync
AND (itemFk, warehouseFk) = (vItemFk, vWarehouseFk)
ORDER BY dated, lotFk;
DECLARE vOuts CURSOR FOR
SELECT outFk FROM buyOut
WHERE NOT isSync
AND (itemFk, warehouseFk) = (vItemFk, vWarehouseFk)
ORDER BY dated, created, outFk;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET vDone = TRUE;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
DO RELEASE_LOCK(vLock);
ROLLBACK;
RESIGNAL;
END;
SET vLock = CONCAT_WS('/', 'stock.stock_syncItem', vWarehouseFk, vItemFk);
IF NOT GET_LOCK(vLock, vWait) THEN
LEAVE myProc;
END IF;
REPEAT
SET vHasPendingSync = FALSE;
OPEN vLots;
lotLoop: LOOP
SET vDone = FALSE;
FETCH vLots INTO vLotFk;
IF vDone THEN
LEAVE lotLoop;
END IF;
START TRANSACTION;
CALL buyLot_sync(vLotFk);
COMMIT;
SET vHasPendingSync = TRUE;
END LOOP;
CLOSE vLots;
OPEN vOuts;
outLoop: LOOP
SET vDone = FALSE;
FETCH vOuts INTO vOutFk;
IF vDone THEN
LEAVE outLoop;
END IF;
START TRANSACTION;
CALL buyOut_sync(vOutFk);
COMMIT;
SET vHasPendingSync = TRUE;
END LOOP;
CLOSE vOuts;
UNTIL NOT vHasPendingSync END REPEAT;
END$$
DELIMITER ;

View File

@ -1,20 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `stock`.`visible_log`(
vIsPicked BOOL,
vWarehouseFk INT,
vItemFk INT,
vQuantity INT
)
proc: BEGIN
IF !vIsPicked THEN
LEAVE proc;
END IF;
INSERT INTO visible
SET itemFk = vItemFk,
warehouseFk = vWarehouseFk,
quantity = vQuantity
ON DUPLICATE KEY UPDATE
quantity = quantity + VALUES(quantity);
END$$
DELIMITER ;

View File

@ -1,22 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `stock`.`inbound_afterDelete`
AFTER DELETE ON `inbound`
FOR EACH ROW
BEGIN
UPDATE outbound o
JOIN inboundPick ou ON ou.outboundFk = o.id
SET o.lack = o.lack + ou.quantity,
o.isSync = FALSE
WHERE ou.inboundFk = OLD.id;
DELETE FROM inboundPick
WHERE inboundFk = OLD.id;
CALL visible_log(
OLD.isPicked,
OLD.warehouseFk,
OLD.itemFk,
-OLD.quantity
);
END$$
DELIMITER ;

View File

@ -1,15 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `stock`.`inbound_beforeInsert`
BEFORE INSERT ON `inbound`
FOR EACH ROW
BEGIN
SET NEW.isPicked = NEW.isPicked OR NEW.dated < util.VN_CURDATE();
CALL visible_log(
NEW.isPicked,
NEW.warehouseFk,
NEW.itemFk,
NEW.quantity
);
END$$
DELIMITER ;

View File

@ -1,22 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `stock`.`outbound_afterDelete`
AFTER DELETE ON `outbound`
FOR EACH ROW
BEGIN
UPDATE inbound i
JOIN inboundPick ou ON ou.inboundFk = i.id
SET i.available = i.available + ou.quantity,
i.isSync = FALSE
WHERE ou.outboundFk = OLD.id;
DELETE FROM inboundPick
WHERE outboundFk = OLD.id;
CALL visible_log(
OLD.isPicked,
OLD.warehouseFk,
OLD.itemFk,
OLD.quantity
);
END$$
DELIMITER ;

View File

@ -1,16 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `stock`.`outbound_beforeInsert`
BEFORE INSERT ON `outbound`
FOR EACH ROW
BEGIN
SET NEW.lack = NEW.quantity;
SET NEW.isPicked = NEW.isPicked OR NEW.dated < util.VN_CURDATE();
CALL visible_log(
NEW.isPicked,
NEW.warehouseFk,
NEW.itemFk,
-NEW.quantity
);
END$$
DELIMITER ;

View File

@ -0,0 +1,15 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` FUNCTION `vn`.`item_getLife`(vItemFk INT)
RETURNS INT
NOT DETERMINISTIC
BEGIN
DECLARE vLife INT;
SELECT t.life INTO vLife
FROM itemType t
JOIN item i ON i.typeFk = t.id
WHERE i.id = vItemFk;
RETURN vLife;
END$$
DELIMITER ;

View File

@ -104,6 +104,24 @@ BEGIN
LEFT JOIN agencyModeItemType ait
ON ait.agencyModeFk = vAgencyModeFk
AND ait.itemTypeFk = itt.id
LEFT JOIN (
SELECT i.id
FROM item i
JOIN priceDelta pd
ON pd.itemTypeFk = i.typeFk
AND (pd.minSize IS NULL OR pd.minSize <= i.`size`)
AND (pd.maxSize IS NULL OR pd.maxSize >= i.`size`)
AND (pd.inkFk IS NULL OR pd.inkFk = i.inkFk)
AND (pd.originFk IS NULL OR pd.originFk = i.originFk)
AND (pd.producerFk IS NULL OR pd.producerFk = i.producerFk)
AND (pd.warehouseFk IS NULL OR pd.warehouseFk = vWarehouseFk)
LEFT JOIN zoneGeo zg ON zg.id = pd.zoneGeoFk
LEFT JOIN zoneGeo zg2 ON zg2.id = address_getGeo(vAddressFk)
WHERE (pd.fromDated IS NULL OR pd.fromDated <= vShipped)
AND (pd.toDated IS NULL OR pd.toDated >= vShipped)
AND (pd.zoneGeoFk IS NULL OR zg2.lft BETWEEN zg.lft AND zg.rgt)
AND pd.isHidden
GROUP BY i.id) pd ON pd.id = i.itemFk
WHERE a.calc_id = vAvailableCalc
AND a.available > 0
AND (ag.isAnyVolumeAllowed OR NOT itt.isUnconventionalSize)
@ -113,7 +131,9 @@ BEGIN
it.size <= z.itemMaxSize OR z.itemMaxSize IS NULL))
AND cit.id IS NULL
AND zit.id IS NULL
AND ait.id IS NULL;
AND ait.id IS NULL
AND pd.id IS NULL
;
DROP TEMPORARY TABLE tmp.buyUltimate;

View File

@ -1,83 +1,86 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`report_print`(
vReportName VARCHAR(100),
vPrinterFk INT,
vUserFk INT,
vParams JSON,
vPriorityName VARCHAR(100)
)
vReportName VARCHAR(100),
vPrinterFk INT,
vUserFk INT,
vParams JSON,
vPriorityName VARCHAR(100)
)
BEGIN
/**
* Inserts in the print queue the report to be printed and the necessary parameters for this
* one taking into account the paper size of both the printer and the report.
*
* @param vReportName the report to be printed.
* @param vPrinterFk the printer selected.
* @param vUserFk user id.
* @param vParams JSON with report parameters.
* @param vPriorityName the printing priority.
*/
DECLARE vI INT DEFAULT 0;
DECLARE vKeys TEXT DEFAULT JSON_KEYS(vParams);
DECLARE vLength INT DEFAULT JSON_LENGTH(vKeys);
DECLARE vKey VARCHAR(255);
DECLARE vVal VARCHAR(255);
DECLARE vPrintQueueFk INT;
DECLARE vReportSize VARCHAR(255);
DECLARE vIsThePrinterReal INT;
DECLARE vPrinteSize VARCHAR(255);
DECLARE vPriorityFk INT;
DECLARE vReportFk INT;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
/**
* Inserts in the print queue the report to be printed and the necessary parameters for this
* one taking into account the paper size of both the printer and the report.
*
* @param vReportName the report to be printed.
* @param vPrinterFk the printer selected.
* @param vUserFk user id.
* @param vParams JSON with report parameters.
* @param vPriorityName the printing priority.
*/
DECLARE vI INT DEFAULT 0;
DECLARE vKeys TEXT DEFAULT JSON_KEYS(vParams);
DECLARE vLength INT DEFAULT JSON_LENGTH(vKeys);
DECLARE vKey VARCHAR(255);
DECLARE vVal VARCHAR(255);
DECLARE vPrintQueueFk INT;
DECLARE vReportSize VARCHAR(255);
DECLARE vIsThePrinterReal INT;
DECLARE vPrinterSize VARCHAR(255);
DECLARE vPriorityFk INT;
DECLARE vReportFk INT;
DECLARE vTx BOOLEAN DEFAULT NOT @@in_transaction;
SELECT id, paperSizeFk INTO vReportFk, vReportSize
FROM report
WHERE name = vReportName;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
CALL util.tx_rollback(vTx);
RESIGNAL;
END;
SELECT id, paperSizeFk INTO vIsThePrinterReal, vPrinteSize
FROM printer
WHERE id = vPrinterFk;
SELECT id INTO vPriorityFk
FROM queuePriority
WHERE code = vPriorityName;
SELECT id, paperSizeFk INTO vReportFk, vReportSize
FROM report
WHERE name = vReportName;
IF vIsThePrinterReal IS NULL THEN
CALL util.throw('printerNotExists');
END IF;
SELECT id, paperSizeFk INTO vIsThePrinterReal, vPrinterSize
FROM printer
WHERE id = vPrinterFk;
IF vReportFk IS NULL THEN
CALL util.throw('reportNotExists');
END IF;
SELECT id INTO vPriorityFk
FROM queuePriority
WHERE code = vPriorityName;
IF vReportSize <> vPrinteSize THEN
CALL util.throw('incorrectSize');
END IF;
IF vIsThePrinterReal IS NULL THEN
CALL util.throw('printerNotExists');
END IF;
START TRANSACTION;
INSERT INTO printQueue
SET printerFk = vPrinterFk,
priorityFk = vPriorityFk,
reportFk = vReportFk,
workerFk = vUserFk;
SET vPrintQueueFk = LAST_INSERT_ID();
IF vReportFk IS NULL THEN
CALL util.throw('reportNotExists');
END IF;
WHILE vI < vLength DO
SET vKey = JSON_VALUE(vKeys, CONCAT('$[', vI ,']'));
SET vVal = JSON_VALUE(vParams, CONCAT('$.', vKey));
IF vReportSize <> vPrinterSize THEN
CALL util.throw('incorrectSize');
END IF;
CALL util.tx_start(vTx);
INSERT INTO printQueue
SET printerFk = vPrinterFk,
priorityFk = vPriorityFk,
reportFk = vReportFk,
workerFk = vUserFk;
INSERT INTO printQueueArgs
SET printQueueFk = vPrintQueueFk,
name = vKey,
value = vVal;
SET vPrintQueueFk = LAST_INSERT_ID();
SET vI = vI + 1;
END WHILE;
COMMIT;
WHILE vI < vLength DO
SET vKey = JSON_VALUE(vKeys, CONCAT('$[', vI ,']'));
SET vVal = JSON_VALUE(vParams, CONCAT('$.', vKey));
INSERT INTO printQueueArgs
SET printQueueFk = vPrintQueueFk,
name = vKey,
value = vVal;
SET vI = vI + 1;
END WHILE;
CALL util.tx_commit(vTx);
END$$
DELIMITER ;

View File

@ -22,6 +22,10 @@ trig: BEGIN
SET NEW.editorFk = account.myUser_getId();
IF NEW.life IS NULL THEN
SET NEW.life = item_getLife(NEW.itemFk);
END IF;
SELECT it.workerFk INTO vBuyerFk
FROM item i
JOIN itemType it ON it.id = i.typeFk
@ -57,7 +61,7 @@ trig: BEGIN
IF NEW.groupingMode IS NULL THEN
SET NEW.groupingMode = vGroupingMode;
END IF;
-- Generics
SELECT i.genericFk INTO vGenericFk
FROM item i
@ -78,7 +82,7 @@ trig: BEGIN
END IF;
IF NEW.quantity < 0 THEN
SET NEW.isIgnored = TRUE;
SET NEW.isIgnored = TRUE;
END IF;
IF NEW.weight AND NEW.packing

View File

@ -25,6 +25,10 @@ trig:BEGIN
SET NEW.editorFk = account.myUser_getId();
IF NOT (NEW.itemFk <=> OLD.itemFk) AND NEW.life <=> OLD.life THEN
SET NEW.life = item_getLife(NEW.itemFk);
END IF;
SELECT defaultEntry INTO vDefaultEntry
FROM entryConfig;

View File

@ -0,0 +1 @@
DROP TABLE stock.log;

View File

@ -0,0 +1,16 @@
DROP TABLE IF EXISTS stock.inboundPick;
CREATE TABLE stock.buyPick (
id INT UNSIGNED auto_increment NOT NULL,
lotFk INT(11) NOT NULL
COMMENT 'Buy id',
outFk INT(11) NOT NULL
COMMENT 'Out id',
quantity INT UNSIGNED NOT NULL
COMMENT 'Picked quantity',
PRIMARY KEY (id),
CONSTRAINT buyPick_unique UNIQUE KEY (lotFk, outFk)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb3
COLLATE=utf8mb3_unicode_ci;

View File

@ -0,0 +1,6 @@
ALTER TABLE vn.buy
ADD life INT UNSIGNED NULL
COMMENT 'Lot life expressed in days',
ADD isAlive BOOL NOT NULL DEFAULT TRUE
COMMENT 'Whether it is alive',
ADD INDEX(isAlive);

View File

@ -0,0 +1,4 @@
ALTER TABLE hedera.orderRow
ADD isReserved BOOL NOT NULL DEFAULT TRUE
COMMENT 'Whether has an available reservation',
ADD INDEX(isReserved);

View File

@ -0,0 +1,4 @@
ALTER TABLE vn.ticket
ADD isAlive BOOL NOT NULL DEFAULT TRUE
COMMENT 'Whether it is alive',
ADD INDEX(isAlive);

View File

@ -0,0 +1,10 @@
RENAME TABLE IF EXISTS stock.inbound TO stock.buyLot;
ALTER TABLE stock.buyLot
DROP KEY source,
DROP COLUMN tableName,
CHANGE tableId lotFk int(10) unsigned NOT NULL,
CHANGE isSync isSync tinyint(4) NOT NULL AFTER lotFk,
DROP PRIMARY KEY,
DROP COLUMN id,
ADD PRIMARY KEY (lotFk);

View File

@ -0,0 +1 @@
DROP TABLE stock.visible;

View File

@ -0,0 +1,10 @@
RENAME TABLE IF EXISTS stock.outbound TO stock.buyOut;
ALTER TABLE stock.buyOut
CHANGE IF EXISTS tableName source enum('buy','sale','orderRow') CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
CHANGE IF EXISTS id outFk int(10) UNSIGNED NOT NULL,
DROP INDEX IF EXISTS source,
DROP COLUMN IF EXISTS tableId,
DROP INDEX IF EXISTS expired,
DROP COLUMN IF EXISTS expired,
ADD INDEX IF NOT EXISTS (source);

View File

@ -0,0 +1,8 @@
CREATE TABLE stock.config (
id INT UNSIGNED auto_increment NOT NULL,
saleLife INT UNSIGNED NOT NULL COMMENT 'Maximum sales cache lifetime in days',
CONSTRAINT config_pk PRIMARY KEY (id)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb3
COLLATE=utf8mb3_general_ci;

View File

@ -0,0 +1,2 @@
INSERT INTO stock.config (id, saleLife)
VALUES (1, 90);

View File

@ -0,0 +1 @@
CREATE SEQUENCE IF NOT EXISTS vn.buyLot;

View File

@ -0,0 +1,2 @@
ALTER TABLE vn.sale
ADD COLUMN IF NOT EXISTS lotFk INT UNSIGNED NOT NULL DEFAULT nextval(vn.buyLot) AFTER id;

View File

@ -0,0 +1,2 @@
ALTER TABLE hedera.orderRow
ADD COLUMN IF NOT EXISTS lotFk INT UNSIGNED NOT NULL DEFAULT nextval(vn.buyLot) AFTER id;

View File

@ -0,0 +1,2 @@
ALTER TABLE vn.buy
ADD COLUMN IF NOT EXISTS lotFk INT UNSIGNED NOT NULL DEFAULT nextval(vn.buyLot) AFTER id;

View File

@ -0,0 +1,2 @@
ALTER TABLE vn.sale
ADD UNIQUE IF NOT EXISTS (lotFk);

View File

@ -0,0 +1,2 @@
ALTER TABLE hedera.orderRow
ADD UNIQUE IF NOT EXISTS (lotFk);

View File

@ -0,0 +1,2 @@
ALTER TABLE vn.buy
ADD UNIQUE IF NOT EXISTS (lotFk);

View File

@ -0,0 +1,14 @@
ALTER TABLE `vn`.`claimConfig`
ADD COLUMN `pickupDeliveryFk` INT(11)
COMMENT 'Agencia utilizada para las recogidas mediante reparto',
ADD COLUMN `warehouseFk` smallint(6) unsigned
COMMENT 'Almacén usado para los tickets de reclamaciones',
ADD CONSTRAINT `fk_claimConfig_pickupdeliveryFk`
FOREIGN KEY (`pickupdeliveryFk`)
REFERENCES `agencyMode` (`id`),
ADD CONSTRAINT `fk_claimConfig_warehouseFk`
FOREIGN KEY (`warehouseFk`)
REFERENCES `warehouse` (`id`);

View File

@ -0,0 +1,2 @@
INSERT IGNORE INTO vn.state (name,`order`,alertLevel,code,isPreviousPreparable,isPicked)
VALUES ('Recogido',3,4,'PICKED_UP',0,1)

View File

@ -0,0 +1,5 @@
ALTER TABLE `vn`.`claimEnd`
ADD COLUMN `shelvingFk` INT(11) DEFAULT NULL AFTER `editorFk`,
ADD INDEX `claimEnd_fk_shelving` (`shelvingFk`),
ADD CONSTRAINT `claimEnd_fk_shelving` FOREIGN KEY (`shelvingFk`)
REFERENCES `shelving` (`id`) ON DELETE SET NULL ON UPDATE CASCADE;

View File

@ -0,0 +1,8 @@
UPDATE vn.claimDestination cd
SET cd.addressFk = NULL
WHERE code IN ('garbage/loss','manufacturing','supplierClaim','corrected');
UPDATE vn.claimDestination cd
JOIN vn.addressWaste aw ON aw.`type` = 'fault'
SET cd.addressFk = aw.addressFk
WHERE code IN ('good');

View File

@ -0,0 +1,8 @@
UPDATE vn.claimEnd ce
JOIN(
SELECT id
FROM vn.claimEnd
WHERE claimDestinationFk NOT IN
(SELECT id FROM vn.claimDestination WHERE id IS NOT NULL)
) s ON ce.id = s.id
SET ce.claimDestinationFk = 1;

View File

@ -0,0 +1,9 @@
ALTER TABLE vn.claimEnd
MODIFY COLUMN claimDestinationFk tinyint(3) unsigned NOT NULL DEFAULT 1;
ALTER TABLE vn.claimEnd
ADD CONSTRAINT fk_claimEnd_claimDestination
FOREIGN KEY (claimDestinationFk)
REFERENCES claimDestination(id)
ON UPDATE CASCADE
ON DELETE RESTRICT;

View File

@ -0,0 +1,3 @@
-- Place your SQL code here
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('Worker','getWorkerBusiness','READ','ALLOW','ROLE','hr');

View File

@ -0,0 +1,9 @@
USE vn;
INSERT INTO vn.workerActivityType (code, description)
VALUES('SHELVING_CLEAN_START', 'SE INICIA LIMPIEZA CARRO'),
('SHELVING_CLEAN_STOP', 'SE FINALIZA LIMPIEZA CARRO');

View File

@ -0,0 +1,3 @@
ALTER TABLE vn.priceDelta ADD IF NOT EXISTS isHidden BOOL
DEFAULT FALSE NOT NULL
COMMENT 'Hides the itemType when building de catalog recordset';

View File

@ -258,5 +258,6 @@
"clonedFromTicketWeekly": ", that is a cloned sale from ticket {{ ticketWeekly }}",
"negativeReplaced": "Replaced item [#{{oldItemId}}]({{{oldItemUrl}}}) {{oldItem}} with [#{{newItemId}}]({{{newItemUrl}}}) {{newItem}} from ticket [{{ticketId}}]({{{ticketUrl}}})",
"The tag and priority can't be repeated": "The tag and priority can't be repeated",
"duplicateWarehouse": "The introduced warehouse already exists"
"The introduced warehouse already exists": "The introduced warehouse already exists",
"The code already exists": "The code already exists"
}

View File

@ -1,403 +1,404 @@
{
"Phone format is invalid": "El formato del teléfono no es correcto",
"You are not allowed to change the credit": "No tienes privilegios para modificar el crédito",
"Unable to mark the equivalence surcharge": "No se puede marcar el recargo de equivalencia",
"The default consignee can not be unchecked": "No se puede desmarcar el consignatario predeterminado",
"Unable to default a disabled consignee": "No se puede poner predeterminado un consignatario desactivado",
"Can't be blank": "No puede estar en blanco",
"Invalid TIN": "NIF/CIF inválido",
"TIN must be unique": "El NIF/CIF debe ser único",
"A client with that Web User name already exists": "Ya existe un cliente con ese Usuario Web",
"Is invalid": "Es inválido",
"Quantity cannot be zero": "La cantidad no puede ser cero",
"Enter an integer different to zero": "Introduce un entero distinto de cero",
"Package cannot be blank": "El embalaje no puede estar en blanco",
"The company name must be unique": "La razón social debe ser única",
"Invalid email": "Correo electrónico inválido",
"The IBAN does not have the correct format": "El IBAN no tiene el formato correcto",
"That payment method requires an IBAN": "El método de pago seleccionado requiere un IBAN",
"That payment method requires a BIC": "El método de pago seleccionado requiere un BIC",
"State cannot be blank": "El estado no puede estar en blanco",
"Worker cannot be blank": "El trabajador no puede estar en blanco",
"Cannot change the payment method if no salesperson": "No se puede cambiar la forma de pago si no hay comercial asignado",
"can't be blank": "El campo no puede estar vacío",
"Observation type must be unique": "El tipo de observación no puede repetirse",
"Phone format is invalid": "El formato del teléfono no es correcto",
"You are not allowed to change the credit": "No tienes privilegios para modificar el crédito",
"Unable to mark the equivalence surcharge": "No se puede marcar el recargo de equivalencia",
"The default consignee can not be unchecked": "No se puede desmarcar el consignatario predeterminado",
"Unable to default a disabled consignee": "No se puede poner predeterminado un consignatario desactivado",
"Can't be blank": "No puede estar en blanco",
"Invalid TIN": "NIF/CIF inválido",
"TIN must be unique": "El NIF/CIF debe ser único",
"A client with that Web User name already exists": "Ya existe un cliente con ese Usuario Web",
"Is invalid": "Es inválido",
"Quantity cannot be zero": "La cantidad no puede ser cero",
"Enter an integer different to zero": "Introduce un entero distinto de cero",
"Package cannot be blank": "El embalaje no puede estar en blanco",
"The company name must be unique": "La razón social debe ser única",
"Invalid email": "Correo electrónico inválido",
"The IBAN does not have the correct format": "El IBAN no tiene el formato correcto",
"That payment method requires an IBAN": "El método de pago seleccionado requiere un IBAN",
"That payment method requires a BIC": "El método de pago seleccionado requiere un BIC",
"State cannot be blank": "El estado no puede estar en blanco",
"Worker cannot be blank": "El trabajador no puede estar en blanco",
"Cannot change the payment method if no salesperson": "No se puede cambiar la forma de pago si no hay comercial asignado",
"can't be blank": "El campo no puede estar vacío",
"Observation type must be unique": "El tipo de observación no puede repetirse",
"The credit must be an integer greater than or equal to zero": "The credit must be an integer greater than or equal to zero",
"The grade must be similar to the last one": "El grade debe ser similar al último",
"Only manager can change the credit": "Solo el gerente puede cambiar el credito de este cliente",
"Name cannot be blank": "El nombre no puede estar en blanco",
"Phone cannot be blank": "El teléfono no puede estar en blanco",
"Period cannot be blank": "El periodo no puede estar en blanco",
"Choose a company": "Selecciona una empresa",
"Se debe rellenar el campo de texto": "Se debe rellenar el campo de texto",
"Description should have maximum of 45 characters": "La descripción debe tener maximo 45 caracteres",
"Cannot be blank": "El campo no puede estar en blanco",
"The grade must be an integer greater than or equal to zero": "El grade debe ser un entero mayor o igual a cero",
"Sample type cannot be blank": "El tipo de plantilla no puede quedar en blanco",
"Description cannot be blank": "Se debe rellenar el campo de texto",
"The price of the item changed": "El precio del artículo cambió",
"The value should not be greater than 100%": "El valor no debe de ser mayor de 100%",
"The value should be a number": "El valor debe ser un numero",
"This order is not editable": "Esta orden no se puede modificar",
"You can't create an order for a frozen client": "No puedes crear una orden para un cliente congelado",
"You can't create an order for a client that has a debt": "No puedes crear una orden para un cliente con deuda",
"is not a valid date": "No es una fecha valida",
"Barcode must be unique": "El código de barras debe ser único",
"The warehouse can't be repeated": "El almacén no puede repetirse",
"The tag or priority can't be repeated for an item": "El tag o prioridad no puede repetirse para un item",
"The observation type can't be repeated": "El tipo de observación no puede repetirse",
"A claim with that sale already exists": "Ya existe una reclamación para esta línea",
"You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo",
"Warehouse cannot be blank": "El almacén no puede quedar en blanco",
"Agency cannot be blank": "La agencia no puede quedar en blanco",
"Not enough privileges to edit a client with verified data": "No tienes permisos para hacer cambios en un cliente con datos comprobados",
"This address doesn't exist": "Este consignatario no existe",
"You must delete the claim id %d first": "Antes debes borrar la reclamación %d",
"You don't have enough privileges": "No tienes suficientes permisos",
"Cannot check Equalization Tax in this NIF/CIF": "No se puede marcar RE en este NIF/CIF",
"You can't make changes on the basic data of an confirmed order or with rows": "No puedes cambiar los datos básicos de una orden con artículos",
"INVALID_USER_NAME": "El nombre de usuario solo debe contener letras minúsculas o, a partir del segundo carácter, números o subguiones, no está permitido el uso de la letra ñ",
"You can't create a ticket for a frozen client": "No puedes crear un ticket para un cliente congelado",
"You can't create a ticket for an inactive client": "No puedes crear un ticket para un cliente inactivo",
"Tag value cannot be blank": "El valor del tag no puede quedar en blanco",
"ORDER_EMPTY": "Cesta vacía",
"You don't have enough privileges to do that": "No tienes permisos para cambiar esto",
"NO SE PUEDE DESACTIVAR EL CONSIGNAT": "NO SE PUEDE DESACTIVAR EL CONSIGNAT",
"Error. El NIF/CIF está repetido": "Error. El NIF/CIF está repetido",
"Street cannot be empty": "Dirección no puede estar en blanco",
"City cannot be empty": "Ciudad no puede estar en blanco",
"Code cannot be blank": "Código no puede estar en blanco",
"You cannot remove this department": "No puedes eliminar este departamento",
"The extension must be unique": "La extensión debe ser unica",
"The secret can't be blank": "La contraseña no puede estar en blanco",
"We weren't able to send this SMS": "No hemos podido enviar el SMS",
"This client can't be invoiced": "Este cliente no puede ser facturado",
"You must provide the correction information to generate a corrective invoice": "Debes informar la información de corrección para generar una factura rectificativa",
"This ticket can't be invoiced": "Este ticket no puede ser facturado",
"You cannot add or modify services to an invoiced ticket": "No puedes añadir o modificar servicios a un ticket facturado",
"This ticket can not be modified": "Este ticket no puede ser modificado",
"The introduced hour already exists": "Esta hora ya ha sido introducida",
"INFINITE_LOOP": "Existe una dependencia entre dos Jefes",
"The sales of the receiver ticket can't be modified": "Las lineas del ticket al que envias no pueden ser modificadas",
"NO_AGENCY_AVAILABLE": "No hay una zona de reparto disponible con estos parámetros",
"ERROR_PAST_SHIPMENT": "No puedes seleccionar una fecha de envío en pasado",
"The current ticket can't be modified": "El ticket actual no puede ser modificado",
"The current claim can't be modified": "La reclamación actual no puede ser modificada",
"The sales of this ticket can't be modified": "Las lineas de este ticket no pueden ser modificadas",
"The sales do not exists": "La(s) línea(s) seleccionada(s) no existe(n)",
"Please select at least one sale": "Por favor selecciona al menos una linea",
"All sales must belong to the same ticket": "Todas las lineas deben pertenecer al mismo ticket",
"NO_ZONE_FOR_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada",
"This item doesn't exists": "El artículo no existe",
"NOT_ZONE_WITH_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada",
"Extension format is invalid": "El formato de la extensión es inválido",
"Invalid parameters to create a new ticket": "Parámetros inválidos para crear un nuevo ticket",
"This item is not available": "Este artículo no está disponible",
"This postcode already exists": "Este código postal ya existe",
"Concept cannot be blank": "El concepto no puede quedar en blanco",
"File doesn't exists": "El archivo no existe",
"You don't have privileges to change the zone": "No tienes permisos para cambiar la zona o para esos parámetros hay más de una opción de envío, hable con las agencias",
"This ticket is already on weekly tickets": "Este ticket ya está en tickets programados",
"Ticket id cannot be blank": "El id de ticket no puede quedar en blanco",
"Weekday cannot be blank": "El día de la semana no puede quedar en blanco",
"You can't delete a confirmed order": "No puedes borrar un pedido confirmado",
"The social name has an invalid format": "El nombre fiscal tiene un formato incorrecto",
"Invalid quantity": "Cantidad invalida",
"This postal code is not valid": "Este código postal no es válido",
"is invalid": "es inválido",
"The postcode doesn't exist. Please enter a correct one": "El código postal no existe. Por favor, introduce uno correcto",
"The department name can't be repeated": "El nombre del departamento no puede repetirse",
"This phone already exists": "Este teléfono ya existe",
"You cannot move a parent to its own sons": "No puedes mover un elemento padre a uno de sus hijos",
"You can't create a claim for a removed ticket": "No puedes crear una reclamación para un ticket eliminado",
"You cannot delete a ticket that part of it is being prepared": "No puedes eliminar un ticket en el que una parte que está siendo preparada",
"You must delete all the buy requests first": "Debes eliminar todas las peticiones de compra primero",
"You should specify a date": "Debes especificar una fecha",
"You should specify at least a start or end date": "Debes especificar al menos una fecha de inicio o de fin",
"Start date should be lower than end date": "La fecha de inicio debe ser menor que la fecha de fin",
"You should mark at least one week day": "Debes marcar al menos un día de la semana",
"Swift / BIC can't be empty": "Swift / BIC no puede estar vacío",
"Customs agent is required for a non UEE member": "El agente de aduanas es requerido para los clientes extracomunitarios",
"Incoterms is required for a non UEE member": "El incoterms es requerido para los clientes extracomunitarios",
"Deleted sales from ticket": "He eliminado las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{deletions}}}",
"Added sale to ticket": "He añadido la siguiente linea al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{addition}}}",
"Changed sale discount": "He cambiado el descuento de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}} {{ticketWeekly}}",
"Created claim": "He creado la reclamación [{{claimId}}]({{{claimUrl}}}) de las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Changed sale price": "He cambiado el precio de [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* del ticket [{{ticketId}}]({{{ticketUrl}}}) {{ticketWeekly}} ",
"Changed sale quantity": "He cambiado {{changes}} del ticket [{{ticketId}}]({{{ticketUrl}}}) {{ticketWeekly}}",
"Changes in sales": "la cantidad de [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}*",
"State": "Estado",
"regular": "normal",
"reserved": "reservado",
"Changed sale reserved state": "He cambiado el estado reservado de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Bought units from buy request": "Se ha comprado {{quantity}} unidades de [{{itemId}} {{concept}}]({{{urlItem}}}) para el ticket id [{{ticketId}}]({{{url}}})",
"Deny buy request": "Se ha rechazado la petición de compra para el ticket id [{{ticketId}}]({{{url}}}). Motivo: {{observation}}",
"MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} ({{clientId}})]({{{url}}}) a *{{credit}} €*",
"Changed client paymethod": "He cambiado la forma de pago del cliente [{{clientName}} ({{clientId}})]({{{url}}})",
"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}}*, con el tipo de recogida *{{claimPickup}}*",
"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 4000": "La distancia debe ser inferior a 4000",
"This ticket is deleted": "Este ticket está eliminado",
"Unable to clone this travel": "No ha sido posible clonar este travel",
"This thermograph id already exists": "La id del termógrafo ya existe",
"Choose a date range or days forward": "Selecciona un rango de fechas o días en adelante",
"ORDER_ALREADY_CONFIRMED": "ORDEN YA CONFIRMADA",
"Invalid password": "Invalid password",
"Password does not meet requirements": "La contraseña no cumple los requisitos",
"Role already assigned": "Rol ya asignado",
"Invalid role name": "Nombre de rol no válido",
"Role name must be written in camelCase": "El nombre del rol debe escribirse en camelCase",
"Email already exists": "El correo ya existe",
"User already exists": "El/La usuario/a ya existe",
"Absence change notification on the labour calendar": "Notificación de cambio de ausencia en el calendario laboral",
"Record of hours week": "Registro de horas semana {{week}} año {{year}} ",
"Created absence": "El empleado <strong>{{author}}</strong> ha añadido una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> para el día {{dated}}.",
"Deleted absence": "El empleado <strong>{{author}}</strong> ha eliminado una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> del día {{dated}}.",
"I have deleted the ticket id": "He eliminado el ticket id [{{id}}]({{{url}}})",
"I have restored the ticket id": "He restaurado el ticket id [{{id}}]({{{url}}})",
"You can only restore a ticket within the first hour after deletion": "Únicamente puedes restaurar el ticket dentro de la primera hora después de su eliminación",
"Changed this data from the ticket": "He cambiado estos datos del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"agencyModeFk": "Agencia",
"clientFk": "Cliente",
"zoneFk": "Zona",
"warehouseFk": "Almacén",
"shipped": "F. envío",
"landed": "F. entrega",
"addressFk": "Consignatario",
"companyFk": "Empresa",
"agency": "Agencia",
"delivery": "Reparto",
"The social name cannot be empty": "La razón social no puede quedar en blanco",
"The nif cannot be empty": "El NIF no puede quedar en blanco",
"You need to fill sage information before you check verified data": "Debes rellenar la información de sage antes de marcar datos comprobados",
"ASSIGN_ZONE_FIRST": "Asigna una zona primero",
"Amount cannot be zero": "El importe no puede ser cero",
"Company has to be official": "Empresa inválida",
"You can not select this payment method without a registered bankery account": "No se puede utilizar este método de pago si no has registrado una cuenta bancaria",
"Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas",
"The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta",
"New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}* y un precio de *{{price}} €*",
"New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}*",
"Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío",
"This BIC already exist.": "Este BIC ya existe.",
"That item doesn't exists": "Ese artículo no existe",
"There's a new urgent ticket:": "Hay un nuevo ticket urgente:",
"Invalid account": "Cuenta inválida",
"Compensation account is empty": "La cuenta para compensar está vacia",
"This genus already exist": "Este genus ya existe",
"This specie already exist": "Esta especie ya existe",
"Client assignment has changed": "He cambiado el comercial ~*\"<{{previousWorkerName}}>\"*~ por *\"<{{currentWorkerName}}>\"* del cliente [{{clientName}} ({{clientId}})]({{{url}}})",
"None": "Ninguno",
"The contract was not active during the selected date": "El contrato no estaba activo durante la fecha seleccionada",
"Cannot add more than one '1/2 day vacation'": "No puedes añadir más de un 'Vacaciones 1/2 dia'",
"This document already exists on this ticket": "Este documento ya existe en el ticket",
"Some of the selected tickets are not billable": "Algunos de los tickets seleccionados no son facturables",
"You can't invoice tickets from multiple clients": "No puedes facturar tickets de multiples clientes",
"nickname": "nickname",
"INACTIVE_PROVIDER": "Proveedor inactivo",
"This client is not invoiceable": "Este cliente no es facturable",
"serial non editable": "Esta serie no permite asignar la referencia",
"Max shipped required": "La fecha límite es requerida",
"Can't invoice to future": "No se puede facturar a futuro",
"Can't invoice to past": "No se puede facturar a pasado",
"This ticket is already invoiced": "Este ticket ya está facturado",
"A ticket with an amount of zero can't be invoiced": "No se puede facturar un ticket con importe cero",
"A ticket with a negative base can't be invoiced": "No se puede facturar un ticket con una base negativa",
"Global invoicing failed": "[Facturación global] No se han podido facturar algunos clientes",
"Wasn't able to invoice the following clients": "No se han podido facturar los siguientes clientes",
"Can't verify data unless the client has a business type": "No se puede verificar datos de un cliente que no tiene tipo de negocio",
"You don't have enough privileges to set this credit amount": "No tienes suficientes privilegios para establecer esta cantidad de crédito",
"You can't change the credit set to zero from a financialBoss": "No puedes cambiar el cŕedito establecido a cero por un jefe de finanzas",
"Amounts do not match": "Las cantidades no coinciden",
"The PDF document does not exist": "El documento PDF no existe. Prueba a regenerarlo desde la opción 'Regenerar PDF factura'",
"The type of business must be filled in basic data": "El tipo de negocio debe estar rellenado en datos básicos",
"You can't create a claim from a ticket delivered more than seven days ago": "No puedes crear una reclamación de un ticket entregado hace más de siete días",
"The worker has hours recorded that day": "El trabajador tiene horas fichadas ese día",
"The worker has a marked absence that day": "El trabajador tiene marcada una ausencia ese día",
"You can not modify is pay method checked": "No se puede modificar el campo método de pago validado",
"The account size must be exactly 10 characters": "El tamaño de la cuenta debe ser exactamente de 10 caracteres",
"Can't transfer claimed sales": "No puedes transferir lineas reclamadas",
"You don't have privileges to create refund": "No tienes permisos para crear un abono",
"The item is required": "El artículo es requerido",
"The agency is already assigned to another autonomous": "La agencia ya está asignada a otro autónomo",
"date in the future": "Fecha en el futuro",
"reference duplicated": "Referencia duplicada",
"This ticket is already a refund": "Este ticket ya es un abono",
"isWithoutNegatives": "Sin negativos",
"routeFk": "routeFk",
"Can't change the password of another worker": "No se puede cambiar la contraseña de otro trabajador",
"No hay un contrato en vigor": "No hay un contrato en vigor",
"No se permite fichar a futuro": "No se permite fichar a futuro",
"No está permitido trabajar": "No está permitido trabajar",
"Fichadas impares": "Fichadas impares",
"Descanso diario 12h.": "Descanso diario 12h.",
"Descanso semanal 36h. / 72h.": "Descanso semanal 36h. / 72h.",
"Dirección incorrecta": "Dirección incorrecta",
"Modifiable user details only by an administrator": "Detalles de usuario modificables solo por un administrador",
"Modifiable password only via recovery or by an administrator": "Contraseña modificable solo a través de la recuperación o por un administrador",
"Not enough privileges to edit a client": "No tienes suficientes privilegios para editar un cliente",
"This route does not exists": "Esta ruta no existe",
"Claim pickup order sent": "Reclamación Orden de recogida enviada [{{claimId}}]({{{claimUrl}}}) al cliente *{{clientName}}*",
"You don't have grant privilege": "No tienes privilegios para dar privilegios",
"You don't own the role and you can't assign it to another user": "No eres el propietario del rol y no puedes asignarlo a otro usuario",
"Ticket merged": "Ticket [{{originId}}]({{{originFullPath}}}) ({{{originDated}}}) fusionado con [{{destinationId}}]({{{destinationFullPath}}}) ({{{destinationDated}}})",
"Already has this status": "Ya tiene este estado",
"There aren't records for this week": "No existen registros para esta semana",
"Empty data source": "Origen de datos vacio",
"App locked": "Aplicación bloqueada por el usuario {{userId}}",
"Email verify": "Correo de verificación",
"Landing cannot be lesser than shipment": "Landing cannot be lesser than shipment",
"Receipt's bank was not found": "No se encontró el banco del recibo",
"This receipt was not compensated": "Este recibo no ha sido compensado",
"Client's email was not found": "No se encontró el email del cliente",
"Negative basis": "Base negativa",
"This worker code already exists": "Este codigo de trabajador ya existe",
"This personal mail already exists": "Este correo personal ya existe",
"This worker already exists": "Este trabajador ya existe",
"App name does not exist": "El nombre de aplicación no es válido",
"Try again": "Vuelve a intentarlo",
"Aplicación bloqueada por el usuario 9": "Aplicación bloqueada por el usuario 9",
"Failed to upload delivery note": "Error al subir albarán {{id}}",
"The DOCUWARE PDF document does not exists": "El documento PDF Docuware no existe",
"It is not possible to modify tracked sales": "No es posible modificar líneas de pedido que se hayan empezado a preparar",
"It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo",
"It is not possible to modify cloned sales": "No es posible modificar líneas de pedido clonadas",
"A supplier with the same name already exists. Change the country.": "Un proveedor con el mismo nombre ya existe. Cambie el país.",
"There is no assigned email for this client": "No hay correo asignado para este cliente",
"Exists an invoice with a future date": "Existe una factura con fecha posterior",
"Invoice date can't be less than max date": "La fecha de factura no puede ser inferior a la fecha límite",
"Warehouse inventory not set": "El almacén inventario no está establecido",
"This locker has already been assigned": "Esta taquilla ya ha sido asignada",
"Tickets with associated refunds": "No se pueden borrar tickets con abonos asociados. Este ticket está asociado al abono Nº %s",
"Not exist this branch": "La rama no existe",
"This ticket cannot be signed because it has not been boxed": "Este ticket no puede firmarse porque no ha sido encajado",
"Collection does not exist": "La colección no existe",
"Cannot obtain exclusive lock": "No se puede obtener un bloqueo exclusivo",
"Insert a date range": "Inserte un rango de fechas",
"Added observation": "{{user}} añadió esta observacion: {{text}} {{defaulterId}} ({{{defaulterUrl}}})",
"Comment added to client": "Observación añadida al cliente {{clientFk}}",
"Invalid auth code": "Código de verificación incorrecto",
"Invalid or expired verification code": "Código de verificación incorrecto o expirado",
"Cannot create a new claimBeginning from a different ticket": "No se puede crear una línea de reclamación de un ticket diferente al origen",
"company": "Compañía",
"country": "País",
"clientId": "Id cliente",
"clientSocialName": "Cliente",
"amount": "Importe",
"taxableBase": "Base",
"ticketFk": "Id ticket",
"isActive": "Activo",
"hasToInvoice": "Facturar",
"isTaxDataChecked": "Datos comprobados",
"comercialId": "Id comercial",
"comercialName": "Comercial",
"Pass expired": "La contraseña ha caducado, cambiela desde Salix",
"Invalid NIF for VIES": "Invalid NIF for VIES",
"Ticket does not exist": "Este ticket no existe",
"Ticket is already signed": "Este ticket ya ha sido firmado",
"Authentication failed": "Autenticación fallida",
"You can't use the same password": "No puedes usar la misma contraseña",
"You can only add negative amounts in refund tickets": "Solo se puede añadir cantidades negativas en tickets abono",
"Fecha fuera de rango": "Fecha fuera de rango",
"Error while generating PDF": "Error al generar PDF",
"Error when sending mail to client": "Error al enviar el correo al cliente",
"Mail not sent": "Se ha producido un fallo al enviar la factura al cliente [{{clientId}}]({{{clientUrl}}}), por favor revisa la dirección de correo electrónico",
"The renew period has not been exceeded": "El periodo de renovación no ha sido superado",
"Valid priorities": "Prioridades válidas: %d",
"hasAnyNegativeBase": "Base negativa para los tickets: {{ticketsIds}}",
"hasAnyPositiveBase": "Base positivas para los tickets: {{ticketsIds}}",
"You cannot assign an alias that you are not assigned to": "No puede asignar un alias que no tenga asignado",
"This ticket cannot be left empty.": "Este ticket no se puede dejar vacío. %s",
"The company has not informed the supplier account for bank transfers": "La empresa no tiene informado la cuenta de proveedor para transferencias bancarias",
"You cannot assign/remove an alias that you are not assigned to": "No puede asignar/eliminar un alias que no tenga asignado",
"This invoice has a linked vehicle.": "Esta factura tiene un vehiculo vinculado",
"You don't have enough privileges.": "No tienes suficientes permisos.",
"This ticket is locked": "Este ticket está bloqueado.",
"This ticket is not editable.": "Este ticket no es editable.",
"The ticket doesn't exist.": "No existe el ticket.",
"Social name should be uppercase": "La razón social debe ir en mayúscula",
"Street should be uppercase": "La dirección fiscal debe ir en mayúscula",
"Ticket without Route": "Ticket sin ruta",
"Select a different client": "Seleccione un cliente distinto",
"Fill all the fields": "Rellene todos los campos",
"The response is not a PDF": "La respuesta no es un PDF",
"Booking completed": "Reserva completada",
"The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación",
"The notification subscription of this worker cant be modified": "La subscripción a la notificación de este trabajador no puede ser modificada",
"User disabled": "Usuario desactivado",
"The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mínima",
"quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mínima",
"Cannot past travels with entries": "No se pueden pasar envíos con entradas",
"It was not able to remove the next expeditions:": "No se pudo eliminar las siguientes expediciones: {{expeditions}}",
"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",
"Field are invalid": "El campo '{{tag}}' no es válido",
"Incorrect pin": "Pin incorrecto.",
"You already have the mailAlias": "Ya tienes este alias de correo",
"The alias cant be modified": "Este alias de correo no puede ser modificado",
"No tickets to invoice": "No hay tickets para facturar que cumplan los requisitos de facturación",
"this warehouse has not dms": "El Almacén no acepta documentos",
"This ticket already has a cmr saved": "Este ticket ya tiene un cmr guardado",
"Name should be uppercase": "El nombre debe ir en mayúscula",
"Bank entity must be specified": "La entidad bancaria es obligatoria",
"An email is necessary": "Es necesario un email",
"You cannot update these fields": "No puedes actualizar estos campos",
"CountryFK cannot be empty": "El país no puede estar vacío",
"Cmr file does not exist": "El archivo del cmr no existe",
"You are not allowed to modify the alias": "No estás autorizado a modificar el alias",
"The address of the customer must have information about Incoterms and Customs Agent": "El consignatario del cliente debe tener informado Incoterms y Agente de aduanas",
"No invoice series found for these parameters": "No se encontró una serie para estos parámetros",
"The line could not be marked": "La linea no puede ser marcada",
"Through this procedure, it is not possible to modify the password of users with verified email": "Mediante este procedimiento, no es posible modificar la contraseña de usuarios con correo verificado",
"They're not your subordinate": "No es tu subordinado/a.",
"No results found": "No se han encontrado resultados",
"InvoiceIn is already booked": "La factura recibida está contabilizada",
"This workCenter is already assigned to this agency": "Este centro de trabajo ya está asignado a esta agencia",
"Select ticket or client": "Elija un ticket o un client",
"It was not able to create the invoice": "No se pudo crear la factura",
"Incoterms and Customs agent are required for a non UEE member": "Se requieren Incoterms y agente de aduanas para un no miembro de la UEE",
"You can not use the same password": "No puedes usar la misma contraseña",
"This PDA is already assigned to another user": "Este PDA ya está asignado a otro usuario",
"You can only have one PDA": "Solo puedes tener un PDA",
"The invoices have been created but the PDFs could not be generated": "Se ha facturado pero no se ha podido generar el PDF",
"It has been invoiced but the PDF of refund not be generated": "Se ha facturado pero no se ha podido generar el PDF del abono",
"Payment method is required": "El método de pago es obligatorio",
"Cannot send mail": "Não é possível enviar o email",
"CONSTRAINT `supplierAccountTooShort` failed for `vn`.`supplier`": "La cuenta debe tener exactamente 10 dígitos",
"The sale not exists in the item shelving": "La venta no existe en la estantería del artículo",
"The entry not have stickers": "La entrada no tiene etiquetas",
"Too many records": "Demasiados registros",
"Original invoice not found": "Factura original no encontrada",
"The entry has no lines or does not exist": "La entrada no tiene lineas o no existe",
"Weight already set": "El peso ya está establecido",
"This ticket is not allocated to your department": "Este ticket no está asignado a tu departamento",
"There is already a tray with the same height": "Ya existe una bandeja con la misma altura",
"The height must be greater than 50cm": "La altura debe ser superior a 50cm",
"The maximum height of the wagon is 200cm": "La altura máxima es 200cm",
"The entry does not have stickers": "La entrada no tiene etiquetas",
"This buyer has already made a reservation for this date": "Este comprador ya ha hecho una reserva para esta fecha",
"No valid travel thermograph found": "No se encontró un termógrafo válido",
"The quantity claimed cannot be greater than the quantity of the line": "La cantidad reclamada no puede ser mayor que la cantidad de la línea",
"type cannot be blank": "Se debe rellenar el tipo",
"There are tickets for this area, delete them first": "Hay tickets para esta sección, borralos primero",
"There is no company associated with that warehouse": "No hay ninguna empresa asociada a ese almacén",
"You do not have permission to modify the booked field": "No tienes permisos para modificar el campo contabilizada",
"ticketLostExpedition": "El ticket [{{ticketId}}]({{{ticketUrl}}}) tiene la siguiente expedición perdida:{{ expeditionId }}",
"The web user's email already exists": "El correo del usuario web ya existe",
"Sales already moved": "Ya han sido transferidas",
"The raid information is not correct": "La información de la redada no es correcta",
"An item type with the same code already exists": "Un tipo con el mismo código ya existe",
"Holidays to past days not available": "Las vacaciones a días pasados no están disponibles",
"All tickets have a route order": "Todos los tickets tienen orden de ruta",
"There are tickets to be invoiced": "La zona tiene tickets por facturar",
"Incorrect delivery order alert on route": "Alerta de orden de entrega incorrecta en ruta: {{ route }} zona: {{ zone }}",
"Ticket has been delivered out of order": "El ticket {{ticket}} {{{fullUrl}}} no ha sido entregado en su orden.",
"Price cannot be blank": "El precio no puede estar en blanco",
"clonedFromTicketWeekly": ", que es una linea clonada del ticket {{ticketWeekly}}",
"negativeReplaced": "Sustituido el articulo [#{{oldItemId}}]({{{oldItemUrl}}}) {{oldItem}} por [#{{newItemId}}]({{{newItemUrl}}}) {{newItem}} del ticket [{{ticketId}}]({{{ticketUrl}}})",
"duplicateWarehouse": "El almacén seleccionado ya existe en la zona"
}
"The grade must be similar to the last one": "El grade debe ser similar al último",
"Only manager can change the credit": "Solo el gerente puede cambiar el credito de este cliente",
"Name cannot be blank": "El nombre no puede estar en blanco",
"Phone cannot be blank": "El teléfono no puede estar en blanco",
"Period cannot be blank": "El periodo no puede estar en blanco",
"Choose a company": "Selecciona una empresa",
"Se debe rellenar el campo de texto": "Se debe rellenar el campo de texto",
"Description should have maximum of 45 characters": "La descripción debe tener maximo 45 caracteres",
"Cannot be blank": "El campo no puede estar en blanco",
"The grade must be an integer greater than or equal to zero": "El grade debe ser un entero mayor o igual a cero",
"Sample type cannot be blank": "El tipo de plantilla no puede quedar en blanco",
"Description cannot be blank": "Se debe rellenar el campo de texto",
"The price of the item changed": "El precio del artículo cambió",
"The value should not be greater than 100%": "El valor no debe de ser mayor de 100%",
"The value should be a number": "El valor debe ser un numero",
"This order is not editable": "Esta orden no se puede modificar",
"You can't create an order for a frozen client": "No puedes crear una orden para un cliente congelado",
"You can't create an order for a client that has a debt": "No puedes crear una orden para un cliente con deuda",
"is not a valid date": "No es una fecha valida",
"Barcode must be unique": "El código de barras debe ser único",
"The warehouse can't be repeated": "El almacén no puede repetirse",
"The tag or priority can't be repeated for an item": "El tag o prioridad no puede repetirse para un item",
"The observation type can't be repeated": "El tipo de observación no puede repetirse",
"A claim with that sale already exists": "Ya existe una reclamación para esta línea",
"You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo",
"Warehouse cannot be blank": "El almacén no puede quedar en blanco",
"Agency cannot be blank": "La agencia no puede quedar en blanco",
"Not enough privileges to edit a client with verified data": "No tienes permisos para hacer cambios en un cliente con datos comprobados",
"This address doesn't exist": "Este consignatario no existe",
"You must delete the claim id %d first": "Antes debes borrar la reclamación %d",
"You don't have enough privileges": "No tienes suficientes permisos",
"Cannot check Equalization Tax in this NIF/CIF": "No se puede marcar RE en este NIF/CIF",
"You can't make changes on the basic data of an confirmed order or with rows": "No puedes cambiar los datos básicos de una orden con artículos",
"INVALID_USER_NAME": "El nombre de usuario solo debe contener letras minúsculas o, a partir del segundo carácter, números o subguiones, no está permitido el uso de la letra ñ",
"You can't create a ticket for a frozen client": "No puedes crear un ticket para un cliente congelado",
"You can't create a ticket for an inactive client": "No puedes crear un ticket para un cliente inactivo",
"Tag value cannot be blank": "El valor del tag no puede quedar en blanco",
"ORDER_EMPTY": "Cesta vacía",
"You don't have enough privileges to do that": "No tienes permisos para cambiar esto",
"NO SE PUEDE DESACTIVAR EL CONSIGNAT": "NO SE PUEDE DESACTIVAR EL CONSIGNAT",
"Error. El NIF/CIF está repetido": "Error. El NIF/CIF está repetido",
"Street cannot be empty": "Dirección no puede estar en blanco",
"City cannot be empty": "Ciudad no puede estar en blanco",
"Code cannot be blank": "Código no puede estar en blanco",
"You cannot remove this department": "No puedes eliminar este departamento",
"The extension must be unique": "La extensión debe ser unica",
"The secret can't be blank": "La contraseña no puede estar en blanco",
"We weren't able to send this SMS": "No hemos podido enviar el SMS",
"This client can't be invoiced": "Este cliente no puede ser facturado",
"You must provide the correction information to generate a corrective invoice": "Debes informar la información de corrección para generar una factura rectificativa",
"This ticket can't be invoiced": "Este ticket no puede ser facturado",
"You cannot add or modify services to an invoiced ticket": "No puedes añadir o modificar servicios a un ticket facturado",
"This ticket can not be modified": "Este ticket no puede ser modificado",
"The introduced hour already exists": "Esta hora ya ha sido introducida",
"INFINITE_LOOP": "Existe una dependencia entre dos Jefes",
"The sales of the receiver ticket can't be modified": "Las lineas del ticket al que envias no pueden ser modificadas",
"NO_AGENCY_AVAILABLE": "No hay una zona de reparto disponible con estos parámetros",
"ERROR_PAST_SHIPMENT": "No puedes seleccionar una fecha de envío en pasado",
"The current ticket can't be modified": "El ticket actual no puede ser modificado",
"The current claim can't be modified": "La reclamación actual no puede ser modificada",
"The sales of this ticket can't be modified": "Las lineas de este ticket no pueden ser modificadas",
"The sales do not exists": "La(s) línea(s) seleccionada(s) no existe(n)",
"Please select at least one sale": "Por favor selecciona al menos una linea",
"All sales must belong to the same ticket": "Todas las lineas deben pertenecer al mismo ticket",
"NO_ZONE_FOR_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada",
"This item doesn't exists": "El artículo no existe",
"NOT_ZONE_WITH_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada",
"Extension format is invalid": "El formato de la extensión es inválido",
"Invalid parameters to create a new ticket": "Parámetros inválidos para crear un nuevo ticket",
"This item is not available": "Este artículo no está disponible",
"This postcode already exists": "Este código postal ya existe",
"Concept cannot be blank": "El concepto no puede quedar en blanco",
"File doesn't exists": "El archivo no existe",
"You don't have privileges to change the zone": "No tienes permisos para cambiar la zona o para esos parámetros hay más de una opción de envío, hable con las agencias",
"This ticket is already on weekly tickets": "Este ticket ya está en tickets programados",
"Ticket id cannot be blank": "El id de ticket no puede quedar en blanco",
"Weekday cannot be blank": "El día de la semana no puede quedar en blanco",
"You can't delete a confirmed order": "No puedes borrar un pedido confirmado",
"The social name has an invalid format": "El nombre fiscal tiene un formato incorrecto",
"Invalid quantity": "Cantidad invalida",
"This postal code is not valid": "Este código postal no es válido",
"is invalid": "es inválido",
"The postcode doesn't exist. Please enter a correct one": "El código postal no existe. Por favor, introduce uno correcto",
"The department name can't be repeated": "El nombre del departamento no puede repetirse",
"This phone already exists": "Este teléfono ya existe",
"You cannot move a parent to its own sons": "No puedes mover un elemento padre a uno de sus hijos",
"You can't create a claim for a removed ticket": "No puedes crear una reclamación para un ticket eliminado",
"You cannot delete a ticket that part of it is being prepared": "No puedes eliminar un ticket en el que una parte que está siendo preparada",
"You must delete all the buy requests first": "Debes eliminar todas las peticiones de compra primero",
"You should specify a date": "Debes especificar una fecha",
"You should specify at least a start or end date": "Debes especificar al menos una fecha de inicio o de fin",
"Start date should be lower than end date": "La fecha de inicio debe ser menor que la fecha de fin",
"You should mark at least one week day": "Debes marcar al menos un día de la semana",
"Swift / BIC can't be empty": "Swift / BIC no puede estar vacío",
"Customs agent is required for a non UEE member": "El agente de aduanas es requerido para los clientes extracomunitarios",
"Incoterms is required for a non UEE member": "El incoterms es requerido para los clientes extracomunitarios",
"Deleted sales from ticket": "He eliminado las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{deletions}}}",
"Added sale to ticket": "He añadido la siguiente linea al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{addition}}}",
"Changed sale discount": "He cambiado el descuento de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}} {{ticketWeekly}}",
"Created claim": "He creado la reclamación [{{claimId}}]({{{claimUrl}}}) de las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Changed sale price": "He cambiado el precio de [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* del ticket [{{ticketId}}]({{{ticketUrl}}}) {{ticketWeekly}} ",
"Changed sale quantity": "He cambiado {{changes}} del ticket [{{ticketId}}]({{{ticketUrl}}}) {{ticketWeekly}}",
"Changes in sales": "la cantidad de [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}*",
"State": "Estado",
"regular": "normal",
"reserved": "reservado",
"Changed sale reserved state": "He cambiado el estado reservado de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Bought units from buy request": "Se ha comprado {{quantity}} unidades de [{{itemId}} {{concept}}]({{{urlItem}}}) para el ticket id [{{ticketId}}]({{{url}}})",
"Deny buy request": "Se ha rechazado la petición de compra para el ticket id [{{ticketId}}]({{{url}}}). Motivo: {{observation}}",
"MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} ({{clientId}})]({{{url}}}) a *{{credit}} €*",
"Changed client paymethod": "He cambiado la forma de pago del cliente [{{clientName}} ({{clientId}})]({{{url}}})",
"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}}*, con el tipo de recogida *{{claimPickup}}*",
"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 4000": "La distancia debe ser inferior a 4000",
"This ticket is deleted": "Este ticket está eliminado",
"Unable to clone this travel": "No ha sido posible clonar este travel",
"This thermograph id already exists": "La id del termógrafo ya existe",
"Choose a date range or days forward": "Selecciona un rango de fechas o días en adelante",
"ORDER_ALREADY_CONFIRMED": "ORDEN YA CONFIRMADA",
"Invalid password": "Invalid password",
"Password does not meet requirements": "La contraseña no cumple los requisitos",
"Role already assigned": "Rol ya asignado",
"Invalid role name": "Nombre de rol no válido",
"Role name must be written in camelCase": "El nombre del rol debe escribirse en camelCase",
"Email already exists": "El correo ya existe",
"User already exists": "El/La usuario/a ya existe",
"Absence change notification on the labour calendar": "Notificación de cambio de ausencia en el calendario laboral",
"Record of hours week": "Registro de horas semana {{week}} año {{year}} ",
"Created absence": "El empleado <strong>{{author}}</strong> ha añadido una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> para el día {{dated}}.",
"Deleted absence": "El empleado <strong>{{author}}</strong> ha eliminado una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> del día {{dated}}.",
"I have deleted the ticket id": "He eliminado el ticket id [{{id}}]({{{url}}})",
"I have restored the ticket id": "He restaurado el ticket id [{{id}}]({{{url}}})",
"You can only restore a ticket within the first hour after deletion": "Únicamente puedes restaurar el ticket dentro de la primera hora después de su eliminación",
"Changed this data from the ticket": "He cambiado estos datos del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"agencyModeFk": "Agencia",
"clientFk": "Cliente",
"zoneFk": "Zona",
"warehouseFk": "Almacén",
"shipped": "F. envío",
"landed": "F. entrega",
"addressFk": "Consignatario",
"companyFk": "Empresa",
"agency": "Agencia",
"delivery": "Reparto",
"The social name cannot be empty": "La razón social no puede quedar en blanco",
"The nif cannot be empty": "El NIF no puede quedar en blanco",
"You need to fill sage information before you check verified data": "Debes rellenar la información de sage antes de marcar datos comprobados",
"ASSIGN_ZONE_FIRST": "Asigna una zona primero",
"Amount cannot be zero": "El importe no puede ser cero",
"Company has to be official": "Empresa inválida",
"You can not select this payment method without a registered bankery account": "No se puede utilizar este método de pago si no has registrado una cuenta bancaria",
"Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas",
"The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta",
"New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}* y un precio de *{{price}} €*",
"New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}*",
"Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío",
"This BIC already exist.": "Este BIC ya existe.",
"That item doesn't exists": "Ese artículo no existe",
"There's a new urgent ticket:": "Hay un nuevo ticket urgente:",
"Invalid account": "Cuenta inválida",
"Compensation account is empty": "La cuenta para compensar está vacia",
"This genus already exist": "Este genus ya existe",
"This specie already exist": "Esta especie ya existe",
"Client assignment has changed": "He cambiado el comercial ~*\"<{{previousWorkerName}}>\"*~ por *\"<{{currentWorkerName}}>\"* del cliente [{{clientName}} ({{clientId}})]({{{url}}})",
"None": "Ninguno",
"The contract was not active during the selected date": "El contrato no estaba activo durante la fecha seleccionada",
"Cannot add more than one '1/2 day vacation'": "No puedes añadir más de un 'Vacaciones 1/2 dia'",
"This document already exists on this ticket": "Este documento ya existe en el ticket",
"Some of the selected tickets are not billable": "Algunos de los tickets seleccionados no son facturables",
"You can't invoice tickets from multiple clients": "No puedes facturar tickets de multiples clientes",
"nickname": "nickname",
"INACTIVE_PROVIDER": "Proveedor inactivo",
"This client is not invoiceable": "Este cliente no es facturable",
"serial non editable": "Esta serie no permite asignar la referencia",
"Max shipped required": "La fecha límite es requerida",
"Can't invoice to future": "No se puede facturar a futuro",
"Can't invoice to past": "No se puede facturar a pasado",
"This ticket is already invoiced": "Este ticket ya está facturado",
"A ticket with an amount of zero can't be invoiced": "No se puede facturar un ticket con importe cero",
"A ticket with a negative base can't be invoiced": "No se puede facturar un ticket con una base negativa",
"Global invoicing failed": "[Facturación global] No se han podido facturar algunos clientes",
"Wasn't able to invoice the following clients": "No se han podido facturar los siguientes clientes",
"Can't verify data unless the client has a business type": "No se puede verificar datos de un cliente que no tiene tipo de negocio",
"You don't have enough privileges to set this credit amount": "No tienes suficientes privilegios para establecer esta cantidad de crédito",
"You can't change the credit set to zero from a financialBoss": "No puedes cambiar el cŕedito establecido a cero por un jefe de finanzas",
"Amounts do not match": "Las cantidades no coinciden",
"The PDF document does not exist": "El documento PDF no existe. Prueba a regenerarlo desde la opción 'Regenerar PDF factura'",
"The type of business must be filled in basic data": "El tipo de negocio debe estar rellenado en datos básicos",
"You can't create a claim from a ticket delivered more than seven days ago": "No puedes crear una reclamación de un ticket entregado hace más de siete días",
"The worker has hours recorded that day": "El trabajador tiene horas fichadas ese día",
"The worker has a marked absence that day": "El trabajador tiene marcada una ausencia ese día",
"You can not modify is pay method checked": "No se puede modificar el campo método de pago validado",
"The account size must be exactly 10 characters": "El tamaño de la cuenta debe ser exactamente de 10 caracteres",
"Can't transfer claimed sales": "No puedes transferir lineas reclamadas",
"You don't have privileges to create refund": "No tienes permisos para crear un abono",
"The item is required": "El artículo es requerido",
"The agency is already assigned to another autonomous": "La agencia ya está asignada a otro autónomo",
"date in the future": "Fecha en el futuro",
"reference duplicated": "Referencia duplicada",
"This ticket is already a refund": "Este ticket ya es un abono",
"isWithoutNegatives": "Sin negativos",
"routeFk": "routeFk",
"Can't change the password of another worker": "No se puede cambiar la contraseña de otro trabajador",
"No hay un contrato en vigor": "No hay un contrato en vigor",
"No se permite fichar a futuro": "No se permite fichar a futuro",
"No está permitido trabajar": "No está permitido trabajar",
"Fichadas impares": "Fichadas impares",
"Descanso diario 12h.": "Descanso diario 12h.",
"Descanso semanal 36h. / 72h.": "Descanso semanal 36h. / 72h.",
"Dirección incorrecta": "Dirección incorrecta",
"Modifiable user details only by an administrator": "Detalles de usuario modificables solo por un administrador",
"Modifiable password only via recovery or by an administrator": "Contraseña modificable solo a través de la recuperación o por un administrador",
"Not enough privileges to edit a client": "No tienes suficientes privilegios para editar un cliente",
"This route does not exists": "Esta ruta no existe",
"Claim pickup order sent": "Reclamación Orden de recogida enviada [{{claimId}}]({{{claimUrl}}}) al cliente *{{clientName}}*",
"You don't have grant privilege": "No tienes privilegios para dar privilegios",
"You don't own the role and you can't assign it to another user": "No eres el propietario del rol y no puedes asignarlo a otro usuario",
"Ticket merged": "Ticket [{{originId}}]({{{originFullPath}}}) ({{{originDated}}}) fusionado con [{{destinationId}}]({{{destinationFullPath}}}) ({{{destinationDated}}})",
"Already has this status": "Ya tiene este estado",
"There aren't records for this week": "No existen registros para esta semana",
"Empty data source": "Origen de datos vacio",
"App locked": "Aplicación bloqueada por el usuario {{userId}}",
"Email verify": "Correo de verificación",
"Landing cannot be lesser than shipment": "Landing cannot be lesser than shipment",
"Receipt's bank was not found": "No se encontró el banco del recibo",
"This receipt was not compensated": "Este recibo no ha sido compensado",
"Client's email was not found": "No se encontró el email del cliente",
"Negative basis": "Base negativa",
"This worker code already exists": "Este codigo de trabajador ya existe",
"This personal mail already exists": "Este correo personal ya existe",
"This worker already exists": "Este trabajador ya existe",
"App name does not exist": "El nombre de aplicación no es válido",
"Try again": "Vuelve a intentarlo",
"Aplicación bloqueada por el usuario 9": "Aplicación bloqueada por el usuario 9",
"Failed to upload delivery note": "Error al subir albarán {{id}}",
"The DOCUWARE PDF document does not exists": "El documento PDF Docuware no existe",
"It is not possible to modify tracked sales": "No es posible modificar líneas de pedido que se hayan empezado a preparar",
"It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo",
"It is not possible to modify cloned sales": "No es posible modificar líneas de pedido clonadas",
"A supplier with the same name already exists. Change the country.": "Un proveedor con el mismo nombre ya existe. Cambie el país.",
"There is no assigned email for this client": "No hay correo asignado para este cliente",
"Exists an invoice with a future date": "Existe una factura con fecha posterior",
"Invoice date can't be less than max date": "La fecha de factura no puede ser inferior a la fecha límite",
"Warehouse inventory not set": "El almacén inventario no está establecido",
"This locker has already been assigned": "Esta taquilla ya ha sido asignada",
"Tickets with associated refunds": "No se pueden borrar tickets con abonos asociados. Este ticket está asociado al abono Nº %s",
"Not exist this branch": "La rama no existe",
"This ticket cannot be signed because it has not been boxed": "Este ticket no puede firmarse porque no ha sido encajado",
"Collection does not exist": "La colección no existe",
"Cannot obtain exclusive lock": "No se puede obtener un bloqueo exclusivo",
"Insert a date range": "Inserte un rango de fechas",
"Added observation": "{{user}} añadió esta observacion: {{text}} {{defaulterId}} ({{{defaulterUrl}}})",
"Comment added to client": "Observación añadida al cliente {{clientFk}}",
"Invalid auth code": "Código de verificación incorrecto",
"Invalid or expired verification code": "Código de verificación incorrecto o expirado",
"Cannot create a new claimBeginning from a different ticket": "No se puede crear una línea de reclamación de un ticket diferente al origen",
"company": "Compañía",
"country": "País",
"clientId": "Id cliente",
"clientSocialName": "Cliente",
"amount": "Importe",
"taxableBase": "Base",
"ticketFk": "Id ticket",
"isActive": "Activo",
"hasToInvoice": "Facturar",
"isTaxDataChecked": "Datos comprobados",
"comercialId": "Id comercial",
"comercialName": "Comercial",
"Pass expired": "La contraseña ha caducado, cambiela desde Salix",
"Invalid NIF for VIES": "Invalid NIF for VIES",
"Ticket does not exist": "Este ticket no existe",
"Ticket is already signed": "Este ticket ya ha sido firmado",
"Authentication failed": "Autenticación fallida",
"You can't use the same password": "No puedes usar la misma contraseña",
"You can only add negative amounts in refund tickets": "Solo se puede añadir cantidades negativas en tickets abono",
"Fecha fuera de rango": "Fecha fuera de rango",
"Error while generating PDF": "Error al generar PDF",
"Error when sending mail to client": "Error al enviar el correo al cliente",
"Mail not sent": "Se ha producido un fallo al enviar la factura al cliente [{{clientId}}]({{{clientUrl}}}), por favor revisa la dirección de correo electrónico",
"The renew period has not been exceeded": "El periodo de renovación no ha sido superado",
"Valid priorities": "Prioridades válidas: %d",
"hasAnyNegativeBase": "Base negativa para los tickets: {{ticketsIds}}",
"hasAnyPositiveBase": "Base positivas para los tickets: {{ticketsIds}}",
"You cannot assign an alias that you are not assigned to": "No puede asignar un alias que no tenga asignado",
"This ticket cannot be left empty.": "Este ticket no se puede dejar vacío. %s",
"The company has not informed the supplier account for bank transfers": "La empresa no tiene informado la cuenta de proveedor para transferencias bancarias",
"You cannot assign/remove an alias that you are not assigned to": "No puede asignar/eliminar un alias que no tenga asignado",
"This invoice has a linked vehicle.": "Esta factura tiene un vehiculo vinculado",
"You don't have enough privileges.": "No tienes suficientes permisos.",
"This ticket is locked": "Este ticket está bloqueado.",
"This ticket is not editable.": "Este ticket no es editable.",
"The ticket doesn't exist.": "No existe el ticket.",
"Social name should be uppercase": "La razón social debe ir en mayúscula",
"Street should be uppercase": "La dirección fiscal debe ir en mayúscula",
"Ticket without Route": "Ticket sin ruta",
"Select a different client": "Seleccione un cliente distinto",
"Fill all the fields": "Rellene todos los campos",
"The response is not a PDF": "La respuesta no es un PDF",
"Booking completed": "Reserva completada",
"The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación",
"The notification subscription of this worker cant be modified": "La subscripción a la notificación de este trabajador no puede ser modificada",
"User disabled": "Usuario desactivado",
"The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mínima",
"quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mínima",
"Cannot past travels with entries": "No se pueden pasar envíos con entradas",
"It was not able to remove the next expeditions:": "No se pudo eliminar las siguientes expediciones: {{expeditions}}",
"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",
"Field are invalid": "El campo '{{tag}}' no es válido",
"Incorrect pin": "Pin incorrecto.",
"You already have the mailAlias": "Ya tienes este alias de correo",
"The alias cant be modified": "Este alias de correo no puede ser modificado",
"No tickets to invoice": "No hay tickets para facturar que cumplan los requisitos de facturación",
"this warehouse has not dms": "El Almacén no acepta documentos",
"This ticket already has a cmr saved": "Este ticket ya tiene un cmr guardado",
"Name should be uppercase": "El nombre debe ir en mayúscula",
"Bank entity must be specified": "La entidad bancaria es obligatoria",
"An email is necessary": "Es necesario un email",
"You cannot update these fields": "No puedes actualizar estos campos",
"CountryFK cannot be empty": "El país no puede estar vacío",
"Cmr file does not exist": "El archivo del cmr no existe",
"You are not allowed to modify the alias": "No estás autorizado a modificar el alias",
"The address of the customer must have information about Incoterms and Customs Agent": "El consignatario del cliente debe tener informado Incoterms y Agente de aduanas",
"No invoice series found for these parameters": "No se encontró una serie para estos parámetros",
"The line could not be marked": "La linea no puede ser marcada",
"Through this procedure, it is not possible to modify the password of users with verified email": "Mediante este procedimiento, no es posible modificar la contraseña de usuarios con correo verificado",
"They're not your subordinate": "No es tu subordinado/a.",
"No results found": "No se han encontrado resultados",
"InvoiceIn is already booked": "La factura recibida está contabilizada",
"This workCenter is already assigned to this agency": "Este centro de trabajo ya está asignado a esta agencia",
"Select ticket or client": "Elija un ticket o un client",
"It was not able to create the invoice": "No se pudo crear la factura",
"Incoterms and Customs agent are required for a non UEE member": "Se requieren Incoterms y agente de aduanas para un no miembro de la UEE",
"You can not use the same password": "No puedes usar la misma contraseña",
"This PDA is already assigned to another user": "Este PDA ya está asignado a otro usuario",
"You can only have one PDA": "Solo puedes tener un PDA",
"The invoices have been created but the PDFs could not be generated": "Se ha facturado pero no se ha podido generar el PDF",
"It has been invoiced but the PDF of refund not be generated": "Se ha facturado pero no se ha podido generar el PDF del abono",
"Payment method is required": "El método de pago es obligatorio",
"Cannot send mail": "Não é possível enviar o email",
"CONSTRAINT `supplierAccountTooShort` failed for `vn`.`supplier`": "La cuenta debe tener exactamente 10 dígitos",
"The sale not exists in the item shelving": "La venta no existe en la estantería del artículo",
"The entry not have stickers": "La entrada no tiene etiquetas",
"Too many records": "Demasiados registros",
"Original invoice not found": "Factura original no encontrada",
"The entry has no lines or does not exist": "La entrada no tiene lineas o no existe",
"Weight already set": "El peso ya está establecido",
"This ticket is not allocated to your department": "Este ticket no está asignado a tu departamento",
"There is already a tray with the same height": "Ya existe una bandeja con la misma altura",
"The height must be greater than 50cm": "La altura debe ser superior a 50cm",
"The maximum height of the wagon is 200cm": "La altura máxima es 200cm",
"The entry does not have stickers": "La entrada no tiene etiquetas",
"This buyer has already made a reservation for this date": "Este comprador ya ha hecho una reserva para esta fecha",
"No valid travel thermograph found": "No se encontró un termógrafo válido",
"The quantity claimed cannot be greater than the quantity of the line": "La cantidad reclamada no puede ser mayor que la cantidad de la línea",
"type cannot be blank": "Se debe rellenar el tipo",
"There are tickets for this area, delete them first": "Hay tickets para esta sección, borralos primero",
"There is no company associated with that warehouse": "No hay ninguna empresa asociada a ese almacén",
"You do not have permission to modify the booked field": "No tienes permisos para modificar el campo contabilizada",
"ticketLostExpedition": "El ticket [{{ticketId}}]({{{ticketUrl}}}) tiene la siguiente expedición perdida:{{ expeditionId }}",
"The web user's email already exists": "El correo del usuario web ya existe",
"Sales already moved": "Ya han sido transferidas",
"The raid information is not correct": "La información de la redada no es correcta",
"An item type with the same code already exists": "Un tipo con el mismo código ya existe",
"Holidays to past days not available": "Las vacaciones a días pasados no están disponibles",
"All tickets have a route order": "Todos los tickets tienen orden de ruta",
"There are tickets to be invoiced": "La zona tiene tickets por facturar",
"Incorrect delivery order alert on route": "Alerta de orden de entrega incorrecta en ruta: {{ route }} zona: {{ zone }}",
"Ticket has been delivered out of order": "El ticket {{ticket}} {{{fullUrl}}} no ha sido entregado en su orden.",
"Price cannot be blank": "El precio no puede estar en blanco",
"clonedFromTicketWeekly": ", que es una linea clonada del ticket {{ticketWeekly}}",
"negativeReplaced": "Sustituido el articulo [#{{oldItemId}}]({{{oldItemUrl}}}) {{oldItem}} por [#{{newItemId}}]({{{newItemUrl}}}) {{newItem}} del ticket [{{ticketId}}]({{{ticketUrl}}})",
"The introduced warehouse already exists": "El almacén seleccionado ya existe en la zona",
"The code already exists": "El código ya existe"
}

View File

@ -74,66 +74,77 @@ module.exports = Self => {
}
try {
const worker = await models.Worker.findOne({
where: {id: userId}
}, myOptions);
const obsevationType = await models.ObservationType.findOne({
where: {code: 'salesPerson'}
}, myOptions);
const agencyMode = await models.AgencyMode.findOne({
where: {code: 'refund'}
}, myOptions);
const state = await models.State.findOne({
where: {code: 'DELIVERED'}
}, myOptions);
const zone = await models.Zone.findOne({
where: {agencyModeFk: agencyMode.id}
}, myOptions);
const claim = await models.Claim.findOne(filter, myOptions);
const today = Date.vnNew();
let agencyModeFk;
let nickname;
let state;
let discountValue = null;
let packages = 0;
const claimConfig = await models.ClaimConfig.findOne();
const warehouseFk = claimConfig.warehouseFk;
if (claim.pickup === null || claim.pickup == 'agency') {
state = await models.State.findOne({
where: {code: 'DELIVERED'}
}, myOptions);
const agencyMode = await models.AgencyMode.findOne({
where: {code: 'refund'}
}, myOptions);
agencyModeFk = agencyMode.id;
nickname = `Abono del: ${claim.ticketFk}`;
} else {
discountValue = 100;
packages = 1;
state = await models.State.findOne({
where: {code: 'WAITING_FOR_PICKUP'}
}, myOptions);
nickname = `Recogida pendiente del: ${claim.ticketFk}`;
agencyModeFk = claimConfig.pickupDeliveryFk;
}
const nextShipped = await models.Agency.getShipped(
ctx, today, claim.ticket().addressFk, agencyModeFk, warehouseFk, myOptions
);
const newRefundTicket = await models.Ticket.create({
clientFk: claim.ticket().clientFk,
shipped: today,
landed: today,
nickname: `Abono del: ${claim.ticketFk}`,
warehouseFk: claim.ticket().warehouseFk,
shipped: nextShipped.shipped,
landed: null,
nickname,
warehouseFk,
companyFk: claim.ticket().companyFk,
addressFk: claim.ticket().addressFk,
agencyModeFk: agencyMode.id,
zoneFk: zone.id
agencyModeFk,
zoneFk: claim.ticket().zoneFk,
packages
}, myOptions);
if (claim.pickup == 'pickup') {
const observationDelivery =
await models.ObservationType.findOne({where: {code: 'delivery'}}, myOptions);
await saveObservation({
description: `recoger reclamación: ${claim.id}`,
ticketFk: newRefundTicket.id,
observationTypeFk: observationDelivery.id
}, myOptions);
}
await models.TicketRefund.create({
refundTicketFk: newRefundTicket.id,
originalTicketFk: claim.ticket().id
}, myOptions);
await saveObservation({
description: `Reclama ticket: ${claim.ticketFk}`,
ticketFk: newRefundTicket.id,
observationTypeFk: obsevationType.id
}, myOptions);
const salesToRefund = await models.ClaimBeginning.find(salesFilter, myOptions);
const createdSales = await addSalesToTicket(salesToRefund, newRefundTicket.id, discountValue, myOptions);
await insertIntoClaimEnd(createdSales, id, userId, myOptions);
await models.Ticket.state(ctx, {
ticketFk: newRefundTicket.id,
stateFk: state.id,
userFk: worker.id
userFk: userId
}, myOptions);
const salesToRefund = await models.ClaimBeginning.find(salesFilter, myOptions);
const createdSales = await addSalesToTicket(salesToRefund, newRefundTicket.id, myOptions);
await insertIntoClaimEnd(createdSales, id, worker.id, myOptions);
await Self.rawSql('CALL vn.ticketCalculateClon(?, ?)', [
newRefundTicket.id, claim.ticketFk
], myOptions);
if (tx) await tx.commit();
return newRefundTicket;
@ -143,23 +154,36 @@ module.exports = Self => {
}
};
async function addSalesToTicket(salesToRefund, ticketId, options) {
let formatedSales = [];
salesToRefund.forEach(sale => {
let formatedSale = {
itemFk: sale.sale().itemFk,
ticketFk: ticketId,
concept: sale.sale().concept,
quantity: -Math.abs(sale.quantity),
price: sale.sale().price,
discount: sale.sale().discount,
reserved: sale.sale().reserved,
isPicked: sale.sale().isPicked,
created: sale.sale().created
async function addSalesToTicket(salesToRefund, newTicketId, discountValue, options) {
const createdSales = [];
const models = Self.app.models;
for (const saleToRefund of salesToRefund) {
const oldSale = saleToRefund.sale();
const newSaleData = {
itemFk: oldSale.itemFk,
ticketFk: newTicketId,
concept: oldSale.concept,
quantity: -Math.abs(saleToRefund.quantity),
price: oldSale.price,
discount: discountValue ?? oldSale.discount,
reserved: oldSale.reserved,
isPicked: oldSale.isPicked,
created: oldSale.created
};
formatedSales.push(formatedSale);
});
return await Self.app.models.Sale.create(formatedSales, options);
const newSale = await models.Sale.create(newSaleData, options);
const oldSaleComponents = await models.SaleComponent.find({
where: {saleFk: oldSale.id}
}, options);
const newComponents = oldSaleComponents.map(component => {
const data = component.toJSON ? component.toJSON() : {...component};
delete data.id;
data.saleFk = newSale.id;
return data;
});
await models.SaleComponent.create(newComponents, options);
createdSales.push(newSale);
}
return createdSales;
}
async function insertIntoClaimEnd(createdSales, claimId, workerId, options) {

View File

@ -1,44 +0,0 @@
const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
const models = app.models;
describe('claimBeginning', () => {
const claimManagerId = 72;
const activeCtx = {
accessToken: {userId: claimManagerId},
__: value => value
};
const ctx = {req: activeCtx};
describe('importToNewRefundTicket()', () => {
it('should create a new ticket with negative sales and insert the negative sales into claimEnd', async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
let claimId = 1;
const tx = await models.Entry.beginTransaction({});
try {
const options = {transaction: tx};
const ticket = await models.ClaimBeginning.importToNewRefundTicket(ctx, claimId, options);
const refundTicketSales = await models.Sale.find({
where: {ticketFk: ticket.id}
}, options);
const salesInsertedInClaimEnd = await models.ClaimEnd.find({
where: {claimFk: claimId}
}, options);
expect(refundTicketSales.length).toEqual(1);
expect(refundTicketSales[0].quantity).toEqual(-5);
expect(salesInsertedInClaimEnd[0].saleFk).toEqual(refundTicketSales[0].id);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});
});

View File

@ -0,0 +1,99 @@
const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
const models = app.models;
describe('importToNewRefundTicket()', () => {
let tx;
const claimManagerId = 72;
const activeCtx = {
accessToken: {userId: claimManagerId},
};
let ctx = {req: activeCtx};
let options;
const claimId = 1;
const expectedDate = Date.vnNew();
beforeEach(async() => {
LoopBackContext.getCurrentContext = () => ({
active: activeCtx,
});
tx = await models.Entry.beginTransaction({});
options = {transaction: tx};
spyOn(models.Agency, 'getShipped').and.returnValue(Promise.resolve({shipped: expectedDate}));
});
afterEach(async() => {
await tx.rollback();
});
it('should create a new ticket with negative sales and insert the negative sales into claimEnd', async() => {
const ticket = await models.ClaimBeginning.importToNewRefundTicket(ctx, claimId, options);
const refundTicketSales = await models.Sale.find({
where: {ticketFk: ticket.id}
}, options);
const salesInsertedInClaimEnd = await models.ClaimEnd.find({
where: {claimFk: claimId}
}, options);
expect(refundTicketSales.length).toEqual(1);
expect(refundTicketSales[0].quantity).toEqual(-5);
expect(salesInsertedInClaimEnd[0].saleFk).toEqual(refundTicketSales[0].id);
});
it('should set DELIVERED state and refund agency mode', async() => {
const state = await models.State.findOne({
where: {code: 'DELIVERED'}
}, options);
const ticket = await models.ClaimBeginning.importToNewRefundTicket(ctx, claimId, options);
const ticketTracking = await models.TicketTracking.findOne({
where: {ticketFk: ticket.id},
order: 'id DESC'
}, options);
const newSales = await models.Sale.find({
where: {ticketFk: ticket.id}
}, options);
newSales.forEach(sale => {
expect(sale.discount).toEqual(0);
});
expect(ticketTracking.stateFk).toEqual(state.id);
});
it('should set WAITING_FOR_PICKUP state for delivery pickups', async() => {
const state = await models.State.findOne({
where: {code: 'WAITING_FOR_PICKUP'}
}, options);
await models.Claim.updateAll({id: claimId}, {pickup: 'delivery'}, options);
const ticket = await models.ClaimBeginning.importToNewRefundTicket(ctx, claimId, options);
const ticketTracking = await models.TicketTracking.findOne({
where: {ticketFk: ticket.id},
order: 'id DESC'
}, options);
const newSales = await models.Sale.find({
where: {ticketFk: ticket.id}
}, options);
newSales.forEach(sale => {
expect(sale.discount).toEqual(100);
});
expect(ticketTracking.stateFk).toEqual(state.id);
});
it('should set DELIVERED state for agency pickups', async() => {
const state = await models.State.findOne({
where: {code: 'DELIVERED'}
}, options);
await models.Claim.updateAll({id: claimId}, {pickup: 'agency'}, options);
const ticket = await models.ClaimBeginning.importToNewRefundTicket(ctx, claimId, options);
const ticketTracking = await models.TicketTracking.findOne({
where: {ticketFk: ticket.id},
order: 'id DESC'
}, options);
expect(ticketTracking.stateFk).toEqual(state.id);
});
});

View File

@ -40,21 +40,24 @@ module.exports = Self => {
const stmt = new ParameterizedSQL(
`SELECT *
FROM (
SELECT
SELECT
ce.id,
ce.claimFk,
s.itemFk,
s.ticketFk,
ce.claimDestinationFk,
t.landed,
s.quantity,
s.concept,
s.price,
s.itemFk,
s.ticketFk,
ce.claimDestinationFk,
t.landed,
s.quantity,
s.concept,
s.price,
s.discount,
s.quantity * s.price * ((100 - s.discount) / 100) total
s.quantity * s.price * ((100 - s.discount) / 100) total,
ce.shelvingFk,
sh.code shelvingCode
FROM vn.claimEnd ce
LEFT JOIN vn.sale s ON s.id = ce.saleFk
LEFT JOIN vn.sale s ON s.id = ce.saleFk
LEFT JOIN vn.ticket t ON t.id = s.ticketFk
LEFT JOIN vn.shelving sh ON sh.id = ce.shelvingFk
) ce`
);

View File

@ -3,12 +3,14 @@ module.exports = Self => {
description: `Imports lines from claimBeginning to a new ticket
with specific shipped, landed dates, agency and company`,
accessType: 'WRITE',
accepts: [{
arg: 'id',
type: 'number',
description: 'The claim id',
http: {source: 'path'}
}],
accepts: [
{
arg: 'id',
type: 'number',
description: 'The claim id',
http: {source: 'path'}
}
],
returns: {
type: ['Object'],
root: true
@ -22,8 +24,6 @@ module.exports = Self => {
Self.regularizeClaim = async(ctx, claimFk, options) => {
const models = Self.app.models;
const $t = ctx.req.__; // $translate
const resolvedState = 3;
let tx;
const myOptions = {};
@ -37,25 +37,39 @@ module.exports = Self => {
try {
const claimEnds = await models.ClaimEnd.find({
include: {
relation: 'claimDestination',
fields: ['addressFk']
},
where: {claimFk: claimFk}
where: {claimFk: claimFk},
include: [
{
relation: 'claimDestination',
scope: {fields: ['addressFk']}
},
{
relation: 'shelving',
scope: {
fields: ['code', 'parkingFk'],
include: {
relation: 'parking',
scope: {
fields: ['sectorFk'],
include: {
relation: 'sector',
scope: {fields: ['warehouseFk']}
}
}
}
}
}
]
}, myOptions);
for (let claimEnd of claimEnds) {
const destination = claimEnd.claimDestination();
const sale = await getSale(claimEnd.saleFk, myOptions);
const addressId = destination && destination.addressFk;
let address;
if (addressId)
address = await models.Address.findById(addressId, null, myOptions);
const addressId = destination?.addressFk;
const salesPerson = sale.ticket().client().salesPersonUser();
if (salesPerson) {
const nickname = address && address.nickname || destination.description;
const nickname = destination.description;
const url = await Self.app.models.Url.getUrl();
const message = $t('Sent units from ticket', {
quantity: sale.quantity,
@ -69,18 +83,21 @@ module.exports = Self => {
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
}
if (!address) continue;
if (!addressId) continue;
const warehouseFk = claimEnd.shelving().parking().sector().warehouseFk;
const address = await models.Address.findById(addressId, null, myOptions);
let ticketFk = await getTicketId({
addressFk: addressId,
companyFk: sale.ticket().companyFk,
warehouseFk: sale.ticket().warehouseFk
warehouseFk: warehouseFk
}, myOptions);
if (!ticketFk) {
ctx.args = {
clientId: address.clientFk,
warehouseId: sale.ticket().warehouseFk,
warehouseId: warehouseFk,
companyId: sale.ticket().companyFk,
addressId: addressId
};
@ -90,15 +107,43 @@ module.exports = Self => {
ticketFk: ticketFk,
itemFk: sale.itemFk,
concept: sale.concept,
quantity: -sale.quantity,
quantity: sale.quantity,
price: sale.price,
discount: 100
}, myOptions);
const [buyFk] = await Self.rawSql('SELECT vn.buy_getLastWithoutInventory(?, ?) buyFk',
[sale.itemFk, warehouseFk], myOptions
);
await Self.rawSql('CALL vn.itemShelving_add(?, ?, ?, NULL, NULL, NULL, ?)',
[claimEnd.shelving().code, buyFk.buyFk, -sale.quantity, warehouseFk],
myOptions
);
const operator = await models.Operator.findById(
ctx.req.accessToken.userId, {fields: ['labelerFk']}, myOptions);
const params = JSON.stringify({
copies: 1,
id: buyFk.buyFk,
labelType: 'qr'
});
await Self.rawSql(`CALL vn.report_print( ?, ?, ?, ?, ?)`,
['LabelBuy',
operator?.labelerFk,
ctx.req.accessToken.userId,
params,
'normal'
],
myOptions);
}
const resolvedState = await models.ClaimState.findOne({
where: {code: 'resolved'}
}, myOptions);
let claim = await Self.findById(claimFk, null, myOptions);
claim = await claim.updateAttributes({
claimStateFk: resolvedState
claimStateFk: resolvedState.id
}, myOptions);
if (tx) await tx.commit();
@ -151,7 +196,7 @@ module.exports = Self => {
}
}, options);
return ticket && ticket.id;
return ticket?.id;
}
async function createTicket(ctx, options) {

View File

@ -1,11 +1,8 @@
const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('claim regularizeClaim()', () => {
const userId = 18;
const ctx = beforeAll.mockLoopBackContext(userId);
ctx.req.__ = (value, params) => {
return params.nickname;
};
const userId = 72;
const chatModel = models.Chat;
const claimId = 1;
const ticketId = 1;
@ -13,9 +10,27 @@ describe('claim regularizeClaim()', () => {
const resolvedState = 3;
const trashDestination = 2;
const okDestination = 1;
const trashAddress = 12;
let claimEnds = [];
let trashTicket;
const activeCtx = {accessToken: {userId}};
let ctx = {req: activeCtx};
let tx;
let options;
beforeEach(async() => {
LoopBackContext.getCurrentContext = () => ({
active: activeCtx,
});
ctx.req.__ = (_value, params) => {
return params.nickname;
};
tx = await models.Claim.beginTransaction({});
options = {transaction: tx};
});
afterEach(async() => {
await tx.rollback();
});
async function importTicket(ticketId, claimId, userId, options) {
const ticketSales = await models.Sale.find({
@ -34,84 +49,73 @@ describe('claim regularizeClaim()', () => {
}
it('should send a chat message with value "Trash" and then change claim state to resolved', async() => {
const tx = await models.Claim.beginTransaction({});
spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
try {
const options = {transaction: tx};
claimEnds = await importTicket(ticketId, claimId, userId, options);
spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
for (const claimEnd of claimEnds)
await claimEnd.updateAttributes({claimDestinationFk: trashDestination}, options);
claimEnds = await importTicket(ticketId, claimId, userId, options);
let claimBefore = await models.Claim.findById(claimId, null, options);
await models.Claim.regularizeClaim(ctx, claimId, options);
let claimAfter = await models.Claim.findById(claimId, null, options);
for (const claimEnd of claimEnds)
await claimEnd.updateAttributes({claimDestinationFk: trashDestination}, options);
let claimBefore = await models.Claim.findById(claimId, null, options);
await models.Claim.regularizeClaim(ctx, claimId, options);
let claimAfter = await models.Claim.findById(claimId, null, options);
trashTicket = await models.Ticket.findOne({where: {addressFk: 12}}, options);
expect(trashTicket.addressFk).toEqual(trashAddress);
expect(claimBefore.claimStateFk).toEqual(pendentState);
expect(claimAfter.claimStateFk).toEqual(resolvedState);
expect(chatModel.sendCheckingPresence).toHaveBeenCalledWith(ctx, 18, 'Trash');
expect(chatModel.sendCheckingPresence).toHaveBeenCalledTimes(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
expect(claimBefore.claimStateFk).toEqual(pendentState);
expect(claimAfter.claimStateFk).toEqual(resolvedState);
});
it('should send a chat message with value "Bueno" and then change claim state to resolved', async() => {
const tx = await models.Claim.beginTransaction({});
it('should change claim state to resolved', async() => {
const addressMissingFk = 11;
const shelvingFk = 1;
const warehouseFk = 6;
const minDate = Date.vnNew();
minDate.setHours(0, 0, 0, 0);
try {
const options = {transaction: tx};
const maxDate = Date.vnNew();
maxDate.setHours(23, 59, 59, 59);
spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
claimEnds = await importTicket(ticketId, claimId, userId, options);
claimEnds = await importTicket(ticketId, claimId, userId, options);
for (claimEnd of claimEnds)
await claimEnd.updateAttributes({claimDestinationFk: okDestination}, options);
for (let claimEnd of claimEnds)
await claimEnd.updateAttributes({claimDestinationFk: okDestination, shelvingFk}, options);
await models.Claim.regularizeClaim(ctx, claimId, options);
const sale = await models.Sale.findOne({
include: [
{
relation: 'ticket',
scope: {
fields: ['clientFk', 'companyFk']
}
}],
where: {id: claimEnds[0].saleFk}
}, options);
expect(chatModel.sendCheckingPresence).toHaveBeenCalledWith(ctx, 18, 'Bueno');
expect(chatModel.sendCheckingPresence).toHaveBeenCalledTimes(4);
await models.Claim.regularizeClaim(ctx, claimId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
const ticket = await models.Ticket.findOne({
where: {
addressFk: addressMissingFk,
companyFk: sale.ticket().companyFk,
warehouseFk: warehouseFk,
shipped: {between: [minDate, maxDate]},
landed: {between: [minDate, maxDate]}
}
}, options);
it('should send a chat message to the salesPerson when claim isPickUp is enabled', async() => {
const tx = await models.Claim.beginTransaction({});
const missingSale = await models.Sale.findOne({
where: {
ticketFk: ticket.id
}
}, options);
try {
const options = {transaction: tx};
const claimBeginning = await models.ClaimBeginning.findOne({
where: {
claimFk: claimId
}
}, options);
spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
claimEnds = await importTicket(ticketId, claimId, userId, options);
for (claimEnd of claimEnds)
await claimEnd.updateAttributes({claimDestinationFk: okDestination}, options);
await models.Claim.regularizeClaim(ctx, claimId, options);
expect(chatModel.sendCheckingPresence).toHaveBeenCalledWith(ctx, 18, 'Bueno');
expect(chatModel.sendCheckingPresence).toHaveBeenCalledTimes(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
expect(missingSale.quantity).toBe(claimBeginning.quantity);
});
});

View File

@ -2,6 +2,9 @@
"Claim": {
"dataSource": "vn"
},
"ClaimConfig": {
"dataSource": "vn"
},
"ClaimContainer": {
"dataSource": "claimStorage"
},
@ -43,5 +46,5 @@
},
"ClaimObservation": {
"dataSource": "vn"
}
}
}

View File

@ -0,0 +1,42 @@
{
"name": "ClaimConfig",
"base": "VnModel",
"mixins": {
"Loggable": true
},
"options": {
"mysql": {
"table": "claimConfig"
}
},
"properties": {
"id": {
"type": "number",
"id": true,
"description": "Identifier"
},
"maxResponsibility": {
"type": "number"
},
"monthsToRefund": {
"type": "number"
},
"minShipped": {
"type": "date"
},
"pickupdeliveryFk": {
"type": "number"
},
"warehouseFk": {
"type": "number"
}
},
"relations": {
"pickupDelivery": {
"type": "belongsTo",
"model": "AgencyMode",
"foreignKey": "pickupdeliveryFk"
}
}
}

View File

@ -41,6 +41,11 @@
"type": "belongsTo",
"model": "ClaimDestination",
"foreignKey": "claimDestinationFk"
},
"shelving": {
"type": "belongsTo",
"model": "Shelving",
"foreignKey": "shelvingFk"
}
}
}

View File

@ -43,6 +43,17 @@ module.exports = function(Self) {
password: String(Math.random() * 100000000000000)
};
const supplier = await models.Supplier.findOne({
where: {nif: data.fi}
});
const role = supplier ? await models.VnRole.findOne({
where: {name: 'supplier'}
}) : null;
if (role)
user.roleFk = role.id;
try {
const province = await models.Province.findOne({
where: {id: data.provinceFk},

View File

@ -48,11 +48,11 @@ describe('Client Create', () => {
expect(error.message).toEqual('An email is necessary');
});
it('should create a new account with dailyInvoice', async() => {
it('should create a new account with dailyInvoice and role supplier', async() => {
const newAccount = {
userName: 'deadpool',
email: 'deadpool@marvel.com',
fi: '16195279J',
fi: '07972486L',
name: 'Wade',
socialName: 'DEADPOOL MARVEL',
street: 'WALL STREET',
@ -61,33 +61,30 @@ describe('Client Create', () => {
provinceFk: 1
};
try {
const province = await models.Province.findById(newAccount.provinceFk, {
fields: ['id', 'name', 'autonomyFk'],
include: {
relation: 'autonomy'
}
}, options);
const province = await models.Province.findById(newAccount.provinceFk, {
fields: ['id', 'name', 'autonomyFk'],
include: {
relation: 'autonomy'
}
}, options);
const client = await models.Client.createWithUser(newAccount, options);
const account = await models.VnUser.findOne({where: {name: newAccount.userName}}, options);
const client = await models.Client.createWithUser(newAccount, options);
const account = await models.VnUser.findOne({where: {name: newAccount.userName}}, options);
const supplierRole = await models.VnRole.findOne({where: {name: 'supplier'}}, options);
expect(province.autonomy().hasDailyInvoice).toBeTruthy();
expect(account.name).toEqual(newAccount.userName);
expect(client.id).toEqual(account.id);
expect(client.name).toEqual(newAccount.name);
expect(client.email).toEqual(newAccount.email);
expect(client.fi).toEqual(newAccount.fi);
expect(client.socialName).toEqual(newAccount.socialName);
expect(client.businessTypeFk).toEqual(newAccount.businessTypeFk);
expect(client.hasDailyInvoice).toBeTruthy();
} catch (e) {
await tx.rollback();
throw e;
}
expect(account.roleFk).toEqual(supplierRole.id);
expect(province.autonomy().hasDailyInvoice).toBeTruthy();
expect(account.name).toEqual(newAccount.userName);
expect(client.id).toEqual(account.id);
expect(client.name).toEqual(newAccount.name);
expect(client.email).toEqual(newAccount.email);
expect(client.fi).toEqual(newAccount.fi);
expect(client.socialName).toEqual(newAccount.socialName);
expect(client.businessTypeFk).toEqual(newAccount.businessTypeFk);
expect(client.hasDailyInvoice).toBeTruthy();
});
it('should create a new account without dailyInvoice', async() => {
it('should create a new account without dailyInvoice and role customer', async() => {
const newAccount = {
userName: 'deadpool',
email: 'deadpool@marvel.com',
@ -100,22 +97,20 @@ describe('Client Create', () => {
provinceFk: 3
};
try {
const province = await models.Province.findById(newAccount.provinceFk, {
fields: ['id', 'name', 'autonomyFk'],
include: {
relation: 'autonomy'
}
}, options);
const province = await models.Province.findById(newAccount.provinceFk, {
fields: ['id', 'name', 'autonomyFk'],
include: {
relation: 'autonomy'
}
}, options);
const client = await models.Client.createWithUser(newAccount, options);
const client = await models.Client.createWithUser(newAccount, options);
const vnUser = await models.VnUser.findOne({where: {name: newAccount.userName}}, options);
const customerRole = await models.VnRole.findOne({where: {name: 'customer'}}, options);
expect(province.autonomy.hasDailyInvoice).toBeFalsy();
expect(client.hasDailyInvoice).toBeFalsy();
} catch (e) {
await tx.rollback();
throw e;
}
expect(vnUser.roleFk).toEqual(customerRole.id);
expect(province.autonomy.hasDailyInvoice).toBeFalsy();
expect(client.hasDailyInvoice).toBeFalsy();
});
it('should not be able to create a user if exists', async() => {

View File

@ -46,29 +46,24 @@ module.exports = Self => {
}
try {
const itemDestination = await models.ClaimDestination.findOne({
include: {
relation: 'address',
scope: {
fields: ['clientFk']
}
},
where: {description: 'Corregido'}
const addressWaste = await models.AddressWaste.findOne({
where: {type: 'fault'},
include: 'address'
}, myOptions);
const item = await models.Item.findById(itemFk, null, myOptions);
let ticketId = await getTicketId({
clientFk: itemDestination.address.clientFk,
addressFk: itemDestination.addressFk,
clientFk: addressWaste.address().clientFk,
addressFk: addressWaste.addressFk,
warehouseFk: warehouseFk
}, myOptions);
if (!ticketId) {
ctx.args = {
clientId: itemDestination.address().clientFk,
clientId: addressWaste.address().clientFk,
warehouseId: warehouseFk,
addressId: itemDestination.addressFk
addressId: addressWaste.addressFk
};
ticketId = await createTicket(ctx, myOptions);
}
@ -121,7 +116,7 @@ module.exports = Self => {
}
}, options);
return ticket && ticket.id;
return ticket?.id;
}
};
};

View File

@ -0,0 +1,5 @@
module.exports = Self => {
Self.validatesUniquenessOf('code', {
message: `The code already exists`
});
};

View File

@ -1,4 +1,8 @@
module.exports = Self => {
require('../methods/shelving/getSummary')(Self);
require('../methods/shelving/addLog')(Self);
Self.validatesUniquenessOf('code', {
message: `The code already exists`
});
};

View File

@ -1,5 +1,6 @@
const smtp = require('vn-print/core/smtp');
const config = require('vn-print/core/config');
const Email = require('vn-print/core/email');
module.exports = Self => {
Self.remoteMethodCtx('closeAll', {
@ -44,8 +45,7 @@ module.exports = Self => {
LIMIT 1`, [toDate, toDate], myOptions);
await Self.rawSql(`
DROP TEMPORARY TABLE IF EXISTS tmp.ticket_close;
CREATE TEMPORARY TABLE tmp.ticket_close
CREATE OR REPLACE TEMPORARY TABLE tmp.ticket_close
ENGINE = MEMORY
WITH wTickets AS(
SELECT t.id ticketFk
@ -63,11 +63,12 @@ module.exports = Self => {
FROM wTicketsTracking wt
JOIN ticketTracking tt ON tt.id = wt.maxTracking
) SELECT tls.ticketFk,
t.clientFk,
t.clientFk clientId,
c.name clientName,
c.email recipient,
c.isToBeMailed,
eu.email salesPersonEmail,
t.addressFk,
t.addressFk addressId,
c.hasDailyInvoice,
c.hasToInvoiceByAddress,
t.totalWithVat,
@ -79,7 +80,7 @@ module.exports = Self => {
JOIN agencyMode am ON am.id = t.agencyModeFk
JOIN client c ON c.id = t.clientFk
LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk
WHERE (al.code = 'PACKED' OR (am.code = 'refund' AND al.code <> 'delivered'));
WHERE al.code = 'PACKED' OR (am.code = 'refund' AND al.code <> 'delivered');
CALL ticket_close();
`, [dateFrom, dateTo], myOptions);
@ -98,21 +99,27 @@ module.exports = Self => {
AND tob.id IS NULL
AND t.routeFk`, [dateFrom, dateTo], myOptions);
const [clients] = await Self.rawSql(`
SELECT clientFk clientId,
clientName,
recipient,
salesPersonEmail,
addressFk addressId,
companyFk,
const clients = await Self.rawSql(`
SELECT *,
SUM(totalWithVat) total,
'quick' serialType
FROM tmp.ticket_close
WHERE hasDailyInvoice
GROUP BY IF (hasToInvoiceByAddress, addressFk, clientFk), companyFk
HAVING total > 0;
GROUP BY IF (hasToInvoiceByAddress, addressId, clientId), companyFk
HAVING total > 0
`, [], myOptions);
const [ticketsToMail] = await Self.rawSql(`
SELECT *
FROM tmp.ticket_close
WHERE NOT hasDailyInvoice
AND isToBeMailed
AND salesPersonEmail IS NOT NULL
AND salesPersonEmail <> ''
AND recipient IS NOT NULL
AND recipient <> '';
DROP TEMPORARY TABLE tmp.ticket_close;
`, [], myOptions);
`, [], myOptions);
if (tx)
await tx.commit();
@ -130,21 +137,23 @@ module.exports = Self => {
if (id)
await Self.app.models.InvoiceOut.makePdfAndNotify(ctx, id, null, nestedTransaction);
} catch (error) {
await Self.rawSql(`
INSERT INTO util.debug (variable, value)
VALUES ('invoicingTicketError', ?)
`, [client.clientId + ' - ' + error]);
await handleInvoicingError(error, client, failedClients);
}
}
if (error.responseCode == 450) {
await invalidEmail(client);
continue;
}
for (const ticket of ticketsToMail) {
const args = {
id: ticket.ticketFk,
recipientId: ticket.clientId,
recipient: ticket.recipient,
replyTo: ticket.salesPersonEmail,
};
failedClients.push({
id: client.clientId,
address: client.addressId,
error
});
try {
const email = new Email('delivery-note-link', args);
await email.send();
} catch (error) {
await handleInvoicingError(error, ticket, failedClients);
}
}
@ -188,4 +197,22 @@ module.exports = Self => {
html: body,
}).catch(err => console.error(err));
}
async function handleInvoicingError(error, entity, failedClients) {
await Self.rawSql(`
INSERT INTO util.debug (variable, value)
VALUES ('invoicingTicketError', ?)
`, [entity.clientId + ' - ' + error]);
if (error.responseCode == 450) {
await invalidEmail(entity);
return;
}
failedClients.push({
id: entity.clientId,
address: entity.addressId,
error
});
}
};

View File

@ -33,6 +33,11 @@ module.exports = Self => {
type: 'date',
description: `The to date filter`
},
{
arg: 'shipped',
type: 'date',
description: `The shipped date filter`
},
{
arg: 'nickname',
type: 'string',
@ -201,6 +206,7 @@ module.exports = Self => {
case 'clientFk':
case 'agencyModeFk':
case 'warehouseFk':
case 'shipped':
param = `t.${param}`;
return {[param]: value};
}

View File

@ -0,0 +1,255 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const buildFilter = require('vn-loopback/util/filter').buildFilter;
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
module.exports = Self => {
Self.remoteMethodCtx('getWorkerBusiness', {
description: 'Returns an array of business from an specified worker',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'The worker id',
http: {source: 'path'}
},
{
arg: 'filter',
type: 'object',
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string'
},
{
arg: 'started',
type: 'date',
description: 'started',
http: {source: 'query'}
},
{
arg: 'ended',
type: 'date',
description: 'ended',
http: {source: 'query'}
},
{
arg: 'companyCodeFk',
type: 'string',
description: 'companyCodeFk',
http: {source: 'query'}
},
{
arg: 'reasonEndFk',
type: 'number',
description: 'reasonEndFk',
http: {source: 'query'}
},
{
arg: 'reasondEnd',
type: 'string',
description: 'reasondEnd',
http: {source: 'query'}
},
{
arg: 'departmentFk',
type: 'number',
description: 'departmentFk',
http: {source: 'query'}
},
{
arg: 'department',
type: 'string',
description: 'department',
http: {source: 'query'}
},
{
arg: 'workerBusinessProfessionalCategoryFk',
type: 'number',
description: 'workerBusinessProfessionalCategoryFk',
http: {source: 'query'}
},
{
arg: 'workerBusinessProfessionalCategory',
type: 'string',
description: 'workerBusinessProfessionalCategory',
http: {source: 'query'}
},
{
arg: 'calendarTypeFk',
type: 'number',
description: 'calendarTypeFk',
http: {source: 'query'}
},
{
arg: 'calendarType',
type: 'string',
description: 'calendarType',
http: {source: 'query'}
},
{
arg: 'workcenterFk',
type: 'number',
description: 'workcenterFk',
http: {source: 'query'}
},
{
arg: 'workCenter',
type: 'string',
description: 'workCenter',
http: {source: 'query'}
},
{
arg: 'workerBusinessCategoryFk',
type: 'number',
description: 'WorkerBusinessCategoryFk',
http: {source: 'query'}
},
{
arg: 'workerBusinessCategory',
type: 'string',
description: 'workerBusinessCategory',
http: {source: 'query'}
},
{
arg: 'occupationCodeFk',
type: 'number',
description: 'occupationCodeFk',
http: {source: 'query'}
},
{
arg: 'occupationCode',
type: 'string',
description: 'occupationCode',
http: {source: 'query'}
},
{
arg: 'rate',
type: 'number',
description: 'rate',
http: {source: 'query'}
},
{
arg: 'workerBusinessTypeFk',
type: 'number',
description: 'workerBusinessTypeFk',
http: {source: 'query'}
},
{
arg: 'workerBusinessType',
type: 'string',
description: 'workerBusinessType',
http: {source: 'query'}
},
{
arg: 'amount',
type: 'number',
description: 'amount',
http: {source: 'query'}
},
{
arg: 'basicSalary',
type: 'number',
description: 'amount',
http: {source: 'query'}
},
{
arg: 'notes',
type: 'string',
description: 'notes',
http: {source: 'query'}
}],
returns: {
type: ['object'],
root: true
},
http: {
path: '/:id/getWorkerBusiness',
verb: 'GET'
}
});
Self.getWorkerBusiness = async(ctx, id, filter, options) => {
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
let conn = Self.dataSource.connector;
let where = buildFilter(ctx.args, (param, value) => {
switch (param) {
case 'started':
case 'ended':
case 'companyCodeFk':
case 'reasonEndFk':
case 'reasondEnd':
case 'departmentFk':
case 'department':
case 'workerBusinessProfessionalCategoryFk':
case 'workerBusinessProfessionalCategory':
case 'calendarTypeFk':
case 'calendarType':
case 'workcenterFk':
case 'workCenter':
case 'workerBusinessCategoryFk':
case 'workerBusinessCategory':
case 'occupationCodeFk':
case 'occupationCode':
case 'rate':
case 'workerBusinessTypeFk':
case 'workerBusinessType':
case 'amount':
case 'basicSalary':
case 'notes':
}
});
where = {...where, ...{workerFk: id}};
filter = mergeFilters(filter, {where});
let stmts = [];
let stmt;
stmt = new ParameterizedSQL(
`SELECT * FROM(
SELECT b.id,
b.workerFk,
b.started,
b.ended,
b.companyCodeFk,
b.reasonEndFk,
bre.reason,
b.departmentFk,
d.name departmentName,
b.occupationCodeFk,
oc.name occupationName,
b.workerBusinessProfessionalCategoryFk,
pf.description professionalDescription,
b.calendarTypeFk,
ct.description calendarTypeDescription,
b.workcenterFk,
wc.name workCenterName,
b.workerBusinessCategoryFk,
py.description payrollDescription,
b.workerBusinessTypeFk,
wt.name workerBusinessTypeName,
b.amount,
b.basicSalary,
b.notes
FROM business b
LEFT JOIN businessReasonEnd bre ON bre.id = b.reasonEndFk
LEFT JOIN occupationCode oc ON oc.code = b.occupationCodeFk
LEFT JOIN department d ON d.id = b.departmentFk
LEFT JOIN professionalCategory pf ON pf.id = b.workerBusinessProfessionalCategoryFk
JOIN calendarType ct ON ct.id = b.calendarTypeFk
LEFT JOIN workCenter wc ON wc.id = b.workcenterFk
LEFT JOIN payrollCategories py ON py.id = b.workerBusinessCategoryFk
LEFT JOIN workerBusinessType wt ON wt.id = b.workerBusinessTypeFk
) sub
`
);
stmt.merge(conn.makeSuffix(filter));
stmts.push(stmt);
let sql = ParameterizedSQL.join(stmts, ';');
let result = await conn.executeStmt(sql, myOptions);
return result;
};
};

View File

@ -21,6 +21,7 @@ module.exports = Self => {
require('../methods/worker/setPassword')(Self);
require('../methods/worker/getAvailablePda')(Self);
require('../methods/worker/myTeam')(Self);
require('../methods/worker/getWorkerBusiness')(Self);
Self.canModifyAbsenceInPast = async(ctx, time) => {
const hasPrivs = await Self.app.models.ACL.checkAccessAcl(ctx, 'Worker', 'canModifyAbsenceInPast', 'WRITE');

View File

@ -3,7 +3,7 @@ let UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY')
return new UserError(`duplicateWarehouse`);
return new UserError(`The introduced warehouse already exists`);
return err;
});
};

View File

@ -0,0 +1,18 @@
buttons:
webAcccess: Visita la nostra Web
info: Ajuda'ns a millorar
fiscalAddress: VERDNATURA LEVANTE SL, B97367486 C/ Fenollar, 2. 46680 ALGEMESÍ
· verdnatura.es · clientes@verdnatura.es
disclaimer: '- AVÍS - Aquest missatge és privat i confidencial, i ha de ser utilitzat
exclusivament per la persona destinatària del mateix. Si has rebut aquest missatge
per error, et preguem que ho comuniquis al remitent i esborres aquest missatge
i qualsevol document adjunt que pugui contenir. Verdnatura Levante SL no renuncia
a la confidencialitat ni a cap privilegi per causa de transmissió errònia o mal
funcionament. Igualment, no es fa responsable dels canvis, alteracions, errors
o omissions que es puguin fer al missatge un cop enviat.'
privacy: En compliment del que disposa la Llei Orgànica 15/1999, de Protecció de
Dades de Caràcter Personal, et comuniquem que les dades personals que facilitis
s'inclouran en fitxers automatitzats de VERDNATURA LEVANTE S.L., podent en tot
moment exercir els drets d'accés, rectificació, cancel·lació i oposició,
comunicant-ho per escrit al domicili social de l'entitat. La finalitat del
fitxer és la gestió administrativa, comptabilitat i facturació.

View File

@ -0,0 +1,11 @@
subject: El teu albarà
title: El teu albarà
dear: Estimat client
description: Ja està disponible l'albarà corresponent a la comanda <strong>{0}</strong>. <br/>
Pots veure'l fent clic <a href="https://shop.verdnatura.es/#!form=ecomerce/ticket&ticket={0}">en aquest enllaç</a>.
copyLink: 'Com a alternativa, pots copiar el següent enllaç al teu navegador:'
poll: Si ho desitges, pots respondre a la nostra enquesta de satisfacció per
ajudar-nos a oferir un millor servei. La teva opinió és molt important per a nosaltres!
help: Qualsevol dubte que tinguis, no dubtis a consultar-nos, <strong>estem aquí
per atendre't!</strong>
conclusion: Gràcies per la teva atenció!