Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 7406-workerFormation
gitea/salix/pipeline/pr-dev This commit looks good
Details
gitea/salix/pipeline/pr-dev This commit looks good
Details
This commit is contained in:
commit
a80b0c3b4c
|
@ -28,7 +28,7 @@ Pull from repository.
|
|||
|
||||
Run this commands on project root directory to install Node dependencies.
|
||||
```
|
||||
$ npm install
|
||||
$ pnpm install
|
||||
$ gulp install
|
||||
```
|
||||
|
||||
|
|
|
@ -3,17 +3,23 @@
|
|||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "productionConfig"
|
||||
"table": "productionConfig"
|
||||
}
|
||||
},
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
"required": true,
|
||||
"id": true
|
||||
},
|
||||
"sectorFromCode": {
|
||||
"type": "string"
|
||||
},
|
||||
"sectorToCode": {
|
||||
"type": "string"
|
||||
},
|
||||
"backupPrinterNotificationDelay": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,4 +22,4 @@
|
|||
"modules/worker/front/**/*",
|
||||
"modules/zone/front/**/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1711,7 +1711,7 @@ INSERT INTO `ACL` VALUES (570,'InvoiceOut','canCreatePdf','WRITE','ALLOW','ROLE'
|
|||
INSERT INTO `ACL` VALUES (571,'Supplier','editPayMethodCheck','WRITE','ALLOW','ROLE','financial');
|
||||
INSERT INTO `ACL` VALUES (572,'Worker','isTeamBoss','WRITE','ALLOW','ROLE','teamBoss');
|
||||
INSERT INTO `ACL` VALUES (573,'Worker','forceIsSubordinate','READ','ALLOW','ROLE','hr');
|
||||
INSERT INTO `ACL` VALUES (574,'Claim','editState','WRITE','ALLOW','ROLE','claimManager');
|
||||
INSERT INTO `ACL` VALUES (574,'Claim','editPickup','WRITE','ALLOW','ROLE','claimManager');
|
||||
INSERT INTO `ACL` VALUES (577,'Claim','findOne','READ','ALLOW','ROLE','salesPerson');
|
||||
INSERT INTO `ACL` VALUES (579,'Claim','updateClaim','WRITE','ALLOW','ROLE','salesPerson');
|
||||
INSERT INTO `ACL` VALUES (580,'Claim','regularizeClaim','WRITE','ALLOW','ROLE','claimManager');
|
||||
|
|
|
@ -839,9 +839,9 @@ INSERT INTO `vn`.`config`(`id`, `mdbServer`, `fakeEmail`, `defaultersMaxAmount`,
|
|||
INSERT INTO `vn`.`greugeType`(`id`, `name`, `code`)
|
||||
VALUES
|
||||
(1, 'Diff', 'diff'),
|
||||
(2, 'Recover', 'recover'),
|
||||
(2, 'Recovery', 'recovery'),
|
||||
(3, 'Mana', 'mana'),
|
||||
(4, 'Reclaim', 'reclaim'),
|
||||
(4, 'Claim', 'claim'),
|
||||
(5, 'Heritage', 'heritage'),
|
||||
(6, 'Miscellaneous', 'miscellaneous'),
|
||||
(7, 'Freight Pickup', 'freightPickUp');
|
||||
|
@ -1885,9 +1885,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`)
|
||||
INSERT INTO `vn`.`claimConfig`(`id`, `maxResponsibility`, `monthsToRefund`, `minShipped`)
|
||||
VALUES
|
||||
(1, 50);
|
||||
(1, 5, 4, '2016-10-01');
|
||||
|
||||
INSERT INTO `vn`.`claimRatio`(`clientFk`, `yearSale`, `claimAmount`, `claimingRate`, `priceIncreasing`, `packingRate`)
|
||||
VALUES
|
||||
|
@ -3126,6 +3126,18 @@ INSERT INTO `vn`.`entryDms`(`entryFk`, `dmsFk`, `editorFk`)
|
|||
VALUES
|
||||
(1, 9, 9);
|
||||
|
||||
INSERT INTO vn.entryType (code,description,isInformal)
|
||||
VALUES ('devaluation','Devaluation',0),
|
||||
('internal','Internal',1),
|
||||
('inventory','Inventory',1),
|
||||
('life','Life',1),
|
||||
('packaging','Packaging',0),
|
||||
('payment','Refund',0),
|
||||
('product','Product',0),
|
||||
('regularization','Regularization',1),
|
||||
('return','Return',0),
|
||||
('transport','Delivery',0);
|
||||
|
||||
INSERT INTO `vn`.`cmr` (id,truckPlate,observations,senderInstruccions,paymentInstruccions,specialAgreements,companyFk,addressToFk,addressFromFk,supplierFk,packagesList,merchandiseDetail,state)
|
||||
VALUES (1,'123456A','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet',442,1,2,1,'Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet'),
|
||||
(2,'123456N','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet',69,3,4,2,'Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet'),
|
||||
|
@ -3865,3 +3877,17 @@ INSERT INTO vn.sectorCollectionSaleGroup
|
|||
SET id = 9,
|
||||
sectorCollectionFk = 3,
|
||||
saleGroupFk = 6;
|
||||
|
||||
|
||||
INSERT INTO `vn`.`calendarHolidays` (calendarHolidaysTypeFk, dated, calendarHolidaysNameFk, workCenterFk)
|
||||
VALUES
|
||||
(1, '2001-05-08', 1, 1),
|
||||
(1, '2001-05-09', 1, 1),
|
||||
(1, '2001-05-10', 1, 1),
|
||||
(1, '2001-05-11', 1, 1),
|
||||
(1, '2001-05-14', 1, 5),
|
||||
(1, '2001-05-15', 1, 5),
|
||||
(1, '2001-05-16', 1, 5),
|
||||
(1, '2001-05-17', 1, 5),
|
||||
(1, '2001-05-18', 1, 5);
|
||||
|
||||
|
|
|
@ -1,167 +0,0 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `bi`.`claim_ratio_routine`()
|
||||
BEGIN
|
||||
DECLARE vMonthToRefund INT DEFAULT 4;
|
||||
|
||||
/*
|
||||
* PAK 2015-11-20
|
||||
* Se trata de añadir a la tabla Greuges todos los
|
||||
* cargos que luego vamos a utilizar para calcular el recobro
|
||||
*/
|
||||
|
||||
-- Reclamaciones demasiado sensibles
|
||||
|
||||
INSERT INTO vn.greuge(shipped, clientFk, description,
|
||||
amount, greugeTypeFk, ticketFk)
|
||||
SELECT c.ticketCreated
|
||||
, c.clientFk
|
||||
, concat('Claim ', c.id,' : ', s.concept)
|
||||
,round( -1 * ((c.responsibility -1)/4) * s.quantity *
|
||||
s.price * (100 - s.discount) / 100, 2)
|
||||
, 4
|
||||
, s.ticketFk
|
||||
FROM vn.sale s
|
||||
JOIN vn.claimEnd ce ON ce.saleFk = s.id
|
||||
JOIN vn.claim c ON c.id = ce.claimFk
|
||||
WHERE ce.claimDestinationFk NOT IN (1,5)
|
||||
AND NOT ce.isGreuge
|
||||
AND c.claimStateFk = 3;
|
||||
|
||||
-- Reclamaciones que pasan a Maná
|
||||
|
||||
INSERT INTO vn.greuge(shipped, clientFk, description,
|
||||
amount, greugeTypeFk, ticketFk)
|
||||
SELECT c.ticketCreated
|
||||
, c.clientFk
|
||||
, concat('Claim_mana ',c.id,' : ', s.concept)
|
||||
,round( ((c.responsibility -1)/4) * s.quantity * s.price * (100 - s.discount) / 100, 2)
|
||||
,3
|
||||
,s.ticketFk
|
||||
FROM vn.sale s
|
||||
JOIN vn.claimEnd ce ON ce.saleFk = s.id
|
||||
JOIN vn.claim c ON c.id = ce.claimFk
|
||||
WHERE ce.claimDestinationFk NOT IN (1,5)
|
||||
AND NOT ce.isGreuge
|
||||
AND c.claimStateFk = 3
|
||||
AND c.isChargedToMana;
|
||||
|
||||
-- Marcamos para no repetir
|
||||
UPDATE vn.claimEnd ce
|
||||
JOIN vn.claim c ON c.id = ce.claimFk
|
||||
SET ce.isGreuge = TRUE
|
||||
WHERE ce.claimDestinationFk NOT IN (1,5)
|
||||
AND NOT ce.isGreuge
|
||||
AND c.claimStateFk = 3;
|
||||
|
||||
-- Recobros
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.ticket_list;
|
||||
CREATE TEMPORARY TABLE tmp.ticket_list
|
||||
(PRIMARY KEY (Id_Ticket))
|
||||
SELECT DISTINCT t.id Id_Ticket
|
||||
FROM vn.saleComponent sc
|
||||
JOIN vn.sale s ON sc.saleFk = s.id
|
||||
JOIN vn.ticket t ON t.id = s.ticketFk
|
||||
JOIN vn.ticketLastState ts ON ts.ticketFk = t.id
|
||||
JOIN vn.ticketTracking tt ON tt.id = ts.ticketTrackingFk
|
||||
JOIN vn.state st ON st.id = tt.stateFk
|
||||
JOIN vn.alertLevel al ON al.code = 'DELIVERED'
|
||||
WHERE sc.componentFk = 17
|
||||
AND sc.isGreuge = 0
|
||||
AND t.shipped >= '2016-10-01'
|
||||
AND t.shipped < util.VN_CURDATE()
|
||||
AND st.alertLevel >= al.id;
|
||||
|
||||
DELETE g.*
|
||||
FROM vn.greuge g
|
||||
JOIN tmp.ticket_list t ON g.ticketFk = t.Id_Ticket
|
||||
WHERE g.greugeTypeFk = 2;
|
||||
|
||||
INSERT INTO vn.greuge(clientFk, description, amount,shipped,
|
||||
greugeTypeFk, ticketFk)
|
||||
SELECT t.clientFk
|
||||
,concat('recobro ', s.ticketFk), - round(SUM(sc.value*s.quantity),2)
|
||||
AS dif,
|
||||
date(t.shipped)
|
||||
, 2
|
||||
,tt.Id_Ticket
|
||||
FROM vn.sale s
|
||||
JOIN vn.ticket t ON t.id = s.ticketFk
|
||||
JOIN tmp.ticket_list tt ON tt.Id_Ticket = t.id
|
||||
JOIN vn.saleComponent sc
|
||||
ON sc.saleFk = s.id AND sc.componentFk = 17
|
||||
GROUP BY t.id
|
||||
HAVING ABS(dif) > 1;
|
||||
|
||||
UPDATE vn.saleComponent sc
|
||||
JOIN vn.sale s ON s.id = sc.saleFk
|
||||
JOIN tmp.ticket_list tt ON tt.Id_Ticket = s.ticketFk
|
||||
SET sc.isGreuge = 1
|
||||
WHERE sc.componentFk = 17;
|
||||
|
||||
/*
|
||||
* Recalculamos la ratio de las reclamaciones, que luego
|
||||
* se va a utilizar en el recobro
|
||||
*/
|
||||
|
||||
REPLACE bi.claims_ratio(Id_Cliente, Consumo, Reclamaciones, Ratio, recobro)
|
||||
SELECT id, 0,0,0,0
|
||||
FROM vn.client;
|
||||
|
||||
REPLACE bi.claims_ratio(Id_Cliente, Consumo, Reclamaciones, Ratio, recobro)
|
||||
SELECT fm.Id_Cliente, 12 * fm.Consumo, Reclamaciones,
|
||||
round(Reclamaciones / (12*fm.Consumo),4), 0
|
||||
FROM bi.facturacion_media_anual fm
|
||||
LEFT JOIN(
|
||||
SELECT c.clientFk, round(sum(-1 * ((c.responsibility -1)/4) *
|
||||
s.quantity * s.price * (100 - s.discount) / 100))
|
||||
AS Reclamaciones
|
||||
FROM vn.sale s
|
||||
JOIN vn.claimEnd ce ON ce.saleFk = s.id
|
||||
JOIN vn.claim c ON c.id = ce.claimFk
|
||||
WHERE ce.claimDestinationFk NOT IN (1,5)
|
||||
AND c.claimStateFk = 3
|
||||
AND c.ticketCreated >= TIMESTAMPADD(YEAR, -1, util.VN_CURDATE())
|
||||
GROUP BY c.clientFk
|
||||
) claims ON claims.clientFk = fm.Id_Cliente;
|
||||
|
||||
|
||||
-- Calculamos el porcentaje del recobro para añadirlo al precio de venta
|
||||
UPDATE bi.claims_ratio cr
|
||||
JOIN (
|
||||
SELECT clientFk Id_Cliente, IFNULL(SUM(amount), 0) AS Greuge
|
||||
FROM vn.greuge
|
||||
WHERE shipped <= util.VN_CURDATE()
|
||||
GROUP BY clientFk
|
||||
) g ON g.Id_Cliente = cr.Id_Cliente
|
||||
SET recobro = GREATEST(0,round(IFNULL(Greuge, 0) /
|
||||
(IFNULL(Consumo, 0) * vMonthToRefund / 12 ) ,3));
|
||||
|
||||
-- Protección neonatos
|
||||
UPDATE bi.claims_ratio cr
|
||||
JOIN vn.firstTicketShipped fts ON fts.clientFk = cr.Id_Cliente
|
||||
SET recobro = 0, Ratio = 0
|
||||
WHERE fts.shipped > TIMESTAMPADD(MONTH,-1,util.VN_CURDATE());
|
||||
|
||||
-- CLIENTE 7983, JULIAN SUAU
|
||||
UPDATE bi.claims_ratio SET recobro = LEAST(0.05, recobro) WHERE Id_Cliente = 7983;
|
||||
|
||||
-- CLIENTE 4358
|
||||
UPDATE bi.claims_ratio SET recobro = GREATEST(0.05, recobro) WHERE Id_Cliente = 4358;
|
||||
|
||||
-- CLIENTE 5523, VERDECORA
|
||||
UPDATE bi.claims_ratio SET recobro = GREATEST(0.12, recobro) WHERE Id_Cliente = 5523;
|
||||
|
||||
-- CLIENTE 15979, SERVEIS VETERINARIS
|
||||
UPDATE bi.claims_ratio SET recobro = GREATEST(0.05, recobro) WHERE Id_Cliente = 15979;
|
||||
|
||||
-- CLIENTE 5189 i 8942, son de CSR i son el mateix client
|
||||
UPDATE bi.claims_ratio cr
|
||||
JOIN (SELECT sum(Consumo * recobro)/sum(Consumo) as recobro
|
||||
FROM bi.claims_ratio
|
||||
WHERE Id_Cliente IN ( 5189,8942)
|
||||
) sub
|
||||
SET cr.recobro = sub.recobro
|
||||
WHERE Id_Cliente IN ( 5189,8942);
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -21,7 +21,8 @@ BEGIN
|
|||
DECLARE vTransactionExportTaxFreeFk INT;
|
||||
DECLARE vSerialDua VARCHAR(1) DEFAULT 'D';
|
||||
DECLARE vInvoiceTypeInformativeCode VARCHAR(1);
|
||||
DECLARE vCountryCanariasCode, vCountryCeutaMelillaCode VARCHAR(2) ;
|
||||
DECLARE vCountryCanariasCode, vCountryCeutaMelillaCode VARCHAR(2);
|
||||
DECLARE vCompanyCode INT;
|
||||
|
||||
SELECT SiglaNacion INTO vCountryCanariasCode
|
||||
FROM Naciones
|
||||
|
@ -31,9 +32,6 @@ BEGIN
|
|||
FROM Naciones
|
||||
WHERE Nacion ='CEUTA Y MELILLA';
|
||||
|
||||
SELECT pendingServiceTransactionTypeFk INTO vDuaTransactionFk
|
||||
FROM config;
|
||||
|
||||
SELECT id INTO vTaxImportFk
|
||||
FROM taxType
|
||||
WHERE code = 'import21';
|
||||
|
@ -46,10 +44,14 @@ BEGIN
|
|||
FROM taxType
|
||||
WHERE code = 'import4';
|
||||
|
||||
SELECT definitiveExportTransactionTypeFk INTO vTransactionExportFk
|
||||
FROM config;
|
||||
|
||||
SELECT shipmentTransactionTypeFk INTO vTransactionExportTaxFreeFk
|
||||
SELECT shipmentTransactionTypeFk,
|
||||
definitiveExportTransactionTypeFk,
|
||||
pendingServiceTransactionTypeFk,
|
||||
company_getCode(vCompanyFk)
|
||||
INTO vTransactionExportTaxFreeFk,
|
||||
vTransactionExportFk,
|
||||
vDuaTransactionFk,
|
||||
vCompanyCode
|
||||
FROM config;
|
||||
|
||||
SELECT codeSage INTO vInvoiceTypeInformativeCode
|
||||
|
@ -64,8 +66,6 @@ BEGIN
|
|||
WHERE enlazadoSage = FALSE
|
||||
AND Asiento <> 1 ;
|
||||
|
||||
CALL clientSupplier_add(vCompanyFk);
|
||||
CALL pgc_add(vCompanyFk);
|
||||
CALL invoiceOut_manager(vYear, vCompanyFk);
|
||||
CALL invoiceIn_manager(vYear, vCompanyFk);
|
||||
|
||||
|
@ -158,7 +158,7 @@ BEGIN
|
|||
)
|
||||
SELECT 'EN' TipoEntrada,
|
||||
YEAR(x.FECHA) Ejercicio,
|
||||
company_getCode(vCompanyFk) AS CodigoEmpresa,
|
||||
vCompanyCode CodigoEmpresa,
|
||||
x.ASIEN Asiento,
|
||||
IF(EURODEBE <> 0 OR (EURODEBE = 0 AND EUROHABER IS NULL),
|
||||
'D', 'H') CargoAbono,
|
||||
|
@ -291,20 +291,6 @@ BEGIN
|
|||
WHERE m.CargoAbono = 'D'
|
||||
AND m.enlazadoSage = FALSE;
|
||||
|
||||
-- Elimina cuentas de cliente/proveedor que no se utilizarán en la importación
|
||||
DELETE cp
|
||||
FROM clientesProveedores cp
|
||||
LEFT JOIN movConta mc ON mc.codigoCuenta = cp.codigoCuenta
|
||||
AND mc.enlazadoSage = FALSE
|
||||
WHERE mc.codigoCuenta IS NULL;
|
||||
|
||||
-- Elimina cuentas contables que no se utilizarán en la importación
|
||||
DELETE pc
|
||||
FROM planCuentasPGC pc
|
||||
LEFT JOIN movConta mc ON mc.codigoCuenta = pc.codigoCuenta
|
||||
AND mc.enlazadoSage = FALSE
|
||||
WHERE mc.codigoCuenta IS NULL;
|
||||
|
||||
-- DUAS
|
||||
UPDATE movConta mci
|
||||
JOIN vn.XDiario x ON x.ASIEN = mci.Asiento
|
||||
|
@ -411,5 +397,44 @@ BEGIN
|
|||
AND importeDivisa > 0
|
||||
AND ImporteAsiento < 0;
|
||||
|
||||
CREATE OR REPLACE TEMPORARY TABLE tmp.clientSupplier
|
||||
(INDEX(idClientSupplier, `type`))
|
||||
ENGINE = MEMORY
|
||||
WITH client AS(
|
||||
SELECT DISTINCT c.id
|
||||
FROM sage.movConta mc
|
||||
JOIN vn.client c ON c.accountingAccount = mc.CodigoCuenta
|
||||
WHERE NOT enlazadoSage
|
||||
),supplier AS(
|
||||
SELECT DISTINCT s.id
|
||||
FROM sage.movConta mc
|
||||
JOIN vn.supplier s ON s.account = mc.CodigoCuenta
|
||||
WHERE NOT enlazadoSage
|
||||
)SELECT idClientSupplier, `type`
|
||||
FROM sage.clientSupplier cs
|
||||
WHERE NOT isSync
|
||||
UNION
|
||||
SELECT id, 'C'
|
||||
FROM client
|
||||
UNION
|
||||
SELECT id, 'P'
|
||||
FROM supplier;
|
||||
|
||||
CALL clientSupplier_add(vCompanyFk);
|
||||
|
||||
INSERT IGNORE INTO sage.clientSupplier (companyFk, `type`, idClientSupplier, isSync)
|
||||
SELECT vCompanyCode, `type`, idClientSupplier, FALSE
|
||||
FROM tmp.clientSupplier;
|
||||
|
||||
DROP TEMPORARY TABLE tmp.clientSupplier;
|
||||
|
||||
CALL pgc_add(vCompanyFk);
|
||||
-- Elimina cuentas contables que no se utilizarán en la importación
|
||||
DELETE pc
|
||||
FROM planCuentasPGC pc
|
||||
LEFT JOIN movConta mc ON mc.codigoCuenta = pc.codigoCuenta
|
||||
AND mc.enlazadoSage = FALSE
|
||||
WHERE mc.codigoCuenta IS NULL;
|
||||
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `sage`.`clientSupplier_add`(vCompanyFk INT)
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `sage`.`clientSupplier_add`(
|
||||
vCompanyFk INT
|
||||
)
|
||||
BEGIN
|
||||
/**
|
||||
* Prepara los datos de clientes y proveedores para exportarlos a Sage
|
||||
* @vCompanyFk Empresa dela que se quiere trasladar datos
|
||||
* Inserta en la tabla sage.clientesProveedores los datos de clientes y proveedores
|
||||
* que se actualizaran o se daran de alta en Sage
|
||||
* @vCompanyFk Id de empresa
|
||||
* @table tmp.clientSupplier(idClientSupplier, `type`)
|
||||
*/
|
||||
DECLARE vCountryCeutaMelillaFk INT;
|
||||
DECLARE vCompanyCode INT DEFAULT company_getCode(vCompanyFk);
|
||||
DECLARE vCountryCanariasCode, vCountryCeutaMelillaCode VARCHAR(2);
|
||||
|
||||
SELECT SiglaNacion INTO vCountryCanariasCode
|
||||
|
@ -45,7 +50,7 @@ BEGIN
|
|||
Email1,
|
||||
iban)
|
||||
SELECT
|
||||
company_getCode(vCompanyFk),
|
||||
vCompanyCode,
|
||||
'C',
|
||||
c.id,
|
||||
c.socialName,
|
||||
|
@ -53,7 +58,7 @@ BEGIN
|
|||
IFNULL(c.street, ''),
|
||||
c.accountingAccount,
|
||||
@fi := IF(cu.code = LEFT(TRIM(c.fi), 2) AND c.isVies, MID(TRIM(c.fi), 3, LENGTH(TRIM(c.fi))-1), TRIM(c.fi)),
|
||||
IF(c.isVies, CONCAT(IFNULL(cu.viesCode,cu.code), @fi ), TRIM(c.fi)),
|
||||
IF(c.isVies, CONCAT(IFNULL(cu.viesCode,cu.code), @fi ), TRIM(c.fi)),
|
||||
IFNULL(c.postcode, ''),
|
||||
IFNULL(c.city, ''),
|
||||
IFNULL(pr.CodigoProvincia, ''),
|
||||
|
@ -75,15 +80,14 @@ BEGIN
|
|||
IFNULL(SUBSTR(c.email, 1, LOCATE(',', CONCAT(c.email, ','))-1), ''),
|
||||
IFNULL(c.iban, '')
|
||||
FROM vn.`client` c
|
||||
JOIN clientLastTwoMonths clm ON clm.clientFk = c.id
|
||||
JOIN tmp.clientSupplier cs ON cs.idClientSupplier = c.id
|
||||
LEFT JOIN vn.country cu ON cu.id = c.countryFk
|
||||
LEFT JOIN Naciones n ON n.countryFk = cu.id
|
||||
LEFT JOIN vn.province p ON p.id = c.provinceFk
|
||||
LEFT JOIN Provincias pr ON pr.provinceFk = p.id
|
||||
WHERE c.isRelevant
|
||||
AND clm.companyFk = vCompanyFk
|
||||
WHERE cs.type = 'C'
|
||||
UNION ALL
|
||||
SELECT company_getCode(vCompanyFk),
|
||||
SELECT vCompanyCode,
|
||||
'P',
|
||||
s.id,
|
||||
s.name,
|
||||
|
@ -107,18 +111,16 @@ BEGIN
|
|||
IFNULL(s.transactionTypeSageFk, 0),
|
||||
IFNULL(s.withholdingSageFk, '0'),
|
||||
IFNULL(SUBSTR(sc.email, 1, (COALESCE(NULLIF(LOCATE(',', sc.email), 0), 99) - 1)), ''),
|
||||
IFNULL(iban, '')
|
||||
IFNULL(sa.iban, '')
|
||||
FROM vn.supplier s
|
||||
JOIN supplierLastThreeMonths pl ON pl.supplierFk = s.id
|
||||
JOIN tmp.clientSupplier cs ON cs.idClientSupplier = s.id
|
||||
LEFT JOIN vn.country co ON co.id = s.countryFk
|
||||
LEFT JOIN Naciones n ON n.countryFk = co.id
|
||||
LEFT JOIN vn.province p ON p.id = s.provinceFk
|
||||
LEFT JOIN Provincias pr ON pr.provinceFk = p.id
|
||||
LEFT JOIN vn.supplierContact sc ON sc.supplierFk = s.id
|
||||
LEFT JOIN vn.supplierAccount sa ON sa.supplierFk = s.id
|
||||
WHERE pl.companyFk = vCompanyFk AND
|
||||
s.isActive AND
|
||||
s.nif <> ''
|
||||
GROUP BY pl.supplierFk, pl.companyFk;
|
||||
WHERE cs.type = 'P'
|
||||
GROUP BY s.id;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `util`.`debugAdd`(vVariable VARCHAR(255), vValue VARCHAR(255))
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `util`.`debugAdd`(
|
||||
vVariable VARCHAR(255),
|
||||
vValue TEXT
|
||||
)
|
||||
MODIFIES SQL DATA
|
||||
BEGIN
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`claimRatio_add`()
|
||||
BEGIN
|
||||
/*
|
||||
* Añade a la tabla greuges todos los cargos necesario y
|
||||
* que luego lo utilizamos para calcular el recobro.
|
||||
*/
|
||||
DECLARE vMonthToRefund INT
|
||||
DEFAULT (SELECT monthsToRefund FROM claimConfig);
|
||||
DECLARE vRecoveryGreugeType INT
|
||||
DEFAULT (SELECT id FROM greugeType WHERE code = 'recovery');
|
||||
DECLARE vManaGreugeType INT
|
||||
DEFAULT (SELECT id FROM greugeType WHERE code = 'mana');
|
||||
DECLARE vClaimGreugeType INT
|
||||
DEFAULT (SELECT id FROM greugeType WHERE code = 'claim');
|
||||
DECLARE vDebtComponentType INT
|
||||
DEFAULT (SELECT id FROM component WHERE code = 'debtCollection');
|
||||
|
||||
IF vMonthToRefund IS NULL
|
||||
OR vRecoveryGreugeType IS NULL
|
||||
OR vManaGreugeType IS NULL
|
||||
OR vClaimGreugeType IS NULL
|
||||
OR vDebtComponentType IS NULL THEN
|
||||
|
||||
CALL util.throw('Required variables not found');
|
||||
END IF;
|
||||
|
||||
-- Reclamaciones demasiado sensibles
|
||||
INSERT INTO greuge(
|
||||
shipped,
|
||||
clientFk,
|
||||
`description`,
|
||||
amount,
|
||||
greugeTypeFk,
|
||||
ticketFk
|
||||
)
|
||||
SELECT c.ticketCreated,
|
||||
c.clientFk,
|
||||
CONCAT('Claim ', c.id,' : ', s.concept),
|
||||
ROUND(-1 * ((c.responsibility - 1) / 4) * s.quantity *
|
||||
s.price * (100 - s.discount) / 100, 2),
|
||||
vClaimGreugeType,
|
||||
s.ticketFk
|
||||
FROM sale s
|
||||
JOIN claimEnd ce ON ce.saleFk = s.id
|
||||
JOIN claimDestination cd ON cd.id = ce.claimDestinationFk
|
||||
JOIN claim c ON c.id = ce.claimFk
|
||||
JOIN claimState cs ON cs.id = c.claimStateFk
|
||||
WHERE cd.description NOT IN ('Bueno', 'Corregido')
|
||||
AND NOT ce.isGreuge
|
||||
AND cs.code = 'resolved';
|
||||
|
||||
-- Reclamaciones que pasan a Maná
|
||||
INSERT INTO greuge(
|
||||
shipped,
|
||||
clientFk,
|
||||
`description`,
|
||||
amount,
|
||||
greugeTypeFk,
|
||||
ticketFk
|
||||
)
|
||||
SELECT c.ticketCreated,
|
||||
c.clientFk,
|
||||
CONCAT('Claim_mana ', c.id,' : ', s.concept),
|
||||
ROUND(((c.responsibility - 1) / 4) * s.quantity *
|
||||
s.price * (100 - s.discount) / 100, 2),
|
||||
vManaGreugeType,
|
||||
s.ticketFk
|
||||
FROM sale s
|
||||
JOIN claimEnd ce ON ce.saleFk = s.id
|
||||
JOIN claimDestination cd ON cd.id = ce.claimDestinationFk
|
||||
JOIN claim c ON c.id = ce.claimFk
|
||||
JOIN claimState cs ON cs.id = c.claimStateFk
|
||||
WHERE cd.description NOT IN ('Bueno', 'Corregido')
|
||||
AND NOT ce.isGreuge
|
||||
AND cs.code = 'resolved'
|
||||
AND c.isChargedToMana;
|
||||
|
||||
-- Marcamos para no repetir
|
||||
UPDATE claimEnd ce
|
||||
JOIN claimDestination cd ON cd.id = ce.claimDestinationFk
|
||||
JOIN claim c ON c.id = ce.claimFk
|
||||
JOIN claimState cs ON cs.id = c.claimStateFk
|
||||
SET ce.isGreuge = TRUE
|
||||
WHERE cd.description NOT IN ('Bueno', 'Corregido')
|
||||
AND NOT ce.isGreuge
|
||||
AND cs.code = 'resolved';
|
||||
|
||||
-- Recobros
|
||||
CREATE OR REPLACE TEMPORARY TABLE tTicketList
|
||||
(PRIMARY KEY (ticketFk))
|
||||
ENGINE = MEMORY
|
||||
SELECT DISTINCT s.ticketFk
|
||||
FROM saleComponent sc
|
||||
JOIN sale s ON sc.saleFk = s.id
|
||||
JOIN ticket t ON t.id = s.ticketFk
|
||||
JOIN ticketLastState ts ON ts.ticketFk = t.id
|
||||
JOIN ticketTracking tt ON tt.id = ts.ticketTrackingFk
|
||||
JOIN state st ON st.id = tt.stateFk
|
||||
JOIN alertLevel al ON al.id = st.alertLevel
|
||||
WHERE sc.componentFk = vDebtComponentType
|
||||
AND NOT sc.isGreuge
|
||||
AND t.shipped >= (SELECT minShipped FROM claimConfig)
|
||||
AND t.shipped < util.VN_CURDATE()
|
||||
AND al.code = 'DELIVERED';
|
||||
|
||||
DELETE g.*
|
||||
FROM greuge g
|
||||
JOIN tTicketList t ON t.ticketFk = g.ticketFk
|
||||
WHERE g.greugeTypeFk = vRecoveryGreugeType;
|
||||
|
||||
INSERT INTO greuge(
|
||||
clientFk,
|
||||
`description`,
|
||||
amount,
|
||||
shipped,
|
||||
greugeTypeFk,
|
||||
ticketFk
|
||||
)
|
||||
SELECT t.clientFk,
|
||||
'Recobro',
|
||||
- ROUND(SUM(sc.value * s.quantity), 2) dif,
|
||||
DATE(t.shipped),
|
||||
vRecoveryGreugeType,
|
||||
tl.ticketFk
|
||||
FROM sale s
|
||||
JOIN ticket t ON t.id = s.ticketFk
|
||||
JOIN tTicketList tl ON tl.ticketFk = t.id
|
||||
JOIN saleComponent sc ON sc.saleFk = s.id
|
||||
AND sc.componentFk = vDebtComponentType
|
||||
GROUP BY t.id
|
||||
HAVING ABS(dif) > 1;
|
||||
|
||||
UPDATE saleComponent sc
|
||||
JOIN sale s ON s.id = sc.saleFk
|
||||
JOIN tTicketList tl ON tl.ticketFk = s.ticketFk
|
||||
SET sc.isGreuge = TRUE
|
||||
WHERE sc.componentFk = vDebtComponentType;
|
||||
|
||||
REPLACE claimRatio(
|
||||
clientFk,
|
||||
yearSale,
|
||||
claimAmount,
|
||||
claimingRate,
|
||||
priceIncreasing
|
||||
)
|
||||
SELECT c.id,
|
||||
12 * cac.invoiced,
|
||||
totalClaims,
|
||||
ROUND(totalClaims / (12 * cac.invoiced), 4),
|
||||
0
|
||||
FROM client c
|
||||
LEFT JOIN bs.clientAnnualConsumption cac ON cac.clientFk = c.id
|
||||
LEFT JOIN (
|
||||
SELECT c.clientFk,
|
||||
ROUND(SUM(-1 * ((c.responsibility - 1) / 4) *
|
||||
s.quantity * s.price * (100 - s.discount)
|
||||
/ 100)) totalClaims
|
||||
FROM sale s
|
||||
JOIN claimEnd ce ON ce.saleFk = s.id
|
||||
JOIN claimDestination cd ON cd.id = ce.claimDestinationFk
|
||||
JOIN claim c ON c.id = ce.claimFk
|
||||
JOIN claimState cs ON cs.id = c.claimStateFk
|
||||
WHERE cd.description NOT IN ('Bueno', 'Corregido')
|
||||
AND cs.code = 'resolved'
|
||||
AND c.ticketCreated >= util.VN_CURDATE() - INTERVAL 1 YEAR
|
||||
GROUP BY c.clientFk
|
||||
) sub ON sub.clientFk = c.id;
|
||||
|
||||
-- Calculamos el porcentaje del recobro para añadirlo al precio de venta
|
||||
UPDATE claimRatio cr
|
||||
JOIN (
|
||||
SELECT clientFk, IFNULL(SUM(amount), 0) greuge
|
||||
FROM greuge
|
||||
WHERE shipped <= util.VN_CURDATE()
|
||||
GROUP BY clientFk
|
||||
) sub ON sub.clientFk = cr.clientFk
|
||||
SET cr.priceIncreasing = GREATEST(0, ROUND(IFNULL(sub.greuge, 0) /
|
||||
(IFNULL(cr.yearSale, 0) * vMonthToRefund / 12 ), 3));
|
||||
|
||||
-- Protección neonatos
|
||||
UPDATE claimRatio cr
|
||||
JOIN firstTicketShipped fts ON fts.clientFk = cr.clientFk
|
||||
SET cr.priceIncreasing = 0,
|
||||
cr.claimingRate = 0
|
||||
WHERE fts.shipped > util.VN_CURDATE() - INTERVAL 1 MONTH;
|
||||
|
||||
DROP TEMPORARY TABLE tTicketList;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -28,6 +28,8 @@ BEGIN
|
|||
DECLARE vLockName VARCHAR(215);
|
||||
DECLARE vLockTime INT DEFAULT 30;
|
||||
DECLARE vFreeWagonFk INT;
|
||||
DECLARE vErrorNumber INT;
|
||||
DECLARE vErrorMsg TEXT;
|
||||
|
||||
DECLARE c1 CURSOR FOR
|
||||
SELECT ticketFk, `lines`, m3
|
||||
|
@ -47,12 +49,18 @@ BEGIN
|
|||
|
||||
DECLARE EXIT HANDLER FOR SQLEXCEPTION
|
||||
BEGIN
|
||||
GET DIAGNOSTICS CONDITION 1
|
||||
vErrorNumber = MYSQL_ERRNO,
|
||||
vErrorMsg = MESSAGE_TEXT;
|
||||
|
||||
IF vLockName IS NOT NULL THEN
|
||||
DO RELEASE_LOCK(vLockName);
|
||||
CALL util.debugAdd(JSON_OBJECT(
|
||||
'type', 'releaseLock',
|
||||
'userFk', vUserFk
|
||||
), vLockName); -- Tmp
|
||||
CALL util.debugAdd('collection_new', JSON_OBJECT(
|
||||
'errorNumber', vErrorNumber,
|
||||
'errorMsg', vErrorMsg,
|
||||
'lockName', vLockName,
|
||||
'userFk', vUserFk
|
||||
)); -- Tmp
|
||||
END IF;
|
||||
|
||||
RESIGNAL;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`duaInvoiceInBooking`(vDuaFk INT)
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`duaInvoiceInBooking`(
|
||||
vDuaFk INT
|
||||
)
|
||||
BEGIN
|
||||
/**
|
||||
* Genera el asiento de un DUA y marca las entradas como confirmadas
|
||||
|
@ -29,9 +31,7 @@ BEGIN
|
|||
SET ii.booked = IFNULL(ii.booked, d.booked),
|
||||
ii.operated = IFNULL(ii.operated, d.operated),
|
||||
ii.issued = IFNULL(ii.issued, d.issued),
|
||||
ii.bookEntried = IFNULL(ii.bookEntried, d.bookEntried),
|
||||
e.isBooked = TRUE,
|
||||
e.isConfirmed = TRUE
|
||||
ii.bookEntried = IFNULL(ii.bookEntried, d.bookEntried)
|
||||
WHERE d.id = vDuaFk;
|
||||
|
||||
SELECT ASIEN INTO vBookEntry FROM dua WHERE id = vDuaFk;
|
||||
|
@ -39,7 +39,7 @@ BEGIN
|
|||
IF vBookEntry IS NULL THEN
|
||||
SELECT YEAR(IFNULL(ii.bookEntried, d.bookEntried)) INTO vFiscalYear
|
||||
FROM invoiceIn ii
|
||||
JOIN entry e ON e.invoiceInFk = ii.id
|
||||
JOIN `entry` e ON e.invoiceInFk = ii.id
|
||||
JOIN duaEntry de ON de.entryFk = e.id
|
||||
JOIN dua d ON d.id = de.duaFk
|
||||
WHERE d.id = vDuaFk
|
||||
|
@ -49,7 +49,7 @@ BEGIN
|
|||
|
||||
OPEN vInvoicesIn;
|
||||
|
||||
l: LOOP
|
||||
l: LOOP
|
||||
SET vDone = FALSE;
|
||||
FETCH vInvoicesIn INTO vInvoiceFk;
|
||||
|
||||
|
@ -70,5 +70,28 @@ l: LOOP
|
|||
JOIN duaInvoiceIn dii ON dii.invoiceInFk = ii.id
|
||||
SET ii.isBooked = TRUE
|
||||
WHERE dii.duaFk = vDuaFk;
|
||||
|
||||
UPDATE `entry` e
|
||||
JOIN (
|
||||
WITH entries AS (
|
||||
SELECT e.id, de.duaFk
|
||||
FROM `entry` e
|
||||
JOIN duaEntry de ON de.entryFk = e.id
|
||||
WHERE de.duaFk = vDuaFk
|
||||
AND (NOT e.isBooked OR NOT e.isConfirmed)
|
||||
),
|
||||
notBookedEntries AS (
|
||||
SELECT e.id
|
||||
FROM duaEntry
|
||||
WHERE duaFk = vDuaFk
|
||||
AND NOT customsValue
|
||||
)
|
||||
SELECT e.id
|
||||
FROM entries e
|
||||
LEFT JOIN notBookedEntries nbe ON nbe.entryFk = e.id
|
||||
WHERE nbe.entryFk IS NULL
|
||||
) sub ON sub.id = e.id
|
||||
SET e.isBooked = TRUE,
|
||||
e.isConfirmed = TRUE;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`itemShelving_add`(IN vShelvingFk VARCHAR(8), IN vBarcode VARCHAR(22), IN vQuantity INT, IN vPackagingFk VARCHAR(10), IN vGrouping INT, IN vPacking INT, IN vWarehouseFk INT)
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`itemShelving_add`(
|
||||
vShelvingFk VARCHAR(8),
|
||||
vBarcode VARCHAR(22),
|
||||
vQuantity INT,
|
||||
vPackagingFk VARCHAR(10),
|
||||
vGrouping INT,
|
||||
vPacking INT,
|
||||
vWarehouseFk INT
|
||||
)
|
||||
BEGIN
|
||||
|
||||
/**
|
||||
* Añade registro o lo actualiza si ya existe.
|
||||
*
|
||||
|
@ -23,12 +30,7 @@ BEGIN
|
|||
SELECT barcodeToItem(vBarcode) INTO vItemFk;
|
||||
|
||||
IF vBuyFk IS NULL THEN
|
||||
CALL cache.last_buy_refresh(FALSE);
|
||||
|
||||
SELECT buy_id INTO vBuyFk
|
||||
FROM cache.last_buy
|
||||
WHERE item_id = vItemFk
|
||||
AND warehouse_id = vWarehouseFk;
|
||||
CALL util.throw('The buy is required');
|
||||
END IF;
|
||||
|
||||
IF vPacking IS NULL
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`item_getAtp`(vDated DATE)
|
||||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`item_getAtp`(vDated DATE)
|
||||
BEGIN
|
||||
/**
|
||||
* Calcula el valor mínimo acumulado para cada artículo ordenado por fecha y
|
||||
|
@ -44,5 +44,5 @@ BEGIN
|
|||
GROUP BY itemFk, wareHouseFk;
|
||||
|
||||
DROP TEMPORARY TABLE tItemOrdered;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
|
|
@ -30,6 +30,7 @@ BEGIN
|
|||
FROM sale s
|
||||
JOIN ticket t ON t.id = s.ticketFk
|
||||
WHERE t.shipped BETWEEN vDatedFrom AND vDatedTo
|
||||
AND t.warehouseFk
|
||||
AND s.quantity != 0
|
||||
AND (vItemFk IS NULL OR s.itemFk = vItemFk)
|
||||
AND (vWarehouseFk IS NULL OR t.warehouseFk = vWarehouseFk)
|
||||
|
@ -40,7 +41,7 @@ BEGIN
|
|||
t.warehouseInFk
|
||||
FROM buy b
|
||||
JOIN entry e ON e.id = b.entryFk
|
||||
LEFT JOIN travel t ON t.id = e.travelFk
|
||||
JOIN travel t ON t.id = e.travelFk
|
||||
WHERE t.landed BETWEEN vDatedFrom AND vDatedTo
|
||||
AND (vWarehouseFk IS NULL OR t.warehouseInFk = vWarehouseFk)
|
||||
AND !e.isExcludedFromAvailable
|
||||
|
@ -53,7 +54,7 @@ BEGIN
|
|||
t.warehouseOutFk
|
||||
FROM buy b
|
||||
JOIN entry e ON e.id = b.entryFk
|
||||
LEFT JOIN travel t ON t.id = e.travelFk
|
||||
JOIN travel t ON t.id = e.travelFk
|
||||
WHERE t.shipped BETWEEN vDatedFrom AND vDatedTo
|
||||
AND (vWarehouseFk IS NULL OR t.warehouseOutFk = vWarehouseFk)
|
||||
AND !e.isExcludedFromAvailable
|
||||
|
|
|
@ -5,10 +5,10 @@ CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`supplierPackaging_R
|
|||
)
|
||||
BEGIN
|
||||
/**
|
||||
* Selecciona los embalajes de un proveedor a partir de una fecha
|
||||
* Create a report with packaging balance
|
||||
*
|
||||
* @param vFromDated Fecha de la que partir
|
||||
* @param vSupplierFk Id del proveedor
|
||||
* @param vFromDated Starting date
|
||||
* @param vSupplierFk Supplier ID
|
||||
*/
|
||||
SET @vBalance = 0;
|
||||
SET @vItemFk = NULL;
|
||||
|
@ -99,6 +99,7 @@ BEGIN
|
|||
WHERE su.id = vSupplierFk
|
||||
AND t.shipped < vFromDated
|
||||
AND p.isPackageReturnable
|
||||
AND NOT t.isDeleted
|
||||
GROUP BY s.itemFk
|
||||
UNION ALL
|
||||
SELECT vSupplierFk,
|
||||
|
@ -138,8 +139,9 @@ BEGIN
|
|||
JOIN client c ON c.id = t.clientFk
|
||||
JOIN supplier su ON su.nif = c.fi
|
||||
WHERE su.id = vSupplierFk
|
||||
AND t.shipped >= vFromDated
|
||||
AND t.shipped < vFromDated
|
||||
AND p.isPackageReturnable
|
||||
AND NOT t.isDeleted
|
||||
GROUP BY p.itemFk
|
||||
ORDER BY itemFk, landed, entryFk
|
||||
) sub
|
||||
|
|
|
@ -13,7 +13,15 @@ trig:BEGIN
|
|||
LEAVE trig;
|
||||
END IF;
|
||||
|
||||
CALL entry_isEditable(OLD.entryFk);
|
||||
IF NOT (NEW.entryFk <=> OLD.entryFk) OR
|
||||
NOT (NEW.quantity <=> OLD.quantity) OR
|
||||
NOT (NEW.buyingValue <=> OLD.buyingValue) OR
|
||||
NOT (NEW.packing <=> OLD.packing)
|
||||
THEN
|
||||
|
||||
CALL entry_isEditable(OLD.entryFk);
|
||||
END IF;
|
||||
|
||||
SET NEW.editorFk = account.myUser_getId();
|
||||
|
||||
SELECT defaultEntry INTO vDefaultEntry
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`clientUnpaid_beforeInsert`
|
||||
BEFORE INSERT ON `clientUnpaid`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
SET NEW.editorFk = account.myUser_getId();
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,8 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`clientUnpaid_beforeUpdate`
|
||||
BEFORE UPDATE ON `clientUnpaid`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
SET NEW.editorFk = account.myUser_getId();
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -3,12 +3,35 @@ CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`client_afterUpdate`
|
|||
AFTER UPDATE ON `client`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF !(NEW.defaultAddressFk <=> OLD.defaultAddressFk) THEN
|
||||
IF NOT (NEW.defaultAddressFk <=> OLD.defaultAddressFk) THEN
|
||||
UPDATE `address` SET isDefaultAddress = FALSE
|
||||
WHERE clientFk = NEW.id;
|
||||
|
||||
UPDATE `address` SET isDefaultAddress = TRUE
|
||||
WHERE id = NEW.defaultAddressFk;
|
||||
END IF;
|
||||
|
||||
IF NEW.id <> OLD.id
|
||||
OR NOT (NEW.provinceFk <=> OLD.provinceFk)
|
||||
OR NOT (NEW.socialName <=> OLD.socialName)
|
||||
OR NOT (NEW.street <=> OLD.street)
|
||||
OR NOT (NEW.accountingAccount <=> OLD.accountingAccount)
|
||||
OR NOT (NEW.isVies <=> OLD.isVies)
|
||||
OR NOT (NEW.fi <=> OLD.fi)
|
||||
OR NOT (NEW.postcode <=> OLD.postcode)
|
||||
OR NOT (NEW.city <=> OLD.city)
|
||||
OR NOT (NEW.countryFk <=> OLD.countryFk)
|
||||
OR NOT (NEW.taxTypeSageFk <=> OLD.taxTypeSageFk)
|
||||
OR NOT (NEW.transactionTypeSageFk <=> OLD.transactionTypeSageFk)
|
||||
OR NOT (NEW.email <=> OLD.email)
|
||||
OR NOT (NEW.iban <=> OLD.iban)
|
||||
OR NOT (NEW.phone <=> OLD.phone)
|
||||
OR NOT (NEW.mobile <=> OLD.mobile) THEN
|
||||
|
||||
UPDATE sage.clientSupplier
|
||||
SET isSync = FALSE
|
||||
WHERE idClientSupplier IN (NEW.id, OLD.id)
|
||||
AND `type` = 'C';
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
|
|
@ -65,11 +65,11 @@ BEGIN
|
|||
END IF;
|
||||
END IF;
|
||||
|
||||
IF !(NEW.salesPersonFk <=> OLD.salesPersonFk) THEN
|
||||
IF NOT (NEW.salesPersonFk <=> OLD.salesPersonFk) THEN
|
||||
SET NEW.lastSalesPersonFk = IFNULL(NEW.salesPersonFk, OLD.salesPersonFk);
|
||||
END IF;
|
||||
|
||||
IF !(NEW.businessTypeFk <=> OLD.businessTypeFk) AND (NEW.businessTypeFk = 'individual' OR OLD.businessTypeFk = 'individual') THEN
|
||||
IF NOT (NEW.businessTypeFk <=> OLD.businessTypeFk) AND (NEW.businessTypeFk = 'individual' OR OLD.businessTypeFk = 'individual') THEN
|
||||
SET NEW.isTaxDataChecked = 0;
|
||||
END IF;
|
||||
END$$
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`deviceProductionConfig_afterUpdate`
|
||||
AFTER UPDATE ON `deviceProductionConfig`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF NEW.isAllUsersAllowed = 1 OR NEW.isAllUsersAllowed = -1 THEN
|
||||
SET @username := (SELECT account.myUser_getName());
|
||||
INSERT INTO vn.mail (receiver, subject, body)
|
||||
VALUES ('cau@verdnatura.es', 'Se ha activado la autenticación sin restricciones en la app',
|
||||
CONCAT('El usuario ', @username,' ha habilitado la opción para que todos los usuarios puedan acceder a la app sin restricciones'));
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -7,10 +7,21 @@ BEGIN
|
|||
DECLARE vPrintedCount INT;
|
||||
DECLARE vHasDistinctWarehouses BOOL;
|
||||
DECLARE vTotalBuy INT;
|
||||
|
||||
IF NEW.isBooked = OLD.isBooked THEN
|
||||
|
||||
IF NEW.isBooked = OLD.isBooked AND (
|
||||
NOT (NEW.supplierFk <=> OLD.supplierFk) OR
|
||||
NOT (NEW.dated <=> OLD.dated) OR
|
||||
NOT (NEW.invoiceNumber <=> OLD.invoiceNumber) OR
|
||||
NOT (NEW.travelFk <=> OLD.travelFk) OR
|
||||
NOT (NEW.companyFk <=> OLD.companyFk) OR
|
||||
NOT (NEW.invoiceInFk <=> OLD.invoiceInFk) OR
|
||||
NOT (NEW.invoiceAmount <=> OLD.invoiceAmount) OR
|
||||
NOT (NEW.typeFk <=> OLD.typeFk)
|
||||
) THEN
|
||||
|
||||
CALL entry_isEditable(OLD.id);
|
||||
ELSE
|
||||
|
||||
IF NEW.isBooked THEN
|
||||
SELECT COUNT(*) INTO vTotalBuy
|
||||
FROM buy
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`supplier_afterUpdate`
|
||||
BEFORE UPDATE ON `supplier`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF NEW.id <> OLD.id
|
||||
OR NOT (NEW.name <=> OLD.name)
|
||||
OR NOT (NEW.street <=> OLD.street)
|
||||
OR NOT (NEW.account <=> OLD.account)
|
||||
OR NOT (NEW.nif <=> OLD.nif)
|
||||
OR NOT (NEW.isVies <=> OLD.isVies)
|
||||
OR NOT (NEW.provinceFk <=> OLD.provinceFk)
|
||||
OR NOT (NEW.countryFk <=> OLD.countryFk)
|
||||
OR NOT (NEW.postCode <=> OLD.postCode)
|
||||
OR NOT (NEW.city <=> OLD.city)
|
||||
OR NOT (NEW.taxTypeSageFk <=> OLD.taxTypeSageFk)
|
||||
OR NOT (NEW.transactionTypeSageFk <=> OLD.transactionTypeSageFk)
|
||||
OR NOT (NEW.withholdingSageFk <=> OLD.withholdingSageFk) THEN
|
||||
|
||||
UPDATE sage.clientSupplier
|
||||
SET isSync = FALSE
|
||||
WHERE idClientSupplier IN (NEW.id, OLD.id)
|
||||
AND `type` = 'P';
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,5 @@
|
|||
-- Place your SQL code here
|
||||
|
||||
USE vn;
|
||||
|
||||
ALTER TABLE vn.productionConfig ADD itemOlderReviewHours int(11) DEFAULT 0 NOT NULL COMMENT 'Horas que se tienen en cuenta para comprobar si un ítem es más viejo.';
|
|
@ -0,0 +1,3 @@
|
|||
ALTER TABLE vn.client
|
||||
CHANGE hasIncoterms hasIncoterms__ tinyint(1) DEFAULT 0 NOT NULL
|
||||
COMMENT '@deprecated 2024-06-12 refs #7545 Received incoterms authorization from client';
|
|
@ -0,0 +1,12 @@
|
|||
UPDATE IGNORE bs.nightTask
|
||||
SET `schema` = 'vn',
|
||||
`procedure` = 'claimRatio_add'
|
||||
WHERE `procedure` = 'claim_ratio_routine';
|
||||
|
||||
ALTER TABLE vn.claimConfig
|
||||
ADD monthsToRefund int(11) DEFAULT NULL NULL,
|
||||
ADD minShipped date DEFAULT NULL NULL;
|
||||
|
||||
UPDATE IGNORE vn.claimConfig
|
||||
SET monthsToRefund = 4,
|
||||
minShipped = '2016-10-01';
|
|
@ -0,0 +1,17 @@
|
|||
CREATE TABLE IF NOT EXISTS vn.travelKgPercentage (
|
||||
value INT(3) PRIMARY KEY,
|
||||
className VARCHAR(50)
|
||||
);
|
||||
|
||||
INSERT INTO vn.travelKgPercentage (value, className)
|
||||
VALUES
|
||||
(80, 'primary'),
|
||||
(100, 'alert');
|
||||
|
||||
INSERT INTO salix.ACL
|
||||
SET model = 'TravelKgPercentage',
|
||||
property = '*',
|
||||
accessType = 'READ',
|
||||
permission = 'ALLOW',
|
||||
principalType = 'ROLE',
|
||||
principalId = 'employee';
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE vn.ticketPackaging DROP FOREIGN KEY ticketPackaging_fk3;
|
||||
ALTER TABLE vn.ticketPackaging DROP COLUMN workerFk;
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
CREATE OR REPLACE TABLE sage.clientSupplier (
|
||||
`companyFk` smallint(6) NOT NULL,
|
||||
`type` ENUM('C','P') NOT NULL,
|
||||
`idClientSupplier` INT NOT NULL,
|
||||
`isSync` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`companyFk`,`idClientSupplier`,`type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci
|
||||
COMMENT='Clients and suppliers present in Sage and their synchronization status';
|
|
@ -0,0 +1,9 @@
|
|||
-- Place your SQL code here
|
||||
|
||||
USE vn;
|
||||
|
||||
ALTER TABLE vn.productionConfig ADD sectorFromCode varchar(15) NULL COMMENT 'Sector origen que se revisa ítems más nuevos al parkinear';
|
||||
ALTER TABLE vn.productionConfig ADD sectorToCode varchar(15) NULL COMMENT 'Sector destino que se revisa ítems más nuevos al parkinear';
|
||||
|
||||
ALTER TABLE vn.productionConfig ADD CONSTRAINT productionConfig_sector_FK FOREIGN KEY (sectorFromCode) REFERENCES vn.sector(code) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
ALTER TABLE vn.productionConfig ADD CONSTRAINT productionConfig_sector_FK_1 FOREIGN KEY (sectorToCode) REFERENCES vn.sector(code) ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
@ -0,0 +1,14 @@
|
|||
-- Place your SQL code here
|
||||
|
||||
USE vn;
|
||||
|
||||
ALTER TABLE vn.itemShelving DROP FOREIGN KEY itemShelvingBuy_FK;
|
||||
ALTER TABLE vn.itemShelving DROP FOREIGN KEY itemShelving_fk2;
|
||||
ALTER TABLE vn.itemShelving DROP INDEX itemShelving_UN;
|
||||
|
||||
ALTER TABLE vn.itemShelving ADD CONSTRAINT itemShelving_fk2 FOREIGN KEY (shelvingFk) REFERENCES vn.shelving(code) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE vn.itemShelving ADD CONSTRAINT itemShelvingBuy_FK FOREIGN KEY (buyFk) REFERENCES vn.buy(id) ON DELETE RESTRICT ON UPDATE RESTRICT;
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
ALTER TABLE vn.clientUnpaid
|
||||
ADD editorFk INT UNSIGNED NULL,
|
||||
ADD CONSTRAINT ClientUnpaid_editorFk FOREIGN KEY (editorFk)
|
||||
REFERENCES account.`user`(id);
|
|
@ -33,106 +33,4 @@ describe('Travel descriptor path', () => {
|
|||
|
||||
expect(state).toBe('travel.card.summary');
|
||||
});
|
||||
|
||||
it('should be redirected to the create entry view', async() => {
|
||||
await page.waitToClick(selectors.travelDescriptor.dotMenu);
|
||||
await page.waitToClick(selectors.travelDescriptor.dotMenuAddEntry);
|
||||
await page.waitForState('entry.create');
|
||||
const state = await page.getState();
|
||||
|
||||
expect(state).toBe('entry.create');
|
||||
});
|
||||
|
||||
it('should check some data was imported from the travel', async() => {
|
||||
const travel = await page.waitToGetProperty(selectors.entryCreate.travel, 'value');
|
||||
const campany = await page.waitToGetProperty(selectors.entryCreate.company, 'value');
|
||||
|
||||
expect(travel).toContain('Warehouse');
|
||||
expect(campany).toContain('VNL');
|
||||
});
|
||||
|
||||
it('should navigate back to the travel index', async() => {
|
||||
await page.waitToClick('.cancel');
|
||||
await page.waitToClick(selectors.globalItems.homeButton);
|
||||
await page.selectModule('travel');
|
||||
await page.waitForState('travel.index');
|
||||
const state = await page.getState();
|
||||
|
||||
expect(state).toBe('travel.index');
|
||||
});
|
||||
|
||||
it('should click on the add entry button of the third result to be redirected to create entry', async() => {
|
||||
await page.keyboard.press('Enter');
|
||||
await page.waitToClick(selectors.travelIndex.firstTravelAddEntryButton);
|
||||
await page.waitForState('entry.create');
|
||||
const state = await page.getState();
|
||||
|
||||
expect(state).toBe('entry.create');
|
||||
});
|
||||
|
||||
it('should check again some data was imported from the travel', async() => {
|
||||
const travel = await page.waitToGetProperty(selectors.entryCreate.travel, 'value');
|
||||
const campany = await page.waitToGetProperty(selectors.entryCreate.company, 'value');
|
||||
|
||||
expect(travel).toContain('Warehouse');
|
||||
expect(campany).toContain('VNL');
|
||||
});
|
||||
|
||||
it('should navigate to the travel summary of a given travel', async() => {
|
||||
await page.waitToClick('.cancel');
|
||||
await page.waitToClick(selectors.globalItems.homeButton);
|
||||
await page.selectModule('travel');
|
||||
await page.write(selectors.travelIndex.generalSearchFilter, '3');
|
||||
await page.keyboard.press('Enter');
|
||||
await page.waitForState('travel.card.summary');
|
||||
const state = await page.getState();
|
||||
|
||||
expect(state).toBe('travel.card.summary');
|
||||
});
|
||||
|
||||
it('should be redirected to the create travel when using the clone option of the dot menu', async() => {
|
||||
await page.waitToClick(selectors.travelDescriptor.dotMenu);
|
||||
await page.waitToClick(selectors.travelDescriptor.dotMenuClone);
|
||||
await page.respondToDialog('accept');
|
||||
await page.waitForState('travel.create');
|
||||
const state = await page.getState();
|
||||
|
||||
expect(state).toBe('travel.create');
|
||||
});
|
||||
|
||||
it('should edit the data to clone and then get redirected to the cloned travel basic data', async() => {
|
||||
await page.clearInput(selectors.travelCreate.reference);
|
||||
await page.write(selectors.travelCreate.reference, 'reference');
|
||||
await page.autocompleteSearch(selectors.travelCreate.agency, 'entanglement');
|
||||
await page.pickDate(selectors.travelCreate.shipped);
|
||||
await page.pickDate(selectors.travelCreate.landed);
|
||||
await page.autocompleteSearch(selectors.travelCreate.warehouseOut, 'warehouse one');
|
||||
await page.autocompleteSearch(selectors.travelCreate.warehouseIn, 'warehouse two');
|
||||
await page.waitToClick(selectors.travelCreate.saveButton);
|
||||
await page.waitForState('travel.card.basicData');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it('should update the landed date to a future date to enable cloneWithEntries', async() => {
|
||||
const nextMonth = Date.vnNew();
|
||||
nextMonth.setMonth(nextMonth.getMonth() + 1);
|
||||
await page.pickDate(selectors.travelBasicData.deliveryDate, nextMonth);
|
||||
await page.waitToClick(selectors.travelBasicData.save);
|
||||
await page.waitForState('travel.card.basicData');
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it('should navigate to the summary and then clone the travel and its entries using the descriptor menu to get redirected to the cloned travel basic data', async() => {
|
||||
await page.waitToClick('vn-icon[icon="launch"]');
|
||||
await page.waitForState('travel.card.summary');
|
||||
await page.waitForTimeout(1000);
|
||||
await page.waitToClick(selectors.travelDescriptor.dotMenu);
|
||||
await page.waitToClick(selectors.travelDescriptor.dotMenuCloneWithEntries);
|
||||
await page.waitToClick(selectors.travelDescriptor.acceptClonation);
|
||||
await page.waitForState('travel.card.basicData');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Entry summary path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('buyer', 'entry');
|
||||
await page.accessToSearchResult('4');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should reach the second entry summary section', async() => {
|
||||
await page.waitForState('entry.card.summary');
|
||||
});
|
||||
|
||||
it(`should display details from the entry on the header`, async() => {
|
||||
await page.waitForTextInElement(selectors.entrySummary.header, 'The farmer');
|
||||
const result = await page.waitToGetProperty(selectors.entrySummary.header, 'innerText');
|
||||
|
||||
expect(result).toContain('The farmer');
|
||||
});
|
||||
|
||||
it('should display some entry details like the reference', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.entrySummary.reference, 'innerText');
|
||||
|
||||
expect(result).toContain('Movement 4');
|
||||
});
|
||||
|
||||
it('should display other entry details like the confirmed', async() => {
|
||||
const result = await page.checkboxState(selectors.entrySummary.confirmed, 'innerText');
|
||||
|
||||
expect(result).toContain('unchecked');
|
||||
});
|
||||
|
||||
it('should display all buys for the entry', async() => {
|
||||
const result = await page.countElement(selectors.entrySummary.anyBuyLine);
|
||||
|
||||
expect(result).toEqual(4);
|
||||
});
|
||||
});
|
|
@ -1,49 +0,0 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Entry descriptor path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('buyer', 'entry');
|
||||
await page.accessToSearchResult('2');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should reach the second entry summary section', async() => {
|
||||
await page.waitForState('entry.card.summary');
|
||||
});
|
||||
|
||||
it('should show some entry information', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.entryDescriptor.agency, 'innerText');
|
||||
|
||||
expect(result).toContain('inhouse pickup');
|
||||
});
|
||||
|
||||
it('should click the travels button to be redirected to the travels index filtered by the current agency', async() => {
|
||||
await page.waitToClick(selectors.entryDescriptor.travelsQuicklink);
|
||||
await page.expectURL('/travel/index');
|
||||
await page.expectURL('agencyModeFk');
|
||||
});
|
||||
|
||||
it('should go back to the entry summary', async() => {
|
||||
await page.waitToClick(selectors.globalItems.homeButton);
|
||||
await page.selectModule('entry');
|
||||
await page.accessToSearchResult('2');
|
||||
await page.waitForState('entry.card.summary');
|
||||
});
|
||||
|
||||
it('should click the entries button to be redirected to the entries index filtered by the current supplier', async() => {
|
||||
await page.waitToClick(selectors.entryDescriptor.entriesQuicklink);
|
||||
await page.expectURL('/entry/index');
|
||||
await page.expectURL('supplierFk');
|
||||
await page.expectURL('to');
|
||||
await page.expectURL('from');
|
||||
});
|
||||
});
|
|
@ -1,116 +0,0 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Entry lastest buys path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
const httpRequests = [];
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
page.on('request', req => {
|
||||
if (req.url().includes(`Buys/latestBuysFilter`))
|
||||
httpRequests.push(req.url());
|
||||
});
|
||||
await page.loginAndModule('buyer', 'entry');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should access the latest buys seccion and search not seeing the edit buys button yet', async() => {
|
||||
await page.waitToClick(selectors.entryLatestBuys.latestBuysSectionButton);
|
||||
await page.waitForSelector(selectors.entryLatestBuys.editBuysButton, {visible: false});
|
||||
});
|
||||
|
||||
it('should filter by name', async() => {
|
||||
await page.write(selectors.entryLatestBuys.generalSearchInput, 'Melee');
|
||||
await page.keyboard.press('Enter');
|
||||
await page.waitToClick(selectors.entryLatestBuys.chip);
|
||||
|
||||
expect(httpRequests.find(req => req.includes(('search=Melee')))).toBeDefined();
|
||||
});
|
||||
|
||||
it('should filter by reign and type', async() => {
|
||||
await page.click(selectors.entryLatestBuys.firstReignIcon);
|
||||
await page.autocompleteSearch(selectors.entryLatestBuys.typeInput, 'Alstroemeria');
|
||||
await page.click(selectors.entryLatestBuys.chip);
|
||||
|
||||
expect(httpRequests.find(req => req.includes(('categoryFk')))).toBeDefined();
|
||||
expect(httpRequests.find(req => req.includes(('typeFk')))).toBeDefined();
|
||||
});
|
||||
|
||||
it('should filter by sales person', async() => {
|
||||
await page.autocompleteSearch(selectors.entryLatestBuys.salesPersonInput, 'buyerNick');
|
||||
await page.waitToClick(selectors.entryLatestBuys.chip);
|
||||
|
||||
expect(httpRequests.find(req => req.includes(('salesPersonFk')))).toBeDefined();
|
||||
});
|
||||
|
||||
it('should filter by supplier', async() => {
|
||||
await page.autocompleteSearch(selectors.entryLatestBuys.supplierInput, 'Farmer King');
|
||||
await page.waitToClick(selectors.entryLatestBuys.chip);
|
||||
|
||||
expect(httpRequests.find(req => req.includes(('supplierFk')))).toBeDefined();
|
||||
});
|
||||
|
||||
it('should filter by active', async() => {
|
||||
await page.waitToClick(selectors.entryLatestBuys.activeCheck);
|
||||
await page.waitToClick(selectors.entryLatestBuys.activeCheck);
|
||||
await page.waitToClick(selectors.entryLatestBuys.chip);
|
||||
|
||||
expect(httpRequests.find(req => req.includes(('active=true')))).toBeDefined();
|
||||
expect(httpRequests.find(req => req.includes(('active=false')))).toBeDefined();
|
||||
});
|
||||
|
||||
it('should filter by visible', async() => {
|
||||
await page.waitToClick(selectors.entryLatestBuys.visibleCheck);
|
||||
await page.waitToClick(selectors.entryLatestBuys.visibleCheck);
|
||||
await page.waitToClick(selectors.entryLatestBuys.chip);
|
||||
|
||||
expect(httpRequests.find(req => req.includes(('visible=true')))).toBeDefined();
|
||||
expect(httpRequests.find(req => req.includes(('visible=false')))).toBeDefined();
|
||||
});
|
||||
|
||||
it('should filter by floramondo', async() => {
|
||||
await page.waitToClick(selectors.entryLatestBuys.floramondoCheck);
|
||||
await page.waitToClick(selectors.entryLatestBuys.floramondoCheck);
|
||||
await page.waitToClick(selectors.entryLatestBuys.chip);
|
||||
|
||||
expect(httpRequests.find(req => req.includes(('floramondo=true')))).toBeDefined();
|
||||
expect(httpRequests.find(req => req.includes(('floramondo=false')))).toBeDefined();
|
||||
});
|
||||
|
||||
it('should filter by tag Color', async() => {
|
||||
await page.waitToClick(selectors.entryLatestBuys.addTagButton);
|
||||
await page.autocompleteSearch(selectors.entryLatestBuys.itemTagInput, 'Color');
|
||||
await page.autocompleteSearch(selectors.entryLatestBuys.itemTagValueInput, 'Brown');
|
||||
await page.waitToClick(selectors.entryLatestBuys.chip);
|
||||
|
||||
expect(httpRequests.find(req => req.includes(('tags')))).toBeDefined();
|
||||
});
|
||||
|
||||
it('should select all lines but one and then check the edit buys button appears', async() => {
|
||||
await page.waitToClick(selectors.entryLatestBuys.allBuysCheckBox);
|
||||
await page.waitToClick(selectors.entryLatestBuys.secondBuyCheckBox);
|
||||
await page.waitForSelector(selectors.entryLatestBuys.editBuysButton, {visible: true});
|
||||
});
|
||||
|
||||
it('should open the edit dialog', async() => {
|
||||
await page.waitToClick(selectors.entryLatestBuys.editBuysButton);
|
||||
await page.waitForSelector(selectors.entryLatestBuys.fieldAutocomplete, {visible: true});
|
||||
});
|
||||
|
||||
it('should search for the "Description" and type a new one for the items in each selected buy', async() => {
|
||||
await page.autocompleteSearch(selectors.entryLatestBuys.fieldAutocomplete, 'Description');
|
||||
await page.write(selectors.entryLatestBuys.newValueInput, 'Crafted item');
|
||||
await page.waitToClick(selectors.entryLatestBuys.acceptEditBuysDialog);
|
||||
});
|
||||
|
||||
it('should navigate to the entry.buy section by clicking one of the buys', async() => {
|
||||
await page.waitToClick(selectors.entryLatestBuys.firstBuy);
|
||||
await page.waitForState('entry.card.buy.index');
|
||||
});
|
||||
});
|
|
@ -1,34 +0,0 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Entry create path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('buyer', 'entry');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should click the create entry button to open the form', async() => {
|
||||
await page.waitToClick(selectors.entryIndex.createEntryButton);
|
||||
await page.waitForState('entry.create');
|
||||
});
|
||||
|
||||
it('should fill the form to create a valid entry then redirect to basic Data', async() => {
|
||||
await page.autocompleteSearch(selectors.entryIndex.newEntrySupplier, 'The farmer');
|
||||
await page.autocompleteSearch(selectors.entryIndex.newEntryTravel, 'Warehouse');
|
||||
await page.autocompleteSearch(selectors.entryIndex.newEntryCompany, 'ORN');
|
||||
|
||||
await page.waitToClick(selectors.entryIndex.saveNewEntry);
|
||||
await page.waitForNavigation({
|
||||
waitUntil: 'load',
|
||||
});
|
||||
await page.waitForState('entry.card.basicData');
|
||||
});
|
||||
});
|
|
@ -1,81 +0,0 @@
|
|||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
const $ = {
|
||||
reference: 'vn-entry-basic-data vn-textfield[ng-model="$ctrl.entry.reference"]',
|
||||
invoiceNumber: 'vn-entry-basic-data vn-textfield[ng-model="$ctrl.entry.invoiceNumber"]',
|
||||
notes: 'vn-entry-basic-data vn-textfield[ng-model="$ctrl.entry.notes"]',
|
||||
observations: 'vn-entry-basic-data vn-textarea[ng-model="$ctrl.entry.observation"]',
|
||||
supplier: 'vn-entry-basic-data vn-autocomplete[ng-model="$ctrl.entry.supplierFk"]',
|
||||
currency: 'vn-entry-basic-data vn-autocomplete[ng-model="$ctrl.entry.currencyFk"]',
|
||||
commission: 'vn-entry-basic-data vn-input-number[ng-model="$ctrl.entry.commission"]',
|
||||
company: 'vn-entry-basic-data vn-autocomplete[ng-model="$ctrl.entry.companyFk"]',
|
||||
ordered: 'vn-entry-basic-data vn-check[ng-model="$ctrl.entry.isOrdered"]',
|
||||
confirmed: 'vn-entry-basic-data vn-check[ng-model="$ctrl.entry.isConfirmed"]',
|
||||
inventory: 'vn-entry-basic-data vn-check[ng-model="$ctrl.entry.isExcludedFromAvailable"]',
|
||||
raid: 'vn-entry-basic-data vn-check[ng-model="$ctrl.entry.isRaid"]',
|
||||
booked: 'vn-entry-basic-data vn-check[ng-model="$ctrl.entry.isBooked"]',
|
||||
save: 'vn-entry-basic-data button[type=submit]',
|
||||
};
|
||||
|
||||
describe('Entry basic data path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('buyer', 'entry');
|
||||
await page.accessToSearchResult('2');
|
||||
await page.accessToSection('entry.card.basicData');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should edit the basic data and confirm the reference was edited', async() => {
|
||||
await page.write($.reference, 'new movement 8');
|
||||
await page.write($.invoiceNumber, 'new movement 8');
|
||||
await page.write($.observations, ' edited');
|
||||
await page.autocompleteSearch($.supplier, 'Plants nick');
|
||||
await page.autocompleteSearch($.currency, 'eur');
|
||||
await page.clearInput($.commission);
|
||||
await page.write($.commission, '100');
|
||||
await page.autocompleteSearch($.company, 'CCs');
|
||||
await page.waitToClick($.ordered);
|
||||
await page.waitToClick($.confirmed);
|
||||
await page.waitToClick($.inventory);
|
||||
await page.waitToClick($.raid);
|
||||
await page.waitToClick($.booked);
|
||||
await page.waitToClick($.save);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
await page.reloadSection('entry.card.basicData');
|
||||
const reference = await page.waitToGetProperty($.reference, 'value');
|
||||
const supplier = await page.waitToGetProperty($.supplier, 'value');
|
||||
const invoiceNumber = await page.waitToGetProperty($.invoiceNumber, 'value');
|
||||
const observations = await page.waitToGetProperty($.observations, 'value');
|
||||
const currency = await page.waitToGetProperty($.currency, 'value');
|
||||
const commission = await page.waitToGetProperty($.commission, 'value');
|
||||
const company = await page.waitToGetProperty($.company, 'value');
|
||||
const ordered = await page.checkboxState($.ordered);
|
||||
const confirmed = await page.checkboxState($.confirmed);
|
||||
const inventory = await page.checkboxState($.inventory);
|
||||
const raid = await page.checkboxState($.raid);
|
||||
const booked = await page.checkboxState($.booked);
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
expect(reference).toEqual('new movement 8');
|
||||
expect(supplier).toEqual('Plants nick');
|
||||
expect(invoiceNumber).toEqual('new movement 8');
|
||||
expect(observations).toEqual('observation two edited');
|
||||
expect(currency).toEqual('EUR');
|
||||
expect(commission).toEqual('100');
|
||||
expect(company).toEqual('CCs');
|
||||
expect(ordered).toBe('checked');
|
||||
expect(confirmed).toBe('checked');
|
||||
expect(inventory).toBe('checked');
|
||||
expect(raid).toBe('checked');
|
||||
expect(booked).toBe('unchecked');
|
||||
});
|
||||
});
|
|
@ -1,65 +0,0 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Entry observations path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('buyer', 'entry');
|
||||
await page.accessToSearchResult('2');
|
||||
await page.accessToSection('entry.card.observation');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it(`should add two new observations of the same type then fail to save as they can't be repeated`, async() => {
|
||||
await page.waitToClick(selectors.entryObservations.addNewObservation);
|
||||
await page.waitToClick(selectors.entryObservations.addNewObservation);
|
||||
await page.autocompleteSearch(selectors.entryObservations.firstObservationType, 'SalesPerson');
|
||||
await page.autocompleteSearch(selectors.entryObservations.secondObservationType, 'SalesPerson');
|
||||
await page.write(selectors.entryObservations.firstObservationDescription, 'first observation');
|
||||
await page.write(selectors.entryObservations.secondObservationDescription, 'second observation');
|
||||
await page.waitToClick(selectors.entryObservations.saveObservationsButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain(`The observation type can't be repeated`);
|
||||
});
|
||||
|
||||
it('should set the 2nd observation of a different one and successfully save both', async() => {
|
||||
await page.autocompleteSearch(selectors.entryObservations.secondObservationType, 'Delivery');
|
||||
await page.waitToClick(selectors.entryObservations.saveObservationsButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it('should reload the section and make sure the first observation type was saved correctly', async() => {
|
||||
await page.reloadSection('entry.card.observation');
|
||||
const result = await page.waitToGetProperty(selectors.entryObservations.firstObservationType, 'value');
|
||||
|
||||
expect(result).toEqual('SalesPerson');
|
||||
});
|
||||
|
||||
it('should make sure the first observation description was saved correctly', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.entryObservations.firstObservationDescription, 'value');
|
||||
|
||||
expect(result).toEqual('first observation');
|
||||
});
|
||||
|
||||
it('should make sure the second observation type was saved correctly', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.entryObservations.secondObservationType, 'value');
|
||||
|
||||
expect(result).toEqual('Delivery');
|
||||
});
|
||||
|
||||
it('should make sure the second observation description was saved correctly', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.entryObservations.secondObservationDescription, 'value');
|
||||
|
||||
expect(result).toEqual('second observation');
|
||||
});
|
||||
});
|
|
@ -1,69 +0,0 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Entry import, create and edit buys path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('buyer', 'entry');
|
||||
await page.accessToSearchResult('3');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should count the summary buys and find there only one at this point', async() => {
|
||||
const buysCount = await page.countElement(selectors.entrySummary.anyBuyLine);
|
||||
|
||||
expect(buysCount).toEqual(2);
|
||||
});
|
||||
|
||||
it('should navigate to the buy section and then click the import button opening the import form', async() => {
|
||||
await page.accessToSection('entry.card.buy.index');
|
||||
await page.waitToClick(selectors.entryBuys.importButton);
|
||||
await page.waitForState('entry.card.buy.import');
|
||||
});
|
||||
|
||||
it('should fill the form, import the a JSON file and select items for each import and confirm import', async() => {
|
||||
let currentDir = process.cwd();
|
||||
let filePath = `${currentDir}/e2e/assets/07_import_buys.json`;
|
||||
|
||||
const [fileChooser] = await Promise.all([
|
||||
page.waitForFileChooser(),
|
||||
page.waitToClick(selectors.entryBuys.file)
|
||||
]);
|
||||
await fileChooser.accept([filePath]);
|
||||
|
||||
await page.waitForTextInField(selectors.entryBuys.ref, '200573095, 200573106, 200573117, 200573506');
|
||||
await page.waitForTextInField(selectors.entryBuys.observation, '729-6340 2846');
|
||||
|
||||
await page.autocompleteSearch(selectors.entryBuys.firstImportedItem, 'Ranged weapon longbow 200cm');
|
||||
await page.autocompleteSearch(selectors.entryBuys.secondImportedItem, 'Ranged weapon longbow 200cm');
|
||||
await page.autocompleteSearch(selectors.entryBuys.thirdImportedItem, 'Ranged weapon sniper rifle 113cm');
|
||||
await page.autocompleteSearch(selectors.entryBuys.fourthImportedItem, 'Melee weapon heavy shield 100cm');
|
||||
|
||||
await page.waitToClick(selectors.entryBuys.importBuysButton);
|
||||
|
||||
const message = await page.waitForSnackbar();
|
||||
const state = await page.getState();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
expect(state).toBe('entry.card.buy.index');
|
||||
});
|
||||
|
||||
it('should count the buys to find 4 buys have been added', async() => {
|
||||
await page.waitForNumberOfElements(selectors.entryBuys.anyBuyLine, 6);
|
||||
});
|
||||
|
||||
it('should delete the four buys that were just added', async() => {
|
||||
await page.waitToClick(selectors.entryBuys.allBuyCheckbox);
|
||||
await page.waitToClick(selectors.entryBuys.firstBuyCheckbox);
|
||||
await page.waitToClick(selectors.entryBuys.deleteBuysButton);
|
||||
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||
await page.waitForNumberOfElements(selectors.entryBuys.anyBuyLine, 1);
|
||||
});
|
||||
});
|
|
@ -65,11 +65,6 @@ describe('Supplier summary & descriptor path', () => {
|
|||
await page.waitForState('supplier.card.summary');
|
||||
});
|
||||
|
||||
it(`should navigate to the supplier's entries`, async() => {
|
||||
await page.waitToClick(selectors.supplierDescriptor.entriesButton);
|
||||
await page.waitForState('entry.index');
|
||||
});
|
||||
|
||||
it(`should navigate back to suppliers but a different one this time`, async() => {
|
||||
await page.waitToClick(selectors.globalItems.homeButton);
|
||||
await page.waitForState('home');
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
|
||||
export default function moduleImport(moduleName) {
|
||||
// TODO: Webpack watches module backend files when using dynamic import
|
||||
//return import(
|
||||
// return import(
|
||||
// /* webpackInclude: /modules\/[a-z0-9-]+\/front\/index.js$/ */
|
||||
// '../modules/'+ moduleName +'/front/index.js'
|
||||
//);
|
||||
// );
|
||||
|
||||
switch(moduleName) {
|
||||
case 'client' : return import('client/front');
|
||||
case 'item' : return import('item/front');
|
||||
case 'ticket' : return import('ticket/front');
|
||||
case 'order' : return import('order/front');
|
||||
case 'claim' : return import('claim/front');
|
||||
case 'zone' : return import('zone/front');
|
||||
case 'travel' : return import('travel/front');
|
||||
case 'worker' : return import('worker/front');
|
||||
case 'invoiceOut' : return import('invoiceOut/front');
|
||||
case 'invoiceIn' : return import('invoiceIn/front');
|
||||
case 'route' : return import('route/front');
|
||||
case 'entry' : return import('entry/front');
|
||||
case 'account' : return import('account/front');
|
||||
case 'supplier' : return import('supplier/front');
|
||||
case 'shelving' : return import('shelving/front');
|
||||
case 'monitor' : return import('monitor/front');
|
||||
switch (moduleName) {
|
||||
case 'client': return import('client/front');
|
||||
case 'item': return import('item/front');
|
||||
case 'ticket': return import('ticket/front');
|
||||
case 'order': return import('order/front');
|
||||
case 'claim': return import('claim/front');
|
||||
case 'zone': return import('zone/front');
|
||||
case 'travel': return import('travel/front');
|
||||
case 'worker': return import('worker/front');
|
||||
case 'invoiceOut': return import('invoiceOut/front');
|
||||
case 'invoiceIn': return import('invoiceIn/front');
|
||||
case 'route': return import('route/front');
|
||||
case 'account': return import('account/front');
|
||||
case 'supplier': return import('supplier/front');
|
||||
case 'shelving': return import('shelving/front');
|
||||
case 'monitor': return import('monitor/front');
|
||||
case 'entry': return import('entry/front');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -229,8 +229,9 @@
|
|||
"InvoiceIn is already booked": "InvoiceIn is already booked",
|
||||
"This workCenter is already assigned to this agency": "This workCenter is already assigned to this agency",
|
||||
"You can only have one PDA": "You can only have one PDA",
|
||||
"Incoterms and Customs agent are required for a non UEE member": "Incoterms and Customs agent are required for a non UEE member",
|
||||
"Incoterms and Customs agent are required for a non UEE member": "Incoterms and Customs agent are required for a non UEE member",
|
||||
"It has been invoiced but the PDF could not be generated": "It has been invoiced but the PDF could not be generated",
|
||||
"It has been invoiced but the PDF of refund not be generated": "It has been invoiced but the PDF of refund not be generated"
|
||||
|
||||
"It has been invoiced but the PDF of refund not be generated": "It has been invoiced but the PDF of refund not be generated",
|
||||
"Cannot add holidays on this day": "Cannot add holidays on this day",
|
||||
"Cannot send mail": "Cannot send mail"
|
||||
}
|
||||
|
|
|
@ -351,7 +351,7 @@
|
|||
"You are not allowed to modify the alias": "No estás autorizado a modificar el alias",
|
||||
"The address of the customer must have information about Incoterms and Customs Agent": "El consignatario del cliente debe tener informado Incoterms y Agente de aduanas",
|
||||
"The line could not be marked": "La linea no puede ser marcada",
|
||||
"This password can only be changed by the user themselves": "Esta contraseña solo puede ser modificada por el propio usuario",
|
||||
"Through this procedure, it is not possible to modify the password of users with verified email": "Mediante este procedimiento, no es posible modificar la contraseña de usuarios con correo verificado",
|
||||
"They're not your subordinate": "No es tu subordinado/a.",
|
||||
"No results found": "No se han encontrado resultados",
|
||||
"InvoiceIn is already booked": "La factura recibida está contabilizada",
|
||||
|
@ -365,5 +365,6 @@
|
|||
"You can only have one PDA": "Solo puedes tener un PDA",
|
||||
"It has been invoiced but the PDF could not be generated": "Se ha facturado pero no se ha podido generar el PDF",
|
||||
"It has been invoiced but the PDF of refund not be generated": "Se ha facturado pero no se ha podido generar el PDF del abono",
|
||||
"Payment method is required": "El método de pago es obligatorio"
|
||||
}
|
||||
"Payment method is required": "El método de pago es obligatorio",
|
||||
"Cannot send mail": "Não é possível enviar o email"
|
||||
}
|
||||
|
|
|
@ -359,5 +359,6 @@
|
|||
"Select ticket or client": "Choisissez un ticket ou un client",
|
||||
"It was not able to create the invoice": "Il n'a pas été possible de créer la facture",
|
||||
"It has been invoiced but the PDF could not be generated": "La facture a été émise mais le PDF n'a pas pu être généré",
|
||||
"It has been invoiced but the PDF of refund not be generated": "Il a été facturé mais le PDF de remboursement n'a pas été généré"
|
||||
"It has been invoiced but the PDF of refund not be generated": "Il a été facturé mais le PDF de remboursement n'a pas été généré",
|
||||
"Cannot send mail": "Impossible d'envoyer le mail"
|
||||
}
|
||||
|
|
|
@ -13,7 +13,11 @@ module.exports = Self => {
|
|||
|
||||
Self.setUnverifiedPassword = async(id, pass, options) => {
|
||||
const {emailVerified} = await models.VnUser.findById(id, {fields: ['emailVerified']}, options);
|
||||
if (emailVerified) throw new ForbiddenError('This password can only be changed by the user themselves');
|
||||
if (emailVerified) {
|
||||
throw new ForbiddenError(
|
||||
'Through this procedure, it is not possible to modify the password of users with verified email'
|
||||
);
|
||||
}
|
||||
|
||||
await models.VnUser.setPassword(id, pass, options);
|
||||
};
|
||||
|
|
|
@ -69,30 +69,26 @@ module.exports = Self => {
|
|||
}
|
||||
}
|
||||
}, myOptions);
|
||||
// Get sales person from claim client
|
||||
const salesPerson = claim.client().salesPersonUser();
|
||||
|
||||
const changedPickup = args.pickup != claim.pickup;
|
||||
const changedPickup = args.pickup && args.pickup != claim.pickup;
|
||||
|
||||
// Validate when claimState has been changed
|
||||
if (args.claimStateFk) {
|
||||
const canEditOldState = await models.ClaimState.isEditable(ctx, claim.claimStateFk, myOptions);
|
||||
const canEditNewState = await models.ClaimState.isEditable(ctx, args.claimStateFk, myOptions);
|
||||
const canEditState = await models.ACL.checkAccessAcl(ctx, 'Claim', 'editState', 'WRITE');
|
||||
const canEditPickup = await models.ACL.checkAccessAcl(ctx, 'Claim', 'editPickup', 'WRITE');
|
||||
|
||||
if (!canEditOldState || !canEditNewState || changedPickup && !canEditState)
|
||||
if (!canEditOldState || !canEditNewState || (changedPickup && !canEditPickup))
|
||||
throw new UserError(`You don't have enough privileges to change that field`);
|
||||
}
|
||||
|
||||
delete args.ctx;
|
||||
const updatedClaim = await claim.updateAttributes(args, myOptions);
|
||||
|
||||
// When pickup has been changed
|
||||
const salesPerson = claim.client().salesPersonUser();
|
||||
if (salesPerson) {
|
||||
if (changedPickup && updatedClaim.pickup)
|
||||
await notifyPickUp(ctx, salesPerson.id, claim);
|
||||
|
||||
// When claimState has been changed
|
||||
if (args.claimStateFk) {
|
||||
const newState = await models.ClaimState.findById(args.claimStateFk, null, myOptions);
|
||||
await notifyStateChange(ctx, salesPerson.id, claim, newState.description);
|
||||
|
@ -113,7 +109,7 @@ module.exports = Self => {
|
|||
async function notifyStateChange(ctx, workerId, claim, newState) {
|
||||
const models = Self.app.models;
|
||||
const url = await models.Url.getUrl();
|
||||
const $t = ctx.req.__; // $translate
|
||||
const $t = ctx.req.__;
|
||||
|
||||
const message = $t(`Claim state has changed to`, {
|
||||
claimId: claim.id,
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
name: unpaid
|
||||
columns:
|
||||
clientFk: client
|
||||
dated: date
|
||||
amount: amount
|
|
@ -0,0 +1,5 @@
|
|||
name: impagado
|
||||
columns:
|
||||
clientFk: cliente
|
||||
dated: fecha
|
||||
amount: cantidad
|
|
@ -96,10 +96,6 @@ module.exports = Self => {
|
|||
arg: 'despiteOfClient',
|
||||
type: 'any'
|
||||
},
|
||||
{
|
||||
arg: 'hasIncoterms',
|
||||
type: 'boolean'
|
||||
},
|
||||
{
|
||||
arg: 'hasElectronicInvoice',
|
||||
type: 'boolean'
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
{
|
||||
"name": "ClientUnpaid",
|
||||
"base": "VnModel",
|
||||
"mixins": {
|
||||
"Loggable": true
|
||||
},
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "clientUnpaid"
|
||||
|
@ -25,4 +28,4 @@
|
|||
"foreignKey": "clientFk"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,9 +97,6 @@
|
|||
"hasCoreVnh": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hasIncoterms": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isTaxDataChecked":{
|
||||
"type": "boolean"
|
||||
},
|
||||
|
|
|
@ -184,11 +184,6 @@
|
|||
</vn-check>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-check
|
||||
label="Incoterms authorization"
|
||||
ng-model="$ctrl.client.hasIncoterms"
|
||||
vn-acl="administrative">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
label="Electronic invoice"
|
||||
ng-model="$ctrl.client.hasElectronicInvoice"
|
||||
|
|
|
@ -1,234 +0,0 @@
|
|||
<mg-ajax path="Entries/{{patch.params.id}}" options="vnPatch"></mg-ajax>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.entry"
|
||||
form="form"
|
||||
save="patch">
|
||||
</vn-watcher>
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="Warehouses"
|
||||
data="warehouses">
|
||||
</vn-crud-model>
|
||||
<form name="form" ng-submit="watcher.submit()" class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
ng-model="$ctrl.entry.supplierFk"
|
||||
url="Suppliers"
|
||||
show-field="nickname"
|
||||
search-function="{or: [{id: $search}, {nickname: {like: '%'+ $search +'%'}}]}"
|
||||
value-field="id"
|
||||
order="nickname"
|
||||
label="Supplier"
|
||||
required="true">
|
||||
<tpl-item>
|
||||
<div>#{{::nickname}}</div>
|
||||
<div class="text-secondary text-caption">#{{::id}}</div>
|
||||
</tpl-item>
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
ng-model="$ctrl.entry.travelFk"
|
||||
url="Travels/filter"
|
||||
search-function="$ctrl.searchFunction($search)"
|
||||
value-field="id"
|
||||
show-field="warehouseInName"
|
||||
order="id"
|
||||
label="Travel"
|
||||
required="true">
|
||||
<tpl-item>
|
||||
<div>
|
||||
{{::agencyModeName}} - {{::warehouseInName}} ({{::shipped | date: 'dd/MM/yyyy'}}) →
|
||||
{{::warehouseOutName}} ({{::landed | date: 'dd/MM/yyyy'}})
|
||||
</div>
|
||||
<div class="text-secondary text-caption">#{{::id}}</div>
|
||||
</tpl-item>
|
||||
<append>
|
||||
<vn-icon-button
|
||||
icon="filter_alt"
|
||||
vn-click-stop="$ctrl.showFilterDialog($ctrl.entry.travelFk)"
|
||||
vn-tooltip="Filter...">
|
||||
</vn-icon-button>
|
||||
</append>
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
vn-one
|
||||
label="Reference"
|
||||
ng-model="$ctrl.entry.reference"
|
||||
rule
|
||||
vn-focus>
|
||||
</vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
vn-one
|
||||
label="Invoice number"
|
||||
ng-model="$ctrl.entry.invoiceNumber"
|
||||
rule
|
||||
vn-focus>
|
||||
</vn-textfield>
|
||||
<vn-autocomplete
|
||||
url="Companies"
|
||||
label="Company"
|
||||
show-field="code"
|
||||
value-field="id"
|
||||
ng-model="$ctrl.entry.companyFk">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
label="Currency"
|
||||
ng-model="$ctrl.entry.currencyFk"
|
||||
url="Currencies"
|
||||
show-field="code"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
<vn-input-number
|
||||
vn-one
|
||||
step="0.1"
|
||||
label="Commission"
|
||||
ng-model="$ctrl.entry.commission"
|
||||
rule>
|
||||
</vn-input-number>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textarea
|
||||
vn-one
|
||||
label="Observation"
|
||||
ng-model="$ctrl.entry.observation"
|
||||
rule>
|
||||
</vn-textarea>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-check
|
||||
label="Ordered"
|
||||
ng-model="$ctrl.entry.isOrdered">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
label="Confirmed"
|
||||
ng-model="$ctrl.entry.isConfirmed">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
label="Inventory"
|
||||
ng-model="$ctrl.entry.isExcludedFromAvailable">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
label="Raid"
|
||||
ng-model="$ctrl.entry.isRaid">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
label="Booked"
|
||||
ng-model="$ctrl.entry.isBooked"
|
||||
vn-acl="administrative"
|
||||
>
|
||||
</vn-check>
|
||||
</vn-horizontal>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit
|
||||
disabled="!watcher.dataChanged()"
|
||||
label="Save">
|
||||
</vn-submit>
|
||||
<vn-button
|
||||
class="cancel"
|
||||
label="Undo changes"
|
||||
disabled="!watcher.dataChanged()"
|
||||
ng-click="watcher.loadOriginalData()">
|
||||
</vn-button>
|
||||
</vn-button-bar>
|
||||
</form>
|
||||
|
||||
<!-- Filter travel dialog -->
|
||||
<vn-dialog
|
||||
vn-id="filterDialog"
|
||||
message="Filter travel">
|
||||
<tpl-body class="travelFilter">
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete
|
||||
label="Agency"
|
||||
ng-model="$ctrl.travelFilterParams.agencyModeFk"
|
||||
url="AgencyModes"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
label="Warehouse Out"
|
||||
ng-model="$ctrl.travelFilterParams.warehouseOutFk"
|
||||
data="warehouses"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
label="Warehouse In"
|
||||
ng-model="$ctrl.travelFilterParams.warehouseInFk"
|
||||
data="warehouses"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
<vn-date-picker
|
||||
label="Shipped"
|
||||
ng-model="$ctrl.travelFilterParams.shipped">
|
||||
</vn-date-picker>
|
||||
<vn-date-picker
|
||||
label="Landed"
|
||||
ng-model="$ctrl.travelFilterParams.landed">
|
||||
</vn-date-picker>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal class="vn-mb-md">
|
||||
<vn-button vn-none
|
||||
label="Search"
|
||||
ng-click="$ctrl.filter()">
|
||||
</vn-button>
|
||||
</vn-horizontal>
|
||||
<vn-crud-model
|
||||
vn-id="travelsModel"
|
||||
url="Travels"
|
||||
filter="$ctrl.travelFilter"
|
||||
data="travels"
|
||||
limit="10">
|
||||
</vn-crud-model>
|
||||
<vn-data-viewer
|
||||
model="travelsModel"
|
||||
class="vn-w-lg">
|
||||
<vn-table class="scrollable">
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-th shrink>ID</vn-th>
|
||||
<vn-th expand>Agency</vn-th>
|
||||
<vn-th expand>Warehouse Out</vn-th>
|
||||
<vn-th expand>Warehouse In</vn-th>
|
||||
<vn-th expand>Shipped</vn-th>
|
||||
<vn-th expand>Landed</vn-th>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<a ng-repeat="travel in travels"
|
||||
class="clickable vn-tr search-result"
|
||||
ng-click="$ctrl.selectTravel(travel.id)">
|
||||
<vn-td shrink>
|
||||
<span
|
||||
vn-click-stop="travelDescriptor.show($event, travel.id)"
|
||||
class="link">
|
||||
{{::travel.id}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td expand>{{::travel.agency.name}}</vn-td>
|
||||
<vn-td expand>{{::travel.warehouseOut.name}}</vn-td>
|
||||
<vn-td expand>{{::travel.warehouseIn.name}}</vn-td>
|
||||
<vn-td expand>{{::travel.shipped | date: 'dd/MM/yyyy'}}</vn-td>
|
||||
<vn-td expand>{{::travel.landed | date: 'dd/MM/yyyy'}}</vn-td>
|
||||
</a>
|
||||
</vn-tbody>
|
||||
</vn-table>
|
||||
</vn-data-viewer>
|
||||
<vn-travel-descriptor-popover
|
||||
vn-id="travel-descriptor"
|
||||
warehouse-fk="$ctrl.vnConfig.warehouseFk">
|
||||
</vn-travel-descriptor-popover>
|
||||
</tpl-body>
|
||||
</vn-dialog>
|
|
@ -1,68 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
import './style.scss';
|
||||
|
||||
class Controller extends Section {
|
||||
showFilterDialog(travel) {
|
||||
this.activeTravel = travel;
|
||||
this.travelFilterParams = {};
|
||||
this.travelFilter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'agency',
|
||||
scope: {
|
||||
fields: ['name']
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'warehouseIn',
|
||||
scope: {
|
||||
fields: ['name']
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'warehouseOut',
|
||||
scope: {
|
||||
fields: ['name']
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
this.$.filterDialog.show();
|
||||
}
|
||||
|
||||
selectTravel(id) {
|
||||
this.entry.travelFk = id;
|
||||
this.$.filterDialog.hide();
|
||||
}
|
||||
|
||||
filter() {
|
||||
const filter = this.travelFilter;
|
||||
const params = this.travelFilterParams;
|
||||
const where = {};
|
||||
for (let key in params) {
|
||||
const value = params[key];
|
||||
if (!value) continue;
|
||||
|
||||
switch (key) {
|
||||
case 'agencyModeFk':
|
||||
case 'warehouseInFk':
|
||||
case 'warehouseOutFk':
|
||||
case 'shipped':
|
||||
case 'landed':
|
||||
where[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
filter.where = where;
|
||||
this.$.travelsModel.applyFilter(filter);
|
||||
}
|
||||
}
|
||||
ngModule.vnComponent('vnEntryBasicData', {
|
||||
template: require('./index.html'),
|
||||
bindings: {
|
||||
entry: '<'
|
||||
},
|
||||
controller: Controller
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
.travelFilter{
|
||||
width: 950px;
|
||||
}
|
|
@ -1,205 +0,0 @@
|
|||
<mg-ajax path="dms/upload" options="vnPost"></mg-ajax>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.dms">
|
||||
</vn-watcher>
|
||||
<form
|
||||
name="form"
|
||||
ng-submit="$ctrl.onSubmit()"
|
||||
class="vn-ma-md">
|
||||
<div class="vn-w-lg">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
<vn-input-file
|
||||
vn-one
|
||||
label="File"
|
||||
ng-model="$ctrl.import.file"
|
||||
on-change="$ctrl.onFileChange($event)"
|
||||
accept="application/json"
|
||||
required="true">
|
||||
<append>
|
||||
<vn-icon vn-none
|
||||
color-marginal
|
||||
title="{{'JSON files only' | translate}}"
|
||||
icon="info">
|
||||
</vn-icon>
|
||||
</append>
|
||||
</vn-input-file>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal ng-show="$ctrl.import.ref">
|
||||
<vn-textfield vn-focus
|
||||
vn-one
|
||||
label="Reference"
|
||||
ng-model="$ctrl.import.ref">
|
||||
</vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal ng-show="$ctrl.import.observation">
|
||||
<vn-textarea
|
||||
vn-one
|
||||
label="Observation"
|
||||
ng-model="$ctrl.import.observation">
|
||||
</vn-textarea>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal ng-show="$ctrl.import.buys.length > 0">
|
||||
<table class="vn-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th translate>Item</th>
|
||||
<th translate expand>Description</th>
|
||||
<th translate center>Size</th>
|
||||
<th translate center>Packing</th>
|
||||
<th translate center>Grouping</th>
|
||||
<th translate center>Buying value</th>
|
||||
<th translate center>Box</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ng-repeat="buy in $ctrl.import.buys">
|
||||
<tr>
|
||||
<td title="{{::buy.itemFk}}">
|
||||
<vn-autocomplete
|
||||
class="dense"
|
||||
vn-focus
|
||||
url="Entries/{{$ctrl.$params.id}}/lastItemBuys"
|
||||
ng-model="buy.itemFk"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
search-function="$ctrl.itemSearchFunc($search)"
|
||||
order="id DESC"
|
||||
tabindex="1">
|
||||
<tpl-item>
|
||||
{{::id}} - {{::name}}
|
||||
</tpl-item>
|
||||
<append>
|
||||
<vn-icon-button
|
||||
icon="filter_alt"
|
||||
vn-click-stop="$ctrl.showFilterDialog(buy)"
|
||||
vn-tooltip="Filter...">
|
||||
</vn-icon-button>
|
||||
</append>
|
||||
</vn-autocomplete>
|
||||
</td>
|
||||
<td title="{{::buy.description}}" expand>{{::buy.description | dashIfEmpty}}</td>
|
||||
<td center title="{{::buy.size}}">{{::buy.size | dashIfEmpty}}</td>
|
||||
<td center>{{::buy.packing | dashIfEmpty}}</td>
|
||||
<td center>{{::buy.grouping | dashIfEmpty}}</td>
|
||||
<td>{{::buy.buyingValue | currency: 'EUR':2}}</td>
|
||||
<td center title="{{::buy.packagingFk | dashIfEmpty}}">
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
url="Packagings"
|
||||
show-field="id"
|
||||
value-field="id"
|
||||
where="{isBox: true}"
|
||||
ng-model="buy.packagingFk">
|
||||
</vn-autocomplete>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</vn-horizontal>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit
|
||||
label="Import buys">
|
||||
</vn-submit>
|
||||
<vn-button
|
||||
class="cancel"
|
||||
label="Cancel"
|
||||
ui-sref="entry.card.buy.index">
|
||||
</vn-button>
|
||||
</vn-button-bar>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<vn-dialog
|
||||
vn-id="filterDialog"
|
||||
message="Filter item">
|
||||
<tpl-body class="itemFilter">
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
label="Name"
|
||||
ng-model="$ctrl.itemFilterParams.name"
|
||||
ng-keydown="$ctrl.onKeyPress($event)"
|
||||
vn-focus>
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
label="Size"
|
||||
ng-model="$ctrl.itemFilterParams.size"
|
||||
ng-keydown="$ctrl.onKeyPress($event)">
|
||||
</vn-textfield>
|
||||
<vn-autocomplete
|
||||
label="Producer"
|
||||
ng-model="$ctrl.itemFilterParams.producerFk"
|
||||
ng-keydown="$ctrl.onKeyPress($event)"
|
||||
url="Producers"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
label="Type"
|
||||
ng-model="$ctrl.itemFilterParams.typeFk"
|
||||
ng-keydown="$ctrl.onKeyPress($event)"
|
||||
url="ItemTypes"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
label="Color"
|
||||
ng-model="$ctrl.itemFilterParams.inkFk"
|
||||
ng-keydown="$ctrl.onKeyPress($event)"
|
||||
url="Inks"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal class="vn-mb-md">
|
||||
<vn-button vn-none
|
||||
label="Search"
|
||||
ng-click="$ctrl.filter()">
|
||||
</vn-button>
|
||||
</vn-horizontal>
|
||||
<vn-crud-model
|
||||
vn-id="itemsModel"
|
||||
url="Entries/{{$ctrl.$params.id}}/lastItemBuys"
|
||||
filter="$ctrl.itemFilter"
|
||||
data="items"
|
||||
limit="10">
|
||||
</vn-crud-model>
|
||||
<vn-data-viewer
|
||||
model="itemsModel"
|
||||
class="vn-w-lg">
|
||||
<vn-table class="scrollable">
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-th shrink>ID</vn-th>
|
||||
<vn-th expand>Item</vn-th>
|
||||
<vn-th number>Size</vn-th>
|
||||
<vn-th expand>Producer</vn-th>
|
||||
<vn-th>Color</vn-th>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<a ng-repeat="item in items"
|
||||
class="clickable vn-tr search-result"
|
||||
ng-click="$ctrl.selectItem(item.id)">
|
||||
<vn-td shrink>
|
||||
<span
|
||||
vn-click-stop="itemDescriptor.show($event, item.id)"
|
||||
class="link">
|
||||
{{::item.id}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td expand>{{::item.name}}</vn-td>
|
||||
<vn-td number>{{::item.size}}</vn-td>
|
||||
<vn-td expand>{{::item.producerName}}</vn-td>
|
||||
<vn-td>{{::item.inkName}}</vn-td>
|
||||
</a>
|
||||
</vn-tbody>
|
||||
</vn-table>
|
||||
</vn-data-viewer>
|
||||
<vn-item-descriptor-popover
|
||||
vn-id="item-descriptor"
|
||||
warehouse-fk="$ctrl.vnConfig.warehouseFk">
|
||||
</vn-item-descriptor-popover>
|
||||
</tpl-body>
|
||||
</vn-dialog>
|
|
@ -1,159 +0,0 @@
|
|||
import ngModule from '../../module';
|
||||
import Section from 'salix/components/section';
|
||||
import './style.scss';
|
||||
|
||||
class Controller extends Section {
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
this.import = {
|
||||
file: '',
|
||||
invoice: null,
|
||||
buys: []
|
||||
};
|
||||
}
|
||||
|
||||
onFileChange($event) {
|
||||
const input = $event.target;
|
||||
const file = input.files[0];
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = event =>
|
||||
this.fillData(event.target.result);
|
||||
reader.readAsText(file, 'UTF-8');
|
||||
}
|
||||
|
||||
fillData(raw) {
|
||||
const data = JSON.parse(raw);
|
||||
const [invoice] = data.invoices;
|
||||
|
||||
this.$.$applyAsync(() => {
|
||||
this.import.observation = invoice.tx_awb;
|
||||
|
||||
const companyName = invoice.tx_company;
|
||||
const boxes = invoice.boxes;
|
||||
const buys = [];
|
||||
for (let box of boxes) {
|
||||
const boxVolume = box.nu_length * box.nu_width * box.nu_height;
|
||||
for (let product of box.products) {
|
||||
const packing = product.nu_stems_bunch * product.nu_bunches;
|
||||
buys.push({
|
||||
description: product.nm_product,
|
||||
companyName: companyName,
|
||||
size: product.nu_length,
|
||||
packing: packing,
|
||||
grouping: product.nu_stems_bunch,
|
||||
buyingValue: parseFloat(product.mny_rate_stem),
|
||||
volume: boxVolume,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const boxesId = boxes.map(box => box.id_box);
|
||||
this.import.ref = boxesId.join(', ');
|
||||
|
||||
this.fetchBuys(buys);
|
||||
});
|
||||
}
|
||||
|
||||
fetchBuys(buys) {
|
||||
const params = {buys};
|
||||
const query = `Entries/${this.$params.id}/importBuysPreview`;
|
||||
this.$http.post(query, params).then(res => {
|
||||
this.import.buys = res.data;
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
try {
|
||||
const params = this.import;
|
||||
const hasAnyEmptyRow = params.buys.some(buy => {
|
||||
return buy.itemFk == null;
|
||||
});
|
||||
|
||||
if (hasAnyEmptyRow)
|
||||
throw new Error(`Some of the imported buys doesn't have an item`);
|
||||
|
||||
const query = `Entries/${this.$params.id}/importBuys`;
|
||||
return this.$http.post(query, params)
|
||||
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')))
|
||||
.then(() => this.$state.go('entry.card.buy.index'));
|
||||
} catch (e) {
|
||||
this.vnApp.showError(this.$t(e.message));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
itemSearchFunc($search) {
|
||||
return /^\d+$/.test($search)
|
||||
? {id: $search}
|
||||
: {name: {like: '%' + $search + '%'}};
|
||||
}
|
||||
|
||||
showFilterDialog(buy) {
|
||||
this.activeBuy = buy;
|
||||
this.itemFilterParams = {};
|
||||
this.itemFilter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'producer',
|
||||
scope: {
|
||||
fields: ['name']
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'ink',
|
||||
scope: {
|
||||
fields: ['name']
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
this.$.filterDialog.show();
|
||||
}
|
||||
|
||||
selectItem(id) {
|
||||
this.activeBuy['itemFk'] = id;
|
||||
this.$.filterDialog.hide();
|
||||
}
|
||||
|
||||
filter() {
|
||||
const filter = this.itemFilter;
|
||||
const params = this.itemFilterParams;
|
||||
const where = {};
|
||||
|
||||
for (let key in params) {
|
||||
const value = params[key];
|
||||
if (!value) continue;
|
||||
|
||||
switch (key) {
|
||||
case 'name':
|
||||
where[key] = {like: `%${value}%`};
|
||||
break;
|
||||
case 'producerFk':
|
||||
case 'typeFk':
|
||||
case 'size':
|
||||
case 'inkFk':
|
||||
where[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
filter.where = where;
|
||||
this.$.itemsModel.applyFilter(filter);
|
||||
}
|
||||
|
||||
onKeyPress($event) {
|
||||
if ($event.key === 'Enter')
|
||||
this.filter();
|
||||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$element', '$scope'];
|
||||
|
||||
ngModule.vnComponent('vnEntryBuyImport', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
worker: '<'
|
||||
}
|
||||
});
|
|
@ -1,199 +0,0 @@
|
|||
import './index.js';
|
||||
|
||||
describe('Entry', () => {
|
||||
describe('Component vnEntryBuyImport', () => {
|
||||
let controller;
|
||||
let $httpParamSerializer;
|
||||
let $httpBackend;
|
||||
|
||||
beforeEach(ngModule('entry'));
|
||||
|
||||
beforeEach(angular.mock.inject(($componentController, $compile, $rootScope, _$httpParamSerializer_, _$httpBackend_) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpParamSerializer = _$httpParamSerializer_;
|
||||
let $element = $compile('<vn-entry-buy-import-buys></vn-entry-latest-buys')($rootScope);
|
||||
controller = $componentController('vnEntryBuyImport', {$element});
|
||||
controller.entry = {
|
||||
id: 1
|
||||
};
|
||||
controller.$params = {id: 1};
|
||||
}));
|
||||
|
||||
describe('fillData()', () => {
|
||||
it(`should call to the fillData() method`, () => {
|
||||
controller.fetchBuys = jest.fn();
|
||||
|
||||
const rawData = `{
|
||||
"invoices": [
|
||||
{
|
||||
"tx_awb": "123456",
|
||||
"boxes": [
|
||||
{
|
||||
"id_box": 1,
|
||||
"nu_length": 1,
|
||||
"nu_width": 15,
|
||||
"nu_height": 80,
|
||||
"products": [
|
||||
{
|
||||
"nm_product": "Bow",
|
||||
"nu_length": 1,
|
||||
"nu_stems_bunch": 1,
|
||||
"nu_bunches": 1,
|
||||
"mny_rate_stem": 5.77
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id_box": 2,
|
||||
"nu_length": 25,
|
||||
"nu_width": 1,
|
||||
"nu_height": 45,
|
||||
"products": [
|
||||
{
|
||||
"nm_product": "Arrow",
|
||||
"nu_length": 25,
|
||||
"nu_stems_bunch": 1,
|
||||
"nu_bunches": 1,
|
||||
"mny_rate_stem": 2.16
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]}`;
|
||||
const expectedBuys = [
|
||||
{
|
||||
'buyingValue': 5.77,
|
||||
'description': 'Bow',
|
||||
'grouping': 1,
|
||||
'packing': 1,
|
||||
'size': 1,
|
||||
'volume': 1200},
|
||||
|
||||
{
|
||||
'buyingValue': 2.16,
|
||||
'description': 'Arrow',
|
||||
'grouping': 1,
|
||||
'packing': 1,
|
||||
'size': 25,
|
||||
'volume': 1125}
|
||||
];
|
||||
controller.fillData(rawData);
|
||||
controller.$.$apply();
|
||||
|
||||
const importData = controller.import;
|
||||
|
||||
expect(importData.observation).toEqual('123456');
|
||||
expect(importData.ref).toEqual('1, 2');
|
||||
|
||||
expect(controller.fetchBuys).toHaveBeenCalledWith(expectedBuys);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchBuys()', () => {
|
||||
it(`should perform a query to fetch the buys data`, () => {
|
||||
const buys = [
|
||||
{
|
||||
'buyingValue': 5.77,
|
||||
'description': 'Bow',
|
||||
'grouping': 1,
|
||||
'packing': 1,
|
||||
'size': 1,
|
||||
'volume': 1200},
|
||||
|
||||
{
|
||||
'buyingValue': 2.16,
|
||||
'description': 'Arrow',
|
||||
'grouping': 1,
|
||||
'packing': 1,
|
||||
'size': 25,
|
||||
'volume': 1125}
|
||||
];
|
||||
|
||||
const query = `Entries/1/importBuysPreview`;
|
||||
$httpBackend.expectPOST(query).respond(200, buys);
|
||||
controller.fetchBuys(buys);
|
||||
$httpBackend.flush();
|
||||
|
||||
const importData = controller.import;
|
||||
|
||||
expect(importData.buys.length).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onSubmit()', () => {
|
||||
it(`should throw an error when some of the rows doesn't have an item`, () => {
|
||||
jest.spyOn(controller.vnApp, 'showError');
|
||||
|
||||
controller.import = {
|
||||
observation: '123456',
|
||||
ref: '1, 2',
|
||||
buys: [
|
||||
{
|
||||
'buyingValue': 5.77,
|
||||
'description': 'Bow',
|
||||
'grouping': 1,
|
||||
'packing': 1,
|
||||
'size': 1,
|
||||
'volume': 1200},
|
||||
{
|
||||
'buyingValue': 2.16,
|
||||
'description': 'Arrow',
|
||||
'grouping': 1,
|
||||
'packing': 1,
|
||||
'size': 25,
|
||||
'volume': 1125}
|
||||
]
|
||||
};
|
||||
|
||||
controller.onSubmit();
|
||||
|
||||
const message = `Some of the imported buys doesn't have an item`;
|
||||
|
||||
expect(controller.vnApp.showError).toHaveBeenCalledWith(message);
|
||||
});
|
||||
|
||||
it(`should now perform a query to update columns`, () => {
|
||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||
controller.$state.go = jest.fn();
|
||||
|
||||
controller.import = {
|
||||
observation: '123456',
|
||||
ref: '1, 2',
|
||||
buys: [
|
||||
{
|
||||
'itemFk': 10,
|
||||
'buyingValue': 5.77,
|
||||
'description': 'Bow',
|
||||
'grouping': 1,
|
||||
'packing': 1,
|
||||
'size': 1,
|
||||
'volume': 1200},
|
||||
{
|
||||
'itemFk': 11,
|
||||
'buyingValue': 2.16,
|
||||
'description': 'Arrow',
|
||||
'grouping': 1,
|
||||
'packing': 1,
|
||||
'size': 25,
|
||||
'volume': 1125}
|
||||
]
|
||||
};
|
||||
const params = controller.import;
|
||||
|
||||
const query = `Entries/1/importBuys`;
|
||||
$httpBackend.expectPOST(query, params).respond(200, params.buys);
|
||||
controller.onSubmit();
|
||||
$httpBackend.flush();
|
||||
|
||||
const importData = controller.import;
|
||||
|
||||
expect(importData.buys.length).toEqual(2);
|
||||
|
||||
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
|
||||
expect(controller.$state.go).toHaveBeenCalledWith('entry.card.buy.index');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
.itemFilter {
|
||||
vn-table.scrollable {
|
||||
height: 500px
|
||||
}
|
||||
}
|
|
@ -1,243 +0,0 @@
|
|||
<vn-crud-model
|
||||
auto-load="true"
|
||||
vn-id="model"
|
||||
url="Entries/{{$ctrl.$params.id}}/getBuys"
|
||||
data="$ctrl.buys"
|
||||
limit="20">
|
||||
</vn-crud-model>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.buys">
|
||||
</vn-watcher>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
class="vn-w-xl">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<div class="tableWrapper">
|
||||
<vn-horizontal class="header">
|
||||
<vn-tool-bar class="vn-mb-md">
|
||||
<vn-button
|
||||
disabled="$ctrl.selectedBuys() == 0"
|
||||
ng-click="deleteBuys.show()"
|
||||
vn-tooltip="Delete buy(s)"
|
||||
icon="delete">
|
||||
</vn-button>
|
||||
</vn-tool-bar>
|
||||
<vn-one class="taxes" ng-if="$ctrl.sales.length > 0">
|
||||
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.ticket.totalWithoutVat | currency: 'EUR':2}}</p>
|
||||
<p><vn-label translate>VAT</vn-label> {{$ctrl.ticket.totalWithVat - $ctrl.ticket.totalWithoutVat | currency: 'EUR':2}}</p>
|
||||
<p><vn-label><strong>Total</strong></vn-label> <strong>{{$ctrl.ticket.totalWithVat | currency: 'EUR':2}}</strong></p>
|
||||
</vn-one>
|
||||
</vn-horizontal>
|
||||
<table class="vn-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th shrink>
|
||||
<vn-multi-check model="model" on-change="$ctrl.resetChanges()">
|
||||
</vn-multi-check>
|
||||
</th>
|
||||
<th translate center>Item</th>
|
||||
<th translate center>Quantity</th>
|
||||
<th translate center>Package</th>
|
||||
<th translate>Stickers</th>
|
||||
<th translate>Weight</th>
|
||||
<th translate>Packing</th>
|
||||
<th translate>Grouping</th>
|
||||
<th translate>Buying value</th>
|
||||
<th translate expand>Grouping price</th>
|
||||
<th translate expand>Packing price</th>
|
||||
<th translate>Import</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ng-repeat="buy in $ctrl.buys">
|
||||
<tr>
|
||||
<td shrink>
|
||||
<vn-check tabindex="-1" ng-model="buy.checked">
|
||||
</vn-check>
|
||||
</td>
|
||||
<td shrink>
|
||||
<span
|
||||
ng-if="buy.id"
|
||||
ng-click="itemDescriptor.show($event, buy.item.id)"
|
||||
class="link">
|
||||
{{::buy.item.id}}
|
||||
</span>
|
||||
<vn-autocomplete ng-if="!buy.id" class="dense"
|
||||
vn-focus
|
||||
url="Items/withName"
|
||||
ng-model="buy.itemFk"
|
||||
show-field="id"
|
||||
value-field="id"
|
||||
search-function="$ctrl.itemSearchFunc($search)"
|
||||
on-change="$ctrl.saveBuy(buy)"
|
||||
order="id DESC"
|
||||
tabindex="1">
|
||||
<tpl-item>
|
||||
<div>{{::name}}</div>
|
||||
<div class="text-secondary text-caption">#{{::id}}</div>
|
||||
</tpl-item>
|
||||
</vn-autocomplete>
|
||||
</td>
|
||||
<td>
|
||||
<vn-input-number class="dense"
|
||||
title="{{::buy.quantity | dashIfEmpty}}"
|
||||
ng-model="buy.quantity"
|
||||
on-change="$ctrl.saveBuy(buy)">
|
||||
</vn-input-number>
|
||||
</td>
|
||||
<td center>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
title="{{::buy.packagingFk | dashIfEmpty}}"
|
||||
url="Packagings"
|
||||
show-field="id"
|
||||
value-field="id"
|
||||
where="{freightItemFk: true}"
|
||||
ng-model="buy.packagingFk"
|
||||
on-change="$ctrl.saveBuy(buy)">
|
||||
</vn-autocomplete>
|
||||
</td>
|
||||
<td>
|
||||
<vn-input-number class="dense"
|
||||
title="{{::buy.stickers | dashIfEmpty}}"
|
||||
ng-model="buy.stickers"
|
||||
on-change="$ctrl.saveBuy(buy)">
|
||||
</vn-input-number>
|
||||
</td>
|
||||
<td>
|
||||
<vn-input-number class="dense"
|
||||
title="{{::buy.weight | dashIfEmpty}}"
|
||||
ng-model="buy.weight"
|
||||
on-change="$ctrl.saveBuy(buy)">
|
||||
</vn-input-number>
|
||||
</td>
|
||||
<td>
|
||||
<vn-input-number
|
||||
title="{{::buy.packing | dashIfEmpty}}"
|
||||
ng-model="buy.packing"
|
||||
on-change="$ctrl.saveBuy(buy)">
|
||||
<append>
|
||||
<vn-icon
|
||||
pointer
|
||||
ng-show="buy.groupingMode == 'packing'"
|
||||
ng-click="$ctrl.toggleGroupingMode(buy, 'packing')"
|
||||
icon="lock">
|
||||
</vn-icon>
|
||||
<vn-icon
|
||||
pointer
|
||||
ng-show="buy.groupingMode != 'packing'"
|
||||
ng-click="$ctrl.toggleGroupingMode(buy, 'packing')"
|
||||
icon="lock_open">
|
||||
</vn-icon>
|
||||
</append>
|
||||
</vn-input-number>
|
||||
</td>
|
||||
<td>
|
||||
<vn-input-number
|
||||
title="{{::buy.grouping | dashIfEmpty}}"
|
||||
ng-model="buy.grouping"
|
||||
on-change="$ctrl.saveBuy(buy)">
|
||||
<append>
|
||||
<vn-icon
|
||||
pointer
|
||||
ng-show="buy.groupingMode == 'grouping'"
|
||||
ng-click="$ctrl.toggleGroupingMode(buy, 'grouping')"
|
||||
icon="lock">
|
||||
</vn-icon>
|
||||
<vn-icon
|
||||
pointer
|
||||
ng-show="buy.groupingMode != 'grouping'"
|
||||
ng-click="$ctrl.toggleGroupingMode(buy, 'grouping')"
|
||||
icon="lock_open">
|
||||
</vn-icon>
|
||||
</append>
|
||||
</vn-input-number>
|
||||
</td>
|
||||
<td>
|
||||
<vn-input-number class="dense"
|
||||
title="{{::buy.buyingValue | dashIfEmpty}}"
|
||||
ng-model="buy.buyingValue"
|
||||
on-change="$ctrl.saveBuy(buy)">
|
||||
</vn-input-number>
|
||||
</td>
|
||||
<td>
|
||||
<vn-input-number class="dense"
|
||||
title="{{::buy.price2 | dashIfEmpty}}"
|
||||
ng-model="buy.price2"
|
||||
on-change="$ctrl.saveBuy(buy)">
|
||||
</vn-input-number>
|
||||
</td>
|
||||
<td>
|
||||
<vn-input-number class="dense"
|
||||
title="{{::buy.price3 | dashIfEmpty}}"
|
||||
ng-model="buy.price3"
|
||||
on-change="$ctrl.saveBuy(buy)">
|
||||
</vn-input-number>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
ng-if="buy.quantity != null && buy.buyingValue != null"
|
||||
title="{{buy.quantity * buy.buyingValue | currency: 'EUR':2}}">
|
||||
{{buy.quantity * buy.buyingValue | currency: 'EUR':2}}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="dark-row">
|
||||
<td shrink>
|
||||
</td>
|
||||
<td shrink>
|
||||
<span translate-attr="{title: 'Item type'}">
|
||||
{{::buy.item.itemType.code}}
|
||||
</span>
|
||||
</td>
|
||||
<td number shrink>
|
||||
<span translate-attr="{title: 'Item size'}">
|
||||
{{::buy.item.size}}
|
||||
</span>
|
||||
</td>
|
||||
<td center>
|
||||
<span translate-attr="{title: 'Minimum price'}">
|
||||
{{::buy.item.minPrice | currency: 'EUR':2}}
|
||||
</span>
|
||||
</td>
|
||||
<td vn-fetched-tags colspan="8">
|
||||
<div>
|
||||
<vn-one title="{{::buy.item.concept}}">{{::buy.item.name}}</vn-one>
|
||||
<vn-one ng-if="::buy.item.subName">
|
||||
<h3 title="{{::buy.item.subName}}">{{::buy.item.subName}}</h3>
|
||||
</vn-one>
|
||||
</div>
|
||||
<vn-fetched-tags
|
||||
max-length="6"
|
||||
item="::buy.item"
|
||||
tabindex="-1">
|
||||
</vn-fetched-tags>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</vn-card>
|
||||
</vn-data-viewer>
|
||||
<div fixed-bottom-right>
|
||||
<vn-vertical style="align-items: center;">
|
||||
<a ui-sref="entry.card.buy.import" >
|
||||
<vn-button class="round md vn-mb-sm"
|
||||
icon="publish"
|
||||
vn-tooltip="Import buys"
|
||||
tooltip-position="left">
|
||||
</vn-button>
|
||||
</a>
|
||||
</vn-vertical>
|
||||
</div>
|
||||
<vn-item-descriptor-popover
|
||||
vn-id="item-descriptor"
|
||||
warehouse-fk="$ctrl.vnConfig.warehouseFk">
|
||||
</vn-item-descriptor-popover>
|
||||
<vn-confirm
|
||||
vn-id="delete-buys"
|
||||
question="You are going to delete buy(s) from this entry"
|
||||
message="Continue anyway?"
|
||||
on-accept="$ctrl.deleteBuys()">
|
||||
</vn-confirm>
|
|
@ -1,81 +0,0 @@
|
|||
import ngModule from '../../module';
|
||||
import './style.scss';
|
||||
import Section from 'salix/components/section';
|
||||
|
||||
export default class Controller extends Section {
|
||||
saveBuy(buy) {
|
||||
const missingData = !buy.itemFk || !buy.quantity || !buy.packagingFk;
|
||||
if (missingData) return;
|
||||
|
||||
let options;
|
||||
if (buy.id) {
|
||||
options = {
|
||||
query: `Buys/${buy.id}`,
|
||||
method: 'patch'
|
||||
};
|
||||
}
|
||||
this.$http[options.method](options.query, buy).then(res => {
|
||||
if (!res.data) return;
|
||||
|
||||
buy = Object.assign(buy, res.data);
|
||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns checked instances
|
||||
*
|
||||
* @return {Array} Checked instances
|
||||
*/
|
||||
selectedBuys() {
|
||||
if (!this.buys) return;
|
||||
|
||||
return this.buys.filter(buy => {
|
||||
return buy.checked;
|
||||
});
|
||||
}
|
||||
|
||||
deleteBuys() {
|
||||
const buys = this.selectedBuys();
|
||||
const actualInstances = buys.filter(buy => buy.id);
|
||||
|
||||
const params = {buys: actualInstances};
|
||||
|
||||
if (actualInstances.length) {
|
||||
this.$http.post(`Buys/deleteBuys`, params).then(() => {
|
||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||
});
|
||||
}
|
||||
buys.forEach(buy => {
|
||||
const index = this.buys.indexOf(buy);
|
||||
this.buys.splice(index, 1);
|
||||
});
|
||||
}
|
||||
|
||||
toggleGroupingMode(buy, mode) {
|
||||
const groupingMode = mode === 'grouping' ? mode : 'packing';
|
||||
const newGroupingMode = buy.groupingMode === groupingMode ? null : groupingMode;
|
||||
const params = {
|
||||
groupingMode: newGroupingMode
|
||||
};
|
||||
|
||||
this.$http.patch(`Buys/${buy.id}`, params).then(() => {
|
||||
buy.groupingMode = newGroupingMode;
|
||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||
});
|
||||
}
|
||||
|
||||
itemSearchFunc($search) {
|
||||
return /^\d+$/.test($search)
|
||||
? {id: $search}
|
||||
: {name: {like: '%' + $search + '%'}};
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnEntryBuyIndex', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
entry: '<'
|
||||
}
|
||||
});
|
|
@ -1,92 +0,0 @@
|
|||
/* eslint max-len: ["error", { "code": 150 }]*/
|
||||
import './index.js';
|
||||
|
||||
describe('Entry buy', () => {
|
||||
let controller;
|
||||
let $httpBackend;
|
||||
|
||||
beforeEach(ngModule('entry'));
|
||||
|
||||
beforeEach(angular.mock.inject(($componentController, $compile, $rootScope, _$httpParamSerializer_, _$httpBackend_) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
let $element = $compile('<vn-entry-buy-index></vn-entry-buy-index')($rootScope);
|
||||
controller = $componentController('vnEntryBuyIndex', {$element});
|
||||
const params = _$httpParamSerializer_({filter: {limit: 20}});
|
||||
$httpBackend.whenGET(`Entries//getBuys?${params}`).respond([{id: 1}]);
|
||||
}));
|
||||
|
||||
describe('saveBuy()', () => {
|
||||
it(`should call the buys patch route if the received buy has an ID`, () => {
|
||||
const buy = {id: 1, itemFk: 1, quantity: 1, packagingFk: 1};
|
||||
|
||||
const query = `Buys/${buy.id}`;
|
||||
|
||||
$httpBackend.expectPATCH(query).respond(200);
|
||||
controller.saveBuy(buy);
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteBuys()', () => {
|
||||
it(`should perform no queries if all buys to delete were not actual instances`, () => {
|
||||
controller.buys = [
|
||||
{checked: true},
|
||||
{checked: true},
|
||||
{checked: false}];
|
||||
|
||||
controller.deleteBuys();
|
||||
|
||||
expect(controller.buys.length).toEqual(1);
|
||||
});
|
||||
|
||||
it(`should perform a query to delete as there's an actual instance at least`, () => {
|
||||
controller.buys = [
|
||||
{checked: true, id: 1},
|
||||
{checked: true},
|
||||
{checked: false}];
|
||||
|
||||
const query = 'Buys/deleteBuys';
|
||||
|
||||
$httpBackend.expectPOST(query).respond(200);
|
||||
controller.deleteBuys();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.buys.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggleGroupingMode()', () => {
|
||||
it(`should toggle grouping mode from grouping to packing`, () => {
|
||||
const buy = {id: 999, groupingMode: 'grouping'};
|
||||
|
||||
const query = `Buys/${buy.id}`;
|
||||
$httpBackend.expectPATCH(query, {groupingMode: 'packing'}).respond(200);
|
||||
controller.toggleGroupingMode(buy, 'packing');
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
it(`should toggle grouping mode from packing to grouping`, () => {
|
||||
const buy = {id: 999, groupingMode: 'packing'};
|
||||
const query = `Buys/${buy.id}`;
|
||||
$httpBackend.expectPATCH(query, {groupingMode: 'grouping'}).respond(200);
|
||||
controller.toggleGroupingMode(buy, 'grouping');
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
it(`should toggle off the grouping mode if it was packing to packing`, () => {
|
||||
const buy = {id: 999, groupingMode: 'packing'};
|
||||
const query = `Buys/${buy.id}`;
|
||||
$httpBackend.expectPATCH(query, {groupingMode: null}).respond(200);
|
||||
controller.toggleGroupingMode(buy, 'packing');
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
it(`should toggle off the grouping mode if it was grouping to grouping`, () => {
|
||||
const buy = {id: 999, groupingMode: 'grouping'};
|
||||
const query = `Buys/${buy.id}`;
|
||||
$httpBackend.expectPATCH(query, {groupingMode: null}).respond(200);
|
||||
controller.toggleGroupingMode(buy, 'grouping');
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
Buys: Compras
|
||||
Delete buy(s): Eliminar compra(s)
|
||||
Add buy: Añadir compra
|
|
@ -1,42 +0,0 @@
|
|||
@import "variables";
|
||||
|
||||
|
||||
vn-entry-buy-index vn-card {
|
||||
max-width: $width-xl;
|
||||
|
||||
.dark-row {
|
||||
background-color: lighten($color-marginal, 10%);
|
||||
}
|
||||
|
||||
thead tr {
|
||||
border: 1px solid white;;
|
||||
}
|
||||
|
||||
tbody tr:nth-child(1),
|
||||
tbody tr:nth-child(2) {
|
||||
border-left: 1px solid $color-spacer;
|
||||
border-right: 1px solid $color-spacer;
|
||||
}
|
||||
|
||||
tbody tr:nth-child(2) {
|
||||
border-bottom: 1px solid $color-spacer;
|
||||
}
|
||||
|
||||
tbody{
|
||||
border-bottom: 1px solid $color-spacer;
|
||||
}
|
||||
|
||||
tbody:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
tbody tr:nth-child(3) {
|
||||
height: inherit
|
||||
}
|
||||
|
||||
tr {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
$color-font-link-medium: lighten($color-font-link, 20%)
|
|
@ -1,8 +0,0 @@
|
|||
reference: Referencia
|
||||
Observation: Observación
|
||||
Box: Embalaje
|
||||
Import buys: Importar compras
|
||||
Some of the imported buys doesn't have an item: Algunas de las compras importadas no tienen un artículo
|
||||
JSON files only: Solo ficheros JSON
|
||||
Filter item: Filtrar artículo
|
||||
Filter...: Filtrar...
|
|
@ -1,5 +0,0 @@
|
|||
<vn-portal slot="menu">
|
||||
<vn-entry-descriptor entry="$ctrl.entry"></vn-entry-descriptor>
|
||||
<vn-left-menu source="card"></vn-left-menu>
|
||||
</vn-portal>
|
||||
<ui-view></ui-view>
|
|
@ -1,59 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import ModuleCard from 'salix/components/module-card';
|
||||
|
||||
class Controller extends ModuleCard {
|
||||
reload() {
|
||||
let filter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'company',
|
||||
scope: {
|
||||
fields: ['id', 'code']
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'travel',
|
||||
scope: {
|
||||
fields: ['id', 'landed', 'agencyModeFk', 'warehouseOutFk'],
|
||||
include: [
|
||||
{
|
||||
relation: 'agency',
|
||||
scope: {
|
||||
fields: ['name']
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'warehouseOut',
|
||||
scope: {
|
||||
fields: ['name']
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'warehouseIn',
|
||||
scope: {
|
||||
fields: ['name']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'supplier',
|
||||
scope: {
|
||||
fields: ['id', 'nickname']
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'currency'
|
||||
}
|
||||
]
|
||||
};
|
||||
this.$http.get(`Entries/${this.$params.id}`, {filter})
|
||||
.then(response => this.entry = response.data);
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnEntryCard', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -1,69 +0,0 @@
|
|||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
url="Entries"
|
||||
data="$ctrl.entry"
|
||||
insert-mode="true"
|
||||
form="form">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-icon color-marginal
|
||||
icon="info"
|
||||
vn-tooltip="Required fields (*)">
|
||||
</vn-icon>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete
|
||||
label="Supplier"
|
||||
ng-model="$ctrl.entry.supplierFk"
|
||||
url="Suppliers"
|
||||
show-field="nickname"
|
||||
search-function="{or: [{id: $search}, {nickname: {like: '%'+ $search +'%'}}]}"
|
||||
order="nickname"
|
||||
required="true">
|
||||
<tpl-item>
|
||||
<div>{{::nickname}}</div>
|
||||
<div class="text-secondary text-caption">#{{::id}}</div>
|
||||
</tpl-item>
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete
|
||||
label="Travel"
|
||||
ng-model="$ctrl.entry.travelFk"
|
||||
url="Travels/filter"
|
||||
search-function="$ctrl.searchFunction($search)"
|
||||
show-field="warehouseInName"
|
||||
order="id"
|
||||
required="true">
|
||||
<tpl-item>
|
||||
<div>
|
||||
{{::agencyModeName}}
|
||||
- {{::warehouseInName}} ({{::shipped | date: 'dd/MM/yyyy'}})
|
||||
→ {{::warehouseOutName}} ({{::landed | date: 'dd/MM/yyyy'}})
|
||||
</div>
|
||||
<div class="text-secondary text-caption">#{{::id}}</div>
|
||||
</tpl-item>
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete
|
||||
label="Company"
|
||||
ng-model="$ctrl.entry.companyFk"
|
||||
url="Companies"
|
||||
show-field="code"
|
||||
required="true">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit
|
||||
disabled="!watcher.dataChanged()"
|
||||
label="Create">
|
||||
</vn-submit>
|
||||
<vn-button
|
||||
class="cancel"
|
||||
label="Cancel"
|
||||
ui-sref="entry.index"></vn-button>
|
||||
</vn-button>
|
||||
</vn-button-bar>
|
||||
</form>
|
|
@ -1,43 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
import './style.scss';
|
||||
|
||||
export default class Controller extends Section {
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
|
||||
this.entry = {
|
||||
companyFk: this.vnConfig.companyFk
|
||||
};
|
||||
|
||||
if (this.$params && this.$params.supplierFk)
|
||||
this.entry.supplierFk = parseInt(this.$params.supplierFk);
|
||||
if (this.$params && this.$params.travelFk)
|
||||
this.entry.travelFk = parseInt(this.$params.travelFk);
|
||||
if (this.$params && this.$params.companyFk)
|
||||
this.entry.companyFk = parseInt(this.$params.companyFk);
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.$.watcher.submit().then(
|
||||
res => this.$state.go('entry.card.basicData', {id: res.data.id})
|
||||
);
|
||||
}
|
||||
|
||||
searchFunction($search) {
|
||||
return {or: [
|
||||
{'agencyModeName': {like: `%${$search}%`}},
|
||||
{'warehouseInName': {like: `%${$search}%`}},
|
||||
{'warehouseOutName': {like: `%${$search}%`}},
|
||||
{'shipped': new Date($search)},
|
||||
{'landed': new Date($search)}
|
||||
]};
|
||||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$element', '$scope'];
|
||||
|
||||
ngModule.vnComponent('vnEntryCreate', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -1,2 +0,0 @@
|
|||
New entry: Nueva entrada
|
||||
Required fields (*): Campos requeridos (*)
|
|
@ -1,10 +0,0 @@
|
|||
vn-entry-create {
|
||||
vn-card {
|
||||
position: relative
|
||||
}
|
||||
vn-icon[icon="info"] {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
<slot-descriptor>
|
||||
<vn-entry-descriptor></vn-entry-descriptor>
|
||||
</slot-descriptor>
|
|
@ -1,9 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import DescriptorPopover from 'salix/components/descriptor-popover';
|
||||
|
||||
class Controller extends DescriptorPopover {}
|
||||
|
||||
ngModule.vnComponent('vnEntryDescriptorPopover', {
|
||||
slotTemplate: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -1,65 +0,0 @@
|
|||
<vn-descriptor-content
|
||||
module="entry"
|
||||
description="$ctrl.entry.supplier.nickname"
|
||||
summary="$ctrl.$.summary">
|
||||
<slot-menu>
|
||||
<vn-item
|
||||
ng-click="$ctrl.showEntryReport()"
|
||||
translate>
|
||||
Show entry report
|
||||
</vn-item>
|
||||
</slot-menu>
|
||||
<slot-body>
|
||||
<div class="attributes">
|
||||
<vn-label-value label="Agency "
|
||||
value="{{$ctrl.entry.travel.agency.name}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value label="Landed"
|
||||
value="{{$ctrl.entry.travel.landed | date: 'dd/MM/yyyy'}}">
|
||||
</vn-label-value>
|
||||
<vn-label-value label="Warehouse Out"
|
||||
value="{{$ctrl.entry.travel.warehouseOut.name}}">
|
||||
</vn-label-value>
|
||||
</div>
|
||||
<div class="icons">
|
||||
<vn-icon
|
||||
vn-tooltip="Is inventory entry"
|
||||
icon="icon-inventory"
|
||||
ng-if="$ctrl.entry.isExcludedFromAvailable">
|
||||
</vn-icon>
|
||||
<vn-icon
|
||||
vn-tooltip="Is virtual entry"
|
||||
icon="icon-net"
|
||||
ng-if="$ctrl.entry.isRaid">
|
||||
</vn-icon>
|
||||
</div>
|
||||
<div class="quicklinks">
|
||||
<div ng-transclude="btnOne">
|
||||
<vn-quick-link
|
||||
tooltip="Supplier card"
|
||||
state="['supplier.card.summary', {id: $ctrl.entry.supplier.id}]"
|
||||
icon="icon-supplier">
|
||||
</vn-quick-link>
|
||||
</div>
|
||||
<div ng-transclude="btnTwo">
|
||||
<vn-quick-link
|
||||
tooltip="All travels with current agency"
|
||||
state="['travel.index', {q: $ctrl.travelFilter}]"
|
||||
icon="local_airport">
|
||||
</vn-quick-link>
|
||||
</div>
|
||||
<div ng-transclude="btnThree">
|
||||
<vn-quick-link
|
||||
tooltip="All entries with current supplier"
|
||||
state="['entry.index', {q: $ctrl.entryFilter}]"
|
||||
icon="icon-entry">
|
||||
</vn-quick-link>
|
||||
</div>
|
||||
<div ng-transclude="btnThree">
|
||||
</div>
|
||||
</div>
|
||||
</slot-body>
|
||||
</vn-descriptor-content>
|
||||
<vn-popup vn-id="summary">
|
||||
<vn-entry-summary entry="$ctrl.entry"></vn-entry-summary>
|
||||
</vn-popup>
|
|
@ -1,99 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Descriptor from 'salix/components/descriptor';
|
||||
|
||||
class Controller extends Descriptor {
|
||||
get entry() {
|
||||
return this.entity;
|
||||
}
|
||||
|
||||
set entry(value) {
|
||||
this.entity = value;
|
||||
}
|
||||
|
||||
get travelFilter() {
|
||||
let travelFilter;
|
||||
const entryTravel = this.entry && this.entry.travel;
|
||||
|
||||
if (entryTravel && entryTravel.agencyModeFk) {
|
||||
travelFilter = this.entry && JSON.stringify({
|
||||
agencyModeFk: entryTravel.agencyModeFk
|
||||
});
|
||||
}
|
||||
return travelFilter;
|
||||
}
|
||||
|
||||
get entryFilter() {
|
||||
let entryTravel = this.entry && this.entry.travel;
|
||||
|
||||
if (!entryTravel || !entryTravel.landed) return null;
|
||||
|
||||
const date = new Date(entryTravel.landed);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
|
||||
const from = new Date(date.getTime());
|
||||
from.setDate(from.getDate() - 10);
|
||||
|
||||
const to = new Date(date.getTime());
|
||||
to.setDate(to.getDate() + 10);
|
||||
|
||||
return JSON.stringify({
|
||||
supplierFk: this.entry.supplierFk,
|
||||
from,
|
||||
to
|
||||
});
|
||||
}
|
||||
|
||||
loadData() {
|
||||
const filter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'travel',
|
||||
scope: {
|
||||
fields: ['id', 'landed', 'agencyModeFk', 'warehouseOutFk'],
|
||||
include: [
|
||||
{
|
||||
relation: 'agency',
|
||||
scope: {
|
||||
fields: ['name']
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'warehouseOut',
|
||||
scope: {
|
||||
fields: ['name']
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'warehouseIn',
|
||||
scope: {
|
||||
fields: ['name']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'supplier',
|
||||
scope: {
|
||||
fields: ['id', 'nickname']
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
return this.getData(`Entries/${this.id}`, {filter})
|
||||
.then(res => this.entity = res.data);
|
||||
}
|
||||
|
||||
showEntryReport() {
|
||||
this.vnReport.show(`Entries/${this.id}/entry-order-pdf`);
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnEntryDescriptor', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
entry: '<'
|
||||
}
|
||||
});
|
|
@ -1,40 +0,0 @@
|
|||
import './index.js';
|
||||
|
||||
describe('Entry Component vnEntryDescriptor', () => {
|
||||
let $httpBackend;
|
||||
let controller;
|
||||
const entry = {id: 2};
|
||||
|
||||
beforeEach(ngModule('entry'));
|
||||
|
||||
beforeEach(inject(($componentController, _$httpBackend_) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
controller = $componentController('vnEntryDescriptor', {$element: null}, {entry});
|
||||
}));
|
||||
|
||||
describe('showEntryReport()', () => {
|
||||
it('should open a new window showing a delivery note PDF document', () => {
|
||||
jest.spyOn(controller.vnReport, 'show');
|
||||
|
||||
window.open = jasmine.createSpy('open');
|
||||
controller.showEntryReport();
|
||||
const expectedPath = `Entries/${entry.id}/entry-order-pdf`;
|
||||
|
||||
expect(controller.vnReport.show).toHaveBeenCalledWith(expectedPath);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadData()', () => {
|
||||
it('should perform ask for the entry', () => {
|
||||
let query = `Entries/${entry.id}`;
|
||||
jest.spyOn(controller, 'getData');
|
||||
|
||||
$httpBackend.expectGET(query).respond();
|
||||
controller.loadData();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.getData).toHaveBeenCalledTimes(1);
|
||||
expect(controller.getData).toHaveBeenCalledWith(query, jasmine.any(Object));
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,7 +0,0 @@
|
|||
Reference: Referencia
|
||||
Supplier card: Ficha del proveedor
|
||||
All travels with current agency: Todos los envios con la agencia actual
|
||||
All entries with current supplier: Todas las entradas con el proveedor actual
|
||||
Show entry report: Ver informe del pedido
|
||||
Is inventory entry: Es una entrada de inventario
|
||||
Is virtual entry: Es una redada
|
|
@ -1,18 +1,3 @@
|
|||
export * from './module';
|
||||
|
||||
import './main';
|
||||
import './index/';
|
||||
import './create';
|
||||
import './basic-data';
|
||||
import './latest-buys';
|
||||
import './search-panel';
|
||||
import './latest-buys-search-panel';
|
||||
import './descriptor';
|
||||
import './descriptor-popover';
|
||||
import './card';
|
||||
import './note';
|
||||
import './summary';
|
||||
import './log';
|
||||
import './buy/index';
|
||||
import './buy/import';
|
||||
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
<vn-auto-search
|
||||
model="model">
|
||||
</vn-auto-search>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
class="vn-mb-xl vn-w-xl">
|
||||
<vn-card>
|
||||
<vn-table model="model">
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-th shrink></vn-th>
|
||||
<vn-th field="id" number>Id</vn-th>
|
||||
<vn-th field="landed" center expand>Landed</vn-th>
|
||||
<vn-th>Reference</vn-th>
|
||||
<vn-th>Invoice number</vn-th>
|
||||
<vn-th field="supplierFk">Supplier</vn-th>
|
||||
<vn-th field="isBooked" center>Booked</vn-th>
|
||||
<vn-th field="isConfirmed" center>Confirmed</vn-th>
|
||||
<vn-th field="isOrdered" center>Ordered</vn-th>
|
||||
<vn-th shrink></vn-th>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<a ng-repeat="entry in model.data"
|
||||
class="clickable vn-tr search-result"
|
||||
ui-sref="entry.card.summary({id: {{::entry.id}}})">
|
||||
<vn-td shrink>
|
||||
<vn-icon
|
||||
ng-show="entry.isExcludedFromAvailable"
|
||||
class="bright"
|
||||
vn-tooltip="Inventory entry"
|
||||
icon="icon-inventory">
|
||||
</vn-icon>
|
||||
<vn-icon
|
||||
ng-show="entry.isRaid"
|
||||
class="bright"
|
||||
vn-tooltip="Virtual entry"
|
||||
icon="icon-net">
|
||||
</vn-icon>
|
||||
</vn-td>
|
||||
<vn-td number>{{::entry.id}}</vn-td>
|
||||
<vn-td center expand>
|
||||
<span
|
||||
class="link"
|
||||
vn-click-stop="travelDescriptor.show($event, entry.travelFk)">
|
||||
{{::entry.landed | date:'dd/MM/yyyy'}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td expand>{{::entry.reference}}</vn-td>
|
||||
<vn-td expand>{{::entry.invoiceNumber}}</vn-td>
|
||||
<vn-td expand>{{::entry.supplierName}}</vn-td>
|
||||
<vn-td center><vn-check ng-model="entry.isBooked" disabled="true"></vn-check></vn-td>
|
||||
<vn-td center><vn-check ng-model="entry.isConfirmed" disabled="true"></vn-check></vn-td>
|
||||
<vn-td center><vn-check ng-model="entry.isOrdered" disabled="true"></vn-check></vn-td>
|
||||
<vn-td shrink>
|
||||
<vn-icon-button
|
||||
vn-click-stop="$ctrl.preview(entry)"
|
||||
vn-tooltip="Preview"
|
||||
icon="preview">
|
||||
</vn-icon-button>
|
||||
</vn-td>
|
||||
</a>
|
||||
</vn-tbody>
|
||||
</vn-table>
|
||||
</vn-card>
|
||||
</vn-data-viewer>
|
||||
<vn-travel-descriptor-popover
|
||||
vn-id="travelDescriptor">
|
||||
</vn-travel-descriptor-popover>
|
||||
<vn-popup vn-id="summary">
|
||||
<vn-entry-summary
|
||||
entry="$ctrl.entrySelected">
|
||||
</vn-entry-summary>
|
||||
</vn-popup>
|
||||
<div fixed-bottom-right>
|
||||
<vn-vertical style="align-items: center;">
|
||||
<a ui-sref="entry.create" vn-bind="+">
|
||||
<vn-button class="round md vn-mb-sm"
|
||||
icon="add"
|
||||
vn-tooltip="New entry"
|
||||
tooltip-position="left">
|
||||
</vn-button>
|
||||
</a>
|
||||
</vn-vertical>
|
||||
</div>
|
|
@ -1,14 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
|
||||
export default class Controller extends Section {
|
||||
preview(entry) {
|
||||
this.entrySelected = entry;
|
||||
this.$.summary.show();
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnEntryIndex', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -1,17 +0,0 @@
|
|||
Inventory entry: Es inventario
|
||||
Virtual entry: Es una redada
|
||||
Supplier: Proveedor
|
||||
Currency: Moneda
|
||||
Company: Empresa
|
||||
Confirmed: Confirmada
|
||||
Ordered: Pedida
|
||||
Is raid: Redada
|
||||
Commission: Comisión
|
||||
Landed: F. entrega
|
||||
Reference: Referencia
|
||||
Created: Creado
|
||||
Booked: Contabilizada
|
||||
Is inventory: Inventario
|
||||
Status: Estado
|
||||
Selection: Selección
|
||||
Invoice number: Núm. factura
|
|
@ -1,242 +0,0 @@
|
|||
<vn-crud-model url="Tags" fields="['id','name', 'isFree']" data="$ctrl.tags" auto-load="true">
|
||||
<vn-crud-model url="ItemCategories" data="$ctrl.categories" auto-load="true"></vn-crud-model>
|
||||
<vn-side-menu side="right">
|
||||
<vn-horizontal class="input">
|
||||
<vn-textfield
|
||||
label="General search"
|
||||
ng-model="$ctrl.filter.search"
|
||||
info="Search items by id, name or barcode"
|
||||
vn-focus
|
||||
ng-keydown="$ctrl.onKeyPress($event)">
|
||||
</vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal class="item-category">
|
||||
<vn-autocomplete
|
||||
vn-id="category"
|
||||
data="$ctrl.categories"
|
||||
ng-model="$ctrl.filter.categoryFk"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Category">
|
||||
</vn-autocomplete>
|
||||
<vn-one ng-repeat="category in $ctrl.categories">
|
||||
<vn-icon
|
||||
ng-class="{'active': $ctrl.filter.categoryFk == category.id}"
|
||||
icon="{{::category.icon}}"
|
||||
vn-tooltip="{{::category.name}}"
|
||||
ng-click="$ctrl.changeCategory(category.id)">
|
||||
</vn-icon>
|
||||
</vn-one>
|
||||
</vn-horizontal>
|
||||
<vn-vertical class="input">
|
||||
<vn-autocomplete
|
||||
vn-id="type"
|
||||
disabled="!$ctrl.filter.categoryFk"
|
||||
url="ItemTypes"
|
||||
label="Type"
|
||||
where="{categoryFk: $ctrl.filter.categoryFk}"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
ng-model="$ctrl.filter.typeFk"
|
||||
fields="['categoryFk']"
|
||||
include="'category'"
|
||||
on-change="$ctrl.addFilters()">
|
||||
<tpl-item>
|
||||
<div>{{name}}</div>
|
||||
<div class="text-caption text-secondary">
|
||||
{{category.name}}
|
||||
</div> </tpl-item
|
||||
>>
|
||||
</vn-autocomplete>
|
||||
</vn-vertical>
|
||||
<vn-horizontal class="input horizontal">
|
||||
<vn-autocomplete
|
||||
vn-id="salesPerson"
|
||||
disabled="false"
|
||||
ng-model="$ctrl.filter.salesPersonFk"
|
||||
url="TicketRequests/getItemTypeWorker"
|
||||
show-field="nickname"
|
||||
search-function="{firstName: $search}"
|
||||
value-field="id"
|
||||
label="Buyer"
|
||||
on-change="$ctrl.addFilters()">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
vn-id="supplier"
|
||||
label="Supplier"
|
||||
ng-model="$ctrl.filter.supplierFk"
|
||||
url="Suppliers"
|
||||
fields="['name','nickname']"
|
||||
search-function="{or: [{nickname: {like: '%'+ $search +'%'}}, {name: {like: '%'+ $search +'%'}}]}"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
on-change="$ctrl.addFilters()">
|
||||
<tpl-item>{{name}}: {{nickname}}</tpl-item>
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-vertical class="input">
|
||||
<vn-date-picker
|
||||
label="From"
|
||||
ng-model="$ctrl.filter.from"
|
||||
on-change="$ctrl.addFilters()">
|
||||
</vn-date-picker>
|
||||
<vn-date-picker
|
||||
label="To"
|
||||
ng-model="$ctrl.filter.to"
|
||||
on-change="$ctrl.addFilters()">
|
||||
</vn-date-picker>
|
||||
</vn-vertical>
|
||||
<vn-horizontal class="checks">
|
||||
<vn-check
|
||||
label="Is active"
|
||||
ng-model="$ctrl.filter.active"
|
||||
triple-state="true"
|
||||
ng-click="$ctrl.addFilters()">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
label="Is visible"
|
||||
ng-model="$ctrl.filter.visible"
|
||||
triple-state="true"
|
||||
ng-click="$ctrl.addFilters()">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
label="Is floramondo"
|
||||
ng-model="$ctrl.filter.floramondo"
|
||||
triple-state="true"
|
||||
ng-click="$ctrl.addFilters()">
|
||||
</vn-check>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal class="tags">
|
||||
<vn-one class="text-subtitle1" translate> Tags </vn-one>
|
||||
<vn-icon-button
|
||||
vn-none
|
||||
vn-tooltip="Add tag"
|
||||
icon="add_circle"
|
||||
ng-click="$ctrl.filter.tags.push({})">
|
||||
</vn-icon-button>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal class="tags horizontal" ng-repeat="itemTag in $ctrl.filter.tags">
|
||||
<vn-autocomplete
|
||||
vn-id="tag"
|
||||
data="$ctrl.tags"
|
||||
ng-model="itemTag.tagFk"
|
||||
show-field="name"
|
||||
label="Tag"
|
||||
on-change="itemTag.value = null">
|
||||
</vn-autocomplete>
|
||||
<vn-textfield
|
||||
ng-show="tag.selection.isFree || tag.selection.isFree == undefined"
|
||||
label="Value"
|
||||
ng-model="itemTag.value"
|
||||
ng-keydown="$ctrl.onKeyPress($event)">
|
||||
</vn-textfield>
|
||||
<vn-autocomplete
|
||||
ng-show="tag.selection.isFree === false"
|
||||
url="{{'Tags/' + itemTag.tagFk + '/filterValue'}}"
|
||||
search-function="{value: $search}"
|
||||
label="Value"
|
||||
ng-model="itemTag.value"
|
||||
show-field="value"
|
||||
value-field="value"
|
||||
on-change="$ctrl.addFilters()">
|
||||
</vn-autocomplete>
|
||||
<vn-icon-button
|
||||
vn-none
|
||||
vn-tooltip="Remove tag"
|
||||
icon="delete"
|
||||
ng-click="$ctrl.removeTag(itemTag)">
|
||||
</vn-icon-button>
|
||||
</vn-horizontal>
|
||||
<div class="chips">
|
||||
<vn-chip
|
||||
ng-if="$ctrl.filter.search"
|
||||
removable="true"
|
||||
vn-tooltip="Item id/name"
|
||||
on-remove="$ctrl.removeItemFilter('search')"
|
||||
class="colored">
|
||||
<span>Id/Name: {{$ctrl.filter.search}}</span>
|
||||
</vn-chip>
|
||||
<vn-chip
|
||||
ng-if="category.selection"
|
||||
removable="true"
|
||||
vn-tooltip="Category"
|
||||
on-remove="$ctrl.removeItemFilter('categoryFk')"
|
||||
class="colored">
|
||||
<span>{{category.selection.name}}</span>
|
||||
</vn-chip>
|
||||
<vn-chip
|
||||
ng-if="type.selection"
|
||||
removable="true"
|
||||
vn-tooltip="Type"
|
||||
on-remove="$ctrl.removeItemFilter('typeFk')"
|
||||
class="colored">
|
||||
<span>{{type.selection.name}}</span>
|
||||
</vn-chip>
|
||||
<vn-chip
|
||||
ng-if="salesPerson.selection"
|
||||
removable="true"
|
||||
vn-tooltip="Sales person"
|
||||
on-remove="$ctrl.removeItemFilter('salesPersonFk')"
|
||||
class="colored">
|
||||
<span>Sales person: {{salesPerson.selection.nickname}}</span>
|
||||
</vn-chip>
|
||||
<vn-chip
|
||||
ng-if="supplier.selection"
|
||||
removable="true"
|
||||
vn-tooltip="Supplier"
|
||||
on-remove="$ctrl.removeItemFilter('supplierFk')"
|
||||
class="colored">
|
||||
<span>Supplier: {{supplier.selection.name}}</span>
|
||||
</vn-chip>
|
||||
<vn-chip
|
||||
ng-if="$ctrl.filter.from"
|
||||
removable="true"
|
||||
vn-tooltip="From date"
|
||||
on-remove="$ctrl.removeItemFilter('from')"
|
||||
class="colored">
|
||||
<span>From: {{$ctrl.filter.from | date:'dd/MM/yyyy'}}</span>
|
||||
</vn-chip>
|
||||
<vn-chip
|
||||
ng-if="$ctrl.filter.to"
|
||||
removable="true"
|
||||
vn-tooltip="To date"
|
||||
on-remove="$ctrl.removeItemFilter('to')"
|
||||
class="colored">
|
||||
<span>To: {{$ctrl.filter.to | date:'dd/MM/yyyy'}}</span>
|
||||
</vn-chip>
|
||||
<vn-chip
|
||||
ng-if="$ctrl.filter.active != null"
|
||||
removable="true"
|
||||
vn-tooltip="Active"
|
||||
on-remove="$ctrl.removeItemFilter('active')"
|
||||
class="colored">
|
||||
<span>Active: {{$ctrl.filter.active ? '✓' : '✗'}}</span>
|
||||
</vn-chip>
|
||||
<vn-chip
|
||||
ng-if="$ctrl.filter.floramondo != null"
|
||||
removable="true"
|
||||
vn-tooltip="Floramondo"
|
||||
on-remove="$ctrl.removeItemFilter('floramondo')"
|
||||
class="colored">
|
||||
<span>Floramondo: {{$ctrl.filter.floramondo ? '✓' : '✗'}}</span>
|
||||
</vn-chip>
|
||||
<vn-chip
|
||||
ng-if="$ctrl.filter.visible != null"
|
||||
removable="true"
|
||||
vn-tooltip="Visible"
|
||||
on-remove="$ctrl.removeItemFilter('visible')"
|
||||
class="colored">
|
||||
<span>Visible: {{$ctrl.filter.visible ? '✓' : '✗'}}</span>
|
||||
</vn-chip>
|
||||
<vn-chip
|
||||
ng-repeat="chipTag in $ctrl.filter.tags"
|
||||
removable="true"
|
||||
vn-tooltip="Tag"
|
||||
on-remove="$ctrl.removeTag(chipTag)"
|
||||
class="colored"
|
||||
ng-if="chipTag.value">
|
||||
<span>{{$ctrl.showTagInfo(chipTag)}}</span>
|
||||
</vn-chip>
|
||||
</vn-chip>
|
||||
</div>
|
||||
</vn-side-menu>
|
|
@ -1,61 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import SearchPanel from 'core/components/searchbar/search-panel';
|
||||
import './style.scss';
|
||||
|
||||
class Controller extends SearchPanel {
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
this.filter = {
|
||||
isActive: true,
|
||||
tags: []
|
||||
};
|
||||
}
|
||||
|
||||
changeCategory(id) {
|
||||
if (this.filter.categoryFk != id) {
|
||||
this.filter.categoryFk = id;
|
||||
this.addFilters();
|
||||
}
|
||||
}
|
||||
|
||||
removeItemFilter(param) {
|
||||
this.filter[param] = null;
|
||||
if (param == 'categoryFk') this.filter['typeFk'] = null;
|
||||
this.addFilters();
|
||||
}
|
||||
|
||||
removeTag(tag) {
|
||||
const index = this.filter.tags.indexOf(tag);
|
||||
if (index > -1) this.filter.tags.splice(index, 1);
|
||||
this.addFilters();
|
||||
}
|
||||
|
||||
onKeyPress($event) {
|
||||
if ($event.key === 'Enter')
|
||||
this.addFilters();
|
||||
}
|
||||
|
||||
addFilters() {
|
||||
for (let i = 0; i < this.filter.tags.length; i++) {
|
||||
if (!this.filter.tags[i].value)
|
||||
this.filter.tags.splice(i, 1);
|
||||
}
|
||||
return this.model.addFilter({}, this.filter);
|
||||
}
|
||||
|
||||
showTagInfo(itemTag) {
|
||||
if (!itemTag.tagFk) return itemTag.value;
|
||||
return `${this.tags.find(tag => tag.id == itemTag.tagFk).name}: ${itemTag.value}`;
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.component('vnLatestBuysSearchPanel', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
model: '<'
|
||||
}
|
||||
});
|
|
@ -1,56 +0,0 @@
|
|||
import './index.js';
|
||||
|
||||
describe('Entry', () => {
|
||||
describe('Component vnLatestBuysSearchPanel', () => {
|
||||
let $element;
|
||||
let controller;
|
||||
|
||||
beforeEach(ngModule('entry'));
|
||||
|
||||
beforeEach(angular.mock.inject($componentController => {
|
||||
$element = angular.element(`<vn-latest-buys-search-panel></vn-latest-buys-search-panel>`);
|
||||
controller = $componentController('vnLatestBuysSearchPanel', {$element});
|
||||
controller.model = {addFilter: () => {}};
|
||||
}));
|
||||
|
||||
describe('removeItemFilter()', () => {
|
||||
it(`should remove param from filter`, () => {
|
||||
controller.filter = {tags: [], categoryFk: 1, typeFk: 1};
|
||||
const expectFilter = {tags: [], categoryFk: null, typeFk: null};
|
||||
|
||||
controller.removeItemFilter('categoryFk');
|
||||
|
||||
expect(controller.filter).toEqual(expectFilter);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeTag()', () => {
|
||||
it(`should remove tag from filter`, () => {
|
||||
const tag = {tagFk: 1, value: 'Value'};
|
||||
controller.filter = {tags: [tag]};
|
||||
const expectFilter = {tags: []};
|
||||
|
||||
controller.removeTag(tag);
|
||||
|
||||
expect(controller.filter).toEqual(expectFilter);
|
||||
});
|
||||
});
|
||||
|
||||
describe('showTagInfo()', () => {
|
||||
it(`should show tag value`, () => {
|
||||
const tag = {value: 'Value'};
|
||||
const result = controller.showTagInfo(tag);
|
||||
|
||||
expect(result).toEqual('Value');
|
||||
});
|
||||
|
||||
it(`should show tag name and value`, () => {
|
||||
const tag = {tagFk: 1, value: 'Value'};
|
||||
controller.tags = [{id: 1, name: 'tagName'}];
|
||||
const result = controller.showTagInfo(tag);
|
||||
|
||||
expect(result).toEqual('tagName: Value');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,70 +0,0 @@
|
|||
@import "variables";
|
||||
|
||||
vn-latest-buys-search-panel vn-side-menu div {
|
||||
& > .input {
|
||||
padding-left: $spacing-md;
|
||||
padding-right: $spacing-md;
|
||||
border-color: $color-spacer;
|
||||
border-bottom: $border-thin;
|
||||
}
|
||||
& > .horizontal {
|
||||
grid-auto-flow: column;
|
||||
grid-column-gap: $spacing-sm;
|
||||
align-items: center;
|
||||
}
|
||||
& > .checks {
|
||||
padding: $spacing-md;
|
||||
flex-wrap: wrap;
|
||||
border-color: $color-spacer;
|
||||
border-bottom: $border-thin;
|
||||
}
|
||||
& > .tags {
|
||||
padding: $spacing-md;
|
||||
padding-bottom: 0%;
|
||||
padding-top: 0%;
|
||||
align-items: center;
|
||||
}
|
||||
& > .chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: $spacing-md;
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
border-color: $color-spacer;
|
||||
border-top: $border-thin;
|
||||
}
|
||||
& > .item-category {
|
||||
padding: $spacing-sm;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
|
||||
vn-autocomplete[vn-id="category"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& > vn-one {
|
||||
padding: $spacing-sm;
|
||||
min-width: 33.33%;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
|
||||
& > vn-icon {
|
||||
padding: $spacing-sm;
|
||||
background-color: $color-font-secondary;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
background-color: $color-main;
|
||||
color: #fff;
|
||||
}
|
||||
& > i:before {
|
||||
font-size: 2.6rem;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,267 +0,0 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Buys/latestBuysFilter"
|
||||
order="itemFk DESC"
|
||||
limit="20"
|
||||
data="$ctrl.buys"
|
||||
on-data-change="$ctrl.reCheck()"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
</vn-portal>
|
||||
<vn-latest-buys-search-panel
|
||||
model="model">
|
||||
</vn-latest-buys-search-panel>
|
||||
<vn-card>
|
||||
<smart-table
|
||||
model="model"
|
||||
view-config-id="latestBuys"
|
||||
options="$ctrl.smartTableOptions"
|
||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||
<slot-table>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th shrink>
|
||||
<vn-multi-check
|
||||
model="model"
|
||||
checked="$ctrl.checkAll"
|
||||
check-field="checked"
|
||||
check-dummy-enabled="true"
|
||||
checked-dummy-count="$ctrl.checkedDummyCount">
|
||||
</vn-multi-check>
|
||||
</th>
|
||||
<th translate>Picture</th>
|
||||
<th field="itemFk">
|
||||
<span translate>Item ID</span>
|
||||
</th>
|
||||
<th field="packing" number>
|
||||
<span translate>Packing</span>
|
||||
</th>
|
||||
<th field="grouping" number>
|
||||
<span translate>Grouping</span>
|
||||
</th>
|
||||
<th field="quantity" number>
|
||||
<span translate>Quantity</span>
|
||||
</th>
|
||||
<th field="description">
|
||||
<span translate>Description</span>
|
||||
</th>
|
||||
<th field="size">
|
||||
<span translate>Size</span>
|
||||
</th>
|
||||
<th field="name">
|
||||
<span translate>Tags</span>
|
||||
</th>
|
||||
<th field="code">
|
||||
<span translate>Type</span>
|
||||
</th>
|
||||
<th field="intrastat">
|
||||
<span translate>Intrastat</span>
|
||||
</th>
|
||||
<th field="origin">
|
||||
<span translate>Origin</span>
|
||||
</th>
|
||||
<th field="weightByPiece">
|
||||
<span translate>Weight/Piece</span>
|
||||
</th>
|
||||
<th field="isActive">
|
||||
<span translate>Active</span>
|
||||
</th>
|
||||
<th field="family">
|
||||
<span translate>Family</span>
|
||||
</th>
|
||||
<th field="entryFk">
|
||||
<span translate>Entry</span>
|
||||
</th>
|
||||
<th field="buyingValue" number>
|
||||
<span translate>Buying value</span>
|
||||
</th>
|
||||
<th field="freightValue" number>
|
||||
<span translate>Freight value</span>
|
||||
</th>
|
||||
<th field="comissionValue" number>
|
||||
<span translate>Commission value</span>
|
||||
</th>
|
||||
<th field="packageValue" number>
|
||||
<span translate>Package value</span>
|
||||
</th>
|
||||
<th field="isIgnored">
|
||||
<span translate>Is ignored</span>
|
||||
</th>
|
||||
<th field="price2" number>
|
||||
<span translate>Grouping</span>
|
||||
</th>
|
||||
<th field="price3" number>
|
||||
<span translate>Packing</span>
|
||||
</th>
|
||||
<th field="minPrice" number>
|
||||
<span translate>Min</span>
|
||||
</th>
|
||||
<th field="ektFk">
|
||||
<span translate>Ekt</span>
|
||||
</th>
|
||||
<th field="weight">
|
||||
<span translate>Weight</span>
|
||||
</th>
|
||||
<th field="packagingFk">
|
||||
<span translate>Package</span>
|
||||
</th>
|
||||
<th field="packingOut">
|
||||
<span translate>Package out</span>
|
||||
</th>
|
||||
<th field="landing">
|
||||
<span translate>Landing</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="buy in $ctrl.buys"
|
||||
vn-anchor="::{
|
||||
state: 'entry.card.buy.index',
|
||||
params: {id: {{::buy.entryFk}}}
|
||||
}">
|
||||
<td>
|
||||
<vn-check
|
||||
ng-model="buy.checked"
|
||||
on-change="$ctrl.saveChecked(buy.id)"
|
||||
vn-click-stop>
|
||||
</vn-check>
|
||||
</td>
|
||||
<td >
|
||||
<img
|
||||
ng-src="{{::$root.imagePath('catalog', '50x50', buy.itemFk)}}"
|
||||
zoom-image="{{::$root.imagePath('catalog', '1600x900', buy.itemFk)}}"
|
||||
vn-click-stop
|
||||
on-error-src/>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
vn-click-stop="itemDescriptor.show($event, buy.itemFk)"
|
||||
class="link">
|
||||
{{::buy.itemFk}}
|
||||
</span>
|
||||
</td>
|
||||
<td number>
|
||||
<vn-chip class="transparent" translate-attr="buy.groupingMode == 'packing' ? {title: 'Minimun amount'} : {title: 'Packing'}" ng-class="{'message': buy.groupingMode == 'packing'}">
|
||||
<span>{{::buy.packing | dashIfEmpty}}</span>
|
||||
</vn-chip>
|
||||
</td>
|
||||
<td number>
|
||||
<vn-chip class="transparent" translate-attr="buy.groupingMode == 'grouping' ? {title: 'Minimun amount'} : {title: 'Grouping'}" ng-class="{'message': buy.groupingMode == 'grouping'}">
|
||||
<span>{{::buy.grouping | dashIfEmpty}}</span>
|
||||
</vn-chip>
|
||||
</td>
|
||||
<td number>{{::buy.quantity}}</td>
|
||||
<td vn-two title="{{::buy.description}}">
|
||||
{{::buy.description | dashIfEmpty}}
|
||||
</td>
|
||||
<td number>{{::buy.size}}</td>
|
||||
<td vn-fetched-tags>
|
||||
<div>
|
||||
<vn-one title="{{::buy.name}}">{{::buy.name}}</vn-one>
|
||||
<vn-one ng-if="::buy.subName">
|
||||
<h3 title="{{::buy.subName}}">{{::buy.subName}}</h3>
|
||||
</vn-one>
|
||||
</div>
|
||||
<vn-fetched-tags
|
||||
max-length="6"
|
||||
item="::buy"
|
||||
tabindex="-1">
|
||||
</vn-fetched-tags>
|
||||
</td>
|
||||
<td title="{{::buy.type}}">
|
||||
{{::buy.code}}
|
||||
</td>
|
||||
<td title="{{::item.intrastat}}">
|
||||
{{::buy.intrastat}}
|
||||
</td>
|
||||
<td>{{::buy.origin}}</td>
|
||||
<td>{{::buy.weightByPiece}}</td>
|
||||
<td>
|
||||
<vn-check
|
||||
disabled="true"
|
||||
ng-model="::buy.isActive">
|
||||
</vn-check>
|
||||
</td>
|
||||
<td>{{::buy.family}}</td>
|
||||
<td>
|
||||
<span
|
||||
vn-click-stop="entryDescriptor.show($event, buy.entryFk)"
|
||||
class="link">
|
||||
{{::buy.entryFk}}
|
||||
</span>
|
||||
</td>
|
||||
<td number>{{::buy.buyingValue | currency: 'EUR':3}}</td>
|
||||
<td number>{{::buy.freightValue | currency: 'EUR':3}}</td>
|
||||
<td number>{{::buy.comissionValue | currency: 'EUR':3}}</td>
|
||||
<td number>{{::buy.packageValue | currency: 'EUR':3}}</td>
|
||||
<td>
|
||||
<vn-check
|
||||
disabled="true"
|
||||
ng-model="::buy.isIgnored">
|
||||
</vn-check>
|
||||
</td>
|
||||
<td number>{{::buy.price2 | currency: 'EUR':3}}</td>
|
||||
<td number>{{::buy.price3 | currency: 'EUR':3}}</td>
|
||||
<td number>{{::buy.minPrice | currency: 'EUR':3}}</td>
|
||||
<td>{{::buy.ektFk | dashIfEmpty}}</td>
|
||||
<td>{{::buy.weight}}</td>
|
||||
<td>{{::buy.packagingFk}}</td>
|
||||
<td>{{::buy.packingOut}}</td>
|
||||
<td>{{::buy.landing | date: 'dd/MM/yyyy'}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</slot-table>
|
||||
</smart-table>
|
||||
</vn-card>
|
||||
<div fixed-bottom-right>
|
||||
<vn-vertical style="align-items: center;">
|
||||
<vn-button class="round sm vn-mb-sm"
|
||||
icon="edit"
|
||||
ng-show="$ctrl.totalChecked > 0"
|
||||
ng-click="edit.show($event)"
|
||||
vn-tooltip="Edit buy(s)"
|
||||
tooltip-position="left">
|
||||
</vn-button>
|
||||
</vn-vertical>
|
||||
</div>
|
||||
<vn-dialog class="edit"
|
||||
vn-id="edit"
|
||||
on-accept="$ctrl.onEditAccept()"
|
||||
on-close="$ctrl.editedColumn = null">
|
||||
<tpl-body>
|
||||
<span translate>Edit</span>
|
||||
<span class="countLines">
|
||||
{{::$ctrl.totalChecked}}
|
||||
</span>
|
||||
<span translate>buy(s)</span>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete
|
||||
vn-two
|
||||
ng-model="$ctrl.editedColumn.field"
|
||||
data="$ctrl.columns"
|
||||
value-field="field"
|
||||
show-field="displayName"
|
||||
label="Field to edit">
|
||||
</vn-autocomplete>
|
||||
<vn-textfield
|
||||
vn-one
|
||||
label="Value"
|
||||
ng-model="$ctrl.editedColumn.newValue">
|
||||
</vn-textfield>
|
||||
</vn-horizontal>
|
||||
</tpl-body>
|
||||
<tpl-buttons>
|
||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||
<button response="accept" translate>Save</button>
|
||||
</tpl-buttons>
|
||||
</vn-dialog>
|
||||
<vn-item-descriptor-popover
|
||||
vn-id="item-descriptor"
|
||||
warehouse-fk="$ctrl.vnConfig.warehouseFk">
|
||||
</vn-item-descriptor-popover>
|
||||
<vn-entry-descriptor-popover
|
||||
vn-id="entry-descriptor">
|
||||
</vn-entry-descriptor-popover>
|
|
@ -1,209 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
import './style.scss';
|
||||
|
||||
export default class Controller extends Section {
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
this.editedColumn;
|
||||
this.checkAll = false;
|
||||
this.checkedBuys = [];
|
||||
|
||||
this.smartTableOptions = {
|
||||
activeButtons: {
|
||||
search: true,
|
||||
shownColumns: true,
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
field: 'code',
|
||||
autocomplete: {
|
||||
url: 'ItemTypes',
|
||||
showField: 'code',
|
||||
valueField: 'code',
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'origin',
|
||||
autocomplete: {
|
||||
url: 'Origins',
|
||||
showField: 'code',
|
||||
valueField: 'code'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'family',
|
||||
autocomplete: {
|
||||
url: 'ItemFamilies',
|
||||
valueField: 'code',
|
||||
showField: 'code'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'intrastat',
|
||||
autocomplete: {
|
||||
url: 'Intrastats',
|
||||
showField: 'description',
|
||||
valueField: 'description'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'packagingFk',
|
||||
autocomplete: {
|
||||
url: 'Packagings',
|
||||
showField: 'id'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'isActive',
|
||||
searchable: false
|
||||
},
|
||||
{
|
||||
field: 'isIgnored',
|
||||
searchable: false
|
||||
},
|
||||
{
|
||||
field: 'landing',
|
||||
searchable: false
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
get columns() {
|
||||
if (this._columns) return this._columns;
|
||||
|
||||
this._columns = [
|
||||
{field: 'packing', displayName: this.$t('Packing')},
|
||||
{field: 'grouping', displayName: this.$t('Grouping')},
|
||||
{field: 'packageValue', displayName: this.$t('Package value')},
|
||||
{field: 'weight', displayName: this.$t('Weight')},
|
||||
{field: 'description', displayName: this.$t('Description')},
|
||||
{field: 'size', displayName: this.$t('Size')},
|
||||
{field: 'weightByPiece', displayName: this.$t('weight/Piece')},
|
||||
{field: 'packingOut', displayName: this.$t('PackingOut')},
|
||||
{field: 'landing', displayName: this.$t('Landing')}
|
||||
];
|
||||
|
||||
return this._columns;
|
||||
}
|
||||
|
||||
get checked() {
|
||||
const buys = this.$.model.data || [];
|
||||
const checkedBuys = [];
|
||||
for (let buy of buys) {
|
||||
if (buy.checked)
|
||||
checkedBuys.push(buy);
|
||||
}
|
||||
|
||||
return checkedBuys;
|
||||
}
|
||||
|
||||
exprBuilder(param, value) {
|
||||
switch (param) {
|
||||
case 'id':
|
||||
case 'size':
|
||||
case 'weightByPiece':
|
||||
case 'isActive':
|
||||
case 'family':
|
||||
case 'minPrice':
|
||||
case 'packingOut':
|
||||
return {[`i.${param}`]: value};
|
||||
case 'name':
|
||||
case 'description':
|
||||
return {[`i.${param}`]: {like: `%${value}%`}};
|
||||
case 'code':
|
||||
return {'it.code': value};
|
||||
case 'intrastat':
|
||||
return {'intr.description': value};
|
||||
case 'origin':
|
||||
return {'ori.code': value};
|
||||
case 'landing':
|
||||
return {[`lb.${param}`]: value};
|
||||
case 'packing':
|
||||
case 'grouping':
|
||||
case 'quantity':
|
||||
case 'entryFk':
|
||||
case 'buyingValue':
|
||||
case 'freightValue':
|
||||
case 'comissionValue':
|
||||
case 'packageValue':
|
||||
case 'isIgnored':
|
||||
case 'price2':
|
||||
case 'price3':
|
||||
case 'ektFk':
|
||||
case 'weight':
|
||||
case 'packagingFk':
|
||||
return {[`b.${param}`]: value};
|
||||
}
|
||||
}
|
||||
|
||||
uncheck() {
|
||||
this.checkAll = false;
|
||||
this.checkedBuys = [];
|
||||
}
|
||||
|
||||
get totalChecked() {
|
||||
if (this.checkedDummyCount)
|
||||
return this.checkedDummyCount;
|
||||
|
||||
return this.checked.length;
|
||||
}
|
||||
|
||||
saveChecked(buyId) {
|
||||
const index = this.checkedBuys.indexOf(buyId);
|
||||
if (index !== -1)
|
||||
return this.checkedBuys.splice(index, 1);
|
||||
return this.checkedBuys.push(buyId);
|
||||
}
|
||||
|
||||
reCheck() {
|
||||
if (!this.$.model.data) return;
|
||||
if (!this.checkedBuys.length) return;
|
||||
|
||||
this.$.model.data.forEach(buy => {
|
||||
if (this.checkedBuys.includes(buy.id))
|
||||
buy.checked = true;
|
||||
});
|
||||
}
|
||||
|
||||
onEditAccept() {
|
||||
const rowsToEdit = [];
|
||||
for (let row of this.checked)
|
||||
rowsToEdit.push({id: row.id, itemFk: row.itemFk});
|
||||
|
||||
const data = {
|
||||
field: this.editedColumn.field,
|
||||
newValue: this.editedColumn.newValue,
|
||||
lines: rowsToEdit
|
||||
};
|
||||
|
||||
if (this.checkedDummyCount && this.checkedDummyCount > 0) {
|
||||
const params = {};
|
||||
if (this.$.model.userParams) {
|
||||
const userParams = this.$.model.userParams;
|
||||
for (let param in userParams) {
|
||||
let newParam = this.exprBuilder(param, userParams[param]);
|
||||
if (!newParam)
|
||||
newParam = {[param]: userParams[param]};
|
||||
Object.assign(params, newParam);
|
||||
}
|
||||
}
|
||||
if (this.$.model.userFilter)
|
||||
Object.assign(params, this.$.model.userFilter.where);
|
||||
|
||||
data.filter = params;
|
||||
}
|
||||
|
||||
return this.$http.post('Buys/editLatestBuys', data)
|
||||
.then(() => {
|
||||
this.uncheck();
|
||||
this.$.model.refresh();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.component('vnEntryLatestBuys', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -1,100 +0,0 @@
|
|||
import './index.js';
|
||||
|
||||
describe('Entry', () => {
|
||||
describe('Component vnEntryLatestBuys', () => {
|
||||
let controller;
|
||||
let $httpBackend;
|
||||
|
||||
beforeEach(ngModule('entry'));
|
||||
|
||||
beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
const $element = angular.element('<vn-entry-latest-buys></vn-entry-latest-buys');
|
||||
controller = $componentController('vnEntryLatestBuys', {$element});
|
||||
controller.$ = {
|
||||
model: {refresh: () => {}},
|
||||
edit: {hide: () => {}}
|
||||
};
|
||||
}));
|
||||
|
||||
describe('get columns', () => {
|
||||
it(`should return a set of columns`, () => {
|
||||
let result = controller.columns;
|
||||
|
||||
let length = result.length;
|
||||
let anyColumn = Object.keys(result[Math.floor(Math.random() * Math.floor(length))]);
|
||||
|
||||
expect(anyColumn).toContain('field', 'displayName');
|
||||
});
|
||||
});
|
||||
|
||||
describe('get checked', () => {
|
||||
it(`should return a set of checked lines`, () => {
|
||||
controller.$.model.data = [
|
||||
{checked: true, id: 1},
|
||||
{checked: true, id: 2},
|
||||
{checked: true, id: 3},
|
||||
{checked: false, id: 4},
|
||||
];
|
||||
|
||||
let result = controller.checked;
|
||||
|
||||
expect(result.length).toEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onEditAccept()', () => {
|
||||
it(`should perform a query to update columns`, () => {
|
||||
controller.editedColumn = {field: 'my field', newValue: 'the new value'};
|
||||
const query = 'Buys/editLatestBuys';
|
||||
|
||||
$httpBackend.expectPOST(query).respond();
|
||||
controller.onEditAccept();
|
||||
$httpBackend.flush();
|
||||
|
||||
const result = controller.checked;
|
||||
|
||||
expect(result.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('reCheck()', () => {
|
||||
it(`should recheck buys`, () => {
|
||||
controller.$.model.data = [
|
||||
{checked: false, id: 1},
|
||||
{checked: false, id: 2},
|
||||
{checked: false, id: 3},
|
||||
{checked: false, id: 4},
|
||||
];
|
||||
controller.checkedBuys = [1, 2];
|
||||
|
||||
controller.reCheck();
|
||||
|
||||
expect(controller.$.model.data[0].checked).toEqual(true);
|
||||
expect(controller.$.model.data[1].checked).toEqual(true);
|
||||
expect(controller.$.model.data[2].checked).toEqual(false);
|
||||
expect(controller.$.model.data[3].checked).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('saveChecked()', () => {
|
||||
it(`should check buy`, () => {
|
||||
const buyCheck = 3;
|
||||
controller.checkedBuys = [1, 2];
|
||||
|
||||
controller.saveChecked(buyCheck);
|
||||
|
||||
expect(controller.checkedBuys[2]).toEqual(buyCheck);
|
||||
});
|
||||
|
||||
it(`should uncheck buy`, () => {
|
||||
const buyUncheck = 3;
|
||||
controller.checkedBuys = [1, 2, 3];
|
||||
|
||||
controller.saveChecked(buyUncheck);
|
||||
|
||||
expect(controller.checkedBuys[2]).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,2 +0,0 @@
|
|||
Minimun amount: Minimun purchase quantity
|
||||
PackageName: Package
|
|
@ -1,19 +0,0 @@
|
|||
Edit buy(s): Editar compra(s)
|
||||
Buying value: Coste
|
||||
Freight value: Porte
|
||||
Commission value: Comisión
|
||||
Package value: Embalaje
|
||||
Is ignored: Ignorado
|
||||
Is visible: Visible
|
||||
Is floramondo: Floramondo
|
||||
Grouping price: Precio grouping
|
||||
Packing price: Precio packing
|
||||
Min price: Precio min
|
||||
Ekt: Ekt
|
||||
Weight: Peso
|
||||
Minimun amount: Cantidad mínima de compra
|
||||
Field to edit: Campo a editar
|
||||
PackageName: Cubo
|
||||
Edit: Editar
|
||||
buy(s): compra(s)
|
||||
Package out: Embalaje envíos
|
|
@ -1,7 +0,0 @@
|
|||
.countLines {
|
||||
flex: 0.15;
|
||||
font-size: 24px;
|
||||
color: orangered;
|
||||
font-weight: bold;
|
||||
max-width: 30px;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
#Ordenar alfabeticamente
|
||||
|
||||
entry: entrada
|
||||
Latest buys: Últimas compras
|
||||
|
||||
# Sections
|
|
@ -1 +0,0 @@
|
|||
<vn-log url="EntryLogs" origin-id="$ctrl.$params.id"></vn-log>
|
|
@ -1,7 +0,0 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
|
||||
ngModule.vnComponent('vnEntryLog', {
|
||||
template: require('./index.html'),
|
||||
controller: Section,
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
Date: Fecha
|
|
@ -1,22 +0,0 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Entries/filter"
|
||||
limit="20"
|
||||
auto-load="true"
|
||||
order="landed DESC, id DESC">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar
|
||||
vn-focus
|
||||
panel="vn-entry-search-panel"
|
||||
info="Search entry by id or a suppliers by name or alias"
|
||||
model="model">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-portal slot="menu">
|
||||
<vn-left-menu></vn-left-menu>
|
||||
</vn-portal>
|
||||
<ui-view></ui-view>
|
||||
|
||||
|
||||
|
|
@ -1,7 +1,15 @@
|
|||
import ngModule from '../module';
|
||||
import ModuleMain from 'salix/components/module-main';
|
||||
|
||||
export default class Entry extends ModuleMain {}
|
||||
export default class Entry extends ModuleMain {
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
}
|
||||
async $onInit() {
|
||||
this.$state.go('home');
|
||||
window.location.href = await this.vnApp.getUrl(`entry/`);
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnEntry', {
|
||||
controller: Entry,
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
entry: entry
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue