7199-devToTest_2316 #2287

Merged
alexm merged 169 commits from 7199-devToTest_2316 into test 2024-04-11 06:25:23 +00:00
49 changed files with 985 additions and 435 deletions
Showing only changes of commit 9f438751d3 - Show all commits

View File

@ -1,5 +0,0 @@
DELETE FROM salix.ACL
WHERE model = 'MailAliasAccount'
AND property = 'canEditAlias'
AND principalType = 'ROLE'
AND principalId = 'marketingBoss';

View File

@ -2400,7 +2400,8 @@ INSERT INTO `vn`.`dmsType`(`id`, `name`, `readRoleFk`, `writeRoleFk`, `code`)
(18, 'dua', NULL, NULL, 'dua'),
(19, 'inmovilizado', NULL, NULL, 'fixedAssets'),
(20, 'Reclamación', 1, 1, 'claim'),
(21, 'Entrada', 1, 1, 'entry');
(21, 'Entrada', 1, 1, 'entry'),
(22, 'Proveedor', 1, 1, 'supplier');
INSERT INTO `vn`.`dms`(`id`, `dmsTypeFk`, `file`, `contentType`, `workerFk`, `warehouseFk`, `companyFk`, `hardCopyNumber`, `hasFile`, `reference`, `description`, `created`)
VALUES
@ -2412,7 +2413,8 @@ INSERT INTO `vn`.`dms`(`id`, `dmsTypeFk`, `file`, `contentType`, `workerFk`, `wa
(6, 5, '6.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'NotExists', 'DoesNotExists', util.VN_CURDATE()),
(7, 20, '7.jpg', 'image/jpeg', 9, 1, 442, NULL, FALSE, '1', 'TICKET ID DEL CLIENTE BRUCE WAYNE ID 1101', util.VN_CURDATE()),
(8, 20, '8.mp4', 'video/mp4', 9, 1, 442, NULL, FALSE, '1', 'TICKET ID DEL CLIENTE BRUCE WAYNE ID 1101', util.VN_CURDATE()),
(9, 21, '7.jpg', 'image/jpeg', 9, 1, 442, NULL, FALSE, '1', 'ENTRADA ID 1', util.VN_CURDATE());
(9, 21, '7.jpg', 'image/jpeg', 9, 1, 442, NULL, FALSE, '1', 'ENTRADA ID 1', util.VN_CURDATE()),
(10, 21, '7.jpg', 'image/jpeg', 9, 1, 442, NULL, FALSE, '1', 'ENTRADA DE PRUEBA', util.VN_CURDATE());
INSERT INTO `vn`.`claimDms`(`claimFk`, `dmsFk`)
VALUES
@ -3740,3 +3742,7 @@ INSERT INTO vn.parkingLog(originFk, userFk, `action`, creationDate, description,
INSERT INTO vn.ticketLog (originFk,userFk,`action`,creationDate,changedModel,newInstance,changedModelId,changedModelValue)
VALUES (18,9,'insert','2001-01-01 11:01:00.000','Ticket','{"isDeleted":true}',45,'Super Man');
INSERT INTO `vn`.`supplierDms`(`supplierFk`, `dmsFk`, `editorFk`)
VALUES
(1, 10, 9);

View File

@ -32,7 +32,7 @@ BEGIN
WHERE c.available > 0
GROUP BY c.item_id;
CALL vn.catalog_calculate(vDelivery, vAddress, vAgencyMode);
CALL vn.catalog_calculate(vDelivery, vAddress, vAgencyMode, FALSE);
DROP TEMPORARY TABLE tmp.item;
END$$

View File

@ -16,7 +16,7 @@ BEGIN
ENGINE = MEMORY
SELECT vSelf itemFk;
CALL vn.catalog_calculate(vLanded, vAddressFk, vAgencyModeFk);
CALL vn.catalog_calculate(vLanded, vAddressFk, vAgencyModeFk, FALSE);
SELECT l.warehouseFk, w.name warehouse,
p.`grouping`, p.price, p.rate, l.available

View File

@ -27,7 +27,7 @@ BEGIN
WHERE orderFk = vSelf
GROUP BY itemFk;
CALL vn.catalog_calculate(vDate, vAddress, vAgencyMode);
CALL vn.catalog_calculate(vDate, vAddress, vAgencyMode, FALSE);
DROP TEMPORARY TABLE tmp.item;
END$$

View File

@ -22,7 +22,7 @@ BEGIN
FROM `order`
WHERE id = vSelf;
CALL vn.catalog_calculate(vDate, vAddress, vAgencyMode);
CALL vn.catalog_calculate(vDate, vAddress, vAgencyMode, FALSE);
IF account.myUser_getName() = 'visitor' THEN
UPDATE tmp.ticketCalculateItem

View File

@ -20,6 +20,7 @@ BEGIN
CALL vn.addressTaxArea();
SELECT areaFk INTO vTaxArea FROM tmp.addressTaxArea;
DROP TEMPORARY TABLE
tmp.addressCompany,
tmp.addressTaxArea;

View File

@ -14,7 +14,10 @@ BEGIN
SELECT `value` INTO price
FROM vn.specialPrice
WHERE itemFk = vItemFk
AND clientFk = vClientFk ;
AND (clientFk = vClientFk OR clientFk IS NULL)
AND started <= util.VN_CURDATE()
AND (ended >= util.VN_CURDATE() OR ended IS NULL)
ORDER BY id DESC LIMIT 1;
RETURN price;
END$$
DELIMITER ;

View File

@ -0,0 +1,283 @@
DELIMITER $$
$$
CREATE OR REPLACE PROCEDURE vn.sale_boxPickingPrint(
IN vPrinterFk INT,
IN vSaleFk INT,
IN vPacking INT,
IN vSectorFk INT,
IN vUserFk INT,
IN vPackagingFk INT,
IN vPackingSiteFk INT)
BEGIN
/** Splits a line of sale to a different ticket and prints the transport sticker
*/
DECLARE vAgencyModeFk INT;
DECLARE vConcept VARCHAR(30);
DECLARE vExpeditionFk INT;
DECLARE vItemFk INT;
DECLARE vItemShelvingFk INT;
DECLARE vItemShelvingSaleFk INT;
DECLARE vItemShelvingSaleFk_old INT;
DECLARE vLastExpeditionTimeStamp DATETIME;
DECLARE vMaxPhoneLength INT DEFAULT 11;
DECLARE vMaxStreetLength INT DEFAULT 36;
DECLARE vNewSaleFk INT;
DECLARE vNewTicketFk INT;
DECLARE vParkingCode VARCHAR(10);
DECLARE vQuantity INT;
DECLARE vRemainder INT DEFAULT 0;
DECLARE vRemainderSaleFk INT;
DECLARE vShelving VARCHAR(10);
DECLARE vTicketFk INT;
SELECT s.quantity,
s.quantity MOD vPacking,
s.ticketFk,
s.itemFk,
s.concept
INTO vQuantity,
vRemainder,
vTicketFk,
vItemFk,
vConcept
FROM sale s
WHERE s.id = vSaleFk;
IF vRemainder THEN
UPDATE sale SET quantity = quantity - vRemainder WHERE id = vSaleFk;
INSERT INTO sale(ticketFk, itemFk, quantity, price, discount, concept)
SELECT ticketFk, itemFk, vRemainder, price, discount, concept
FROM sale
WHERE id = vSaleFk;
SET vRemainderSaleFk = LAST_INSERT_ID();
INSERT INTO saleComponent(saleFk, componentFk, value)
SELECT vRemainderSaleFk, componentFk, value
FROM saleComponent
WHERE saleFk = vSaleFk;
END IF;
w1: WHILE vQuantity >= vPacking DO
SET vItemShelvingFk = NULL;
SELECT sub.id
INTO vItemShelvingFk
FROM productionConfig pc
JOIN (
SELECT ish.id,
ish.visible - IFNULL(SUM(iss.quantity),0) available,
p.pickingOrder,
ish.created
FROM itemShelving ish
JOIN shelving sh ON sh.code = ish.shelvingFk
JOIN parking p ON p.id = sh.parkingFk
LEFT JOIN itemShelvingSale iss
ON iss.itemShelvingFk = ish.id
AND iss.created >= CURDATE()
AND iss.isPicked = FALSE
WHERE ish.itemFk = vItemFk
AND p.sectorFk = vSectorFk
GROUP BY ish.id
HAVING available >= vPacking) sub
ORDER BY IF(pc.orderMode = 'Location',sub.pickingOrder, sub.created)
LIMIT 1;
IF vItemShelvingFk THEN
INSERT INTO itemShelvingSale
SET itemShelvingFk = vItemShelvingFk,
saleFk = vSaleFk,
quantity = vPacking,
userFk = vUserFk,
isPicked = TRUE;
SET vItemShelvingSaleFk = LAST_INSERT_ID();
UPDATE sale SET isPicked = FALSE WHERE id = vSaleFk;
ELSE
LEAVE w1;
END IF;
SET vNewTicketFk = NULL;
SELECT MAX(t.id) INTO vNewTicketFk
FROM ticket t
JOIN ticketLastState tls ON tls.ticketFk = t.id
JOIN (SELECT addressFk, clientFk, date(shipped) shipped, warehouseFk
FROM ticket
WHERE id = vTicketFk) tt
ON tt.addressFk = t.addressFk
AND tt.clientFk = t.clientFk
AND t.shipped BETWEEN tt.shipped AND util.dayend(tt.shipped)
AND t.warehouseFk = tt.warehouseFk
WHERE tls.name = 'Encajado' ;
IF ISNULL(vNewTicketFk) THEN
INSERT INTO ticket( clientFk,
shipped,
addressFk,
agencyModeFk,
nickname,
warehouseFk,
companyFk,
landed,
zoneFk,
zonePrice,
zoneBonus,
routeFk,
priority,
hasPriority,
clonedFrom)
SELECT clientFk,
shipped,
addressFk,
agencyModeFk,
nickname,
warehouseFk,
companyFk,
landed,
zoneFk,
zonePrice,
zoneBonus,
routeFk,
priority,
hasPriority,
id
FROM ticket
WHERE id = vTicketFk;
SET vNewTicketFk = LAST_INSERT_ID();
INSERT INTO ticketTracking(ticketFk, stateFk, userFk)
SELECT vNewTicketFk, id, vUserFk
FROM state
WHERE code = 'PACKED';
END IF;
UPDATE sale SET quantity = quantity - vPacking WHERE id = vSaleFk;
UPDATE itemShelving SET visible = visible - vPacking WHERE id = vItemShelvingFk;
SET vNewSaleFk = NULL;
SELECT MAX(id) INTO vNewSaleFk
FROM sale
WHERE ticketFk = vNewTicketFk
AND itemFk = vItemFk;
IF vNewSaleFk THEN
UPDATE sale
SET quantity = quantity + vPacking
WHERE id = vNewSaleFk;
SET vItemShelvingSaleFk_old = NULL;
SELECT MAX(id) INTO vItemShelvingSaleFk_old
FROM itemShelvingSale
WHERE itemShelvingFk = vItemShelvingFk
AND saleFk = vNewSaleFk;
IF vItemShelvingSaleFk_old THEN
UPDATE itemShelvingSale
SET quantity = quantity + vPacking
WHERE id = vItemShelvingSaleFk_old;
DELETE FROM itemShelvingSale
WHERE id = vItemShelvingSaleFk;
SET vItemShelvingSaleFk = vItemShelvingSaleFk_old;
ELSE
UPDATE itemShelvingSale
SET saleFk = vNewSaleFk
WHERE id = vItemShelvingSaleFk;
END IF;
ELSE
INSERT INTO sale(ticketFk, itemFk, concept, quantity, discount, price)
SELECT vNewTicketFk, itemFk, concept, vPacking, discount, price
FROM sale
WHERE id = vSaleFk;
SET vNewSaleFk = LAST_INSERT_ID();
INSERT INTO saleComponent(saleFk, componentFk, value, isGreuge)
SELECT vNewSaleFk, componentFk, value, isGreuge
FROM saleComponent
WHERE saleFk = vSaleFk;
UPDATE itemShelvingSale
SET saleFk = vNewSaleFk
WHERE id = vItemShelvingSaleFk;
END IF;
INSERT IGNORE INTO saleTracking(saleFk, isChecked, workerFk, stateFk)
SELECT vNewSaleFk, TRUE, vUserFk, id
FROM state
WHERE code = 'PREPARED';
SELECT agencyModeFk INTO vAgencyModeFk
FROM ticket
WHERE id = vNewTicketFk;
INSERT INTO expedition(
agencyModeFk,
ticketFk,
freightItemFk,
workerFk,
packagingFk,
itemPackingTypeFk,
hostFk,
packingSiteFk,
monitorId,
started,
ended
)
SELECT vAgencyModeFk,
vNewTicketFk,
i.id,
vUserFk,
vPackagingFk,
ps.code,
h.code,
vPackingSiteFk,
ps.monitorId,
IFNULL(vLastExpeditionTimeStamp, NOW()),
NOW()
FROM packingSite ps
JOIN host h ON h.id = ps.hostFk
JOIN item i ON i.name = 'Shipping cost'
WHERE ps.id = vPackingSiteFk
LIMIT 1;
SET vExpeditionFk = LAST_INSERT_ID();
SET vLastExpeditionTimeStamp = NOW();
CALL dipole.expedition_Add(vExpeditionFk,vPrinterFk, TRUE);
SELECT shelvingFk, p.code
INTO vShelving, vParkingCode
FROM itemShelving ish
JOIN shelving sh ON sh.code = ish.shelvingFk
JOIN parking p ON p.id = sh.parkingFk
WHERE ish.id = vItemShelvingFk;
UPDATE dipole.expedition_PrintOut
SET isPrinted = FALSE,
itemFk = vItemFk,
quantity = vPacking,
longName = vConcept,
shelvingFk = vShelving,
parkingCode = vParkingCode,
phone = RIGHT(phone,vMaxPhoneLength),
street = RIGHT(street, vMAxStreetLength)
WHERE expeditionFk = vExpeditionFk;
DELETE FROM sale
WHERE quantity = 0
AND id = vSaleFk;
END WHILE;
END$$
DELIMITER ;

View File

@ -20,7 +20,7 @@ BEGIN
ENGINE = MEMORY
SELECT vItemFk itemFk;
CALL catalog_calculate(vLanded, vAddressFk, vAgencyModeFk);
CALL catalog_calculate(vLanded, vAddressFk, vAgencyModeFk, TRUE);
DROP TEMPORARY TABLE tmp.item;
END$$
DELIMITER ;

View File

@ -1,5 +1,9 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`catalog_calculate`(vLanded DATE, vAddressFk INT, vAgencyModeFk INT)
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`catalog_calculate`(
vLanded DATE,
vAddressFk INT,
vAgencyModeFk INT,
vShowExpiredZones BOOLEAN)
BEGIN
/**
* Calcula los articulos disponibles y sus precios
@ -25,7 +29,7 @@ BEGIN
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
CALL vn.zone_getShipped (vLanded, vAddressFk, vAgencyModeFk, FALSE);
CALL vn.zone_getShipped (vLanded, vAddressFk, vAgencyModeFk, vShowExpiredZones);
DROP TEMPORARY TABLE IF EXISTS tmp.ticketLot;
CREATE TEMPORARY TABLE tmp.ticketLot(

View File

@ -25,6 +25,19 @@ BEGIN
FROM address
WHERE id = vAddressFk;
CREATE OR REPLACE TEMPORARY TABLE tSpecialPrice
(INDEX (itemFk))
ENGINE = MEMORY
SELECT * FROM (
SELECT *
FROM specialPrice
WHERE (clientFk = vClientFk OR clientFk IS NULL)
AND started <= vShipped
AND (ended >= vShipped OR ended IS NULL)
ORDER BY (clientFk = vClientFk) DESC, id DESC
LIMIT 10000000000000000000) t
GROUP BY itemFk;
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentCalculate
(PRIMARY KEY (itemFk, warehouseFk))
ENGINE = MEMORY
@ -44,8 +57,7 @@ BEGIN
JOIN item i ON i.id = tl.itemFk
JOIN itemType it ON it.id = i.typeFk
JOIN itemCategory ic ON ic.id = it.categoryFk
LEFT JOIN specialPrice sp ON sp.itemFk = i.id
AND sp.clientFk = vClientFk
LEFT JOIN tSpecialPrice sp ON sp.itemFk = i.id
LEFT JOIN (
SELECT * FROM (
SELECT pf.itemFk,
@ -146,7 +158,7 @@ BEGIN
FROM tmp.ticketComponentBase tcb
JOIN vn.component c2 ON c2.code = 'lastUnitsDiscount'
JOIN tmp.ticketComponentCalculate tcc ON tcc.itemFk = tcb.itemFk AND tcc.warehouseFk = tcb.warehouseFk
LEFT JOIN specialPrice sp ON sp.clientFk = vClientFk AND sp.itemFk = tcc.itemFk
LEFT JOIN tSpecialPrice sp ON sp.itemFk = tcc.itemFk
JOIN vn.item i ON i.id = tcb.itemFk
WHERE sp.value IS NULL
AND i.supplyResponseFk IS NULL;
@ -169,7 +181,7 @@ BEGIN
FROM tmp.ticketComponentCalculate tcc
JOIN vn.component c2 ON c2.code = 'salePerPackage'
JOIN buy b ON b.id = tcc.buyFk
LEFT JOIN specialPrice sp ON sp.clientFk = vClientFk AND sp.itemFk = tcc.itemFk
LEFT JOIN tSpecialPrice sp ON sp.itemFk = tcc.itemFk
WHERE sp.value IS NULL;
CREATE OR REPLACE TEMPORARY TABLE tmp.`zone` (INDEX (id))
@ -208,7 +220,7 @@ BEGIN
sp.value - SUM(tcc.cost) sumCost
FROM tmp.ticketComponentCopy tcc
JOIN component c ON c.id = tcc.componentFk
JOIN specialPrice sp ON sp.clientFk = vClientFK AND sp.itemFk = tcc.itemFk
JOIN tSpecialPrice sp ON sp.itemFk = tcc.itemFk
JOIN vn.component c2 ON c2.code = 'specialPrices'
WHERE c.classRate IS NULL
AND tcc.warehouseFk = vWarehouseFk
@ -289,6 +301,7 @@ BEGIN
tmp.ticketComponentSum,
tmp.ticketComponentBase,
tmp.ticketComponentRate,
tmp.ticketComponentCopy;
tmp.ticketComponentCopy,
tSpecialPrice;
END$$
DELIMITER ;

View File

@ -30,6 +30,7 @@ BEGIN
DELETE IGNORE FROM expedition WHERE created < v26Months;
DELETE FROM sms WHERE created < v18Months;
DELETE FROM saleTracking WHERE created < v1Years;
DELETE FROM productionError WHERE dated < v1Years;
DELETE FROM ticketTracking WHERE created < v18Months;
DELETE tobs FROM ticketObservation tobs
JOIN ticket t ON tobs.ticketFk = t.id

View File

@ -169,7 +169,7 @@ BEGIN
AND vp.supplierFk = rd.supplierFk
AND vp.companyFk = rd.companyFk
AND vp.currencyFk = rd.currencyFk
WHERE vp.isPayment = FALSE;
WHERE NOT vp.isPayment;
SELECT vp.expirationId,
vp.dated,
@ -183,11 +183,15 @@ BEGIN
vp.isPayment,
vp.isReconciled,
vp.endingBalance,
cr.amount clientRiskAmount
cr.amount clientRiskAmount,
co.CEE
FROM tPendingDuedates vp
LEFT JOIN supplier s ON s.id = vp.supplierFk
LEFT JOIN client c ON c.fi = s.nif
LEFT JOIN clientRisk cr ON cr.clientFk = c.id
LEFT JOIN supplierAccount sa ON sa.supplierFk = s.id
LEFT JOIN bankEntity be ON be.id = sa.bankEntityFk
LEFT JOIN country co ON co.id = be.countryFk
AND cr.companyFk = vp.companyFk;
DROP TEMPORARY TABLE tOpeningBalances;

View File

@ -0,0 +1,42 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`creditInsurance_getRisk`()
BEGIN
/**
* Devuelve el riesgo de los clientes que estan asegurados
*/
CREATE OR REPLACE TEMPORARY TABLE tmp.client_list
(PRIMARY KEY (Id_Cliente))
ENGINE = MEMORY
SELECT * FROM (
SELECT cc.client Id_Cliente, ci.grade
FROM creditClassification cc
JOIN creditInsurance ci ON cc.id = ci.creditClassification
WHERE dateEnd IS NULL
ORDER BY ci.creationDate DESC
LIMIT 10000000000000000000) t1
GROUP BY Id_Cliente;
CALL vn2008.risk_vs_client_list(util.VN_CURDATE());
SELECT
c.id,
c.name,
c.credit clientCredit,
c.creditInsurance solunion,
CAST(r.risk AS DECIMAL(10,0)) risk,
CAST(c.creditInsurance - r.risk AS DECIMAL(10,0)) riskAlive,
cac.invoiced billedAnnually,
c.dueDay,
ci.grade,
c2.country
FROM tmp.client_list ci
LEFT JOIN tmp.risk r ON r.Id_Cliente = ci.Id_Cliente
JOIN client c ON c.id = ci.Id_Cliente
JOIN bs.clientAnnualConsumption cac ON c.id = cac.clientFk
JOIN country c2 ON c2.id = c.countryFk
GROUP BY c.id;
DROP TEMPORARY TABLE IF EXISTS tmp.risk;
DROP TEMPORARY TABLE IF EXISTS tmp.client_list;
END$$
DELIMITER ;

View File

@ -46,6 +46,7 @@ BEGIN
JOIN parking p ON p.id = sh.parkingFk
JOIN tmp.productionBuffer pb ON pb.ticketFk = s.ticketFk
JOIN agencyMode am ON am.id = pb.agencyModeFk
JOIN agency a ON a .id = am.agencyFk
LEFT JOIN routesMonitor rm ON rm.routeFk = pb.routeFk
LEFT JOIN saleGroupDetail sgd ON sgd.saleFk = s.id
LEFT JOIN ticketState ts ON ts.ticketFk = s.ticketFk
@ -60,6 +61,7 @@ BEGIN
AND ((rm.bufferFk AND rm.isPickingAllowed)
OR am.code = 'REC_ALG')
AND pb.shipped = vDated
AND a.isOwn
GROUP BY s.id
ORDER BY etd;

View File

@ -77,7 +77,9 @@ BEGIN
vNewItemFk);
SELECT price INTO vNewPrice
FROM tmp.ticketCalculateItem;
FROM tmp.ticketComponentPrice
ORDER BY (vQuantity % `grouping`) ASC
LIMIT 1;
IF vNewPrice IS NULL THEN
CALL util.throw('price retrieval failed');

View File

@ -1,31 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`solunionRiskRequest`()
BEGIN
DROP TEMPORARY TABLE IF EXISTS tmp.client_list;
CREATE TEMPORARY TABLE tmp.client_list
(PRIMARY KEY (Id_Cliente))
ENGINE = MEMORY
SELECT * FROM (SELECT cc.client Id_Cliente, ci.grade FROM vn.creditClassification cc
JOIN vn.creditInsurance ci ON cc.id = ci.creditClassification
WHERE dateEnd IS NULL
ORDER BY ci.creationDate DESC
LIMIT 10000000000000000000) t1 GROUP BY Id_Cliente;
CALL vn2008.risk_vs_client_list(util.VN_CURDATE());
SELECT
c.Id_Cliente, c.Cliente, c.Credito credito_vn, c.creditInsurance solunion, cast(r.risk as DECIMAL(10,0)) riesgo_vivo,
cast(c.creditInsurance - r.risk as decimal(10,0)) margen_vivo,
f.Consumo consumo_anual, c.Vencimiento, ci.grade
FROM
vn2008.Clientes c
JOIN tmp.risk r ON r.Id_Cliente = c.Id_Cliente
JOIN tmp.client_list ci ON c.Id_Cliente = ci.Id_Cliente
JOIN bi.facturacion_media_anual f ON c.Id_Cliente = f.Id_Cliente
GROUP BY Id_cliente;
DROP TEMPORARY TABLE IF EXISTS tmp.risk;
DROP TEMPORARY TABLE IF EXISTS tmp.client_list;
END$$
DELIMITER ;

View File

@ -11,7 +11,7 @@ BEGIN
SELECT id itemFk FROM vn.item
WHERE typeFk = vTypeFk;
CALL catalog_calculate(vLanded, vAddressFk, vAgencyModeFk);
CALL catalog_calculate(vLanded, vAddressFk, vAgencyModeFk, FALSE);
DROP TEMPORARY TABLE tmp.item;
DROP TEMPORARY TABLE tmp.ticketLot;
END$$

View File

@ -23,9 +23,5 @@ BEGIN
CALL buy_checkItem();
END IF;
END IF;
IF (NOT(NEW.awbFk <=> OLD.awbFk)) AND NEW.awbFk IS NOT NULL AND NOT travel_hasUniqueAwb(NEW.id) THEN
CALL util.throw('The AWB is incorrect, there is a different AWB in the associated entries');
END IF;
END$$
DELIMITER ;

View File

@ -32,5 +32,9 @@ BEGIN
CALL util.throw('The travel has entries with booked invoices');
END IF;
END IF;
IF (NOT(NEW.awbFk <=> OLD.awbFk)) AND NEW.awbFk IS NOT NULL AND NOT travel_hasUniqueAwb(NEW.id) THEN
CALL util.throw('The AWB is incorrect, there is a different AWB in the associated entries');
END IF;
END$$
DELIMITER ;

View File

@ -0,0 +1,3 @@
-- Place your SQL code here
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES('SupplierDms', '*', '*', 'ALLOW', 'ROLE', 'employee');

View File

@ -0,0 +1,10 @@
ALTER TABLE vn.specialPrice MODIFY COLUMN clientFk int(11) NULL;
ALTER TABLE vn.specialPrice ADD started date NOT NULL DEFAULT '2024-01-01';
ALTER TABLE vn.specialPrice ADD ended date NULL;
ALTER TABLE vn.specialPrice MODIFY COLUMN itemFk int(11) NOT NULL;
ALTER TABLE vn.specialPrice MODIFY COLUMN value DECIMAL(10,2) NOT NULL;
ALTER TABLE vn.`specialPrice`
ADD CONSTRAINT `check_date_range`
CHECK (`ended` IS NULL OR `ended` >= `started`);

View File

@ -126,5 +126,20 @@
"allowedContentTypes": [
"application/x-7z-compressed"
]
},
"supplierStorage": {
"name": "supplierStorage",
"connector": "loopback-component-storage",
"provider": "filesystem",
"root": "./storage/dms",
"maxFileSize": "31457280",
"allowedContentTypes": [
"image/png",
"image/jpeg",
"image/jpg",
"image/webp",
"video/mp4",
"application/pdf"
]
}
}

View File

@ -23,15 +23,16 @@ export default class Controller extends Section {
}
openPdf() {
const access_token = this.vnToken.tokenMultimedia;
if (this.checked.length <= 1) {
const [invoiceOutId] = this.checked;
const url = `api/InvoiceOuts/${invoiceOutId}/download?access_token=${this.vnToken.tokenMultimedia}`;
const url = `api/InvoiceOuts/${invoiceOutId}/download?access_token=${access_token}`;
window.open(url, '_blank');
} else {
const invoiceOutIds = this.checked;
const invoicesIds = invoiceOutIds.join(',');
const serializedParams = this.$httpParamSerializer({
access_token: this.vnToken.token,
access_token,
ids: invoicesIds
});
const url = `api/InvoiceOuts/downloadZip?${serializedParams}`;

View File

@ -20,7 +20,6 @@ import './botanical';
import './barcode';
import './summary';
import './waste/index/';
import './waste/detail';
import './fixed-price';
import './fixed-price-search-panel';
import './item-type';

View File

@ -1,43 +0,0 @@
<vn-crud-model auto-load="true"
vn-id="model"
url="Items/getWasteByItem"
params="$ctrl.$params"
data="details">
</vn-crud-model>
<vn-data-viewer model="model">
<vn-card class="vn-w-md">
<section ng-repeat="detail in details" class="vn-pa-md">
<vn-horizontal class="header">
<h5>{{detail.family}} ({{detail.buyer}})</h5>
</vn-horizontal>
<vn-table>
<vn-thead>
<vn-tr>
<vn-th shrink>Item</vn-th>
<vn-th number>Percentage</vn-th>
<vn-th number>Dwindle</vn-th>
<vn-th number>Total</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="waste in detail.lines">
<vn-td shrink>
<span
ng-click="itemDescriptor.show($event, waste.itemFk)"
class="link">
{{::waste.itemFk}}
</span>
</vn-td>
<vn-td number>{{::(waste.percentage / 100) | percentage: 2}}</vn-td>
<vn-td number>{{::waste.dwindle | currency: 'EUR'}}</vn-td>
<vn-td number>{{::waste.total | currency: 'EUR'}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</section>
</vn-card>
</vn-data-viewer>
<vn-item-descriptor-popover
vn-id="item-descriptor"
warehouse-fk="$ctrl.vnConfig.warehouseFk">
</vn-item-descriptor-popover>

View File

@ -1,7 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
ngModule.vnComponent('vnItemWasteDetail', {
template: require('./index.html'),
controller: Section
});

View File

@ -1,25 +0,0 @@
@import "variables";
vn-item-waste {
.header {
margin-bottom: 16px;
text-transform: uppercase;
font-size: 1.25rem;
line-height: 1;
padding: 7px;
padding-bottom: 7px;
padding-bottom: 4px;
font-weight: lighter;
background-color: #fde6ca;
border-bottom: 1px solid #f7931e;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
vn-table vn-th.waste-family,
vn-table vn-td.waste-family {
max-width: 64px;
width: 64px
}
}

View File

@ -1,49 +1,2 @@
<vn-crud-model auto-load="true"
vn-id="model"
url="Items/getWasteByWorker"
data="details">
</vn-crud-model>
<vn-data-viewer model="model">
<vn-card>
<vn-table>
<vn-thead>
<vn-tr class="header">
<vn-th class="waste-family">Buyer</vn-th>
<vn-th class="waste-family">Family</vn-th>
<vn-th number>Percentage</vn-th>
<vn-th number>Dwindle</vn-th>
<vn-th number>Total</vn-th>
<vn-th shrink></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody ng-repeat="detail in details" class="vn-mb-md">
<vn-tr class="header">
<vn-td>{{::detail.buyer}}</vn-td>
<vn-td>{{::detail.family}}</vn-td>
<vn-td number>{{::(detail.percentage / 100) | percentage: 2}}</vn-td>
<vn-td number>{{::detail.dwindle | currency: 'EUR'}}</vn-td>
<vn-td number>{{::detail.total | currency: 'EUR'}}</vn-td>
<vn-td shrink>
<vn-icon-button
ng-class="{'hidden': !$ctrl.wasteConfig[detail.buyer].hidden}"
class="arrow pointer"
icon="keyboard_arrow_up"
vn-tooltip="Minimize/Maximize"
ng-click="$ctrl.toggleHidePanel(detail)">
</vn-icon-button>
</vn-td>
</vn-tr>
<a ng-repeat="waste in detail.lines" class="clickable vn-tr"
ui-sref="item.waste.detail({buyer: waste.buyer, family: waste.family})"
ng-show="$ctrl.wasteConfig[detail.buyer].hidden">
<vn-td></vn-td>
<vn-td>{{::waste.family}}</vn-td>
<vn-td number>{{::(waste.percentage / 100) | percentage: 2}}</vn-td>
<vn-td number>{{::waste.dwindle | currency: 'EUR'}}</vn-td>
<vn-td number>{{::waste.total | currency: 'EUR'}}</vn-td>
<vn-td shrink></vn-td>
</a>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-card>
</vn-card>

View File

@ -5,27 +5,11 @@ import './style.scss';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.getWasteConfig();
}
getWasteConfig() {
return this.wasteConfig = JSON.parse(localStorage.getItem('wasteConfig')) || {};
}
setWasteConfig() {
localStorage.setItem('wasteConfig', JSON.stringify(this.wasteConfig));
}
toggleHidePanel(detail) {
if (!this.wasteConfig[detail.buyer]) {
this.wasteConfig[detail.buyer] = {
hidden: true
};
} else
this.wasteConfig[detail.buyer].hidden = !this.wasteConfig[detail.buyer].hidden;
this.setWasteConfig();
async $onInit() {
this.$state.go('item.index');
window.location.href = 'https://grafana.verdnatura.es/d/TTNXQAxVk';
}
}

View File

@ -1,53 +0,0 @@
import './index.js';
import crudModel from 'core/mocks/crud-model';
describe('Item', () => {
describe('Component vnItemWasteIndex', () => {
let $scope;
let controller;
beforeEach(ngModule('item'));
beforeEach(inject(($componentController, $rootScope) => {
$scope = $rootScope.$new();
$scope.model = crudModel;
const $element = angular.element('<vn-item-waste-index></vn-item-waste-index>');
controller = $componentController('vnItemWasteIndex', {$element, $scope});
}));
describe('getWasteConfig / setWasteConfig', () => {
it('should return the local storage wasteConfig', () => {
const result = controller.getWasteConfig();
expect(result).toEqual({});
});
it('should set and return the local storage wasteConfig', () => {
controller.wasteConfig = {salesPerson: {hidden: true}};
controller.setWasteConfig();
const result = controller.getWasteConfig();
expect(result).toEqual(controller.wasteConfig);
});
});
describe('toggleHidePanel()', () => {
it('should make details hidden by default', () => {
controller.wasteConfig = {};
controller.toggleHidePanel({buyer: 'salesPerson'});
expect(controller.wasteConfig.salesPerson.hidden).toEqual(true);
});
it('should toggle hidden false', () => {
controller.wasteConfig = {salesPerson: {hidden: true}};
controller.toggleHidePanel({buyer: 'salesPerson'});
expect(controller.wasteConfig.salesPerson.hidden).toEqual(false);
});
});
});
});

View File

@ -92,10 +92,11 @@ module.exports = Self => {
// Calculate items
const order = await Self.findById(orderFk, null, myOptions);
stmts.push(new ParameterizedSQL(
'CALL vn.catalog_calculate(?, ?, ?)', [
'CALL vn.catalog_calculate(?, ?, ?, ?)', [
order.landed,
order.address_id,
order.agency_id,
false
]
));

View File

@ -64,10 +64,11 @@ module.exports = Self => {
stmts.push(stmt);
stmt = new ParameterizedSQL(
'CALL vn.catalog_calculate(?, ?, ?)', [
'CALL vn.catalog_calculate(?, ?, ?,?)', [
order.landed,
order.addressFk,
order.agencyModeFk,
false
]
);
stmts.push(stmt);

View File

@ -35,16 +35,18 @@ export default class Controller extends Section {
showRouteReport() {
const routesIds = [];
const access_token = this.vnToken.tokenMultimedia;
for (let route of this.checked)
routesIds.push(route.id);
const stringRoutesIds = routesIds.join(',');
if (this.checked.length <= 1) {
const url = `api/Routes/${stringRoutesIds}/driver-route-pdf?access_token=${this.vnToken.tokenMultimedia}`;
const url = `api/Routes/${stringRoutesIds}/driver-route-pdf?access_token=${access_token}`;
window.open(url, '_blank');
} else {
const serializedParams = this.$httpParamSerializer({
access_token: this.vnToken.token,
access_token,
id: stringRoutesIds
});
const url = `api/Routes/downloadZip?${serializedParams}`;

View File

@ -0,0 +1,59 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('downloadFile', {
description: 'Get the supplier file',
accessType: 'READ',
accepts: [
{
arg: 'id',
type: 'Number',
description: 'The file id',
http: {source: 'path'}
}
],
returns: [
{
arg: 'body',
type: 'file',
root: true
},
{
arg: 'Content-Type',
type: 'String',
http: {target: 'header'}
},
{
arg: 'Content-Disposition',
type: 'String',
http: {target: 'header'}
}
],
http: {
path: `/:id/downloadFile`,
verb: 'GET'
}
});
Self.downloadFile = async function(ctx, id) {
const models = Self.app.models;
const SupplierContainer = models.SupplierContainer;
const dms = await models.Dms.findById(id);
const pathHash = SupplierContainer.getHash(dms.id);
try {
await SupplierContainer.getFile(pathHash, dms.file);
} catch (e) {
if (e.code != 'ENOENT')
throw e;
const error = new UserError(`File doesn't exists`);
error.statusCode = 404;
throw error;
}
const stream = SupplierContainer.downloadStream(pathHash, dms.file);
return [stream, dms.contentType, `filename="${dms.file}"`];
};
};

