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

View File

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

View File

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

View File

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

View File

@ -10,9 +10,6 @@ SET foreign_key_checks = 0;
INSERT INTO util.config (id, environment, mockTime, mockUtcTime, mockEnabled) 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); 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 /* #5483
INSERT INTO vn.entryConfig (defaultEntry, mailToNotify, inventorySupplierFk, maxLockTime, defaultSupplierFk) INSERT INTO vn.entryConfig (defaultEntry, mailToNotify, inventorySupplierFk, maxLockTime, defaultSupplierFk)
VALUES(1, NULL, 1, 300, 1); 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, ''), (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, ''); (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 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'), ('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'),
('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'), ('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'),
('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'), ('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'),
('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'), ('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'),
('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'), ('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'),
('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'), ('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'),
('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'), ('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'),
('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'), ('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'),
('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'), ('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'),
('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'), ('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'),
('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'), ('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'),
('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'), ('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'),
('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'), ('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'),
('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'), ('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'),
('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'); ('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`) 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 VALUES
@ -1968,7 +1968,7 @@ INSERT INTO `vn`.`orderTicket`(`orderFk`, `ticketFk`)
(21, 21), (21, 21),
(22, 22); (22, 22);
INSERT INTO `vn`.`userConfig` (`userFk`, `warehouseFk`, `companyFk`) INSERT INTO `vn`.`userMultiConfig` (`userFk`, `warehouseFk`, `companyFk`)
VALUES VALUES
(1, 1, 69), (1, 1, 69),
(5, 1, 442), (5, 1, 442),

View File

@ -1,31 +1,50 @@
DELIMITER $$ DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `bs`.`waste_addSales`() CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `bs`.`waste_addSales`()
BEGIN BEGIN
DECLARE vWeek INT; DECLARE vDateFrom DATE DEFAULT util.VN_CURDATE() - INTERVAL WEEKDAY(CURDATE()) DAY;
DECLARE vYear INT; DECLARE vDateTo DATE DEFAULT vDateFrom + INTERVAL 6 DAY;
CALL cache.last_buy_refresh(FALSE);
SELECT week, year
INTO vWeek, vYear
FROM vn.time
WHERE dated = util.VN_CURDATE();
REPLACE bs.waste REPLACE bs.waste
SELECT *, 100 * mermas / total as porcentaje SELECT YEAR(t.shipped),
FROM ( WEEK(t.shipped, 4),
SELECT buyer, it.workerFk,
year, it.id,
week, s.itemFk,
family, SUM(s.quantity),
itemFk, SUM((b.buyingValue + b.freightValue + b.comissionValue + b.packageValue) * s.quantity) `value`,
itemTypeFk, SUM (
floor(sum(value)) as total, IF(
floor(sum(IF(typeFk = 'loses', value, 0))) as mermas aw.`type` = 'internal',
FROM vn.saleValue (b.buyingValue + b.freightValue + b.comissionValue + b.packageValue) * s.quantity,
where year = vYear and week = vWeek 0
)
GROUP BY family, itemFk ) internalWaste,
SUM (
) sub IF(
ORDER BY mermas DESC; 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$$ END$$
DELIMITER ; DELIMITER ;

View File

@ -10,5 +10,5 @@ AS SELECT `q`.`name` AS `name`,
`c`.`ringInUse` AS `ringinuse` `c`.`ringInUse` AS `ringinuse`
FROM ( FROM (
`pbx`.`queue` `q` `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) SELECT IFNULL(uc.companyFk, rc.defaultCompanyFk)
INTO vCompanyFk INTO vCompanyFk
FROM vn.routeConfig rc FROM vn.routeConfig rc
LEFT JOIN userConfig uc ON uc.userFk = workerFk; LEFT JOIN userMultiConfig uc ON uc.userFk = workerFk;
SELECT SELECT

View File

@ -12,6 +12,7 @@ BEGIN
DECLARE vInvoiceFk INT; DECLARE vInvoiceFk INT;
DECLARE vBookEntry INT; DECLARE vBookEntry INT;
DECLARE vFiscalYear INT; DECLARE vFiscalYear INT;
DECLARE vIncorrectInvoiceInDueDay INT;
DECLARE vInvoicesIn CURSOR FOR DECLARE vInvoicesIn CURSOR FOR
SELECT DISTINCT e.invoiceInFk SELECT DISTINCT e.invoiceInFk
@ -24,6 +25,19 @@ BEGIN
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE; 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 UPDATE invoiceIn ii
JOIN entry e ON e.invoiceInFk = ii.id JOIN entry e ON e.invoiceInFk = ii.id
JOIN duaEntry de ON de.entryFk = e.id JOIN duaEntry de ON de.entryFk = e.id

View File

@ -1,5 +1,7 @@
DELIMITER $$ 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 BEGIN
/** /**
* Calcula los vctos. de una factura recibida * Calcula los vctos. de una factura recibida
@ -56,12 +58,13 @@ BEGIN
COUNT(DISTINCT(pdd.detail)) cont, COUNT(DISTINCT(pdd.detail)) cont,
s.payDay, s.payDay,
ii.issued, ii.issued,
DATE(ii.created) + INTERVAL 2 DAY created DATE(ii.created) + INTERVAL iic.dueDateMarginDays DAY created
FROM invoiceIn ii FROM invoiceIn ii
JOIN invoiceInTax iit ON iit.invoiceInFk = ii.id JOIN invoiceInTax iit ON iit.invoiceInFk = ii.id
LEFT JOIN sage.TiposIva ti ON ti.CodigoIva= iit.taxTypeSageFk LEFT JOIN sage.TiposIva ti ON ti.CodigoIva= iit.taxTypeSageFk
JOIN supplier s ON s.id = ii.supplierFk JOIN supplier s ON s.id = ii.supplierFk
JOIN payDemDetail pdd ON pdd.id = s.payDemFk JOIN payDemDetail pdd ON pdd.id = s.payDemFk
JOIN invoiceInConfig iic
WHERE ii.id = vInvoiceInFk WHERE ii.id = vInvoiceInFk
GROUP BY ii.id GROUP BY ii.id
)sub )sub

View File

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

View File

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

View File

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

View File

@ -46,12 +46,18 @@ BEGIN
i.tag8 = JSON_VALUE(vTags, '$.8'), i.tag8 = JSON_VALUE(vTags, '$.8'),
i.tag9 = JSON_VALUE(vTags, '$.9'), i.tag9 = JSON_VALUE(vTags, '$.9'),
i.tag10 = JSON_VALUE(vTags, '$.10'), 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.value5 = JSON_VALUE(vValues, '$.5'),
i.value6 = JSON_VALUE(vValues, '$.6'), i.value6 = JSON_VALUE(vValues, '$.6'),
i.value7 = JSON_VALUE(vValues, '$.7'), i.value7 = JSON_VALUE(vValues, '$.7'),
i.value8 = JSON_VALUE(vValues, '$.8'), i.value8 = JSON_VALUE(vValues, '$.8'),
i.value9 = JSON_VALUE(vValues, '$.9'), i.value9 = JSON_VALUE(vValues, '$.9'),
i.value10 = JSON_VALUE(vValues, '$.10'), 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.producerFk = p.id,
i.inkFk = k.id, i.inkFk = k.id,
i.originFk = IFNULL(o.id, i.originFk) i.originFk = IFNULL(o.id, i.originFk)

View File

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

View File

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

View File

@ -174,27 +174,30 @@ proc: BEGIN
WHERE NOT `lines`; WHERE NOT `lines`;
-- Lineas por linea de encajado -- Lineas por linea de encajado
CREATE OR REPLACE TEMPORARY TABLE tItemPackingType
(PRIMARY KEY(ticketFk))
ENGINE = MEMORY
SELECT ticketFk,
SUM(sub.H) H,
SUM(sub.V) V,
SUM(sub.N) N
FROM (
SELECT t.ticketFk,
SUM(i.itemPackingTypeFk = 'H') H,
SUM(i.itemPackingTypeFk = 'V') V,
SUM(i.itemPackingTypeFk IS NULL) N
FROM tmp.productionTicket t
JOIN sale s ON s.ticketFk = t.ticketFk
JOIN item i ON i.id = s.itemFk
GROUP BY t.ticketFk, i.itemPackingTypeFk
) sub
GROUP BY ticketFk;
UPDATE tmp.productionBuffer pb UPDATE tmp.productionBuffer pb
JOIN ( JOIN tItemPackingType ti ON ti.ticketFk = pb.ticketFk
SELECT ticketFk, SET pb.H = ti.H,
SUM(sub.H) H, pb.V = ti.V,
SUM(sub.V) V, pb.N = ti.N;
SUM(sub.N) N
FROM (
SELECT t.ticketFk,
SUM(i.itemPackingTypeFk = 'H') H,
SUM(i.itemPackingTypeFk = 'V') V,
SUM(i.itemPackingTypeFk IS NULL) N
FROM tmp.productionTicket t
JOIN sale s ON s.ticketFk = t.ticketFk
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;
-- Colecciones segun tipo de encajado -- Colecciones segun tipo de encajado
UPDATE tmp.productionBuffer pb UPDATE tmp.productionBuffer pb
@ -273,6 +276,7 @@ proc: BEGIN
tmp.risk, tmp.risk,
tmp.ticket_problems, tmp.ticket_problems,
tmp.ticketWithPrevia, tmp.ticketWithPrevia,
tItemShelvingStock; tItemShelvingStock,
tItemPackingType;
END$$ END$$
DELIMITER ; DELIMITER ;

View File

@ -1,14 +1,18 @@
DELIMITER $$ 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 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 UPDATE `route`
LEFT JOIN ( SET m3 = IFNULL(vVolume, 0)
SELECT routeFk, SUM(volume) AS m3 WHERE id = vSelf;
FROM saleVolume
WHERE routeFk = vRoute
) v ON v.routeFk = r.id
SET r.m3 = IFNULL(v.m3,0)
WHERE r.id =vRoute;
END$$ END$$
DELIMITER ; DELIMITER ;

View File

@ -8,13 +8,16 @@ BEGIN
SET NEW.editorFk = account.myUser_getId(); SET NEW.editorFk = account.myUser_getId();
IF NOT (NEW.routeFk <=> OLD.routeFk) THEN IF NOT (NEW.routeFk <=> OLD.routeFk) THEN
INSERT IGNORE INTO `vn`.`routeRecalc` (`routeFk`) IF NEW.isSigned THEN
SELECT r.id CALL util.throw('A signed ticket cannot be rerouted');
FROM vn.route r END IF;
WHERE r.isOk = FALSE INSERT IGNORE INTO routeRecalc(routeFk)
AND r.id IN (OLD.routeFk,NEW.routeFk) SELECT id
AND r.created >= util.VN_CURDATE() FROM `route`
GROUP BY r.id; WHERE NOT isOk
AND id IN (OLD.routeFk, NEW.routeFk)
AND created >= util.VN_CURDATE()
GROUP BY id;
END IF; END IF;
IF NOT (DATE(NEW.shipped) <=> DATE(OLD.shipped)) THEN 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, DISTINCT c.id clientFk,
c.name clientName, c.name clientName,
c.salesPersonFk, c.salesPersonFk,
c.businessTypeFk, c.businessTypeFk = 'worker' isWorker,
u.name salesPersonName, u.name salesPersonName,
d.amount, d.amount,
co.created, co.created,
@ -71,14 +71,24 @@ module.exports = Self => {
d.defaulterSinced, d.defaulterSinced,
cn.name country, cn.name country,
c.countryFk, c.countryFk,
pm.name payMethod pm.name payMethod,
FROM vn.defaulter d r.finished IS NULL hasRecovery,
JOIN vn.client c ON c.id = d.clientFk dp.id departmentFk,
JOIN vn.country cn ON cn.id = c.countryFk dp.name departmentName
JOIN vn.payMethod pm ON pm.id = c.payMethodFk FROM defaulter d
LEFT JOIN vn.clientObservation co ON co.clientFk = c.id 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 u ON u.id = c.salesPersonFk
LEFT JOIN account.user uw ON uw.id = co.workerFk 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 WHERE
d.created = ? d.created = ?
AND d.amount > 0 AND d.amount > 0

View File

@ -82,7 +82,15 @@ module.exports = Self => {
{ {
arg: 'correctedFk', arg: 'correctedFk',
type: 'number', type: 'number',
description: 'The corrected invoice', description: 'The rectified invoice',
},
{
arg: 'correctingFk',
type: 'Boolean',
},
{
arg: 'supplierActivityFk',
type: 'string',
} }
], ],
returns: { returns: {
@ -111,6 +119,7 @@ module.exports = Self => {
} }
let correctings; let correctings;
let correcteds;
if (args.correctedFk) { if (args.correctedFk) {
correctings = await models.InvoiceInCorrection.find({ correctings = await models.InvoiceInCorrection.find({
fields: ['correctingFk'], 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) => { const where = buildFilter(ctx.args, (param, value) => {
switch (param) { switch (param) {
case 'search': case 'search':
@ -141,8 +153,14 @@ module.exports = Self => {
return {[`ii.${param}`]: value}; return {[`ii.${param}`]: value};
case 'awbCode': case 'awbCode':
return {'sub.code': value}; 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': case 'correctedFk':
return {'ii.id': {inq: correctings.map(x => x.correctingFk)}}; 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) => { Self.getWasteByItem = async(buyer, family, options) => {
const models = Self.app.models;
const myOptions = {}; const myOptions = {};
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); 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(); const date = Date.vnNew();
date.setHours(0, 0, 0, 0); date.setHours(0, 0, 0, 0);
const wastes = await Self.rawSql(` const wastes = await Self.rawSql(`
SELECT *, 100 * dwindle / total AS percentage SELECT *, 100 * dwindle / total percentage
FROM ( FROM (
SELECT buyer, SELECT u.name buyer,
ws.family, it.name family,
ws.itemFk, w.itemFk,
sum(ws.saleTotal) AS total, SUM(w.saleTotal) total,
sum(ws.saleWaste) AS dwindle SUM(w.saleInternalWaste + w.saleExternalWaste) dwindle
FROM bs.waste ws FROM bs.waste w
WHERE buyer = ? AND family = ? JOIN account.user u ON u.id = w.buyerFk
AND year = YEAR(TIMESTAMPADD(WEEK,-1, ?)) JOIN vn.itemType it ON it.id = w.itemTypeFk
AND week = WEEK(TIMESTAMPADD(WEEK,-1, ?), 1) WHERE w.buyerFk = ? AND w.itemTypeFk = ?
GROUP BY buyer, itemFk AND w.year = YEAR(TIMESTAMPADD(WEEK, -1, ?))
) sub AND w.week = WEEK(TIMESTAMPADD(WEEK, -1, ?), 1)
ORDER BY family, percentage DESC`, [buyer, family, date, date], myOptions); GROUP BY w.buyerFk, w.itemFk
) sub
ORDER BY family, percentage DESC
`, [user.id, itemType.id, date, date], myOptions);
const details = []; const details = [];

View File

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

View File

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

View File

@ -11,9 +11,9 @@ describe('Item getWasteByWorker()', () => {
const length = result.length; const length = result.length;
const anyResult = result[Math.floor(Math.random() * Math.floor(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.total).toBeGreaterThanOrEqual(1000);
expect(anyResult.lines.length).toBeGreaterThanOrEqual(3); expect(anyResult.lines.length).toBeGreaterThanOrEqual(1);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -48,7 +48,7 @@ module.exports = Self => {
LEFT JOIN vn.expeditionStateType est ON est.id = e.stateTypeFk LEFT JOIN vn.expeditionStateType est ON est.id = e.stateTypeFk
JOIN vn.agencyMode am ON am.id = r.agencyModeFk JOIN vn.agencyMode am ON am.id = r.agencyModeFk
JOIN vn.agency ag ON ag.id = am.agencyFk 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 = ? WHERE t.routeFk = ?
GROUP BY t.addressFk, e.itemPackingTypeFk GROUP BY t.addressFk, e.itemPackingTypeFk
) sub ) 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'; export * from './module';
import './main'; import './main';
import './index/';
import './search-panel';
import './descriptor'; import './descriptor';
import './descriptor-popover'; import './descriptor-popover';
import './summary'; 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'; import ModuleMain from 'salix/components/module-main';
export default class Route extends ModuleMain { export default class Route extends ModuleMain {
$postLink() { constructor($element, $) {
const to = Date.vnNew(); super($element, $);
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);
} }
async $onInit() {
fetchParams($params) { this.$state.go('home');
const hasEntries = Object.entries($params).length; window.location.href = await this.vnApp.getUrl(`route/`);
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;
} }
} }

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"], "dependencies": ["client", "worker", "ticket", "supplier", "invoiceIn"],
"menus": { "menus": {
"main": [ "main": [
{"state": "route.index", "icon": "icon-delivery"}, {"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"}
] ]
}, },
"routes": [ "routes": [
@ -33,108 +22,6 @@
"state": "route.index", "state": "route.index",
"component": "vn-route-index", "component": "vn-route-index",
"description": "Routes" "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