7806_devToTest_2332 #2801

Merged
alexm merged 126 commits from 7806_devToTest_2330 into test 2024-07-30 06:14:09 +00:00
120 changed files with 432 additions and 4585 deletions

View File

@ -24,7 +24,7 @@ module.exports = Self => {
try {
const options = {transaction: tx, userId: ctx.req.accessToken.userId};
const files = await Self.rawSql('SELECT name, checksum, keyValue FROM edi.fileConfig', null, options);
const files = await Self.rawSql('SELECT name, checksum, keyValue FROM edi.fileMultiConfig', null, options);
const updatableFiles = [];
for (const file of files) {
@ -54,7 +54,7 @@ module.exports = Self => {
const tables = await Self.rawSql(`
SELECT fileName, toTable, file
FROM edi.tableConfig
FROM edi.tableMultiConfig
WHERE file IN (?)`, [fileNames], options);
for (const table of tables) {
@ -85,7 +85,7 @@ module.exports = Self => {
for (const file of updatableFiles) {
console.log(`Updating file ${file.name} checksum...`);
await Self.rawSql(`
UPDATE edi.fileConfig
UPDATE edi.fileMultiConfig
SET checksum = ?
WHERE name = ?`,
[file.checksum, file.name], options);
@ -228,7 +228,7 @@ module.exports = Self => {
await Self.rawSql(sqlTemplate, [filePath], options);
await Self.rawSql(`
UPDATE edi.tableConfig
UPDATE edi.tableMultiConfig
SET updated = ?
WHERE fileName = ?
`, [Date.vnNew(), baseName], options);

View File

@ -3,7 +3,7 @@
"base": "VnModel",
"options": {
"mysql": {
"table": "salix.defaultViewConfig"
"table": "salix.defaultViewMultiConfig"
}
},
"properties": {

View File

@ -14,6 +14,9 @@
},
"itemFk": {
"type": "number"
},
"isChecked": {
"type": "boolean"
}
}
}

View File

@ -3,7 +3,7 @@
"base": "VnModel",
"options": {
"mysql": {
"table": "userConfig"
"table": "userMultiConfig"
}
},
"properties": {

View File

@ -10,9 +10,6 @@ SET foreign_key_checks = 0;
INSERT INTO util.config (id, environment, mockTime, mockUtcTime, mockEnabled)
VALUES (1, 'local', '2001-01-01 12:00:00', '2001-01-01 11:00:00', TRUE);
INSERT INTO util.binlogQueue (code,logName, `position`)
VALUES ('mylogger', 'bin.000001', 4);
/* #5483
INSERT INTO vn.entryConfig (defaultEntry, mailToNotify, inventorySupplierFk, maxLockTime, defaultSupplierFk)
VALUES(1, NULL, 1, 300, 1);

View File

@ -1516,23 +1516,23 @@ INSERT INTO `vn`.`entry`(`id`, `supplierFk`, `created`, `travelFk`, `isConfirmed
(9, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL +2 DAY), 10, 0, 442, 'IN2009', 'Movement 9', 1, 1, ''),
(10, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL +2 DAY), 10, 0, 442, 'IN2009', 'Movement 9', 1, 1, '');
INSERT INTO `bs`.`waste`(`buyer`, `year`, `week`, `family`, `itemFk`, `itemTypeFk`, `saleTotal`, `saleWaste`, `rate`)
INSERT INTO `bs`.`waste`(`buyerFk`, `year`, `week`, `itemFk`, `itemTypeFk`, `saleTotal`, `saleQuantity`, `saleInternalWaste`, `saleExternalWaste`)
VALUES
('CharlesXavier', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Carnation', 1, 1, '1062', '51', '4.8'),
('CharlesXavier', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Carnation Colombia', 2, 1, '35074', '687', '2.0'),
('CharlesXavier', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Carnation Mini', 3, 1, '1777', '13', '0.7'),
('CharlesXavier', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Carnation Short', 4, 1, '3182', '59', '0.6'),
('CharlesXavier', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Crisantemo', 5, 1, '1747', '13', '0.7'),
('CharlesXavier', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Lilium Oriental', 6, 1, '7182', '59', '0.6'),
('CharlesXavier', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Alstroemeria', 7, 1, '1777', '13', '0.7'),
('CharlesXavier', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Cymbidium', 1, 1, '4181', '59', '0.6'),
('CharlesXavier', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Cymbidium', 2, 1, '7268', '59', '0.6'),
('DavidCharlesHaller', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Containers', 2, 1, '-74', '0', '0.0'),
('DavidCharlesHaller', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Packagings', 3, 1, '-7', '0', '0.0'),
('DavidCharlesHaller', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Freight', 4, 1, '1100', '0', '0.0'),
('HankPym', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Funeral Accessories', 5, 1, '848', '-187', '-22.1'),
('HankPym', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Miscellaneous Accessories', 6, 1, '186', '0', '0.0'),
('HankPym', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Adhesives', 7, 1, '277', '0', '0.0');
('35', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 1, 1, '1062', '51', '56.20', '56.20'),
('35', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 2, 1, '35074', '687', '53.12', '89.69'),
('35', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 3, 1, '1777', '13', '12.02', '53.12'),
('35', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 4, 1, '3182', '59', '51', '56.20'),
('35', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 5, 1, '1747', '13', '53.12', '53.12'),
('35', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 6, 1, '7182', '59', '51', '53.12'),
('35', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 7, 1, '1777', '13', '89.69', '89.69'),
('35', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 8, 1, '4181', '59', '53.12', '53.12'),
('35', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 9, 1, '7268', '59', '12.02', '56.20'),
('103', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 2, 1, '-74', '0', '51', '89.69'),
('103', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 3, 1, '-7', '0', '12.02', '53.12'),
('103', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 4, 1, '1100', '0', '51', '56.20'),
('103', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 5, 1, '848', '-187', '12.02', '89.69'),
('103', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 6, 1, '186', '0', '51', '53.12'),
('103', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 7, 1, '277', '0', '53.12', '56.20');
INSERT INTO `vn`.`buy`(`id`,`entryFk`,`itemFk`,`buyingValue`,`quantity`,`packagingFk`,`stickers`,`freightValue`,`packageValue`,`comissionValue`,`packing`,`grouping`,`groupingMode`,`location`,`price1`,`price2`,`price3`, `printedStickers`,`isChecked`,`isIgnored`,`weight`, `created`)
VALUES
@ -1968,7 +1968,7 @@ INSERT INTO `vn`.`orderTicket`(`orderFk`, `ticketFk`)
(21, 21),
(22, 22);
INSERT INTO `vn`.`userConfig` (`userFk`, `warehouseFk`, `companyFk`)
INSERT INTO `vn`.`userMultiConfig` (`userFk`, `warehouseFk`, `companyFk`)
VALUES
(1, 1, 69),
(5, 1, 442),

View File

@ -1,31 +1,50 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `bs`.`waste_addSales`()
BEGIN
DECLARE vWeek INT;
DECLARE vYear INT;
DECLARE vDateFrom DATE DEFAULT util.VN_CURDATE() - INTERVAL WEEKDAY(CURDATE()) DAY;
DECLARE vDateTo DATE DEFAULT vDateFrom + INTERVAL 6 DAY;
SELECT week, year
INTO vWeek, vYear
FROM vn.time
WHERE dated = util.VN_CURDATE();
CALL cache.last_buy_refresh(FALSE);
REPLACE bs.waste
SELECT *, 100 * mermas / total as porcentaje
FROM (
SELECT buyer,
year,
week,
family,
itemFk,
itemTypeFk,
floor(sum(value)) as total,
floor(sum(IF(typeFk = 'loses', value, 0))) as mermas
FROM vn.saleValue
where year = vYear and week = vWeek
GROUP BY family, itemFk
) sub
ORDER BY mermas DESC;
SELECT YEAR(t.shipped),
WEEK(t.shipped, 4),
it.workerFk,
it.id,
s.itemFk,
SUM(s.quantity),
SUM((b.buyingValue + b.freightValue + b.comissionValue + b.packageValue) * s.quantity) `value`,
SUM (
IF(
aw.`type` = 'internal',
(b.buyingValue + b.freightValue + b.comissionValue + b.packageValue) * s.quantity,
0
)
) internalWaste,
SUM (
IF(
aw.`type` = 'external',
(b.buyingValue + b.freightValue + b.comissionValue + b.packageValue) * s.quantity,
IF(c.code = 'manaClaim',
sc.value * s.quantity,
0
)
)
) externalWaste
FROM vn.sale s
JOIN vn.item i ON i.id = s.itemFk
JOIN vn.itemType it ON it.id = i.typeFk
JOIN vn.ticket t ON t.id = s.ticketFk
JOIN vn.address a FORCE INDEX (PRIMARY) ON a.id = t.addressFk
LEFT JOIN vn.addressWaste aw ON aw.addressFk = a.id
JOIN vn.warehouse w ON w.id = t.warehouseFk
JOIN cache.last_buy lb ON lb.item_id = i.id
AND lb.warehouse_id = w.id
JOIN vn.buy b ON b.id = lb.buy_id
LEFT JOIN vn.saleComponent sc ON sc.saleFk = s.id
LEFT JOIN vn.component c ON c.id = sc.componentFk
WHERE t.shipped BETWEEN vDateFrom AND vDateTo
AND w.isManaged
GROUP BY it.id, i.id;
END$$
DELIMITER ;

View File

@ -10,5 +10,5 @@ AS SELECT `q`.`name` AS `name`,
`c`.`ringInUse` AS `ringinuse`
FROM (
`pbx`.`queue` `q`
JOIN `pbx`.`queueConfig` `c` ON(`q`.`config` = `c`.`id`)
JOIN `pbx`.`queueMultiConfig` `c` ON(`q`.`config` = `c`.`id`)
)

View File

@ -7,7 +7,7 @@ DECLARE vCompanyFk INT;
SELECT IFNULL(uc.companyFk, rc.defaultCompanyFk)
INTO vCompanyFk
FROM vn.routeConfig rc
LEFT JOIN userConfig uc ON uc.userFk = workerFk;
LEFT JOIN userMultiConfig uc ON uc.userFk = workerFk;
SELECT

View File

@ -12,6 +12,7 @@ BEGIN
DECLARE vInvoiceFk INT;
DECLARE vBookEntry INT;
DECLARE vFiscalYear INT;
DECLARE vIncorrectInvoiceInDueDay INT;
DECLARE vInvoicesIn CURSOR FOR
SELECT DISTINCT e.invoiceInFk
@ -24,6 +25,19 @@ BEGIN
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
SELECT GROUP_CONCAT(ii.id) INTO vIncorrectInvoiceInDueDay
FROM invoiceInDueDay iidd
JOIN invoiceIn ii ON iidd.invoiceInFk = ii.id
JOIN `entry` e ON e.invoiceInFk = ii.id
JOIN duaEntry de ON de.entryFk = e.id
JOIN invoiceInConfig iic
WHERE de.duaFk = vDuaFk
AND iidd.dueDated <= util.VN_CURDATE() + INTERVAL iic.dueDateMarginDays DAY;
IF vIncorrectInvoiceInDueDay THEN
CALL util.throw(CONCAT('Incorrect due date, invoice: ', vIncorrectInvoiceInDueDay));
END IF;
UPDATE invoiceIn ii
JOIN entry e ON e.invoiceInFk = ii.id
JOIN duaEntry de ON de.entryFk = e.id

View File

@ -1,5 +1,7 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`invoiceInDueDay_calculate`(vInvoiceInFk INT)
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`invoiceInDueDay_calculate`(
vInvoiceInFk INT
)
BEGIN
/**
* Calcula los vctos. de una factura recibida
@ -56,12 +58,13 @@ BEGIN
COUNT(DISTINCT(pdd.detail)) cont,
s.payDay,
ii.issued,
DATE(ii.created) + INTERVAL 2 DAY created
DATE(ii.created) + INTERVAL iic.dueDateMarginDays DAY created
FROM invoiceIn ii
JOIN invoiceInTax iit ON iit.invoiceInFk = ii.id
LEFT JOIN sage.TiposIva ti ON ti.CodigoIva= iit.taxTypeSageFk
JOIN supplier s ON s.id = ii.supplierFk
JOIN payDemDetail pdd ON pdd.id = s.payDemFk
JOIN invoiceInConfig iic
WHERE ii.id = vInvoiceInFk
GROUP BY ii.id
)sub

View File

@ -216,7 +216,7 @@ BEGIN
i.transactionTypeSageFk,
@vTaxCodeGeneral := i.taxClassCodeFk
FROM tmp.ticketServiceTax tst
JOIN invoiceOutTaxConfig i ON i.taxClassCodeFk = tst.code
JOIN invoiceOutTaxMultiConfig i ON i.taxClassCodeFk = tst.code
WHERE i.isService
HAVING taxableBase
) sub;
@ -229,7 +229,7 @@ BEGIN
i.taxTypeSageFk ,
i.transactionTypeSageFk
FROM tmp.ticketTax tt
JOIN invoiceOutTaxConfig i ON i.taxClassCodeFk = tt.code
JOIN invoiceOutTaxMultiConfig i ON i.taxClassCodeFk = tt.code
WHERE !i.isService
GROUP BY tt.pgcFk
HAVING taxableBase

View File

@ -10,19 +10,20 @@ CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`item_comparative`(
)
proc: BEGIN
/**
* Genera una tabla de comparativa de artículos por itemType/comprador/fecha.
* Los datos se calculan en función de los parámetros proporcionados.
* Generates a comparison table of items by itemType/buyer/date.
* The data is calculated based on the provided parameters.
*
* @param vDate La fecha para la cual se generará la comparativa.
* @param vDayRange El rango de días a considerar para la comparativa.
* @param vWarehouseFk El identificador del almacén para filtrar los artículos.
* @param vAvailableSince La fecha de disponibilidad desde la cual se consideran los artículos disponibles.
* @param vBuyerFk El identificador del comprador para filtrar los artículos.
* @param vIsFloramondo Indica si se deben incluir solo los artículos de Floramondo (opcional).
* @param vCountryFk El identificador del país.
* @param vDate The date for which the comparison will be generated.
* @param vDayRange The range of days to consider for the comparison.
* @param vWarehouseFk The warehouse identifier to filter the items.
* @param vAvailableSince The availability date from which the items are considered available.
* @param vBuyerFk The buyer identifier to filter the items.
* @param vIsFloramondo Indicates whether only Floramondo items should be included (optional).
* @param vCountryFk The country identifier.
* @param tmp.comparativeFilterType(filterFk INT, itemTypeFk INT)
* @return tmp.comparative
*/
DECLARE vDayRangeStart DATE;
DECLARE vDayRangeEnd DATE;
DECLARE w1, w2, w3, w4, w5, w6, w7 INT;

View File

@ -42,7 +42,7 @@ BEGIN
END IF;
SELECT warehouseFk INTO vWarehouseFk
FROM userConfig
FROM userMultiConfig
WHERE userFk = account.myUser_getId();
IF NOT vWarehouseFk OR vWarehouseFk IS NULL THEN

View File

@ -46,12 +46,18 @@ BEGIN
i.tag8 = JSON_VALUE(vTags, '$.8'),
i.tag9 = JSON_VALUE(vTags, '$.9'),
i.tag10 = JSON_VALUE(vTags, '$.10'),
i.tag11 = JSON_VALUE(vTags, '$.11'),
i.tag12 = JSON_VALUE(vTags, '$.12'),
i.tag13 = JSON_VALUE(vTags, '$.13'),
i.value5 = JSON_VALUE(vValues, '$.5'),
i.value6 = JSON_VALUE(vValues, '$.6'),
i.value7 = JSON_VALUE(vValues, '$.7'),
i.value8 = JSON_VALUE(vValues, '$.8'),
i.value9 = JSON_VALUE(vValues, '$.9'),
i.value10 = JSON_VALUE(vValues, '$.10'),
i.value11 = JSON_VALUE(vValues, '$.11'),
i.value12 = JSON_VALUE(vValues, '$.12'),
i.value13 = JSON_VALUE(vValues, '$.13'),
i.producerFk = p.id,
i.inkFk = k.id,
i.originFk = IFNULL(o.id, i.originFk)

View File

@ -1,6 +1,8 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`item_valuateInventory`(
vDated DATE
vDated DATE,
vItemTypeFk INT,
vItemCategoryFk INT
)
BEGIN
DECLARE vInventoried DATE;
@ -61,11 +63,14 @@ BEGIN
JOIN `entry` e ON e.id = b.entryFk
JOIN travel tr ON tr.id = e.travelFk
JOIN itemType t ON t.id = i.typeFk
JOIN itemCategory ic ON ic.id = t.categoryFk
JOIN warehouse w ON w.id = tr.warehouseInFk
WHERE tr.landed = vDateDayEnd
AND e.supplierFk = vInventorySupplierFk
AND w.valuatedInventory
AND t.isInventory
AND (t.id = vItemTypeFk OR vItemTypeFk IS NULL)
AND (ic.id = vItemCategoryFk OR vItemCategoryFk IS NULL)
GROUP BY tr.warehouseInFk, b.itemFk;
ELSE
INSERT INTO tInventory(warehouseFk, itemFk, quantity, warehouseInventory)
@ -78,11 +83,14 @@ BEGIN
JOIN `entry` e ON e.id = b.entryFk
JOIN travel tr ON tr.id = e.travelFk
JOIN itemType t ON t.id = i.typeFk
JOIN itemCategory ic ON ic.id = t.categoryFk
JOIN warehouse w ON w.id = tr.warehouseInFk
WHERE tr.landed = vInventoried
AND e.supplierFk = vInventorySupplierFk
AND w.valuatedInventory
AND t.isInventory
AND (t.id = vItemTypeFk OR vItemTypeFk IS NULL)
AND (ic.id = vItemCategoryFk OR vItemCategoryFk IS NULL)
GROUP BY tr.warehouseInFk, b.itemFk;
END IF;
@ -97,6 +105,7 @@ BEGIN
JOIN `entry` e ON e.id = b.entryFk
JOIN travel tr ON tr.id = e.travelFk
JOIN itemType t ON t.id = i.typeFk
JOIN itemCategory ic ON ic.id = t.categoryFk
JOIN warehouse w ON w.id = tr.warehouseInFk
WHERE tr.landed BETWEEN vInventoried AND vDateDayEnd
AND IF(tr.landed = util.VN_CURDATE(), tr.isReceived, TRUE)
@ -104,6 +113,8 @@ BEGIN
AND w.valuatedInventory
AND t.isInventory
AND e.supplierFk <> vInventorySupplierFk
AND (t.id = vItemTypeFk OR vItemTypeFk IS NULL)
AND (ic.id = vItemCategoryFk OR vItemCategoryFk IS NULL)
ON DUPLICATE KEY UPDATE tInventory.quantity = tInventory.quantity + (b.quantity * IF(vHasNotInventory, -1, 1));
-- Descontamos las salidas
@ -117,11 +128,14 @@ BEGIN
JOIN `entry` e ON e.id = b.entryFk
JOIN travel tr ON tr.id = e.travelFk
JOIN itemType t ON t.id = i.typeFk
JOIN itemCategory ic ON ic.id = t.categoryFk
JOIN warehouse w ON w.id = tr.warehouseOutFk
WHERE tr.shipped BETWEEN vInventoried AND vDateDayEnd
AND NOT e.isRaid
AND w.valuatedInventory
AND t.isInventory
AND (t.id = vItemTypeFk OR vItemTypeFk IS NULL)
AND (ic.id = vItemCategoryFk OR vItemCategoryFk IS NULL)
ON DUPLICATE KEY UPDATE tInventory.quantity = tInventory.quantity + (b.quantity * IF(vHasNotInventory,1,-1));
-- Descontamos las lineas de venta
@ -135,10 +149,13 @@ BEGIN
JOIN `client` c ON c.id = t.clientFk
JOIN item i ON i.id = s.itemFk
JOIN itemType it ON it.id = i.typeFk
JOIN itemCategory ic ON ic.id = it.categoryFk
JOIN warehouse w ON w.id = t.warehouseFk
WHERE t.shipped BETWEEN vInventoried AND vDateDayEnd
AND w.valuatedInventory
AND it.isInventory
AND (it.id = vItemTypeFk OR vItemTypeFk IS NULL)
AND (ic.id = vItemCategoryFk OR vItemCategoryFk IS NULL)
ON DUPLICATE KEY UPDATE tInventory.quantity = tInventory.quantity + s.quantity * IF(vHasNotInventory, 1, -1);
-- Volver a poner lo que esta aun en las estanterias
@ -153,11 +170,14 @@ BEGIN
JOIN `client` c ON c.id = t.clientFk
JOIN item i ON i.id = s.itemFk
JOIN itemType it ON it.id = i.typeFk
JOIN itemCategory ic ON ic.id = it.categoryFk
JOIN warehouse w ON w.id = t.warehouseFk
WHERE t.shipped BETWEEN vDated AND vDateDayEnd
AND NOT (s.isPicked OR t.isLabeled)
AND w.valuatedInventory
AND it.isInventory
AND (it.id = vItemTypeFk OR vItemTypeFk IS NULL)
AND (ic.id = vItemCategoryFk OR vItemCategoryFk IS NULL)
ON DUPLICATE KEY UPDATE tInventory.quantity = tInventory.quantity + s.quantity * IF(vHasNotInventory, 0, 1);
END IF;
@ -172,6 +192,7 @@ BEGIN
JOIN `entry` e ON e.id = b.entryFk
JOIN travel tr ON tr.id = e.travelFk
JOIN itemType t ON t.id = i.typeFk
JOIN itemCategory ic ON ic.id = t.categoryFk
JOIN warehouse wIn ON wIn.id = tr.warehouseInFk
JOIN warehouse wOut ON wOut.id = tr.warehouseOutFk
WHERE vDated >= tr.shipped AND vDated < tr.landed
@ -179,6 +200,8 @@ BEGIN
AND wIn.valuatedInventory
AND t.isInventory
AND e.isConfirmed
AND (t.id = vItemTypeFk OR vItemTypeFk IS NULL)
AND (ic.id = vItemCategoryFk OR vItemCategoryFk IS NULL)
ON DUPLICATE KEY UPDATE tInventory.quantity = tInventory.quantity + (b.quantity);
CALL buyUltimate(NULL, vDateDayEnd);
@ -204,7 +227,8 @@ BEGIN
ic.name itemCategoryName,
ti.cost,
ti.total,
ti.warehouseInventory
ti.warehouseInventory,
ic.display
FROM tInventory ti
JOIN warehouse w ON w.id = warehouseFk
JOIN item i ON i.id = ti.itemFk

View File

@ -139,8 +139,9 @@ proc: BEGIN
CALL item_getAtp(vDate);
CALL travel_upcomingArrivals(vWarehouseFk, vDate);
UPDATE tmp.itemInventory ai
JOIN (
CREATE OR REPLACE TEMPORARY TABLE tItemAvailableCalc
(PRIMARY KEY (itemFk))
ENGINE = MEMORY
SELECT it.itemFk,
SUM(it.quantity) quantity,
im.quantity minQuantity
@ -152,18 +153,21 @@ proc: BEGIN
WHERE it.dated < IF(vMaxDays < 0 AND t.landing IS NOT NULL,
t.landing,
vDateToTomorrow)
GROUP BY it.itemFk
) sub ON sub.itemFk = ai.id
SET ai.avalaible = IF(sub.minQuantity > 0,
ai.avalaible,
ai.avalaible + sub.minQuantity),
ai.sd = ai.inventory + sub.quantity;
GROUP BY it.itemFk;
UPDATE tmp.itemInventory it
JOIN tItemAvailableCalc iac ON iac.itemFk = it.id
SET it.avalaible = IF(iac.minQuantity > 0,
it.avalaible,
it.avalaible + iac.minQuantity),
it.sd = it.inventory + iac.quantity;
DROP TEMPORARY TABLE
tmp.itemTravel,
tmp.itemCalc,
tmp.itemAtp,
tItemInventoryCalc,
tItemVisibleCalc;
tItemVisibleCalc,
tItemAvailableCalc;
END$$
DELIMITER ;

View File

@ -174,8 +174,9 @@ proc: BEGIN
WHERE NOT `lines`;
-- Lineas por linea de encajado
UPDATE tmp.productionBuffer pb
JOIN (
CREATE OR REPLACE TEMPORARY TABLE tItemPackingType
(PRIMARY KEY(ticketFk))
ENGINE = MEMORY
SELECT ticketFk,
SUM(sub.H) H,
SUM(sub.V) V,
@ -190,11 +191,13 @@ proc: BEGIN
JOIN item i ON i.id = s.itemFk
GROUP BY t.ticketFk, i.itemPackingTypeFk
) sub
GROUP BY ticketFk
) sub2 ON sub2.ticketFk = pb.ticketFk
SET pb.H = sub2.H,
pb.V = sub2.V,
pb.N = sub2.N;
GROUP BY ticketFk;
UPDATE tmp.productionBuffer pb
JOIN tItemPackingType ti ON ti.ticketFk = pb.ticketFk
SET pb.H = ti.H,
pb.V = ti.V,
pb.N = ti.N;
-- Colecciones segun tipo de encajado
UPDATE tmp.productionBuffer pb
@ -273,6 +276,7 @@ proc: BEGIN
tmp.risk,
tmp.ticket_problems,
tmp.ticketWithPrevia,
tItemShelvingStock;
tItemShelvingStock,
tItemPackingType;
END$$
DELIMITER ;

View File

@ -1,14 +1,18 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`route_updateM3`(vRoute INT)
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`route_updateM3`(
vSelf INT
)
BEGIN
/**
* Actualiza el volumen de la ruta.
*
* @param vSelf Id ruta
*/
DECLARE vVolume DECIMAL(10,1)
DEFAULT (SELECT SUM(volume) FROM saleVolume WHERE routeFk = vSelf);
UPDATE vn.route r
LEFT JOIN (
SELECT routeFk, SUM(volume) AS m3
FROM saleVolume
WHERE routeFk = vRoute
) v ON v.routeFk = r.id
SET r.m3 = IFNULL(v.m3,0)
WHERE r.id =vRoute;
UPDATE `route`
SET m3 = IFNULL(vVolume, 0)
WHERE id = vSelf;
END$$
DELIMITER ;

View File

@ -8,13 +8,16 @@ BEGIN
SET NEW.editorFk = account.myUser_getId();
IF NOT (NEW.routeFk <=> OLD.routeFk) THEN
INSERT IGNORE INTO `vn`.`routeRecalc` (`routeFk`)
SELECT r.id
FROM vn.route r
WHERE r.isOk = FALSE
AND r.id IN (OLD.routeFk,NEW.routeFk)
AND r.created >= util.VN_CURDATE()
GROUP BY r.id;
IF NEW.isSigned THEN
CALL util.throw('A signed ticket cannot be rerouted');
END IF;
INSERT IGNORE INTO routeRecalc(routeFk)
SELECT id
FROM `route`
WHERE NOT isOk
AND id IN (OLD.routeFk, NEW.routeFk)
AND created >= util.VN_CURDATE()
GROUP BY id;
END IF;
IF NOT (DATE(NEW.shipped) <=> DATE(OLD.shipped)) THEN

View File

@ -0,0 +1,53 @@
ALTER TABLE bs.waste ADD buyerFk int(10) unsigned NOT NULL;
USE vn;
CREATE OR REPLACE TEMPORARY TABLE tBuyers
ENGINE = MEMORY
WITH tDistinctBuyers AS (
SELECT DISTINCT buyer
FROM bs.waste w
)
SELECT buyer, u.id
FROM tDistinctBuyers tdb
JOIN account.`user` u ON u.name = tdb.buyer COLLATE utf8mb3_unicode_ci;
UPDATE bs.waste w
JOIN tBuyers tb ON tb.buyer = w.buyer
SET w.buyerFk = tb.id;
DROP TEMPORARY TABLE tBuyers;
ALTER TABLE bs.waste
DROP PRIMARY KEY,
DROP COLUMN family,
DROP COLUMN rate,
DROP COLUMN buyer;
ALTER TABLE bs.waste CHANGE buyerFk buyerFk int(10) unsigned NOT NULL AFTER `week`;
ALTER TABLE bs.waste ADD CONSTRAINT waste_user_FK FOREIGN KEY (buyerFk) REFERENCES account.user(id) ON DELETE RESTRICT ON UPDATE RESTRICT;
ALTER TABLE bs.waste ADD saleQuantity int(11) DEFAULT NULL NULL;
ALTER TABLE bs.waste MODIFY COLUMN saleTotal decimal(10,2) DEFAULT NULL NULL;
ALTER TABLE bs.waste MODIFY COLUMN saleWaste decimal(10,2) DEFAULT NULL NULL;
ALTER TABLE bs.waste CHANGE saleWaste saleInternalWaste decimal(10,2) DEFAULT NULL NULL;
ALTER TABLE bs.waste ADD saleExternalWaste decimal(10,2) DEFAULT NULL NULL;
ALTER TABLE bs.waste CHANGE saleQuantity saleQuantity decimal(10,2) DEFAULT NULL NULL AFTER itemTypeFk;
ALTER TABLE bs.waste CHANGE itemFk itemFk int(11) DEFAULT 0 NOT NULL AFTER itemTypeFk;
CREATE OR REPLACE TEMPORARY TABLE tWasteUnique
SELECT *
FROM bs.waste
GROUP BY `year`, `week`, buyerFk, itemTypeFk, itemFk
HAVING COUNT(*) > 1;
DELETE w.* FROM bs.waste w
JOIN tWasteUnique twu ON twu.`year` = w.`year`
AND twu.`week` = w.`week`
AND twu.`buyerFk` = w.`buyerFk`
AND twu.`itemTypeFk` = w.`itemTypeFk`
AND twu.`itemFk` = w.`itemFk`;
INSERT INTO bs.waste
SELECT * FROM tWasteUnique;
ALTER TABLE bs.waste ADD CONSTRAINT waste_pk PRIMARY KEY (`year`, `week`, buyerFk, itemTypeFk, itemFk);
DROP TEMPORARY TABLE tWasteUnique;

View File

@ -0,0 +1,11 @@
RENAME TABLE hedera.shelfConfig TO hedera.shelfMultiConfig ;
RENAME TABLE pbx.queueConfig TO pbx.queueMultiConfig ;
RENAME TABLE salix.defaultViewConfig TO salix.defaultViewMultiConfig;
RENAME TABLE edi.fileConfig TO edi.fileMultiConfig;
RENAME TABLE edi.imapConfig TO edi.imapMultiConfig;
RENAME TABLE edi.tableConfig TO edi.tableMultiConfig;
RENAME TABLE vn.invoiceOutTaxConfig TO vn.invoiceOutTaxMultiConfig;
RENAME TABLE vn.userConfig TO vn.userMultiConfig;
RENAME TABLE vn.conveyorConfig TO vn.conveyorMultiConfig;
GRANT SELECT ON TABLE hedera.shelfMultiConfig TO employee;

View File

@ -0,0 +1,2 @@
ALTER TABLE vn.invoiceInConfig
ADD dueDateMarginDays INT UNSIGNED DEFAULT 2 NULL;

View File

@ -0,0 +1,8 @@
ALTER TABLE vn.item ADD IF NOT EXISTS tag11 varchar(20) DEFAULT NULL NULL;
ALTER TABLE vn.item ADD IF NOT EXISTS value11 varchar(50) DEFAULT NULL NULL;
ALTER TABLE vn.item ADD IF NOT EXISTS tag12 varchar(20) DEFAULT NULL NULL;
ALTER TABLE vn.item ADD IF NOT EXISTS value12 varchar(50) DEFAULT NULL NULL;
ALTER TABLE vn.item ADD IF NOT EXISTS tag13 varchar(20) DEFAULT NULL NULL;
ALTER TABLE vn.item ADD IF NOT EXISTS value13 varchar(50) DEFAULT NULL NULL;

View File

@ -0,0 +1,8 @@
ALTER TABLE dipole.expedition_PrintOut ADD isChecked BOOL DEFAULT FALSE NOT NULL COMMENT 'Indica si la expedición ha sido revisada por un revisor';
DELETE FROM salix.ACL
WHERE model = 'Expedition_PrintOut';
INSERT INTO salix.ACL ( model, property, accessType, permission, principalType, principalId)
VALUES( 'Expedition_PrintOut', '*', '*', 'ALLOW', 'ROLE', 'production');

View File

@ -1,62 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Route summary path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'route');
await page.waitToClick(selectors.routeIndex.previewButton);
await page.waitToClick(selectors.routeSummary.goToRouteSummaryButton);
});
afterAll(async() => {
await browser.close();
});
it('should reach the first route summary section', async() => {
await page.waitForState('route.card.summary');
});
it(`should display details from the route on the header`, async() => {
await page.waitForTextInElement(selectors.routeSummary.header, 'first route');
const result = await page.waitToGetProperty(selectors.routeSummary.header, 'innerText');
expect(result).toContain('first route');
});
it('should display some route details like the cost', async() => {
const result = await page.waitToGetProperty(selectors.routeSummary.cost, 'innerText');
expect(result).toContain('€10.00');
});
it('should click on the first ticket ID making the descriptor popover visible', async() => {
await page.waitForState('route.card.summary');
await page.waitForTimeout(250);
await page.waitToClick(selectors.routeSummary.firstTicketID);
await page.waitForSelector(selectors.routeSummary.firstTicketDescriptor);
const visible = await page.isVisible(selectors.routeSummary.firstTicketDescriptor);
expect(visible).toBe(true);
});
it('should close the ticket descriptor', async() => {
await page.closePopup();
});
it('should click on the first alias making the client descriptor popover visible', async() => {
await page.waitToClick(selectors.routeSummary.firstAlias);
await page.waitForSelector(selectors.routeSummary.firstClientDescriptor);
const visible = await page.isVisible(selectors.routeSummary.firstClientDescriptor);
expect(visible).toBe(true);
});
it('should close the client descriptor', async() => {
await page.closePopup();
});
});

View File

@ -1,42 +0,0 @@
import getBrowser from '../../helpers/puppeteer';
describe('Route basic Data path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('delivery', 'route');
await page.accessToSearchResult('1');
await page.accessToSection('route.card.basicData');
});
afterAll(async() => {
await browser.close();
});
it('should edit the route basic data and confirm the route was edited', async() => {
const nextMonth = Date.vnNew();
nextMonth.setMonth(nextMonth.getMonth() + 1);
nextMonth.setUTCHours(0, 0, 0, 0);
const form = 'vn-route-basic-data form';
const values = {
worker: 'adminBossNick',
vehicle: '1111-IMK',
created: nextMonth,
kmStart: 1,
kmEnd: 2,
started: '08:00',
finished: '12:30',
};
const message = await page.sendForm(form, values);
await page.reloadSection('route.card.basicData');
const formValues = await page.fetchForm(form, Object.keys(values));
expect(message.isSuccess).toBeTrue();
expect(formValues).toEqual(values);
});
});

