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,
@ -45,6 +50,6 @@ module.exports = Self => {
WHERE sub.workerFk IS NULL
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

@ -25,20 +25,23 @@ BEGIN
DECLARE vPrintQueueFk INT;
DECLARE vReportSize VARCHAR(255);
DECLARE vIsThePrinterReal INT;
DECLARE vPrinteSize VARCHAR(255);
DECLARE vPrinterSize VARCHAR(255);
DECLARE vPriorityFk INT;
DECLARE vReportFk INT;
DECLARE vTx BOOLEAN DEFAULT NOT @@in_transaction;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
CALL util.tx_rollback(vTx);
RESIGNAL;
END;
SELECT id, paperSizeFk INTO vReportFk, vReportSize
FROM report
WHERE name = vReportName;
SELECT id, paperSizeFk INTO vIsThePrinterReal, vPrinteSize
SELECT id, paperSizeFk INTO vIsThePrinterReal, vPrinterSize
FROM printer
WHERE id = vPrinterFk;
@ -54,11 +57,10 @@ BEGIN
CALL util.throw('reportNotExists');
END IF;
IF vReportSize <> vPrinteSize THEN
IF vReportSize <> vPrinterSize THEN
CALL util.throw('incorrectSize');
END IF;
START TRANSACTION;
CALL util.tx_start(vTx);
INSERT INTO printQueue
SET printerFk = vPrinterFk,
priorityFk = vPriorityFk,
@ -78,6 +80,7 @@ BEGIN
SET vI = vI + 1;
END WHILE;
COMMIT;
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

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

@ -399,5 +399,6 @@
"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 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}
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 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'}
agencyModeFk = agencyMode.id;
nickname = `Abono del: ${claim.ticketFk}`;
} else {
discountValue = 100;
packages = 1;
state = await models.State.findOne({
where: {code: 'WAITING_FOR_PICKUP'}
}, myOptions);
const zone = await models.Zone.findOne({
where: {agencyModeFk: agencyMode.id}
}, myOptions);
const claim = await models.Claim.findOne(filter, myOptions);
const today = Date.vnNew();
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);
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;
});
return await Self.app.models.Sale.create(formatedSales, options);
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

@ -51,10 +51,13 @@ module.exports = Self => {
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.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: [{
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: {
where: {claimFk: claimFk},
include: [
{
relation: 'claimDestination',
fields: ['addressFk']
scope: {fields: ['addressFk']}
},
where: {claimFk: claimFk}
{
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,11 +49,6 @@ 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({});
try {
const options = {transaction: tx};
spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
claimEnds = await importTicket(ticketId, claimId, userId, options);
@ -50,68 +60,62 @@ describe('claim regularizeClaim()', () => {
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;
}
});
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();
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);
const sale = await models.Sale.findOne({
include: [
{
relation: 'ticket',
scope: {
fields: ['clientFk', 'companyFk']
}
}],
where: {id: claimEnds[0].saleFk}
}, 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;
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({});
try {
const options = {transaction: tx};
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;
const missingSale = await models.Sale.findOne({
where: {
ticketFk: ticket.id
}
}, options);
const claimBeginning = await models.ClaimBeginning.findOne({
where: {
claimFk: claimId
}
}, options);
expect(missingSale.quantity).toBe(claimBeginning.quantity);
});
});

View File

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

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,7 +61,6 @@ describe('Client Create', () => {
provinceFk: 1
};
try {
const province = await models.Province.findById(newAccount.provinceFk, {
fields: ['id', 'name', 'autonomyFk'],
include: {
@ -71,7 +70,9 @@ describe('Client Create', () => {
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(account.roleFk).toEqual(supplierRole.id);
expect(province.autonomy().hasDailyInvoice).toBeTruthy();
expect(account.name).toEqual(newAccount.userName);
expect(client.id).toEqual(account.id);
@ -81,13 +82,9 @@ describe('Client Create', () => {
expect(client.socialName).toEqual(newAccount.socialName);
expect(client.businessTypeFk).toEqual(newAccount.businessTypeFk);
expect(client.hasDailyInvoice).toBeTruthy();
} catch (e) {
await tx.rollback();
throw e;
}
});
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,7 +97,6 @@ describe('Client Create', () => {
provinceFk: 3
};
try {
const province = await models.Province.findById(newAccount.provinceFk, {
fields: ['id', 'name', 'autonomyFk'],
include: {
@ -109,13 +105,12 @@ describe('Client Create', () => {
}, 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(vnUser.roleFk).toEqual(customerRole.id);
expect(province.autonomy.hasDailyInvoice).toBeFalsy();
expect(client.hasDailyInvoice).toBeFalsy();
} catch (e) {
await tx.rollback();
throw e;
}
});
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,19 +99,25 @@ 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);
@ -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]);
if (error.responseCode == 450) {
await invalidEmail(client);
continue;
await handleInvoicingError(error, client, failedClients);
}
}
failedClients.push({
id: client.clientId,
address: client.addressId,
error
});
for (const ticket of ticketsToMail) {
const args = {
id: ticket.ticketFk,
recipientId: ticket.clientId,
recipient: ticket.recipient,
replyTo: ticket.salesPersonEmail,
};
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ó!