7336_devToTest #2414

Merged
alexm merged 220 commits from 7336_devToTest into test 2024-05-07 06:26:53 +00:00
33 changed files with 535 additions and 529 deletions
Showing only changes of commit 94e04c1c2f - Show all commits

View File

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [24.20.01] - 2024-05-14 ## [24.20.01] - 2024-05-14
### Fixed
- (Worker -> time-control) Corrección de errores
- (InvoiceOut -> Crear factura) Cuando falla al crear una factura, se devuelve un error
## [24.18.01] - 2024-05-07 ## [24.18.01] - 2024-05-07
## [24.16.01] - 2024-04-18 ## [24.16.01] - 2024-04-18

View File

@ -0,0 +1,138 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`available_traslate`(
vWarehouseLanding INT,
vDated DATE,
vWarehouseShipment INT)
proc: BEGIN
/**
* Calcular la disponibilidad dependiendo del almacen
* de origen y destino según la fecha.
*
* @param vWarehouseLanding Almacén de llegada
* @param vDated Fecha del calculo para la disponibilidad de articulos
* @param vWarehouseShipment Almacén de destino
*/
DECLARE vDatedFrom DATE;
DECLARE vDatedTo DATETIME;
DECLARE vDatedReserve DATETIME;
DECLARE vDatedInventory DATE;
IF vDated < util.VN_CURDATE() THEN
LEAVE proc;
END IF;
CALL item_getStock (vWarehouseLanding, vDated, NULL);
-- Calcula algunos parámetros necesarios.
SET vDatedFrom = vDated;
SET vDatedTo = util.dayEnd (vDated + INTERVAL 4 DAY);
SELECT inventoried INTO vDatedInventory FROM config;
SELECT SUBTIME(util.VN_NOW(), reserveTime) INTO vDatedReserve
FROM hedera.orderConfig;
-- Calcula el ultimo dia de vida para cada producto.
CREATE OR REPLACE TEMPORARY TABLE tItemRange
(PRIMARY KEY (itemFk))
ENGINE = MEMORY
SELECT c.itemFk, MAX(t.landed) dated
FROM buy c
JOIN entry e ON c.entryFk = e.id
JOIN travel t ON t.id = e.travelFk
JOIN warehouse w ON w.id = t.warehouseInFk
WHERE t.landed BETWEEN vDatedInventory AND vDatedFrom
AND t.warehouseInFk = vWarehouseLanding
AND NOT e.isExcludedFromAvailable
AND NOT e.isRaid
GROUP BY c.itemFk;
-- Tabla con el ultimo dia de last_buy para cada producto
-- que hace un replace de la anterior.
CALL buyUltimate(vWarehouseShipment, util.VN_CURDATE());
INSERT INTO tItemRange
SELECT t.itemFk, tr.landed
FROM tmp.buyUltimate t
JOIN buy b ON b.id = t.buyFk
JOIN entry e ON e.id = b.entryFk
JOIN travel tr ON tr.id = e.travelFk
LEFT JOIN tItemRange i ON t.itemFk = i.itemFk
WHERE t.warehouseFk = vWarehouseShipment
AND NOT e.isRaid
ON DUPLICATE KEY UPDATE tItemRange.dated = GREATEST(tItemRange.dated,
tr.landed);
CREATE OR REPLACE TEMPORARY TABLE tItemRangeLive
(PRIMARY KEY (itemFk))
ENGINE = MEMORY
SELECT ir.itemFk, util.dayEnd(ir.dated + INTERVAL it.life DAY) dated
FROM tItemRange ir
JOIN item i ON i.id = ir.itemFk
JOIN itemType it ON it.id = i.typeFk
HAVING dated >= vDatedFrom OR dated IS NULL;
-- Calcula el ATP.
CREATE OR REPLACE TEMPORARY TABLE tmp.itemCalc
(INDEX (itemFk,warehouseFk))
ENGINE = MEMORY
SELECT i.itemFk,
vWarehouseLanding warehouseFk,
i.shipped dated,
i.quantity
FROM itemTicketOut i
JOIN tItemRangeLive ir ON ir.itemFK = i.itemFk
WHERE i.shipped >= vDatedFrom
AND (ir.dated IS NULL OR i.shipped <= ir.dated)
AND i.warehouseFk = vWarehouseLanding
UNION ALL
SELECT b.itemFk,
vWarehouseLanding,
t.landed,
b.quantity
FROM buy b
JOIN entry e ON b.entryFk = e.id
JOIN travel t ON t.id = e.travelFk
JOIN tItemRangeLive ir ON ir.itemFk = b.itemFk
WHERE NOT e.isExcludedFromAvailable
AND b.quantity <> 0
AND NOT e.isRaid
AND t.warehouseInFk = vWarehouseLanding
AND t.landed >= vDatedFrom
AND (ir.dated IS NULL OR t.landed <= ir.dated)
UNION ALL
SELECT i.itemFk, vWarehouseLanding, i.shipped, i.quantity
FROM itemEntryOut i
JOIN tItemRangeLive ir ON ir.itemFk = i.itemFk
WHERE i.shipped >= vDatedFrom
AND (ir.dated IS NULL OR i.shipped <= ir.dated)
AND i.warehouseOutFk = vWarehouseLanding
UNION ALL
SELECT r.item_id, vWarehouseLanding, r.shipment, -r.amount
FROM hedera.order_row r
JOIN hedera.`order` o ON o.id = r.order_id
JOIN tItemRangeLive ir ON ir.itemFk = r.item_id
WHERE r.shipment >= vDatedFrom
AND (ir.dated IS NULL OR r.shipment <= ir.dated)
AND r.warehouse_id = vWarehouseLanding
AND r.created >= vDatedReserve
AND NOT o.confirmed;
CALL item_getAtp(vDated);
CREATE OR REPLACE TEMPORARY TABLE tmp.availableTraslate
(PRIMARY KEY (item_id))
ENGINE = MEMORY
SELECT t.item_id, SUM(stock) available
FROM (
SELECT ti.itemFk item_id, stock
FROM tmp.itemList ti
JOIN tItemRange ir ON ir.itemFk = ti.itemFk
UNION ALL
SELECT itemFk, quantity
FROM tmp.itemAtp
) t
GROUP BY t.item_id
HAVING available <> 0;
DROP TEMPORARY TABLE tmp.itemList, tItemRange, tItemRangeLive;
END$$
DELIMITER ;

View File

