232201_test_to_master #1582

Merged
alexm merged 126 commits from 232201_test_to_master into master 2023-06-01 06:16:49 +00:00
71 changed files with 755 additions and 405 deletions
Showing only changes of commit 32ce0c1a1a - Show all commits

View File

@ -5,17 +5,48 @@ 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).
## [2316.01] - 2023-05-04 ## [2322.01] - 2023-06-08
### Added
-
### Changed
-
### Fixed
-
## [2320.01] - 2023-05-25
### Added
- (Tickets -> Crear Factura) Al facturar se envia automáticamente el pdf al cliente
### Changed
- (Trabajadores -> Nuevo trabajador) Los clientes se crean sin 'TR' pero se añade tipo de negocio 'Trabajador'
### Fixed
-
## [2318.01] - 2023-05-08
### 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
### Changed ### Changed
- (Artículo -> Precio fijado) Modificado el buscador superior por uno lateral - (Artículo -> Precio fijado) Modificado el buscador superior por uno lateral
### Fixed ### Fixed
- - (Ticket -> Boxing) Arreglado selección de horas
- (Cesta -> Índice) Optimizada búsqueda
## [2314.01] - 2023-04-20 ## [2314.01] - 2023-04-20

View File

@ -34,6 +34,8 @@ async function test() {
app.boot(bootOptions, app.boot(bootOptions,
err => err ? reject(err) : resolve()); err => err ? reject(err) : resolve());
}); });
// FIXME: Workaround to wait for loopback to be ready
await app.models.Application.status();
const Jasmine = require('jasmine'); const Jasmine = require('jasmine');
const jasmine = new Jasmine(); const jasmine = new Jasmine();

View File

@ -1,5 +0,0 @@
UPDATE vn.supplier s
JOIN vn.country c ON c.id = s.countryFk
SET s.nif = MID(REPLACE(s.nif, ' ', ''), 3, LENGTH(REPLACE(s.nif, ' ', '')) - 1)
WHERE s.isVies = TRUE
AND c.code = LEFT(REPLACE(s.nif, ' ', ''), 2);

View File

@ -1,5 +0,0 @@
UPDATE IGNORE vn.client c
JOIN vn.country co ON co.id = c.countryFk
SET c.fi = MID(REPLACE(c.fi, ' ', ''), 3, LENGTH(REPLACE(c.fi, ' ', '')) - 1)
WHERE c.isVies = TRUE
AND co.code = LEFT(REPLACE(c.fi, ' ', ''), 2);

View File

@ -0,0 +1,77 @@
CREATE OR REPLACE
ALGORITHM = UNDEFINED VIEW `vn`.`zoneEstimatedDelivery` AS
select
`t`.`zoneFk` AS `zoneFk`,
cast(`util`.`VN_CURDATE`() + interval hour(ifnull(`zc`.`hour`, `z`.`hour`)) * 60 + minute(ifnull(`zc`.`hour`, `z`.`hour`)) minute as time) AS `hourTheoretical`,
cast(sum(`sv`.`volume`) as decimal(5, 1)) AS `totalVolume`,
cast(sum(if(`s`.`alertLevel` < 2, `sv`.`volume`, 0)) as decimal(5, 1)) AS `remainingVolume`,
greatest(
ifnull(`lhp`.`m3`, 0),
ifnull(`dl`.`minSpeed`, 0)
) AS `speed`,
cast(`zc`.`hour` + interval -sum(if(`s`.`alertLevel` < 2, `sv`.`volume`, 0)) * 60 / greatest(ifnull(`lhp`.`m3`, 0), ifnull(`dl`.`minSpeed`, 0)) minute as time) AS `hourEffective`,
floor(-sum(if(`s`.`alertLevel` < 2, `sv`.`volume`, 0)) * 60 / greatest(ifnull(`lhp`.`m3`, 0), ifnull(`dl`.`minSpeed`, 0))) AS `minutesLess`,
cast(`zc`.`hour` + interval -sum(if(`s`.`alertLevel` < 2, `sv`.`volume`, 0)) * 60 / greatest(ifnull(`lhp`.`m3`, 0), ifnull(`dl`.`minSpeed`, 0)) minute as time) AS `etc`
from
(
(
(
(
(
(
(
(
(
`vn`.`ticket` `t`
join `vn`.`ticketStateToday` `tst` on
(
`tst`.`ticket` = `t`.`id`
)
)
join `vn`.`state` `s` on
(
`s`.`id` = `tst`.`state`
)
)
join `vn`.`saleVolume` `sv` on
(
`sv`.`ticketFk` = `t`.`id`
)
)
left join `vn`.`lastHourProduction` `lhp` on
(
`lhp`.`warehouseFk` = `t`.`warehouseFk`
)
)
join `vn`.`warehouse` `w` on
(
`w`.`id` = `t`.`warehouseFk`
)
)
join `vn`.`warehouseAlias` `wa` on
(
`wa`.`id` = `w`.`aliasFk`
)
)
straight_join `vn`.`zone` `z` on
(
`z`.`id` = `t`.`zoneFk`
)
)
left join `vn`.`zoneClosure` `zc` on
(
`zc`.`zoneFk` = `t`.`zoneFk`
and `zc`.`dated` = `util`.`VN_CURDATE`()
)
)
left join `cache`.`departure_limit` `dl` on
(
`dl`.`warehouse_id` = `t`.`warehouseFk`
and `dl`.`fecha` = `util`.`VN_CURDATE`()
)
)
where
`w`.`hasProduction` <> 0
and cast(`t`.`shipped` as date) = `util`.`VN_CURDATE`()
group by
`t`.`zoneFk`;

View File

@ -0,0 +1,5 @@
UPDATE `vn`.`supplier` s
JOIN `vn`.`country` c ON c.id = s.countryFk
SET s.nif = MID(REPLACE(s.nif, ' ', ''), 3, LENGTH(REPLACE(s.nif, ' ', '')) - 1)
WHERE s.isVies = TRUE
AND c.code = LEFT(REPLACE(s.nif, ' ', ''), 2);

View File

@ -0,0 +1,5 @@
UPDATE IGNORE `vn`.`client` c
JOIN `vn`.`country` co ON co.id = c.countryFk
SET c.fi = MID(REPLACE(c.fi, ' ', ''), 3, LENGTH(REPLACE(c.fi, ' ', '')) - 1)
WHERE c.isVies = TRUE
AND co.code = LEFT(REPLACE(c.fi, ' ', ''), 2);

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

View File

