This commit is contained in:
Carlos Jimenez Ruiz 2019-12-13 15:12:55 +01:00
commit e6130e78fb
61 changed files with 2535 additions and 5045 deletions

View File

@ -12,7 +12,6 @@ RUN apt-get update \
&& apt-get install -y --no-install-recommends \
nodejs \
&& apt-get purge -y --auto-remove \
curl \
gnupg2 \
&& rm -rf /var/lib/apt/lists/* \
&& npm -g install pm2
@ -33,4 +32,7 @@ COPY \
README.md \
./
CMD ["pm2-docker", "./loopback/server/server.js"]
CMD ["pm2-runtime", "./back/process.yml"]
HEALTHCHECK --interval=1m --timeout=10s \
CMD curl -f http://localhost:3000/api/Applications/status || exit 1

22
Jenkinsfile vendored
View File

@ -6,13 +6,15 @@ pipeline {
disableConcurrentBuilds()
}
environment {
PROJECT_NAME = 'salix'
REGISTRY = 'registry.verdnatura.es'
DOCKER_HOST_1 = 'vch1.verdnatura.es'
DOCKER_HOST_2 = 'vch2.verdnatura.es'
PORT_MASTER = '5002'
PORT_TEST = '5001'
TAG = "${env.BRANCH_NAME}"
PROJECT_NAME = 'salix'
REGISTRY = 'registry.verdnatura.es'
DOCKER_HOST_1 = 'vch1.verdnatura.es'
DOCKER_HOST_2 = 'vch2.verdnatura.es'
PORT_MASTER_FRONT = '5002'
PORT_MASTER_BACK = '3001-3002'
PORT_TEST_FRONT = '5001'
PORT_TEST_BACK = '4001-4002'
TAG = "${env.BRANCH_NAME}"
}
stages {
stage('Checkout') {
@ -31,10 +33,12 @@ pipeline {
switch (env.BRANCH_NAME) {
case 'master':
env.PORT = PORT_MASTER
env.PORT_FRONT = PORT_MASTER_FRONT
env.PORT_BACK = PORT_MASTER_BACK
break
case 'test':
env.PORT = PORT_TEST
env.PORT_FRONT = PORT_TEST_FRONT
env.PORT_BACK = PORT_TEST_BACK
break
}
switch (env.BRANCH_NAME) {

5
back/process.yml Normal file
View File

@ -0,0 +1,5 @@
apps:
- script: ./loopback/server/server.js
name: salix-back
instances: 1
max_restarts: 5

View File

@ -1,4 +1,4 @@
FROM mysql:5.6.42
FROM mysql:8.0.18
ENV MYSQL_ROOT_PASSWORD root
ENV TZ Europe/Madrid
@ -7,36 +7,33 @@ ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl ca-certificates \
&& curl -sL https://apt.verdnatura.es/conf/verdnatura.gpg | apt-key add - \
&& echo "deb http://apt.verdnatura.es/ jessie main" > /etc/apt/sources.list.d/vn.list \
&& echo "deb http://apt.verdnatura.es/ stretch main" > /etc/apt/sources.list.d/vn.list \
&& apt-get update \
&& apt-get install -y --allow-unauthenticated vn-mysql \
&& apt-get install -y vn-mysql libmysqlclient21 \
&& apt-get purge -y --auto-remove curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# XXX: Removes the last script line to avoid daemon to be started
RUN cp /usr/local/bin/docker-entrypoint.sh /usr/local/bin/docker-init.sh \
&& sed -i '$ d' /usr/local/bin/docker-init.sh
WORKDIR /docker-entrypoint-initdb.d
ARG STAMP=unknown
COPY import-changes.sh config.ini /docker-entrypoint-initdb.d/import/
COPY docker.cnf /etc/mysql/mysql.conf.d/
COPY changes /docker-entrypoint-initdb.d/import/changes
COPY docker-boot.sh /docker-entrypoint-initdb.d/
COPY dump /docker-entrypoint-initdb.d/dump
COPY docker/docker.cnf /etc/mysql/conf.d/
COPY docker/docker-init.sh docker/docker-start.sh /usr/local/bin/
RUN mkdir /mysql-data \
&& /usr/local/bin/docker-init.sh mysqld --datadir /mysql-data \
&& chown -R mysql:mysql /mysql-data \
&& chown -R mysql:mysql /mysql-data
WORKDIR /docker-entrypoint-initdb.d
COPY dump dump
COPY docker/docker-boot.sh /docker-entrypoint-initdb.d/
COPY changes import/changes
COPY import-changes.sh config.ini import/
ARG STAMP=unknown
RUN gosu mysql docker-init.sh mysqld \
&& rm -rf /docker-entrypoint-initdb.d/*
COPY docker-start.sh /usr/local/bin/
USER mysql
ENTRYPOINT ["docker-start.sh"]
USER mysql
CMD ["mysqld", "--datadir", "/mysql-data"]
CMD ["mysqld"]
#HEALTHCHECK --interval=5s --timeout=10s --retries=200 \
# CMD mysqladmin ping -h 127.0.0.1 -u root || exit 1

View File

@ -6,8 +6,8 @@ USE `vn`$$
CREATE DEFINER=`root`@`%` PROCEDURE `zone_getWarehouse`(vAddress INT, vLanded DATE, vWarehouse INT)
BEGIN
/**
* Devuelve el listado de agencias disponibles para la fecha,
* dirección y warehouse pasadas
* Devuelve el listado de agencias disponibles para la fecha,
* dirección y almacén pasados.
*
* @param vAddress
* @param vWarehouse warehouse
@ -30,7 +30,8 @@ BEGIN
JOIN agencyMode am ON am.id = z.agencyModeFk
JOIN zoneWarehouse zw ON zw.zoneFk = zo.zoneFk
WHERE zw.warehouseFk
GROUP BY z.agencyModeFk;
GROUP BY z.agencyModeFk
ORDER BY agencyMode;
DROP TEMPORARY TABLE
tmp.zone,

View File

@ -36,7 +36,7 @@ BEGIN
CALL vn.zone_getShippedWarehouse(vlanded, vAddressFk, vAgencyModeFk);
SELECT id INTO vZoneFk FROM tmp.zoneGetShipped
SELECT zoneFk INTO vZoneFk FROM tmp.zoneGetShipped
WHERE shipped = vShipped AND warehouseFk = vWarehouseFk LIMIT 1;
INSERT INTO vn2008.Tickets (

View File

@ -1,9 +1,8 @@
USE `hedera`;
DROP procedure IF EXISTS `tpvTransaction_undo`;
DROP procedure IF EXISTS `hedera`.`tpvTransaction_undo`;
DELIMITER $$
USE `hedera`$$
CREATE DEFINER=`root`@`%` PROCEDURE `tpvTransaction_undo`(vSelf INT)
CREATE DEFINER=`root`@`%` PROCEDURE `hedera`.`tpvTransaction_undo`(vSelf INT)
BEGIN
DECLARE vCustomer INT;
DECLARE vAmount DOUBLE;

View File

@ -1,123 +0,0 @@
DROP procedure IF EXISTS `vn2008`.`clean`;
DELIMITER $$
CREATE DEFINER=`root`@`%` PROCEDURE `vn2008`.`clean`(IN `v_full` TINYINT(1))
proc: BEGIN
DECLARE v_date DATETIME;
DECLARE v_date18 DATETIME;
DECLARE v_date26 DATETIME;
DECLARE v_date8 DATE;
DECLARE v_date6 DATE;
DECLARE v_date3Month DATE;
DECLARE vDate2000 DATE;
DECLARE vRangeDeleteTicket INT;
DECLARE strtable varchar(15) DEFAULT NULL;
DECLARE done BIT DEFAULT 0;
SET v_date = TIMESTAMPADD(MONTH, -2, CURDATE());
SET v_date18 = TIMESTAMPADD(MONTH, -18,CURDATE());
SET v_date26 = TIMESTAMPADD(MONTH, -26,CURDATE());
SET v_date3Month = TIMESTAMPADD(MONTH, -3, CURDATE());
SET v_date8 = TIMESTAMPADD(DAY, -8,CURDATE());
SET v_date6 = TIMESTAMPADD(DAY, -6,CURDATE());
SET vRangeDeleteTicket = 60;
DELETE FROM cdr WHERE calldate < v_date;
DELETE FROM Monitoring WHERE ODBC_TIME < v_date;
DELETE FROM Conteo WHERE Fecha < v_date;
DELETE FROM XDiario WHERE FECHA < v_date3Month OR FECHA IS NULL;
DELETE FROM mail WHERE DATE_ODBC < v_date;
-- DELETE FROM Cajas WHERE CajaFecha < v_date18;
DELETE rr FROM Recibos_recorded rr JOIN Recibos r ON rr.Id_Recibos = r.Id WHERE r.Fechacobro < v_date;
SELECT MAX(idTickets_dits)
INTO @id
FROM Tickets_dits
WHERE ODBC_DATE < v_date;
DELETE FROM Tickets_dits WHERE idTickets_dits <= @id;
DELETE FROM expeditions_deleted WHERE odbc_date < v_date26;
DELETE FROM Entradas_dits WHERE ODBC_DATE < v_date18;
DELETE FROM log_articles WHERE ODBC_DATE < v_date;
DELETE FROM Splits WHERE Fecha < v_date18;
DELETE ts FROM Tickets_stack ts JOIN Tickets t ON ts.Id_Ticket = t.Id_Ticket WHERE t.Fecha < v_date;
DELETE tobs FROM movement_label tobs JOIN Movimientos m ON tobs.Id_Movimiento = m.Id_Movimiento
JOIN Tickets t ON m.Id_Ticket = t.Id_Ticket WHERE t.Fecha < v_date;
DELETE FROM chat WHERE odbc_date < v_date;
DELETE FROM Extractos WHERE Fecha < v_date;
DELETE FROM Remesas WHERE `Fecha Remesa` < v_date18;
DELETE FROM Stockcontrol WHERE Datestart < v_date18;
-- DELETE FROM reference_rate WHERE date < v_date18;
DELETE FROM hedera.`order` WHERE date_send < v_date18;
-- DELETE FROM Ordenes WHERE odbc_date < v_date18; JGF 2018-12-21 Si estan en un turno no hay que borrarlas.
SELECT MAX(inter_id)
INTO @id
FROM vncontrol.inter
WHERE odbc_date < v_date18;
DELETE FROM vncontrol.inter WHERE inter_id <= @id;
DELETE FROM Entradas_dits WHERE ODBC_DATE < v_date;
DELETE FROM cyc_declaration WHERE Fecha < v_date18;
DELETE FROM travel_reserve WHERE odbc_date < v_date;
-- DELETE FROM syslog.systemevents WHERE odbc_date < v_date8;
DELETE FROM cache.departure_limit WHERE Fecha < TIMESTAMPADD(MONTH,-1,CURDATE());
DELETE co
FROM Compres_ok co JOIN Compres c ON c.Id_Compra = co.Id_Compra
JOIN Entradas e ON e.Id_Entrada = c.Id_Entrada
JOIN travel t ON t.id = e.travel_id
WHERE t.landing <= v_date;
DELETE FROM vn2008.scan WHERE odbc_date < v_date6 AND id <> 1;
SET vDate2000 = TIMESTAMPADD(YEAR, 2000 - YEAR(CURDATE()), CURDATE());
IF v_full
THEN
DELETE t FROM Tickets t
LEFT JOIN Tickets_turno tt ON tt.Id_Ticket = t.Id_Ticket
WHERE Fecha NOT IN ('2000-01-01','2000-01-02')
AND YEAR(Fecha) = 2000
AND ABS(DATEDIFF(Fecha,vDate2000)) > vRangeDeleteTicket
AND tt.Id_Ticket IS NULL;
DELETE e.* FROM Entradas e
LEFT JOIN recibida_entrada re ON e.Id_Entrada = re.Id_Entrada
WHERE travel_id IS NULL
AND re.Id_Entrada IS NULL;
END IF;
-- Tickets Nulos PAK 11/10/2016
UPDATE vn2008.Tickets
SET empresa_id = 965
WHERE Id_Cliente = 31
AND empresa_id != 965;
-- Equipos duplicados
DELETE w.*
FROM vn2008.workerTeam w
JOIN (SELECT id, team, workerFk, COUNT(*) - 1 as duplicated
FROM vn.workerTeam
GROUP BY team,workerFk
HAVING duplicated
) d ON d.team = w.team AND d.workerFk = w.user AND d.id != w.id;
-- CAP 29/10/2018 Mantenimiento tabla Movimientos_componentes
DELETE mc
FROM vn2008.Movimientos_componentes mc
JOIN vn2008.Movimientos mv
ON mv.Id_Movimiento=mc.Id_Movimiento
JOIN vn2008.Tickets t
ON t.Id_Ticket= mv.Id_Ticket
WHERE t.Fecha<v_date18;
END$$
DELIMITER ;

View File

@ -1,55 +0,0 @@
DROP procedure IF EXISTS `vn`.`clean`;
DELIMITER $$
CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`clean`()
BEGIN
DECLARE vDateShort DATETIME;
DECLARE vOneYearAgo DATE;
DECLARE vFourYearsAgo DATE;
DECLARE v18Month DATE;
DECLARE v26Month DATE;
DECLARE v3Month DATE;
SET vDateShort = TIMESTAMPADD(MONTH, -2, CURDATE());
SET vOneYearAgo = TIMESTAMPADD(YEAR,-1,CURDATE());
SET vFourYearsAgo = TIMESTAMPADD(YEAR,-4,CURDATE());
SET v18Month = TIMESTAMPADD(MONTH, -18,CURDATE());
SET v26Month = TIMESTAMPADD(MONTH, -26,CURDATE());
SET v3Month = TIMESTAMPADD(MONTH, -3, CURDATE());
DELETE FROM `message` WHERE sendDate < vDateShort;
DELETE FROM messageInbox WHERE sendDate < vDateShort;
DELETE FROM messageInbox WHERE sendDate < vDateShort;
DELETE FROM workerTimeControl WHERE timed < vFourYearsAgo;
DELETE FROM itemShelving WHERE created < CURDATE() AND visible = 0;
DELETE FROM ticketDown WHERE created < TIMESTAMPADD(DAY,-1,CURDATE());
DELETE FROM entryLog WHERE creationDate < vDateShort;
DELETE FROM expedition WHERE created < v26Month;
DELETE FROM sms WHERE created < v18Month;
DELETE FROM saleTracking WHERE created < vDateShort;
DELETE tobs FROM ticketObservation tobs
JOIN ticket t ON tobs.shipped = t.id WHERE t.shipped < vDateShort;
DELETE FROM sharingCart where ended < vDateShort;
DELETE FROM sharingClient where ended < vDateShort;
DELETE tw.* FROM ticketWeekly tw
LEFT JOIN sale s ON s.ticketFk = tw.ticketFk WHERE s.itemFk IS NULL;
DELETE FROM claim WHERE ticketCreated < v18Month;
DELETE FROM message WHERE sendDate < vDateShort;
DELETE sc FROM saleChecked sc
JOIN sale s ON mc.Id_Movimiento = s.id WHERE s.created < vDateShort;
DELETE bm
FROM buyMark bm
JOIN buy b ON b.id = bm.id
JOIN entry e ON e.id = b.entryFk
JOIN travel t ON t.id = e.travelFk
WHERE t.landed <= vDateShort;
DELETE FROM stowaway WHERE created < v3Month;
CALL shelving_clean;
CALL ticketPackagingRecovery;
END$$
DELIMITER ;

View File

@ -1,4 +0,0 @@
[mysqld]
innodb_log_file_size = 4M
innodb_autoextend_increment = 4
innodb_page_size = 8K

17
db/docker/docker-init.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
. /usr/local/bin/docker-entrypoint.sh
mysql_check_config "$@"
docker_setup_env "$@"
docker_create_db_directories
docker_verify_minimum_env
docker_init_database_dir "$@"
docker_temp_server_start "$@"
docker_setup_db
docker_process_init_files /docker-entrypoint-initdb.d/*
mysql_expire_root_user
docker_temp_server_stop

10
db/docker/docker.cnf Normal file
View File

@ -0,0 +1,10 @@
[mysqld]
innodb_log_file_size = 4M
innodb_autoextend_increment = 4
innodb_page_size = 8K
innodb_default_row_format = COMPACT
log_bin_trust_function_creators = ON
datadir = /mysql-data
sql_mode = NO_ENGINE_SUBSTITUTION
innodb_temp_data_file_path = /tmp/ibtmp1:12M:autoextend
skip-log-bin

View File

@ -1,10 +1,13 @@
-- FIXME: cuando se eliminen los procedimientos de la cache, se podra eliminar esta linea
CREATE SCHEMA IF NOT EXISTS `vn2008`;
CREATE SCHEMA IF NOT EXISTS `tmp`;
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root';
ALTER TABLE `vn`.`itemTaxCountry` AUTO_INCREMENT = 1;
ALTER TABLE `vn`.`address` AUTO_INCREMENT = 1;
ALTER TABLE `vn`.`zoneGeo` AUTO_INCREMENT = 1;
ALTER TABLE `vn`.`ticket` AUTO_INCREMENT = 1;
INSERT INTO `vn`.`ticketConfig` (`id`, `scopeDays`)
VALUES
@ -18,7 +21,6 @@ INSERT INTO `vn`.`bionicConfig` (`generalInflationCoeficient`, `minimumDensityVo
VALUES
(1.30, 167.00, 138000, 71);
INSERT INTO `account`.`user`(`id`,`name`, `nickname`, `password`,`role`,`active`,`email`, `lang`)
SELECT id, name, CONCAT(name, 'Nick'),MD5('nightmare'), id, 1, CONCAT(name, '@mydomain.com'), 'es'
FROM `account`.`role` WHERE id <> 20;
@ -265,7 +267,7 @@ INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `pr
(121, 'address 21', 'the bat cave', 'Silla', 46460, 1, 1111111111, 222222222, 1, 101, 2, NULL, NULL, 0, 0),
(122, 'address 22', 'NY roofs', 'Silla', 46460, 1, 1111111111, 222222222, 1, 102, 2, NULL, NULL, 0, 0),
(123, 'address 23', 'The phone box', 'Silla', 46460, 1, 1111111111, 222222222, 1, 103, 2, NULL, NULL, 0, 0),
(124, 'address 24', 'Stark tower', 'Silla', 46460, 1, 1111111111, 222222222, 1, 104, 2, NULL, NULL, 0, 0),
(124, 'address 24', 'Stark tower Silla', 'Silla', 46460, 1, 1111111111, 222222222, 1, 104, 2, NULL, NULL, 0, 0),
(125, 'address 25', 'The plastic cell', 'Silla', 46460, 1, 1111111111, 222222222, 1, 105, 2, NULL, NULL, 0, 0),
(126, 'address 26', 'Many places', 'Silla', 46460, 1, 1111111111, 222222222, 1, 106, 2, NULL, NULL, 0, 0),
(127, 'address 27', 'Your pocket', 'Silla', 46460, 1, 1111111111, 222222222, 1, 107, 2, NULL, NULL, 0, 0),
@ -558,14 +560,18 @@ INSERT INTO `vn`.`stowaway`(`id`, `shipFk`, `created`)
VALUES
(12, 13, CURDATE());
INSERT INTO `vn`.`vehicle`(`id`, `numberPlate`, `tradeMark`, `model`, `companyFk`, `warehouseFk`, `description`, `m3`, `isActive`)
INSERT INTO `vn`.`deliveryPoint` (`id`, `name`, `ubication`)
VALUES
(1, '3333-BAT', 'WAYNE INDUSTRIES', 'BATMOBILE', 442, 1, 'The ultimate war machine', 50, 1),
(2, '1111-IMK', 'STARK INDUSTRIES', 'MARK-III', 442, 1, 'Iron-Man Heavy Armor MARK-III', 18, 1),
(3, '2222-IMK', 'STARK INDUSTRIES', 'MARK-VI', 442, 1, 'Iron-Man Heavy Armor MARK-VI', 16, 1),
(4, '3333-IMK', 'STARK INDUSTRIES', 'MARK-VII', 442, 1, 'Iron-Man Heavy Armor MARK-VII', 14, 1),
(5, '4444-IMK', 'STARK INDUSTRIES', 'MARK-XLII', 442, 1, 'Iron-Man Heavy Armor MARK-XLII', 13, 1),
(6, '5555-IMK', 'STARK INDUSTRIES', 'MARK-XLV', 442, 1, 'Iron-Man Heavy Armor MARK-XLV', 12, 0);
(1, 'Silla','46460 Av Espioca 100-Silla');
INSERT INTO `vn`.`vehicle`(`id`, `numberPlate`, `tradeMark`, `model`, `companyFk`, `warehouseFk`, `description`, `m3`, `isActive`, `deliveryPointFk`)
VALUES
(1, '3333-BAT', 'WAYNE INDUSTRIES', 'BATMOBILE', 442, 1, 'The ultimate war machine', 50, 1, 1),
(2, '1111-IMK', 'STARK INDUSTRIES', 'MARK-III', 442, 1, 'Iron-Man Heavy Armor MARK-III', 18, 1, 1),
(3, '2222-IMK', 'STARK INDUSTRIES', 'MARK-VI', 442, 1, 'Iron-Man Heavy Armor MARK-VI', 16, 1, 1),
(4, '3333-IMK', 'STARK INDUSTRIES', 'MARK-VII', 442, 1, 'Iron-Man Heavy Armor MARK-VII', 14, 1, 1),
(5, '4444-IMK', 'STARK INDUSTRIES', 'MARK-XLII', 442, 1, 'Iron-Man Heavy Armor MARK-XLII', 13, 1, 1),
(6, '5555-IMK', 'STARK INDUSTRIES', 'MARK-XLV', 442, 1, 'Iron-Man Heavy Armor MARK-XLV', 12, 0, 1);
INSERT INTO `vn`.`config`(`id`, `mdbServer`, `fakeEmail`, `defaultersMaxAmount`, `inventoried`)
VALUES
@ -1499,13 +1505,13 @@ INSERT INTO `vn`.`ticketServiceType`(`id`, `name`)
(4, 'Cargo FITOSANITARIO'),
(5, 'Documentos');
INSERT INTO `vn`.`ticketService`(`id`, `description`, `quantity`, `price`, `taxClassFk`, `ticketFk`)
INSERT INTO `vn`.`ticketService`(`id`, `description`, `quantity`, `price`, `taxClassFk`, `ticketFk`, `ticketServiceTypeFk`)
VALUES
(1, 'Documentos', 1, 2.00, 1, 1),
(2, 'Porte Agencia', 1, 10.00, 1, 2),
(3, 'Documentos', 1, 5.50, 1, 11),
(4, 'Documentos', 1, 2.00, 1, 9),
(5, 'Documentos', 1, 2.00, 1, 8);
(1, 'Documentos', 1, 2.00, 1, 1, 1),
(2, 'Porte Agencia', 1, 10.00, 1, 2, 1),
(3, 'Documentos', 1, 5.50, 1, 11, 1),
(4, 'Documentos', 1, 2.00, 1, 9, 1),
(5, 'Documentos', 1, 2.00, 1, 8, 1);
INSERT INTO `pbx`.`sip`(`user_id`, `extension`)
VALUES

View File

@ -1,4 +1,4 @@
-- Import compiled functions
CREATE AGGREGATE FUNCTION minacum RETURNS INT SONAME 'minacum.so';
CREATE AGGREGATE FUNCTION multimax RETURNS INT SONAME 'multimax.so';
CREATE AGGREGATE FUNCTION multimax RETURNS INT SONAME 'multimax.so';

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,83 @@ SCHEMAS=(
vncontrol
)
IGNORETABLES=(
--ignore-table=bi.last_Id_Cubo
--ignore-table=bi.v_clientes_jerarquia
--ignore-table=bi.v_ventas_contables
--ignore-table=bs.horasSilla
--ignore-table=bs.productionIndicators
--ignore-table=bs.VentasPorCliente
--ignore-table=bs.v_ventas
--ignore-table=edi.supplyOffer
--ignore-table=postgresql.currentWorkersStats
--ignore-table=vn.accounting__
--ignore-table=vn.agencyModeZone
--ignore-table=vn.agencyProvince
--ignore-table=vn.agencyWarehouse
--ignore-table=vn.awb
--ignore-table=vn.botanicExport__
--ignore-table=vn.clientDefaultCompany
--ignore-table=vn.color
--ignore-table=vn.comparative
--ignore-table=vn.comparativeFilter
--ignore-table=vn.coolerPath
--ignore-table=vn.coolerPathDetail
--ignore-table=vn.department__
--ignore-table=vn.doc
--ignore-table=vn.entity
--ignore-table=vn.especialPrice
--ignore-table=vn.exchangeInsurance
--ignore-table=vn.exchangeInsuranceInPrevious
--ignore-table=vn.exchangeReportSource
--ignore-table=vn.grant
--ignore-table=vn.grantGroup
--ignore-table=vn.invoiceCorrection__
--ignore-table=vn.invoiceIn
--ignore-table=vn.invoiceInAwb
--ignore-table=vn.invoiceInDueDay
--ignore-table=vn.invoiceInEntry
--ignore-table=vn.invoiceInIntrastat
--ignore-table=vn.invoiceInTax
--ignore-table=vn.itemTaxCountrySpain
--ignore-table=vn.mail__
--ignore-table=vn.manaSpellers
--ignore-table=vn.outgoingInvoiceKk
--ignore-table=vn.payment
--ignore-table=vn.paymentExchangeInsurance
--ignore-table=vn.payrollCenter
--ignore-table=vn.plantpassport
--ignore-table=vn.plantpassportAuthority
--ignore-table=vn.preparationException
--ignore-table=vn.priceFixed__
--ignore-table=vn.printer
--ignore-table=vn.printingQueue
--ignore-table=vn.printServerQueue__
--ignore-table=vn.promissoryNote
--ignore-table=vn.rate
--ignore-table=vn.referenceRate__
--ignore-table=vn.routesControl
--ignore-table=vn.salesToPrePrepare
--ignore-table=vn.specialPrice__
--ignore-table=vn.ticketDownBuffer
--ignore-table=vn.ticketeToPreparePrepared
--ignore-table=vn.ticketObservation__
--ignore-table=vn.ticketRequest__
--ignore-table=vn.ticketToPrepare
--ignore-table=vn.till__
--ignore-table=vn.time
--ignore-table=vn.travelThermograph__
--ignore-table=vn.travel_cloneWeekly
--ignore-table=vn.unary
--ignore-table=vn.unaryScan
--ignore-table=vn.unaryScanLine
--ignore-table=vn.unaryScanLineBuy
--ignore-table=vn.unaryScanLineExpedition
--ignore-table=vn.warehouseAlias__
--ignore-table=vn.warehouseJoined
--ignore-table=vn.workerTeam__
--ignore-table=vn.XDiario__
)
mysqldump \
--defaults-file=config.production.ini \
--default-character-set=utf8 \
@ -25,4 +102,5 @@ mysqldump \
--triggers --routines --events \
--databases \
${SCHEMAS[@]} \
${IGNORETABLES[@]} \
> dump/structure.sql

View File

@ -7,16 +7,18 @@ services:
context: .
dockerfile: front/Dockerfile
ports:
- ${PORT}:80
- ${PORT_FRONT}:80
links:
- back
back:
image: registry.verdnatura.es/salix-back:${TAG}
restart: unless-stopped
build: .
ports:
- ${PORT_BACK}:3000
environment:
- NODE_ENV
volumes:
- /containers/salix:/etc/salix
- /mnt/storage/pdfs:/var/lib/salix/pdfs
- /mnt/storage/dms:/var/lib/salix/dms
- /mnt/storage/dms:/var/lib/salix/dms

View File

@ -237,8 +237,8 @@ export default {
nameInput: 'vn-item-basic-data [name="name"]',
relevancyInput: 'vn-item-basic-data [name="relevancy"]',
originAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.originFk"]',
expenceAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.expenceFk"]',
longNameInput: 'vn-item-basic-data [name="longName"]',
expenseAutocomplete: 'vn-autocomplete[ng-model="$ctrl.item.expenseFk"]',
longNameInput: 'vn-textfield[ng-model="$ctrl.item.longName"] input',
isActiveCheckbox: 'vn-check[label="Active"]',
priceInKgCheckbox: 'vn-check[label="Price in kg"]',
submitBasicDataButton: `button[type=submit]`
@ -500,16 +500,17 @@ export default {
},
ticketService: {
addServiceButton: 'vn-ticket-service vn-icon-button[vn-tooltip="Add service"] > button',
firstAddDescriptionButton: 'vn-ticket-service vn-icon-button[vn-tooltip="New service type"]',
firstDescriptionAutocomplete: 'vn-ticket-service vn-autocomplete[ng-model="service.description"]',
firstAddServiceTypeButton: 'vn-ticket-service vn-icon-button[vn-tooltip="New service type"]',
firstServiceTypeAutocomplete: 'vn-ticket-service vn-autocomplete[ng-model="service.ticketServiceTypeFk"]',
firstQuantityInput: 'vn-ticket-service [name="quantity"]',
firstPriceInput: 'vn-ticket-service [name="price"]',
firstVatTypeAutocomplete: 'vn-ticket-service vn-autocomplete[label="Tax class"]',
fistDeleteServiceButton: 'vn-ticket-service form vn-horizontal:nth-child(1) vn-icon-button[icon="delete"]',
newDescriptionInput: '.vn-dialog.shown [name="name"]',
newServiceTypeNameInput: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.newServiceType.name"] input',
newServiceTypeExpenseAutocomplete: '.vn-dialog.shown vn-autocomplete[ng-model="$ctrl.newServiceType.expenseFk"]',
serviceLine: 'vn-ticket-service > form > vn-card > vn-one:nth-child(2) > vn-horizontal',
saveServiceButton: `button[type=submit]`,
saveDescriptionButton: '.vn-dialog.shown tpl-buttons > button'
saveServiceTypeButton: '.vn-dialog.shown tpl-buttons > button'
},
createStateView: {
stateAutocomplete: 'vn-autocomplete[ng-model="$ctrl.stateFk"]',

View File

@ -27,7 +27,7 @@ describe('Item Edit basic data path', () => {
.clearInput(selectors.itemBasicData.relevancyInput)
.write(selectors.itemBasicData.relevancyInput, '1')
.autocompleteSearch(selectors.itemBasicData.originAutocomplete, 'Spain')
.autocompleteSearch(selectors.itemBasicData.expenceAutocomplete, 'Alquiler VNH')
.autocompleteSearch(selectors.itemBasicData.expenseAutocomplete, 'Alquiler VNH')
.clearInput(selectors.itemBasicData.longNameInput)
.write(selectors.itemBasicData.longNameInput, 'RS Rose of Purity')
.waitToClick(selectors.itemBasicData.isActiveCheckbox)
@ -76,7 +76,7 @@ describe('Item Edit basic data path', () => {
it(`should confirm the item expence was edited`, async() => {
const result = await nightmare
.waitToGetProperty(`${selectors.itemBasicData.expenceAutocomplete} input`, 'value');
.waitToGetProperty(`${selectors.itemBasicData.expenseAutocomplete} input`, 'value');
expect(result).toEqual('Alquiler VNH');
});

View File

@ -15,10 +15,10 @@ describe('Ticket services path', () => {
it('should find the add descripton button disabled for this user role', async() => {
const result = await nightmare
.waitForClassPresent(selectors.ticketService.firstAddDescriptionButton, 'disabled')
.waitForClassPresent(selectors.ticketService.firstAddServiceTypeButton, 'disabled')
.waitToClick(selectors.ticketService.addServiceButton)
.wait(selectors.ticketService.firstAddDescriptionButton)
.isDisabled(selectors.ticketService.firstAddDescriptionButton);
.wait(selectors.ticketService.firstAddServiceTypeButton)
.isDisabled(selectors.ticketService.firstAddServiceTypeButton);
expect(result).toBeTruthy();
}, 100000);
@ -50,7 +50,7 @@ describe('Ticket services path', () => {
it('should click on the add button to prepare the form to create a new service', async() => {
const result = await nightmare
.waitToClick(selectors.ticketService.addServiceButton)
.isVisible(selectors.ticketService.firstDescriptionAutocomplete);
.isVisible(selectors.ticketService.firstServiceTypeAutocomplete);
expect(result).toBeTruthy();
});
@ -63,27 +63,28 @@ describe('Ticket services path', () => {
expect(result).toEqual(`can't be blank`);
});
it('should click on the add new description to open the dialog', async() => {
it('should click on the add new service type to open the dialog', async() => {
const result = await nightmare
.waitToClick(selectors.ticketService.firstAddDescriptionButton)
.waitToClick(selectors.ticketService.firstAddServiceTypeButton)
.wait('.vn-dialog.shown')
.isVisible(selectors.ticketService.newDescriptionInput);
.isVisible(selectors.ticketService.newServiceTypeNameInput);
expect(result).toBeTruthy();
});
it('should receive an error if description is empty on submit', async() => {
it('should receive an error if service type is empty on submit', async() => {
const result = await nightmare
.waitToClick(selectors.ticketService.saveDescriptionButton)
.waitToClick(selectors.ticketService.saveServiceTypeButton)
.waitForLastSnackbar();
expect(result).toEqual(`Name can't be empty`);
});
it('should create a new description then add price then create the service', async() => {
it('should create a new service type then add price then create the service', async() => {
const result = await nightmare
.write(selectors.ticketService.newDescriptionInput, 'accurate description')
.waitToClick(selectors.ticketService.saveDescriptionButton)
.write(selectors.ticketService.newServiceTypeNameInput, 'Documentos')
.autocompleteSearch(selectors.ticketService.newServiceTypeExpenseAutocomplete, 'Retencion')
.waitToClick(selectors.ticketService.saveServiceTypeButton)
.write(selectors.ticketService.firstPriceInput, 999)
.waitToClick(selectors.ticketService.saveServiceButton)
.waitForLastSnackbar();
@ -94,9 +95,9 @@ describe('Ticket services path', () => {
it('should confirm the service description was created correctly', async() => {
const result = await nightmare
.reloadSection('ticket.card.service')
.waitToGetProperty(`${selectors.ticketService.firstDescriptionAutocomplete} input`, 'value');
.waitToGetProperty(`${selectors.ticketService.firstServiceTypeAutocomplete} input`, 'value');
expect(result).toEqual('accurate description');
expect(result).toEqual('Documentos');
});
it('should confirm the service quantity was created correctly', async() => {

View File

@ -94,38 +94,4 @@ describe('Claim development', () => {
expect(worker).toEqual('deliveryNick');
expect(redelivery).toEqual('Reparto');
});
it('should delete the first development, add an empty one and save it', async() => {
const result = await nightmare
.waitToClick(selectors.claimDevelopment.firstDeleteDevelopmentButton)
.waitToClick(selectors.claimDevelopment.addDevelopmentButton)
.waitToClick(selectors.claimDevelopment.saveDevelopmentButton)
.waitForLastSnackbar();
expect(result).toEqual('Data saved!');
});
it('should confirm the second development was auto filled', async() => {
const reason = await nightmare
.reloadSection('claim.card.development')
.waitToGetProperty(`${selectors.claimDevelopment.secondClaimReasonAutocomplete} input`, 'value');
const result = await nightmare
.waitToGetProperty(`${selectors.claimDevelopment.secondClaimResultAutocomplete} input`, 'value');
const responsible = await nightmare
.waitToGetProperty(`${selectors.claimDevelopment.secondClaimResponsibleAutocomplete} input`, 'value');
const worker = await nightmare
.waitToGetProperty(`${selectors.claimDevelopment.secondClaimWorkerAutocomplete} input`, 'value');
const redelivery = await nightmare
.waitToGetProperty(`${selectors.claimDevelopment.secondClaimRedeliveryAutocomplete} input`, 'value');
expect(reason).toEqual('Prisas');
expect(result).toEqual('Otros daños');
expect(responsible).toEqual('Compradores');
expect(worker).toEqual('managerNick');
expect(redelivery).toEqual('Cliente');
});
});

View File

@ -1,16 +1,4 @@
set_real_ip_from 0.0.0.0/0;
real_ip_header X-Forwarded-For;
log_format upstreamlog
'[$time_local] $remote_addr -> $proxy_host:$upstream_addr '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log upstreamlog;
upstream back {
server back_1:3000;
server back_2:3000;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
@ -24,9 +12,6 @@ server {
location @notfound {
return 302 /;
}
location ~ ^(/[a-zA-Z0-9_-]+)?/(?<path>api(/.*)?)$ {
proxy_pass http://back/$path$is_args$args;
}
location / {
autoindex on;
}

View File

@ -1,3 +1,5 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
/**
* Returns a set of allowed values defined on table scheme
@ -7,7 +9,6 @@ module.exports = Self => {
Self.getSetValues = async function(column) {
let model = this.app.models[this.modelName].definition;
let properties = model.properties;
let columnName;
let tableName = this.modelName;
let schema = null;
@ -17,35 +18,32 @@ module.exports = Self => {
schema = tableSplit.pop() || null;
}
if (properties[column]) {
columnName = column;
let property = properties[column];
if (properties[column].mysql)
columnName = properties[column].mysql.columnName;
}
if (!property)
throw new UserError(`Column does not exist`);
let findColumn = Object.keys(properties).find(prop => {
return properties[prop].mysql && properties[prop].mysql.columnName === column;
});
let columnName = property.mysql
? property.mysql.columnName
: column;
if (findColumn)
columnName = properties[findColumn].mysql.columnName;
let type = await this.rawSql(
`SELECT DISTINCT column_type FROM information_schema.columns
let columnInfo = await this.rawSql(
`SELECT column_type columnType
FROM information_schema.columns
WHERE table_name = ?
AND table_schema = IFNULL(?, DATABASE())
AND column_name = ?`,
[tableName, schema, columnName]
);
if (!type) return;
if (!columnInfo || !columnInfo[0])
throw new UserError(`Cannot fetch column values`);
let setValues;
setValues = type[0].column_type;
setValues = setValues.replace(/set\((.*)\)/i, '$1');
setValues = setValues.replace(/'/g, '');
setValues = setValues.match(new RegExp(/(\w+)+/, 'ig'));
setValues = columnInfo[0].columnType
.replace(/^set\((.*)\)$/i, '$1')
.replace(/'/g, '')
.match(new RegExp(/(\w+)+/, 'ig'));
let values = [];
setValues.forEach(setValue => {

View File

@ -13,11 +13,4 @@ describe('Model getSetValues()', () => {
expect(result.length).toEqual(6);
expect(result[5].value).toEqual('TABLET_VN');
});
it('should return an array of set values from table column source_app', async() => {
let result = await app.models.Order.getSetValues('source_app');
expect(result.length).toEqual(6);
expect(result[5].value).toEqual('TABLET_VN');
});
});

View File

@ -20,7 +20,7 @@ module.exports = function(Self) {
this.beginTransaction = function(options, cb) {
options = options || {};
if (!options.timeout)
options.timeout = 30000;
options.timeout = 120000;
return orgBeginTransaction.call(this, options, cb);
};
});

View File

@ -56,5 +56,6 @@
"Value has an invalid format": "Value has an invalid format",
"The postcode doesn't exists. Ensure you put the correct format": "The postcode doesn't exists. Ensure you put the correct format",
"Can't create stowaway for this ticket": "Can't create stowaway for this ticket",
"Has deleted the ticket id": "Has deleted the ticket id [#{{id}}]({{{url}}})"
"Has deleted the ticket id": "Has deleted the ticket id [#{{id}}]({{{url}}})",
"Swift / BIC can't be empty": "Swift / BIC can't be empty"
}

View File

@ -117,5 +117,6 @@
"You should specify a date": "Debes especificar una fecha",
"You should specify at least a start or end date": "Debes especificar al menos una fecha de inicio o de fín",
"Start date should be lower than end date": "La fecha de inicio debe ser menor que la fecha de fín",
"You should mark at least one week day": "Debes marcar al menos un día de la semana"
"You should mark at least one week day": "Debes marcar al menos un día de la semana",
"Swift / BIC can't be empty": "Swift / BIC no puede estar vacío"
}

View File

@ -104,9 +104,6 @@ module.exports = Self => {
}
}
},
{
relation: 'claimDestination'
},
{
relation: 'claimReason'
},

View File

@ -18,9 +18,6 @@
"claimResponsibleFk": {
"required": true
},
"workerFk": {
"required": true
},
"claimReasonFk": {
"required": true
},
@ -29,9 +26,6 @@
},
"claimRedeliveryFk": {
"required": true
},
"claimDestinationFk": {
"required": true
}
},
"relations": {
@ -64,11 +58,6 @@
"type": "belongsTo",
"model": "ClaimRedelivery",
"foreignKey": "claimRedeliveryFk"
},
"claimDestination": {
"type": "belongsTo",
"model": "ClaimDestination",
"foreignKey": "claimDestinationFk"
}
}
}

View File

@ -57,11 +57,11 @@
<span class="link"
vn-tooltip="Edit discount"
ng-click="$ctrl.showEditPopover($event, saleClaimed)">
{{::saleClaimed.sale.discount}} %
{{saleClaimed.sale.discount}} %
</span>
</vn-td>
<vn-td number>
{{::$ctrl.getSaleTotal(saleClaimed.sale) | currency: 'EUR':2}}
{{$ctrl.getSaleTotal(saleClaimed.sale) | currency: 'EUR':2}}
</vn-td>
<vn-td shrink>
<vn-icon-button
@ -125,31 +125,33 @@
<vn-popover
class="edit"
vn-id="edit-popover"
on-open="$ctrl.getManaSalespersonMana()"
on-open="$ctrl.getSalespersonMana()"
on-close="$ctrl.mana = null">
<vn-spinner
ng-if="$ctrl.mana == null"
style="padding: 1em;"
enable="true">
</vn-spinner>
<div ng-if="$ctrl.mana != null">
<vn-horizontal class="header vn-pa-md">
<h5>MANÁ: {{$ctrl.mana | currency: 'EUR':0}}</h5>
</vn-horizontal>
<div class="vn-pa-md">
<vn-input-number
vn-focus
label="Discount"
ng-model="$ctrl.newDiscount"
type="text"
step="0.01"
on-change="$ctrl.updateDiscount()"
suffix="€">
</vn-input-number>
<div class="simulator">
<p class="simulatorTitle" translate>New price</p>
<p>{{$ctrl.newPrice | currency: 'EUR':2}}
</p>
<div class="discount-popover">
<vn-spinner
ng-if="$ctrl.mana == null"
style="padding: 1em;"
enable="true">
</vn-spinner>
<div ng-if="$ctrl.mana != null">
<vn-horizontal class="header vn-pa-md">
<h5>MANÁ: {{$ctrl.mana | currency: 'EUR':0}}</h5>
</vn-horizontal>
<div class="vn-pa-md">
<vn-input-number
vn-focus
label="Discount"
ng-model="$ctrl.newDiscount"
type="text"
step="0.01"
on-change="$ctrl.updateDiscount()"
suffix="€">
</vn-input-number>
<div class="simulator">
<p class="simulatorTitle" translate>Total claimed price</p>
<p>{{$ctrl.newPrice | currency: 'EUR':2}}
</p>
</div>
</div>
</div>
</div>

View File

@ -128,25 +128,30 @@ class Controller {
this.$.editPopover.show();
}
getManaSalespersonMana() {
getSalespersonMana() {
this.$http.get(`Tickets/${this.claim.ticketFk}/getSalesPersonMana`).then(res => {
this.mana = res.data;
});
}
updateDiscount() {
if (this.newDiscount != this.saleClaimed.sale.discount) {
const params = {salesIds: [this.saleClaimed.sale.id], newDiscount: this.newDiscount};
const query = `Tickets/${this.saleClaimed.sale.ticketFk}/updateDiscount`;
const claimedSale = this.saleClaimed.sale;
if (this.newDiscount != claimedSale.discount) {
const params = {salesIds: [claimedSale.id], newDiscount: this.newDiscount};
const query = `Tickets/${claimedSale.ticketFk}/updateDiscount`;
this.$http.post(query, params).then(() => {
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
claimedSale.discount = this.newDiscount;
this.calculateTotals();
this.clearDiscount();
this.$.model.refresh();
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
}).catch(err => {
this.vnApp.showError(err.message);
});
}
this.$.editPopover.hide();
}
updateNewPrice() {

View File

@ -29,6 +29,9 @@ describe('claim', () => {
hide: () => {},
show: () => {}
};
controller.$.editPopover = {
hide: () => {}
};
}));
describe('openAddSalesDialog()', () => {
@ -110,16 +113,18 @@ describe('claim', () => {
spyOn(controller.vnApp, 'showSuccess');
spyOn(controller, 'calculateTotals');
spyOn(controller, 'clearDiscount');
spyOn(controller.$.model, 'refresh');
spyOn(controller.$.editPopover, 'hide');
$httpBackend.when('POST', 'Tickets/1/updateDiscount').respond({});
controller.updateDiscount();
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
expect(controller.calculateTotals).toHaveBeenCalledWith();
expect(controller.clearDiscount).toHaveBeenCalledWith();
expect(controller.$.model.refresh).toHaveBeenCalledWith();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
expect(controller.$.editPopover.hide).toHaveBeenCalledWith();
});
});

View File

@ -6,4 +6,5 @@ Price: Precio
Claimable sales from ticket: Lineas reclamables del ticket
Detail: Detalles
Add sale item: Añadir artículo
Insuficient permisos: Permisos insuficientes
Insuficient permisos: Permisos insuficientes
Total claimed price: Precio total reclamado

View File

@ -1,47 +1,27 @@
@import "variables";
vn-claim-detail {
.vn-textfield {
margin: 0!important;
max-width: 100px;
}
vn-dialog[vn-id=addSales] {
tpl-body {
width: 950px;
div {
div.buttons {
display: none;
}
vn-table{
min-width: 950px;
}
}
}
}
vn-popover.edit {
div.popover {
width: 200px;
}
vn-horizontal.header {
background-color: $color-main;
color: $color-font-dark;
.vn-popover .discount-popover {
width: 16em;
h5 {
color: inherit;
margin: 0 auto;
}
}
p.simulatorTitle {
margin-bottom: 0px;
font-size: 12px;
color: $color-main;
}
vn-label-value {
padding-bottom: 20px;
}
div.simulator{
text-align: center;
.header {
background-color: $color-main;
color: $color-font-dark;
h5 {
color: inherit;
margin: 0 auto;
}
}
.simulatorTitle {
margin-bottom: 0px;
font-size: 12px;
color: $color-main;
}
vn-label-value {
padding-bottom: 20px;
}
.simulator{
text-align: center;
}
}

View File

@ -15,5 +15,4 @@ Received B2B VNL: Recibido B2B VNL
Save: Guardar
New bank entity: Nueva entidad bancaria
Name can't be empty: El nombre no puede quedar vacío
Swift / BIC can't be empty: El Swift / BIC no puede quedar vacío
Entity Code: Código

View File

@ -46,7 +46,7 @@ module.exports = Self => {
},
{relation: 'intrastat'},
{relation: 'itemBarcode'},
{relation: 'expence'},
{relation: 'expense'},
{relation: 'origin'},
{relation: 'taxes',
scope: {

View File

@ -1,5 +1,5 @@
{
"Expence": {
"Expense": {
"dataSource": "vn"
},
"Genus": {

View File

@ -1,5 +1,5 @@
{
"name": "Expence",
"name": "Expense",
"base": "VnModel",
"options": {
"mysql": {

View File

@ -116,6 +116,12 @@
"hasKgPrice": {
"type": "Boolean",
"description": "Price per Kg"
},
"expenseFk": {
"type": "Number",
"mysql": {
"columnName": "expenceFk"
}
}
},
"relations": {
@ -144,10 +150,10 @@
"model": "Intrastat",
"foreignKey": "intrastatFk"
},
"expence": {
"expense": {
"type": "belongsTo",
"model": "Expence",
"foreignKey": "expenceFk"
"model": "Expense",
"foreignKey": "expenseFk"
},
"tags": {
"type": "hasMany",

View File

@ -1,7 +1,7 @@
<mg-ajax
path="Items/{{patch.params.id}}"
options="vnPatch"
override="{filter: {include: [{relation: 'itemType'}, {relation: 'origin'}, {relation: 'ink'}, {relation: 'producer'}, {relation: 'expence'}]}}">
override="{filter: {include: [{relation: 'itemType'}, {relation: 'origin'}, {relation: 'ink'}, {relation: 'producer'}, {relation: 'expense'}]}}">
</mg-ajax>
<vn-watcher
vn-id="watcher"
@ -49,10 +49,10 @@
</tpl-item>
</vn-autocomplete>
<vn-autocomplete vn-one
url="Expences"
label="Expence"
ng-model="$ctrl.item.expenceFk"
initial-data="$ctrl.item.expence">
url="Expenses"
label="Expense"
ng-model="$ctrl.item.expenseFk"
initial-data="$ctrl.item.expense">
</vn-autocomplete>
<vn-autocomplete vn-one
url="Origins"

View File

@ -4,5 +4,5 @@ Full name calculates based on tags 1-3. Is not recommended to change it manually
basado en los tags 1-3.
No se recomienda cambiarlo manualmente
Is active: Activo
Expence: Gasto
Expense: Gasto
Price in kg: Precio en kg

View File

@ -60,8 +60,8 @@
<vn-label-value label="Compression"
value="{{$ctrl.summary.item.compression}}">
</vn-label-value>
<vn-label-value label="Expence"
value="{{$ctrl.summary.item.expence.name}}">
<vn-label-value label="Expense"
value="{{$ctrl.summary.item.expense.name}}">
</vn-label-value>
</vn-one>
<vn-one name="tags">

View File

@ -0,0 +1,29 @@
module.exports = Self => {
Self.remoteMethod('getDeliveryPoint', {
description: 'get the deliveryPoint address ',
accessType: 'WRITE',
accepts: {
arg: 'vehicleId',
type: 'number',
required: true,
description: 'vehicle id asigned in the route',
http: {source: 'path'}
},
returns: {
type: 'String',
root: true
},
http: {
path: `/:vehicleId/getDeliveryPoint`
}
});
Self.getDeliveryPoint = async vehicleId => {
let vehicle = await Self.app.models.Vehicle.findById(vehicleId);
let deliveryPoint = await Self.app.models.DeliveryPoint.findById(vehicle.deliveryPointFk);
return deliveryPoint.ubication;
};
};

View File

@ -0,0 +1,13 @@
const app = require('vn-loopback/server/server');
describe('route getDeliveryPoint()', () => {
const routeId = 1;
const deliveryPointAddress = '46460 Av Espioca 100-Silla';
it('should get the delivery point addres of a route with assigned vehicle', async() => {
let route = await app.models.Route.findById(routeId);
let address = await app.models.Route.getDeliveryPoint(route.vehicleFk);
expect(address).toEqual(deliveryPointAddress);
});
});

View File

@ -7,5 +7,10 @@
},
"RouteLog": {
"dataSource": "vn"
},
"DeliveryPoint": {
"dataSource": "vn"
}
}

View File

@ -0,0 +1,22 @@
{
"name": "DeliveryPoint",
"base": "VnModel",
"options": {
"mysql": {
"table": "deliveryPoint"
}
},
"properties": {
"id": {
"type": "Number",
"id": true,
"description": "Identifier"
},
"name": {
"type": "String"
},
"ubication": {
"type": "String"
}
}
}

View File

@ -4,4 +4,5 @@ module.exports = Self => {
require('../methods/route/getTickets')(Self);
require('../methods/route/guessPriority')(Self);
require('../methods/route/updateVolume')(Self);
require('../methods/route/getDeliveryPoint')(Self);
};

View File

@ -41,6 +41,11 @@
"type": "belongsTo",
"model": "Warehouse",
"foreignKey": "warehouseFk"
},
"deliveryPoint": {
"type": "belongsTo",
"model": "DeliveryPoint",
"foreignKey": "deliveryPointFk"
}
},
"scope": {

View File

@ -85,20 +85,26 @@ class Controller {
}
goToBuscaman() {
// firstAddress is a temporal variable, will be replaced with #1298
let firstAddress = `46460 Av Espioca 100-46460 Silla`;
let addresses = firstAddress;
let query = `Routes/${this.route.vehicleFk}/getDeliveryPoint`;
let deliveryPointAddress;
let addresses;
let lines = this.getSelectedItems(this.tickets);
this.$http.get(query).then(response => {
deliveryPointAddress = response.data;
}).then(() => {
addresses = deliveryPointAddress;
let lines = this.getSelectedItems(this.tickets);
let url = 'http://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=';
lines.forEach(line => {
addresses = addresses + '+to:' + line.address.postalCode + ' ' + line.address.street + '-' + line.address.postalCode + ' ' + line.address.city;
let url = 'http://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=';
lines.forEach(line => {
addresses = addresses + '+to:' + line.address.postalCode + ' ' + line.address.city + ' ' + line.address.street;
});
window.open(url + addresses, '_blank');
});
window.open(url + addresses, '_blank');
}
showDeleteConfirm(id) {
this.selectedTicket = id;
this.$.confirm.show();

View File

@ -120,7 +120,11 @@ describe('Route', () => {
describe('goToBuscaman()', () => {
it('should open buscaman with the given arguments', () => {
spyOn(window, 'open');
const expectedUrl = 'http://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=46460 Av Espioca 100-46460 Silla+to:n19 my street-n19 London';
const expectedUrl = 'http://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=46460 Av Espioca 100+to:n19 London my street';
controller.route = {vehicleFk: 1};
const url = `Routes/${controller.route.vehicleFk}/getDeliveryPoint`;
$httpBackend.expectGET(url).respond('46460 Av Espioca 100');
controller.tickets = [
{
id: 1,
@ -134,6 +138,7 @@ describe('Route', () => {
];
controller.goToBuscaman();
$httpBackend.flush();
expect(window.open).toHaveBeenCalledWith(expectedUrl, '_blank');
});

View File

@ -14,6 +14,20 @@
"name": {
"type": "String",
"required": true
},
"expenseFk": {
"type": "Number",
"required": true,
"mysql": {
"columnName": "expenceFk"
}
}
},
"relations": {
"expenditure": {
"type": "belongsTo",
"model": "Expense",
"foreignKey": "expenseFk"
}
}
}

View File

@ -12,6 +12,11 @@ module.exports = Self => {
let isEditable = await models.Ticket.isEditable(httpCtx, ticketId);
if (!isEditable)
throw new UserError(`The current ticket can't be modified`);
if (changes.ticketServiceTypeFk) {
const ticketServiceType = await models.TicketServiceType.findById(changes.ticketServiceTypeFk);
changes.description = ticketServiceType.name;
}
}
});

View File

@ -36,6 +36,10 @@
"taxClassFk": {
"type": "Number",
"required": true
},
"ticketServiceTypeFk": {
"type": "Number",
"required": true
}
},
"relations": {
@ -48,6 +52,11 @@
"type": "belongsTo",
"model": "Ticket",
"foreignKey": "ticketFk"
},
"ticketService": {
"type": "belongsTo",
"model": "TicketServiceType",
"foreignKey": "ticketServiceTypeFk"
}
}
}

View File

@ -12,14 +12,12 @@
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal ng-repeat="service in $ctrl.services track by $index">
<vn-autocomplete
vn-one
vn-focus
<vn-autocomplete vn-two vn-focus
url="TicketServiceTypes"
label="Description"
show-field="name"
value-field="name"
ng-model="service.description">
value-field="id"
ng-model="service.ticketServiceTypeFk">
<append>
<vn-icon-button
icon="add_circle"
@ -87,6 +85,15 @@
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one vn-focus
url="Expenses"
label="Expense"
show-field="name"
value-field="id"
ng-model="$ctrl.newServiceType.expenseFk">
</vn-autocomplete>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>

View File

@ -50,7 +50,7 @@ class Controller {
throw new UserError(`Name can't be empty`);
this.$http.post(`TicketServiceTypes`, this.newServiceType).then(response => {
this.services[this.currentServiceIndex].description = response.data.name;
this.services[this.currentServiceIndex].ticketServiceTypeFk = response.data.id;
});
}
}

View File

@ -54,11 +54,11 @@ describe('Ticket component vnTicketService', () => {
controller.newServiceType = {name: 'totally new stuff'};
controller.currentServiceIndex = 0;
$httpBackend.when('POST', 'TicketServiceTypes').respond({id: 4001, name: 'great service!'});
$httpBackend.when('POST', 'TicketServiceTypes').respond({id: 4001, name: 'totally new stuff'});
controller.onNewServiceTypeResponse('accept');
$httpBackend.flush();
expect(controller.services[0].description).toEqual('great service!');
expect(controller.services[0].ticketServiceTypeFk).toEqual(4001);
});
});
});

View File

@ -35,7 +35,11 @@ xdescribe('Worker absences()', () => {
let workerFk = 106;
let worker = await app.models.WorkerLabour.findById(workerFk);
let endedDate = worker.ended;
await worker.updateAttributes({ended: null});
await app.models.WorkerLabour.rawSql(
`UPDATE postgresql.business SET date_end = ? WHERE business_id = ?`,
[null, worker.businessFk]
);
let ctx = {req: {accessToken: {userId: 9}}};
@ -64,7 +68,10 @@ xdescribe('Worker absences()', () => {
expect(sixthType).toEqual('Holidays');
// restores the contract end date
await worker.updateAttributes({ended: endedDate});
await app.models.WorkerLabour.rawSql(
`UPDATE postgresql.business SET date_end = ? WHERE business_id = ?`,
[endedDate, worker.businessFk]
);
});
it('should give the same holidays as worked days since the holidays amount matches the amount of days in a year', async() => {
@ -113,7 +120,10 @@ xdescribe('Worker absences()', () => {
startingContract.setMonth(today.getMonth());
startingContract.setDate(1);
await contract.updateAttributes({started: startingContract});
await app.models.WorkerLabour.rawSql(
`UPDATE postgresql.business SET date_start = ? WHERE business_id = ?`,
[startingContract, contract.businessFk]
);
let ctx = {req: {accessToken: {userId: 106}}};
let workerFk = 106;
@ -149,6 +159,10 @@ xdescribe('Worker absences()', () => {
days: originalHolidaysValue
}
);
await contract.updateAttributes({started: contractStartDate});
await app.models.WorkerLabour.rawSql(
`UPDATE postgresql.business SET date_start = ? WHERE business_id = ?`,
[contractStartDate, contract.businessFk]
);
});
});

View File

@ -24,7 +24,7 @@ class Controller {
onSubmit() {
this.$scope.watcher.check();
this.$scope.model.save().then(() => {
return this.$scope.model.save().then(() => {
this.$scope.watcher.updateOriginalData();
this.$scope.watcher.notifySaved();
this.card.reload();

View File

@ -1,4 +1,5 @@
import './index';
import watcher from 'core/mocks/watcher';
describe('Component vnWorkerPhones', () => {
let controller;
@ -8,8 +9,12 @@ describe('Component vnWorkerPhones', () => {
beforeEach(angular.mock.inject(($componentController, $rootScope) => {
let $scope = $rootScope.$new();
controller = $componentController('vnWorkerPhones', $scope);
controller.$scope.model = {link: 1};
controller.$scope.$applyAsync = () => {};
controller.$scope.watcher = watcher;
controller.$scope.model = {
link: 1,
save: () => {}
};
controller.card = {reload: () => {}};
}));
describe('setLink()', () => {
@ -21,4 +26,22 @@ describe('Component vnWorkerPhones', () => {
expect(controller.$scope.$applyAsync).toHaveBeenCalledWith(jasmine.any(Function));
});
});
describe('onSubmit()', () => {
it('should call watcher functions, reload the card and save the model', done => {
spyOn(controller.$scope.watcher, 'check');
spyOn(controller.$scope.model, 'save').and.returnValue(Promise.resolve());
spyOn(controller.$scope.watcher, 'updateOriginalData');
spyOn(controller.$scope.watcher, 'notifySaved');
spyOn(controller.card, 'reload');
controller.onSubmit();
controller.onSubmit().then(() => {
expect(controller.$scope.watcher.updateOriginalData).toHaveBeenCalledWith();
expect(controller.$scope.watcher.notifySaved).toHaveBeenCalledWith();
expect(controller.card.reload).toHaveBeenCalledWith();
done();
}).catch(done.fail);
});
});
});