View File

@ -0,0 +1,53 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('removeFile', {
description: 'Removes a entry document',
accessType: 'WRITE',
accepts: {
arg: 'id',
type: 'number',
description: 'The file id',
http: {source: 'path'}
},
returns: {
type: 'object',
root: true
},
http: {
path: `/:id/removeFile`,
verb: 'POST'
}
});
Self.removeFile = async(ctx, id, options) => {
const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const targetSupplierDms = await Self.findById(id, null, myOptions);
const targetDms = await Self.app.models.Dms.removeFile(ctx, targetSupplierDms.dmsFk, myOptions);
if (!targetDms)
throw new UserError('Try again');
const supplierDmsDestroyed = await targetSupplierDms.destroy(myOptions);
if (tx) await tx.commit();
return supplierDmsDestroyed;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,86 @@
module.exports = Self => {
Self.remoteMethodCtx('uploadFile', {
description: 'Upload and attach a file',
accessType: 'WRITE',
accepts: [{
arg: 'id',
type: 'number',
description: 'The supplier id',
http: {source: 'path'}
},
{
arg: 'warehouseId',
type: 'number',
description: 'The warehouse id',
required: true
},
{
arg: 'companyId',
type: 'number',
description: 'The company id',
required: true
},
{
arg: 'dmsTypeId',
type: 'number',
description: 'The dms type id',
required: true
},
{
arg: 'reference',
type: 'string',
required: true
},
{
arg: 'description',
type: 'string',
required: true
},
{
arg: 'hasFile',
type: 'boolean',
description: 'True if has an attached file',
required: true
}],
returns: {
type: 'object',
root: true
},
http: {
path: `/:id/uploadFile`,
verb: 'POST'
}
});
Self.uploadFile = async(ctx, id, options) => {
const {Dms, SupplierDms} = Self.app.models;
const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const uploadedFiles = await Dms.uploadFile(ctx, myOptions);
const promises = uploadedFiles.map(dms => SupplierDms.create({
supplierFk: id,
dmsFk: dms.id
}, myOptions));
await Promise.all(promises);
if (tx) await tx.commit();
return uploadedFiles;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -5,6 +5,12 @@
"Supplier": {
"dataSource": "vn"
},
"SupplierDms": {
"dataSource": "vn"
},
"SupplierContainer": {
"dataSource": "supplierStorage"
},
"SupplierAddress": {
"dataSource": "vn"
},

View File

@ -0,0 +1,10 @@
{
"name": "SupplierContainer",
"base": "Container",
"acls": [{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}]
}

View File

@ -0,0 +1,13 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
require('../methods/supplier/supplier-dms/removeFile')(Self);
require('../methods/supplier/supplier-dms/downloadFile')(Self);
require('../methods/supplier/supplier-dms/uploadFile')(Self);
Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY')
return new UserError(`The file is already attached to another entry`);
return err;
});
};

