Merge branch 'dev' into 5328-abonarTodoTicket
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Carlos Satorres 2023-05-25 08:25:47 +00:00
commit d82a7eeeb7
178 changed files with 8755 additions and 6622 deletions

View File

@ -5,13 +5,29 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2322.01] - 2023-06-08
### Added
- (Tickets -> Crear Factura) Al facturar se envia automáticamente el pdf al cliente
- (Artículos -> Histórico) Filtro para mostrar lo anterior al inventario
### Changed
- (Trabajadores -> Nuevo trabajador) Los clientes se crean sin 'TR' pero se añade tipo de negocio 'Trabajador'
### Fixed
- (Tickets -> Líneas) Se permite hacer split de líneas al mismo ticket
## [2320.01] - 2023-05-25 ## [2320.01] - 2023-05-25
### Added ### Added
- - (Tickets -> Crear Factura) Al facturar se envia automáticamente el pdf al cliente
### Changed ### Changed
- - (Trabajadores -> Nuevo trabajador) Los clientes se crean sin 'TR' pero se añade tipo de negocio 'Trabajador'
### Fixed ### Fixed
- -
@ -23,10 +39,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- (Usuarios -> Histórico) Nueva sección - (Usuarios -> Histórico) Nueva sección
- (Roles -> Histórico) Nueva sección - (Roles -> Histórico) Nueva sección
- (General -> Traducciones) Correo de bienvenida a clientes al portugués y al francés - (Trabajadores -> Dar de alta) Permite elegir el método de pago
### Changed ### Changed
- (Artículo -> Precio fijado) Modificado el buscador superior por uno lateral - (Artículo -> Precio fijado) Modificado el buscador superior por uno lateral
- (Trabajadores -> Dar de alta) Quitada obligatoriedad del iban
### Fixed ### Fixed
- (Ticket -> Boxing) Arreglado selección de horas - (Ticket -> Boxing) Arreglado selección de horas

View File