View File

@ -1,91 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Route create path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'route');
});
afterAll(async() => {
await browser.close();
});
describe('as employee', () => {
it('should click on the add new route button and open the creation form', async() => {
await page.waitToClick(selectors.routeIndex.addNewRouteButton);
await page.waitForState('route.create');
});
it(`should attempt to create a new route but fail since employee has no access rights`, async() => {
await page.write(selectors.createRouteView.description, 'faster faster!!');
await page.waitToClick(selectors.createRouteView.submitButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Access Denied');
});
});
describe('as delivery', () => {
beforeAll(async() => {
await page.login('delivery');
await page.selectModule('route');
});
it('should again click on the add new route button and open the creation form', async() => {
await page.waitToClick(selectors.routeIndex.addNewRouteButton);
await page.waitForState('route.create');
});
it(`should create a new route`, async() => {
await page.autocompleteSearch(selectors.createRouteView.worker, 'teamManagerNick');
await page.pickDate(selectors.createRouteView.createdDatePicker);
await page.autocompleteSearch(selectors.createRouteView.vehicleAuto, '4444-IMK');
await page.autocompleteSearch(selectors.createRouteView.agency, 'Teleportation device');
await page.write(selectors.createRouteView.description, 'faster faster!!');
await page.waitToClick(selectors.createRouteView.submitButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should confirm the redirection to the created route summary`, async() => {
await page.waitForState('route.card.summary');
});
it(`should navigate back to the route index`, async() => {
await page.waitToClick(selectors.globalItems.returnToModuleIndexButton);
await page.waitForState('route.index');
});
let count;
it(`should count the amount of routes before clonation`, async() => {
await page.waitForFunction(selector => {
return document.querySelectorAll(selector).length > 6;
}, {}, selectors.routeIndex.anyResult);
count = await page.countElement(selectors.routeIndex.anyResult);
expect(count).toBeGreaterThanOrEqual(7);
});
it(`should clone the first route`, async() => {
await page.waitToClick(selectors.routeIndex.firstRouteCheckbox);
await page.waitToClick(selectors.routeIndex.cloneButton);
await page.waitToClick(selectors.routeIndex.submitClonationButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should reload the section and count the amount of routes after clonation`, async() => {
await page.waitForNumberOfElements(selectors.routeIndex.anyResult, count + 1);
const result = await page.countElement(selectors.routeIndex.anyResult);
expect(result).toEqual(count + 1);
});
});
});

View File

@ -1,59 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Route tickets path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('delivery', 'route');
await page.accessToSearchResult('2');
await page.accessToSection('route.card.tickets');
});
afterAll(async() => {
await browser.close();
});
it('should modify the first ticket priority', async() => {
await page.writeOnEditableTD(selectors.routeTickets.firstTicketPriority, '9');
await page.keyboard.press('Enter');
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should confirm the buscaman button is disabled', async() => {
await page.waitForSelector(`${selectors.routeTickets.buscamanButton}.disabled`);
});
it('should check the first ticket checkbox and confirm the buscamanButton button is no longer disabled', async() => {
await page.waitForSelector(`${selectors.routeTickets.buscamanButton}.disabled`, {visible: false});
});
it('should check the route volume on the descriptor', async() => {
const result = await page.waitToGetProperty(selectors.routeDescriptor.volume, 'innerText');
expect(result).toEqual('0.2 / 50 m³');
});
it('should count how many tickets are in route', async() => {
const result = await page.countElement(selectors.routeTickets.anyTicket);
expect(result).toEqual(1);
});
it('should delete the first ticket in route', async() => {
await page.waitToClick(selectors.routeTickets.firstTicketDeleteButton);
await page.waitToClick(selectors.routeTickets.confirmButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Ticket removed from route');
});
it('should now count how many tickets are in route to find one less', async() => {
await page.waitForNumberOfElements(selectors.routeTickets.anyTicket, 0);
});
});