@ -2801,9 +2801,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`, `businessTypeFk`)
VALUES VALUES
(1, NULL, 1); (1, NULL, 1, 'worker');
INSERT INTO `vn`.`ticketRefund`(`refundTicketFk`, `originalTicketFk`) INSERT INTO `vn`.`ticketRefund`(`refundTicketFk`, `originalTicketFk`)
VALUES VALUES

View File

@ -285,21 +285,6 @@ export default {
clientMandate: { clientMandate: {
firstMandateText: 'vn-client-mandate vn-card vn-table vn-tbody > vn-tr' firstMandateText: 'vn-client-mandate vn-card vn-table vn-tbody > vn-tr'
}, },
clientBalance: {
company: 'vn-client-balance-index vn-autocomplete[ng-model="$ctrl.companyId"]',
newPaymentButton: `vn-float-button`,
newPaymentBank: '.vn-dialog.shown vn-autocomplete[ng-model="$ctrl.bankFk"]',
newPaymentAmount: '.vn-dialog.shown vn-input-number[ng-model="$ctrl.amountPaid"]',
newDescription: 'vn-textfield[ng-model="$ctrl.receipt.description"]',
deliveredAmount: '.vn-dialog vn-input-number[ng-model="$ctrl.deliveredAmount"]',
refundAmount: '.vn-dialog vn-input-number[ng-model="$ctrl.amountToReturn"]',
saveButton: '.vn-dialog.shown [response="accept"]',
anyBalanceLine: 'vn-client-balance-index vn-tbody > vn-tr',
firstLineBalance: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(8)',
firstLineReference: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td-editable',
firstLineReferenceInput: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td-editable > div > field > vn-textfield',
compensationButton: 'vn-client-balance-index vn-icon-button[vn-dialog="send_compensation"]'
},
webPayment: { webPayment: {
confirmFirstPaymentButton: 'vn-client-web-payment vn-tr:nth-child(1) vn-icon-button[icon="done_all"]', confirmFirstPaymentButton: 'vn-client-web-payment vn-tr:nth-child(1) vn-icon-button[icon="done_all"]',
firstPaymentConfirmed: 'vn-client-web-payment vn-tr:nth-child(1) vn-icon[icon="check"]' firstPaymentConfirmed: 'vn-client-web-payment vn-tr:nth-child(1) vn-icon[icon="check"]'
@ -841,15 +826,6 @@ export default {
landedDatePicker: 'vn-date-picker[label="Landed"]', landedDatePicker: 'vn-date-picker[label="Landed"]',
createButton: 'button[type=submit]' createButton: 'button[type=submit]'
}, },
orderSummary: {
id: 'vn-order-summary vn-one:nth-child(1) > vn-label-value:nth-child(1) span',
alias: 'vn-order-summary vn-one:nth-child(1) > vn-label-value:nth-child(2) span',
consignee: 'vn-order-summary vn-one:nth-child(2) > vn-label-value:nth-child(6) span',
subtotal: 'vn-order-summary vn-one.taxes > p:nth-child(1)',
vat: 'vn-order-summary vn-one.taxes > p:nth-child(2)',
total: 'vn-order-summary vn-one.taxes > p:nth-child(3)',
sale: 'vn-order-summary vn-tbody > vn-tr',
},
orderCatalog: { orderCatalog: {
plantRealmButton: 'vn-order-catalog > vn-side-menu vn-icon[icon="icon-plant"]', plantRealmButton: 'vn-order-catalog > vn-side-menu vn-icon[icon="icon-plant"]',
type: 'vn-order-catalog vn-autocomplete[data="$ctrl.itemTypes"]', type: 'vn-order-catalog vn-autocomplete[data="$ctrl.itemTypes"]',

View File

@ -29,19 +29,16 @@ describe('Client Add greuge path', () => {
expect(message.text).toContain('Some fields are invalid'); expect(message.text).toContain('Some fields are invalid');
}); });
it(`should create a new greuge with all its data`, async() => { it(`should create a new greuge with all its data and confirm the greuge was added to the list`, async() => {
await page.write(selectors.clientGreuge.amount, '999'); await page.write(selectors.clientGreuge.amount, '999');
await page.waitForTextInField(selectors.clientGreuge.amount, '999'); await page.waitForTextInField(selectors.clientGreuge.amount, '999');
await page.write(selectors.clientGreuge.description, 'new armor for Batman!'); await page.write(selectors.clientGreuge.description, 'new armor for Batman!');
await page.waitToClick(selectors.clientGreuge.saveButton); await page.waitToClick(selectors.clientGreuge.saveButton);
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should confirm the greuge was added to the list', async() => {
const result = await page.waitToGetProperty(selectors.clientGreuge.firstGreugeText, 'innerText'); const result = await page.waitToGetProperty(selectors.clientGreuge.firstGreugeText, 'innerText');
expect(message.text).toContain('Data saved!');
expect(result).toContain(999); expect(result).toContain(999);
expect(result).toContain('new armor for Batman!'); expect(result).toContain('new armor for Batman!');
expect(result).toContain('Diff'); expect(result).toContain('Diff');

View File

@ -1,6 +1,17 @@
import selectors from '../../helpers/selectors'; import selectors from '../../helpers/selectors';
import getBrowser from '../../helpers/puppeteer'; import getBrowser from '../../helpers/puppeteer';
const $ = {
company: 'vn-client-balance-index vn-autocomplete[ng-model="$ctrl.companyId"]',
newPaymentButton: `vn-float-button`,
newPayment: '.vn-dialog.shown',
refundAmount: '.vn-dialog.shown [vn-name="amountToReturn"]',
saveButton: '.vn-dialog.shown [response="accept"]',
firstLineBalance: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(8)',
firstLineReference: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td-editable',
firstLineReferenceInput: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td-editable vn-textfield',
};
describe('Client balance path', () => { describe('Client balance path', () => {
let browser; let browser;
let page; let page;
@ -18,125 +29,100 @@ describe('Client balance path', () => {
it('should now edit the local user config data', async() => { it('should now edit the local user config data', async() => {
await page.waitToClick(selectors.globalItems.userMenuButton); await page.waitToClick(selectors.globalItems.userMenuButton);
await page.autocompleteSearch(selectors.globalItems.userLocalCompany, 'CCs'); await page.autocompleteSearch(selectors.globalItems.userLocalCompany, 'CCs');
const message = await page.waitForSnackbar(); const companyMessage = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should access to the balance section to check the data shown matches the local settings', async() => {
await page.accessToSection('client.card.balance.index'); await page.accessToSection('client.card.balance.index');
let result = await page.waitToGetProperty(selectors.clientBalance.company, 'value'); const company = await page.getValue($.company);
expect(result).toEqual('CCs');
});
it('should now clear the user local settings', async() => {
await page.waitToClick(selectors.globalItems.userMenuButton); await page.waitToClick(selectors.globalItems.userMenuButton);
await page.clearInput(selectors.globalItems.userConfigThirdAutocomplete); await page.clearInput(selectors.globalItems.userConfigThirdAutocomplete);
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should reload the section', async() => {
await page.closePopup(); await page.closePopup();
await page.reloadSection('client.card.balance.index'); await page.reloadSection('client.card.balance.index');
expect(companyMessage.isSuccess).toBeTrue();
expect(company).toEqual('CCs');
expect(message.isSuccess).toBeTrue();
}); });
it('should create a new payment that clears the debt', async() => { it('should create a new payment that clears the debt', async() => {
await page.closePopup(); await page.waitToClick($.newPaymentButton);
await page.waitToClick(selectors.clientBalance.newPaymentButton); await page.fillForm($.newPayment, {
await page.autocompleteSearch(selectors.clientBalance.newPaymentBank, 'Cash'); bank: 'Cash',
await page.clearInput(selectors.clientBalance.newDescription); description: 'Description',
await page.write(selectors.clientBalance.newDescription, 'Description'); viewReceipt: false
await page.waitToClick(selectors.clientBalance.saveButton); });
await page.respondToDialog('accept');
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!'); expect(message.isSuccess).toBeTrue();
}); });
it('should edit the 1st line reference', async() => { it('should edit the 1st line reference and check data', async() => {
await page.waitToClick(selectors.clientBalance.firstLineReference); await page.waitToClick($.firstLineReference);
await page.write(selectors.clientBalance.firstLineReferenceInput, 'Miscellaneous payment'); await page.write($.firstLineReferenceInput, 'Miscellaneous payment');
await page.keyboard.press('Enter'); await page.keyboard.press('Enter');
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should check balance is now 0, the reference was saved and the company is now VNL becouse the user local settings were removed', async() => {
await page.waitForSpinnerLoad(); await page.waitForSpinnerLoad();
let company = await page let company = await page.getValue($.company);
.waitToGetProperty(selectors.clientBalance.company, 'value'); let reference = await page.innerText($.firstLineReference);
let firstBalanceLine = await page.innerText($.firstLineBalance);
let reference = await page
.waitToGetProperty(selectors.clientBalance.firstLineReference, 'innerText');
let firstBalanceLine = await page
.waitToGetProperty(selectors.clientBalance.firstLineBalance, 'innerText');
expect(message.isSuccess).toBeTrue();
expect(company).toEqual('VNL'); expect(company).toEqual('VNL');
expect(reference).toEqual('Miscellaneous payment'); expect(reference).toEqual('Miscellaneous payment');
expect(firstBalanceLine).toContain('0.00'); expect(firstBalanceLine).toContain('0.00');
}); });
it('should create a new payment and check the cash comparison works correctly', async() => { it('should create a new payment, check the cash comparison works correctly and balance value is -100', async() => {
const amountPaid = '100'; await page.waitToClick($.newPaymentButton);
const cashHanded = '500'; await page.fillForm($.newPayment, {
const expectedRefund = '400'; amountPaid: 100,
description: 'Payment',
await page.waitToClick(selectors.clientBalance.newPaymentButton); deliveredAmount: 500,
await page.write(selectors.clientBalance.newPaymentAmount, amountPaid); viewReceipt: false
await page.clearInput(selectors.clientBalance.newDescription); });
await page.write(selectors.clientBalance.newDescription, 'Payment'); const refund = await page.getValue($.refundAmount);
await page.write(selectors.clientBalance.deliveredAmount, cashHanded); await page.respondToDialog('accept');
const refund = await page.waitToGetProperty(selectors.clientBalance.refundAmount, 'value');
await page.waitToClick(selectors.clientBalance.saveButton);
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
expect(refund).toEqual(expectedRefund); const result = await page.innerText($.firstLineBalance);
expect(message.text).toContain('Data saved!');
});
it('should check the balance value is now -100', async() => {
let result = await page
.waitToGetProperty(selectors.clientBalance.firstLineBalance, 'innerText');
expect(refund).toEqual('400');
expect(message.isSuccess).toBeTrue();
expect(result).toContain('-€100.00'); expect(result).toContain('-€100.00');
}); });
it('should create a new payment and check the cash exceeded the maximum', async() => { it('should create a new payment and check the cash exceeded the maximum', async() => {
const amountPaid = '1001'; await page.waitToClick($.newPaymentButton);
await page.fillForm($.newPayment, {
await page.closePopup(); bank: 'Cash',
await page.waitToClick(selectors.clientBalance.newPaymentButton); amountPaid: 1001,
await page.autocompleteSearch(selectors.clientBalance.newPaymentBank, 'Cash'); description: 'Payment'
await page.write(selectors.clientBalance.newPaymentAmount, amountPaid); });
await page.clearInput(selectors.clientBalance.newDescription); await page.waitToClick($.saveButton);
await page.write(selectors.clientBalance.newDescription, 'Payment');
await page.waitToClick(selectors.clientBalance.saveButton);
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
expect(message.text).toContain('Amount exceeded'); expect(message.text).toContain('Amount exceeded');
}); });
it('should create a new payment that sets the balance back to the original negative value', async() => { it('should create a new payment that sets the balance back to negative value and check it', async() => {
await page.closePopup(); await page.closePopup();
await page.waitToClick(selectors.clientBalance.newPaymentButton); await page.waitToClick($.newPaymentButton);
await page.autocompleteSearch(selectors.clientBalance.newPaymentBank, 'Pay on receipt');
await page.overwrite(selectors.clientBalance.newPaymentAmount, '-150'); await page.fillForm($.newPayment, {
await page.clearInput(selectors.clientBalance.newDescription); bank: 'Pay on receipt',
await page.write(selectors.clientBalance.newDescription, 'Description'); amountPaid: -150,
await page.waitToClick(selectors.clientBalance.saveButton); description: 'Description'
});
await page.respondToDialog('accept');
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!'); const result = await page.innerText($.firstLineBalance);
});
it('should check balance is now 50', async() => {
let result = await page
.waitToGetProperty(selectors.clientBalance.firstLineBalance, 'innerText');
expect(message.isSuccess).toBeTrue();
expect(result).toEqual('€50.00'); expect(result).toEqual('€50.00');
}); });
@ -149,12 +135,9 @@ describe('Client balance path', () => {
await page.waitForState('client.index'); await page.waitForState('client.index');
}); });
it('should now search for the user Petter Parker', async() => { it('should now search for the user Petter Parker not check the payment button is not present', async() => {
await page.accessToSearchResult('Petter Parker'); await page.accessToSearchResult('Petter Parker');
await page.accessToSection('client.card.balance.index'); await page.accessToSection('client.card.balance.index');
}); await page.waitForSelector($.newPaymentButton, {hidden: true});
it('should not be able to click the new payment button as it isnt present', async() => {
await page.waitForSelector(selectors.clientBalance.newPaymentButton, {hidden: true});
}); });
}); });

View File

@ -1,6 +1,11 @@
import selectors from '../../helpers/selectors';
import getBrowser from '../../helpers/puppeteer'; import getBrowser from '../../helpers/puppeteer';
const $ = {
company: 'vn-client-balance-index vn-autocomplete[ng-model="$ctrl.companyId"]',
compensationButton: 'vn-client-balance-index vn-icon-button[vn-dialog="send_compensation"]',
saveButton: '.vn-dialog.shown [response="accept"]'
};
describe('Client Send balance compensation', () => { describe('Client Send balance compensation', () => {
let browser; let browser;
let page; let page;
@ -17,9 +22,9 @@ describe('Client Send balance compensation', () => {
}); });
it(`should click on send compensation button`, async() => { it(`should click on send compensation button`, async() => {
await page.autocompleteSearch(selectors.clientBalance.company, 'VNL'); await page.autocompleteSearch($.company, 'VNL');
await page.waitToClick(selectors.clientBalance.compensationButton); await page.waitToClick($.compensationButton);
await page.waitToClick(selectors.clientBalance.saveButton); await page.waitToClick($.saveButton);
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
expect(message.text).toContain('Notification sent!'); expect(message.text).toContain('Notification sent!');

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

@ -53,12 +53,4 @@ describe('Item edit tax path', () => {
expect(firstVatType).toEqual('Reduced VAT'); expect(firstVatType).toEqual('Reduced VAT');
}); });
// # #2680 Undo changes button bugs
xit(`should now click the undo changes button and see the form is restored`, async() => {
await page.waitToClick(selectors.itemTax.undoChangesButton);
const firstVatType = await page.waitToGetProperty(selectors.itemTax.firstClass, 'value');
expect(firstVatType).toEqual('General VAT');
});
}); });

View File

@ -316,7 +316,7 @@ describe('Ticket Edit sale path', () => {
it('should confirm the transfered quantity is the correct one', async() => { it('should confirm the transfered quantity is the correct one', async() => {
const result = await page.waitToGetProperty(selectors.ticketSales.secondSaleQuantityCell, 'innerText'); const result = await page.waitToGetProperty(selectors.ticketSales.secondSaleQuantityCell, 'innerText');
expect(result).toContain('20'); expect(result).toContain('10');
}); });
it('should go back to the original ticket sales section', async() => { it('should go back to the original ticket sales section', async() => {
@ -425,20 +425,6 @@ describe('Ticket Edit sale path', () => {
expect(result).toBeFalsy(); expect(result).toBeFalsy();
}); });
// tickets no longer update their totals instantly, a task performed ever 5-10 mins does it. disabled this test until it changes.
xit('should update all sales discount', async() => {
await page.closePopup();
await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuUpdateDiscount);
await page.waitForSelector(selectors.ticketSales.moreMenuUpdateDiscountInput);
await page.type(selectors.ticketSales.moreMenuUpdateDiscountInput, '100');
await page.keyboard.press('Enter');
await page.waitForTextInElement(selectors.ticketSales.totalImport, '0.00');
const result = await page.waitToGetProperty(selectors.ticketSales.totalImport, 'innerText');
expect(result).toContain('0.00');
});
it('should log in as Production role and go to a target ticket summary', async() => { it('should log in as Production role and go to a target ticket summary', async() => {
await page.loginAndModule('production', 'ticket'); await page.loginAndModule('production', 'ticket');
await page.accessToSearchResult('13'); await page.accessToSearchResult('13');

View File

@ -1,5 +1,10 @@
import selectors from '../../helpers/selectors.js'; import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer'; import getBrowser from '../../helpers/puppeteer';
const $ = {
newPayment: '.vn-dialog.shown',
anyBalanceLine: 'vn-client-balance-index vn-tbody > vn-tr',
firstLineReference: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td-editable'
};
describe('Ticket index payout path', () => { describe('Ticket index payout path', () => {
let browser; let browser;
@ -8,17 +13,14 @@ describe('Ticket index payout path', () => {
beforeAll(async() => { beforeAll(async() => {
browser = await getBrowser(); browser = await getBrowser();
page = browser.page; page = browser.page;
await page.loginAndModule('administrative', 'ticket');
await page.waitForState('ticket.index');
}); });
afterAll(async() => { afterAll(async() => {
await browser.close(); await browser.close();
}); });
it('should navigate to the ticket index', async() => {
await page.loginAndModule('administrative', 'ticket');
await page.waitForState('ticket.index');
});
it('should check the second ticket from a client and 1 of another', async() => { it('should check the second ticket from a client and 1 of another', async() => {
await page.waitToClick(selectors.globalItems.searchButton); await page.waitToClick(selectors.globalItems.searchButton);
await page.waitToClick(selectors.ticketsIndex.thirdTicketCheckbox); await page.waitToClick(selectors.ticketsIndex.thirdTicketCheckbox);
@ -42,27 +44,27 @@ describe('Ticket index payout path', () => {
await page.waitForSelector(selectors.ticketsIndex.payoutCompany); await page.waitForSelector(selectors.ticketsIndex.payoutCompany);
}); });
it('should fill the company and bank to perform a payout', async() => { it('should fill the company and bank to perform a payout and check a new balance line was entered', async() => {
await page.autocompleteSearch(selectors.ticketsIndex.payoutCompany, 'VNL'); await page.fillForm($.newPayment, {
await page.autocompleteSearch(selectors.ticketsIndex.payoutBank, 'cash'); company: 'VNL',
await page.write(selectors.clientBalance.newPaymentAmount, '100'); bank: 'cash',
await page.write(selectors.ticketsIndex.payoutDescription, 'Payment'); amountPaid: 100,
await page.waitToClick(selectors.ticketsIndex.submitPayout); description: 'Payment',
viewReceipt: false
});
await page.respondToDialog('accept');
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should navigate to the client balance section and check a new balance line was entered', async() => {
await page.waitToClick(selectors.globalItems.homeButton); await page.waitToClick(selectors.globalItems.homeButton);
await page.selectModule('client'); await page.selectModule('client');
await page.accessToSearchResult('1101'); await page.accessToSearchResult('1101');
await page.accessToSection('client.card.balance.index'); await page.accessToSection('client.card.balance.index');
await page.waitForSelector(selectors.clientBalance.anyBalanceLine); await page.waitForSelector($.anyBalanceLine);
const count = await page.countElement(selectors.clientBalance.anyBalanceLine); const count = await page.countElement($.anyBalanceLine);
const reference = await page.waitToGetProperty(selectors.clientBalance.firstLineReference, 'innerText'); const reference = await page.innerText($.firstLineReference);
expect(message.isSuccess).toBeTrue();
expect(count).toEqual(4); expect(count).toEqual(4);
expect(reference).toContain('Cash, Albaran: 7, 8Payment'); expect(reference).toContain('Payment');
}); });
}); });

View File

@ -1,6 +1,15 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer'; import getBrowser from '../../helpers/puppeteer';
const $ = {
id: 'vn-order-summary vn-one:nth-child(1) > vn-label-value:nth-child(1) span',
alias: 'vn-order-summary vn-one:nth-child(1) > vn-label-value:nth-child(2) span',
consignee: 'vn-order-summary vn-one:nth-child(2) > vn-label-value:nth-child(6) span',
subtotal: 'vn-order-summary vn-one.taxes > p:nth-child(1)',
vat: 'vn-order-summary vn-one.taxes > p:nth-child(2)',
total: 'vn-order-summary vn-one.taxes > p:nth-child(3)',
sale: 'vn-order-summary vn-tbody > vn-tr',
};
describe('Order summary path', () => { describe('Order summary path', () => {
let browser; let browser;
let page; let page;
@ -15,49 +24,23 @@ describe('Order summary path', () => {
await browser.close(); await browser.close();
}); });
it('should reach the order summary section', async() => { it('should reach the order summary section and check data', async() => {
await page.waitForState('order.card.summary'); await page.waitForState('order.card.summary');
});
it('should check the summary contains the order id', async() => { const id = await page.innerText($.id);
const result = await page.waitToGetProperty(selectors.orderSummary.id, 'innerText'); const alias = await page.innerText($.alias);
const consignee = await page.innerText($.consignee);
const subtotal = await page.innerText($.subtotal);
const vat = await page.innerText($.vat);
const total = await page.innerText($.total);
const sale = await page.countElement($.sale);
expect(result).toEqual('16'); expect(id).toEqual('16');
}); expect(alias).toEqual('Many places');
expect(consignee).toEqual('address 26 - Gotham (Province one)');
it('should check the summary contains the order alias', async() => { expect(subtotal.length).toBeGreaterThan(1);
const result = await page.waitToGetProperty(selectors.orderSummary.alias, 'innerText'); expect(vat.length).toBeGreaterThan(1);
expect(total.length).toBeGreaterThan(1);
expect(result).toEqual('Many places'); expect(sale).toBeGreaterThan(0);
});
it('should check the summary contains the order consignee', async() => {
const result = await page.waitToGetProperty(selectors.orderSummary.consignee, 'innerText');
expect(result).toEqual('address 26 - Gotham (Province one)');
});
it('should check the summary contains the order subtotal', async() => {
const result = await page.waitToGetProperty(selectors.orderSummary.subtotal, 'innerText');
expect(result.length).toBeGreaterThan(1);
});
it('should check the summary contains the order vat', async() => {
const result = await page.waitToGetProperty(selectors.orderSummary.vat, 'innerText');
expect(result.length).toBeGreaterThan(1);
});
it('should check the summary contains the order total', async() => {
const result = await page.waitToGetProperty(selectors.orderSummary.total, 'innerText');
expect(result.length).toBeGreaterThan(1);
});
it('should check the summary contains the order sales', async() => {
const result = await page.countElement(selectors.orderSummary.sale);
expect(result).toBeGreaterThan(0);
}); });
}); });

View File

@ -57,11 +57,4 @@ describe('Route tickets path', () => {
it('should now count how many tickets are in route to find one less', async() => { it('should now count how many tickets are in route to find one less', async() => {
await page.waitForNumberOfElements(selectors.routeTickets.anyTicket, 0); await page.waitForNumberOfElements(selectors.routeTickets.anyTicket, 0);
}); });
// #2862 updateVolume() route descriptor no actualiza volumen
xit('should confirm the route volume on the descriptor has been updated by the changes made', async() => {
const result = await page.waitToGetProperty(selectors.routeDescriptor.volume, 'innerText');
expect(result).toEqual('0 / 50 m³');
});
}); });

View File

@ -42,20 +42,6 @@ describe('Entry lastest buys path', () => {
expect(httpRequests.find(req => req.includes(('typeFk')))).toBeDefined(); expect(httpRequests.find(req => req.includes(('typeFk')))).toBeDefined();
}); });
it('should filter by from date', async() => {
await page.pickDate(selectors.entryLatestBuys.fromInput, new Date());
await page.waitToClick(selectors.entryLatestBuys.chip);
expect(httpRequests.find(req => req.includes(('from')))).toBeDefined();
});
it('should filter by to date', async() => {
await page.pickDate(selectors.entryLatestBuys.toInput, new Date());
await page.waitToClick(selectors.entryLatestBuys.chip);
expect(httpRequests.find(req => req.includes(('to')))).toBeDefined();
});
it('should filter by sales person', async() => { it('should filter by sales person', async() => {
await page.autocompleteSearch(selectors.entryLatestBuys.salesPersonInput, 'buyerNick'); await page.autocompleteSearch(selectors.entryLatestBuys.salesPersonInput, 'buyerNick');
await page.waitToClick(selectors.entryLatestBuys.chip); await page.waitToClick(selectors.entryLatestBuys.chip);

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

@ -36,30 +36,6 @@ describe('Component vnPopover', () => {
expect(controller.emit).not.toHaveBeenCalledWith('open'); expect(controller.emit).not.toHaveBeenCalledWith('open');
}); });
// #1615 migrar karma a jest (this doesn't work anymore, needs fixing)
xit(`should check that popover is visible into the screen`, () => {
$parent.css({
backgroundColor: 'red',
position: 'absolute',
width: '50px',
height: '50px',
top: '0',
left: '100px'
});
controller.show($parent[0]);
let rect = controller.popover.getBoundingClientRect();
let style = controller.window.getComputedStyle(controller.element);
expect(style.visibility).toEqual('visible');
expect(style.display).not.toEqual('none');
expect(0).toBeLessThanOrEqual(rect.top);
expect(0).toBeLessThanOrEqual(rect.left);
expect(controller.window.innerHeight).toBeGreaterThan(rect.bottom);
expect(controller.window.innerWidth).toBeGreaterThan(rect.right);
});
}); });
describe('hide()', () => { describe('hide()', () => {

View File

@ -8,18 +8,8 @@ export default class Th {
$element.on('click', () => this.onToggleOrder()); $element.on('click', () => this.onToggleOrder());
} }
/**
* Changes the order if the cell has a field and defaultOrder property
*/
$onInit() { $onInit() {
if (!this.field) return; if (!this.field) return;
if (this.defaultOrder) {
this.order = this.defaultOrder;
this.table.applyOrder(this.field, this.order);
this.updateArrow();
}
this.updateArrow(); this.updateArrow();
} }
@ -82,9 +72,6 @@ ngModule.vnComponent('vnTh', {
template: require('./index.html'), template: require('./index.html'),
transclude: true, transclude: true,
controller: Th, controller: Th,
bindings: {
defaultOrder: '@?'
},
require: { require: {
table: '^^vnTable' table: '^^vnTable'
} }

View File

@ -17,17 +17,6 @@ describe('Component vnTh', () => {
controller.column.setAttribute('field', 'MyField'); controller.column.setAttribute('field', 'MyField');
})); }));
describe('onInit()', () => {
it(`should define controllers order as per defaultOrder then call setOrder()`, () => {
controller.defaultOrder = 'DESC';
jest.spyOn(controller.table, 'setOrder');
controller.$onInit();
expect(controller.order).toEqual('DESC');
expect(controller.table.setOrder).toHaveBeenCalledWith('MyField', 'DESC');
});
});
describe('toggleOrder()', () => { describe('toggleOrder()', () => {
it(`should change the ordenation to DESC (descendant) if it was ASC (ascendant)`, () => { it(`should change the ordenation to DESC (descendant) if it was ASC (ascendant)`, () => {
controller.order = 'ASC'; controller.order = 'ASC';

View File

@ -14,9 +14,12 @@
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"
is-loading="model.isLoading"
class="vn-w-md 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 : 'System'}}"
@ -33,7 +36,7 @@
<div class="header vn-mb-sm"> <div class="header vn-mb-sm">
<div <div
class="date text-secondary text-caption" class="date text-secondary text-caption"
title="{{::log.creationDate | date:'dd/MM/yyyy HH:mm'}}"> title="{{::log.creationDate | date:'dd/MM/yyyy HH:mm:ss'}}">
{{::$ctrl.relativeDate(log.creationDate)}} {{::$ctrl.relativeDate(log.creationDate)}}
</div> </div>
<span class="chip" ng-class="::$ctrl.actionsClass[log.action]" translate> <span class="chip" ng-class="::$ctrl.actionsClass[log.action]" translate>

View File

@ -4,7 +4,7 @@ vn-log {
.change { .change {
display: flex; display: flex;
& > .user-wrapper { & > .left {
position: relative; position: relative;
padding-right: 10px; padding-right: 10px;
@ -34,7 +34,7 @@ vn-log {
bottom: -8px; bottom: -8px;
} }
} }
&:last-child > .user-wrapper > .line { &:last-child > .left > .line {
display: none; display: none;
} }
.detail { .detail {

View File

@ -170,5 +170,6 @@
"comercialName": "Comercial", "comercialName": "Comercial",
"Added observation": "Added observation", "Added observation": "Added observation",
"Comment added to client": "Comment added to client", "Comment added to client": "Comment added to client",
"This ticket is already a refund": "This ticket is already a refund" "This ticket is already a refund": "This ticket is already a refund",
"A claim with that sale already exists": "A claim with that sale already exists"
} }

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

@ -13,6 +13,7 @@
<vn-date-picker <vn-date-picker
label="Date" label="Date"
ng-model="$ctrl.receipt.payed" ng-model="$ctrl.receipt.payed"
vn-name="payed"
required="true"> required="true">
</vn-date-picker> </vn-date-picker>
<vn-autocomplete <vn-autocomplete
@ -21,6 +22,7 @@
show-field="code" show-field="code"
value-field="id" value-field="id"
ng-model="$ctrl.companyFk" ng-model="$ctrl.companyFk"
vn-name="company"
required="true"> required="true">
</vn-autocomplete> </vn-autocomplete>
</vn-horizontal> </vn-horizontal>
@ -33,6 +35,7 @@
fields="['accountingTypeFk']" fields="['accountingTypeFk']"
include="{relation: 'accountingType'}" include="{relation: 'accountingType'}"
ng-model="$ctrl.bankFk" ng-model="$ctrl.bankFk"
vn-name="bank"
search-function="$ctrl.bankSearchFunc($search)" search-function="$ctrl.bankSearchFunc($search)"
selection="$ctrl.bankSelection" selection="$ctrl.bankSelection"
order="id" order="id"
@ -43,6 +46,7 @@
vn-focus vn-focus
label="Amount" label="Amount"
ng-model="$ctrl.amountPaid" ng-model="$ctrl.amountPaid"
vn-name="amountPaid"
step="0.01" step="0.01"
required="true" required="true"
max="$ctrl.maxAmount"> max="$ctrl.maxAmount">
@ -52,6 +56,7 @@
<h6 translate>Compensation</h6> <h6 translate>Compensation</h6>
<vn-textfield <vn-textfield
ng-model="$ctrl.receipt.compensationAccount" ng-model="$ctrl.receipt.compensationAccount"
vn-name="compensationAccount"
label="Compensation Account" label="Compensation Account"
on-change="$ctrl.accountShortToStandard(value)"> on-change="$ctrl.accountShortToStandard(value)">
</vn-textfield> </vn-textfield>
@ -60,6 +65,7 @@
<vn-textfield <vn-textfield
label="Reference" label="Reference"
ng-model="$ctrl.receipt.description" ng-model="$ctrl.receipt.description"
vn-name="description"
rule rule
required="true"> required="true">
</vn-textfield> </vn-textfield>
@ -70,23 +76,27 @@
<vn-input-number <vn-input-number
ng-model="$ctrl.deliveredAmount" ng-model="$ctrl.deliveredAmount"
label="Delivered amount" label="Delivered amount"
step="0.01"> step="0.01"
vn-name="deliveredAmount">
</vn-input-number> </vn-input-number>
<vn-input-number <vn-input-number
disabled="true" disabled="true"
ng-model="$ctrl.amountToReturn" ng-model="$ctrl.amountToReturn"
label="Amount to return"> label="Amount to return"
vn-name="amountToReturn">
</vn-input-number> </vn-input-number>
</vn-horizontal> </vn-horizontal>
</vn-vertical> </vn-vertical>
<vn-horizontal ng-show="$ctrl.bankSelection.accountingType.code == 'cash'"> <vn-horizontal ng-show="$ctrl.bankSelection.accountingType.code == 'cash'">
<vn-check <vn-check
label="View receipt" label="View receipt"
ng-model="$ctrl.viewReceipt"> ng-model="$ctrl.viewReceipt"
vn-name="viewReceipt">
</vn-check> </vn-check>
<vn-check <vn-check
label="Send email" label="Send email"
ng-model="$ctrl.sendEmail"> ng-model="$ctrl.sendEmail"
vn-name="sendEmail">
</vn-check> </vn-check>
</vn-horizontal> </vn-horizontal>
</tpl-body> </tpl-body>

View File

@ -28,7 +28,7 @@
<vn-table model="model" auto-load="false"> <vn-table model="model" auto-load="false">
<vn-thead> <vn-thead>
<vn-tr> <vn-tr>
<vn-th field="shipped" default-order="DESC" expand>Date</vn-th> <vn-th field="shipped" expand>Date</vn-th>
<vn-th field="userFk">Created by</vn-thfield></vn-th> <vn-th field="userFk">Created by</vn-thfield></vn-th>
<vn-th field="description">Comment</vn-th> <vn-th field="description">Comment</vn-th>
<vn-th field="greugeTypeFk">Type</vn-th> <vn-th field="greugeTypeFk">Type</vn-th>

View File

@ -11,8 +11,7 @@ class Controller extends Section {
scope: { scope: {
fields: ['id', 'name'] fields: ['id', 'name']
}, },
}, }, {
{
relation: 'user', relation: 'user',
scope: { scope: {
fields: ['id', 'name'] fields: ['id', 'name']
@ -24,8 +23,6 @@ class Controller extends Section {
} }
} }
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnClientGreugeIndex', { ngModule.vnComponent('vnClientGreugeIndex', {
template: require('./index.html'), template: require('./index.html'),
controller: Controller controller: Controller

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

@ -141,7 +141,7 @@ module.exports = Self => {
let stmt; let stmt;
stmt = new ParameterizedSQL( stmt = new ParameterizedSQL(
`CREATE TEMPORARY TABLE tmp.filter `CREATE OR REPLACE TEMPORARY TABLE tmp.filter
(INDEX (id)) (INDEX (id))
ENGINE = MEMORY ENGINE = MEMORY
SELECT SELECT

View File

@ -24,7 +24,7 @@
<vn-table model="model"> <vn-table model="model">
<vn-thead> <vn-thead>
<vn-tr> <vn-tr>
<vn-th shrink field="itemFk" default-order="ASC" number>Item</vn-th> <vn-th shrink field="itemFk" number>Item</vn-th>
<vn-th>Description</vn-th> <vn-th>Description</vn-th>
<vn-th shrink field="quantity" number>Quantity</vn-th> <vn-th shrink field="quantity" number>Quantity</vn-th>
<vn-th shrink number>m³ per quantity</vn-th> <vn-th shrink number>m³ per quantity</vn-th>

View File

@ -6,9 +6,10 @@ class Controller extends Section {
constructor($element, $) { constructor($element, $) {
super($element, $); super($element, $);
this.filter = { this.filter = {
include: [{ include: {
relation: 'item' relation: 'item'
}] },
order: 'itemFk'
}; };
this.order = {}; this.order = {};
this.ticketVolumes = []; this.ticketVolumes = [];

View File

@ -1,3 +1,4 @@
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('editableStates', { Self.remoteMethodCtx('editableStates', {
description: 'Gets the editable states according the user role ', description: 'Gets the editable states according the user role ',
@ -19,25 +20,16 @@ module.exports = Self => {
Self.editableStates = async(ctx, filter, options) => { Self.editableStates = async(ctx, filter, options) => {
const models = Self.app.models; const models = Self.app.models;
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const myOptions = {}; const myOptions = {...(options || {})};
if (typeof options == 'object')
Object.assign(myOptions, options);
let statesList = await models.State.find(filter, myOptions);
const isProduction = await models.VnUser.hasRole(userId, 'production', myOptions); const isProduction = await models.VnUser.hasRole(userId, 'production', myOptions);
const isSalesPerson = await models.VnUser.hasRole(userId, 'salesPerson', myOptions);
const isAdministrative = await models.VnUser.hasRole(userId, 'administrative', myOptions); const isAdministrative = await models.VnUser.hasRole(userId, 'administrative', myOptions);
if (isProduction || isAdministrative) if (!isProduction && !isAdministrative)
return statesList; filter = mergeFilters(filter, {where: {alertLevel: 0}});
if (isSalesPerson) { const states = await models.State.find(filter, myOptions);
return statesList = statesList.filter(stateList =>
stateList.alertLevel === 0 || stateList.code === 'PICKER_DESIGNED'
);
}
return statesList.filter(stateList => stateList.alertLevel === 0); return states;
}; };
}; };

View File

@ -24,31 +24,6 @@ describe('ticket editableStates()', () => {
} }
}); });
it(`should return the expected states by a specific role`, async() => {
const tx = await models.State.beginTransaction({});
try {
const options = {transaction: tx};
const productionRole = 18;
const ctx = {req: {accessToken: {userId: productionRole}}};
const editableStates = await models.State.editableStates(ctx, filter, options);
const deliveredState = editableStates.some(state => state.code == 'DELIVERED');
const pickerDesignedState = editableStates.some(state => state.code == 'PICKER_DESIGNED');
expect(deliveredState).toBeFalsy();
expect(pickerDesignedState).toBeTruthy();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it(`should return again the expected state by a specific role`, async() => { it(`should return again the expected state by a specific role`, async() => {
const tx = await models.State.beginTransaction({}); const tx = await models.State.beginTransaction({});
@ -70,4 +45,25 @@ describe('ticket editableStates()', () => {
throw e; throw e;
} }
}); });
it(`should return the expected state matching with the name`, async() => {
const tx = await models.State.beginTransaction({});
try {
const options = {transaction: tx};
const employeeRole = 1;
const ctx = {req: {accessToken: {userId: employeeRole}}};
const filter = {where: {name: {like: '%Previa OK%'}}};
const [editableStates] = await models.State.editableStates(ctx, filter, options);
expect(editableStates.name).toBe('Previa OK');
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
}); });

View File

@ -124,6 +124,7 @@ module.exports = Self => {
const isDeliveryBoss = await models.VnUser.hasRole(userId, 'deliveryBoss', myOptions); const isDeliveryBoss = await models.VnUser.hasRole(userId, 'deliveryBoss', myOptions);
if (!isDeliveryBoss) { if (!isDeliveryBoss) {
const zoneShipped = await models.Agency.getShipped( const zoneShipped = await models.Agency.getShipped(
ctx,
args.landed, args.landed,
args.addressFk, args.addressFk,
args.agencyModeFk, args.agencyModeFk,

View File

@ -24,7 +24,6 @@ module.exports = function(Self) {
}); });
Self.makeInvoice = async(ctx, ticketsIds, options) => { Self.makeInvoice = async(ctx, ticketsIds, options) => {
const userId = ctx.req.accessToken.userId;
const models = Self.app.models; const models = Self.app.models;
const date = Date.vnNew(); const date = Date.vnNew();
date.setHours(0, 0, 0, 0); date.setHours(0, 0, 0, 0);
@ -42,6 +41,7 @@ module.exports = function(Self) {
let serial; let serial;
let invoiceId; let invoiceId;
let invoiceOut;
try { try {
const tickets = await models.Ticket.find({ const tickets = await models.Ticket.find({
where: { where: {
@ -89,24 +89,14 @@ module.exports = function(Self) {
invoiceId = resultInvoice.id; invoiceId = resultInvoice.id;
for (let ticket of tickets) {
const ticketInvoice = await models.Ticket.findById(ticket.id, {
fields: ['refFk']
}, myOptions);
await models.TicketLog.create({
originFk: ticket.id,
userFk: userId,
action: 'insert',
changedModel: 'Ticket',
changedModelId: ticket.id,
newInstance: ticketInvoice
}, myOptions);
}
if (serial != 'R' && invoiceId) if (serial != 'R' && invoiceId)
await Self.rawSql('CALL invoiceOutBooking(?)', [invoiceId], myOptions); await Self.rawSql('CALL invoiceOutBooking(?)', [invoiceId], myOptions);
invoiceOut = await models.InvoiceOut.findById(invoiceId, {
include: {
relation: 'client'
}
}, myOptions);
if (tx) await tx.commit(); if (tx) await tx.commit();
} catch (e) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();
@ -116,6 +106,15 @@ module.exports = function(Self) {
if (serial != 'R' && invoiceId) if (serial != 'R' && invoiceId)
await models.InvoiceOut.createPdf(ctx, invoiceId); await models.InvoiceOut.createPdf(ctx, invoiceId);
if (invoiceId) {
ctx.args = {
reference: invoiceOut.ref,
recipientId: invoiceOut.clientFk,
recipient: invoiceOut.client().email
};
await models.InvoiceOut.invoiceEmail(ctx, invoiceOut.ref);
}
return {invoiceFk: invoiceId, serial: serial}; return {invoiceFk: invoiceId, serial: serial};
}; };
}; };

View File

@ -100,7 +100,7 @@ module.exports = Self => {
} }
if (!args.shipped && args.landed) { if (!args.shipped && args.landed) {
const shippedResult = await models.Agency.getShipped(args.landed, const shippedResult = await models.Agency.getShipped(ctx, args.landed,
address.id, args.agencyModeId, args.warehouseId, myOptions); address.id, args.agencyModeId, args.warehouseId, myOptions);
args.shipped = (shippedResult && shippedResult.shipped) || args.landed; args.shipped = (shippedResult && shippedResult.shipped) || args.landed;
} }

View File

@ -81,6 +81,7 @@ module.exports = Self => {
const isDeliveryBoss = await models.VnUser.hasRole(userId, 'deliveryBoss', myOptions); const isDeliveryBoss = await models.VnUser.hasRole(userId, 'deliveryBoss', myOptions);
if (!isDeliveryBoss) { if (!isDeliveryBoss) {
const zoneShipped = await models.Agency.getShipped( const zoneShipped = await models.Agency.getShipped(
ctx,
args.landed, args.landed,
args.addressId, args.addressId,
args.agencyModeId, args.agencyModeId,

View File

@ -6,6 +6,9 @@ describe('ticket makeInvoice()', () => {
const ticketId = 11; const ticketId = 11;
const clientId = 1102; const clientId = 1102;
const activeCtx = { const activeCtx = {
getLocale: () => {
return 'en';
},
accessToken: {userId: userId}, accessToken: {userId: userId},
headers: {origin: 'http://localhost:5000'}, headers: {origin: 'http://localhost:5000'},
}; };
@ -67,6 +70,7 @@ describe('ticket makeInvoice()', () => {
it('should invoice a ticket, then try again to fail', async() => { it('should invoice a ticket, then try again to fail', async() => {
const invoiceOutModel = models.InvoiceOut; const invoiceOutModel = models.InvoiceOut;
spyOn(invoiceOutModel, 'createPdf'); spyOn(invoiceOutModel, 'createPdf');
spyOn(invoiceOutModel, 'invoiceEmail');
const tx = await models.Ticket.beginTransaction({}); const tx = await models.Ticket.beginTransaction({});
@ -90,6 +94,7 @@ describe('ticket makeInvoice()', () => {
it('should success to invoice a ticket', async() => { it('should success to invoice a ticket', async() => {
const invoiceOutModel = models.InvoiceOut; const invoiceOutModel = models.InvoiceOut;
spyOn(invoiceOutModel, 'createPdf'); spyOn(invoiceOutModel, 'createPdf');
spyOn(invoiceOutModel, 'invoiceEmail');
const tx = await models.Ticket.beginTransaction({}); const tx = await models.Ticket.beginTransaction({});

View File

@ -1,4 +1,5 @@
<vn-crud-model auto-load="false" <vn-crud-model
auto-load="true"
vn-id="model" vn-id="model"
url="sales" url="sales"
filter="::$ctrl.filter" filter="::$ctrl.filter"
@ -23,7 +24,7 @@
<vn-thead> <vn-thead>
<vn-tr> <vn-tr>
<vn-th field="itemFk" number>Item</vn-th> <vn-th field="itemFk" number>Item</vn-th>
<vn-th field="concept" default-order="ASC">Description</vn-th> <vn-th field="concept">Description</vn-th>
<vn-th field="itemPackingTypeFk" number>Packing type</vn-th> <vn-th field="itemPackingTypeFk" number>Packing type</vn-th>
<vn-th field="quantity" number>Quantity</vn-th> <vn-th field="quantity" number>Quantity</vn-th>
<vn-th number>m³ per quantity</vn-th> <vn-th number>m³ per quantity</vn-th>

View File

@ -5,9 +5,10 @@ class Controller extends Section {
constructor($element, $) { constructor($element, $) {
super($element, $); super($element, $);
this.filter = { this.filter = {
include: [{ include: {
relation: 'item' relation: 'item'
}] },
order: 'concept'
}; };
this.ticketVolumes = []; this.ticketVolumes = [];

View File

@ -1,4 +1,4 @@
/* eslint max-len: ["error", { "code": 130 }]*/ /* eslint max-len: ["error", { "code": 150 }]*/
const UserError = require('vn-loopback/util/user-error'); const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
@ -139,7 +139,7 @@ module.exports = Self => {
if (!client) { if (!client) {
const nickname = args.firstName.concat(' ', args.lastNames); const nickname = args.firstName.concat(' ', args.lastNames);
const workerConfig = await models.WorkerConfig.findOne({fields: ['roleFk']}); const workerConfig = await models.WorkerConfig.findOne({fields: ['roleFk', 'businessTypeFk']});
const [randomPassword] = await models.Worker.rawSql( const [randomPassword] = await models.Worker.rawSql(
'SELECT account.passwordGenerate() as password;' 'SELECT account.passwordGenerate() as password;'
); );
@ -196,7 +196,7 @@ module.exports = Self => {
client = await models.Client.findById( client = await models.Client.findById(
user.id, user.id,
{fields: ['id', 'name', 'socialName', 'street', 'city', 'iban', 'bankEntityFk', 'defaultAddressFk', 'fi']}, {fields: ['id', 'name', 'socialName', 'street', 'city', 'iban', 'bankEntityFk', 'defaultAddressFk', 'businessTypeFk', 'fi']},
myOptions myOptions
); );
@ -205,6 +205,7 @@ module.exports = Self => {
iban: args.iban, iban: args.iban,
bankEntityFk: args.bankEntityFk, bankEntityFk: args.bankEntityFk,
defaultAddressFk: address.id, defaultAddressFk: address.id,
businessTypeFk: workerConfig.businessTypeFk,
}, },
myOptions myOptions
); );

View File

@ -14,6 +14,9 @@
}, },
"roleFk": { "roleFk": {
"type": "number" "type": "number"
},
"businessTypeFk": {
"type": "string"
} }
}, },
"acls": [ "acls": [

View File

@ -1,7 +1,7 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
module.exports = Self => { module.exports = Self => {
Self.remoteMethod('getShipped', { Self.remoteMethodCtx('getShipped', {
description: 'Returns the first shipped possible for params', description: 'Returns the first shipped possible for params',
accessType: 'READ', accessType: 'READ',
accepts: [{ accepts: [{
@ -34,18 +34,22 @@ module.exports = Self => {
} }
}); });
Self.getShipped = async(landed, addressFk, agencyModeFk, warehouseFk, options) => { Self.getShipped = async(ctx, landed, addressFk, agencyModeFk, warehouseFk, options) => {
const myOptions = {}; const myOptions = {};
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
const stmts = []; const stmts = [];
const userId = ctx.req.accessToken.userId;
const models = Self.app.models;
const isProductionAssistant = await models.VnUser.hasRole(userId, 'productionAssi', myOptions);
stmts.push(new ParameterizedSQL( stmts.push(new ParameterizedSQL(
`CALL vn.zone_getShipped(?, ?, ?, TRUE)`, [ `CALL vn.zone_getShipped(?, ?, ?, ?)`, [
landed, landed,
addressFk, addressFk,
agencyModeFk agencyModeFk,
isProductionAssistant
] ]
)); ));

View File

@ -1,6 +1,9 @@
const {models} = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('agency getShipped()', () => { describe('agency getShipped()', () => {
const employeeId = 1;
const ctx = {req: {accessToken: {userId: employeeId}}};
it('should return a shipment date', async() => { it('should return a shipment date', async() => {
const landed = Date.vnNew(); const landed = Date.vnNew();
landed.setDate(landed.getDate() + 1); landed.setDate(landed.getDate() + 1);
@ -12,8 +15,7 @@ describe('agency getShipped()', () => {
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
const result = await models.Agency.getShipped(ctx, landed, addressFk, agencyModeFk, warehouseFk, options);
const result = await models.Agency.getShipped(landed, addressFk, agencyModeFk, warehouseFk, options);
expect(result).toBeDefined(); expect(result).toBeDefined();
@ -37,7 +39,7 @@ describe('agency getShipped()', () => {
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
const result = await models.Agency.getShipped(landed, addressFk, agencyModeFk, warehouseFk, options); const result = await models.Agency.getShipped(ctx, landed, addressFk, agencyModeFk, warehouseFk, options);
expect(result).toBeUndefined(); expect(result).toBeUndefined();

4
package-lock.json generated
View File

@ -1,7 +1,7 @@
{ {
"name": "salix-back", "name": "salix-back",
"version": "23.16.01", "version": "23.22.01",
"lockfileVersion": 2, "lockfileVersion": 1,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {

View File

@ -1,6 +1,6 @@
{ {
"name": "salix-back", "name": "salix-back",
"version": "23.16.01", "version": "23.22.01",
"author": "Verdnatura Levante SL", "author": "Verdnatura Levante SL",
"description": "Salix backend", "description": "Salix backend",
"license": "GPL-3.0", "license": "GPL-3.0",

View File

@ -4,13 +4,14 @@ const db = require('../../database');
module.exports = { module.exports = {
name: 'report-footer', name: 'report-footer',
async serverPrefetch() { async serverPrefetch() {
this.company = await db.findOne( this.company = await db.findOne(`
`SELECT SELECT IFNULL(ci.footnotes, cl.footnotes) as footnotes
ci.footnotes FROM company c
FROM companyI18n ci LEFT JOIN companyL10n cl ON c.id = cl.id
JOIN company c ON c.id = ci.companyFk LEFT JOIN companyI18n ci ON ci.companyFk = cl.id
WHERE c.code = ? AND ci.lang = (SELECT lang FROM account.user WHERE id = ?)`, AND ci.lang = (SELECT lang FROM account.user where id = ?)
[this.companyCode, this.recipientId]); WHERE c.code = ?`,
[this.recipientId, this.companyCode]);
}, },
props: ['leftText', 'companyCode', 'recipientId', 'centerText'] props: ['leftText', 'companyCode', 'recipientId', 'centerText']

View File

@ -1,6 +1,7 @@
subject: Weekly time log subject: Weekly time log
title: Record of hours week {0} year {1} title: Record of hours week {0} year {1}
dear: Dear worker dear: Dear worker
description: Access the following link:<br/><br/> Acceda al siguiente enlace: Access the following link:<br/>
{0} <br/><br/> description:
Click 'SATISFIED' if you agree with the hours worked. Otherwise, press 'NOT SATISFIED', detailing the cause of the disagreement. Click 'SATISFIED' if you agree with the hours worked. Otherwise, press 'NOT SATISFIED', detailing the cause of the disagreement.
Hours: Hours

View File

@ -1,6 +1,7 @@
subject: Registro de horas semanal subject: Registro de horas semanal
title: Registro de horas semana {0} año {1} title: Registro de horas semana {0} año {1}
dear: Estimado trabajador dear: Estimado trabajador
description: Acceda al siguiente enlace:<br/><br/> toaccess: Acceda al siguiente enlace:<br/>
{0} <br/><br/> description:
Pulse 'CONFORME' si esta de acuerdo con las horas trabajadas. En caso contrario pulse 'NO CONFORME', detallando la causa de la disconformidad. Pulse 'CONFORME' si esta de acuerdo con las horas trabajadas. En caso contrario pulse 'NO CONFORME', detallando la causa de la disconformidad.
Hours: Horas

View File

@ -3,7 +3,9 @@
<div class="grid-block vn-pa-ml"> <div class="grid-block vn-pa-ml">
<h1>{{ $t('title', [week, year]) }}</h1> <h1>{{ $t('title', [week, year]) }}</h1>
<p>{{$t('dear')}},</p> <p>{{$t('dear')}},</p>
<p v-html="$t('description', [url])"></p> <p v-html="$t('toaccess')"></p>
<a :href="url"><button>{{$t('Hours')}}</button></a>
<p>{{$t('description')}}</p>
</div> </div>
</div> </div>
</email-body> </email-body>