@ -4,25 +4,25 @@ module.exports = Self => {
Self.remoteMethodCtx('deliveryNoteEmail', { Self.remoteMethodCtx('deliveryNoteEmail', {
description: 'Sends the delivery note email with an docuware attached PDF', description: 'Sends the delivery note email with an docuware attached PDF',
accessType: 'WRITE', accessType: 'WRITE',
accessScopes: ['docuwareDeliveryNoteEmail'],
accepts: [ accepts: [
{ {
arg: 'id', arg: 'id',
type: 'string', type: 'number',
required: true, required: true,
description: 'The ticket id', description: 'The ticket id',
http: {source: 'path'}
},
{
arg: 'recipient',
type: 'string',
description: 'The recipient email',
required: true,
}, },
{ {
arg: 'recipientId', arg: 'recipientId',
type: 'number', type: 'number',
description: 'The client id', description: 'The client id',
required: false required: true
},
{
arg: 'recipient',
type: 'string',
description: 'The recipient email',
required: false,
} }
], ],
returns: [ returns: [
@ -41,12 +41,13 @@ module.exports = Self => {
} }
], ],
http: { http: {
path: '/:id/delivery-note-email', path: '/delivery-note-email',
verb: 'POST' verb: 'POST'
} }
}); });
Self.deliveryNoteEmail = async(ctx, id) => { Self.deliveryNoteEmail = async(ctx, id, recipientId, recipient) => {
const models = Self.app.models;
const args = Object.assign({}, ctx.args); const args = Object.assign({}, ctx.args);
const params = { const params = {
recipient: args.recipient, recipient: args.recipient,
@ -57,9 +58,14 @@ module.exports = Self => {
for (const param in args) for (const param in args)
params[param] = args[param]; params[param] = args[param];
if (!recipient) {
client = await models.Client.findById(recipientId, {fields: ['email']});
params.recipient = client.email;
}
const email = new Email('delivery-note', params); const email = new Email('delivery-note', params);
const docuwareFile = await Self.app.models.Docuware.download(ctx, id, 'deliveryNote'); const docuwareFile = await models.Docuware.download(ctx, id, 'deliveryNote');
return email.send({ return email.send({
overrideAttachments: true, overrideAttachments: true,

View File

@ -23,13 +23,7 @@ module.exports = Self => {
let models = Self.app.models; let models = Self.app.models;
let user = await Self.findById(userId, { let user = await Self.findById(userId, {
fields: ['id', 'name', 'nickname', 'email', 'lang'], fields: ['id', 'name', 'nickname', 'email', 'lang']
include: {
relation: 'userConfig',
scope: {
fields: ['darkMode']
}
}
}); });
let roles = await models.RoleMapping.find({ let roles = await models.RoleMapping.find({

View File

@ -55,7 +55,7 @@ async function test() {
const JunitReporter = require('jasmine-reporters'); const JunitReporter = require('jasmine-reporters');
jasmine.addReporter(new JunitReporter.JUnitXmlReporter()); jasmine.addReporter(new JunitReporter.JUnitXmlReporter());
jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000;
jasmine.exitOnCompletion = true; jasmine.exitOnCompletion = true;
} }

View File

@ -1,9 +1,10 @@
FROM mariadb:10.7.3 FROM mariadb:10.7.5
ENV MYSQL_ROOT_PASSWORD root ENV MYSQL_ROOT_PASSWORD root
ENV TZ Europe/Madrid ENV TZ Europe/Madrid
ARG MOCKDATE=2001-01-01 11:00:00
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y --no-install-recommends curl ca-certificates \ && apt-get install -y --no-install-recommends curl ca-certificates \
&& curl -sL https://apt.verdnatura.es/conf/verdnatura.gpg | apt-key add - \ && curl -sL https://apt.verdnatura.es/conf/verdnatura.gpg | apt-key add - \
@ -31,14 +32,15 @@ COPY \
import-changes.sh \ import-changes.sh \
config.ini \ config.ini \
dump/mysqlPlugins.sql \ dump/mysqlPlugins.sql \
dump/mockDate.sql \
dump/structure.sql \ dump/structure.sql \
dump/mockDate.sql \
dump/dumpedFixtures.sql \ dump/dumpedFixtures.sql \
./ ./
RUN gosu mysql docker-init.sh \ RUN gosu mysql docker-init.sh \
&& docker-dump.sh mysqlPlugins \ && docker-dump.sh mysqlPlugins \
&& docker-dump.sh mockDate \
&& docker-dump.sh structure \ && docker-dump.sh structure \
&& sed -i -e 's/@mockDate/'"$MOCKDATE"'/g' mockDate.sql \
&& docker-dump.sh mockDate \
&& docker-dump.sh dumpedFixtures \ && docker-dump.sh dumpedFixtures \
&& gosu mysql docker-temp-stop.sh && gosu mysql docker-temp-stop.sh

View File

@ -0,0 +1,12 @@
-- vn.companyL10n source
CREATE OR REPLACE
ALGORITHM = UNDEFINED VIEW `vn`.`companyL10n` AS
select
`c`.`id` AS `id`,
ifnull(`ci`.`footnotes`, `c`.`footnotes`) AS `footnotes`
from
(`vn`.`company` `c`
left join `vn`.`companyI18n` `ci` on
(`ci`.`companyFk` = `c`.`id`
and `ci`.`lang` = `util`.`LANG`()));

View File

@ -0,0 +1,73 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`clientCreate`(
vFirstname VARCHAR(50),
vSurnames VARCHAR(50),
vFi VARCHAR(9),
vAddress TEXT,
vPostcode CHAR(5),
vCity VARCHAR(25),
vProvinceFk SMALLINT(5),
vCompanyFk SMALLINT(5),
vPhone VARCHAR(11),
vEmail VARCHAR(255),
vUserFk INT)
BEGIN
/**
* Create new client
*
*/
DECLARE vPayMethodFk INT DEFAULT 4;
DECLARE vDueDay INT DEFAULT 5;
DECLARE vDefaultCredit DECIMAL(10, 2) DEFAULT 300.00;
DECLARE vIsTaxDataChecked TINYINT(1) DEFAULT 1;
DECLARE vHasCoreVnl BOOLEAN DEFAULT TRUE;
DECLARE vMandateTypeFk INT DEFAULT 2;
INSERT INTO `client` (
id,
name,
street,
fi,
phone,
email,
provinceFk,
city,
postcode,
socialName,
payMethodFk,
dueDay,
credit,
isTaxDataChecked,
hasCoreVnl,
isEqualizated)
VALUES (
vUserFk,
CONCAT(vFirstname, ' ', vSurnames),
vAddress,
TRIM(vFi),
vPhone,
vEmail,
vProvinceFk,
vCity,
vPostcode,
CONCAT(vSurnames, ' ', vFirstname),
vPayMethodFk,
vDueDay,
vDefaultCredit,
vIsTaxDataChecked,
vHasCoreVnl,
FALSE
) ON duplicate key update
payMethodFk = vPayMethodFk,
dueDay = vDueDay,
credit = vDefaultCredit,
isTaxDataChecked = vIsTaxDataChecked,
hasCoreVnl = vHasCoreVnl,
isActive = TRUE;
IF (SELECT COUNT(*) FROM mandate WHERE clientFk = vUserFk AND companyFk = vCompanyFk AND mandateTypeFk = vMandateTypeFk) = 0 THEN
INSERT INTO mandate (clientFk, companyFk, mandateTypeFk)
VALUES (vUserFk, vCompanyFk, vMandateTypeFk);
END IF;
END$$
DELIMITER ;

View File

@ -0,0 +1,14 @@
INSERT INTO `vn`.`businessType` (`code`, `description`)
VALUES ('worker','Trabajador');
ALTER TABLE `vn`.`workerConfig` ADD businessTypeFk varchar(100) NULL
COMMENT 'Tipo de negocio por defecto al dar de alta un trabajador nuevo';
UPDATE `vn`.`workerConfig`
SET businessTypeFk = 'worker'
WHERE id = 1;
UPDATE `vn`.`client` c
JOIN `vn`.`worker` w ON w.id = c.id
SET c.name = REPLACE(c.name, 'TR ', ''),
c.businessTypeFk = 'worker';

View File

@ -0,0 +1,254 @@
DROP PROCEDURE IF EXISTS `vn`.`invoiceOut_new`;
DELIMITER $$
$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`invoiceOut_new`(
vSerial VARCHAR(255),
vInvoiceDate DATE,
vTaxArea VARCHAR(25),
OUT vNewInvoiceId INT)
BEGIN
/**
* Creación de facturas emitidas.
* requiere previamente tabla tmp.ticketToInvoice(id).
*
* @param vSerial serie a la cual se hace la factura
* @param vInvoiceDate fecha de la factura
* @param vTaxArea tipo de iva en relacion a la empresa y al cliente
* @param vNewInvoiceId id de la factura que se acaba de generar
* @return vNewInvoiceId
*/
DECLARE vIsAnySaleToInvoice BOOL;
DECLARE vIsAnyServiceToInvoice BOOL;
DECLARE vNewRef VARCHAR(255);
DECLARE vWorker INT DEFAULT account.myUser_getId();
DECLARE vCompanyFk INT;
DECLARE vInterCompanyFk INT;
DECLARE vClientFk INT;
DECLARE vCplusStandardInvoiceTypeFk INT DEFAULT 1;
DECLARE vCplusCorrectingInvoiceTypeFk INT DEFAULT 6;
DECLARE vCplusSimplifiedInvoiceTypeFk INT DEFAULT 2;
DECLARE vCorrectingSerial VARCHAR(1) DEFAULT 'R';
DECLARE vSimplifiedSerial VARCHAR(1) DEFAULT 'S';
DECLARE vNewInvoiceInFk INT;
DECLARE vIsInterCompany BOOL DEFAULT FALSE;
DECLARE vIsCEESerial BOOL DEFAULT FALSE;
DECLARE vIsCorrectInvoiceDate BOOL;
DECLARE vMaxShipped DATE;
SET vInvoiceDate = IFNULL(vInvoiceDate, util.VN_CURDATE());
SELECT t.clientFk,
t.companyFk,
MAX(DATE(t.shipped)),
DATE(vInvoiceDate) >= invoiceOut_getMaxIssued(
vSerial,
t.companyFk,
YEAR(vInvoiceDate))
INTO vClientFk,
vCompanyFk,
vMaxShipped,
vIsCorrectInvoiceDate
FROM tmp.ticketToInvoice tt
JOIN ticket t ON t.id = tt.id;
IF(vMaxShipped > vInvoiceDate) THEN
CALL util.throw("Invoice date can't be less than max date");
END IF;
IF NOT vIsCorrectInvoiceDate THEN
CALL util.throw('Exists an invoice with a previous date');
END IF;
-- Eliminem de tmp.ticketToInvoice els tickets que no han de ser facturats
DELETE ti.*
FROM tmp.ticketToInvoice ti
JOIN ticket t ON t.id = ti.id
JOIN sale s ON s.ticketFk = t.id
JOIN item i ON i.id = s.itemFk
JOIN supplier su ON su.id = t.companyFk
JOIN client c ON c.id = t.clientFk
LEFT JOIN itemTaxCountry itc ON itc.itemFk = i.id AND itc.countryFk = su.countryFk
WHERE (YEAR(t.shipped) < 2001 AND t.isDeleted)
OR c.isTaxDataChecked = FALSE
OR t.isDeleted
OR c.hasToInvoice = FALSE
OR itc.id IS NULL;
SELECT SUM(s.quantity * s.price * (100 - s.discount)/100) <> 0
INTO vIsAnySaleToInvoice
FROM tmp.ticketToInvoice t
JOIN sale s ON s.ticketFk = t.id;
SELECT COUNT(*) > 0 INTO vIsAnyServiceToInvoice
FROM tmp.ticketToInvoice t
JOIN ticketService ts ON ts.ticketFk = t.id;
IF (vIsAnySaleToInvoice OR vIsAnyServiceToInvoice)
AND (vCorrectingSerial = vSerial OR NOT hasAnyNegativeBase())
THEN
-- el trigger añade el siguiente Id_Factura correspondiente a la vSerial
INSERT INTO invoiceOut(
ref,
serial,
issued,
clientFk,
dued,
companyFk,
cplusInvoiceType477Fk
)
SELECT
1,
vSerial,
vInvoiceDate,
vClientFk,
getDueDate(vInvoiceDate, dueDay),
vCompanyFk,
IF(vSerial = vCorrectingSerial,
vCplusCorrectingInvoiceTypeFk,
IF(vSerial = vSimplifiedSerial,
vCplusSimplifiedInvoiceTypeFk,
vCplusStandardInvoiceTypeFk))
FROM client
WHERE id = vClientFk;
SET vNewInvoiceId = LAST_INSERT_ID();
SELECT `ref`
INTO vNewRef
FROM invoiceOut
WHERE id = vNewInvoiceId;
UPDATE ticket t
JOIN tmp.ticketToInvoice ti ON ti.id = t.id
SET t.refFk = vNewRef;
DROP TEMPORARY TABLE IF EXISTS tmp.updateInter;
CREATE TEMPORARY TABLE tmp.updateInter ENGINE = MEMORY
SELECT s.id,ti.id ticket_id,vWorker Id_Trabajador
FROM tmp.ticketToInvoice ti
LEFT JOIN ticketState ts ON ti.id = ts.ticket
JOIN state s
WHERE IFNULL(ts.alertLevel,0) < 3 and s.`code` = getAlert3State(ti.id);
INSERT INTO ticketTracking(stateFk,ticketFk,workerFk)
SELECT * FROM tmp.updateInter;
CALL invoiceExpenceMake(vNewInvoiceId);
CALL invoiceTaxMake(vNewInvoiceId,vTaxArea);
UPDATE invoiceOut io
JOIN (
SELECT SUM(amount) total
FROM invoiceOutExpence
WHERE invoiceOutFk = vNewInvoiceId
) base
JOIN (
SELECT SUM(vat) total
FROM invoiceOutTax
WHERE invoiceOutFk = vNewInvoiceId
) vat
SET io.amount = base.total + vat.total
WHERE io.id = vNewInvoiceId;
DROP TEMPORARY TABLE tmp.updateInter;
SELECT COUNT(*), id
INTO vIsInterCompany, vInterCompanyFk
FROM company
WHERE clientFk = vClientFk;
IF (vIsInterCompany) THEN
INSERT INTO invoiceIn(supplierFk, supplierRef, issued, companyFk)
SELECT vCompanyFk, vNewRef, vInvoiceDate, vInterCompanyFk;
SET vNewInvoiceInFk = LAST_INSERT_ID();
DROP TEMPORARY TABLE IF EXISTS tmp.ticket;
CREATE TEMPORARY TABLE tmp.ticket
(KEY (ticketFk))
ENGINE = MEMORY
SELECT id ticketFk
FROM tmp.ticketToInvoice;
CALL `ticket_getTax`('NATIONAL');
SET @vTaxableBaseServices := 0.00;
SET @vTaxCodeGeneral := NULL;
INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenceFk, taxTypeSageFk, transactionTypeSageFk)
SELECT vNewInvoiceInFk,
@vTaxableBaseServices,
sub.expenceFk,
sub.taxTypeSageFk,
sub.transactionTypeSageFk
FROM (
SELECT @vTaxableBaseServices := SUM(tst.taxableBase) taxableBase,
i.expenceFk,
i.taxTypeSageFk,
i.transactionTypeSageFk,
@vTaxCodeGeneral := i.taxClassCodeFk
FROM tmp.ticketServiceTax tst
JOIN invoiceOutTaxConfig i ON i.taxClassCodeFk = tst.code
WHERE i.isService
HAVING taxableBase
) sub;
INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenceFk, taxTypeSageFk, transactionTypeSageFk)
SELECT vNewInvoiceInFk,
SUM(tt.taxableBase) - IF(tt.code = @vTaxCodeGeneral,
@vTaxableBaseServices, 0) taxableBase,
i.expenceFk,
i.taxTypeSageFk ,
i.transactionTypeSageFk
FROM tmp.ticketTax tt
JOIN invoiceOutTaxConfig i ON i.taxClassCodeFk = tt.code
WHERE !i.isService
GROUP BY tt.pgcFk
HAVING taxableBase
ORDER BY tt.priority;
CALL invoiceInDueDay_calculate(vNewInvoiceInFk);
SELECT COUNT(*) INTO vIsCEESerial
FROM invoiceOutSerial
WHERE code = vSerial;
IF vIsCEESerial THEN
INSERT INTO invoiceInIntrastat (
invoiceInFk,
intrastatFk,
amount,
stems,
countryFk,
net)
SELECT
vNewInvoiceInFk,
i.intrastatFk,
SUM(CAST((s.quantity * s.price * (100 - s.discount) / 100 ) AS DECIMAL(10, 2))),
SUM(CAST(IFNULL(i.stems, 1) * s.quantity AS DECIMAL(10, 2))),
su.countryFk,
CAST(SUM(IFNULL(i.stems, 1)
* s.quantity
* IF(ic.grams, ic.grams, IFNULL(i.weightByPiece, 0)) / 1000) AS DECIMAL(10, 2))
FROM sale s
JOIN ticket t ON s.ticketFk = t.id
JOIN supplier su ON su.id = t.companyFk
JOIN item i ON i.id = s.itemFk
LEFT JOIN itemCost ic ON ic.itemFk = i.id AND ic.warehouseFk = t.warehouseFk
WHERE t.refFk = vNewRef
GROUP BY i.intrastatFk;
END IF;
DROP TEMPORARY TABLE tmp.ticket;
DROP TEMPORARY TABLE tmp.ticketAmount;
DROP TEMPORARY TABLE tmp.ticketTax;
DROP TEMPORARY TABLE tmp.ticketServiceTax;
END IF;
END IF;
DROP TEMPORARY TABLE `tmp`.`ticketToInvoice`;
END$$
DELIMITER ;

View File

@ -0,0 +1,127 @@
DELIMITER $$
$$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_canAdvance`(vDateFuture DATE, vDateToAdvance DATE, vWarehouseFk INT)
BEGIN
/**
* Devuelve los tickets y la cantidad de lineas de venta que se pueden adelantar.
*
* @param vDateFuture Fecha de los tickets que se quieren adelantar.
* @param vDateToAdvance Fecha a cuando se quiere adelantar.
* @param vWarehouseFk Almacén
*/
DECLARE vDateInventory DATE;
SELECT inventoried INTO vDateInventory FROM config;
DROP TEMPORARY TABLE IF EXISTS tmp.stock;
CREATE TEMPORARY TABLE tmp.stock
(itemFk INT PRIMARY KEY,
amount INT)
ENGINE = MEMORY;
INSERT INTO tmp.stock(itemFk, amount)
SELECT itemFk, SUM(quantity) amount FROM
(
SELECT itemFk, quantity
FROM itemTicketOut
WHERE shipped >= vDateInventory
AND shipped < vDateFuture
AND warehouseFk = vWarehouseFk
UNION ALL
SELECT itemFk, quantity
FROM itemEntryIn
WHERE landed >= vDateInventory
AND landed < vDateFuture
AND isVirtualStock = FALSE
AND warehouseInFk = vWarehouseFk
UNION ALL
SELECT itemFk, quantity
FROM itemEntryOut
WHERE shipped >= vDateInventory
AND shipped < vDateFuture
AND warehouseOutFk = vWarehouseFk
) t
GROUP BY itemFk HAVING amount != 0;
DROP TEMPORARY TABLE IF EXISTS tmp.filter;
CREATE TEMPORARY TABLE tmp.filter
(INDEX (id))
SELECT
origin.ticketFk futureId,
dest.ticketFk id,
dest.state,
origin.futureState,
origin.futureIpt,
dest.ipt,
origin.workerFk,
origin.futureLiters,
origin.futureLines,
dest.shipped,
origin.shipped futureShipped,
dest.totalWithVat,
origin.totalWithVat futureTotalWithVat,
dest.agency,
origin.futureAgency,
dest.lines,
dest.liters,
origin.futureLines - origin.hasStock AS notMovableLines,
(origin.futureLines = origin.hasStock) AS isFullMovable,
origin.classColor,
dest.classColor futureClassColor
FROM (
SELECT
s.ticketFk,
t.workerFk,
t.shipped,
t.totalWithVat,
st.name futureState,
t.addressFk,
am.name futureAgency,
count(s.id) futureLines,
GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) futureIpt,
CAST(SUM(litros) AS DECIMAL(10,0)) futureLiters,
SUM((s.quantity <= IFNULL(st.amount,0))) hasStock,
st.classColor
FROM ticket t
JOIN sale s ON s.ticketFk = t.id
JOIN saleVolume sv ON sv.saleFk = s.id
JOIN item i ON i.id = s.itemFk
JOIN ticketState ts ON ts.ticketFk = t.id
JOIN state st ON st.id = ts.stateFk
JOIN agencyMode am ON t.agencyModeFk = am.id
LEFT JOIN itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
LEFT JOIN tmp.stock st ON st.itemFk = i.id
WHERE t.shipped BETWEEN vDateFuture AND util.dayend(vDateFuture)
AND t.warehouseFk = vWarehouseFk
GROUP BY t.id
) origin
JOIN (
SELECT
t.id ticketFk,
t.addressFk,
st.name state,
GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) ipt,
t.shipped,
t.totalWithVat,
am.name agency,
CAST(SUM(litros) AS DECIMAL(10,0)) liters,
CAST(COUNT(*) AS DECIMAL(10,0)) `lines`,
st.classColor
FROM ticket t
JOIN sale s ON s.ticketFk = t.id
JOIN saleVolume sv ON sv.saleFk = s.id
JOIN item i ON i.id = s.itemFk
JOIN ticketState ts ON ts.ticketFk = t.id
JOIN state st ON st.id = ts.stateFk
JOIN agencyMode am ON t.agencyModeFk = am.id
LEFT JOIN itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
WHERE t.shipped BETWEEN vDateToAdvance AND util.dayend(vDateToAdvance)
AND t.warehouseFk = vWarehouseFk
AND st.order <= 5
GROUP BY t.id
) dest ON dest.addressFk = origin.addressFk
WHERE origin.hasStock != 0;
DROP TEMPORARY TABLE tmp.stock;
END$$
DELIMITER ;

View File

@ -0,0 +1,72 @@
DELIMITER $$
$$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_canbePostponed`(vOriginDated DATE, vFutureDated DATE, vWarehouseFk INT)
BEGIN
/**
* Devuelve un listado de tickets susceptibles de fusionarse con otros tickets en el futuro
*
* @param vOriginDated Fecha en cuestión
* @param vFutureDated Fecha en el futuro a sondear
* @param vWarehouseFk Identificador de vn.warehouse
*/
DROP TEMPORARY TABLE IF EXISTS tmp.filter;
CREATE TEMPORARY TABLE tmp.filter
(INDEX (id))
SELECT sv.ticketFk id,
sub2.id futureId,
GROUP_CONCAT(DISTINCT i.itemPackingTypeFk ORDER BY i.itemPackingTypeFk) ipt,
CAST(sum(litros) AS DECIMAL(10,0)) liters,
CAST(count(*) AS DECIMAL(10,0)) `lines`,
st.name state,
sub2.iptd futureIpt,
sub2.state futureState,
t.clientFk,
t.warehouseFk,
ts.alertLevel,
t.shipped,
sub2.shipped futureShipped,
t.workerFk,
st.code stateCode,
sub2.code futureStateCode,
st.classColor
FROM vn.saleVolume sv
JOIN vn.sale s ON s.id = sv.saleFk
JOIN vn.item i ON i.id = s.itemFk
JOIN vn.ticket t ON t.id = sv.ticketFk
JOIN vn.address a ON a.id = t.addressFk
JOIN vn.province p ON p.id = a.provinceFk
JOIN vn.country c ON c.id = p.countryFk
JOIN vn.ticketState ts ON ts.ticketFk = t.id
JOIN vn.state st ON st.id = ts.stateFk
JOIN vn.alertLevel al ON al.id = ts.alertLevel
LEFT JOIN vn.ticketParking tp ON tp.ticketFk = t.id
LEFT JOIN (
SELECT *
FROM (
SELECT
t.addressFk,
t.id,
t.shipped,
st.name state,
st.code code,
GROUP_CONCAT(DISTINCT i.itemPackingTypeFk ORDER BY i.itemPackingTypeFk) iptd
FROM vn.ticket t
JOIN vn.ticketState ts ON ts.ticketFk = t.id
JOIN vn.state st ON st.id = ts.stateFk
JOIN vn.sale s ON s.ticketFk = t.id
JOIN vn.item i ON i.id = s.itemFk
WHERE t.shipped BETWEEN vFutureDated
AND util.dayend(vFutureDated)
AND t.warehouseFk = vWarehouseFk
GROUP BY t.id
) sub
GROUP BY sub.addressFk
) sub2 ON sub2.addressFk = t.addressFk AND t.id != sub2.id
WHERE t.shipped BETWEEN vOriginDated AND util.dayend(vOriginDated)
AND t.warehouseFk = vWarehouseFk
AND al.code = 'FREE'
AND tp.ticketFk IS NULL
GROUP BY sv.ticketFk
HAVING futureId;
END$$
DELIMITER ;

View File

@ -0,0 +1,20 @@
-- vn.defaulter source
CREATE OR REPLACE
ALGORITHM = UNDEFINED VIEW `vn`.`defaulter` AS
select
`d`.`clientFk` AS `clientFk`,
`d`.`created` AS `created`,
`d`.`amount` AS `amount`,
`d`.`defaulterSinced` AS `defaulterSinced`,
`d`.`hasChanged` AS `hasChanged`,
`c`.`countryFk` AS `country`,
`c`.`payMethodFk` AS `payMethod`
from
(((`bs`.`defaulter` `d`
join `vn`.`client` `c` on
(`c`.`id` = `d`.`clientFk`))
join `vn`.`country` `co` on
(`co`.`id` = `c`.`countryFk`))
join `vn`.`payMethod` `pm` on
(`pm`.`id` = `c`.`payMethodFk`));

View File

@ -0,0 +1,7 @@
ALTER TABLE `vn`.`workerConfig` ADD payMethodFk tinyint(3) unsigned NULL;
ALTER TABLE `vn`.`workerConfig` ADD CONSTRAINT workerConfig_FK FOREIGN KEY (roleFk) REFERENCES account.`role`(id) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE `vn`.`workerConfig` ADD CONSTRAINT workerConfig_FK_1 FOREIGN KEY (payMethodFk) REFERENCES `vn`.`payMethod`(id) ON DELETE SET NULL ON UPDATE CASCADE;
-- Cuando se apruebe el PR quitar y poner en redmine para hacerse manualmente
UPDATE `vn`.`workerConfig`
SET payMethodFk = 4
WHERE id=1;

File diff suppressed because one or more lines are too long

View File

@ -349,20 +349,20 @@ INSERT INTO `vn`.`contactChannel`(`id`, `name`)
(4, 'GCN Channel'), (4, 'GCN Channel'),
(5, 'The Newspaper'); (5, 'The Newspaper');
INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city`,`postcode`,`phone`,`mobile`,`isRelevant`,`email`,`iban`,`dueDay`,`accountingAccount`,`isEqualizated`,`provinceFk`,`hasToInvoice`,`credit`,`countryFk`,`isActive`,`gestdocFk`,`quality`,`payMethodFk`,`created`,`isToBeMailed`,`contactChannelFk`,`hasSepaVnl`,`hasCoreVnl`,`hasCoreVnh`,`riskCalculated`,`clientTypeFk`,`mailAddress`,`hasToInvoiceByAddress`,`isTaxDataChecked`,`isFreezed`,`creditInsurance`,`isCreatedAsServed`,`hasInvoiceSimplified`,`salesPersonFk`,`isVies`,`eypbc`, `businessTypeFk`) INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city`,`postcode`,`phone`,`mobile`,`isRelevant`,`email`,`iban`,`dueDay`,`accountingAccount`,`isEqualizated`,`provinceFk`,`hasToInvoice`,`credit`,`countryFk`,`isActive`,`gestdocFk`,`quality`,`payMethodFk`,`created`,`isToBeMailed`,`contactChannelFk`,`hasSepaVnl`,`hasCoreVnl`,`hasCoreVnh`,`riskCalculated`,`clientTypeFk`, `hasToInvoiceByAddress`,`isTaxDataChecked`,`isFreezed`,`creditInsurance`,`isCreatedAsServed`,`hasInvoiceSimplified`,`salesPersonFk`,`isVies`,`eypbc`, `businessTypeFk`)
VALUES VALUES
(1101, 'Bruce Wayne', '84612325V', 'Batman', 'Alfred', '1007 Mountain Drive, Gotham', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceWayne@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'), (1101, 'Bruce Wayne', '84612325V', 'Batman', 'Alfred', '1007 Mountain Drive, Gotham', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceWayne@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'),
(1102, 'Petter Parker', '87945234L', 'Spider man', 'Aunt May', '20 Ingram Street, Queens, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'PetterParker@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'), (1102, 'Petter Parker', '87945234L', 'Spider man', 'Aunt May', '20 Ingram Street, Queens, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'PetterParker@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'),
(1103, 'Clark Kent', '06815934E', 'Super man', 'lois lane', '344 Clinton Street, Apartament 3-D', 'Gotham', 46460, 1111111111, 222222222, 1, 'ClarkKent@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 0, 19, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'), (1103, 'Clark Kent', '06815934E', 'Super man', 'lois lane', '344 Clinton Street, Apartament 3-D', 'Gotham', 46460, 1111111111, 222222222, 1, 'ClarkKent@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 0, 19, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'),
(1104, 'Tony Stark', '06089160W', 'Iron man', 'Pepper Potts', '10880 Malibu Point, 90265', 'Gotham', 46460, 1111111111, 222222222, 1, 'TonyStark@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'), (1104, 'Tony Stark', '06089160W', 'Iron man', 'Pepper Potts', '10880 Malibu Point, 90265', 'Gotham', 46460, 1111111111, 222222222, 1, 'TonyStark@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'),
(1105, 'Max Eisenhardt', '251628698', 'Magneto', 'Rogue', 'Unknown Whereabouts', 'Gotham', 46460, 1111111111, 222222222, 1, 'MaxEisenhardt@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 300, 8, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, 1, NULL, 0, 0, 18, 0, 1, 'florist'), (1105, 'Max Eisenhardt', '251628698', 'Magneto', 'Rogue', 'Unknown Whereabouts', 'Gotham', 46460, 1111111111, 222222222, 1, 'MaxEisenhardt@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 300, 8, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 18, 0, 1, 'florist'),
(1106, 'DavidCharlesHaller', '53136686Q', 'Legion', 'Charles Xavier', 'City of New York, New York, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'DavidCharlesHaller@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 0, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, 0, NULL, 0, 0, 19, 0, 1, 'florist'), (1106, 'DavidCharlesHaller', '53136686Q', 'Legion', 'Charles Xavier', 'City of New York, New York, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'DavidCharlesHaller@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 0, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 19, 0, 1, 'florist'),
(1107, 'Hank Pym', '09854837G', 'Ant man', 'Hawk', 'Anthill, San Francisco, California', 'Gotham', 46460, 1111111111, 222222222, 1, 'HankPym@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, 0, NULL, 0, 0, 19, 0, 1, 'florist'), (1107, 'Hank Pym', '09854837G', 'Ant man', 'Hawk', 'Anthill, San Francisco, California', 'Gotham', 46460, 1111111111, 222222222, 1, 'HankPym@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 19, 0, 1, 'florist'),
(1108, 'Charles Xavier', '22641921P', 'Professor X', 'Beast', '3800 Victory Pkwy, Cincinnati, OH 45207, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'CharlesXavier@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, 1, NULL, 0, 0, 19, 0, 1, 'florist'), (1108, 'Charles Xavier', '22641921P', 'Professor X', 'Beast', '3800 Victory Pkwy, Cincinnati, OH 45207, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'CharlesXavier@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 19, 0, 1, 'florist'),
(1109, 'Bruce Banner', '16104829E', 'Hulk', 'Black widow', 'Somewhere in New York', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceBanner@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, 0, NULL, 0, 0, 9, 0, 1, 'florist'), (1109, 'Bruce Banner', '16104829E', 'Hulk', 'Black widow', 'Somewhere in New York', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceBanner@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 9, 0, 1, 'florist'),
(1110, 'Jessica Jones', '58282869H', 'Jessica Jones', 'Luke Cage', 'NYCC 2015 Poster', 'Gotham', 46460, 1111111111, 222222222, 1, 'JessicaJones@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, 0, NULL, 0, 0, NULL, 0, 1, 'florist'), (1110, 'Jessica Jones', '58282869H', 'Jessica Jones', 'Luke Cage', 'NYCC 2015 Poster', 'Gotham', 46460, 1111111111, 222222222, 1, 'JessicaJones@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, NULL, 0, 1, 'florist'),
(1111, 'Missing', NULL, 'Missing man', 'Anton', 'The space, Universe far away', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 1, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others'), (1111, 'Missing', NULL, 'Missing man', 'Anton', 'The space, Universe far away', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others'),
(1112, 'Trash', NULL, 'Garbage man', 'Unknown name', 'New York city, Underground', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 1, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others'); (1112, 'Trash', NULL, 'Garbage man', 'Unknown name', 'New York city, Underground', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others');
INSERT INTO `vn`.`client`(`id`, `name`, `fi`, `socialName`, `contact`, `street`, `city`, `postcode`, `isRelevant`, `email`, `iban`,`dueDay`,`accountingAccount`, `isEqualizated`, `provinceFk`, `hasToInvoice`, `credit`, `countryFk`, `isActive`, `gestdocFk`, `quality`, `payMethodFk`,`created`, `isTaxDataChecked`) INSERT INTO `vn`.`client`(`id`, `name`, `fi`, `socialName`, `contact`, `street`, `city`, `postcode`, `isRelevant`, `email`, `iban`,`dueDay`,`accountingAccount`, `isEqualizated`, `provinceFk`, `hasToInvoice`, `credit`, `countryFk`, `isActive`, `gestdocFk`, `quality`, `payMethodFk`,`created`, `isTaxDataChecked`)
SELECT id, name, CONCAT(RPAD(CONCAT(id,9),8,id),'A'), CONCAT(name, 'Social'), CONCAT(name, 'Contact'), CONCAT(name, 'Street'), 'GOTHAM', 46460, 1, CONCAT(name,'@mydomain.com'), NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5, util.VN_CURDATE(), 1 SELECT id, name, CONCAT(RPAD(CONCAT(id,9),8,id),'A'), CONCAT(name, 'Social'), CONCAT(name, 'Contact'), CONCAT(name, 'Street'), 'GOTHAM', 46460, 1, CONCAT(name,'@mydomain.com'), NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5, util.VN_CURDATE(), 1
@ -707,7 +707,7 @@ INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeF
(15, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1105, 'An incredibly long alias for testing purposes', 125, NULL, 0, 3, 5, 1, util.VN_CURDATE()), (15, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1105, 'An incredibly long alias for testing purposes', 125, NULL, 0, 3, 5, 1, util.VN_CURDATE()),
(16, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1106, 'Many Places', 126, NULL, 0, 3, 5, 1, util.VN_CURDATE()), (16, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1106, 'Many Places', 126, NULL, 0, 3, 5, 1, util.VN_CURDATE()),
(17, 1, 7, 2, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1106, 'Many Places', 126, NULL, 0, 3, 5, 1, util.VN_CURDATE()), (17, 1, 7, 2, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1106, 'Many Places', 126, NULL, 0, 3, 5, 1, util.VN_CURDATE()),
(18, 1, 4, 4, 4, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1108, 'Cerebro', 128, NULL, 0, 12, 5, 1, util.VN_CURDATE()), (18, 1, 4, 4, 4, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1108, 'Cerebro', 128, NULL, 0, 12, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +12 HOUR)),
(19, 1, 5, 5, NULL, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1109, 'Somewhere in Thailand', 129, NULL, 1, NULL, 5, 1, util.VN_CURDATE()), (19, 1, 5, 5, NULL, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1109, 'Somewhere in Thailand', 129, NULL, 1, NULL, 5, 1, util.VN_CURDATE()),
(20, 1, 5, 5, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 1109, 'Somewhere in Thailand', 129, NULL, 0, 13, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH)), (20, 1, 5, 5, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 1109, 'Somewhere in Thailand', 129, NULL, 0, 13, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH)),
(21, NULL, 5, 5, 5, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 1109, 'Somewhere in Holland', 102, NULL, 0, 13, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH)), (21, NULL, 5, 5, 5, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 1109, 'Somewhere in Holland', 102, NULL, 0, 13, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH)),
@ -1940,6 +1940,16 @@ INSERT INTO `pbx`.`sip`(`user_id`, `extension`)
(5, 1102), (5, 1102),
(9, 1201); (9, 1201);
INSERT INTO `vn`.`professionalCategory` (`id`, `name`, `level`, `dayBreak`)
VALUES
(1, 'employee', NULL, NULL),
(2, 'florist', NULL, NULL);
INSERT INTO `vn`.`calendarType` (`id`, `description`, `hoursWeek`, `isPartial`)
VALUES
(1, 'General schedule', 40, 0);
DROP TEMPORARY TABLE IF EXISTS tmp.worker; DROP TEMPORARY TABLE IF EXISTS tmp.worker;
CREATE TEMPORARY TABLE tmp.worker CREATE TEMPORARY TABLE tmp.worker
(PRIMARY KEY (id)) (PRIMARY KEY (id))
@ -1968,7 +1978,7 @@ UPDATE `vn`.`business`
WHERE `id`= 1106; WHERE `id`= 1106;
UPDATE `vn`.`business` b UPDATE `vn`.`business` b
SET b.`workerBusinessProfessionalCategoryFk` = 31 SET b.`workerBusinessProfessionalCategoryFk` = 2
WHERE b.`workerFk` = 1110; WHERE b.`workerFk` = 1110;
UPDATE `vn`.`business` b UPDATE `vn`.`business` b
@ -2775,6 +2785,19 @@ INSERT INTO `vn`.`ticketLog` (`originFk`, userFk, `action`, changedModel, oldIns
(7, 18, 'update', 'Sale', '{"price":3}', '{"price":5}', 1, NULL), (7, 18, 'update', 'Sale', '{"price":3}', '{"price":5}', 1, NULL),
(7, 18, 'update', NULL, NULL, NULL, NULL, "Cambio cantidad Melee weapon heavy shield 1x0.5m de '5' a '10'"); (7, 18, 'update', NULL, NULL, NULL, NULL, "Cambio cantidad Melee weapon heavy shield 1x0.5m de '5' a '10'");
INSERT INTO `vn`.`ticketLog` (originFk, userFk, `action`, creationDate, changedModel, changedModelId, changedModelValue, oldInstance, newInstance, description)
VALUES
(1, NULL, 'delete', '2001-06-09 11:00:04', 'Ticket', 45, 'Spider Man' , NULL, NULL, NULL),
(1, 18, 'select', '2001-06-09 11:00:03', 'Ticket', 45, 'Spider Man' , NULL, NULL, NULL),
(1, NULL, 'update', '2001-05-09 10:00:02', 'Sale', 69854, 'Armor' , '{"isPicked": false}','{"isPicked": true}', NULL),
(1, 18, 'update', '2001-01-01 10:05:01', 'Sale', 69854, 'Armor' , NULL, NULL, 'Armor quantity changed from ''15'' to ''10'''),
(1, NULL, 'delete', '2001-01-01 10:00:10', 'Sale', 5689, 'Shield' , '{"quantity":10,"concept":"Shield"}', NULL, NULL),
(1, 18, 'insert', '2000-12-31 15:00:05', 'Sale', 69854, 'Armor' , NULL,'{"quantity":15,"concept":"Armor", "price": 345.99, "itemFk": 2}', NULL),
(1, 18, 'update', '2000-12-28 08:40:45', 'Ticket', 45, 'Spider Man' , '{"warehouseFk":60,"shipped":"2023-05-16T22:00:00.000Z","nickname":"Super Man","isSigned":true,"isLabeled":true,"isPrinted":true,"packages":0,"hour":0,"isBlocked":false,"hasPriority":false,"companyFk":442,"landed":"2023-05-17T22:00:00.000Z","isBoxed":true,"isDeleted":true,"zoneFk":713,"zonePrice":13,"zoneBonus":0}','{"warehouseFk":61,"shipped":"2023-05-17T22:00:00.000Z","nickname":"Spider Man","isSigned":false,"isLabeled":false,"isPrinted":false,"packages":1,"hour":0,"isBlocked":true,"hasPriority":true,"companyFk":443,"landed":"2023-05-18T22:00:00.000Z","isBoxed":false,"isDeleted":false,"zoneFk":713,"zonePrice":13,"zoneBonus":1}', NULL),
(1, 18, 'select', '2000-12-27 03:40:30', 'Ticket', 45, NULL , NULL, NULL, NULL),
(1, 18, 'insert', '2000-04-10 09:40:15', 'Sale', 5689, 'Shield' , NULL, '{"quantity":10,"concept":"Shield", "price": 10.5, "itemFk": 1}', NULL),
(1, 18, 'insert', '1999-05-09 10:00:00', 'Ticket', 45, 'Super Man' , NULL, '{"id":45,"clientFk":8608,"warehouseFk":60,"shipped":"2023-05-16T22:00:00.000Z","nickname":"Super Man","addressFk":48637,"isSigned":true,"isLabeled":true,"isPrinted":true,"packages":0,"hour":0,"created":"2023-05-16T11:42:56.000Z","isBlocked":false,"hasPriority":false,"companyFk":442,"agencyModeFk":639,"landed":"2023-05-17T22:00:00.000Z","isBoxed":true,"isDeleted":true,"zoneFk":713,"zonePrice":13,"zoneBonus":0}', NULL);
INSERT INTO `vn`.`osTicketConfig` (`id`, `host`, `user`, `password`, `oldStatus`, `newStatusId`, `day`, `comment`, `hostDb`, `userDb`, `passwordDb`, `portDb`, `responseType`, `fromEmailId`, `replyTo`) INSERT INTO `vn`.`osTicketConfig` (`id`, `host`, `user`, `password`, `oldStatus`, `newStatusId`, `day`, `comment`, `hostDb`, `userDb`, `passwordDb`, `portDb`, `responseType`, `fromEmailId`, `replyTo`)
VALUES VALUES
(0, 'http://localhost:56596/scp', 'ostadmin', 'Admin1', '1,6', 3, 60, 'Este CAU se ha cerrado automáticamente. Si el problema persiste responda a este mensaje.', 'localhost', 'osticket', 'osticket', 40003, 'reply', 1, 'all'); (0, 'http://localhost:56596/scp', 'ostadmin', 'Admin1', '1,6', 3, 60, 'Este CAU se ha cerrado automáticamente. Si el problema persiste responda a este mensaje.', 'localhost', 'osticket', 'osticket', 40003, 'reply', 1, 'all');
@ -2801,9 +2824,9 @@ INSERT INTO `vn`.`payDemDetail` (`id`, `detail`)
VALUES VALUES
(1, 1); (1, 1);
INSERT INTO `vn`.`workerConfig` (`id`, `businessUpdated`, `roleFk`) INSERT INTO `vn`.`workerConfig` (`id`, `businessUpdated`, `roleFk`, `payMethodFk`, `businessTypeFk`)
VALUES VALUES
(1, NULL, 1); (1, NULL, 1, 4, 'worker');
INSERT INTO `vn`.`ticketRefund`(`refundTicketFk`, `originalTicketFk`) INSERT INTO `vn`.`ticketRefund`(`refundTicketFk`, `originalTicketFk`)
VALUES VALUES

View File

@ -1,42 +1,33 @@
CREATE SCHEMA IF NOT EXISTS `util`; DROP FUNCTION IF EXISTS `util`.`mockTime`;
USE `util`;
DELIMITER ;; DELIMITER $$
DROP FUNCTION IF EXISTS `util`.`mockedDate`; $$
CREATE FUNCTION `util`.`mockedDate`() CREATE DEFINER=`root`@`localhost` FUNCTION `util`.`mockTime`() RETURNS datetime
RETURNS DATETIME DETERMINISTIC
DETERMINISTIC
BEGIN BEGIN
RETURN CONVERT_TZ('2001-01-01 11:00:00', 'utc', 'Europe/Madrid'); RETURN CONVERT_TZ('@mockDate', 'utc', 'Europe/Madrid');
END ;; END$$
DELIMITER ; DELIMITER ;
DELIMITER ;; DROP FUNCTION IF EXISTS `util`.`mockUtcTime`;
DROP FUNCTION IF EXISTS `util`.`VN_CURDATE`;
CREATE FUNCTION `util`.`VN_CURDATE`() DELIMITER $$
RETURNS DATE $$
DETERMINISTIC CREATE DEFINER=`root`@`localhost` FUNCTION `util`.`mockUtcTime`() RETURNS datetime
DETERMINISTIC
BEGIN BEGIN
RETURN DATE(mockedDate()); RETURN CONVERT_TZ('@mockDate', 'utc', 'Europe/Madrid');
END ;; END$$
DELIMITER ; DELIMITER ;
DELIMITER ;; DROP FUNCTION IF EXISTS `util`.`mockTimeBase`;
DROP FUNCTION IF EXISTS `util`.`VN_CURTIME`;
CREATE FUNCTION `util`.`VN_CURTIME`() DELIMITER $$
RETURNS TIME $$
DETERMINISTIC CREATE DEFINER=`root`@`localhost` FUNCTION `util`.`mockTimeBase`(vIsUtc BOOL) RETURNS datetime
DETERMINISTIC
BEGIN BEGIN
RETURN TIME(mockedDate()); RETURN CONVERT_TZ('@mockDate', 'utc', 'Europe/Madrid');
END ;; END$$
DELIMITER ; DELIMITER ;
DELIMITER ;;
DROP FUNCTION IF EXISTS `util`.`VN_NOW`;
CREATE FUNCTION `util`.`VN_NOW`()
RETURNS DATETIME
DETERMINISTIC
BEGIN
RETURN mockedDate();
END ;;
DELIMITER ;

File diff suppressed because it is too large Load Diff

View File

@ -100,11 +100,8 @@ dump_tables ${TABLES[@]}
TABLES=( TABLES=(
postgresql postgresql
calendar_labour_type
labour_agreement labour_agreement
media_type media_type
professional_category
profile_type
) )
dump_tables ${TABLES[@]} dump_tables ${TABLES[@]}

View File

@ -93,12 +93,5 @@ mysqldump \
--databases \ --databases \
${SCHEMAS[@]} \ ${SCHEMAS[@]} \
${IGNORETABLES[@]} \ ${IGNORETABLES[@]} \
| sed 's/\bCURDATE\b/util.VN_CURDATE/ig'\
| sed 's/\bCURTIME\b/util.VN_CURTIME/ig' \
| sed 's/\bNOW\b/util.VN_NOW/ig' \
| sed 's/\bCURRENT_DATE\b/util.VN_CURDATE/ig' \
| sed 's/\bCURRENT_TIME\b/util.VN_CURTIME/ig' \
| sed 's/\bLOCALTIME\b/util.VN_NOW/ig' \
| sed 's/\bLOCALTIMESTAMP\b/util.VN_NOW/ig' \
| sed 's/ AUTO_INCREMENT=[0-9]* //g' \ | sed 's/ AUTO_INCREMENT=[0-9]* //g' \
> dump/structure.sql > dump/structure.sql

View File

@ -313,7 +313,7 @@ export default {
anyClient: 'vn-client-defaulter tbody > tr', anyClient: 'vn-client-defaulter tbody > tr',
firstClientName: 'vn-client-defaulter tbody > tr:nth-child(1) > td:nth-child(2) > span', firstClientName: 'vn-client-defaulter tbody > tr:nth-child(1) > td:nth-child(2) > span',
firstSalesPersonName: 'vn-client-defaulter tbody > tr:nth-child(1) > td:nth-child(3) > span', firstSalesPersonName: 'vn-client-defaulter tbody > tr:nth-child(1) > td:nth-child(3) > span',
firstObservation: 'vn-client-defaulter tbody > tr:nth-child(1) > td:nth-child(6) > vn-textarea[ng-model="defaulter.observation"]', firstObservation: 'vn-client-defaulter tbody > tr:nth-child(1) > td:nth-child(8) > vn-textarea[ng-model="defaulter.observation"]',
allDefaulterCheckbox: 'vn-client-defaulter thead vn-multi-check', allDefaulterCheckbox: 'vn-client-defaulter thead vn-multi-check',
addObservationButton: 'vn-client-defaulter vn-button[icon="icon-notes"]', addObservationButton: 'vn-client-defaulter vn-button[icon="icon-notes"]',
observation: '.vn-dialog.shown vn-textarea[ng-model="$ctrl.defaulter.observation"]', observation: '.vn-dialog.shown vn-textarea[ng-model="$ctrl.defaulter.observation"]',

View File

@ -5,8 +5,8 @@ const $ = {
userName: 'vn-client-web-access vn-textfield[ng-model="$ctrl.account.name"]', userName: 'vn-client-web-access vn-textfield[ng-model="$ctrl.account.name"]',
email: 'vn-client-web-access vn-textfield[ng-model="$ctrl.account.email"]', email: 'vn-client-web-access vn-textfield[ng-model="$ctrl.account.email"]',
saveButton: 'vn-client-web-access button[type=submit]', saveButton: 'vn-client-web-access button[type=submit]',
nameValue: 'vn-client-log .change:nth-child(1) .basic-json:nth-child(1) vn-json-value', nameValue: 'vn-client-log .change:nth-child(1) .basic-json:nth-child(2) vn-json-value',
activeValue: 'vn-client-log .change:nth-child(2) .basic-json:nth-child(2) vn-json-value' activeValue: 'vn-client-log .change:nth-child(2) .basic-json:nth-child(1) vn-json-value'
}; };
describe('Client web access path', () => { describe('Client web access path', () => {

View File

@ -50,6 +50,7 @@ describe('Item Edit basic data path', () => {
it(`should create a new intrastat and save it`, async() => { it(`should create a new intrastat and save it`, async() => {
await page.click($.newIntrastatButton); await page.click($.newIntrastatButton);
await page.waitForSelector($.intrastatForm);
await page.fillForm($.intrastatForm, { await page.fillForm($.intrastatForm, {
id: '588420239', id: '588420239',
description: 'Tropical Flowers' description: 'Tropical Flowers'

View File

@ -251,6 +251,7 @@ describe('Ticket Edit sale path', () => {
await page.waitToClick(selectors.ticketSales.moreMenu); await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim); await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim);
await page.waitToClick(selectors.globalItems.acceptButton); await page.waitToClick(selectors.globalItems.acceptButton);
await page.waitToClick(selectors.globalItems.acceptButton);
await page.waitForState('claim.card.basicData'); await page.waitForState('claim.card.basicData');
}); });

View File

@ -1,5 +1,20 @@
import getBrowser from '../../helpers/puppeteer'; import getBrowser from '../../helpers/puppeteer';
const $ = {
saveButton: 'vn-supplier-fiscal-data button[type="submit"]',
};
const $inputs = {
province: 'vn-supplier-fiscal-data [name="province"]',
country: 'vn-supplier-fiscal-data [name="country"]',
postcode: 'vn-supplier-fiscal-data [name="postcode"]',
city: 'vn-supplier-fiscal-data [name="city"]',
socialName: 'vn-supplier-fiscal-data [name="socialName"]',
taxNumber: 'vn-supplier-fiscal-data [name="taxNumber"]',
account: 'vn-supplier-fiscal-data [name="account"]',
sageWithholding: 'vn-supplier-fiscal-data [ng-model="$ctrl.supplier.sageWithholdingFk"]',
sageTaxType: 'vn-supplier-fiscal-data [ng-model="$ctrl.supplier.sageTaxTypeFk"]'
};
describe('Supplier fiscal data path', () => { describe('Supplier fiscal data path', () => {
let browser; let browser;
let page; let page;

View File

@ -1,5 +1,6 @@
import ngModule from '../../module'; import ngModule from '../../module';
import Component from 'core/lib/component'; import Component from 'core/lib/component';
import {hashToColor} from '../../lib/string';
import './style.scss'; import './style.scss';
/** /**
@ -16,13 +17,8 @@ export default class Avatar extends Component {
this._val = value; this._val = value;
const val = value || ''; const val = value || '';
let hash = 0;
for (let i = 0; i < val.length; i++)
hash += val.charCodeAt(i);
const color = '#' + colors[hash % colors.length];
const el = this.element; const el = this.element;
el.style.backgroundColor = color; el.style.backgroundColor = hashToColor(val);
el.title = val; el.title = val;
} }
} }
@ -35,29 +31,3 @@ ngModule.vnComponent('vnAvatar', {
}, },
transclude: true transclude: true
}); });
const colors = [
'e2553d', // Coral
'FFA07A', // Salmon
'FFDAB9', // Peach
'a17077', // Pink
'bf0e99', // Pink light
'52a500', // Green chartreuse
'00aeae', // Cian
'b754cf', // Purple middle
'8a69cd', // Blue lavender
'1fa8a1', // Green ocean
'DC143C', // Red crimson
'5681cf', // Blue steel
'FF1493', // Ping intense
'02ba02', // Green lime
'1E90FF', // Blue sky
'8B008B', // Purple dark
'cc7000', // Orange bright
'00b5b8', // Turquoise
'8B0000', // Red dark
'008080', // Green bluish
'2F4F4F', // Gray board
'7e7e7e', // Gray
'5d5d5d', // Gray dark
];

View File

@ -139,7 +139,7 @@ export default class CrudModel extends ModelProxy {
filter.limit = this.page * this.limit; filter.limit = this.page * this.limit;
} }
return this.sendRequest(filter, append, true); return this.sendRequest(filter, append);
} }
clear() { clear() {
@ -231,12 +231,12 @@ export default class CrudModel extends ModelProxy {
return params; return params;
} }
sendRequest(filter, append, loadMore) { sendRequest(filter, append) {
this.cancelRequest(); this.cancelRequest();
this.canceler = this.$q.defer(); this.canceler = this.$q.defer();
this.isPaging = append; this.isPaging = append;
if (!loadMore) if (!append && this.status != 'ready')
this.status = 'loading'; this.status = 'loading';
let params = Object.assign( let params = Object.assign(

View File

@ -150,7 +150,7 @@ describe('Component vnCrudModel', () => {
controller.loadMore(true); controller.loadMore(true);
expect(controller.sendRequest).toHaveBeenCalledWith({'skip': 2}, true, true); expect(controller.sendRequest).toHaveBeenCalledWith({'skip': 2}, true);
}); });
}); });

View File

@ -7,11 +7,10 @@ export default class DataViewer {
} }
get status() { get status() {
if (this.model)
return this.model.status;
if (this.isLoading) if (this.isLoading)
return 'loading'; return 'loading';
if (this.model)
return this.model.status;
if (!this.data) if (!this.data)
return 'clear'; return 'clear';
if (this.data.length) if (this.data.length)

View File

@ -2,7 +2,7 @@ import ngModule from '../../module';
import Component from 'core/lib/component'; import Component from 'core/lib/component';
import './style.scss'; import './style.scss';
const maxStrLen = 50; const maxStrLen = 512;
/** /**
* Displays pretty JSON value. * Displays pretty JSON value.
@ -28,6 +28,12 @@ export default class Controller extends Component {
} else { } else {
cssClass = type; cssClass = type;
switch (type) { switch (type) {
case 'number':
if (Number.isInteger(value))
text = value;
else
text = Math.round((value + Number.EPSILON) * 1000) / 1000;
break;
case 'boolean': case 'boolean':
text = value ? '✓' : '✗'; text = value ? '✓' : '✗';
cssClass = value ? 'true' : 'false'; cssClass = value ? 'true' : 'false';

View File

@ -75,5 +75,19 @@ describe('Component vnJsonValue', () => {
expect(el.textContent).toEqual('2050'); expect(el.textContent).toEqual('2050');
expect(el.className).toContain('json-number'); expect(el.className).toContain('json-number');
}); });
it('should display number when value is decimal', () => {
controller.value = 10.1;
expect(el.textContent).toEqual('10.1');
expect(el.className).toContain('json-number');
});
it('should display rounded number when value is decimal with lot of decimals', () => {
controller.value = 10.124323234;
expect(el.textContent).toEqual('10.124');
expect(el.className).toContain('json-number');
});
}); });
}); });

View File

@ -1,3 +1,3 @@
SelectAllRows: Seleccionar las {{rows}} fila(s) SelectAllRows: Seleccionar las {{rows}} fila(s)
All: Se han seleccionado Have been selected: Se han seleccionado
row(s) have been selected.: fila(s). row(s) have been selected.: fila(s).

View File

@ -203,7 +203,7 @@ export default class Searchbar extends Component {
doSearch(filter, source) { doSearch(filter, source) {
if (filter === this.filter && !this.isIndex) return; if (filter === this.filter && !this.isIndex) return;
let promise = this.onSearch({$params: filter}); let promise = this.onSearch({$params: filter}, source);
promise = promise || this.$q.resolve(); promise = promise || this.$q.resolve();
promise.then(data => this.onFilter(filter, source, data)); promise.then(data => this.onFilter(filter, source, data));
this.toBar(filter); this.toBar(filter);
@ -259,12 +259,6 @@ export default class Searchbar extends Component {
this.filter = filter; this.filter = filter;
if (source == 'removeBar') {
delete params[this.toRemove];
delete this.model.userParams[this.toRemove];
this.model.refresh();
}
if (!filter && this.model) if (!filter && this.model)
this.model.clear(); this.model.clear();
if (source != 'state') if (source != 'state')
@ -279,7 +273,7 @@ export default class Searchbar extends Component {
return {id: params.$row.id}; return {id: params.$row.id};
} }
onSearch(args) { onSearch(args, source) {
if (!this.model) return; if (!this.model) return;
let filter = args.$params; let filter = args.$params;
@ -325,6 +319,12 @@ export default class Searchbar extends Component {
for (let param in stateFilter.tableQ) for (let param in stateFilter.tableQ)
params[param] = stateFilter.tableQ[param]; params[param] = stateFilter.tableQ[param];
if (source == 'removeBar') {
delete params[this.toRemove];
delete this.model.userParams[this.toRemove];
delete stateFilter[this.toRemove];
}
const newParams = Object.assign(stateFilter, params); const newParams = Object.assign(stateFilter, params);
return this.model.applyParams(newParams) return this.model.applyParams(newParams)
.then(() => this.model.data); .then(() => this.model.data);

View File

@ -197,7 +197,7 @@ describe('Component vnSearchbar', () => {
controller.doSearch(filter, 'any'); controller.doSearch(filter, 'any');
$scope.$apply(); $scope.$apply();
expect(controller.onSearch).toHaveBeenCalledWith({$params: filter}); expect(controller.onSearch).toHaveBeenCalledWith({$params: filter}, 'any');
expect(controller.onFilter).toHaveBeenCalledWith(filter, 'any', undefined); expect(controller.onFilter).toHaveBeenCalledWith(filter, 'any', undefined);
}); });
}); });

View File

@ -30,3 +30,39 @@ export function camelToKebab(str) {
export function firstUpper(str) { export function firstUpper(str) {
return str.charAt(0).toUpperCase() + str.substr(1); return str.charAt(0).toUpperCase() + str.substr(1);
} }
export function djb2a(string) {
let hash = 5381;
for (let i = 0; i < string.length; i++)
hash = ((hash << 5) + hash) ^ string.charCodeAt(i);
return hash >>> 0;
}
export function hashToColor(value) {
return '#' + colors[djb2a(value || '') % colors.length];
}
const colors = [
'b5b941', // Yellow
'ae9681', // Peach
'd78767', // Salmon
'cc7000', // Orange bright
'e2553d', // Coral
'8B0000', // Red dark
'de4362', // Red crimson
'FF1493', // Ping intense
'be39a2', // Pink light
'b754cf', // Purple middle
'a87ba8', // Pink
'8a69cd', // Blue lavender
'ab20ab', // Purple dark
'00b5b8', // Turquoise
'1fa8a1', // Green ocean
'5681cf', // Blue steel
'3399fe', // Blue sky
'6d9c3e', // Green chartreuse
'51bb51', // Green lime
'518b8b', // Gray board
'7e7e7e', // Gray
'5d5d5d', // Gray dark
];

View File

@ -38,6 +38,9 @@
.text-overline { .text-overline {
font-size: .8rem; font-size: .8rem;
} }
.text-capitalize {
text-transform: capitalize;
}
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
padding: 0; padding: 0;

View File

@ -10,16 +10,18 @@
</vn-crud-model> </vn-crud-model>
<vn-crud-model <vn-crud-model
url="{{$ctrl.url}}/{{$ctrl.originId}}/models" url="{{$ctrl.url}}/{{$ctrl.originId}}/models"
data="models" data="$ctrl.models"
order="changedModel" order="changedModel"
auto-load="true"> auto-load="true">
</vn-crud-model> </vn-crud-model>
<vn-data-viewer model="model" class="vn-w-md vn-px-sm"> <vn-data-viewer
model="model"
class="vn-w-sm vn-px-sm">
<div class="change vn-mb-sm" ng-repeat="log in $ctrl.logs"> <div class="change vn-mb-sm" ng-repeat="log in $ctrl.logs">
<div class="user-wrapper"> <div class="left">
<vn-avatar class="vn-mt-xs" <vn-avatar class="vn-mt-xs"
ng-class="::{system: !log.user}" ng-class="::{system: !log.user}"
val="{{::log.user ? log.user.nickname : 'System'}}" val="{{::log.user ? log.user.nickname : $ctrl.$t('System')}}"
ng-click="$ctrl.showWorkerDescriptor($event, log)"> ng-click="$ctrl.showWorkerDescriptor($event, log)">
<img <img
ng-if="::log.user.image" ng-if="::log.user.image"
@ -29,61 +31,55 @@
<div class="arrow bg-panel"></div> <div class="arrow bg-panel"></div>
<div class="line"></div> <div class="line"></div>
</div> </div>
<vn-card class="detail vn-pa-sm"> <vn-card class="detail">
<div class="header vn-mb-sm"> <div class="header vn-pa-sm">
<div <div
class="date text-secondary text-caption" class="action-date text-secondary text-caption vn-mr-sm"
title="{{::log.creationDate | date:'dd/MM/yyyy HH:mm'}}"> title="{{::log.creationDate | date:'dd/MM/yyyy HH:mm:ss'}}">
<vn-icon
class="action vn-mr-xs"
ng-class="::$ctrl.actionsClass[log.action]"
icon="{{::$ctrl.actionsIcon[log.action]}}"
translate-attr="::{title: $ctrl.actionsText[log.action]}">
</vn-icon>
{{::$ctrl.relativeDate(log.creationDate)}} {{::$ctrl.relativeDate(log.creationDate)}}
</div> </div>
<span class="chip" ng-class="::$ctrl.actionsClass[log.action]" translate> <div class="action-model">
{{::$ctrl.actionsText[log.action]}}
</span>
</div>
<div
class="model vn-mb-sm"
title="{{::log.changedModelValue}}"
ng-if="::log.changedModel || log.changedModelValue">
<span class="model-name" <span class="model-name"
ng-if="::$ctrl.showModelName" ng-if="::$ctrl.showModelName && log.changedModel"
ng-style="::{backgroundColor: $ctrl.hashToColor(log.changedModel)}"
title="{{::log.changedModel}}"> title="{{::log.changedModel}}">
{{::log.changedModelI18n}} {{::log.changedModelI18n}}
</span> </span>
<span class="model-id"
ng-if="::log.changedModelId">
#{{::log.changedModelId}}
</span>
<span class="model-value">
{{::log.changedModelValue}}
</span>
</div> </div>
<div class="changes" </div>
ng-class="::log.props.length ? 'props' : 'no-props'" <div
vn-id="changes"> class="model vn-pb-sm vn-px-sm"
<vn-icon icon="visibility" title="{{::log.changedModelValue}}"
class="expand-button" ng-if="::log.changedModelId || log.changedModelValue">
ng-click="$ctrl.toggleAttributes(log, changes, true)"> <span class="model-id" ng-if="::log.changedModelId">#{{::log.changedModelId}}</span>
<span class="model-value">{{::log.changedModelValue}}</span>
</div>
<div class="changes vn-pa-sm"
ng-class="{expanded: log.expand}"
ng-if="::log.props.length || log.description">
<vn-icon
icon="expand_more"
translate-attr="{title: 'Details'}"
ng-click="log.expand = !log.expand">
</vn-icon> </vn-icon>
<vn-icon icon="visibility_off"
class="shrink-button"
ng-click="$ctrl.toggleAttributes(log, changes, false)">
</vn-icon>
<div class="changes-wrapper">
<span ng-if="::log.props.length" <span ng-if="::log.props.length"
class="attributes"> class="attributes">
<span ng-if="!log.expand" ng-repeat="prop in ::log.props" <span ng-if="!log.expand" ng-repeat="prop in ::log.props"
class="basic-json"> class="basic-json">
<span class="json-field" <span class="json-field" title="{{::prop.name}}">
title="{{::prop.name}}">
{{::prop.nameI18n}}: {{::prop.nameI18n}}:
</span> </span>
<vn-json-value value="::$ctrl.mainVal(prop, log.action)"></vn-json-value><span ng-if="::!$last">,</span> <vn-json-value value="::$ctrl.mainVal(prop, log.action)"></vn-json-value><span ng-if="::!$last">,</span>
</span> </span>
<div ng-if="log.expand" <div ng-if="log.expand" class="expanded-json">
class="expanded-json">
<div ng-repeat="prop in ::log.props"> <div ng-repeat="prop in ::log.props">
<span class="json-field" <span class="json-field" title="{{::prop.name}}">
title="{{::prop.name}}">
{{::prop.nameI18n}}: {{::prop.nameI18n}}:
</span> </span>
<vn-json-value value="::$ctrl.mainVal(prop, log.action)"></vn-json-value> <vn-json-value value="::$ctrl.mainVal(prop, log.action)"></vn-json-value>
@ -93,16 +89,9 @@
</div> </div>
</div> </div>
</span> </span>
<span ng-if="::!log.props.length" <span ng-if="::!log.props.length" class="description">
class="description">
{{::log.description}} {{::log.description}}
</span> </span>
<span ng-if="::!log.description && !log.props.length"
class="no-changes"
translate>
No changes
</span>
</div>
</vn-card> </vn-card>
</div> </div>
</div> </div>
@ -111,10 +100,6 @@
<form vn-vertical <form vn-vertical
ng-model-options="{updateOn: 'change blur'}" ng-model-options="{updateOn: 'change blur'}"
class="vn-pa-md filter"> class="vn-pa-md filter">
<vn-textfield
label="Name"
ng-model="filter.changedModelValue">
</vn-textfield>
<vn-vertical> <vn-vertical>
<vn-radio <vn-radio
label="All" label="All"
@ -131,7 +116,7 @@
val="system" val="system"
ng-model="filter.who"> ng-model="filter.who">
</vn-radio> </vn-radio>
</div> </vn-vertical>
<vn-autocomplete <vn-autocomplete
ng-show="filter.who != 'system'" ng-show="filter.who != 'system'"
label="User" label="User"
@ -159,24 +144,38 @@
</div> </div>
</tpl-item> </tpl-item>
</vn-autocomplete> </vn-autocomplete>
<vn-textfield
label="Search"
ng-model="filter.search">
<append>
<vn-icon
icon="info_outline"
vn-tooltip="Search by id or concept"
pointer>
</vn-icon>
</append>
</vn-textfield>
<vn-autocomplete <vn-autocomplete
label="Model" label="Entity"
ng-model="filter.changedModel" ng-model="filter.changedModel"
value-field="changedModel" value-field="changedModel"
show-field="changedModel" show-field="changedModelI18n"
data="models"> data="$ctrl.models"
class="changed-model">
</vn-autocomplete> </vn-autocomplete>
<!-- FIXME: Cannot use LIKE with JSON columns
<vn-textfield <vn-textfield
label="Id" label="Changes"
ng-model="filter.changedModelId"> ng-model="filter.changes">
</vn-textfield> </vn-textfield>
-->
<vn-vertical> <vn-vertical>
<vn-check <vn-check
label="Creates" label="Creates"
ng-model="filter.actions.insert"> ng-model="filter.actions.insert">
</vn-check> </vn-check>
<vn-check <vn-check
label="Updates" label="Edits"
ng-model="filter.actions.update"> ng-model="filter.actions.update">
</vn-check> </vn-check>
<vn-check <vn-check
@ -184,10 +183,10 @@
ng-model="filter.actions.delete"> ng-model="filter.actions.delete">
</vn-check> </vn-check>
<vn-check <vn-check
label="Views" label="Accesses"
ng-model="filter.actions.select"> ng-model="filter.actions.select">
</vn-check> </vn-check>
</div> </vn-vertical>
<vn-date-picker <vn-date-picker
label="Date" label="Date"
ng-model="filter.from"> ng-model="filter.from">
@ -196,7 +195,7 @@
label="To" label="To"
ng-model="filter.to"> ng-model="filter.to">
</vn-date-picker> </vn-date-picker>
<vn-button-bar vn-vertical class="vn-mt-sm"> <vn-button-bar vn-vertical>
<vn-button <vn-button
label="Filter" label="Filter"
ng-click="$ctrl.applyFilter(filter)"> ng-click="$ctrl.applyFilter(filter)">

View File

@ -1,5 +1,6 @@
import ngModule from '../../module'; import ngModule from '../../module';
import Section from '../section'; import Section from '../section';
import {hashToColor} from 'core/lib/string';
import './style.scss'; import './style.scss';
const validDate = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$/; const validDate = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$/;
@ -7,11 +8,12 @@ const validDate = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[
export default class Controller extends Section { export default class Controller extends Section {
constructor($element, $) { constructor($element, $) {
super($element, $); super($element, $);
this.hashToColor = hashToColor;
this.actionsText = { this.actionsText = {
insert: 'Creates', insert: 'Creates',
update: 'Updates', update: 'Edits',
delete: 'Deletes', delete: 'Deletes',
select: 'Views' select: 'Accesses'
}; };
this.actionsClass = { this.actionsClass = {
insert: 'success', insert: 'success',
@ -19,6 +21,12 @@ export default class Controller extends Section {
delete: 'alert', delete: 'alert',
select: 'notice' select: 'notice'
}; };
this.actionsIcon = {
insert: 'add',
update: 'edit',
delete: 'remove',
select: 'visibility'
};
this.filter = { this.filter = {
include: [{ include: [{
relation: 'user', relation: 'user',
@ -55,14 +63,14 @@ export default class Controller extends Section {
set logs(value) { set logs(value) {
this._logs = value; this._logs = value;
if (!this.logs) return; if (!value) return;
const empty = {}; const empty = {};
const validations = window.validations; const validations = window.validations;
for (const log of value) { for (const log of value) {
const oldValues = log.oldInstance || empty; const oldValues = log.oldInstance || empty;
const newValues = log.newInstance || empty; const newValues = log.newInstance || empty;
const locale = validations[log.changedModel]?.locale || empty; const locale = validations[log.changedModel]?.locale || empty;
log.changedModelI18n = locale.name || log.changedModel; log.changedModelI18n = firstUpper(locale.name) || log.changedModel;
let props = Object.keys(oldValues).concat(Object.keys(newValues)); let props = Object.keys(oldValues).concat(Object.keys(newValues));
props = [...new Set(props)]; props = [...new Set(props)];
@ -71,11 +79,27 @@ export default class Controller extends Section {
for (const prop of props) { for (const prop of props) {
log.props.push({ log.props.push({
name: prop, name: prop,
nameI18n: locale.columns?.[prop] || prop, nameI18n: firstUpper(locale.columns?.[prop]) || prop,
old: this.castJsonValue(oldValues[prop]), old: this.castJsonValue(oldValues[prop]),
new: this.castJsonValue(newValues[prop]) new: this.castJsonValue(newValues[prop])
}); });
} }
log.props.sort(
(a, b) => a.nameI18n.localeCompare(b.nameI18n));
}
}
get models() {
return this._models;
}
set models(value) {
this._models = value;
if (!value) return;
for (const model of value) {
const name = model.changedModel;
model.changedModelI18n =
firstUpper(window.validations[name]?.locale?.name) || name;
} }
} }
@ -93,11 +117,6 @@ export default class Controller extends Section {
return action == 'delete' ? prop.old : prop.new; return action == 'delete' ? prop.old : prop.new;
} }
toggleAttributes(log, changesEl, force) {
log.expand = force;
changesEl.classList.toggle('expanded', force);
}
relativeDate(dateVal) { relativeDate(dateVal) {
if (dateVal == null) return ''; if (dateVal == null) return '';
const date = new Date(dateVal); const date = new Date(dateVal);
@ -130,8 +149,17 @@ export default class Controller extends Section {
function getParam(prop, value) { function getParam(prop, value) {
if (value == null || value == '') return null; if (value == null || value == '') return null;
switch (prop) { switch (prop) {
case 'changedModelValue': case 'search':
return {[prop]: {like: `%${value}%`}}; const or = [{changedModelId: value}];
if (!/^[0-9]+$/.test(value))
or.push({changedModelValue: {like: `%${value}%`}});
return {or};
case 'changes':
return {or: [
{oldInstance: {like: `%${value}%`}},
{newInstance: {like: `%${value}%`}},
{description: {like: `%${value}%`}}
]};
case 'who': case 'who':
switch (value) { switch (value) {
case 'all': case 'all':
@ -195,6 +223,10 @@ export default class Controller extends Section {
} }
} }
function firstUpper(str) {
return str && str.charAt(0).toUpperCase() + str.substr(1);
}
ngModule.vnComponent('vnLog', { ngModule.vnComponent('vnLog', {
controller: Controller, controller: Controller,
template: require('./index.html'), template: require('./index.html'),

View File

@ -1,5 +1,8 @@
Date: Fecha Date: Fecha
Model: Modelo Concept: Concepto
Search: Buscar
Search by id or concept: Buscar por identificador o concepto
Entity: Entidad
Action: Acción Action: Acción
Author: Autor Author: Autor
Before: Antes Before: Antes
@ -7,12 +10,12 @@ After: Despues
History: Historial History: Historial
Name: Nombre Name: Nombre
Creates: Crea Creates: Crea
Updates: Actualiza Edits: Modifica
Deletes: Elimina Deletes: Elimina
Views: Visualiza Accesses: Accede
System: Sistema System: Sistema
Details: Detalles
note: nota note: nota
Changes: Cambios Changes: Cambios
No changes: No hay cambios
today: hoy today: hoy
yesterday: ayer yesterday: ayer

View File

@ -1,10 +1,11 @@
@import "variables"; @import "variables";
@import "effects";
vn-log { vn-log {
.change { .change {
display: flex; display: flex;
& > .user-wrapper { & > .left {
position: relative; position: relative;
padding-right: 10px; padding-right: 10px;
@ -34,7 +35,7 @@ vn-log {
bottom: -8px; bottom: -8px;
} }
} }
&:last-child > .user-wrapper > .line { &:last-child > .left > .line {
display: none; display: none;
} }
.detail { .detail {
@ -45,18 +46,41 @@ vn-log {
overflow: hidden; overflow: hidden;
& > .header { & > .header {
display: flex;
justify-content: space-between;
align-items: center;
overflow: hidden;
& > .action-model {
display: inline-flex;
overflow: hidden;
& > .model-name {
display: inline-block;
padding: 2px 5px;
color: $color-font-dark;
border-radius: 8px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
}
}
& > .action-date {
white-space: nowrap;
& > .chip { & > .action {
padding: 2px 4px; display: inline-flex;
border-radius: 4px; align-items: center;
display: inline-block; justify-content: center;
color: $color-font-bg; color: $color-font-bg;
vertical-align: middle;
border-radius: 50%;
width: 24px;
height: 24px;
font-size: 1.4em;
&.notice { &.notice {
background-color: $color-notice-medium; background-color: $color-notice-medium
} }
&.success { &.success {
background-color: $color-success-medium; background-color: $color-success-medium;
@ -68,8 +92,6 @@ vn-log {
background-color: lighten($color-alert, 5%); background-color: lighten($color-alert, 5%);
} }
} }
.date {
float: right;
} }
} }
& > .model { & > .model {
@ -77,16 +99,12 @@ vn-log {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
& > .model-name {
text-transform: capitalize;
}
& > .model-value { & > .model-value {
font-style: italic; font-style: italic;
color: #c7bd2b;
} }
& > .model-id { & > .model-id {
color: $color-font-secondary; color: $color-font-secondary;
font-size: .9em; font-size: .9rem;
} }
} }
} }
@ -94,59 +112,35 @@ vn-log {
.changes { .changes {
overflow: hidden; overflow: hidden;
background-color: rgba(255, 255, 255, .05); background-color: rgba(255, 255, 255, .05);
border-radius: 4px; color: $color-font-light;
color: $color-font-secondary;
transition: max-height 150ms ease-in-out;
max-height: 28px;
position: relative; position: relative;
& > .expand-button,
& > .shrink-button {
display: none;
}
&.props {
padding-right: 24px;
& > .expand-button,
& > .shrink-button {
position: absolute;
top: 6px;
right: 8px;
font-size: inherit;
float: right;
cursor: pointer;
}
& > .expand-button {
display: block;
}
&.expanded {
max-height: 500px;
padding-right: 0;
& > .changes-wrapper {
text-overflow: initial;
white-space: initial;
}
& > .shrink-button {
display: block;
}
& > .expand-button {
display: none;
}
}
}
& > .changes-wrapper {
padding: 4px 6px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
min-height: 34px;
box-sizing: border-box;
& > vn-icon {
@extend %clickable;
float: right;
position: relative;
transition-property: transform, background-color;
transition-duration: 150ms;
margin: -5px;
margin-left: 4px;
padding: 1px;
border-radius: 50%;
}
&.expanded {
text-overflow: initial;
white-space: initial;
& > vn-icon {
transform: rotate(180deg);
}
}
& > .no-changes { & > .no-changes {
font-style: italic; font-style: italic;
} }
.json-field {
text-transform: capitalize;
}
}
} }
} }

View File

@ -33,7 +33,7 @@ export default class Controller {
const newPassword = this.newPassword; const newPassword = this.newPassword;
this.$http.post('users/reset-password', {newPassword}, {headers}) this.$http.post('VnUsers/reset-password', {newPassword}, {headers})
.then(() => { .then(() => {
this.vnApp.showSuccess(this.$translate.instant('Password changed!')); this.vnApp.showSuccess(this.$translate.instant('Password changed!'));
this.$state.go('login'); this.$state.go('login');

View File

@ -84,6 +84,7 @@
"The current ticket can't be modified": "El ticket actual no puede ser modificado", "The current ticket can't be modified": "El ticket actual no puede ser modificado",
"The current claim can't be modified": "La reclamación actual no puede ser modificada", "The current claim can't be modified": "La reclamación actual no puede ser modificada",
"The sales of this ticket can't be modified": "Las lineas de este ticket no pueden ser modificadas", "The sales of this ticket can't be modified": "Las lineas de este ticket no pueden ser modificadas",
"The sales do not exists": "La(s) línea(s) seleccionada(s) no existe(n)",
"Please select at least one sale": "Por favor selecciona al menos una linea", "Please select at least one sale": "Por favor selecciona al menos una linea",
"All sales must belong to the same ticket": "Todas las lineas deben pertenecer al mismo ticket", "All sales must belong to the same ticket": "Todas las lineas deben pertenecer al mismo ticket",
"NO_ZONE_FOR_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada", "NO_ZONE_FOR_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada",
@ -290,5 +291,7 @@
"isTaxDataChecked": "Datos comprobados", "isTaxDataChecked": "Datos comprobados",
"comercialId": "Id comercial", "comercialId": "Id comercial",
"comercialName": "Comercial", "comercialName": "Comercial",
"Invalid NIF for VIES": "Invalid NIF for VIES" "Invalid NIF for VIES": "Invalid NIF for VIES",
"Ticket does not exist": "Este ticket no existe",
"Ticket is already signed": "Este ticket ya ha sido firmado"
} }

View File

@ -0,0 +1,4 @@
name: mail forward
columns:
account: account
forwardTo: forward to

View File

@ -0,0 +1,4 @@
name: reenvio de correo
columns:
account: cuenta
forwardTo: reenviar a

View File

@ -1,4 +1,4 @@
name: mail name: correo electrónico
columns: columns:
id: id id: id
receiver: receptor receiver: receptor

View File

@ -0,0 +1,4 @@
name: subrole
columns:
role: rol
inheritsFrom: inherits

View File

@ -0,0 +1,4 @@
name: subrol
columns:
role: rol
inheritsFrom: hereda

View File

@ -0,0 +1,5 @@
name: role
columns:
id: id
name: name
description: description

View File

@ -0,0 +1,5 @@
name: rol
columns:
id: id
name: nombre
description: descripción

View File

@ -1,4 +1,4 @@
name: account name: user
columns: columns:
id: id id: id
name: name name: name
@ -6,7 +6,7 @@ columns:
nickname: nickname nickname: nickname
lang: lang lang: lang
password: password password: password
bcryptPassword: bcrypt password bcryptPassword: BCRYPT password
active: active active: active
email: email email: email
emailVerified: email verified emailVerified: email verified

View File

@ -1,4 +1,4 @@
name: cuenta name: usuario
columns: columns:
id: id id: id
name: nombre name: nombre
@ -6,12 +6,12 @@ columns:
nickname: apodo nickname: apodo
lang: idioma lang: idioma
password: contraseña password: contraseña
bcryptPassword: contraseña bcrypt bcryptPassword: contraseña BCRYPT
active: activo active: activo
email: email email: correo electrónico
emailVerified: email verificado emailVerified: correo verificado
created: creado created: creado
updated: actualizado updated: actualizado
image: imagen image: imagen
hasGrant: tiene permiso hasGrant: puede delegar
userFk: usuario userFk: usuario

View File

@ -1,4 +1,4 @@
name: claim beginning name: beginning
columns: columns:
id: id id: id
quantity: quantity quantity: quantity

View File

@ -1,4 +1,4 @@
name: comienzo reclamación name: comienzo
columns: columns:
id: id id: id
quantity: cantidad quantity: cantidad

View File

@ -1,4 +1,4 @@
name: claim development name: development
columns: columns:
id: id id: id
claimFk: claim claimFk: claim

View File

@ -1,4 +1,4 @@
name: desarrollo reclamación name: desarrollo
columns: columns:
id: id id: id
claimFk: reclamación claimFk: reclamación

View File

@ -1,4 +1,4 @@
name: claim dms name: document
columns: columns:
dmsFk: dms dmsFk: dms
claimFk: claim claimFk: claim

View File

@ -1,4 +1,4 @@
name: documento reclamación name: documento
columns: columns:
dmsFk: dms dmsFk: dms
claimFk: reclamación claimFk: reclamación

View File

@ -1,4 +1,4 @@
name: claim end name: end
columns: columns:
id: id id: id
claimFk: claim claimFk: claim

View File

@ -1,4 +1,4 @@
name: final reclamación name: finalización
columns: columns:
id: id id: id
claimFk: reclamación claimFk: reclamación

View File

@ -1,4 +1,4 @@
name: claim observation name: observation
columns: columns:
id: id id: id
claimFk: claim claimFk: claim

View File

@ -1,4 +1,4 @@
name: observación reclamación name: observación
columns: columns:
id: id id: id
claimFk: reclamación claimFk: reclamación

View File

@ -18,3 +18,4 @@ columns:
addressFk: address addressFk: address
incotermsFk: incoterms incotermsFk: incoterms
customsAgentFk: customs agent customsAgentFk: customs agent
isDefaultAddress: default

View File

@ -18,3 +18,4 @@ columns:
addressFk: dirección addressFk: dirección
incotermsFk: incoterms incotermsFk: incoterms
customsAgentFk: agente adunanas customsAgentFk: agente adunanas
isDefaultAddress: predeterminada

View File

@ -1,4 +1,4 @@
name: client contact name: contact
columns: columns:
id: id id: id
name: name name: name

View File

@ -1,4 +1,4 @@
name: contacto cliente name: contacto
columns: columns:
id: id id: id
name: nombre name: nombre

View File

@ -1,4 +1,4 @@
name: client dms name: document
columns: columns:
dmsFk: dms dmsFk: dms
clientFk: client clientFk: client

View File

@ -1,4 +1,4 @@
name: documento cliente name: documento
columns: columns:
dmsFk: dms dmsFk: dms
clientFk: client clientFk: client

View File

@ -1,4 +1,4 @@
name: client observation name: observation
columns: columns:
id: id id: id
clientFk: client clientFk: client

View File

@ -1,4 +1,4 @@
name: observación cliente name: observación
columns: columns:
id: id id: id
clientFk: cliente clientFk: cliente

View File

@ -1,4 +1,4 @@
name: client sample name: sample
columns: columns:
id: id id: id
created: created created: created
@ -6,3 +6,5 @@ columns:
typeFk: type typeFk: type
userFk: user userFk: user
companyFk: company companyFk: company
workerFk: worker
balance: balance

View File

@ -1,4 +1,4 @@
name: muestra cliente name: muestra
columns: columns:
id: id id: id
created: creado created: creado
@ -6,3 +6,5 @@ columns:
typeFk: tipo typeFk: tipo
userFk: usuario userFk: usuario
companyFk: compañia companyFk: compañia
workerFk: trabajador
balance: balance

View File

@ -2,7 +2,7 @@ name: client
columns: columns:
id: id id: id
name: name name: name
fi: fi fi: tax identifier
socialName: socialName socialName: socialName
contact: contact contact: contact
street: street street: street
@ -14,22 +14,22 @@ columns:
isActive: active isActive: active
credit: credit credit: credit
creditInsurance: credit insurance creditInsurance: credit insurance
iban: iban iban: IBAN
dueDay: due day dueDay: due day
isEqualizated: equalizated isEqualizated: equalizated
isFreezed: freezed isFreezed: freezed
hasToInvoiceByAddress: invoice by address hasToInvoiceByAddress: invoice by address
hasToInvoice: has to invoice hasToInvoice: has to invoice
isToBeMailed: be mailed isToBeMailed: mailed
hasSepaVnl: sepa nnl hasSepaVnl: sepa VNL
hasLcr: lcr hasLcr: LCR
hasCoreVnl: core vnl hasCoreVnl: core VNL
hasCoreVnh: core vnh hasCoreVnh: core VNH
hasIncoterms: incoterms hasIncoterms: incoterms
isTaxDataChecked: tax data checked isTaxDataChecked: tax data checked
eypbc: eypbc eypbc: EYPBC
quality: quality quality: quality
isVies: vies isVies: VIES
isRelevant: relevant isRelevant: relevant
accountingAccount: accounting account accountingAccount: accounting account
created: created created: created
@ -47,4 +47,9 @@ columns:
defaultAddressFk: default address defaultAddressFk: default address
bankEntityFk: bank entity bankEntityFk: bank entity
transferorFk: transferor transferorFk: transferor
riskCalculated: risk calculated
isCreatedAsServed: created as served
hasInvoiceSimplified: simplified invoice
typeFk: type
lastSalesPersonFk: last salesperson

View File

@ -2,7 +2,7 @@ name: cliente
columns: columns:
id: id id: id
name: nombre name: nombre
fi: fi fi: identificador fiscal
socialName: nombre social socialName: nombre social
contact: contacto contact: contacto
street: calle street: calle
@ -14,27 +14,27 @@ columns:
isActive: activo isActive: activo
credit: crédito credit: crédito
creditInsurance: seguro crédito creditInsurance: seguro crédito
iban: iban iban: IBAN
dueDay: día vencimiento dueDay: día vencimiento
isEqualizated: igualado isEqualizated: igualado
isFreezed: congelado isFreezed: congelado
hasToInvoiceByAddress: factura por dirección hasToInvoiceByAddress: factura por dirección
hasToInvoice: tiene que facturar hasToInvoice: tiene que facturar
isToBeMailed: envío por email isToBeMailed: envío por email
hasSepaVnl: sepa nnl hasSepaVnl: sepa VNL
hasLcr: lcr hasLcr: LCR
hasCoreVnl: centro vnl hasCoreVnl: centro VNL
hasCoreVnh: cenrto vnh hasCoreVnh: centro VNH
hasIncoterms: incoterms hasIncoterms: incoterms
isTaxDataChecked: datos fiscales comprobados isTaxDataChecked: datos fiscales comprobados
eypbc: eypbc eypbc: EYPBC
quality: calidad quality: calidad
isVies: vies isVies: VIES
isRelevant: importante isRelevant: importante
accountingAccount: cuenta contable accountingAccount: cuenta contable
created: creado created: creado
sageTaxTypeFk: tipo impuesto sage sageTaxTypeFk: tipo impuesto sage
sageTransactionTypeFk: tipo transacción sage sageTransactionTypeFk: tipo transacción Sage
businessTypeFk: tipo negocio businessTypeFk: tipo negocio
salesPersonFk: comercial salesPersonFk: comercial
hasElectronicInvoice: factura electrónica hasElectronicInvoice: factura electrónica
@ -47,4 +47,9 @@ columns:
defaultAddressFk: dirección predeterminada defaultAddressFk: dirección predeterminada
bankEntityFk: entidad bancaria bankEntityFk: entidad bancaria
transferorFk: cedente transferorFk: cedente
riskCalculated: riesgo calculado
isCreatedAsServed: creado como servido
hasInvoiceSimplified: factura simple
typeFk: tipo
lastSalesPersonFk: último comercial

View File

@ -1,4 +1,4 @@
name: tpv transaction name: TPV transaction
columns: columns:
id: id id: id
merchantFk: merchant merchantFk: merchant

View File

@ -1,4 +1,4 @@
name: transacción tpv name: transacción TPV
columns: columns:
id: id id: id
merchantFk: comerciante merchantFk: comerciante

View File

@ -27,7 +27,6 @@ module.exports = Self => {
Object.assign(myOptions, options); Object.assign(myOptions, options);
const summaryObj = await getSummary(models.Client, clientFk, myOptions); const summaryObj = await getSummary(models.Client, clientFk, myOptions);
summaryObj.mana = await models.Client.getMana(clientFk, myOptions); summaryObj.mana = await models.Client.getMana(clientFk, myOptions);
summaryObj.debt = await models.Client.getDebt(clientFk, myOptions); summaryObj.debt = await models.Client.getDebt(clientFk, myOptions);
summaryObj.averageInvoiced = await models.Client.getAverageInvoiced(clientFk, myOptions); summaryObj.averageInvoiced = await models.Client.getAverageInvoiced(clientFk, myOptions);
@ -115,6 +114,12 @@ module.exports = Self => {
fields: ['claimingRate', 'priceIncreasing'], fields: ['claimingRate', 'priceIncreasing'],
limit: 1 limit: 1
} }
},
{
relation: 'businessType',
scope: {
fields: ['description']
}
} }
], ],
where: {id: clientId} where: {id: clientId}

View File

@ -67,9 +67,13 @@ module.exports = Self => {
uw.id workerFk, uw.id workerFk,
uw.name workerName, uw.name workerName,
c.creditInsurance, c.creditInsurance,
d.defaulterSinced d.defaulterSinced,
cn.country,
pm.name payMethod
FROM vn.defaulter d FROM vn.defaulter d
JOIN vn.client c ON c.id = d.clientFk JOIN vn.client c ON c.id = d.clientFk
JOIN vn.country cn ON cn.id = c.countryFk
JOIN vn.payMethod pm ON pm.id = c.payMethodFk
LEFT JOIN vn.clientObservation co ON co.clientFk = c.id LEFT JOIN vn.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

View File

@ -29,6 +29,16 @@
"type": "belongsTo", "type": "belongsTo",
"model": "Client", "model": "Client",
"foreignKey": "clientFk" "foreignKey": "clientFk"
},
"country": {
"type": "belongsTo",
"model": "Country",
"foreignKey": "country"
},
"payMethod": {
"type": "belongsTo",
"model": "PayMethod",
"foreignKey": "payMethod"
} }
} }
} }

View File

@ -57,6 +57,13 @@
<th field="salesPersonFk"> <th field="salesPersonFk">
<span translate>Comercial</span> <span translate>Comercial</span>
</th> </th>
<th field="country">
<span translate>Country</span>
</th>
<th field="payMethod"
vn-tooltip="Pay Method">
<span translate>P.Method</span>
</th>
<th <th
field="amount" field="amount"
vn-tooltip="Balance due"> vn-tooltip="Balance due">
@ -111,6 +118,12 @@
{{::defaulter.salesPersonName | dashIfEmpty}} {{::defaulter.salesPersonName | dashIfEmpty}}
</span> </span>
</td> </td>
<td>
{{::defaulter.country}}
</td>
<td>
{{::defaulter.payMethod}}
</td>
<td>{{::defaulter.amount | currency: 'EUR': 2}}</td> <td>{{::defaulter.amount | currency: 'EUR': 2}}</td>
<td> <td>
<span <span

View File

@ -20,8 +20,7 @@ export default class Controller extends Section {
showField: 'name', showField: 'name',
valueField: 'id' valueField: 'id'
} }
}, }, {
{
field: 'salesPersonFk', field: 'salesPersonFk',
autocomplete: { autocomplete: {
url: 'Workers/activeWithInheritedRole', url: 'Workers/activeWithInheritedRole',
@ -30,6 +29,18 @@ export default class Controller extends Section {
showField: 'name', showField: 'name',
valueField: 'id', valueField: 'id',
} }
}, {
field: 'country',
autocomplete: {
showField: 'country',
valueField: 'country'
}
}, {
field: 'payMethodFk',
autocomplete: {
showField: 'name',
valueField: 'id'
}
}, },
{ {
field: 'workerFk', field: 'workerFk',
@ -132,7 +143,7 @@ export default class Controller extends Section {
sendMail() { sendMail() {
const params = { const params = {
defaulters: this.checked, defaulters: this.checked,
observation: this.defaulter.observation observation: this.defaulter.observation,
}; };
this.$http.post(`Defaulters/observationEmail`, params); this.$http.post(`Defaulters/observationEmail`, params);
} }
@ -143,6 +154,8 @@ export default class Controller extends Section {
case 'amount': case 'amount':
case 'clientFk': case 'clientFk':
case 'workerFk': case 'workerFk':
case 'country':
case 'payMethod':
case 'salesPersonFk': case 'salesPersonFk':
return {[`d.${param}`]: value}; return {[`d.${param}`]: value};
case 'created': case 'created':

View File

@ -9,3 +9,6 @@ Search client: Buscar clientes
Worker who made the last observation: Trabajador que ha realizado la última observación Worker who made the last observation: Trabajador que ha realizado la última observación
Email sended!: Email enviado! Email sended!: Email enviado!
Observation saved!: Observación añadida! Observation saved!: Observación añadida!
P.Method: F.Pago
Pay Method: Forma de Pago
Country: Pais

View File

@ -64,6 +64,9 @@
<vn-label-value label="Channel" <vn-label-value label="Channel"
value="{{$ctrl.summary.contactChannel.name}}"> value="{{$ctrl.summary.contactChannel.name}}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Business type"
value="{{$ctrl.summary.businessType.description}}">
</vn-label-value>
</vn-one> </vn-one>
<vn-one> <vn-one>
<h4 ng-show="$ctrl.isEmployee"> <h4 ng-show="$ctrl.isEmployee">

View File

@ -23,3 +23,4 @@ Latest tickets: Últimos tickets
Rating: Clasificación Rating: Clasificación
Value from 1 to 20. The higher the better value: Valor del 1 al 20. Cuanto más alto mejor valoración Value from 1 to 20. The higher the better value: Valor del 1 al 20. Cuanto más alto mejor valoración
Go to grafana: Ir a grafana Go to grafana: Ir a grafana
Business type: Tipo de negocio

View File

@ -1,4 +1,4 @@
name: entry observation name: observation
columns: columns:
id: id id: id
description: description description: description

View File

@ -1,4 +1,4 @@
name: observación entrada name: observación
columns: columns:
id: id id: id
description: descripción description: descripción

View File

@ -1,4 +1,4 @@
name: item barcode name: barcode
columns: columns:
id: id id: id
code: code code: code

View File

@ -1,4 +1,4 @@
name: código barras artículo name: código barras
columns: columns:
id: id id: id
code: código code: código

View File

@ -1,4 +1,4 @@
name: item botanical name: botanical
columns: columns:
itemFk: item itemFk: item
genusFk: genus genusFk: genus

View File

@ -1,4 +1,4 @@
name: artículo botánico name: botánico
columns: columns:
itemFk: artículo itemFk: artículo
genusFk: género genusFk: género

View File

@ -1,4 +1,4 @@
name: item tag name: tag
columns: columns:
id: id id: id
value: value value: value

View File

@ -1,4 +1,4 @@
name: etiqueta artículo name: etiqueta
columns: columns:
id: id id: id
value: valor value: valor

View File

@ -1,4 +1,4 @@
name: item tax country name: country tax
columns: columns:
id: id id: id
effectived: effectived effectived: effectived

View File

@ -1,7 +1,7 @@
name: impuesto país del artículo name: impuesto país
columns: columns:
id: id id: id
effectived: efectivo effectived: efectivo
itemFk: artículo itemFk: artículo
countryFk: país countryFk: país
taxClassFk: clase impuestos taxClassFk: clase impuesto

View File

@ -44,4 +44,6 @@ columns:
intrastatFk: intrastat intrastatFk: intrastat
genericFk: generic genericFk: generic
itemFk: item itemFk: item
density: density
compression: compression

View File

@ -27,8 +27,8 @@ columns:
value9: valor9 value9: valor9
tag10: etiqueta10 tag10: etiqueta10
value10: valor10 value10: valor10
itemPackingTypeFk: embalaje del artículo itemPackingTypeFk: tipo embalaje
hasKgPrice: tiene precio kg hasKgPrice: precio kg
family: familia family: familia
expenseFk: gasto expenseFk: gasto
minPrice: precio mínimo minPrice: precio mínimo
@ -44,4 +44,6 @@ columns:
intrastatFk: intrastat intrastatFk: intrastat
genericFk: genérico genericFk: genérico
itemFk: artículo itemFk: artículo
density: densidad
compression: compresión

View File

@ -26,8 +26,8 @@ module.exports = Self => {
Object.assign(myOptions, options); Object.assign(myOptions, options);
const where = filter.where; const where = filter.where;
const query = 'CALL vn.item_getBalance(?, ?)'; const query = 'CALL vn.item_getBalance(?, ?, ?)';
const [diary] = await Self.rawSql(query, [where.itemFk, where.warehouseFk], myOptions); const [diary] = await Self.rawSql(query, [where.itemFk, where.warehouseFk, where.date], myOptions);
for (const entry of diary) for (const entry of diary)
if (entry.clientType === 'loses') entry.highlighted = true; if (entry.clientType === 'loses') entry.highlighted = true;

View File

@ -21,7 +21,8 @@ describe('item getBalance()', () => {
const filter = { const filter = {
where: { where: {
itemFk: 1, itemFk: 1,
warehouseFk: 1 warehouseFk: 1,
date: null
} }
}; };
const results = await models.Item.getBalance(filter, options); const results = await models.Item.getBalance(filter, options);
@ -45,14 +46,16 @@ describe('item getBalance()', () => {
const firstFilter = { const firstFilter = {
where: { where: {
itemFk: 1, itemFk: 1,
warehouseFk: 1 warehouseFk: 1,
date: null
} }
}; };
const secondFilter = { const secondFilter = {
where: { where: {
itemFk: 2, itemFk: 2,
warehouseFk: 1 warehouseFk: 1,
date: null
} }
}; };

View File

@ -25,6 +25,16 @@
ng-model="$ctrl.warehouseFk" ng-model="$ctrl.warehouseFk"
label="Select warehouse"> label="Select warehouse">
</vn-autocomplete> </vn-autocomplete>
<vn-check
ng-class="{'table-check':$ctrl.showOld}"
label="Show what's before the inventory"
ng-model="$ctrl.showOld">
</vn-check>
<vn-date-picker
label="Since"
ng-model="$ctrl.date"
ng-show="$ctrl.showOld">
</vn-date-picker>
</vn-horizontal> </vn-horizontal>
<vn-table model="model"> <vn-table model="model">
<vn-thead> <vn-thead>

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