View File

@ -60,7 +60,7 @@ module.exports = Self => {
DISTINCT c.id clientFk,
c.name clientName,
c.salesPersonFk,
c.businessTypeFk,
c.businessTypeFk = 'worker' isWorker,
u.name salesPersonName,
d.amount,
co.created,
@ -71,14 +71,24 @@ module.exports = Self => {
d.defaulterSinced,
cn.name country,
c.countryFk,
pm.name payMethod
FROM vn.defaulter d
JOIN vn.client c ON c.id = d.clientFk
JOIN vn.country cn ON cn.id = c.countryFk
JOIN vn.payMethod pm ON pm.id = c.payMethodFk
LEFT JOIN vn.clientObservation co ON co.clientFk = c.id
pm.name payMethod,
r.finished IS NULL hasRecovery,
dp.id departmentFk,
dp.name departmentName
FROM defaulter d
JOIN client c ON c.id = d.clientFk
JOIN country cn ON cn.id = c.countryFk
JOIN payMethod pm ON pm.id = c.payMethodFk
LEFT JOIN clientObservation co ON co.clientFk = c.id
LEFT JOIN account.user u ON u.id = c.salesPersonFk
LEFT JOIN account.user uw ON uw.id = co.workerFk
LEFT JOIN (
SELECT MAX(started), clientFk, finished
FROM recovery
GROUP BY clientFk
) r ON r.clientFk = c.id
LEFT JOIN workerDepartment wd ON wd.workerFk = u.id
JOIN department dp ON dp.id = wd.departmentFk
WHERE
d.created = ?
AND d.amount > 0

View File

@ -82,7 +82,15 @@ module.exports = Self => {
{
arg: 'correctedFk',
type: 'number',
description: 'The corrected invoice',
description: 'The rectified invoice',
},
{
arg: 'correctingFk',
type: 'Boolean',
},
{
arg: 'supplierActivityFk',
type: 'string',
}
],
returns: {
@ -111,6 +119,7 @@ module.exports = Self => {
}
let correctings;
let correcteds;
if (args.correctedFk) {
correctings = await models.InvoiceInCorrection.find({
fields: ['correctingFk'],
@ -118,6 +127,9 @@ module.exports = Self => {
});
}
if (args.correctingFk || args.correctingFk === false)
correcteds = await models.InvoiceInCorrection.find();
const where = buildFilter(ctx.args, (param, value) => {
switch (param) {
case 'search':
@ -141,8 +153,14 @@ module.exports = Self => {
return {[`ii.${param}`]: value};
case 'awbCode':
return {'sub.code': value};
case 'correctingFk':
return args.correctingFk
? {'ii.id': {inq: correcteds.map(x => x.correctingFk)}}
: {'ii.id': {nin: correcteds.map(x => x.correctingFk)}};
case 'correctedFk':
return {'ii.id': {inq: correctings.map(x => x.correctingFk)}};
case 'supplierActivityFk':
return {'s.supplierActivityFk': value};
}
});

View File

@ -27,28 +27,42 @@ module.exports = Self => {
});
Self.getWasteByItem = async(buyer, family, options) => {
const models = Self.app.models;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const user = await models.VnUser.findOne({
fields: ['id'],
where: {name: buyer}
});
const itemType = await models.ItemType.findOne({
fields: ['id'],
where: {name: family}
}, options);
const date = Date.vnNew();
date.setHours(0, 0, 0, 0);
const wastes = await Self.rawSql(`
SELECT *, 100 * dwindle / total AS percentage
SELECT *, 100 * dwindle / total percentage
FROM (
SELECT buyer,
ws.family,
ws.itemFk,
sum(ws.saleTotal) AS total,
sum(ws.saleWaste) AS dwindle
FROM bs.waste ws
WHERE buyer = ? AND family = ?
AND year = YEAR(TIMESTAMPADD(WEEK,-1, ?))
AND week = WEEK(TIMESTAMPADD(WEEK,-1, ?), 1)
GROUP BY buyer, itemFk
SELECT u.name buyer,
it.name family,
w.itemFk,
SUM(w.saleTotal) total,
SUM(w.saleInternalWaste + w.saleExternalWaste) dwindle
FROM bs.waste w
JOIN account.user u ON u.id = w.buyerFk
JOIN vn.itemType it ON it.id = w.itemTypeFk
WHERE w.buyerFk = ? AND w.itemTypeFk = ?
AND w.year = YEAR(TIMESTAMPADD(WEEK, -1, ?))
AND w.week = WEEK(TIMESTAMPADD(WEEK, -1, ?), 1)
GROUP BY w.buyerFk, w.itemFk
) sub
ORDER BY family, percentage DESC`, [buyer, family, date, date], myOptions);
ORDER BY family, percentage DESC
`, [user.id, itemType.id, date, date], myOptions);
const details = [];

View File

@ -22,31 +22,37 @@ module.exports = Self => {
const date = Date.vnNew();
date.setHours(0, 0, 0, 0);
const wastes = await Self.rawSql(`
SELECT *, 100 * dwindle / total AS percentage
SELECT *, 100 * dwindle / total percentage
FROM (
SELECT buyer,
ws.family,
sum(ws.saleTotal) AS total,
sum(ws.saleWaste) AS dwindle
FROM bs.waste ws
SELECT u.name buyer,
it.name family,
w.itemFk,
SUM(w.saleTotal) total,
SUM(w.saleInternalWaste + w.saleExternalWaste) dwindle
FROM bs.waste w
JOIN account.user u ON u.id = w.buyerFk
JOIN vn.itemType it ON it.id = w.itemTypeFk
WHERE year = YEAR(TIMESTAMPADD(WEEK, -1, ?))
AND week = WEEK(TIMESTAMPADD(WEEK, -1, ?), 1)
GROUP BY buyer, family
GROUP BY buyerFk, itemTypeFk
) sub
ORDER BY percentage DESC`, [date, date], myOptions);
ORDER BY percentage DESC
`, [date, date], myOptions);
const wastesTotal = await Self.rawSql(`
SELECT *, 100 * dwindle / total AS percentage
SELECT *, 100 * dwindle / total percentage
FROM (
SELECT buyer,
sum(ws.saleTotal) AS total,
sum(ws.saleWaste) AS dwindle
FROM bs.waste ws
WHERE year = YEAR(TIMESTAMPADD(WEEK,-1, ?))
AND week = WEEK(TIMESTAMPADD(WEEK,-1, ?), 1)
GROUP BY buyer
SELECT u.name buyer,
SUM(w.saleTotal) total,
SUM(w.saleInternalWaste + w.saleExternalWaste) dwindle
FROM bs.waste w
JOIN account.user u ON u.id = w.buyerFk
WHERE w.year = YEAR(TIMESTAMPADD(WEEK, -1, ?))
AND w.week = WEEK(TIMESTAMPADD(WEEK, -1, ?), 1)
GROUP BY w.buyerFk
) sub
ORDER BY percentage DESC`, [date, date], myOptions);
ORDER BY percentage DESC
`, [date, date], myOptions);
const details = [];

View File

@ -6,13 +6,13 @@ describe('Item getWasteByItem()', () => {
const options = {transaction: tx};
try {
const result = await models.Item.getWasteByItem('CharlesXavier', 'Cymbidium', options);
const result = await models.Item.getWasteByItem('buyer', 'Crisantemo', options);
const length = result.length;
const anyResult = result[Math.floor(Math.random() * Math.floor(length))];
expect(anyResult.buyer).toEqual('CharlesXavier');
expect(anyResult.family).toEqual('Cymbidium');
expect(anyResult.buyer).toEqual('buyer');
expect(anyResult.family).toEqual('Crisantemo');
expect(anyResult.lines.length).toBeGreaterThanOrEqual(2);
await tx.rollback();

View File

@ -11,9 +11,9 @@ describe('Item getWasteByWorker()', () => {
const length = result.length;
const anyResult = result[Math.floor(Math.random() * Math.floor(length))];
expect(anyResult.buyer).toMatch(/(CharlesXavier|HankPym|DavidCharlesHaller)/);
expect(anyResult.buyer).toMatch(/(buyer|it)/);
expect(anyResult.total).toBeGreaterThanOrEqual(1000);
expect(anyResult.lines.length).toBeGreaterThanOrEqual(3);
expect(anyResult.lines.length).toBeGreaterThanOrEqual(1);
await tx.rollback();
} catch (e) {

View File

@ -48,7 +48,7 @@ module.exports = Self => {
LEFT JOIN vn.expeditionStateType est ON est.id = e.stateTypeFk
JOIN vn.agencyMode am ON am.id = r.agencyModeFk
JOIN vn.agency ag ON ag.id = am.agencyFk
LEFT JOIN vn.userConfig uc ON uc.userFk = account.myUser_getId()
LEFT JOIN vn.userMultiConfig uc ON uc.userFk = account.myUser_getId()
WHERE t.routeFk = ?
GROUP BY t.addressFk, e.itemPackingTypeFk
) sub

View File

@ -1,52 +0,0 @@
<div class="search-panel">
<vn-crud-model
auto-load="true"
url="Agencies"
data="agencies">
</vn-crud-model>
<form id="manifold-form" ng-submit="$ctrl.onSearch()">
<vn-horizontal class="vn-px-lg vn-pt-lg">
<vn-textfield
vn-one
label="General search"
ng-model="filter.search"
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal class="vn-px-lg">
<vn-autocomplete vn-one
url="AgencyModes"
label="Agency route"
show-field="name"
value-field="id"
ng-model="filter.agencyModeFk">
</vn-autocomplete>
<vn-autocomplete vn-one
data="agencies"
label="Agency Agreement"
show-field="name"
value-field="id"
ng-model="filter.agencyFk">
</vn-autocomplete>
</vn-horizontal>
<section class="vn-px-md">
<vn-horizontal class="manifold-panel vn-pa-md">
<vn-date-picker
vn-one
label="From"
ng-model="filter.from"
on-change="$ctrl.from = value">
</vn-date-picker>
<vn-date-picker
vn-one
label="To"
ng-model="filter.to"
on-change="$ctrl.to = value">
</vn-date-picker>
</vn-horizontal>
</section>
<vn-horizontal class="vn-px-lg vn-pb-lg vn-mt-lg">
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -1,17 +0,0 @@
import ngModule from '../module';
import SearchPanel from 'core/components/searchbar/search-panel';
class Controller extends SearchPanel {
get filter() {
return this.$.filter;
}
set filter(value = {}) {
this.$.filter = value;
}
}
ngModule.vnComponent('vnAgencyTermSearchPanel', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,2 +0,0 @@
Search by invoiceIn id or autonomous name: Buscar por id de recibida o por nombre de autónomo
Search autonomous: Buscar autónomos

View File

@ -1,108 +0,0 @@
<mg-ajax path="dms/upload" options="vnPost"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.dms">
</vn-watcher>
<vn-crud-model
auto-load="true"
url="Companies"
data="companies"
order="code">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="Warehouses"
data="warehouses"
order="name">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="DmsTypes"
data="dmsTypes"
order="name">
</vn-crud-model>
<form
name="form"
ng-submit="$ctrl.onSubmit()"
class="vn-ma-md"
enctype="multipart/form-data">
<div class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield
vn-one
vn-focus
label="Reference"
ng-model="$ctrl.dms.reference"
rule>
</vn-textfield>
<vn-autocomplete vn-one
label="Company"
ng-model="$ctrl.dms.companyId"
data="companies"
show-field="code"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
label="Warehouse"
ng-model="$ctrl.dms.warehouseId"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete vn-one
label="Type"
ng-model="$ctrl.dms.dmsTypeId"
data="dmsTypes"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textarea
vn-one
label="Description"
ng-model="$ctrl.dms.description"
rule>
</vn-textarea>
</vn-horizontal>
<vn-horizontal>
<vn-input-file
vn-one
label="File"
ng-model="$ctrl.dms.files"
on-change="$ctrl.onFileChange($files)"
accept="{{$ctrl.allowedContentTypes}}"
required="true"
multiple="true">
<append>
<vn-icon vn-none
color-marginal
title="{{$ctrl.contentTypesInfo}}"
icon="info">
</vn-icon>
</append>
</vn-input-file>
</vn-horizontal>
<vn-vertical>
<vn-check
label="Generate identifier for original file"
ng-model="$ctrl.dms.hasFile">
</vn-check>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Upload">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="client.card.dms.index">
</vn-button>
</vn-button-bar>
</div>
</form>

View File

@ -1,120 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
import './style.scss';
import UserError from 'core/lib/user-error';
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.dms = {
files: [],
hasFile: false,
hasFileAttached: false
};
}
get route() {
return this._route;
}
set route(value) {
this._route = value;
this.setDefaultParams();
this.getAllowedContentTypes();
}
$onChanges() {
if (this.$params && this.$params.q)
this.params = JSON.parse(this.$params.q);
}
getAllowedContentTypes() {
this.$http.get('DmsContainers/allowedContentTypes').then(res => {
const contentTypes = res.data.join(', ');
this.allowedContentTypes = contentTypes;
});
}
get contentTypesInfo() {
return this.$t('ContentTypesInfo', {
allowedContentTypes: this.allowedContentTypes
});
}
setDefaultParams() {
const params = {filter: {
where: {code: 'invoiceIn'}
}};
this.$http.get('DmsTypes/findOne', {params}).then(res => {
const dmsType = res.data && res.data;
const companyId = this.vnConfig.companyFk;
const warehouseId = this.vnConfig.warehouseFk;
const defaultParams = {
warehouseId: warehouseId,
companyId: companyId,
dmsTypeId: dmsType.id,
description: this.params.supplierName
};
this.dms = Object.assign(this.dms, defaultParams);
});
}
onSubmit() {
if (this.dms.files.length > 1) throw new UserError('You cannot attach more than one document');
const query = `dms/uploadFile`;
const options = {
method: 'POST',
url: query,
params: this.dms,
headers: {
'Content-Type': undefined
},
transformRequest: files => {
const formData = new FormData();
formData.append(files[0].name, files[0]);
return formData;
},
data: this.dms.files
};
this.$http(options).then(res => {
if (res) {
const addedDms = res.data;
this.$.watcher.updateOriginalData();
const params = {
rows: this.params.rows,
dms: addedDms
};
this.$http.post('AgencyTerms/createInvoiceIn', params)
.then(() => {
this.$state.go('route.agencyTerm.index');
this.vnApp.showSuccess(this.$t('Data saved!'));
});
}
});
}
onFileChange(files) {
let hasFileAttached = false;
if (files.length > 0)
hasFileAttached = true;
this.$.$applyAsync(() => {
this.dms.hasFileAttached = hasFileAttached;
});
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnAgencyTermCreateInvoiceIn', {
template: require('./index.html'),
controller: Controller,
bindings: {
route: '<'
}
});

View File

@ -1,107 +0,0 @@
import './index';
import watcher from 'core/mocks/watcher.js';
describe('AgencyTerm', () => {
describe('Component vnAgencyTermCreateInvoiceIn', () => {
let controller;
let $scope;
let $httpBackend;
let $httpParamSerializer;
beforeEach(ngModule('route'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => {
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
const $element = angular.element('<vn-agency-term-create-invoice-in></vn-agency-term-create-invoice-in>');
controller = $componentController('vnAgencyTermCreateInvoiceIn', {$element});
controller._route = {
id: 1
};
}));
describe('$onChanges()', () => {
it('should update the params data when $params.q is defined', () => {
controller.$params = {q: '{"supplierName": "Plants SL","rows": null}'};
const params = {q: '{"supplierName": "Plants SL", "rows": null}'};
const json = JSON.parse(params.q);
controller.$onChanges();
expect(controller.params).toEqual(json);
});
});
describe('route() setter', () => {
it('should set the ticket data and then call setDefaultParams() and getAllowedContentTypes()', () => {
jest.spyOn(controller, 'setDefaultParams');
jest.spyOn(controller, 'getAllowedContentTypes');
controller.route = {
id: 1
};
expect(controller.route).toBeDefined();
expect(controller.setDefaultParams).toHaveBeenCalledWith();
expect(controller.getAllowedContentTypes).toHaveBeenCalledWith();
});
});
describe('getAllowedContentTypes()', () => {
it('should make an HTTP GET request to get the allowed content types', () => {
const expectedResponse = ['image/png', 'image/jpg'];
$httpBackend.expect('GET', `DmsContainers/allowedContentTypes`).respond(expectedResponse);
controller.getAllowedContentTypes();
$httpBackend.flush();
expect(controller.allowedContentTypes).toBeDefined();
expect(controller.allowedContentTypes).toEqual('image/png, image/jpg');
});
});
describe('setDefaultParams()', () => {
it('should perform a GET query and define the dms property on controller', () => {
const params = {filter: {
where: {code: 'invoiceIn'}
}};
const serializedParams = $httpParamSerializer(params);
$httpBackend.expect('GET', `DmsTypes/findOne?${serializedParams}`).respond({id: 1, code: 'invoiceIn'});
controller.params = {supplierName: 'Plants SL'};
controller.setDefaultParams();
$httpBackend.flush();
expect(controller.dms).toBeDefined();
expect(controller.dms.dmsTypeId).toEqual(1);
});
});
describe('onSubmit()', () => {
it('should make an HTTP POST request to save the form data', () => {
controller.$.watcher = watcher;
jest.spyOn(controller.$.watcher, 'updateOriginalData');
const files = [{id: 1, name: 'MyFile'}];
controller.dms = {files};
const serializedParams = $httpParamSerializer(controller.dms);
const query = `dms/uploadFile?${serializedParams}`;
controller.params = {rows: null};
$httpBackend.expect('POST', query).respond({});
$httpBackend.expect('POST', 'AgencyTerms/createInvoiceIn').respond({});
controller.onSubmit();
$httpBackend.flush();
});
});
describe('onFileChange()', () => {
it('should set dms hasFileAttached property to true if has any files', () => {
const files = [{id: 1, name: 'MyFile'}];
controller.onFileChange(files);
$scope.$apply();
expect(controller.dms.hasFileAttached).toBeTruthy();
});
});
});
});

View File

@ -1,7 +0,0 @@
vn-ticket-request {
.vn-textfield {
margin: 0!important;
max-width: 100px;
}
}

View File

@ -1,152 +0,0 @@
<vn-crud-model
vn-id="model"
url="AgencyTerms/filter"
data="agencyTerms"
auto-load="true">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
auto-state="false"
panel="vn-agency-term-search-panel"
info="Search by invoiceIn id or autonomous name"
placeholder="Search autonomous"
filter="{}"
model="model">
</vn-searchbar>
</vn-portal>
<vn-card>
<smart-table
model="model"
options="$ctrl.smartTableOptions"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-actions>
<div>
<div class="totalBox" style="text-align: center;">
<h6 translate>Total</h6>
<vn-label-value
label="Price"
value="{{$ctrl.totalPrice | currency: 'EUR': 2}}">
</vn-label-value>
</div>
</div>
</slot-actions>
<slot-table>
<table>
<thead>
<tr>
<th shrink>
<vn-multi-check
model="model">
</vn-multi-check>
</th>
<th shrink field="routeFk">
<span translate>Id</span>
</th>
<th field="created">
<span translate>Date</span>
</th>
<th field="agencyModeFk">
<span translate>Agency route</span>
</th>
<th field="agencyFk">
<span translate>Agency Agreement</span>
</th>
<th field="packages">
<span translate>Packages</span>
</th>
<th field="m3">
<span translate>M3</span>
</th>
<th field="kmTotal">
<span translate>Km</span>
</th>
<th field="price">
<span translate>Price</span>
</th>
<th field="invoiceInFk">
<span translate>Received</span>
</th>
<th field="supplierFk">
<span translate>Autonomous</span>
</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="agencyTerm in agencyTerms">
<td shrink>
<vn-check
ng-model="agencyTerm.checked"
vn-click-stop>
</vn-check>
</td>
<td>
<span
title="{{::agencyTerm.id}}"
vn-click-stop="routeDescriptor.show($event, agencyTerm.routeFk)"
class="link">
{{::agencyTerm.routeFk}}
</span>
</td>
<td shrink-date>{{::agencyTerm.created | date:'dd/MM/yyyy'}}</td>
<td>{{::agencyTerm.agencyModeName | dashIfEmpty}}</td>
<td>{{::agencyTerm.agencyAgreement | dashIfEmpty}}</td>
<td>{{::agencyTerm.packages | dashIfEmpty}}</td>
<td>{{::agencyTerm.m3 | dashIfEmpty}}</td>
<td>{{::agencyTerm.kmTotal | dashIfEmpty}}</td>
<td>{{::agencyTerm.price | dashIfEmpty}}</td>
<td>
<span
vn-click-stop="invoiceInDescriptor.show($event, agencyTerm.invoiceInFk)"
class="link">
{{::agencyTerm.invoiceInFk}}
</span>
</td>
<td>
<span
class="link"
vn-click-stop="supplierDescriptor.show($event, agencyTerm.supplierFk)">
{{::agencyTerm.supplierName}}
</span>
</td>
<td>
<vn-icon-button
vn-click-stop="$ctrl.preview(agencyTerm.route)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</td>
</tr>
</tbody>
</table>
</slot-table>
</smart-table>
</vn-card>
<vn-popup vn-id="summary">
<vn-route-summary
route="$ctrl.routeSelected">
</vn-route-summary>
</vn-popup>
<vn-route-descriptor-popover
vn-id="routeDescriptor">
</vn-route-descriptor-popover>
<vn-supplier-descriptor-popover
vn-id="supplierDescriptor">
</vn-supplier-descriptor-popover>
<vn-invoice-in-descriptor-popover
vn-id="invoiceInDescriptor">
</vn-invoice-in-descriptor-popover>
<div fixed-bottom-right>
<vn-vertical style="align-items: center;">
<vn-button class="round sm vn-mb-sm"
icon="icon-invoice-in-create"
ng-show="$ctrl.totalChecked > 0"
ng-click="$ctrl.createInvoiceIn()"
vn-tooltip="Create invoiceIn"
tooltip-position="left">
</vn-button>
</vn-vertical>
</div>

View File

@ -1,121 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.smartTableOptions = {
activeButtons: {
search: true
},
columns: [
{
field: 'agencyModeFk',
autocomplete: {
url: 'AgencyModes',
showField: 'name',
valueField: 'id'
}
},
{
field: 'agencyFk',
autocomplete: {
url: 'Agencies',
showField: 'name',
valueField: 'id'
}
},
{
field: 'supplierFk',
autocomplete: {
url: 'Suppliers',
showField: 'name',
valueField: 'name',
}
}
]
};
}
exprBuilder(param, value) {
switch (param) {
case 'agencyModeFk':
return {'a.agencyModeFk': value};
case 'supplierFk':
return {'a.supplierName': value};
case 'routeFk':
return {'a.routeFk': value};
case 'created':
case 'agencyFk':
case 'packages':
case 'm3':
case 'kmTotal':
case 'price':
case 'invoiceInFk':
return {[`a.${param}`]: value};
}
}
get checked() {
const agencyTerms = this.$.model.data || [];
const checkedAgencyTerms = [];
for (let agencyTerm of agencyTerms) {
if (agencyTerm.checked)
checkedAgencyTerms.push(agencyTerm);
}
return checkedAgencyTerms;
}
get totalChecked() {
return this.checked.length;
}
get totalPrice() {
let totalPrice = 0;
if (this.checked.length > 0) {
for (let agencyTerm of this.checked)
totalPrice += agencyTerm.price;
return totalPrice;
}
return totalPrice;
}
preview(route) {
this.routeSelected = route;
this.$.summary.show();
}
createInvoiceIn() {
const rowsToCreateInvoiceIn = [];
const supplierFk = this.checked[0].supplierFk;
for (let agencyTerm of this.checked) {
let hasSameSupplier = supplierFk == agencyTerm.supplierFk;
if (hasSameSupplier) {
rowsToCreateInvoiceIn.push({
routeFk: agencyTerm.routeFk,
supplierFk: agencyTerm.supplierFk,
created: agencyTerm.created,
totalPrice: this.totalPrice});
} else {
this.vnApp.showError(this.$t('Two autonomous cannot be counted at the same time'));
return false;
}
}
const params = JSON.stringify({
supplierName: this.checked[0].supplierName,
rows: rowsToCreateInvoiceIn
});
this.$state.go('route.agencyTerm.createInvoiceIn', {q: params});
}
}
ngModule.vnComponent('vnAgencyTermIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,94 +0,0 @@
import './index.js';
import crudModel from 'core/mocks/crud-model';
describe('AgencyTerm', () => {
describe('Component vnAgencyTermIndex', () => {
let controller;
let $window;
beforeEach(ngModule('route'));
beforeEach(inject(($componentController, _$window_) => {
$window = _$window_;
const $element = angular.element('<vn-agency-term-index></vn-agency-term-index>');
controller = $componentController('vnAgencyTermIndex', {$element});
controller.$.model = crudModel;
controller.$.model.data = [
{supplierFk: 1, totalPrice: null},
{supplierFk: 1},
{supplierFk: 2}
];
}));
describe('checked() getter', () => {
it('should return the checked lines', () => {
const data = controller.$.model.data;
data[0].checked = true;
const checkedRows = controller.checked;
const firstCheckedRow = checkedRows[0];
expect(firstCheckedRow.supplierFk).toEqual(1);
});
});
describe('totalCheked() getter', () => {
it('should return the total checked lines', () => {
const data = controller.$.model.data;
data[0].checked = true;
const checkedRows = controller.totalChecked;
expect(checkedRows).toEqual(1);
});
});
describe('preview()', () => {
it('should show the summary dialog', () => {
controller.$.summary = {show: () => {}};
jest.spyOn(controller.$.summary, 'show');
let event = new MouseEvent('click', {
view: $window,
bubbles: true,
cancelable: true
});
const route = {id: 1};
controller.preview(event, route);
expect(controller.$.summary.show).toHaveBeenCalledWith();
});
});
describe('createInvoiceIn()', () => {
it('should throw an error if more than one autonomous are checked', () => {
jest.spyOn(controller.vnApp, 'showError');
const data = controller.$.model.data;
data[0].checked = true;
data[2].checked = true;
controller.createInvoiceIn();
expect(controller.vnApp.showError).toHaveBeenCalled();
});
it('should call the function go() on $state to go to the file management', () => {
jest.spyOn(controller.$state, 'go');
const data = controller.$.model.data;
data[0].checked = true;
controller.createInvoiceIn();
delete data[0].checked;
const params = JSON.stringify({
supplierName: data[0].supplierName,
rows: [data[0]]
});
expect(controller.$state.go).toHaveBeenCalledWith('route.agencyTerm.createInvoiceIn', {q: params});
});
});
});
});

View File

@ -1,5 +0,0 @@
Agency route: Agencia ruta
Agency Agreement: Acuerdo agencia
Autonomous: Autónomos
Two autonomous cannot be counted at the same time: Dos autonónomos no pueden ser contabilizados al mismo tiempo
You cannot attach more than one document: No puedes adjuntar más de un documento

View File

@ -1,96 +0,0 @@
<mg-ajax path="Routes/{{patch.params.id}}" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.route"
form="form"
save="patch">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-worker-autocomplete
ng-model="$ctrl.route.workerFk"
show-field="nickname"
vn-name="worker">
</vn-worker-autocomplete>
<vn-autocomplete
label="Vehicle"
ng-model="$ctrl.route.vehicleFk"
data="$ctrl.vehicles"
show-field="numberPlate"
value-field="id"
order="false"
vn-name="vehicle">
<tpl-item>{{::numberPlate}} - {{::name}}</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-date-picker
label="Created"
ng-model="$ctrl.route.created"
vn-name="created">
</vn-date-picker>
<vn-autocomplete
ng-model="$ctrl.route.agencyModeFk"
url="AgencyModes"
show-field="name"
value-field="id"
label="Agency"
vn-name="agencyMode">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
label="Km start"
ng-model="$ctrl.route.kmStart"
vn-name="kmStart"
rule>
</vn-input-number>
<vn-input-number
label="Km end"
ng-model="$ctrl.route.kmEnd"
vn-name="kmEnd"
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-input-time
label="Hour started"
ng-model="$ctrl.route.started"
vn-name="started">
</vn-input-time>
<vn-input-time
label="Hour finished"
ng-model="$ctrl.route.finished"
vn-name="finished">
</vn-input-time>
<vn-check
class="vn-mr-md"
label="Is served"
ng-model="$ctrl.route.isOk"
vn-name="isOk">
</vn-check>
</vn-horizontal>
<vn-horizontal>
<vn-textArea
label="Description"
ng-model="$ctrl.route.description"
vn-name="description"
rule
vn-focus>
</vn-textArea>
</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>

View File

@ -1,28 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
class Controller extends Section {
$onInit() {
this.$http.post(`Vehicles/sorted`, {warehouseFk: this.vnConfig.warehouseFk})
.then(res => {
this.vehicles = res.data;
});
}
onSubmit() {
this.$.watcher.submit().then(() =>
this.card.reload()
);
}
}
ngModule.vnComponent('vnRouteBasicData', {
template: require('./index.html'),
controller: Controller,
bindings: {
route: '<'
},
require: {
card: '^vnRouteCard'
}
});

View File

@ -1,8 +0,0 @@
Date finished: Fecha fin
Date started: Fecha inicio
Km start: Km de inicio
Km end: Km de fin
Description: Descripción
Hour started: Hora inicio
Hour finished: Hora fin
Is served: Se ha servido

View File

@ -1,5 +0,0 @@
<vn-portal slot="menu">
<vn-route-descriptor route="$ctrl.route" card-reload="$ctrl.reload()"></vn-route-descriptor>
<vn-left-menu source="card"></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -1,71 +0,0 @@
import ngModule from '../module';
import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard {
reload() {
let filter = {
fields: [
'id',
'workerFk',
'agencyModeFk',
'created',
'm3',
'warehouseFk',
'description',
'vehicleFk',
'kmStart',
'kmEnd',
'started',
'finished',
'cost',
'zoneFk',
'isOk'
],
include: [
{
relation: 'agencyMode',
scope: {
fields: ['id', 'name']
}
}, {
relation: 'vehicle',
scope: {
fields: ['id', 'm3']
}
}, {
relation: 'zone',
scope: {
fields: ['id', 'name']
}
},
{
relation: 'worker',
scope: {
fields: ['id'],
include: {
relation: 'user',
scope: {
fields: ['id'],
include: {
relation: 'emailUser',
scope: {
fields: ['email']
}
}
}
}
}
}
]
};
this.$http.get(`Routes/${this.$params.id}`, {filter})
.then(res => this.route = res.data);
}
}
ngModule.vnComponent('vnRouteCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,52 +0,0 @@
<vn-watcher
vn-id="watcher"
url="Routes"
data="$ctrl.route"
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-horizontal>
<vn-worker-autocomplete
ng-model="$ctrl.route.workerFk"
show-field="nickname">
</vn-worker-autocomplete>
<vn-date-picker
label="Created"
ng-model="$ctrl.route.created">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
label="Vehicle"
ng-model="$ctrl.route.vehicleFk"
url="Vehicles"
show-field="numberPlate">
</vn-autocomplete>
<vn-autocomplete
label="Agency"
ng-model="$ctrl.route.agencyModeFk"
url="AgencyModes">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
label="Description"
ng-model="$ctrl.route.description"
rule>
</vn-textfield>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Create">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="route.index">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,20 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
onSubmit() {
this.$.watcher.submit().then(
res => {
this.$http.post(`Routes/${res.data.id}/updateWorkCenter`, null)
.then(() => {
this.$state.go('route.card.summary', {id: res.data.id});
});
}
);
}
}
ngModule.vnComponent('vnRouteCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,19 +1,6 @@
export * from './module';
import './main';
import './index/';
import './search-panel';
import './descriptor';
import './descriptor-popover';
import './summary';
import './card';
import './create';
import './basic-data';
import './log';
import './tickets';
import './agency-term/index';
import './agency-term/createInvoiceIn';
import './agency-term-search-panel';
import './ticket-popup';
import './sms';
import './roadmap';

View File

@ -1,228 +0,0 @@
<vn-auto-search
model="model">
</vn-auto-search>
<vn-crud-model
auto-load="true"
url="AgencyModes"
data="agencyModes">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="Vehicles"
data="vehicles">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="Workers/activeWithInheritedRole"
data="activeWithInheritedRole">
</vn-crud-model>
<div class="vn-w-xl">
<vn-card>
<smart-table
model="model"
options="$ctrl.smartTableOptions"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-actions>
<section class="header">
<vn-tool-bar class="vn-mb-md">
<vn-button
icon="icon-clone"
ng-show="$ctrl.totalChecked > 0"
ng-click="$ctrl.openClonationDialog()"
vn-tooltip="Clone selected routes">
</vn-button>
<vn-button
icon="cloud_download"
ng-show="$ctrl.totalChecked > 0"
ng-click="$ctrl.showRouteReport()"
vn-tooltip="Download selected routes as PDF">
</vn-button>
<vn-button
icon="check"
ng-show="$ctrl.totalChecked > 0"
ng-click="$ctrl.markAsServed()"
vn-tooltip="Mark as served">
</vn-button>
</section>
</slot-actions>
<slot-table>
<table model="model">
<thead>
<tr>
<th shrink>
<vn-multi-check
model="model">
</vn-multi-check>
</th>
<th field="id" number>
<span translate>Id</span>
</th>
<th field="workerFk">
<span translate>Worker</span>
</th>
<th field="agencyName">
<span translate>Agency</span>
</th>
<th field="vehiclePlateNumber">
<span translate>Vehicle</span>
</th>
<th field="created" shrink-date>
<span translate>Date</span>
</th>
<th field="m3" number>
<span translate></span>
</th>
<th field="description">
<span translate>Description</span>
</th>
<th field="started">
<span translate>Hour started</span>
</th>
<th field="finished">
<span translate>Hour finished</span>
</th>
<th shrink></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="route in model.data"
class="clickable vn-tr search-result"
ng-attr-id="{{::route.id}}" vn-droppable="$ctrl.onDrop($event)">
<td shrink>
<vn-check
ng-model="route.checked"
vn-click-stop>
</vn-check>
</td>
<td number>{{::route.id | dashIfEmpty}}</td>
<td>
<vn-autocomplete
ng-model="route.workerFk"
data="activeWithInheritedRole"
show-field="nickname"
search-function="{firstName: $search}"
value-field="id"
where="{role: 'employee'}"
on-change="$ctrl.updateAttributes(route)"
vn-click-stop>
<tpl-item>
<div>{{name}} - {{nickname}}</div>
</tpl-item>
</vn-autocomplete>
</td>
<td expand>
<vn-autocomplete
ng-model="route.agencyModeFk"
data="agencyModes"
show-field="name"
value-field="id"
on-change="$ctrl.updateAttributes(route)"
vn-click-stop>
</vn-autocomplete>
</td>
<td expand>
<vn-autocomplete
ng-model="route.vehicleFk"
data="vehicles"
show-field="numberPlate"
value-field="id"
on-change="$ctrl.updateAttributes(route)"
vn-click-stop>
</vn-autocomplete>
</td >
<td>
<vn-date-picker
ng-model="route.created"
on-change="$ctrl.updateAttributes(route)">
</vn-horizontal>
</td>
<td number>{{::route.m3 | dashIfEmpty}}</td>
<td>
<vn-textfield
ng-model="route.description"
on-change="$ctrl.updateAttributes(route)">
</vn-textfield>
</td>
<td expand>
<vn-input-time
ng-model="route.started"
on-change="$ctrl.updateAttributes(route)">
</vn-input-time>
</td>
<td expand>
<vn-input-time
ng-model="route.finished"
on-change="$ctrl.updateAttributes(route)">
</vn-input-time>
</td>
<td>
<vn-icon-button
vn-click-stop="$ctrl.showTicketPopup(route)"
vn-tooltip="Añadir tickets"
icon="icon-ticketAdd">
</vn-icon-button>
<vn-icon-button
vn-click-stop="$ctrl.preview(route)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</td>
</tr>
</tbody>
</table>
</slot-table>
</smart-table>
</vn-card>
</div>
<vn-popup vn-id="summary">
<vn-route-summary
route="$ctrl.routeSelected">
</vn-route-summary>
</vn-popup>
<vn-route-ticket-popup
vn-id="ticketPopup"
route="$ctrl.routeSelected">
</vn-route-ticket-popup>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>
<vn-ticket-descriptor-popover
vn-id="ticketDescriptor">
</vn-ticket-descriptor-popover>
<vn-client-descriptor-popover
vn-id="clientDescriptor">
</vn-client-descriptor-popover>
<div fixed-bottom-right>
<vn-vertical style="align-items: center;">
<a ui-sref="route.create" vn-bind="+">
<vn-button class="round md vn-mb-sm"
icon="add"
vn-tooltip="New route"
tooltip-position="left">
</vn-button>
</a>
</vn-vertical>
</div>
<!-- Clonation dialog -->
<vn-dialog class="edit"
vn-id="clonationDialog"
on-accept="$ctrl.cloneSelectedRoutes()"
message="Select the starting date">
<tpl-body>
<vn-horizontal>
<vn-date-picker
label="Starting date"
ng-model="$ctrl.createdDate">
</vn-date-picker>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Clone</button>
</tpl-buttons>
</vn-dialog>

View File

@ -1,151 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
constructor($element, $, vnReport) {
super($element, $);
this.vnReport = vnReport;
this.droppableElement = 'a.vn-tr';
}
preview(route) {
this.routeSelected = route;
this.$.summary.show();
}
showTicketPopup(route) {
this.routeSelected = route;
this.$.ticketPopup.show();
}
get checked() {
const rows = this.$.model.data || [];
const checkedRows = [];
for (let row of rows) {
if (row.checked)
checkedRows.push(row);
}
return checkedRows;
}
get totalChecked() {
return this.checked.length;
}
showRouteReport() {
const routesIds = [];
const access_token = this.vnToken.tokenMultimedia;
for (let route of this.checked)
routesIds.push(route.id);
const stringRoutesIds = routesIds.join(',');
if (this.checked.length <= 1) {
const url = `api/Routes/${stringRoutesIds}/driver-route-pdf?access_token=${access_token}`;
window.open(url, '_blank');
} else {
const serializedParams = this.$httpParamSerializer({
access_token,
id: stringRoutesIds
});
const url = `api/Routes/downloadZip?${serializedParams}`;
window.open(url, '_blank');
}
}
openClonationDialog() {
this.$.clonationDialog.show();
this.createdDate = Date.vnNew();
}
cloneSelectedRoutes() {
try {
if (!this.createdDate)
throw new Error(`The date can't be empty`);
const routesIds = [];
for (let route of this.checked)
routesIds.push(route.id);
return this.$http.post('Routes/clone', {ids: routesIds, created: this.createdDate}).then(() => {
this.$.model.refresh();
this.vnApp.showSuccess(this.$t('Data saved!'));
});
} catch (e) {
this.vnApp.showError(this.$t(e.message));
}
}
onDrop($event) {
const target = $event.target;
const droppable = target.closest(this.droppableElement);
const ticketId = $event.dataTransfer.getData('Text');
const routeId = droppable.id;
if (isNaN(ticketId)) {
const regexp = new RegExp(/\/ticket\/([0-9]+)\//i);
const matches = ticketId.match(regexp);
if (matches && matches.length)
this.insert(routeId, matches[1]);
else
this.vnApp.showError(this.$t('Ticket not found'));
}
if (!isNaN(ticketId))
this.insert(routeId, ticketId);
}
insert(routeId, ticketId) {
routeId = parseInt(routeId);
ticketId = parseInt(ticketId);
const query = `Routes/${routeId}/insertTicket`;
return this.$http.patch(query, {ticketId}).then(() => {
this.vnApp.showSuccess(this.$t('Data saved!'));
this.$.model.refresh();
}).catch(error => {
if (error.status == 404)
return this.vnApp.showError(this.$t('Ticket not found'));
throw error;
});
}
updateAttributes(route) {
if (route.started == null || route.finished == null)
return this.vnApp.showError(this.$t('You must select a valid time'));
if (route.created == null)
return this.vnApp.showError(this.$t('You must select a valid date'));
const params = {
workerFk: route.workerFk,
agencyModeFk: route.agencyModeFk,
vehicleFk: route.vehicleFk,
created: route.created,
description: route.description,
started: route.started,
finished: route.finished
};
const query = `Routes/${route.id}/`;
this.$http.patch(query, params).then(res => {
this.vnApp.showSuccess(this.$t('Data saved!'));
});
}
markAsServed() {
const routes = [];
for (let route of this.checked)
routes.push(route.id);
const params = {isOk: true};
for (let routeId of routes)
this.$http.patch(`Routes/${routeId}`, params);
}
}
Controller.$inject = ['$element', '$scope', 'vnReport'];
ngModule.vnComponent('vnRouteIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,156 +0,0 @@
import './index.js';
import crudModel from 'core/mocks/crud-model';
describe('Component vnRouteIndex', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('route'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
const $element = angular.element('<vn-route-index></vn-route-index>');
controller = $componentController('vnRouteIndex', {$element});
controller.$.model = crudModel;
controller.$.model.data = [{id: 1, checked: true}, {id: 2}, {id: 3}];
}));
describe('checked() getter', () => {
it('should return the checked lines', () => {
const data = controller.$.model.data;
data[0].checked = true;
data[2].checked = true;
const checkedRows = controller.checked;
const firstCheckedRow = checkedRows[0];
const secondCheckedRow = checkedRows[1];
expect(firstCheckedRow.id).toEqual(1);
expect(secondCheckedRow.id).toEqual(3);
});
});
describe('totalCheked() getter', () => {
it('should return the total checked lines', () => {
const data = controller.$.model.data;
data[0].checked = true;
const checkedRows = controller.totalChecked;
expect(checkedRows).toEqual(1);
});
});
describe('showRouteReport()', () => {
it('should call to the vnReport show method', () => {
jest.spyOn(window, 'open').mockReturnThis();
const data = controller.$.model.data;
data[0].checked = true;
data[2].checked = true;
controller.showRouteReport();
expect(window.open).toHaveBeenCalled();
});
});
describe('cloneSelectedRoutes()', () => {
it('should perform an http request to Routes/clone', () => {
controller.createdDate = Date.vnNew();
$httpBackend.expect('POST', 'Routes/clone').respond();
controller.cloneSelectedRoutes();
$httpBackend.flush();
});
});
describe('onDrop()', () => {
it('should call the insert method when dragging a ticket number', () => {
jest.spyOn(controller, 'insert');
const routeId = '1';
const expectedTicketId = '16';
const draggedElement = '16';
const droppable = document.createElement('a');
droppable.setAttribute('id', 1);
droppable.classList.add('vn-tr');
const $event = {
dataTransfer: {
getData: () => draggedElement
},
target: droppable
};
controller.onDrop($event);
expect(controller.insert).toHaveBeenCalledWith(routeId, expectedTicketId);
});
it('should call the insert method when dragging a ticket link', () => {
jest.spyOn(controller, 'insert');
const routeId = '1';
const expectedTicketId = '11';
const draggedElement = 'http://arkamcity.com/#!/ticket/11/summary';
const droppable = document.createElement('a');
droppable.setAttribute('id', 1);
droppable.classList.add('vn-tr');
const $event = {
dataTransfer: {
getData: () => draggedElement
},
target: droppable
};
controller.onDrop($event);
expect(controller.insert).toHaveBeenCalledWith(routeId, expectedTicketId);
});
it('should throw an error when dragging an invalid ticket link', () => {
jest.spyOn(controller.vnApp, 'showError');
const draggedElement = 'http://arkamcity.com/#!/item/11/summary';
const droppable = document.createElement('a');
droppable.setAttribute('id', 1);
droppable.classList.add('vn-tr');
const $event = {
dataTransfer: {
getData: () => draggedElement
},
target: droppable
};
controller.onDrop($event);
expect(controller.vnApp.showError).toHaveBeenCalledWith('Ticket not found');
});
});
describe('insert()', () => {
it('should perform a HTTP patch query and then call both refresh and showSuccess methods', () => {
jest.spyOn(controller.$.model, 'refresh').mockReturnThis();
jest.spyOn(controller.vnApp, 'showSuccess');
const routeId = 1;
const ticketId = 11;
const data = {ticketId};
$httpBackend.expect('PATCH', `Routes/1/insertTicket`, data).respond();
controller.insert(routeId, ticketId);
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
expect(controller.$.model.refresh).toHaveBeenCalledWith();
});
});
describe('markAsServed()', () => {
it('should perform a HTTP patch query', () => {
const data = {isOk: true};
$httpBackend.expect('PATCH', `Routes/1`, data).respond();
controller.markAsServed();
$httpBackend.flush();
});
});
});