View File

@ -0,0 +1,37 @@
{
"name": "SupplierDms",
"base": "VnModel",
"mixins": {
"Loggable": true
},
"options": {
"mysql": {
"table": "supplierDms"
}
},
"allowedContentTypes": [
"image/png",
"image/jpeg",
"image/jpg",
"application/pdf"
],
"properties": {
"dmsFk": {
"type": "number",
"id": true,
"required": true
}
},
"relations": {
"supplier": {
"type": "belongsTo",
"model": "Supplier",
"foreignKey": "supplierFk"
},
"dms": {
"type": "belongsTo",
"model": "Dms",
"foreignKey": "dmsFk"
}
}
}

View File

@ -0,0 +1,40 @@
module.exports = Self => {
Self.remoteMethod('hasUncheckedTicket', {
description:
'Get boolean if the collection of ticket has a ticket not checked',
accessType: 'READ',
accepts: [{
arg: 'ticketFk',
type: 'integer',
required: true,
description: 'Ticket id'
}],
returns: {
type: 'boolean',
root: true
},
http: {
path: `/hasUncheckedTicket`,
verb: 'GET'
}
});
Self.hasUncheckedTicket = async(ticketFk, options) => {
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const result = await Self.rawSql(`
SELECT tc2.ticketFk
FROM vn.ticketCollection tc
JOIN vn.ticketCollection tc2 ON tc2.collectionFk = tc.collectionFk
LEFT JOIN vn.ticketState ts ON ts.ticketFk = tc2.ticketFk
JOIN vn.state s ON s.id = ts.stateFk
JOIN vn.state s2 ON s2.code = 'CHECKED'
WHERE tc.ticketFk = ? AND s.order < s2.id
LIMIT 1;`,
[ticketFk], myOptions);
return result.length > 0 && result[0]['ticketFk'] > 0;
};
};