@ -0,0 +1,217 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`balance_create`(
IN vStartingMonth INT,
IN vEndingMonth INT,
IN vCompany INT,
IN vIsConsolidated BOOLEAN,
IN vInterGroupSalesIncluded BOOLEAN)
BEGIN
/**
* Crea un balance financiero para una empresa durante
* un período de tiempo determinado.
*
* @param vStartingMonth Mes de inicio del período
* @param vEndingMonth Mes de finalización del período
* @param vCompany Identificador de la empresa
* @param vIsConsolidated Indica si se trata de un balance consolidado
* @param vInterGroupSalesIncluded Indica si se incluyen las ventas del grupo
*/
DECLARE intGAP INT DEFAULT 7;
DECLARE vYears INT DEFAULT 2;
DECLARE vYear TEXT;
DECLARE vOneYearAgo TEXT;
DECLARE vTwoYearsAgo TEXT;
DECLARE vQuery TEXT;
DECLARE vConsolidatedGroup INT;
DECLARE vStartingDate DATE DEFAULT '2020-01-01';
DECLARE vCurYear INT DEFAULT YEAR(util.VN_CURDATE());
DECLARE vStartingYear INT DEFAULT vCurYear - 2;
DECLARE vTable TEXT;
SET vTable = util.quoteIdentifier('balanceNestTree');
SET vYear = util.quoteIdentifier(vCurYear);
SET vOneYearAgo = util.quoteIdentifier(vCurYear-1);
SET vTwoYearsAgo = util.quoteIdentifier(vCurYear-2);
-- Solicitamos la tabla tmp.nest, como base para el balance.
DROP TEMPORARY TABLE IF EXISTS tmp.nest;
EXECUTE IMMEDIATE CONCAT(
'CREATE TEMPORARY TABLE tmp.nest
SELECT node.id
,CONCAT( REPEAT(REPEAT(" ",?), COUNT(parent.id) - 1),
node.name) name,
node.lft,
node.rgt,
COUNT(parent.id) - 1 depth,
CAST((node.rgt - node.lft - 1) / 2 AS DECIMAL) sons
FROM ', vTable, ' node,
', vTable, ' parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.id
ORDER BY node.lft')
USING intGAP;
CREATE OR REPLACE TEMPORARY TABLE tmp.balance
SELECT * FROM tmp.nest;
SELECT companyGroupFk INTO vConsolidatedGroup
FROM company
WHERE id = vCompany;
CREATE OR REPLACE TEMPORARY TABLE tCompanyReceiving
SELECT id companyFk
FROM company
WHERE id = vCompany
OR companyGroupFk = IF(vIsConsolidated, vConsolidatedGroup, NULL);
CREATE OR REPLACE TEMPORARY TABLE tCompanyIssuing
SELECT id companyFk
FROM supplier p;
IF NOT vInterGroupSalesIncluded THEN
DELETE ci
FROM tCompanyIssuing ci
JOIN company e on e.id = ci.companyFk
WHERE e.companyGroupFk = vConsolidatedGroup;
END IF;
-- Se calculan las facturas que intervienen,
-- para luego poder servir el desglose desde aqui.
CREATE OR REPLACE TEMPORARY TABLE tmp.balanceDetail
SELECT cr.companyFk receivingId,
ci.companyFk issuingId,
YEAR(IFNULL(r.bookEntried,IFNULL(r.booked, r.issued))) `year`,
MONTH(IFNULL(r.bookEntried,IFNULL(r.booked, r.issued))) `month`,
expenseFk,
SUM(taxableBase) amount
FROM invoiceIn r
JOIN invoiceInTax ri on ri.invoiceInFk = r.id
JOIN tCompanyReceiving cr on cr.companyFk = r.companyFk
JOIN tCompanyIssuing ci ON ci.companyFk = r.supplierFk
WHERE COALESCE(r.bookEntried, r.booked, r.issued) >= vStartingDate
AND r.isBooked
GROUP BY expenseFk, `year`, `month`, ci.companyFk, cr.companyFk;
INSERT INTO tmp.balanceDetail(
receivingId,
issuingId,
`year`,
`month`,
expenseFk,
amount)
SELECT em.companyFk,
em.companyFk,
`year`,
`month`,
expenseFk,
SUM(em.amount)
FROM expenseManual em
JOIN tCompanyReceiving er ON er.companyFk = em.companyFk
WHERE `year` >= vStartingYear
AND `month` BETWEEN vStartingMonth AND vEndingMonth
GROUP BY expenseFk, `year`, `month`, em.companyFk;
DELETE FROM tmp.balanceDetail
WHERE `month` < vStartingMonth
OR `month` > vEndingMonth;
-- Ahora el balance
EXECUTE IMMEDIATE CONCAT(
'ALTER TABLE tmp.balance
ADD COLUMN ', vTwoYearsAgo ,' INT(10) NULL ,
ADD COLUMN ', vOneYearAgo ,' INT(10) NULL ,
ADD COLUMN ', vYear,' INT(10) NULL ,
ADD COLUMN expenseFk VARCHAR(10) NULL,
ADD COLUMN expenseName VARCHAR(45) NULL');
-- Añadimos los gastos, para facilitar el formulario
UPDATE tmp.balance b
JOIN balanceNestTree bnt on bnt.id = b.id
JOIN expense e ON e.id = bnt.expenseFk COLLATE utf8_general_ci
SET b.expenseFk = e.id COLLATE utf8_general_ci,
b.expenseName = e.name COLLATE utf8_general_ci ;
-- Rellenamos los valores de primer nivel, los que corresponden
-- a los gastos simples.
WHILE vYears >= 0 DO
SET vQuery = CONCAT(
'UPDATE tmp.balance b
JOIN (
SELECT expenseFk, SUM(amount) amount
FROM tmp.balanceDetail
WHERE year = ?
GROUP BY expenseFk
) sub on sub.expenseFk = b.expenseFk COLLATE utf8_general_ci
SET ', util.quoteIdentifier(vCurYear - vYears), ' = - amount');
EXECUTE IMMEDIATE vQuery
USING vCurYear - vYears;
SET vYears = vYears - 1;
END WHILE;
-- Añadimos las ventas.
EXECUTE IMMEDIATE CONCAT(
'UPDATE tmp.balance b
JOIN (
SELECT SUM(IF(year = ?, venta, 0)) y2,
SUM(IF(year = ?, venta, 0)) y1,
SUM(IF(year = ?, venta, 0)) y0,
c.Gasto
FROM bs.ventas_contables c
JOIN tCompanyReceiving cr ON cr.companyFk = c.empresa_id
WHERE month BETWEEN ? AND ?
GROUP BY c.Gasto
) sub ON sub.gasto = b.expenseFk COLLATE utf8_general_ci
SET b.', vTwoYearsAgo, '= IFNULL(b.', vTwoYearsAgo, ', 0) + sub.y2,
b.', vOneYearAgo, '= IFNULL(b.', vOneYearAgo, ', 0) + sub.y1,
b.', vYear, '= IFNULL(b.', vYear, ', 0) + sub.y0')
USING vCurYear-2,
vCurYear-1,
vCurYear,
vStartingMonth,
vEndingMonth;
-- Ventas intra grupo.
IF NOT vInterGroupSalesIncluded THEN
SELECT lft, rgt INTO @groupLft, @groupRgt
FROM tmp.balance b
WHERE TRIM(b.`name`) = 'Grupo';
DELETE
FROM tmp.balance
WHERE lft BETWEEN @groupLft AND @groupRgt;
END IF;
-- Rellenamos el valor de los padres con la suma de los hijos.
CREATE OR REPLACE TEMPORARY TABLE tmp.balance_aux
SELECT * FROM tmp.balance;
EXECUTE IMMEDIATE
CONCAT('UPDATE tmp.balance b
JOIN (
SELECT b1.id,
b1.name,
SUM(b2.', vYear,') thisYear,
SUM(b2.', vOneYearAgo,') oneYearAgo,
SUM(b2.', vTwoYearsAgo,') twoYearsAgo
FROM tmp.nest b1
JOIN tmp.balance_aux b2 on b2.lft BETWEEN b1.lft and b1.rgt
GROUP BY b1.id
)sub ON sub.id = b.id
SET b.', vYear, ' = thisYear,
b.', vOneYearAgo, ' = oneYearAgo,
b.', vTwoYearsAgo, ' = twoYearsAgo');
SELECT *, CONCAT('',IFNULL(expenseFk,'')) newgasto
FROM tmp.balance;
DROP TEMPORARY TABLE IF EXISTS tCompanyReceiving, tCompanyIssuing;
END$$
DELIMITER ;

View File

@ -68,19 +68,19 @@ BEGIN
AND v.`visible` AND v.`visible`
ON DUPLICATE KEY UPDATE visibleLanding = v.`visible`; ON DUPLICATE KEY UPDATE visibleLanding = v.`visible`;
CALL vn2008.availableTraslate(vWarehouseOut, vDateShipped, NULL); CALL available_traslate(vWarehouseOut, vDateShipped, NULL);
INSERT INTO tItem(itemFk, available) INSERT INTO tItem(itemFk, available)
SELECT a.item_id, a.available SELECT a.item_id, a.available
FROM vn2008.availableTraslate a FROM tmp.availableTraslate a
WHERE a.available WHERE a.available
ON DUPLICATE KEY UPDATE available = a.available; ON DUPLICATE KEY UPDATE available = a.available;
CALL vn2008.availableTraslate(vWarehouseIn, vDateLanded, vWarehouseOut); CALL available_traslate(vWarehouseIn, vDateLanded, vWarehouseOut);
INSERT INTO tItem(itemFk, availableLanding) INSERT INTO tItem(itemFk, availableLanding)
SELECT a.item_id, a.available SELECT a.item_id, a.available
FROM vn2008.availableTraslate a FROM tmp.availableTraslate a
WHERE a.available WHERE a.available
ON DUPLICATE KEY UPDATE availableLanding = a.available; ON DUPLICATE KEY UPDATE availableLanding = a.available;
ELSE ELSE

View File

@ -26,7 +26,7 @@ BEGIN
LEAVE l; LEAVE l;
END IF; END IF;
CALL vn2008.buy_tarifas_entry(vEntryFk); CALL buy_recalcPricesByEntry(vEntryFk);
END LOOP; END LOOP;
CLOSE vCur; CLOSE vCur;

View File

@ -155,27 +155,28 @@ BEGIN
SET @currentLineFk := 0; SET @currentLineFk := 0;
SET @shipped := ''; SET @shipped := '';
SELECT DATE(@shipped:= shipped) shipped, SELECT DATE(@shipped:= t.shipped) shipped,
alertLevel, t.alertLevel,
stateName, t.stateName,
origin, t.origin,
reference, t.reference,
clientFk, t.clientFk,
name, t.name,
`in` invalue, t.`in` invalue,
`out`, t.`out`,
@a := @a + IFNULL(`in`, 0) - IFNULL(`out`, 0) balance, @a := @a + IFNULL(t.`in`, 0) - IFNULL(t.`out`, 0) balance,
@currentLineFk := IF (@shipped < util.VN_CURDATE() @currentLineFk := IF (@shipped < util.VN_CURDATE()
OR (@shipped = util.VN_CURDATE() AND (isPicked OR a.`code` >= 'ON_PREPARATION')), OR (@shipped = util.VN_CURDATE() AND (t.isPicked OR a.`code` >= 'ON_PREPARATION')),
lineFk, t.lineFk,
@currentLineFk) lastPreparedLineFk, @currentLineFk) lastPreparedLineFk,
isTicket, t.isTicket,
lineFk, t.lineFk,
isPicked, t.isPicked,
clientType, t.clientType,
claimFk t.claimFk,
FROM tItemDiary t.`order`
LEFT JOIN alertLevel a ON a.id = tItemDiary.alertLevel; FROM tItemDiary t
LEFT JOIN alertLevel a ON a.id = t.alertLevel;
ELSE ELSE
SELECT SUM(`in`) - SUM(`out`) INTO @a SELECT SUM(`in`) - SUM(`out`) INTO @a
@ -197,7 +198,8 @@ BEGIN
0 lineFk, 0 lineFk,
0 isPicked, 0 isPicked,
0 clientType, 0 clientType,
0 claimFk 0 claimFk,
NULL `order`
UNION ALL UNION ALL
SELECT shipped, SELECT shipped,
alertlevel, alertlevel,
@ -213,7 +215,8 @@ BEGIN
lineFk, lineFk,
isPicked, isPicked,
clientType, clientType,
claimFk claimFk,
`order`
FROM tItemDiary FROM tItemDiary
WHERE shipped >= vDate; WHERE shipped >= vDate;
END IF; END IF;

View File

@ -8,38 +8,14 @@ BEGIN
* @param vDateToAdvance Fecha a cuando se quiere adelantar. * @param vDateToAdvance Fecha a cuando se quiere adelantar.
* @param vWarehouseFk Almacén * @param vWarehouseFk Almacén
*/ */
DECLARE vDateInventory DATE;
SELECT inventoried INTO vDateInventory FROM config; CALL item_getStock(vWarehouseFk, vDateToAdvance, NULL);
CALL item_getMinacum(
CREATE OR REPLACE TEMPORARY TABLE tmp.stock vWarehouseFk,
(itemFk INT PRIMARY KEY, vDateToAdvance,
amount INT) DATEDIFF(DATE_SUB(vDateFuture, INTERVAL 1 DAY), vDateToAdvance),
ENGINE = MEMORY; NULL
);
INSERT INTO tmp.stock(itemFk, amount)
SELECT itemFk, SUM(quantity) amount FROM
(
SELECT itemFk, quantity
FROM itemTicketOut
WHERE shipped >= vDateInventory
AND shipped < vDateFuture
AND warehouseFk = vWarehouseFk
UNION ALL
SELECT itemFk, quantity
FROM itemEntryIn
WHERE landed >= vDateInventory
AND landed <= vDateToAdvance
AND isVirtualStock = FALSE
AND warehouseInFk = vWarehouseFk
UNION ALL
SELECT itemFk, quantity
FROM itemEntryOut
WHERE shipped >= vDateInventory
AND shipped < vDateFuture
AND warehouseOutFk = vWarehouseFk
) t
GROUP BY itemFk HAVING amount != 0;
CREATE OR REPLACE TEMPORARY TABLE tmp.filter CREATE OR REPLACE TEMPORARY TABLE tmp.filter
(INDEX (id)) (INDEX (id))
@ -87,7 +63,7 @@ BEGIN
count(s.id) futureLines, count(s.id) futureLines,
GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) futureIpt, GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) futureIpt,
CAST(SUM(litros) AS DECIMAL(10,0)) futureLiters, CAST(SUM(litros) AS DECIMAL(10,0)) futureLiters,
SUM((s.quantity <= IFNULL(st.amount,0))) hasStock, SUM(s.quantity <= (IFNULL(il.stock,0) + IFNULL(im.amount, 0))) hasStock,
z.id futureZoneFk, z.id futureZoneFk,
z.name futureZoneName, z.name futureZoneName,
st.classColor, st.classColor,
@ -107,7 +83,9 @@ BEGIN
JOIN agencyMode am ON t.agencyModeFk = am.id JOIN agencyMode am ON t.agencyModeFk = am.id
JOIN zone z ON t.zoneFk = z.id JOIN zone z ON t.zoneFk = z.id
LEFT JOIN itemPackingType ipt ON ipt.code = i.itemPackingTypeFk LEFT JOIN itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
LEFT JOIN tmp.stock st ON st.itemFk = i.id LEFT JOIN tmp.itemMinacum im ON im.itemFk = i.id
AND im.warehouseFk = vWarehouseFk
LEFT JOIN tmp.itemList il ON il.itemFk = i.id
WHERE t.shipped BETWEEN vDateFuture AND util.dayend(vDateFuture) WHERE t.shipped BETWEEN vDateFuture AND util.dayend(vDateFuture)
AND t.warehouseFk = vWarehouseFk AND t.warehouseFk = vWarehouseFk
GROUP BY t.id GROUP BY t.id
@ -146,6 +124,8 @@ BEGIN
) dest ON dest.addressFk = origin.addressFk ) dest ON dest.addressFk = origin.addressFk
WHERE origin.hasStock; WHERE origin.hasStock;
DROP TEMPORARY TABLE tmp.stock; DROP TEMPORARY TABLE IF EXISTS
tmp.itemList,
tmp.itemMinacum;
END$$ END$$
DELIMITER ; DELIMITER ;

View File

@ -21,7 +21,7 @@ BEGIN
WHERE t.id = vTicketFk; WHERE t.id = vTicketFk;
-- Añadimos un dia más para calcular el stock hasta vNewShipped inclusive -- Añadimos un dia más para calcular el stock hasta vNewShipped inclusive
CALL item_getStock(vWarehouseFk, DATE_ADD(vNewShipped, INTERVAL 1 DAY), NULL); CALL item_getStock(vWarehouseFk, vNewShipped, NULL);
CALL item_getMinacum( CALL item_getMinacum(
vWarehouseFk, vWarehouseFk,
vNewShipped, vNewShipped,
@ -38,7 +38,7 @@ BEGIN
s.discount, s.discount,
i.image, i.image,
i.subName, i.subName,
il.stock + IFNULL(im.amount, 0) AS movable IFNULL(il.stock,0) + IFNULL(im.amount, 0) AS movable
FROM ticket t FROM ticket t
JOIN sale s ON s.ticketFk = t.id JOIN sale s ON s.ticketFk = t.id
JOIN item i ON i.id = s.itemFk JOIN item i ON i.id = s.itemFk
@ -48,8 +48,8 @@ BEGIN
WHERE t.id = vTicketFk; WHERE t.id = vTicketFk;
DROP TEMPORARY TABLE IF EXISTS DROP TEMPORARY TABLE IF EXISTS
tmp.itemList, tmp.itemList,
tmp.itemMinacum; tmp.itemMinacum;
END$$ END$$
DELIMITER ; DELIMITER ;

View File

@ -121,15 +121,17 @@ BEGIN
CALL util.throw(vErrorCode); CALL util.throw(vErrorCode);
END IF; END IF;
-- DIRECCION CORRECTA -- DIRECCION CORRECTA
CALL workerTimeControl_direction(vWorkerFk, vTimed); CALL workerTimeControl_direction(vWorkerFk, vTimed);
IF (SELECT IF (SELECT
IF(IF(option1 IN ('inMiddle', 'outMiddle'), IF((IF(option1 IN ('inMiddle', 'outMiddle'),
'middle', 'middle',
option1) <> vDirection option1) <> vDirection
AND IF(option2 IN ('inMiddle', 'outMiddle'), AND IF(option2 IN ('inMiddle', 'outMiddle'),
'middle', 'middle',
IFNULL(option2, '')) <> vDirection, IFNULL(option2, '')) <> vDirection)
OR (option1 IS NULL AND option2 IS NULL),
TRUE , TRUE ,
FALSE) FALSE)
FROM tmp.workerTimeControlDirection FROM tmp.workerTimeControlDirection
@ -137,12 +139,17 @@ BEGIN
SET vIsError = TRUE; SET vIsError = TRUE;
END IF; END IF;
DROP TEMPORARY TABLE tmp.workerTimeControlDirection;
IF vIsError THEN IF vIsError THEN
SET vErrorCode = 'WRONG_DIRECTION'; SET vErrorCode = 'WRONG_DIRECTION';
IF(SELECT option1 IS NULL AND option2 IS NULL
FROM tmp.workerTimeControlDirection) THEN
SET vErrorCode = 'DAY_MAX_TIME';
END IF;
CALL util.throw(vErrorCode); CALL util.throw(vErrorCode);
END IF; END IF;
DROP TEMPORARY TABLE tmp.workerTimeControlDirection;
-- FICHADAS IMPARES -- FICHADAS IMPARES
SELECT timed INTO vLastIn SELECT timed INTO vLastIn
FROM workerTimeControl FROM workerTimeControl

View File

@ -1,6 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn2008`.`article_multiple_buy`(v_date DATETIME, wh INT)
BEGIN
CALL vn.item_multipleBuy(v_date, wh);
END$$
DELIMITER ;

View File

@ -1,9 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn2008`.`article_multiple_buy_date`(
IN vDated DATETIME,
IN vWarehouseFk TINYINT(3)
)
BEGIN
CALL vn.item_multipleBuyByDate(vDated, vWarehouseFk);
END$$
DELIMITER ;

View File

@ -1,126 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn2008`.`availableTraslate`(
vWarehouseLanding INT,
vDated DATE,
vWarehouseShipment INT)
proc: BEGIN
DECLARE vDatedFrom DATE;
DECLARE vDatedTo DATETIME;
DECLARE vDatedReserve DATETIME;
DECLARE vDatedInventory DATE;
IF vDated < util.VN_CURDATE() THEN
LEAVE proc;
END IF;
CALL vn.item_getStock (vWarehouseLanding, vDated, NULL);
-- Calcula algunos parámetros necesarios
SET vDatedFrom = TIMESTAMP(vDated, '00:00:00');
SET vDatedTo = TIMESTAMP(TIMESTAMPADD(DAY, 4, vDated), '23:59:59');
SELECT inventoried INTO vDatedInventory FROM vn.config;
SELECT SUBTIME(util.VN_NOW(), reserveTime) INTO vDatedReserve
FROM hedera.orderConfig;
-- Calcula el ultimo dia de vida para cada producto
DROP TEMPORARY TABLE IF EXISTS itemRange;
CREATE TEMPORARY TABLE itemRange
(PRIMARY KEY (itemFk))
ENGINE = MEMORY
SELECT c.itemFk, MAX(t.landed) dated
FROM vn.buy c
JOIN vn.entry e ON c.entryFk = e.id
JOIN vn.travel t ON t.id = e.travelFk
JOIN vn.warehouse w ON w.id = t.warehouseInFk
WHERE t.landed BETWEEN vDatedInventory AND vDatedFrom
AND t.warehouseInFk = vWarehouseLanding
AND NOT e.isExcludedFromAvailable
AND NOT e.isRaid
GROUP BY c.itemFk;
-- Tabla con el ultimo dia de last_buy para cada producto que hace un replace de la anterior
CALL vn.buyUltimate(vWarehouseShipment, util.VN_CURDATE());
INSERT INTO itemRange
SELECT t.itemFk, tr.landed
FROM tmp.buyUltimate t
JOIN vn.buy b ON b.id = t.buyFk
JOIN vn.entry e ON e.id = b.entryFk
JOIN vn.travel tr ON tr.id = e.travelFk
LEFT JOIN itemRange i ON t.itemFk = i.itemFk
WHERE t.warehouseFk = vWarehouseShipment
AND NOT e.isRaid
ON DUPLICATE KEY UPDATE itemRange.dated = GREATEST(itemRange.dated, tr.landed);
DROP TEMPORARY TABLE IF EXISTS itemRangeLive;
CREATE TEMPORARY TABLE itemRangeLive
(PRIMARY KEY (itemFk))
ENGINE = MEMORY
SELECT ir.itemFk, TIMESTAMP(TIMESTAMPADD(DAY, it.life, ir.dated), '23:59:59') dated
FROM itemRange ir
JOIN vn.item i ON i.id = ir.itemFk
JOIN vn.itemType it ON it.id = i.typeFk
HAVING dated >= vDatedFrom OR dated IS NULL;
-- Calcula el ATP
DROP TEMPORARY TABLE IF EXISTS tmp.itemCalc;
CREATE TEMPORARY TABLE tmp.itemCalc
(INDEX (itemFk,warehouseFk))
ENGINE = MEMORY
SELECT i.itemFk, vWarehouseLanding warehouseFk, i.shipped dated, i.quantity
FROM vn.itemTicketOut i
JOIN itemRangeLive ir ON ir.itemFK = i.itemFk
WHERE i.shipped >= vDatedFrom
AND (ir.dated IS NULL OR i.shipped <= ir.dated)
AND i.warehouseFk = vWarehouseLanding
UNION ALL
SELECT b.itemFk, vWarehouseLanding, t.landed, b.quantity
FROM vn.buy b
JOIN vn.entry e ON b.entryFk = e.id
JOIN vn.travel t ON t.id = e.travelFk
JOIN itemRangeLive ir ON ir.itemFk = b.itemFk
WHERE NOT e.isExcludedFromAvailable
AND b.quantity <> 0
AND NOT e.isRaid
AND t.warehouseInFk = vWarehouseLanding
AND t.landed >= vDatedFrom
AND (ir.dated IS NULL OR t.landed <= ir.dated)
UNION ALL
SELECT i.itemFk, vWarehouseLanding, i.shipped, i.quantity
FROM vn.itemEntryOut i
JOIN itemRangeLive ir ON ir.itemFk = i.itemFk
WHERE i.shipped >= vDatedFrom
AND (ir.dated IS NULL OR i.shipped <= ir.dated)
AND i.warehouseOutFk = vWarehouseLanding
UNION ALL
SELECT r.item_id, vWarehouseLanding, r.shipment, -r.amount
FROM hedera.order_row r
JOIN hedera.`order` o ON o.id = r.order_id
JOIN itemRangeLive ir ON ir.itemFk = r.item_id
WHERE r.shipment >= vDatedFrom
AND (ir.dated IS NULL OR r.shipment <= ir.dated)
AND r.warehouse_id = vWarehouseLanding
AND r.created >= vDatedReserve
AND NOT o.confirmed;
CALL vn.item_getAtp(vDated);
DROP TEMPORARY TABLE IF EXISTS availableTraslate;
CREATE TEMPORARY TABLE availableTraslate
(PRIMARY KEY (item_id))
ENGINE = MEMORY
SELECT t.item_id, SUM(stock) available
FROM (
SELECT ti.itemFk item_id, stock
FROM tmp.itemList ti
JOIN itemRange ir ON ir.itemFk = ti.itemFk
UNION ALL
SELECT itemFk, quantity
FROM tmp.itemAtp
) t
GROUP BY t.item_id
HAVING available <> 0;
DROP TEMPORARY TABLE tmp.itemList, itemRange, itemRangeLive;
END$$
DELIMITER ;

View File

@ -1,207 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn2008`.`balance_create`(
IN vStartingMonth INT,
IN vEndingMonth INT,
IN vCompany INT,
IN vIsConsolidated BOOLEAN,
IN vInterGroupSalesIncluded BOOLEAN)
BEGIN
DECLARE intGAP INT DEFAULT 7;
DECLARE vYears INT DEFAULT 2;
DECLARE vYear TEXT;
DECLARE vOneYearAgo TEXT;
DECLARE vTwoYearsAgo TEXT;
DECLARE vQuery TEXT;
DECLARE vConsolidatedGroup INT;
DECLARE vStartingDate DATE DEFAULT '2020-01-01';
DECLARE vCurYear INT DEFAULT YEAR(util.VN_CURDATE());
DECLARE vStartingYear INT DEFAULT vCurYear - 2;
DECLARE vTable TEXT;
SET vTable = util.quoteIdentifier('balance_nest_tree');
SET vYear = util.quoteIdentifier(vCurYear);
SET vOneYearAgo = util.quoteIdentifier(vCurYear-1);
SET vTwoYearsAgo = util.quoteIdentifier(vCurYear-2);
-- Solicitamos la tabla tmp.nest, como base para el balance
DROP TEMPORARY TABLE IF EXISTS tmp.nest;
EXECUTE IMMEDIATE CONCAT(
'CREATE TEMPORARY TABLE tmp.nest
SELECT node.id
,CONCAT( REPEAT(REPEAT(" ",?), COUNT(parent.id) - 1), node.name) AS name
,node.lft
,node.rgt
,COUNT(parent.id) - 1 as depth
,cast((node.rgt - node.lft - 1) / 2 as DECIMAL) as sons
FROM ', vTable, ' AS node,
', vTable, ' AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.id
ORDER BY node.lft')
USING intGAP;
DROP TEMPORARY TABLE IF EXISTS tmp.balance;
CREATE TEMPORARY TABLE tmp.balance
SELECT * FROM tmp.nest;
DROP TEMPORARY TABLE IF EXISTS tmp.empresas_receptoras;
DROP TEMPORARY TABLE IF EXISTS tmp.empresas_emisoras;
SELECT empresa_grupo INTO vConsolidatedGroup
FROM empresa
WHERE id = vCompany;
CREATE TEMPORARY TABLE tmp.empresas_receptoras
SELECT id as empresa_id
FROM vn2008.empresa
WHERE id = vCompany
OR empresa_grupo = IF(vIsConsolidated, vConsolidatedGroup, NULL);
CREATE TEMPORARY TABLE tmp.empresas_emisoras
SELECT Id_Proveedor as empresa_id FROM vn2008.Proveedores p;
IF vInterGroupSalesIncluded = FALSE THEN
DELETE ee.*
FROM tmp.empresas_emisoras ee
JOIN vn2008.empresa e on e.id = ee.empresa_id
WHERE e.empresa_grupo = vConsolidatedGroup;
END IF;
-- Se calculan las facturas que intervienen, para luego poder servir el desglose desde aqui
DROP TEMPORARY TABLE IF EXISTS tmp.balance_desglose;
CREATE TEMPORARY TABLE tmp.balance_desglose
SELECT er.empresa_id receptora_id,
ee.empresa_id emisora_id,
year(IFNULL(r.bookEntried,IFNULL(r.dateBooking, r.Fecha))) `year`,
month(IFNULL(r.bookEntried,IFNULL(r.dateBooking, r.Fecha))) `month`,
gastos_id Id_Gasto,
SUM(bi) importe
FROM recibida r
JOIN recibida_iva ri on ri.recibida_id = r.id
JOIN tmp.empresas_receptoras er on er.empresa_id = r.empresa_id
JOIN tmp.empresas_emisoras ee ON ee.empresa_id = r.proveedor_id
WHERE IFNULL(r.bookEntried,IFNULL(r.dateBooking, r.Fecha)) >= vStartingDate
AND r.contabilizada
GROUP BY Id_Gasto, year, month, emisora_id, receptora_id;
INSERT INTO tmp.balance_desglose(
receptora_id,
emisora_id,
year,
month,
Id_Gasto,
importe)
SELECT gr.empresa_id,
gr.empresa_id,
year,
month,
Id_Gasto,
SUM(importe)
FROM gastos_resumen gr
JOIN tmp.empresas_receptoras er on gr.empresa_id = er.empresa_id
WHERE year >= vStartingYear
AND month BETWEEN vStartingMonth AND vEndingMonth
GROUP BY Id_Gasto, year, month, gr.empresa_id;
DELETE FROM tmp.balance_desglose
WHERE month < vStartingMonth
OR month > vEndingMonth;
-- Ahora el balance
EXECUTE IMMEDIATE CONCAT(
'ALTER TABLE tmp.balance
ADD COLUMN ', vTwoYearsAgo ,' INT(10) NULL ,
ADD COLUMN ', vOneYearAgo ,' INT(10) NULL ,
ADD COLUMN ', vYear,' INT(10) NULL ,
ADD COLUMN Id_Gasto VARCHAR(10) NULL,
ADD COLUMN Gasto VARCHAR(45) NULL');
-- Añadimos los gastos, para facilitar el formulario
UPDATE tmp.balance b
JOIN vn2008.balance_nest_tree bnt on bnt.id = b.id
JOIN (SELECT id Id_Gasto, name Gasto
FROM vn.expense
GROUP BY id) g ON g.Id_Gasto = bnt.Id_Gasto COLLATE utf8_general_ci
SET b.Id_Gasto = g.Id_Gasto COLLATE utf8_general_ci
, b.Gasto = g.Gasto COLLATE utf8_general_ci ;
-- Rellenamos los valores de primer nivel, los que corresponden a los gastos simples
WHILE vYears >= 0 DO
SET vQuery = CONCAT(
'UPDATE tmp.balance b
JOIN
(SELECT Id_Gasto, SUM(Importe) as Importe
FROM tmp.balance_desglose
WHERE year = ?
GROUP BY Id_Gasto
) sub on sub.Id_Gasto = b.Id_Gasto COLLATE utf8_general_ci
SET ', util.quoteIdentifier(vCurYear - vYears), ' = - Importe');
EXECUTE IMMEDIATE vQuery
USING vCurYear - vYears;
SET vYears = vYears - 1;
END WHILE;
-- Añadimos las ventas
EXECUTE IMMEDIATE CONCAT(
'UPDATE tmp.balance b
JOIN (
SELECT SUM(IF(year = ?, venta, 0)) y2,
SUM(IF(year = ?, venta, 0)) y1,
SUM(IF(year = ?, venta, 0)) y0,
c.Gasto
FROM bs.ventas_contables c
JOIN tmp.empresas_receptoras er on er.empresa_id = c.empresa_id
WHERE month BETWEEN ? AND ?
GROUP BY c.Gasto
) sub ON sub.Gasto = b.Id_Gasto COLLATE utf8_general_ci
SET b.', vTwoYearsAgo, '= IFNULL(b.', vTwoYearsAgo, ', 0) + sub.y2,
b.', vOneYearAgo, '= IFNULL(b.', vOneYearAgo, ', 0) + sub.y1,
b.', vYear, '= IFNULL(b.', vYear, ', 0) + sub.y0')
USING vCurYear-2,
vCurYear-1,
vCurYear,
vStartingMonth,
vEndingMonth;
-- Ventas intra grupo
IF NOT vInterGroupSalesIncluded THEN
SELECT lft, rgt INTO @grupoLft, @grupoRgt
FROM tmp.balance b
WHERE TRIM(b.`name`) = 'Grupo';
DELETE
FROM tmp.balance
WHERE lft BETWEEN @grupoLft AND @grupoRgt;
END IF;
-- Rellenamos el valor de los padres con la suma de los hijos
DROP TEMPORARY TABLE IF EXISTS tmp.balance_aux;
CREATE TEMPORARY TABLE tmp.balance_aux
SELECT * FROM tmp.balance;
EXECUTE IMMEDIATE
CONCAT('UPDATE tmp.balance b
JOIN (
SELECT b1.id,
b1.name,
SUM(b2.', vYear,') thisYear,
SUM(b2.', vOneYearAgo,') oneYearAgo,
SUM(b2.', vTwoYearsAgo,') twoYearsAgo
FROM tmp.nest b1
JOIN tmp.balance_aux b2 on b2.lft BETWEEN b1.lft and b1.rgt
GROUP BY b1.id)sub ON sub.id = b.id
SET b.', vYear, ' = thisYear,
b.', vOneYearAgo, ' = oneYearAgo,
b.', vTwoYearsAgo, ' = twoYearsAgo');
SELECT *, CONCAT('',ifnull(Id_Gasto,'')) newgasto
FROM tmp.balance;
END$$
DELIMITER ;

View File

@ -1,6 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn2008`.`buy_tarifas`(vBuyFk INT)
BEGIN
CALL vn.buy_recalcPricesByBuy(vBuyFk);
END$$
DELIMITER ;

View File

@ -1,11 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn2008`.`buy_tarifas_entry`(IN vEntryFk INT(11))
BEGIN
/**
* Recalcula los precios de una entrada
*
* @param vEntryFk
*/
CALL vn.buy_recalcPricesByEntry(vEntryFk);
END$$
DELIMITER ;

View File

@ -0,0 +1,7 @@
CREATE OR REPLACE PROCEDURE `vn`.`balance_create`() BEGIN END;
CREATE OR REPLACE PROCEDURE `vn`.`buy_recalcPricesByEntry`() BEGIN END;
CREATE OR REPLACE PROCEDURE `vn`.`buy_recalcPricesByBuy`() BEGIN END;
GRANT EXECUTE ON PROCEDURE vn.balance_create TO `financialBoss`, `hrBoss`;
GRANT EXECUTE ON PROCEDURE vn.buy_recalcPricesByEntry TO `buyer`, `claimManager`, `employee`;
GRANT EXECUTE ON PROCEDURE vn.buy_recalcPricesByBuy TO `buyer`, `entryEditor`, `claimManager`, `employee`;

View File

@ -1,4 +1,3 @@
CREATE OR REPLACE TABLE `vn`.`farmingDeliveryNote` ( CREATE OR REPLACE TABLE `vn`.`farmingDeliveryNote` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`farmingFk` int(10) unsigned NOT NULL, `farmingFk` int(10) unsigned NOT NULL,
@ -10,3 +9,8 @@ CREATE OR REPLACE TABLE `vn`.`farmingDeliveryNote` (
CONSTRAINT `farmingDeliveryNoteFk_FK` FOREIGN KEY (`deliveryNoteFk`) REFERENCES `deliveryNote` (`id`), CONSTRAINT `farmingDeliveryNoteFk_FK` FOREIGN KEY (`deliveryNoteFk`) REFERENCES `deliveryNote` (`id`),
CONSTRAINT `farmingDeliveryNoteFk_FK_1` FOREIGN KEY (`farmingFk`) REFERENCES `farming` (`id`) CONSTRAINT `farmingDeliveryNoteFk_FK_1` FOREIGN KEY (`farmingFk`) REFERENCES `farming` (`id`)
); );
INSERT IGNORE INTO `vn`.`farmingDeliveryNote` (farmingFk, deliveryNoteFk, amount)
SELECT farmingFk, id, amount
FROM vn.deliveryNote dn
WHERE farmingFk;

View File

@ -0,0 +1,2 @@
-- Place your SQL code here
ALTER TABLE vn.claimBeginning MODIFY COLUMN quantity double DEFAULT 0 NULL;

View File

@ -40,7 +40,7 @@ describe('InvoiceOut manual invoice path', () => {
await page.waitToClick(selectors.invoiceOutIndex.createInvoice); await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm); await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm);
await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceClient, 'Max Eisenhardt'); await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceClient, 'Bruce Wayne');
await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceSerial, 'Global nacional'); await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceSerial, 'Global nacional');
await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceTaxArea, 'national'); await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceTaxArea, 'national');
await page.waitToClick(selectors.invoiceOutIndex.saveInvoice); await page.waitToClick(selectors.invoiceOutIndex.saveInvoice);

View File

@ -353,5 +353,7 @@
"This password can only be changed by the user themselves": "Esta contraseña solo puede ser modificada por el propio usuario", "This password can only be changed by the user themselves": "Esta contraseña solo puede ser modificada por el propio usuario",
"They're not your subordinate": "No es tu subordinado/a.", "They're not your subordinate": "No es tu subordinado/a.",
"No results found": "No se han encontrado resultados", "No results found": "No se han encontrado resultados",
"InvoiceIn is already booked": "La factura recibida está contabilizada" "InvoiceIn is already booked": "La factura recibida está contabilizada",
"Select ticket or client": "Elija un ticket o un client",
"It was not able to create the invoice": "No se pudo crear la factura"
} }

View File

@ -83,7 +83,6 @@ module.exports = Self => {
const newClaimBeginning = models.ClaimBeginning.create({ const newClaimBeginning = models.ClaimBeginning.create({
saleFk: sale.id, saleFk: sale.id,
claimFk: newClaim.id, claimFk: newClaim.id,
quantity: sale.quantity
}, myOptions); }, myOptions);
promises.push(newClaimBeginning); promises.push(newClaimBeginning);

View File

@ -37,7 +37,7 @@ describe('Claim createFromSales()', () => {
let claimBeginning = await models.ClaimBeginning.findOne({where: {claimFk: claim.id}}, options); let claimBeginning = await models.ClaimBeginning.findOne({where: {claimFk: claim.id}}, options);
expect(claimBeginning.saleFk).toEqual(newSale[0].id); expect(claimBeginning.saleFk).toEqual(newSale[0].id);
expect(claimBeginning.quantity).toEqual(newSale[0].quantity); expect(claimBeginning.quantity).toEqual(0);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
@ -67,7 +67,7 @@ describe('Claim createFromSales()', () => {
const claimBeginning = await models.ClaimBeginning.findOne({where: {claimFk: claim.id}}, options); const claimBeginning = await models.ClaimBeginning.findOne({where: {claimFk: claim.id}}, options);
expect(claimBeginning.saleFk).toEqual(newSale[0].id); expect(claimBeginning.saleFk).toEqual(newSale[0].id);
expect(claimBeginning.quantity).toEqual(newSale[0].quantity); expect(claimBeginning.quantity).toEqual(0);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -16,8 +16,7 @@
"description": "Identifier" "description": "Identifier"
}, },
"quantity": { "quantity": {
"type": "number", "type": "number"
"required": true
} }
}, },
"relations": { "relations": {

View File

@ -46,12 +46,11 @@ module.exports = Self => {
} }
}); });
Self.createManualInvoice = async(ctx, options) => { Self.createManualInvoice = async(ctx, clientFk, ticketFk, maxShipped, serial, taxArea, reference, options) => {
if (!clientFk && !ticketFk) throw new UserError(`Select ticket or client`);
const models = Self.app.models; const models = Self.app.models;
const args = ctx.args;
let tx;
const myOptions = {userId: ctx.req.accessToken.userId}; const myOptions = {userId: ctx.req.accessToken.userId};
let tx;
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
@ -61,18 +60,15 @@ module.exports = Self => {
myOptions.transaction = tx; myOptions.transaction = tx;
} }
const ticketId = args.ticketFk;
let clientId = args.clientFk;
let maxShipped = args.maxShipped;
let companyId; let companyId;
let newInvoice; let newInvoice;
let query; let query;
try { try {
if (ticketId) { if (ticketFk) {
const ticket = await models.Ticket.findById(ticketId, null, myOptions); const ticket = await models.Ticket.findById(ticketFk, null, myOptions);
const company = await models.Company.findById(ticket.companyFk, null, myOptions); const company = await models.Company.findById(ticket.companyFk, null, myOptions);
clientId = ticket.clientFk; clientFk = ticket.clientFk;
maxShipped = ticket.shipped; maxShipped = ticket.shipped;
companyId = ticket.companyFk; companyId = ticket.companyFk;
@ -85,7 +81,7 @@ module.exports = Self => {
throw new UserError(`A ticket with an amount of zero can't be invoiced`); throw new UserError(`A ticket with an amount of zero can't be invoiced`);
// Validates ticket nagative base // Validates ticket nagative base
const hasNegativeBase = await getNegativeBase(maxShipped, clientId, companyId, myOptions); const hasNegativeBase = await getNegativeBase(maxShipped, clientFk, companyId, myOptions);
if (hasNegativeBase && company.code == 'VNL') if (hasNegativeBase && company.code == 'VNL')
throw new UserError(`A ticket with a negative base can't be invoiced`); throw new UserError(`A ticket with a negative base can't be invoiced`);
} else { } else {
@ -95,7 +91,7 @@ module.exports = Self => {
const company = await models.Ticket.findOne({ const company = await models.Ticket.findOne({
fields: ['companyFk'], fields: ['companyFk'],
where: { where: {
clientFk: clientId, clientFk: clientFk,
shipped: {lte: maxShipped} shipped: {lte: maxShipped}
} }
}, myOptions); }, myOptions);
@ -103,7 +99,7 @@ module.exports = Self => {
} }
// Validate invoiceable client // Validate invoiceable client
const isClientInvoiceable = await isInvoiceable(clientId, myOptions); const isClientInvoiceable = await isInvoiceable(clientFk, myOptions);
if (!isClientInvoiceable) if (!isClientInvoiceable)
throw new UserError(`This client is not invoiceable`); throw new UserError(`This client is not invoiceable`);
@ -114,27 +110,27 @@ module.exports = Self => {
if (maxShipped >= tomorrow) if (maxShipped >= tomorrow)
throw new UserError(`Can't invoice to future`); throw new UserError(`Can't invoice to future`);
const maxInvoiceDate = await getMaxIssued(args.serial, companyId, myOptions); const maxInvoiceDate = await getMaxIssued(serial, companyId, myOptions);
if (Date.vnNew() < maxInvoiceDate) if (Date.vnNew() < maxInvoiceDate)
throw new UserError(`Can't invoice to past`); throw new UserError(`Can't invoice to past`);
if (ticketId) { if (ticketFk) {
query = `CALL invoiceOut_newFromTicket(?, ?, ?, ?, @newInvoiceId)`; query = `CALL invoiceOut_newFromTicket(?, ?, ?, ?, @newInvoiceId)`;
await Self.rawSql(query, [ await Self.rawSql(query, [
ticketId, ticketFk,
args.serial, serial,
args.taxArea, taxArea,
args.reference reference
], myOptions); ], myOptions);
} else { } else {
query = `CALL invoiceOut_newFromClient(?, ?, ?, ?, ?, ?, @newInvoiceId)`; query = `CALL invoiceOut_newFromClient(?, ?, ?, ?, ?, ?, @newInvoiceId)`;
await Self.rawSql(query, [ await Self.rawSql(query, [
clientId, clientFk,
args.serial, serial,
maxShipped, maxShipped,
companyId, companyId,
args.taxArea, taxArea,
args.reference reference
], myOptions); ], myOptions);
} }
@ -146,26 +142,27 @@ module.exports = Self => {
throw e; throw e;
} }
if (newInvoice.id) if (!newInvoice.id) throw new UserError('It was not able to create the invoice');
await Self.createPdf(ctx, newInvoice.id);
await Self.createPdf(ctx, newInvoice.id);
return newInvoice; return newInvoice;
}; };
async function isInvoiceable(clientId, options) { async function isInvoiceable(clientFk, options) {
const models = Self.app.models; const models = Self.app.models;
const query = `SELECT (hasToInvoice AND isTaxDataChecked) AS invoiceable const query = `SELECT (hasToInvoice AND isTaxDataChecked) AS invoiceable
FROM client FROM client
WHERE id = ?`; WHERE id = ?`;
const [result] = await models.InvoiceOut.rawSql(query, [clientId], options); const [result] = await models.InvoiceOut.rawSql(query, [clientFk], options);
return result.invoiceable; return result.invoiceable;
} }
async function getNegativeBase(maxShipped, clientId, companyId, options) { async function getNegativeBase(maxShipped, clientFk, companyId, options) {
const models = Self.app.models; const models = Self.app.models;
await models.InvoiceOut.rawSql('CALL invoiceOut_exportationFromClient(?,?,?)', await models.InvoiceOut.rawSql('CALL invoiceOut_exportationFromClient(?,?,?)',
[maxShipped, clientId, companyId], options [maxShipped, clientFk, companyId], options
); );
const query = 'SELECT vn.hasAnyNegativeBase() AS base'; const query = 'SELECT vn.hasAnyNegativeBase() AS base';
const [result] = await models.InvoiceOut.rawSql(query, [], options); const [result] = await models.InvoiceOut.rawSql(query, [], options);

View File

@ -70,7 +70,7 @@ module.exports = Self => {
c.hasToInvoice, c.hasToInvoice,
c.isTaxDataChecked, c.isTaxDataChecked,
w.id comercialId, w.id comercialId,
CONCAT(w.firstName, ' ', w.lastName) comercialName u.name workerName
FROM vn.ticket t FROM vn.ticket t
JOIN vn.company co ON co.id = t.companyFk JOIN vn.company co ON co.id = t.companyFk
JOIN vn.sale s ON s.ticketFk = t.id JOIN vn.sale s ON s.ticketFk = t.id

View File

@ -1,13 +1,10 @@
const models = require('vn-loopback/server/server').models; const {models} = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
describe('InvoiceOut createManualInvoice()', () => { describe('InvoiceOut createManualInvoice()', () => {
const userId = 1;
const ticketId = 16; const ticketId = 16;
const clientId = 1106; const clientId = 1106;
const activeCtx = { const activeCtx = {accessToken: {userId: 1}};
accessToken: {userId: userId},
};
const ctx = {req: activeCtx}; const ctx = {req: activeCtx};
it('should throw an error trying to invoice again', async() => { it('should throw an error trying to invoice again', async() => {
@ -18,13 +15,8 @@ describe('InvoiceOut createManualInvoice()', () => {
let error; let error;
try { try {
ctx.args = { await createInvoice(ctx, options, undefined, ticketId);
ticketFk: ticketId, await createInvoice(ctx, options, undefined, ticketId);
serial: 'T',
taxArea: 'CEE'
};
await models.InvoiceOut.createManualInvoice(ctx, options);
await models.InvoiceOut.createManualInvoice(ctx, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
@ -47,17 +39,9 @@ describe('InvoiceOut createManualInvoice()', () => {
let error; let error;
try { try {
const ticket = await models.Ticket.findById(ticketId, null, options); const ticket = await models.Ticket.findById(ticketId, null, options);
await ticket.updateAttributes({ await ticket.updateAttributes({totalWithVat: 0}, options);
totalWithVat: 0
}, options);
ctx.args = {
ticketFk: ticketId,
serial: 'T',
taxArea: 'CEE'
};
await models.InvoiceOut.createManualInvoice(ctx, options);
await createInvoice(ctx, options, undefined, ticketId);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
error = e; error = e;
@ -75,13 +59,7 @@ describe('InvoiceOut createManualInvoice()', () => {
let error; let error;
try { try {
ctx.args = { await createInvoice(ctx, options, clientId);
clientFk: clientId,
serial: 'T',
taxArea: 'CEE'
};
await models.InvoiceOut.createManualInvoice(ctx, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
error = e; error = e;
@ -103,16 +81,9 @@ describe('InvoiceOut createManualInvoice()', () => {
let error; let error;
try { try {
const client = await models.Client.findById(clientId, null, options); const client = await models.Client.findById(clientId, null, options);
await client.updateAttributes({ await client.updateAttributes({isTaxDataChecked: false}, options);
isTaxDataChecked: false
}, options);
ctx.args = { await createInvoice(ctx, options, undefined, ticketId);
ticketFk: ticketId,
serial: 'T',
taxArea: 'CEE'
};
await models.InvoiceOut.createManualInvoice(ctx, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
@ -130,12 +101,7 @@ describe('InvoiceOut createManualInvoice()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
try { try {
ctx.args = { const result = await createInvoice(ctx, options, undefined, ticketId);
ticketFk: ticketId,
serial: 'T',
taxArea: 'CEE'
};
const result = await models.InvoiceOut.createManualInvoice(ctx, options);
expect(result.id).toEqual(jasmine.any(Number)); expect(result.id).toEqual(jasmine.any(Number));
@ -146,3 +112,18 @@ describe('InvoiceOut createManualInvoice()', () => {
} }
}); });
}); });
function createInvoice(
ctx,
options,
clientFk = undefined,
ticketFk = undefined,
maxShipped = undefined,
serial = 'T',
taxArea = 'CEE',
reference = undefined
) {
return models.InvoiceOut.createManualInvoice(
ctx, clientFk, ticketFk, maxShipped, serial, taxArea, reference, options
);
}

View File

@ -15,5 +15,13 @@
"description": { "description": {
"type": "string" "type": "string"
} }
} },
"acls": [
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
]
} }

View File

@ -114,7 +114,7 @@
<vn-span <vn-span
class="link" class="link"
ng-click="workerDescriptor.show($event, client.comercialId)"> ng-click="workerDescriptor.show($event, client.comercialId)">
{{::client.comercialName | dashIfEmpty}} {{::client.workerName | dashIfEmpty}}
</vn-span> </vn-span>
</td> </td>
</tr> </tr>

View File

@ -1,4 +1,3 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const buildFilter = require('vn-loopback/util/filter').buildFilter; const buildFilter = require('vn-loopback/util/filter').buildFilter;
const mergeFilters = require('vn-loopback/util/filter').mergeFilters; const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
@ -135,7 +134,8 @@ module.exports = Self => {
tr.requesterFk, tr.requesterFk,
tr.isOk, tr.isOk,
s.quantity saleQuantity, s.quantity saleQuantity,
s.itemFk, s.itemFk saleItemFk,
i.id itemFk,
i.name itemDescription, i.name itemDescription,
t.shipped, t.shipped,
DATE(t.shipped) shippedDate, DATE(t.shipped) shippedDate,

View File

@ -150,7 +150,7 @@ module.exports = Self => {
const salesNewTicket = salesMovable.filter(sale => (sale.movable ? sale.movable : 0) >= sale.quantity); const salesNewTicket = salesMovable.filter(sale => (sale.movable ? sale.movable : 0) >= sale.quantity);
const salesNewTicketLength = salesNewTicket.length; const salesNewTicketLength = salesNewTicket.length;
if (salesNewTicketLength && sales.length != salesNewTicketLength) { if (salesNewTicketLength && (args.newTicket || sales.length != salesNewTicketLength)) {
const newTicket = await models.Ticket.transferSales( const newTicket = await models.Ticket.transferSales(
ctx, ctx,
args.id, args.id,

View File

@ -118,7 +118,7 @@ module.exports = Self => {
const [salesMovable] = await Self.rawSql(query, params, myOptions); const [salesMovable] = await Self.rawSql(query, params, myOptions);
const itemMovable = new Map(); const itemMovable = new Map();
for (sale of salesMovable) { for (let sale of salesMovable) {
const saleMovable = sale.movable ? sale.movable : 0; const saleMovable = sale.movable ? sale.movable : 0;
itemMovable.set(sale.id, saleMovable); itemMovable.set(sale.id, saleMovable);
} }
@ -129,7 +129,7 @@ module.exports = Self => {
const [difComponents] = await Self.rawSql(query, params, myOptions); const [difComponents] = await Self.rawSql(query, params, myOptions);
const map = new Map(); const map = new Map();
for (difComponent of difComponents) for (let difComponent of difComponents)
map.set(difComponent.saleFk, difComponent); map.set(difComponent.saleFk, difComponent);
for (sale of salesObj.items) { for (sale of salesObj.items) {

View File

@ -1,5 +1,4 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
const UserError = require('vn-loopback/util/user-error');
const ForbiddenError = require('vn-loopback/util/forbiddenError'); const ForbiddenError = require('vn-loopback/util/forbiddenError');
describe('sale priceDifference()', () => { describe('sale priceDifference()', () => {
@ -83,12 +82,10 @@ describe('sale priceDifference()', () => {
warehouseId: 1 warehouseId: 1
}; };
const result = await models.Ticket.priceDifference(ctx, options); const {items} = await models.Ticket.priceDifference(ctx, options);
const firstItem = result.items[0];
const secondtItem = result.items[1];
expect(firstItem.movable).toEqual(380); expect(items[0].movable).toEqual(410);
expect(secondtItem.movable).toEqual(1790); expect(items[1].movable).toEqual(1810);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -45,7 +45,7 @@ describe('workerTimeControl clockIn()', () => {
throw e; throw e;
} }
}); });
it('should throw an error trying to change a middle hour to out not resting 12h', async() => { it('should throw an error trying to change a middle hour to out not resting 12h', async() => {
activeCtx.accessToken.userId = HHRRId; activeCtx.accessToken.userId = HHRRId;
const workerId = teamBossId; const workerId = teamBossId;
@ -99,6 +99,32 @@ describe('workerTimeControl clockIn()', () => {
} }
}); });
it('should throw an error trying to add an "in" entry if the last clockIn is not out', async() => {
activeCtx.accessToken.userId = HHRRId;
const workerId = teamBossId;
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
ctx.args = {timed: "2000-12-25T21:00:00.000Z", direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
ctx.args = {timed: "2000-12-25T22:00:00.000Z", direction: 'middle'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
ctx.args = {timed: "2000-12-25T22:30:00.000Z", direction: 'middle'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
ctx.args = {timed: "2000-12-26T01:00:00.000Z", direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
await tx.rollback();
} catch (e) {
expect(e.message).toBe('Dirección incorrecta');
await tx.rollback();
}
});
describe('as Role errors', () => { describe('as Role errors', () => {
it('should add if the current user is team boss and the target user is himself', async() => { it('should add if the current user is team boss and the target user is himself', async() => {
activeCtx.accessToken.userId = teamBossId; activeCtx.accessToken.userId = teamBossId;