View File

@ -1,13 +0,0 @@
Vehicle: Vehículo
Download selected routes as PDF: Descargar rutas seleccionadas como PDF
Clone selected routes: Clonar rutas seleccionadas
The date can't be empty: La fecha no puede estar vacía
Starting date: Fecha de inicio
Hour started: Hora inicio
Hour finished: Hora fin
Go to route: Ir a la ruta
You must select a valid time: Debe seleccionar una hora válida
You must select a valid date: Debe seleccionar una fecha válida
Mark as served: Marcar como servidas
Retrieving data from the routes: Recuperando datos de las rutas
Send SMS to all clients: Mandar sms a todos los clientes de las rutas

View File

@ -1 +0,0 @@
Unlink zone: Unlink zone {{zoneName}} from agency {{agencyName}}

View File

@ -1,5 +0,0 @@
Routes: Rutas
Search routes by id: Buscar rutas por identificador
New route: Nueva ruta
route: ruta
Unlink zone: Desvincular zona {{zoneName}} de agencia {{agencyName}}

View File

@ -1 +0,0 @@
<vn-log url="RouteLogs" origin-id="$ctrl.$params.id"></vn-log>

View File

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

View File

@ -1,20 +0,0 @@
<vn-crud-model
vn-id="model"
url="Routes/filter"
limit="20"
order="created DESC, id DESC">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
vn-focus
panel="vn-route-search-panel"
info="Search routes by id"
fetch-params="$ctrl.fetchParams($params)"
filter="$ctrl.filterParams"
model="model">
</vn-searchbar>
</vn-portal>
<vn-portal slot="menu">
<vn-left-menu></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -2,36 +2,12 @@ import ngModule from '../module';
import ModuleMain from 'salix/components/module-main';
export default class Route extends ModuleMain {
$postLink() {
const to = Date.vnNew();
to.setDate(to.getDate() + 1);
to.setHours(0, 0, 0, 0);
const from = Date.vnNew();
from.setDate(from.getDate());
from.setHours(0, 0, 0, 0);
this.filterParams = {from, to};
this.$.model.applyFilter(null, this.filterParams);
constructor($element, $) {
super($element, $);
}
fetchParams($params) {
const hasEntries = Object.entries($params).length;
if (!hasEntries)
$params.scopeDays = 1;
if (typeof $params.scopeDays === 'number') {
const from = Date.vnNew();
from.setHours(0, 0, 0, 0);
const to = new Date(from.getTime());
to.setDate(to.getDate() + $params.scopeDays);
to.setHours(23, 59, 59, 999);
Object.assign($params, {from, to});
}
return $params;
async $onInit() {
this.$state.go('home');
window.location.href = await this.vnApp.getUrl(`route/`);
}
}

View File

@ -1,49 +0,0 @@
import './index.js';
describe('Route Component vnRoute', () => {
let controller;
beforeEach(ngModule('route'));
beforeEach(inject($componentController => {
let $element = angular.element(`<div></div>`);
controller = $componentController('vnRoute', {$element});
}));
describe('fetchParams()', () => {
it('should return a range of dates with passed scope days', () => {
let params = controller.fetchParams({
scopeDays: 2
});
const from = Date.vnNew();
from.setHours(0, 0, 0, 0);
const to = new Date(from.getTime());
to.setDate(to.getDate() + params.scopeDays);
to.setHours(23, 59, 59, 999);
const expectedParams = {
from,
scopeDays: params.scopeDays,
to
};
expect(params).toEqual(expectedParams);
});
it('should return default value for scope days', () => {
let params = controller.fetchParams({
scopeDays: 1
});
expect(params.scopeDays).toEqual(1);
});
it('should return the given scope days', () => {
let params = controller.fetchParams({
scopeDays: 2
});
expect(params.scopeDays).toEqual(2);
});
});
});

View File

@ -1,98 +0,0 @@
<mg-ajax path="Roadmaps/{{patch.params.id}}" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.roadmap"
form="form"
save="patch">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield vn-focus
vn-one
label="Roadmap"
ng-model="$ctrl.roadmap.name"
rule>
</vn-textfield>
<vn-date-picker
vn-one
label="ETD date"
ng-model="$ctrl.roadmap.etd">
</vn-date-picker>
<vn-input-time
vn-one
label="ETD hour"
ng-model="$ctrl.roadmap.etd">
</vn-input-time>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Tractor plate"
ng-model="$ctrl.roadmap.tractorPlate"
rule>
</vn-textfield>
<vn-textfield
vn-one
label="Trailer plate"
ng-model="$ctrl.roadmap.trailerPlate"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
ng-model="$ctrl.roadmap.supplierFk"
url="Suppliers"
show-field="nickname"
search-function="{or: [{id: $search}, {nickname: {like: '%'+ $search +'%'}}]}"
value-field="id"
order="nickname"
label="Carrier">
<tpl-item>
{{::id}} - {{::nickname}}
</tpl-item>
</vn-autocomplete>
<vn-input-number
vn-one
label="Price"
ng-model="$ctrl.roadmap.price"
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Driver name"
ng-model="$ctrl.roadmap.driverName"
rule>
</vn-textfield>
<vn-textfield
vn-one
label="Phone"
ng-model="$ctrl.roadmap.phone"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textArea
vn-one
label="Observations"
ng-model="$ctrl.roadmap.observations"
rule>
</vn-textArea>
</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>

View File

@ -1,16 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
onSubmit() {
this.$.watcher.submit();
}
}
ngModule.component('vnRoadmapBasicData', {
template: require('./index.html'),
controller: Controller,
bindings: {
roadmap: '<'
}
});

View File

@ -1,5 +0,0 @@
<vn-portal slot="menu">
<vn-roadmap-descriptor roadmap="$ctrl.roadmap"></vn-roadmap-descriptor>
<vn-left-menu source="roadmap"></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -1,19 +0,0 @@
import ngModule from '../../module';
import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard {
reload() {
const filter = {
include: [
{relation: 'supplier'}
]
};
this.$http.get(`Roadmaps/${this.$params.id}`, {filter})
.then(res => this.roadmap = res.data);
}
}
ngModule.vnComponent('vnRoadmapCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,37 +0,0 @@
<vn-watcher
vn-id="watcher"
url="Roadmaps"
data="$ctrl.roadmap"
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-horizontal>
<vn-textfield
label="Roadmap"
ng-model="$ctrl.roadmap.name"
rule>
</vn-textfield>
<vn-date-picker
label="ETD date"
ng-model="$ctrl.roadmap.etd">
</vn-date-picker>
<vn-input-time
label="ETD hour"
ng-model="$ctrl.roadmap.etd">
</vn-input-time>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Create">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="roadmap.index">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,23 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
constructor($element, $, $transclude, vnReport, vnEmail) {
super($element, $, $transclude);
this.roadmap = {etd: Date.vnNew()};
}
onSubmit() {
this.$.watcher.submit().then(
res => this.$state.go('route.roadmap.card.summary', {id: res.data.id})
);
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnRoadmapCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,6 +0,0 @@
vn-ticket-request {
.vn-textfield {
margin: 0!important;
max-width: 100px;
}
}

View File

@ -1,39 +0,0 @@
<vn-descriptor-content
module="route"
base-state="route.roadmap"
description="$ctrl.roadmap.name">
<slot-menu>
<vn-item
ng-click="deleteRoadmap.show()"
name="deleteRoadmap"
translate>
Delete roadmap
</vn-item>
</slot-menu>
<slot-body>
<div class="attributes">
<vn-label-value
label="Roadmap"
value="{{$ctrl.roadmap.name}}">
</vn-label-value>
<vn-label-value
label="ETD"
value="{{$ctrl.roadmap.etd | date:'dd/MM/yyyy HH:mm'}}">
</vn-label-value>
<vn-label-value label="Carrier">
<span ng-click="supplierDescriptor.show($event, $ctrl.roadmap.supplier.id)" class="link">
{{$ctrl.roadmap.supplier.nickname}}
</span>
</vn-label-value>
</div>
</slot-body>
</vn-descriptor-content>
<vn-confirm
vn-id="deleteRoadmap"
on-accept="$ctrl.onDelete()"
question="Are you sure you want to continue?"
message="The roadmap will be removed">
</vn-confirm>
<vn-supplier-descriptor-popover
vn-id="supplierDescriptor">
</vn-supplier-descriptor-popover>

View File

@ -1,26 +0,0 @@
import ngModule from '../../module';
import Descriptor from 'salix/components/descriptor';
class Controller extends Descriptor {
get roadmap() {
return this.entity;
}
set roadmap(value) {
this.entity = value;
}
onDelete() {
return this.$http.delete(`Roadmaps/${this.roadmap.id}`)
.then(() => this.$state.go('route.roadmap'))
.then(() => this.vnApp.showSuccess(this.$t('Roadmap removed')));
}
}
ngModule.component('vnRoadmapDescriptor', {
template: require('./index.html'),
controller: Controller,
bindings: {
roadmap: '<'
}
});

View File

@ -1,3 +0,0 @@
Delete roadmap: Eliminar troncal
The roadmap will be removed: La troncal será eliminada
Roadmap removed: Troncal eliminada

View File

@ -1,9 +0,0 @@
import './main';
import './index/';
import './summary';
import './card';
import './descriptor';
import './create';
import './basic-data';
import './search-panel';
import './stops';

View File

@ -1,112 +0,0 @@
<vn-auto-search
model="model">
</vn-auto-search>
<vn-data-viewer
model="model"
class="vn-w-lg">
<vn-card class="vn-pa-md vn-w-lg">
<vn-tool-bar class="vn-ma-md">
<vn-button
disabled="$ctrl.totalChecked == 0"
ng-click="$ctrl.openClonationDialog()"
icon="icon-clone"
vn-tooltip="Clone selected roadmaps">
</vn-button>
<vn-button
disabled="$ctrl.totalChecked == 0"
ng-click="deleteRoadmaps.show()"
vn-tooltip="Delete roadmap(s)"
icon="delete">
</vn-button>
</vn-tool-bar>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th shrink>
<vn-multi-check
model="model">
</vn-multi-check>
</vn-th>
<vn-th field="description">Roadmap</vn-th>
<vn-th field="etd" expand date>ETD</vn-th>
<vn-th field="supplierFk">Carrier</vn-th>
<vn-th field="plate">Plate</vn-th>
<vn-th field="price">Price</vn-th>
<vn-th field="observations" expand>Observations</vn-th>
<vn-th></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<a ng-repeat="roadmap in model.data"
class="clickable vn-tr search-result"
ui-sref="route.roadmap.card.summary({id: {{::roadmap.id}}})">
<vn-td>
<vn-check
ng-model="roadmap.checked"
vn-click-stop>
</vn-check>
</vn-td>
<vn-td>{{::roadmap.name}}</vn-td>
<vn-td expand date>{{::roadmap.etd | date:'dd/MM/yyyy HH:mm'}}</vn-td>
<vn-td expand>
<span
class="link"
vn-click-stop="supplierDescriptor.show($event, roadmap.supplierFk)">
{{::roadmap.supplier.nickname}}
</span>
</vn-td>
<vn-td>{{::roadmap.tractorPlate | dashIfEmpty}}</vn-td>
<vn-td expand>{{::roadmap.price | currency: 'EUR':2 | dashIfEmpty}}</vn-td>
<vn-td expand>{{::roadmap.observations | dashIfEmpty}}</vn-td>
<vn-td shrink>
<vn-icon-button
vn-click-stop="$ctrl.preview(roadmap)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-td>
</a>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<a
ui-sref="route.roadmap.create"
vn-tooltip="Create roadmap"
vn-bind="+"
fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>
<vn-popup vn-id="summary">
<vn-roadmap-summary
roadmap="$ctrl.roadmapSelected">
</vn-roadmap-summary>
</vn-popup>
<vn-supplier-descriptor-popover
vn-id="supplierDescriptor">
</vn-supplier-descriptor-popover>
<!-- Clonation dialog -->
<vn-dialog class="edit"
vn-id="clonationDialog"
on-accept="$ctrl.cloneSelectedRoadmaps()"
message="Select the estimated time of departure (ETD)">
<tpl-body>
<vn-horizontal>
<vn-date-picker
label="ETD"
ng-model="$ctrl.etd">
</vn-date-picker>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Clone</button>
</tpl-buttons>
</vn-dialog>
<vn-confirm
vn-id="deleteRoadmaps"
question="Are you sure you want to continue?"
message="Selected roadmaps will be removed"
on-accept="$ctrl.deleteRoadmaps()">
</vn-confirm>

View File

@ -1,60 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
class Controller extends Section {
get checked() {
const roadmaps = this.$.model.data || [];
const checkedRoadmap = [];
for (let roadmap of roadmaps) {
if (roadmap.checked)
checkedRoadmap.push(roadmap);
}
return checkedRoadmap;
}
get totalChecked() {
return this.checked.length;
}
preview(roadmap) {
this.roadmapSelected = roadmap;
this.$.summary.show();
}
openClonationDialog() {
this.$.clonationDialog.show();
this.etd = Date.vnNew();
}
cloneSelectedRoadmaps() {
try {
if (!this.etd)
throw new Error(`The date can't be empty`);
const roadmapsIds = [];
for (let roadmap of this.checked)
roadmapsIds.push(roadmap.id);
return this.$http.post('Roadmaps/clone', {ids: roadmapsIds, etd: this.etd}).then(() => {
this.$.model.refresh();
this.vnApp.showSuccess(this.$t('Data saved!'));
});
} catch (e) {
this.vnApp.showError(this.$t(e.message));
}
}
deleteRoadmaps() {
for (const roadmap of this.checked) {
this.$http.delete(`Roadmaps/${roadmap.id}`)
.then(() => this.$.model.refresh())
.then(() => this.vnApp.showSuccess(this.$t('Roadmaps removed')));
}
}
}
ngModule.vnComponent('vnRoadmapIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,3 +0,0 @@
Delete roadmap(s): Eliminar troncal(es)
Selected roadmaps will be removed: Los troncales seleccionados serán eliminados
Roadmaps removed: Troncales eliminados

View File

@ -1,14 +0,0 @@
Roadmaps: Troncales
Roadmap: Troncal
Driver name: Nombre conductor
Plate: Matrícula
Price: Precio
Observations: Observaciones
Clone selected roadmaps: Clonar troncales seleccionadas
Select the estimated time of departure (ETD): Seleccione la hora estimada de salida (ETD)
Create roadmap: Crear troncal
Tractor plate: Matrícula tractor
Trailer plate: Matrícula trailer
Carrier: Transportista
ETD date: Fecha ETD
ETD hour: Hora ETD

View File

@ -1,20 +0,0 @@
<vn-crud-model
vn-id="model"
url="Roadmaps"
include="$ctrl.include"
auto-load="true"
limit="20">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
info="Search roadmap by id or trunk"
panel="vn-roadmap-search-panel"
model="model"
filter="$ctrl.filterParams"
expr-builder="$ctrl.exprBuilder(param, value)"
base-state="route.roadmap">
</vn-searchbar>
</vn-portal>
<ui-view>
<vn-roadmap-index></vn-roadmap-index>
</ui-view>

View File

@ -1,61 +0,0 @@
import ngModule from '../../module';
import ModuleMain from 'salix/components/module-main';
export default class Roadmap extends ModuleMain {
constructor($element, $) {
super($element, $);
this.include = {
relation: 'supplier',
scope: {
fields: ['nickname']
}
};
}
$postLink() {
const from = Date.vnNew();
from.setHours(0, 0, 0, 0);
const to = Date.vnNew();
to.setHours(23, 59, 59, 999);
this.filterParams = {
from: from,
to: to
};
this.$.model.addFilter({where: {
and: [
{etd: {gte: from}},
{etd: {lte: to}}
]
}});
}
exprBuilder(param, value) {
switch (param) {
case 'search':
return /^\d+$/.test(value)
? {id: value}
: {name: {like: `%${value}%`}};
case 'from':
return {etd: {gte: value}};
case 'to':
return {etd: {lte: value}};
case 'supplierFk':
case 'price':
return {[param]: value};
case 'tractorPlate':
case 'trailerPlate':
case 'phone':
case 'driverName':
return {[param]: {like: `%${value}%`}};
}
}
}
ngModule.vnComponent('vnRoadmap', {
controller: Roadmap,
template: require('./index.html')
});

View File

@ -1 +0,0 @@
Search roadmap by id or trunk: Buscar troncales por id o troncal

View File

@ -1,74 +0,0 @@
<div class="search-panel">
<form id="manifold-form" ng-submit="$ctrl.onSearch()">
<vn-horizontal class="vn-px-lg vn-pt-lg">
<vn-textfield
vn-one
label="General search"
ng-model="filter.search"
info="Search routes by id"
vn-focus>
</vn-textfield>
</vn-horizontal>
<section class="vn-px-md">
<vn-horizontal class="manifold-panel vn-pa-md">
<vn-date-picker
vn-one
label="From"
ng-model="filter.from">
</vn-date-picker>
<vn-date-picker
vn-one
label="To"
ng-model="filter.to">
</vn-date-picker>
</vn-horizontal>
</section>
<vn-horizontal class="vn-px-lg">
<vn-textfield
vn-one
label="Tractor plate"
ng-model="filter.tractorPlate">
</vn-textfield>
<vn-textfield
vn-one
label="Trailer plate"
ng-model="filter.trailerPlate">
</vn-textfield>
</vn-horizontal>
<vn-horizontal class="vn-px-lg">
<vn-autocomplete
vn-one
ng-model="filter.supplierFk"
url="Suppliers"
show-field="nickname"
search-function="{or: [{id: $search}, {nickname: {like: '%'+ $search +'%'}}]}"
value-field="id"
order="nickname"
label="Carrier">
<tpl-item>
{{::id}} - {{::nickname}}
</tpl-item>
</vn-autocomplete>
<vn-input-number
vn-one
label="Price"
ng-model="filter.price">
</vn-input-number>
</vn-horizontal>
<vn-horizontal class="vn-px-lg">
<vn-textfield
vn-one
label="Driver name"
ng-model="filter.driverName">
</vn-textfield>
<vn-textfield
vn-one
label="Phone"
ng-model="filter.phone">
</vn-textfield>
</vn-horizontal>
<vn-horizontal class="vn-px-lg vn-pb-lg vn-mt-lg">
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -1,7 +0,0 @@
import ngModule from '../../module';
import SearchPanel from 'core/components/searchbar/search-panel';
ngModule.component('vnRoadmapSearchPanel', {
template: require('./index.html'),
controller: SearchPanel
});

View File

@ -1,71 +0,0 @@
<vn-crud-model
vn-id="model"
url="RoadmapStops"
where="{roadmapFk: $ctrl.$params.id}"
order="eta ASC"
data="$ctrl.roadmapStops"
auto-load="true">
</vn-crud-model>
<vn-watcher
vn-id="watcher"
data="$ctrl.roadmapStops"
form="form">
</vn-watcher>
<form class="vn-w-md" name="form" ng-submit="$ctrl.onSubmit()">
<vn-card class="vn-pa-lg">
<vn-horizontal ng-repeat="roadmapStop in $ctrl.roadmapStops">
<vn-autocomplete vn-one
label="Warehouse"
ng-model="roadmapStop.warehouseFk"
url="Warehouses"
show-field="name"
value-field="id"
vn-focus
rule>
</vn-autocomplete>
<vn-date-picker vn-one
label="ETA date"
ng-model="roadmapStop.eta"
rule>
</vn-date-picker>
<vn-input-time
vn-one
label="ETA hour"
ng-model="roadmapStop.eta">
</vn-input-time>
<vn-textArea
vn-one
label="Description"
ng-model="roadmapStop.description"
rule>
</vn-textArea>
<vn-none>
<vn-icon-button
vn-tooltip="Remove stop"
icon="delete"
ng-click="model.remove($index)"
tabindex="-1">
</vn-icon-button>
</vn-none>
</vn-horizontal>
<vn-one>
<vn-icon-button
vn-bind="+"
vn-tooltip="Add stop"
icon="add_circle"
ng-click="$ctrl.add()">
</vn-icon-button>
</vn-one>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
</vn-button-bar>
</form>
<vn-confirm
vn-id="confirm"
question="Delete stop?"
on-accept="$ctrl.removeTicketFromRoute($index)">
</vn-confirm>

View File

@ -1,39 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
add() {
const filter = {
fields: ['etd']
};
this.$http.get(`Roadmaps/${this.$params.id}`, {filter})
.then(res => {
this.roadmap = res.data;
const eta = new Date(this.roadmap.etd);
eta.setDate(eta.getDate() + 1);
this.$.model.insert({
roadmapFk: this.$params.id,
eta: eta
});
});
}
onSubmit() {
this.$.watcher.check();
this.$.model.save().then(() => {
this.$.watcher.notifySaved();
this.$.watcher.updateOriginalData();
this.$.model.refresh();
});
}
}
ngModule.component('vnRoadmapStops', {
template: require('./index.html'),
controller: Controller,
bindings: {
roadmap: '<'
}
});

View File

@ -1,4 +0,0 @@
Remove stop: Eliminar parada
Add stop: Añadir parada
ETA date: Fecha ETA
ETA hour: Hora ETA

View File

@ -1,116 +0,0 @@
<vn-card class="summary">
<h5>
<span>{{summary.id}} - {{summary.name}}</span>
</h5>
<vn-horizontal class="vn-pa-md">
<vn-one>
<vn-label-value label="Carrier">
<span ng-click="supplierDescriptor.show($event, summary.supplier.id)" class="link">
{{summary.supplier.nickname}}
</span>
</vn-label-value>
<vn-label-value
label="ETD"
value="{{summary.etd | date:'dd/MM/yyyy HH:mm'}}">
</vn-label-value>
<vn-label-value
label="Tractor plate"
value="{{summary.tractorPlate}}">
</vn-label-value>
<vn-label-value
label="Trailer plate"
value="{{summary.trailerPlate}}">
</vn-label-value>
</vn-one>
<vn-one>
<vn-label-value
label="Phone"
>
<vn-link-phone
phone-number="summary.phone"
></vn-link-phone>
</vn-label-value>
<vn-label-value
label="Worker"
value="{{summary.worker.firstName}} {{summary.worker.lastName}}">
</vn-label-value>
<vn-label-value
label="Observations"
value="{{summary.observations}}">
</vn-label-value>
</vn-one>
<vn-auto>
<h4>
<a
ui-sref="route.roadmap.card.stops({id:summary.id})"
target="_self">
<span translate vn-tooltip="Go to">Stops</span>
<vn-icon-button
vn-bind="+"
vn-tooltip="Add stop"
icon="add_circle"
vn-click-stop="addRoadmapStop.show()">
</vn-icon-button>
</a>
</h4>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th>Wharehouse</vn-th>
<vn-th>ETA</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="roadmapStop in summary.roadmapStop">
<vn-td>{{roadmapStop.warehouse.name}}</vn-td>
<vn-td expand>{{roadmapStop.eta | date:'dd/MM/yyyy HH:mm'}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-auto>
</vn-horizontal>
</vn-card>
<vn-supplier-descriptor-popover
vn-id="supplierDescriptor">
</vn-supplier-descriptor-popover>
<vn-dialog
vn-id="addRoadmapStop"
on-open="$ctrl.getETD()"
on-accept="$ctrl.onAddAccept()">
<tpl-body>
<vn-horizontal>
<vn-autocomplete
label="Warehouse"
ng-model="$ctrl.roadmapStop.warehouseFk"
url="Warehouses"
show-field="name"
value-field="id"
vn-focus
rule>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-date-picker
label="ETA date"
ng-model="$ctrl.roadmapStop.eta"
rule>
</vn-date-picker>
<vn-input-time
label="ETA hour"
ng-model="$ctrl.roadmapStop.eta">
</vn-input-time>
</vn-horizontal>
<vn-horizontal>
<vn-textArea
label="Description"
ng-model="$ctrl.roadmapStop.description"
rule>
</vn-textArea>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Confirm</button>
</tpl-buttons>
</vn-dialog>

View File

@ -1,68 +0,0 @@
import ngModule from '../../module';
import Component from 'core/lib/component';
import './style.scss';
class Controller extends Component {
set roadmap(value) {
this._roadmap = value;
this.$.summary = null;
if (!value) return;
this.loadData();
}
get roadmap() {
return this._roadmap;
}
loadData() {
const filter = {
include: [
{relation: 'supplier'},
{relation: 'worker'},
{relation: 'roadmapStop',
scope: {
include: [
{relation: 'warehouse'}
]
}}
]
};
this.$http.get(`Roadmaps/${this.roadmap.id}`, {filter})
.then(res => this.$.summary = res.data);
}
getETD() {
const eta = new Date(this.roadmap.etd);
eta.setDate(eta.getDate() + 1);
this.roadmapStop = {eta: eta};
}
onAddAccept() {
try {
const data = {
roadmapFk: this.roadmap.id,
warehouseFk: this.roadmapStop.warehouseFk,
eta: this.roadmapStop.eta,
description: this.roadmapStop.description
};
this.$http.post(`RoadmapStops`, data)
.then(() => {
this.loadData();
this.vnApp.showSuccess(this.$t('Data saved!'));
});
} catch (e) {
this.vnApp.showError(this.$t(e.message));
}
}
}
ngModule.component('vnRoadmapSummary', {
template: require('./index.html'),
controller: Controller,
bindings: {
roadmap: '<'
}
});

View File

@ -1,3 +0,0 @@
Stops: Paradas
Wharehouse: Almacén
You must fill all the fields: Debes rellenar todos los campos

View File

@ -1,9 +0,0 @@
@import "variables";
vn-roadmap-summary .summary {
a:not(vn-link-phone a) {
display: flex;
align-items: center;
height: 18.328px;
}
}

View File

@ -6,18 +6,7 @@
"dependencies": ["client", "worker", "ticket", "supplier", "invoiceIn"],
"menus": {
"main": [
{"state": "route.index", "icon": "icon-delivery"},
{"state": "route.agencyTerm.index", "icon": "icon-agency-term"},
{"state": "route.roadmap", "icon": "icon-trailer"}
],
"card": [
{"state": "route.card.basicData", "icon": "settings"},
{"state": "route.card.tickets", "icon": "icon-ticket"},
{"state": "route.card.log", "icon": "history"}
],
"roadmap": [
{"state": "route.roadmap.card.basicData", "icon": "settings"},
{"state": "route.roadmap.card.stops", "icon": "icon-lines"}
{"state": "route.index", "icon": "icon-delivery"}
]
},
"routes": [
@ -33,108 +22,6 @@
"state": "route.index",
"component": "vn-route-index",
"description": "Routes"
}, {
"url": "/create",
"state": "route.create",
"component": "vn-route-create",
"description": "New route"
}, {
"url": "/:id",
"state": "route.card",
"abstract": true,
"component": "vn-route-card"
}, {
"url": "/agency-term?q",
"abstract": true,
"state": "route.agencyTerm",
"component": "ui-view"
}, {
"url": "/index",
"state": "route.agencyTerm.index",
"component": "vn-agency-term-index",
"description": "Autonomous",
"acl": ["administrative"]
},{
"url": "/createInvoiceIn?q",
"state": "route.agencyTerm.createInvoiceIn",
"component": "vn-agency-term-create-invoice-in",
"description": "File management",
"params": {
"route": "$ctrl.route"
},
"acl": ["administrative"]
}, {
"url": "/summary",
"state": "route.card.summary",
"component": "vn-route-summary",
"description": "Summary",
"params": {
"route": "$ctrl.route"
}
}, {
"url": "/basic-data",
"state": "route.card.basicData",
"component": "vn-route-basic-data",
"description": "Basic data",
"params": {
"route": "$ctrl.route"
},
"acl": ["delivery"]
}, {
"url" : "/log",
"state": "route.card.log",
"component": "vn-route-log",
"description": "Log",
"acl": ["delivery"]
}, {
"url": "/tickets",
"state": "route.card.tickets",
"component": "vn-route-tickets",
"description": "Tickets",
"params": {
"route": "$ctrl.route"
},
"acl": ["delivery"]
}, {
"url": "/roadmap?q",
"state": "route.roadmap",
"component": "vn-roadmap",
"description": "Roadmaps"
}, {
"url": "/create",
"state": "route.roadmap.create",
"component": "vn-roadmap-create",
"description": "Create roadmap"
},{
"url": "/:id",
"state": "route.roadmap.card",
"component": "vn-roadmap-card",
"abstract": true,
"description": "Detail"
},{
"url": "/summary",
"state": "route.roadmap.card.summary",
"component": "vn-roadmap-summary",
"description": "Summary",
"params": {
"roadmap": "$ctrl.roadmap"
}
},{
"url": "/basic-data",
"state": "route.roadmap.card.basicData",
"component": "vn-roadmap-basic-data",
"description": "Basic data",
"params": {
"roadmap": "$ctrl.roadmap"
}
}, {
"url": "/stops",
"state": "route.roadmap.card.stops",
"component": "vn-roadmap-stops",
"description": "Stops",
"params": {
"route": "$ctrl.roadmap"
}
}
]
}

View File

@ -1,96 +0,0 @@
<div class="search-panel">
<vn-crud-model
auto-load="true"
url="Warehouses"
data="warehouses">
</vn-crud-model>
<form id="manifold-form" ng-submit="$ctrl.onSearch()">
<vn-horizontal class="vn-px-lg vn-pt-lg">
<vn-textfield
vn-one
label="General search"
ng-model="filter.search"
info="Search routes by id"
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal class="vn-px-lg">
<vn-worker-autocomplete
vn-one
ng-model="filter.workerFk"
show-field="nickname">
</vn-worker-autocomplete>
<vn-autocomplete
vn-one
label="Agency"
ng-model="filter.agencyModeFk"
url="AgencyModes/isActive"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<section class="vn-px-md">
<vn-horizontal class="manifold-panel vn-pa-md">
<vn-date-picker
vn-one
label="From"
ng-model="filter.from"
on-change="$ctrl.from = value">
</vn-date-picker>
<vn-date-picker
vn-one
label="To"
ng-model="filter.to"
on-change="$ctrl.to = value">
</vn-date-picker>
<vn-none class="or vn-px-md" translate>Or</vn-none>
<vn-input-number
vn-one
min="0"
step="1"
label="Days onward"
ng-model="filter.scopeDays"
on-change="$ctrl.scopeDays = value"
display-controls="true">
</vn-input-number>
<vn-icon color-marginal
icon="info"
vn-tooltip="Cannot choose a range of dates and days onward at the same time">
</vn-icon>
</vn-horizontal>
</section>
<vn-horizontal class="vn-px-lg">
<vn-autocomplete
vn-one
label="Vehicle"
ng-model="filter.vehicleFk"
url="Vehicles"
show-field="numberPlate"
value-field="id">
</vn-autocomplete>
<vn-textfield
vn-one
label="m³"
ng-model="filter.m3">
</vn-textfield>
</vn-horizontal>
<vn-horizontal class="vn-px-lg">
<vn-autocomplete
vn-one
label="Warehouse"
ng-model="filter.warehouseFk"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-textfield
vn-one
label="Description"
ng-model="filter.description">
</vn-textfield>
</vn-horizontal>
<vn-horizontal class="vn-px-lg vn-pb-lg vn-mt-lg">
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -1,43 +0,0 @@
import ngModule from '../module';
import SearchPanel from 'core/components/searchbar/search-panel';
class Controller extends SearchPanel {
constructor($, $element) {
super($, $element);
this.filter = this.$.filter;
}
get from() {
return this._from;
}
set from(value) {
this._from = value;
this.filter.scopeDays = null;
}
get to() {
return this._to;
}
set to(value) {
this._to = value;
this.filter.scopeDays = null;
}
get scopeDays() {
return this._scopeDays;
}
set scopeDays(value) {
this._scopeDays = value;
this.filter.from = null;
this.filter.to = null;
}
}
ngModule.vnComponent('vnRouteSearchPanel', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,48 +0,0 @@
import './index';
describe('Route Component vnRouteSearchPanel', () => {
let controller;
beforeEach(ngModule('route'));
beforeEach(inject($componentController => {
controller = $componentController('vnRouteSearchPanel', {$element: null});
controller.$t = () => {};
controller.filter = {};
}));
describe('from() setter', () => {
it('should clear the scope days when setting the from property', () => {
controller.filter.scopeDays = 1;
controller.from = Date.vnNew();
expect(controller.filter.scopeDays).toBeNull();
expect(controller.from).toBeDefined();
});
});
describe('to() setter', () => {
it('should clear the scope days when setting the to property', () => {
controller.filter.scopeDays = 1;
controller.to = Date.vnNew();
expect(controller.filter.scopeDays).toBeNull();
expect(controller.to).toBeDefined();
});
});
describe('scopeDays() setter', () => {
it('should clear the date range when setting the scopeDays property', () => {
controller.filter.from = Date.vnNew();
controller.filter.to = Date.vnNew();
controller.scopeDays = 1;
expect(controller.filter.from).toBeNull();
expect(controller.filter.to).toBeNull();
expect(controller.scopeDays).toBeDefined();
});
});
});

View File

@ -1,36 +0,0 @@
<vn-dialog
vn-id="SMSDialog"
on-accept="$ctrl.onResponse()"
message="Send SMS to the selected tickets">
<tpl-body>
<section class="SMSDialog">
<vn-horizontal>
<vn-textarea vn-one
vn-id="message"
label="Message"
ng-model="$ctrl.sms.message"
info="Special characters like accents counts as a multiple"
rows="5"
required="true"
rule>
</vn-textarea>
</vn-horizontal>
<vn-horizontal>
<span>
{{'Characters remaining' | translate}}:
<vn-chip translate-attr="{title: 'Packing'}" ng-class="{
'colored': $ctrl.charactersRemaining() > 25,
'warning': $ctrl.charactersRemaining() <= 25,
'alert': $ctrl.charactersRemaining() < 0,
}">
{{$ctrl.charactersRemaining()}}
</vn-chip>
</span>
</vn-horizontal>
</section>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Send</button>
</tpl-buttons>
</vn-dialog>

View File

@ -1,47 +0,0 @@
import ngModule from '../module';
import Component from 'core/lib/component';
import './style.scss';
class Controller extends Component {
open() {
this.$.SMSDialog.show();
}
charactersRemaining() {
const element = this.$.message;
const value = element.input.value;
const maxLength = 160;
const textAreaLength = new Blob([value]).size;
return maxLength - textAreaLength;
}
onResponse() {
try {
if (!this.sms.destination)
throw new Error(`The destination can't be empty`);
if (!this.sms.message)
throw new Error(`The message can't be empty`);
if (this.charactersRemaining() < 0)
throw new Error(`The message it's too long`);
this.$http.post(`Routes/sendSms`, this.sms).then(res => {
this.vnApp.showMessage(this.$t('SMS sent'));
if (res.data) this.emit('send', {response: res.data});
});
} catch (e) {
this.vnApp.showError(this.$t(e.message));
return false;
}
return true;
}
}
ngModule.vnComponent('vnRouteSms', {
template: require('./index.html'),
controller: Controller,
bindings: {
sms: '<',
}
});

View File

@ -1,71 +0,0 @@
import './index';
describe('Route', () => {
describe('Component vnRouteSms', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('route'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
let $scope = $rootScope.$new();
const $element = angular.element('<vn-dialog></vn-dialog>');
controller = $componentController('vnRouteSms', {$element, $scope});
controller.$.message = {
input: {
value: 'My SMS'
}
};
}));
describe('onResponse()', () => {
it('should perform a POST query and show a success snackbar', () => {
let params = {destinationFk: 1101, destination: 111111111, message: 'My SMS'};
controller.sms = {destinationFk: 1101, destination: 111111111, message: 'My SMS'};
jest.spyOn(controller.vnApp, 'showMessage');
$httpBackend.expect('POST', `Routes/sendSms`, params).respond(200, params);
controller.onResponse();
$httpBackend.flush();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('SMS sent');
});
it('should call onResponse without the destination and show an error snackbar', () => {
controller.sms = {destinationFk: 1101, message: 'My SMS'};
jest.spyOn(controller.vnApp, 'showError');
controller.onResponse();
expect(controller.vnApp.showError).toHaveBeenCalledWith(`The destination can't be empty`);
});
it('should call onResponse without the message and show an error snackbar', () => {
controller.sms = {destinationFk: 1101, destination: 222222222};
jest.spyOn(controller.vnApp, 'showError');
controller.onResponse();
expect(controller.vnApp.showError).toHaveBeenCalledWith(`The message can't be empty`);
});
});
describe('charactersRemaining()', () => {
it('should return the characters remaining in a element', () => {
controller.$.message = {
input: {
value: 'My message 0€'
}
};
let result = controller.charactersRemaining();
expect(result).toEqual(145);
});
});
});
});

Some files were not shown because too many files have changed in this diff Show More