View File

@ -0,0 +1,38 @@
const {models} = require('vn-loopback/server/server');
describe('ticketCollection hasUncheckedTicket()', () => {
it('should return false because there are not tickets not checked', async() => {
const ticketFk = 1;
const result = await models.TicketCollection.hasUncheckedTicket(ticketFk);
expect(result).toBe(false);
});
it('should return true because there is a ticket not checked', async() => {
const ticketFk = 1;
const stateFkCurrent = 16;
const stateFkUpdated = 7;
const tx = await models.TicketTracking.beginTransaction({});
const myOptions = {transaction: tx};
const filter = {where: {
ticketFk: 1,
stateFk: stateFkCurrent}
};
try {
const ticketTracking = await models.TicketTracking.findOne(filter, myOptions);
await ticketTracking.updateAttributes({
stateFk: stateFkUpdated
}, myOptions);
const result = await models.TicketCollection.hasUncheckedTicket(ticketFk, myOptions);
expect(result).toBe(true);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -0,0 +1,3 @@
module.exports = Self => {
require('../methods/ticket-collection/hasUncheckedTicket')(Self);
};

View File

@ -13,6 +13,9 @@
},
"usedShelves": {
"type": "number"
},
"collectionFk": {
"type": "number"
}
},
"relations": {
@ -20,6 +23,11 @@
"type": "belongsTo",
"model": "Ticket",
"foreignKey": "ticketFk"
},
"collection": {
"type": "belongsTo",
"model": "Collection",
"foreignKey": "collectionFk"
}
}
}

View File

@ -71,6 +71,34 @@ describe('workerTimeControl clockIn()', () => {
}
});
it('should updates the time entry direction and remaining not be manual', async() => {
activeCtx.accessToken.userId = HHRRId;
const workerId = teamBossId;
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
const entryTime = "2000-12-25T11:00:00.000Z";
ctx.args = {timed: entryTime, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
const middleTime ="2000-12-25T16:00:00.000Z";
ctx.args = {timed: middleTime, direction: 'middle'};
const middleEntryTime = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
middleEntryTime.updateAttribute('manual', false);
const direction = 'out';
const outTimeEntryId = await models.WorkerTimeControl.updateTimeEntry(ctx, middleEntryTime.id, direction, options);
const outTimeEntry = await models.WorkerTimeControl.findById(outTimeEntryId, null, options);
expect(outTimeEntry.manual).toBeFalsy();
await tx.rollback();
} catch (e) {
await tx.rollback();
}
});
describe('as Role errors', () => {
it('should add if the current user is team boss and the target user is himself', async() => {
activeCtx.accessToken.userId = teamBossId;
@ -144,7 +172,7 @@ describe('workerTimeControl clockIn()', () => {
const middleTime = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
const direction = 'out';
const {id:outTimeEntryId} = await models.WorkerTimeControl.updateTimeEntry(
const outTimeEntryId = await models.WorkerTimeControl.updateTimeEntry(
ctx, middleTime.id, direction, options
);

View File

@ -29,7 +29,7 @@ module.exports = Self => {
Self.updateTimeEntry = async(ctx, timeEntryId, direction, options) => {
const currentUserId = ctx.req.accessToken.userId;
const models = Self.app.models;
const myOptions = {};
const myOptions = {userId: currentUserId};
let tx;
if (typeof options == 'object')
@ -41,7 +41,7 @@ module.exports = Self => {
}
try {
const {id, userFk, timed} = await Self.findById(timeEntryId, null, myOptions);
const {id, userFk, timed, manual} = await Self.findById(timeEntryId, null, myOptions);
const isSubordinate = await models.Worker.isSubordinate(ctx, userFk, myOptions);
const isTeamBoss = await models.ACL.checkAccessAcl(ctx, 'Worker', 'isTeamBoss', 'WRITE');
const isHimself = currentUserId == userFk;
@ -50,12 +50,15 @@ module.exports = Self => {
if (notAllowed) throw new UserError(`You don't have enough privileges`);
await models.WorkerTimeControl.deleteById(id, myOptions);
const timeEntryUpdatedId = await Self.clockIn(userFk, timed, direction, null, myOptions);
const {id: newTimeEntryId} = await Self.clockIn(
userFk, timed, direction, null, myOptions
);
if (!manual) await Self.updateAll({id: newTimeEntryId}, {manual: false}, myOptions);
await models.WorkerTimeControl.resendWeeklyHourEmail(ctx, userFk, timed, myOptions);
if (tx) await tx.commit();
return timeEntryUpdatedId;
return newTimeEntryId;
} catch (e) {
if (tx) await tx.rollback();
throw e;

View File

@ -28,8 +28,8 @@
</table>
<p v-html="$t('wasteDetailLink')"></p>
<div class="external-link vn-pa-sm vn-m-md">
<a href="https://salix.verdnatura.es/#!/item/waste/index" target="_blank">
https://salix.verdnatura.es/#!/item/waste/index
<a href="https://grafana.verdnatura.es/d/TTNXQAxVk" target="_blank">
https://grafana.verdnatura.es/d/TTNXQAxVk
</a>
</div>
</div>