5000-invoiceOut.global-invoicing #1322
|
@ -4,5 +4,6 @@
|
||||||
"files.eol": "\n",
|
"files.eol": "\n",
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true
|
"source.fixAll.eslint": true
|
||||||
}
|
},
|
||||||
|
"search.useIgnoreFiles": false
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
ALTER TABLE vn.invoiceOutSerial
|
||||||
|
ADD `type` ENUM('global', 'quick') DEFAULT NULL NULL;
|
||||||
|
|
||||||
|
UPDATE vn.invoiceOutSerial
|
||||||
|
SET type = 'global'
|
||||||
|
WHERE code IN ('A','V');
|
|
@ -2,13 +2,15 @@ DROP FUNCTION IF EXISTS `vn`.`invoiceOut_getWeight`;
|
||||||
|
|
||||||
DELIMITER $$
|
DELIMITER $$
|
||||||
$$
|
$$
|
||||||
CREATE DEFINER=`root`@`localhost` FUNCTION `vn`.`invoiceOut_getWeight`(vInvoice VARCHAR(15)) RETURNS decimal(10,2)
|
CREATE DEFINER=`root`@`localhost` FUNCTION `vn`.`invoiceOut_getWeight`(
|
||||||
|
vInvoiceRef VARCHAR(15)
|
||||||
|
)RETURNS decimal(10,2)
|
||||||
READS SQL DATA
|
READS SQL DATA
|
||||||
BEGIN
|
BEGIN
|
||||||
/**
|
/**
|
||||||
* Calcula el peso de una factura emitida
|
* Calcula el peso de una factura emitida
|
||||||
*
|
*
|
||||||
* @param vInvoice Id de la factura
|
* @param vInvoiceRef referencia de la factura
|
||||||
* @return vTotalWeight peso de la factura
|
* @return vTotalWeight peso de la factura
|
||||||
*/
|
*/
|
||||||
DECLARE vTotalWeight DECIMAL(10,2);
|
DECLARE vTotalWeight DECIMAL(10,2);
|
||||||
|
@ -22,7 +24,7 @@ BEGIN
|
||||||
JOIN item i ON i.id = s.itemFk
|
JOIN item i ON i.id = s.itemFk
|
||||||
JOIN itemCost ic ON ic.itemFk = i.id
|
JOIN itemCost ic ON ic.itemFk = i.id
|
||||||
AND ic.warehouseFk = t.warehouseFk
|
AND ic.warehouseFk = t.warehouseFk
|
||||||
WHERE t.refFk = vInvoice
|
WHERE t.refFk = vInvoiceRef
|
||||||
AND i.intrastatFk;
|
AND i.intrastatFk;
|
||||||
|
|
||||||
RETURN vTotalWeight;
|
RETURN vTotalWeight;
|
|
@ -0,0 +1,6 @@
|
||||||
|
UPDATE `vn`.`report`
|
||||||
|
SET `method`='InvoiceOuts/{refFk}/invoice-out-pdf'
|
||||||
|
WHERE name='invoice';
|
||||||
|
|
||||||
|
ALTER TABLE `vn`.`printQueue` MODIFY COLUMN printerFk tinyint(3) unsigned DEFAULT 82 NOT NULL;
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
DROP FUNCTION IF EXISTS `vn`.`invoiceOut_getMaxIssued`;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
$$
|
||||||
|
CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `vn`.`invoiceOut_getMaxIssued`(
|
||||||
|
vSerial VARCHAR(2),
|
||||||
|
vCompanyFk INT,
|
||||||
|
vYear INT
|
||||||
|
) RETURNS DATE
|
||||||
|
READS SQL DATA
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Retorna la fecha a partir de la cual es válido emitir una factura
|
||||||
|
*
|
||||||
|
* @param vSerial Serie de facturación
|
||||||
|
* @param vCompanyFk Empresa factura emitida
|
||||||
|
* @param vYear Año contable
|
||||||
|
* @return vInvoiceOutIssued fecha factura válida
|
||||||
|
*/
|
||||||
|
DECLARE vInvoiceOutIssued DATE;
|
||||||
|
DECLARE vFirstDayOfYear DATE;
|
||||||
|
|
||||||
|
SET vFirstDayOfYear := MAKEDATE(vYear, 1);
|
||||||
|
|
||||||
|
SELECT IFNULL(MAX(io.issued), vFirstDayOfYear) INTO vInvoiceOutIssued
|
||||||
|
FROM invoiceOut io
|
||||||
|
WHERE io.serial = vSerial
|
||||||
|
AND io.companyFk = vCompanyFk
|
||||||
|
AND io.issued BETWEEN vFirstDayOfYear
|
||||||
|
AND util.lastDayOfYear(vFirstDayOfYear);
|
||||||
|
|
||||||
|
RETURN vInvoiceOutIssued;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -0,0 +1,258 @@
|
||||||
|
DROP PROCEDURE IF EXISTS `vn`.`invoiceOut_new`;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
$$
|
||||||
|
CREATE OR REPLACE 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 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.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 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 ticketToInvoice els tickets que no han de ser facturats
|
||||||
|
DELETE ti.*
|
||||||
|
FROM 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 ticketToInvoice t
|
||||||
|
JOIN sale s ON s.ticketFk = t.id;
|
||||||
|
|
||||||
|
SELECT COUNT(*) > 0 INTO vIsAnyServiceToInvoice
|
||||||
|
FROM 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 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 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;
|
||||||
|
|
||||||
|
INSERT INTO ticketLog (action, userFk, originFk, description)
|
||||||
|
SELECT 'UPDATE', account.myUser_getId(), ti.id, CONCAT('Crea factura ', vNewRef)
|
||||||
|
FROM ticketToInvoice ti;
|
||||||
|
|
||||||
|
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 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 `ticketToInvoice`;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -0,0 +1,141 @@
|
||||||
|
DROP PROCEDURE IF EXISTS `vn`.`ticketPackaging_add`;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
$$
|
||||||
|
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticketPackaging_add`(
|
||||||
|
vClientFk INT,
|
||||||
|
vDated DATE,
|
||||||
|
vCompanyFk INT,
|
||||||
|
vWithoutPeriodGrace BOOLEAN)
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Genera nuevos tickets de embalajes para los clientes no han los han retornado
|
||||||
|
* y actualiza los valores para la tabla ticketPackaging
|
||||||
|
*
|
||||||
|
* @param vClientFk Cliente en caso de NULL todos los clientes
|
||||||
|
* @param vDated Fecha hasta la cual se revisan los embalajes
|
||||||
|
* @param vCompanyFk Empresa de la cual se comprobaran sus clientes
|
||||||
|
* @param vWithoutPeriodGrace si no se aplica el periodo de gracia de un mes
|
||||||
|
*/
|
||||||
|
DECLARE vNewTicket INT;
|
||||||
|
DECLARE vDateStart DATE;
|
||||||
|
DECLARE vDateEnd DATE;
|
||||||
|
DECLARE vGraceDate DATE DEFAULT vDated;
|
||||||
|
DECLARE vWarehouseInventory INT;
|
||||||
|
DECLARE vComponentCost INT;
|
||||||
|
DECLARE vDone INT DEFAULT FALSE;
|
||||||
|
DECLARE vClientId INT;
|
||||||
|
|
||||||
|
DECLARE vCursor CURSOR FOR
|
||||||
|
SELECT DISTINCT clientFk
|
||||||
|
FROM (
|
||||||
|
SELECT clientFk, SUM(quantity) totalQuantity
|
||||||
|
FROM tmp.packagingToInvoice
|
||||||
|
GROUP BY itemFk, clientFk
|
||||||
|
HAVING totalQuantity > 0)sub;
|
||||||
|
|
||||||
|
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
|
||||||
|
|
||||||
|
DECLARE EXIT HANDLER FOR SQLEXCEPTION
|
||||||
|
BEGIN
|
||||||
|
ROLLBACK;
|
||||||
|
RESIGNAL;
|
||||||
|
END;
|
||||||
|
|
||||||
|
SELECT id INTO vWarehouseInventory
|
||||||
|
FROM warehouse
|
||||||
|
WHERE `code`= 'inv';
|
||||||
|
|
||||||
|
SELECT id INTO vComponentCost
|
||||||
|
FROM component
|
||||||
|
WHERE `code`= 'purchaseValue';
|
||||||
|
|
||||||
|
SELECT packagingInvoicingDated INTO vDateStart
|
||||||
|
FROM ticketConfig;
|
||||||
|
|
||||||
|
IF vWarehouseInventory IS NULL THEN
|
||||||
|
CALL util.throw('Warehouse inventory not set');
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF vComponentCost IS NULL THEN
|
||||||
|
CALL util.throw('Component cost not set');
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
SET vDateEnd = vDated + INTERVAL 1 DAY;
|
||||||
|
|
||||||
|
IF NOT vWithoutPeriodGrace THEN
|
||||||
|
SET vGraceDate = vGraceDate -INTERVAL 1 MONTH;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.packagingToInvoice;
|
||||||
|
CREATE TEMPORARY TABLE tmp.packagingToInvoice
|
||||||
|
(INDEX (clientFk))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT p.itemFk,
|
||||||
|
tp.packagingFk,
|
||||||
|
tp.quantity,
|
||||||
|
tp.ticketFk,
|
||||||
|
p.price,
|
||||||
|
t.clientFk
|
||||||
|
FROM ticketPackaging tp
|
||||||
|
JOIN packaging p ON p.id = tp.packagingFk
|
||||||
|
JOIN ticket t ON t.id = tp.ticketFk
|
||||||
|
JOIN client c ON c.id = t.clientFk
|
||||||
|
WHERE c.isActive
|
||||||
|
AND (vClientFk IS NULL OR t.clientFk = vClientFk)
|
||||||
|
AND t.shipped BETWEEN vDateStart AND vDateEnd
|
||||||
|
AND (tp.quantity < 0 OR (tp.quantity > 0 AND t.shipped < vGraceDate))
|
||||||
|
AND tp.quantity
|
||||||
|
AND p.itemFk;
|
||||||
|
|
||||||
|
OPEN vCursor;
|
||||||
|
l: LOOP
|
||||||
|
|
||||||
|
FETCH vCursor INTO vClientId;
|
||||||
|
|
||||||
|
IF vDone THEN
|
||||||
|
LEAVE l;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
START TRANSACTION;
|
||||||
|
|
||||||
|
CALL ticket_add(
|
||||||
|
vClientId,
|
||||||
|
vDateEnd,
|
||||||
|
vWarehouseInventory,
|
||||||
|
vCompanyFk,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
vDateEnd,
|
||||||
|
account.myUser_getId(),
|
||||||
|
TRUE,
|
||||||
|
vNewTicket);
|
||||||
|
|
||||||
|
INSERT INTO ticketPackaging(ticketFk, packagingFk, quantity, pvp)
|
||||||
|
SELECT vNewTicket, packagingFk, - SUM(quantity) totalQuantity, price
|
||||||
|
FROM tmp.packagingToInvoice
|
||||||
|
WHERE clientFk = vClientId
|
||||||
|
GROUP BY packagingFk
|
||||||
|
HAVING IF(vWithoutPeriodGrace, totalQuantity <> 0, totalQuantity < 0);
|
||||||
|
|
||||||
|
INSERT INTO sale(ticketFk, itemFk, concept, quantity, price)
|
||||||
|
SELECT vNewTicket, pti.itemFk, i.name, SUM(pti.quantity) totalQuantity, pti.price
|
||||||
|
FROM tmp.packagingToInvoice pti
|
||||||
|
JOIN item i ON i.id = pti.itemFk
|
||||||
|
WHERE pti.clientFk = vClientId
|
||||||
|
GROUP BY pti.itemFk
|
||||||
|
HAVING IF(vWithoutPeriodGrace, totalQuantity <> 0, totalQuantity > 0);
|
||||||
|
|
||||||
|
INSERT INTO saleComponent(saleFk, componentFk, value)
|
||||||
|
SELECT id, vComponentCost, price
|
||||||
|
FROM sale
|
||||||
|
WHERE ticketFk = vNewTicket;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE vCursor;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE tmp.packagingToInvoice;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -164,7 +164,7 @@ INSERT INTO `vn`.`warehouse`(`id`, `name`, `code`, `isComparative`, `isInventory
|
||||||
(3, 'Warehouse Three', NULL, 1, 1, 1, 1, 0, 0, 2, 1, 1),
|
(3, 'Warehouse Three', NULL, 1, 1, 1, 1, 0, 0, 2, 1, 1),
|
||||||
(4, 'Warehouse Four', NULL, 1, 1, 1, 1, 0, 0, 2, 1, 1),
|
(4, 'Warehouse Four', NULL, 1, 1, 1, 1, 0, 0, 2, 1, 1),
|
||||||
(5, 'Warehouse Five', NULL, 1, 1, 1, 1, 0, 0, 2, 1, 1),
|
(5, 'Warehouse Five', NULL, 1, 1, 1, 1, 0, 0, 2, 1, 1),
|
||||||
(13, 'Inventory', NULL, 1, 1, 1, 0, 0, 0, 2, 1, 0),
|
(13, 'Inventory', 'inv', 1, 1, 1, 0, 0, 0, 2, 1, 0),
|
||||||
(60, 'Algemesi', NULL, 1, 1, 1, 0, 0, 0, 2, 1, 0);
|
(60, 'Algemesi', NULL, 1, 1, 1, 0, 0, 0, 2, 1, 0);
|
||||||
|
|
||||||
|
|
||||||
|
@ -173,10 +173,11 @@ INSERT INTO `vn`.`sector`(`id`, `description`, `warehouseFk`, `isPreviousPrepare
|
||||||
(1, 'First sector', 1, 1, 'FIRST'),
|
(1, 'First sector', 1, 1, 'FIRST'),
|
||||||
(2, 'Second sector', 2, 0, 'SECOND');
|
(2, 'Second sector', 2, 0, 'SECOND');
|
||||||
|
|
||||||
INSERT INTO `vn`.`printer` (`id`, `name`, `path`, `isLabeler`, `sectorFk`)
|
INSERT INTO `vn`.`printer` (`id`, `name`, `path`, `isLabeler`, `sectorFk`, `ipAddress`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'printer1', 'path1', 0, 1),
|
(1, 'printer1', 'path1', 0, 1 , NULL),
|
||||||
(2, 'printer2', 'path2', 1, 1);
|
(2, 'printer2', 'path2', 1, 1 , NULL),
|
||||||
|
(4, 'printer4', 'path4', 0, NULL, '10.1.10.4');
|
||||||
|
|
||||||
INSERT INTO `vn`.`worker`(`id`, `code`, `firstName`, `lastName`, `userFk`,`bossFk`, `phone`, `sectorFk`, `labelerFk`)
|
INSERT INTO `vn`.`worker`(`id`, `code`, `firstName`, `lastName`, `userFk`,`bossFk`, `phone`, `sectorFk`, `labelerFk`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -571,14 +572,13 @@ INSERT INTO `vn`.`taxArea` (`code`, `claveOperacionFactura`, `CodigoTransaccion`
|
||||||
('NATIONAL', 0, 1),
|
('NATIONAL', 0, 1),
|
||||||
('WORLD', 2, 15);
|
('WORLD', 2, 15);
|
||||||
|
|
||||||
INSERT INTO `vn`.`invoiceOutSerial` (`code`, `description`, `isTaxed`, `taxAreaFk`, `isCEE`)
|
INSERT INTO `vn`.`invoiceOutSerial` (`code`, `description`, `isTaxed`, `taxAreaFk`, `isCEE`, `type`)
|
||||||
VALUES
|
VALUES
|
||||||
('A', 'Global nacional', 1, 'NATIONAL', 0),
|
('A', 'Global nacional', 1, 'NATIONAL', 0, 'global'),
|
||||||
('T', 'Española rapida', 1, 'NATIONAL', 0),
|
('T', 'Española rapida', 1, 'NATIONAL', 0, 'quick'),
|
||||||
('V', 'Intracomunitaria global', 0, 'CEE', 1),
|
('V', 'Intracomunitaria global', 0, 'CEE', 1, 'global'),
|
||||||
('M', 'Múltiple nacional', 1, 'NATIONAL', 0),
|
('M', 'Múltiple nacional', 1, 'NATIONAL', 0, 'quick'),
|
||||||
('E', 'Exportación rápida', 0, 'WORLD', 0);
|
('E', 'Exportación rápida', 0, 'WORLD', 0, 'quick');
|
||||||
;
|
|
||||||
|
|
||||||
INSERT INTO `vn`.`invoiceOut`(`id`, `serial`, `amount`, `issued`,`clientFk`, `created`, `companyFk`, `dued`, `booked`, `bankFk`, `hasPdf`)
|
INSERT INTO `vn`.`invoiceOut`(`id`, `serial`, `amount`, `issued`,`clientFk`, `created`, `companyFk`, `dued`, `booked`, `bankFk`, `hasPdf`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -2367,11 +2367,11 @@ INSERT INTO `vn`.`device` (`sn`, `model`, `userFk`)
|
||||||
VALUES
|
VALUES
|
||||||
('aaa', 'android', '9');
|
('aaa', 'android', '9');
|
||||||
|
|
||||||
INSERT INTO `vn`.`queuePriority`(`id`, `priority`)
|
INSERT INTO `vn`.`queuePriority`(`id`, `priority`, `code`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'Alta'),
|
(1, 'Alta', 'high'),
|
||||||
(2, 'Normal'),
|
(2, 'Normal', 'normal'),
|
||||||
(3, 'Baja');
|
(3, 'Baja', 'low');
|
||||||
|
|
||||||
INSERT INTO `vn`.`workerTimeControlParams` (`id`, `dayBreak`, `weekBreak`, `weekScope`, `dayWorkMax`, `dayStayMax`, `weekMaxBreak`, `weekMaxScope`, `askInOut`)
|
INSERT INTO `vn`.`workerTimeControlParams` (`id`, `dayBreak`, `weekBreak`, `weekScope`, `dayWorkMax`, `dayStayMax`, `weekMaxBreak`, `weekMaxScope`, `askInOut`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -2783,6 +2783,10 @@ INSERT INTO `salix`.`url` (`appName`, `environment`, `url`)
|
||||||
('lilium', 'dev', 'http://localhost:8080/#/'),
|
('lilium', 'dev', 'http://localhost:8080/#/'),
|
||||||
('salix', 'dev', 'http://localhost:5000/#!/');
|
('salix', 'dev', 'http://localhost:5000/#!/');
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`report` (`id`, `name`, `paperSizeFk`, `method`)
|
||||||
|
VALUES
|
||||||
|
(3, 'invoice', NULL, 'InvoiceOuts/{refFk}/invoice-out-pdf');
|
||||||
|
|
||||||
INSERT INTO `vn`.`payDemDetail` (`id`, `detail`)
|
INSERT INTO `vn`.`payDemDetail` (`id`, `detail`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 1);
|
(1, 1);
|
||||||
|
|
|
@ -19742,6 +19742,102 @@ DELIMITER ;
|
||||||
/*!50003 SET character_set_client = @saved_cs_client */ ;
|
/*!50003 SET character_set_client = @saved_cs_client */ ;
|
||||||
/*!50003 SET character_set_results = @saved_cs_results */ ;
|
/*!50003 SET character_set_results = @saved_cs_results */ ;
|
||||||
/*!50003 SET collation_connection = @saved_col_connection */ ;
|
/*!50003 SET collation_connection = @saved_col_connection */ ;
|
||||||
|
/*!50003 DROP FUNCTION IF EXISTS `CURDATE` */;
|
||||||
|
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
|
||||||
|
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
|
||||||
|
/*!50003 SET @saved_col_connection = @@collation_connection */ ;
|
||||||
|
/*!50003 SET character_set_client = utf8mb4 */ ;
|
||||||
|
/*!50003 SET character_set_results = utf8mb4 */ ;
|
||||||
|
/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
|
||||||
|
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
|
||||||
|
/*!50003 SET sql_mode = 'IGNORE_SPACE,NO_ENGINE_SUBSTITUTION' */ ;
|
||||||
|
DELIMITER ;;
|
||||||
|
CREATE DEFINER=`root`@`localhost` FUNCTION `util`.`CURDATE`() RETURNS date
|
||||||
|
DETERMINISTIC
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* @return The mock date
|
||||||
|
**/
|
||||||
|
|
||||||
|
RETURN DATE(mockTime());
|
||||||
|
END ;;
|
||||||
|
DELIMITER ;
|
||||||
|
/*!50003 SET sql_mode = @saved_sql_mode */ ;
|
||||||
|
/*!50003 SET character_set_client = @saved_cs_client */ ;
|
||||||
|
/*!50003 SET character_set_results = @saved_cs_results */ ;
|
||||||
|
/*!50003 SET collation_connection = @saved_col_connection */ ;
|
||||||
|
/*!50003 DROP FUNCTION IF EXISTS `mockTime` */;
|
||||||
|
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
|
||||||
|
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
|
||||||
|
/*!50003 SET @saved_col_connection = @@collation_connection */ ;
|
||||||
|
/*!50003 SET character_set_client = utf8mb4 */ ;
|
||||||
|
/*!50003 SET character_set_results = utf8mb4 */ ;
|
||||||
|
/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
|
||||||
|
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
|
||||||
|
/*!50003 SET sql_mode = 'IGNORE_SPACE,NO_ENGINE_SUBSTITUTION' */ ;
|
||||||
|
DELIMITER ;;
|
||||||
|
CREATE DEFINER=`root`@`localhost` FUNCTION `util`.`mockTime`() RETURNS datetime
|
||||||
|
DETERMINISTIC
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Returns the mockTime with predefined timezone or current dateTime
|
||||||
|
* depending of config.mockEnabled
|
||||||
|
*
|
||||||
|
* @return formatted datetime
|
||||||
|
*/
|
||||||
|
DECLARE vMockEnabled BOOL;
|
||||||
|
|
||||||
|
SELECT mockEnabled INTO vMockEnabled FROM config LIMIT 1;
|
||||||
|
|
||||||
|
IF vMockEnabled THEN
|
||||||
|
RETURN mockTimeBase(FALSE);
|
||||||
|
ELSE
|
||||||
|
RETURN NOW();
|
||||||
|
END IF;
|
||||||
|
END ;;
|
||||||
|
DELIMITER ;
|
||||||
|
/*!50003 SET sql_mode = @saved_sql_mode */ ;
|
||||||
|
/*!50003 SET character_set_client = @saved_cs_client */ ;
|
||||||
|
/*!50003 SET character_set_results = @saved_cs_results */ ;
|
||||||
|
/*!50003 SET collation_connection = @saved_col_connection */ ;
|
||||||
|
/*!50003 DROP FUNCTION IF EXISTS `mockTimeBase` */;
|
||||||
|
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
|
||||||
|
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
|
||||||
|
/*!50003 SET @saved_col_connection = @@collation_connection */ ;
|
||||||
|
/*!50003 SET character_set_client = utf8mb4 */ ;
|
||||||
|
/*!50003 SET character_set_results = utf8mb4 */ ;
|
||||||
|
/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
|
||||||
|
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
|
||||||
|
/*!50003 SET sql_mode = 'IGNORE_SPACE,NO_ENGINE_SUBSTITUTION' */ ;
|
||||||
|
DELIMITER ;;
|
||||||
|
CREATE DEFINER=`root`@`localhost` FUNCTION `util`.`mockTimeBase`(vIsUtc BOOL) RETURNS datetime
|
||||||
|
DETERMINISTIC
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Returns the date formatted to utc if vIsUtc or config.mocTz if not
|
||||||
|
*
|
||||||
|
* @param vIsUtc If date must be returned as UTC format
|
||||||
|
* @return The formatted mock time
|
||||||
|
*/
|
||||||
|
DECLARE vMockUtcTime DATETIME;
|
||||||
|
DECLARE vMockTz VARCHAR(255);
|
||||||
|
|
||||||
|
SELECT mockUtcTime, mockTz
|
||||||
|
INTO vMockUtcTime, vMockTz
|
||||||
|
FROM config
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
IF vIsUtc OR vMockTz IS NULL THEN
|
||||||
|
RETURN vMockUtcTime;
|
||||||
|
ELSE
|
||||||
|
RETURN CONVERT_TZ(vMockUtcTime, '+00:00', vMockTz);
|
||||||
|
END IF;
|
||||||
|
END ;;
|
||||||
|
DELIMITER ;
|
||||||
|
/*!50003 SET sql_mode = @saved_sql_mode */ ;
|
||||||
|
/*!50003 SET character_set_client = @saved_cs_client */ ;
|
||||||
|
/*!50003 SET character_set_results = @saved_cs_results */ ;
|
||||||
|
/*!50003 SET collation_connection = @saved_col_connection */ ;
|
||||||
/*!50003 DROP FUNCTION IF EXISTS `firstDayOfYear` */;
|
/*!50003 DROP FUNCTION IF EXISTS `firstDayOfYear` */;
|
||||||
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
|
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
|
||||||
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
|
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
|
||||||
|
@ -68286,11 +68382,11 @@ BEGIN
|
||||||
FROM ticketConfig;
|
FROM ticketConfig;
|
||||||
|
|
||||||
IF vWarehouseInventory IS NULL THEN
|
IF vWarehouseInventory IS NULL THEN
|
||||||
CALL util.throw('Warehouse inventory not seted');
|
CALL util.throw('Warehouse inventory not set');
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF vComponentCost IS NULL THEN
|
IF vComponentCost IS NULL THEN
|
||||||
CALL util.throw('Component cost not seted');
|
CALL util.throw('Component cost not set');
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
SET vDateEnd = vDated + INTERVAL 1 DAY;
|
SET vDateEnd = vDated + INTERVAL 1 DAY;
|
||||||
|
|
|
@ -1052,20 +1052,22 @@ export default {
|
||||||
invoiceOutIndex: {
|
invoiceOutIndex: {
|
||||||
topbarSearch: 'vn-searchbar',
|
topbarSearch: 'vn-searchbar',
|
||||||
searchResult: 'vn-invoice-out-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
|
searchResult: 'vn-invoice-out-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
|
||||||
createInvoice: 'vn-invoice-out-index > div > vn-vertical > vn-button > button vn-icon[icon="add"]',
|
createInvoice: 'vn-invoice-out-index > div > vn-button > button vn-icon[icon="add"]',
|
||||||
createManualInvoice: 'vn-item[name="manualInvoice"]',
|
|
||||||
createGlobalInvoice: 'vn-item[name="globalInvoice"]',
|
|
||||||
manualInvoiceForm: '.vn-invoice-out-manual',
|
manualInvoiceForm: '.vn-invoice-out-manual',
|
||||||
manualInvoiceTicket: 'vn-autocomplete[ng-model="$ctrl.invoice.ticketFk"]',
|
manualInvoiceTicket: 'vn-autocomplete[ng-model="$ctrl.invoice.ticketFk"]',
|
||||||
manualInvoiceClient: 'vn-autocomplete[ng-model="$ctrl.invoice.clientFk"]',
|
manualInvoiceClient: 'vn-autocomplete[ng-model="$ctrl.invoice.clientFk"]',
|
||||||
manualInvoiceSerial: 'vn-autocomplete[ng-model="$ctrl.invoice.serial"]',
|
manualInvoiceSerial: 'vn-autocomplete[ng-model="$ctrl.invoice.serial"]',
|
||||||
manualInvoiceTaxArea: 'vn-autocomplete[ng-model="$ctrl.invoice.taxArea"]',
|
manualInvoiceTaxArea: 'vn-autocomplete[ng-model="$ctrl.invoice.taxArea"]',
|
||||||
saveInvoice: 'button[response="accept"]',
|
saveInvoice: 'button[response="accept"]'
|
||||||
globalInvoiceForm: '.vn-invoice-out-global-invoicing',
|
},
|
||||||
globalInvoiceClientsRange: 'vn-radio[val="clientsRange"]',
|
invoiceOutGlobalInvoicing: {
|
||||||
globalInvoiceDate: '[ng-model="$ctrl.invoice.invoiceDate"]',
|
oneClient: 'vn-invoice-out-global-invoicing vn-side-menu form > vn-vertical > vn-vertical > vn-radio[val="one"]',
|
||||||
globalInvoiceFromClient: '[ng-model="$ctrl.invoice.fromClientId"]',
|
allClients: 'vn-invoice-out-global-invoicing vn-side-menu form > vn-vertical > vn-vertical > vn-radio[val="all"]',
|
||||||
globalInvoiceToClient: '[ng-model="$ctrl.invoice.toClientId"]',
|
clientId: 'vn-invoice-out-global-invoicing vn-side-menu form > vn-vertical > vn-autocomplete[ng-model="$ctrl.clientId"]',
|
||||||
|
printer: 'vn-invoice-out-global-invoicing vn-side-menu form > vn-vertical > vn-autocomplete[ng-model="$ctrl.printerFk"]',
|
||||||
|
makeInvoice: 'vn-invoice-out-global-invoicing vn-side-menu form > vn-vertical > vn-submit',
|
||||||
|
invoiceDate: 'vn-invoice-out-global-invoicing vn-side-menu form > vn-vertical > vn-date-picker[ng-model="$ctrl.invoiceDate"]',
|
||||||
|
maxShipped: 'vn-invoice-out-global-invoicing vn-side-menu form > vn-vertical > vn-date-picker[ng-model="$ctrl.maxShipped"]'
|
||||||
},
|
},
|
||||||
invoiceOutDescriptor: {
|
invoiceOutDescriptor: {
|
||||||
moreMenu: 'vn-invoice-out-descriptor vn-icon-button[icon=more_vert]',
|
moreMenu: 'vn-invoice-out-descriptor vn-icon-button[icon=more_vert]',
|
||||||
|
|
|
@ -17,7 +17,6 @@ describe('InvoiceOut manual invoice path', () => {
|
||||||
|
|
||||||
it('should open the manual invoice form', async() => {
|
it('should open the manual invoice form', async() => {
|
||||||
await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
|
await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
|
||||||
await page.waitToClick(selectors.invoiceOutIndex.createManualInvoice);
|
|
||||||
await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm);
|
await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -45,7 +44,6 @@ describe('InvoiceOut manual invoice path', () => {
|
||||||
|
|
||||||
it('should now open the manual invoice form', async() => {
|
it('should now open the manual invoice form', async() => {
|
||||||
await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
|
await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
|
||||||
await page.waitToClick(selectors.invoiceOutIndex.createManualInvoice);
|
|
||||||
await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm);
|
await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -17,47 +17,27 @@ describe('InvoiceOut global invoice path', () => {
|
||||||
await browser.close();
|
await browser.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
let invoicesBefore;
|
let invoicesBeforeOneClient;
|
||||||
|
let invoicesBeforeAllClients;
|
||||||
|
let now = Date.vnNew();
|
||||||
|
|
||||||
it('should count the amount of invoices listed before globla invoces are made', async() => {
|
it('should count the amount of invoices listed before globla invoces are made', async() => {
|
||||||
invoicesBefore = await page.countElement(selectors.invoiceOutIndex.searchResult);
|
invoicesBeforeOneClient = await page.countElement(selectors.invoiceOutIndex.searchResult);
|
||||||
|
|
||||||
expect(invoicesBefore).toBeGreaterThanOrEqual(4);
|
expect(invoicesBeforeOneClient).toBeGreaterThanOrEqual(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should open the global invoice form', async() => {
|
it('should open the global invoice form', async() => {
|
||||||
await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
|
await page.accessToSection('invoiceOut.global-invoicing');
|
||||||
await page.waitToClick(selectors.invoiceOutIndex.createGlobalInvoice);
|
|
||||||
await page.waitForSelector(selectors.invoiceOutIndex.globalInvoiceForm);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a global invoice for charles xavier today', async() => {
|
it('should create a global invoice for charles xavier today', async() => {
|
||||||
await page.pickDate(selectors.invoiceOutIndex.globalInvoiceDate);
|
await page.waitToClick(selectors.invoiceOutGlobalInvoicing.oneClient);
|
||||||
await page.waitToClick(selectors.invoiceOutIndex.globalInvoiceClientsRange);
|
await page.autocompleteSearch(selectors.invoiceOutGlobalInvoicing.clientId, '1108');
|
||||||
await page.autocompleteSearch(selectors.invoiceOutIndex.globalInvoiceFromClient, 'Petter Parker');
|
await page.pickDate(selectors.invoiceOutGlobalInvoicing.invoiceDate, now);
|
||||||
await page.autocompleteSearch(selectors.invoiceOutIndex.globalInvoiceToClient, 'Petter Parker');
|
await page.pickDate(selectors.invoiceOutGlobalInvoicing.maxShipped, now);
|
||||||
await page.waitToClick(selectors.invoiceOutIndex.saveInvoice);
|
await page.autocompleteSearch(selectors.invoiceOutGlobalInvoicing.printer, '1');
|
||||||
const message = await page.waitForSnackbar();
|
await page.waitToClick(selectors.invoiceOutGlobalInvoicing.makeInvoice);
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
expect(message.text).toContain('Data saved!');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should count the amount of invoices listed after globla invocing', async() => {
|
|
||||||
await page.waitToClick('[icon="search"]');
|
|
||||||
await page.waitForTimeout(1000); // index search needs time to return results
|
|
||||||
const currentInvoices = await page.countElement(selectors.invoiceOutIndex.searchResult);
|
|
||||||
|
|
||||||
expect(currentInvoices).toBeGreaterThan(invoicesBefore);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create a global invoice for all clients today', async() => {
|
|
||||||
await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
|
|
||||||
await page.waitToClick(selectors.invoiceOutIndex.createGlobalInvoice);
|
|
||||||
await page.waitForSelector(selectors.invoiceOutIndex.globalInvoiceForm);
|
|
||||||
await page.pickDate(selectors.invoiceOutIndex.globalInvoiceDate);
|
|
||||||
await page.waitToClick(selectors.invoiceOutIndex.saveInvoice);
|
|
||||||
const message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
expect(message.text).toContain('Data saved!');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,8 +25,7 @@
|
||||||
},
|
},
|
||||||
"node_modules/@uirouter/angularjs": {
|
"node_modules/@uirouter/angularjs": {
|
||||||
"version": "1.0.30",
|
"version": "1.0.30",
|
||||||
"resolved": "https://registry.npmjs.org/@uirouter/angularjs/-/angularjs-1.0.30.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-qkc3RFZc91S5K0gc/QVAXc9LGDPXjR04vDgG/11j8+yyZEuQojXxKxdLhKIepiPzqLmGRVqzBmBc27gtqaEeZg==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@uirouter/core": "6.0.8"
|
"@uirouter/core": "6.0.8"
|
||||||
},
|
},
|
||||||
|
@ -39,28 +38,22 @@
|
||||||
},
|
},
|
||||||
"node_modules/@uirouter/core": {
|
"node_modules/@uirouter/core": {
|
||||||
"version": "6.0.8",
|
"version": "6.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@uirouter/core/-/core-6.0.8.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-Gc/BAW47i4L54p8dqYCJJZuv2s3tqlXQ0fvl6Zp2xrblELPVfxmjnc0eurx3XwfQdaqm3T6uls6tQKkof/4QMw==",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4.0.0"
|
"node": ">=4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/angular": {
|
"node_modules/angular": {
|
||||||
"version": "1.8.3",
|
"version": "1.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/angular/-/angular-1.8.3.tgz",
|
"license": "MIT"
|
||||||
"integrity": "sha512-5qjkWIQQVsHj4Sb5TcEs4WZWpFeVFHXwxEBHUhrny41D8UrBAd6T/6nPPAsLngJCReIOqi95W3mxdveveutpZw==",
|
|
||||||
"deprecated": "For the actively supported Angular, see https://www.npmjs.com/package/@angular/core. AngularJS support has officially ended. For extended AngularJS support options, see https://goo.gle/angularjs-path-forward."
|
|
||||||
},
|
},
|
||||||
"node_modules/angular-animate": {
|
"node_modules/angular-animate": {
|
||||||
"version": "1.8.2",
|
"version": "1.8.2",
|
||||||
"resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.8.2.tgz",
|
"license": "MIT"
|
||||||
"integrity": "sha512-Jbr9+grNMs9Kj57xuBU3Ju3NOPAjS1+g2UAwwDv7su1lt0/PLDy+9zEwDiu8C8xJceoTbmBNKiWGPJGBdCQLlA==",
|
|
||||||
"deprecated": "For the actively supported Angular, see https://www.npmjs.com/package/@angular/core. AngularJS support has officially ended. For extended AngularJS support options, see https://goo.gle/angularjs-path-forward."
|
|
||||||
},
|
},
|
||||||
"node_modules/angular-moment": {
|
"node_modules/angular-moment": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/angular-moment/-/angular-moment-1.3.0.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-KG8rvO9MoaBLwtGnxTeUveSyNtrL+RNgGl1zqWN36+HDCCVGk2DGWOzqKWB6o+eTTbO3Opn4hupWKIElc8XETA==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"moment": ">=2.8.0 <3.0.0"
|
"moment": ">=2.8.0 <3.0.0"
|
||||||
},
|
},
|
||||||
|
@ -70,8 +63,7 @@
|
||||||
},
|
},
|
||||||
"node_modules/angular-translate": {
|
"node_modules/angular-translate": {
|
||||||
"version": "2.19.0",
|
"version": "2.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.19.0.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-Z/Fip5uUT2N85dPQ0sMEe1JdF5AehcDe4tg/9mWXNDVU531emHCg53ZND9Oe0dyNiGX5rWcJKmsL1Fujus1vGQ==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"angular": "^1.8.0"
|
"angular": "^1.8.0"
|
||||||
},
|
},
|
||||||
|
@ -81,29 +73,25 @@
|
||||||
},
|
},
|
||||||
"node_modules/angular-translate-loader-partial": {
|
"node_modules/angular-translate-loader-partial": {
|
||||||
"version": "2.19.0",
|
"version": "2.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/angular-translate-loader-partial/-/angular-translate-loader-partial-2.19.0.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-NnMw13LMV4bPQmJK7/pZOZAnPxe0M5OtUHchADs5Gye7V7feonuEnrZ8e1CKhBlv9a7IQyWoqcBa4Lnhg8gk5w==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"angular-translate": "~2.19.0"
|
"angular-translate": "~2.19.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/argparse": {
|
"node_modules/argparse": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"sprintf-js": "~1.0.2"
|
"sprintf-js": "~1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/croppie": {
|
"node_modules/croppie": {
|
||||||
"version": "2.6.5",
|
"version": "2.6.5",
|
||||||
"resolved": "https://registry.npmjs.org/croppie/-/croppie-2.6.5.tgz",
|
"license": "MIT"
|
||||||
"integrity": "sha512-IlChnVUGG5T3w2gRZIaQgBtlvyuYnlUWs2YZIXXR3H9KrlO1PtBT3j+ykxvy9eZIWhk+V5SpBmhCQz5UXKrEKQ=="
|
|
||||||
},
|
},
|
||||||
"node_modules/esprima": {
|
"node_modules/esprima": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
"license": "BSD-2-Clause",
|
||||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"esparse": "bin/esparse.js",
|
"esparse": "bin/esparse.js",
|
||||||
"esvalidate": "bin/esvalidate.js"
|
"esvalidate": "bin/esvalidate.js"
|
||||||
|
@ -114,8 +102,7 @@
|
||||||
},
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "3.14.1",
|
"version": "3.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"argparse": "^1.0.7",
|
"argparse": "^1.0.7",
|
||||||
"esprima": "^4.0.0"
|
"esprima": "^4.0.0"
|
||||||
|
@ -126,42 +113,36 @@
|
||||||
},
|
},
|
||||||
"node_modules/mg-crud": {
|
"node_modules/mg-crud": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/mg-crud/-/mg-crud-1.1.2.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-mAR6t0aQHKnT0QHKHpLOi0kNPZfO36iMpIoiLjFHxuio6mIJyuveBJ4VNlNXJRxLh32/FLADEb41/sYo7QUKFw==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"angular": "^1.6.1"
|
"angular": "^1.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/moment": {
|
"node_modules/moment": {
|
||||||
"version": "2.29.4",
|
"version": "2.29.4",
|
||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/oclazyload": {
|
"node_modules/oclazyload": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/oclazyload/-/oclazyload-0.6.3.tgz",
|
"license": "MIT"
|
||||||
"integrity": "sha512-HpOSYUgjtt6sTB/C6+FWsExR+9HCnXKsUA96RWkDXfv11C8Cc9X2DlR0WIZwFIiG6FQU0pwB5dhoYyut8bFAOQ=="
|
|
||||||
},
|
},
|
||||||
"node_modules/require-yaml": {
|
"node_modules/require-yaml": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/require-yaml/-/require-yaml-0.0.1.tgz",
|
"license": "BSD",
|
||||||
"integrity": "sha512-M6eVEgLPRbeOhgSCnOTtdrOOEQzbXRchg24Xa13c39dMuraFKdI9emUo97Rih0YEFzSICmSKg8w4RQp+rd9pOQ==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"js-yaml": ""
|
"js-yaml": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/require-yaml/node_modules/argparse": {
|
"node_modules/require-yaml/node_modules/argparse": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
"license": "Python-2.0"
|
||||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
|
||||||
},
|
},
|
||||||
"node_modules/require-yaml/node_modules/js-yaml": {
|
"node_modules/require-yaml/node_modules/js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"argparse": "^2.0.1"
|
"argparse": "^2.0.1"
|
||||||
},
|
},
|
||||||
|
@ -171,13 +152,11 @@
|
||||||
},
|
},
|
||||||
"node_modules/sprintf-js": {
|
"node_modules/sprintf-js": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
"license": "BSD-3-Clause"
|
||||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
|
|
||||||
},
|
},
|
||||||
"node_modules/validator": {
|
"node_modules/validator": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/validator/-/validator-6.3.0.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-BylxTwhqwjQI5MDJF7amCy/L0ejJO+74DvCsLV52Lq3+3bhVcVMKqNqOiNcQJm2G48u9EAcw4xFERAmFbwXM9Q==",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
|
@ -186,73 +165,51 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@uirouter/angularjs": {
|
"@uirouter/angularjs": {
|
||||||
"version": "1.0.30",
|
"version": "1.0.30",
|
||||||
"resolved": "https://registry.npmjs.org/@uirouter/angularjs/-/angularjs-1.0.30.tgz",
|
|
||||||
"integrity": "sha512-qkc3RFZc91S5K0gc/QVAXc9LGDPXjR04vDgG/11j8+yyZEuQojXxKxdLhKIepiPzqLmGRVqzBmBc27gtqaEeZg==",
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@uirouter/core": "6.0.8"
|
"@uirouter/core": "6.0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@uirouter/core": {
|
"@uirouter/core": {
|
||||||
"version": "6.0.8",
|
"version": "6.0.8"
|
||||||
"resolved": "https://registry.npmjs.org/@uirouter/core/-/core-6.0.8.tgz",
|
|
||||||
"integrity": "sha512-Gc/BAW47i4L54p8dqYCJJZuv2s3tqlXQ0fvl6Zp2xrblELPVfxmjnc0eurx3XwfQdaqm3T6uls6tQKkof/4QMw=="
|
|
||||||
},
|
},
|
||||||
"angular": {
|
"angular": {
|
||||||
"version": "1.8.3",
|
"version": "1.8.3"
|
||||||
"resolved": "https://registry.npmjs.org/angular/-/angular-1.8.3.tgz",
|
|
||||||
"integrity": "sha512-5qjkWIQQVsHj4Sb5TcEs4WZWpFeVFHXwxEBHUhrny41D8UrBAd6T/6nPPAsLngJCReIOqi95W3mxdveveutpZw=="
|
|
||||||
},
|
},
|
||||||
"angular-animate": {
|
"angular-animate": {
|
||||||
"version": "1.8.2",
|
"version": "1.8.2"
|
||||||
"resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.8.2.tgz",
|
|
||||||
"integrity": "sha512-Jbr9+grNMs9Kj57xuBU3Ju3NOPAjS1+g2UAwwDv7su1lt0/PLDy+9zEwDiu8C8xJceoTbmBNKiWGPJGBdCQLlA=="
|
|
||||||
},
|
},
|
||||||
"angular-moment": {
|
"angular-moment": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/angular-moment/-/angular-moment-1.3.0.tgz",
|
|
||||||
"integrity": "sha512-KG8rvO9MoaBLwtGnxTeUveSyNtrL+RNgGl1zqWN36+HDCCVGk2DGWOzqKWB6o+eTTbO3Opn4hupWKIElc8XETA==",
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"moment": ">=2.8.0 <3.0.0"
|
"moment": ">=2.8.0 <3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"angular-translate": {
|
"angular-translate": {
|
||||||
"version": "2.19.0",
|
"version": "2.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.19.0.tgz",
|
|
||||||
"integrity": "sha512-Z/Fip5uUT2N85dPQ0sMEe1JdF5AehcDe4tg/9mWXNDVU531emHCg53ZND9Oe0dyNiGX5rWcJKmsL1Fujus1vGQ==",
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"angular": "^1.8.0"
|
"angular": "^1.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"angular-translate-loader-partial": {
|
"angular-translate-loader-partial": {
|
||||||
"version": "2.19.0",
|
"version": "2.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/angular-translate-loader-partial/-/angular-translate-loader-partial-2.19.0.tgz",
|
|
||||||
"integrity": "sha512-NnMw13LMV4bPQmJK7/pZOZAnPxe0M5OtUHchADs5Gye7V7feonuEnrZ8e1CKhBlv9a7IQyWoqcBa4Lnhg8gk5w==",
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"angular-translate": "~2.19.0"
|
"angular-translate": "~2.19.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"argparse": {
|
"argparse": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
|
||||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"sprintf-js": "~1.0.2"
|
"sprintf-js": "~1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"croppie": {
|
"croppie": {
|
||||||
"version": "2.6.5",
|
"version": "2.6.5"
|
||||||
"resolved": "https://registry.npmjs.org/croppie/-/croppie-2.6.5.tgz",
|
|
||||||
"integrity": "sha512-IlChnVUGG5T3w2gRZIaQgBtlvyuYnlUWs2YZIXXR3H9KrlO1PtBT3j+ykxvy9eZIWhk+V5SpBmhCQz5UXKrEKQ=="
|
|
||||||
},
|
},
|
||||||
"esprima": {
|
"esprima": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1"
|
||||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
|
||||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
|
|
||||||
},
|
},
|
||||||
"js-yaml": {
|
"js-yaml": {
|
||||||
"version": "3.14.1",
|
"version": "3.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
|
||||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"argparse": "^1.0.7",
|
"argparse": "^1.0.7",
|
||||||
"esprima": "^4.0.0"
|
"esprima": "^4.0.0"
|
||||||
|
@ -260,39 +217,27 @@
|
||||||
},
|
},
|
||||||
"mg-crud": {
|
"mg-crud": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/mg-crud/-/mg-crud-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-mAR6t0aQHKnT0QHKHpLOi0kNPZfO36iMpIoiLjFHxuio6mIJyuveBJ4VNlNXJRxLh32/FLADEb41/sYo7QUKFw==",
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"angular": "^1.6.1"
|
"angular": "^1.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"moment": {
|
"moment": {
|
||||||
"version": "2.29.4",
|
"version": "2.29.4"
|
||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
|
||||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
|
|
||||||
},
|
},
|
||||||
"oclazyload": {
|
"oclazyload": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3"
|
||||||
"resolved": "https://registry.npmjs.org/oclazyload/-/oclazyload-0.6.3.tgz",
|
|
||||||
"integrity": "sha512-HpOSYUgjtt6sTB/C6+FWsExR+9HCnXKsUA96RWkDXfv11C8Cc9X2DlR0WIZwFIiG6FQU0pwB5dhoYyut8bFAOQ=="
|
|
||||||
},
|
},
|
||||||
"require-yaml": {
|
"require-yaml": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/require-yaml/-/require-yaml-0.0.1.tgz",
|
|
||||||
"integrity": "sha512-M6eVEgLPRbeOhgSCnOTtdrOOEQzbXRchg24Xa13c39dMuraFKdI9emUo97Rih0YEFzSICmSKg8w4RQp+rd9pOQ==",
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"js-yaml": ""
|
"js-yaml": ""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"argparse": {
|
"argparse": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1"
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
|
||||||
},
|
},
|
||||||
"js-yaml": {
|
"js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"argparse": "^2.0.1"
|
"argparse": "^2.0.1"
|
||||||
}
|
}
|
||||||
|
@ -300,14 +245,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sprintf-js": {
|
"sprintf-js": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3"
|
||||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
|
||||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
|
|
||||||
},
|
},
|
||||||
"validator": {
|
"validator": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.0"
|
||||||
"resolved": "https://registry.npmjs.org/validator/-/validator-6.3.0.tgz",
|
|
||||||
"integrity": "sha512-BylxTwhqwjQI5MDJF7amCy/L0ejJO+74DvCsLV52Lq3+3bhVcVMKqNqOiNcQJm2G48u9EAcw4xFERAmFbwXM9Q=="
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,5 +152,7 @@
|
||||||
"It is not possible to modify sales that their articles are from Floramondo": "It is not possible to modify sales that their articles are from Floramondo",
|
"It is not possible to modify sales that their articles are from Floramondo": "It is not possible to modify sales that their articles are from Floramondo",
|
||||||
"It is not possible to modify cloned sales": "It is not possible to modify cloned sales",
|
"It is not possible to modify cloned sales": "It is not possible to modify cloned sales",
|
||||||
"Valid priorities: 1,2,3": "Valid priorities: 1,2,3",
|
"Valid priorities: 1,2,3": "Valid priorities: 1,2,3",
|
||||||
|
"Warehouse inventory not set": "Almacén inventario no está establecido",
|
||||||
|
|||||||
|
"Component cost not set": "Componente coste no está estabecido",
|
||||||
"Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2"
|
"Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2"
|
||||||
}
|
}
|
|
@ -251,6 +251,7 @@
|
||||||
"Receipt's bank was not found": "No se encontró el banco del recibo",
|
"Receipt's bank was not found": "No se encontró el banco del recibo",
|
||||||
"This receipt was not compensated": "Este recibo no ha sido compensado",
|
"This receipt was not compensated": "Este recibo no ha sido compensado",
|
||||||
"Client's email was not found": "No se encontró el email del cliente",
|
"Client's email was not found": "No se encontró el email del cliente",
|
||||||
|
"Negative basis": "Base negativa",
|
||||||
"This worker code already exists": "Este codigo de trabajador ya existe",
|
"This worker code already exists": "Este codigo de trabajador ya existe",
|
||||||
"This personal mail already exists": "Este correo personal ya existe",
|
"This personal mail already exists": "Este correo personal ya existe",
|
||||||
"This worker already exists": "Este trabajador ya existe",
|
"This worker already exists": "Este trabajador ya existe",
|
||||||
|
@ -264,6 +265,9 @@
|
||||||
"It is not possible to modify cloned sales": "No es posible modificar líneas de pedido clonadas",
|
"It is not possible to modify cloned sales": "No es posible modificar líneas de pedido clonadas",
|
||||||
"A supplier with the same name already exists. Change the country.": "Un proveedor con el mismo nombre ya existe. Cambie el país.",
|
"A supplier with the same name already exists. Change the country.": "Un proveedor con el mismo nombre ya existe. Cambie el país.",
|
||||||
"There is no assigned email for this client": "No hay correo asignado para este cliente",
|
"There is no assigned email for this client": "No hay correo asignado para este cliente",
|
||||||
|
"Exists an invoice with a previous date": "Existe una factura con fecha anterior",
|
||||||
|
"Invoice date can't be less than max date": "La fecha de factura no puede ser inferior a la fecha límite",
|
||||||
|
"Warehouse inventory not set": "El almacén inventario no está establecido",
|
||||||
"This locker has already been assigned": "Esta taquilla ya ha sido asignada",
|
"This locker has already been assigned": "Esta taquilla ya ha sido asignada",
|
||||||
"Tickets with associated refunds": "No se pueden borrar tickets con abonos asociados. Este ticket está asociado al abono Nº {{id}}",
|
"Tickets with associated refunds": "No se pueden borrar tickets con abonos asociados. Este ticket está asociado al abono Nº {{id}}",
|
||||||
"Not exist this branch": "La rama no existe"
|
"Not exist this branch": "La rama no existe"
|
||||||
|
|
|
@ -4,47 +4,37 @@ module.exports = Self => {
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: [
|
accepts: [
|
||||||
{
|
{
|
||||||
|
arg: 'clientId',
|
||||||
|
type: 'number',
|
||||||
|
description: 'The client id'
|
||||||
|
}, {
|
||||||
arg: 'invoiceDate',
|
arg: 'invoiceDate',
|
||||||
type: 'date',
|
type: 'date',
|
||||||
description: 'The invoice date'
|
description: 'The invoice date',
|
||||||
},
|
required: true
|
||||||
{
|
}, {
|
||||||
arg: 'maxShipped',
|
arg: 'maxShipped',
|
||||||
type: 'date',
|
type: 'date',
|
||||||
description: 'The maximum shipped date'
|
description: 'The maximum shipped date',
|
||||||
},
|
required: true
|
||||||
{
|
}, {
|
||||||
arg: 'fromClientId',
|
|
||||||
type: 'number',
|
|
||||||
description: 'The minimum client id'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'toClientId',
|
|
||||||
type: 'number',
|
|
||||||
description: 'The maximum client id'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'companyFk',
|
arg: 'companyFk',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
description: 'The company id to invoice'
|
description: 'The company id to invoice',
|
||||||
}
|
required: true
|
||||||
],
|
},
|
||||||
returns: [{
|
],
|
||||||
arg: 'clientsAndAddresses',
|
returns: {
|
||||||
type: ['object']
|
type: 'Object',
|
||||||
|
root: true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
arg: 'invoice',
|
|
||||||
type: 'object'
|
|
||||||
}],
|
|
||||||
http: {
|
http: {
|
||||||
path: '/clientsToInvoice',
|
path: '/clientsToInvoice',
|
||||||
verb: 'POST'
|
verb: 'POST'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.clientsToInvoice = async(ctx, options) => {
|
Self.clientsToInvoice = async(ctx, clientId, invoiceDate, maxShipped, companyFk, options) => {
|
||||||
const args = ctx.args;
|
|
||||||
let tx;
|
let tx;
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
|
|
||||||
|
@ -56,134 +46,52 @@ module.exports = Self => {
|
||||||
myOptions.transaction = tx;
|
myOptions.transaction = tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
let query;
|
|
||||||
try {
|
try {
|
||||||
query = `
|
|
||||||
SELECT MAX(issued) issued
|
|
||||||
FROM vn.invoiceOut io
|
|
||||||
JOIN vn.time t ON t.dated = io.issued
|
|
||||||
WHERE io.serial = 'A'
|
|
||||||
AND t.year = YEAR(?)
|
|
||||||
AND io.companyFk = ?`;
|
|
||||||
const [maxIssued] = await Self.rawSql(query, [
|
|
||||||
args.invoiceDate,
|
|
||||||
args.companyFk
|
|
||||||
], myOptions);
|
|
||||||
|
|
||||||
const maxSerialDate = maxIssued.issued || args.invoiceDate;
|
|
||||||
if (args.invoiceDate < maxSerialDate)
|
|
||||||
args.invoiceDate = maxSerialDate;
|
|
||||||
|
|
||||||
if (args.invoiceDate < args.maxShipped)
|
|
||||||
args.maxShipped = args.invoiceDate;
|
|
||||||
|
|
||||||
const minShipped = Date.vnNew();
|
|
||||||
minShipped.setFullYear(minShipped.getFullYear() - 1);
|
|
||||||
minShipped.setMonth(1);
|
|
||||||
minShipped.setDate(1);
|
|
||||||
minShipped.setHours(0, 0, 0, 0);
|
|
||||||
|
|
||||||
// Packaging liquidation
|
// Packaging liquidation
|
||||||
const vIsAllInvoiceable = false;
|
const vIsAllInvoiceable = false;
|
||||||
const clientsWithPackaging = await getClientsWithPackaging(ctx, myOptions);
|
await Self.rawSql('CALL ticketPackaging_add(?, ?, ?, ?)', [
|
||||||
for (let client of clientsWithPackaging) {
|
clientId,
|
||||||
await Self.rawSql('CALL packageInvoicing(?, ?, ?, ?, @newTicket)', [
|
invoiceDate,
|
||||||
client.id,
|
companyFk,
|
||||||
args.invoiceDate,
|
|
||||||
args.companyFk,
|
|
||||||
vIsAllInvoiceable
|
vIsAllInvoiceable
|
||||||
], myOptions);
|
], myOptions);
|
||||||
}
|
|
||||||
|
|
||||||
const invoiceableClients = await getInvoiceableClients(ctx, myOptions);
|
const minShipped = Date.vnNew();
|
||||||
|
minShipped.setFullYear(maxShipped.getFullYear() - 1);
|
||||||
|
|
||||||
if (!invoiceableClients) return;
|
const query = `
|
||||||
|
SELECT c.id clientId,
|
||||||
|
c.name clientName,
|
||||||
|
a.id,
|
||||||
|
a.nickname
|
||||||
|
FROM ticket t
|
||||||
|
JOIN address a ON a.id = t.addressFk
|
||||||
|
JOIN client c ON c.id = t.clientFk
|
||||||
|
WHERE t.refFk IS NULL
|
||||||
|
AND t.shipped BETWEEN ? AND util.dayEnd(?)
|
||||||
|
AND (t.clientFk = ? OR ? IS NULL )
|
||||||
|
AND t.companyFk = ?
|
||||||
|
AND c.hasToInvoice
|
||||||
|
AND c.isTaxDataChecked
|
||||||
|
AND c.isActive
|
||||||
|
AND NOT t.isDeleted
|
||||||
|
GROUP BY c.id, IF(c.hasToInvoiceByAddress, a.id, TRUE)
|
||||||
|
HAVING SUM(t.totalWithVat) > 0;`;
|
||||||
|
|
||||||
const clientsAndAddresses = invoiceableClients.map(invoiceableClient => {
|
const addresses = await Self.rawSql(query, [
|
||||||
return {
|
minShipped,
|
||||||
clientId: invoiceableClient.id,
|
maxShipped,
|
||||||
addressId: invoiceableClient.addressFk
|
clientId,
|
||||||
|
clientId,
|
||||||
};
|
companyFk
|
||||||
}
|
], myOptions);
|
||||||
);
|
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
return [
|
return addresses;
|
||||||
clientsAndAddresses,
|
|
||||||
{
|
|
||||||
invoiceDate: args.invoiceDate,
|
|
||||||
maxShipped: args.maxShipped,
|
|
||||||
fromClientId: args.fromClientId,
|
|
||||||
toClientId: args.toClientId,
|
|
||||||
companyFk: args.companyFk,
|
|
||||||
minShipped: minShipped
|
|
||||||
}
|
|
||||||
];
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (tx) await tx.rollback();
|
if (tx) await tx.rollback();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
async function getClientsWithPackaging(ctx, options) {
|
|
||||||
const models = Self.app.models;
|
|
||||||
const args = ctx.args;
|
|
||||||
const query = `SELECT DISTINCT clientFk AS id
|
|
||||||
FROM ticket t
|
|
||||||
JOIN ticketPackaging tp ON t.id = tp.ticketFk
|
|
||||||
JOIN client c ON c.id = t.clientFk
|
|
||||||
WHERE t.shipped BETWEEN '2017-11-21' AND ?
|
|
||||||
AND t.clientFk >= ?
|
|
||||||
AND (t.clientFk <= ? OR ? IS NULL)
|
|
||||||
AND c.isActive`;
|
|
||||||
return models.InvoiceOut.rawSql(query, [
|
|
||||||
args.maxShipped,
|
|
||||||
args.fromClientId,
|
|
||||||
args.toClientId,
|
|
||||||
args.toClientId
|
|
||||||
], options);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getInvoiceableClients(ctx, options) {
|
|
||||||
const models = Self.app.models;
|
|
||||||
const args = ctx.args;
|
|
||||||
const minShipped = Date.vnNew();
|
|
||||||
minShipped.setFullYear(minShipped.getFullYear() - 1);
|
|
||||||
|
|
||||||
const query = `SELECT
|
|
||||||
c.id,
|
|
||||||
SUM(IFNULL
|
|
||||||
(
|
|
||||||
s.quantity *
|
|
||||||
s.price * (100-s.discount)/100,
|
|
||||||
0)
|
|
||||||
+ IFNULL(ts.quantity * ts.price,0)
|
|
||||||
) AS sumAmount,
|
|
||||||
c.hasToInvoiceByAddress,
|
|
||||||
c.email,
|
|
||||||
c.isToBeMailed,
|
|
||||||
a.id addressFk
|
|
||||||
FROM ticket t
|
|
||||||
LEFT JOIN sale s ON s.ticketFk = t.id
|
|
||||||
LEFT JOIN ticketService ts ON ts.ticketFk = t.id
|
|
||||||
JOIN address a ON a.id = t.addressFk
|
|
||||||
JOIN client c ON c.id = t.clientFk
|
|
||||||
WHERE ISNULL(t.refFk) AND c.id >= ?
|
|
||||||
AND (t.clientFk <= ? OR ? IS NULL)
|
|
||||||
AND t.shipped BETWEEN ? AND util.dayEnd(?)
|
|
||||||
AND t.companyFk = ? AND c.hasToInvoice
|
|
||||||
AND c.isTaxDataChecked AND c.isActive
|
|
||||||
GROUP BY c.id, IF(c.hasToInvoiceByAddress,a.id,TRUE) HAVING sumAmount > 0`;
|
|
||||||
|
|
||||||
return models.InvoiceOut.rawSql(query, [
|
|
||||||
args.fromClientId,
|
|
||||||
args.toClientId,
|
|
||||||
args.toClientId,
|
|
||||||
minShipped,
|
|
||||||
args.maxShipped,
|
|
||||||
args.companyFk
|
|
||||||
], options);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -44,7 +44,7 @@ module.exports = Self => {
|
||||||
try {
|
try {
|
||||||
const invoiceOut = await Self.findById(id, null, myOptions);
|
const invoiceOut = await Self.findById(id, null, myOptions);
|
||||||
const hasInvoicing = await models.Account.hasRole(userId, 'invoicing', myOptions);
|
const hasInvoicing = await models.Account.hasRole(userId, 'invoicing', myOptions);
|
||||||
console.log(invoiceOut, !hasInvoicing);
|
|
||||||
if (invoiceOut.hasPdf && !hasInvoicing)
|
if (invoiceOut.hasPdf && !hasInvoicing)
|
||||||
throw new UserError(`You don't have enough privileges`);
|
throw new UserError(`You don't have enough privileges`);
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,6 @@ module.exports = Self => {
|
||||||
ids = ids.split(',');
|
ids = ids.split(',');
|
||||||
|
|
||||||
for (let id of ids) {
|
for (let id of ids) {
|
||||||
console.log(zipConfig, totalSize, zipConfig ? zipConfig.maxSize : null);
|
|
||||||
if (zipConfig && totalSize > zipConfig.maxSize) throw new UserError('Files are too large');
|
if (zipConfig && totalSize > zipConfig.maxSize) throw new UserError('Files are too large');
|
||||||
const invoiceOutPdf = await models.InvoiceOut.download(ctx, id, myOptions);
|
const invoiceOutPdf = await models.InvoiceOut.download(ctx, id, myOptions);
|
||||||
const fileName = extractFileName(invoiceOutPdf[2]);
|
const fileName = extractFileName(invoiceOutPdf[2]);
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('getInvoiceDate', {
|
||||||
|
description: 'Returns default Invoice Date',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'companyFk',
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/getInvoiceDate`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.getInvoiceDate = async companyFk => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const [invoiceDate] = await models.InvoiceOut.rawSql(
|
||||||
|
`SELECT MAX(io.issued) issued
|
||||||
|
FROM invoiceOut io
|
||||||
|
JOIN invoiceOutSerial ios ON ios.code = io.serial
|
||||||
|
WHERE ios.type = 'global'
|
||||||
|
AND io.issued
|
||||||
|
AND io.companyFk = ?`,
|
||||||
|
[companyFk]
|
||||||
|
);
|
||||||
|
return invoiceDate;
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,43 +1,42 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('invoiceClient', {
|
Self.remoteMethodCtx('invoiceClient', {
|
||||||
description: 'Make a invoice of a client',
|
description: 'Make a invoice of a client',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: [{
|
accepts: [
|
||||||
|
{
|
||||||
arg: 'clientId',
|
arg: 'clientId',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
description: 'The client id to invoice',
|
description: 'The client id to invoice',
|
||||||
required: true
|
required: true
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
arg: 'addressId',
|
arg: 'addressId',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
description: 'The address id to invoice',
|
description: 'The address id to invoice',
|
||||||
required: true
|
required: true
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
arg: 'invoiceDate',
|
arg: 'invoiceDate',
|
||||||
type: 'date',
|
type: 'date',
|
||||||
description: 'The invoice date',
|
description: 'The invoice date',
|
||||||
required: true
|
required: true
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
arg: 'maxShipped',
|
arg: 'maxShipped',
|
||||||
type: 'date',
|
type: 'date',
|
||||||
description: 'The maximum shipped date',
|
description: 'The maximum shipped date',
|
||||||
required: true
|
required: true
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
arg: 'companyFk',
|
arg: 'companyFk',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
description: 'The company id to invoice',
|
description: 'The company id to invoice',
|
||||||
required: true
|
required: true
|
||||||
},
|
}, {
|
||||||
{
|
arg: 'printerFk',
|
||||||
arg: 'minShipped',
|
type: 'number',
|
||||||
type: 'date',
|
description: 'The printer to print',
|
||||||
description: 'The minium shupped date',
|
|
||||||
required: true
|
required: true
|
||||||
}],
|
}
|
||||||
|
],
|
||||||
returns: {
|
returns: {
|
||||||
carlosap marked this conversation as resolved
Outdated
carlosap
commented
shipped shipped
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
root: true
|
root: true
|
||||||
|
@ -62,16 +61,19 @@ module.exports = Self => {
|
||||||
myOptions.transaction = tx;
|
myOptions.transaction = tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const minShipped = Date.vnNew();
|
||||||
|
minShipped.setFullYear(args.maxShipped.getFullYear() - 1);
|
||||||
|
|
||||||
let invoiceId;
|
let invoiceId;
|
||||||
let invoiceOut;
|
let invoiceOut;
|
||||||
try {
|
try {
|
||||||
const client = await models.Client.findById(args.clientId, {
|
const client = await models.Client.findById(args.clientId, {
|
||||||
fields: ['id', 'hasToInvoiceByAddress']
|
fields: ['id', 'hasToInvoiceByAddress']
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
try {
|
|
||||||
if (client.hasToInvoiceByAddress) {
|
if (client.hasToInvoiceByAddress) {
|
||||||
await Self.rawSql('CALL ticketToInvoiceByAddress(?, ?, ?, ?)', [
|
await Self.rawSql('CALL ticketToInvoiceByAddress(?, ?, ?, ?)', [
|
||||||
args.minShipped,
|
minShipped,
|
||||||
args.maxShipped,
|
args.maxShipped,
|
||||||
args.addressId,
|
args.addressId,
|
||||||
args.companyFk
|
args.companyFk
|
||||||
|
@ -90,7 +92,7 @@ module.exports = Self => {
|
||||||
// Validates ticket nagative base
|
// Validates ticket nagative base
|
||||||
const hasAnyNegativeBase = await getNegativeBase(myOptions);
|
const hasAnyNegativeBase = await getNegativeBase(myOptions);
|
||||||
if (hasAnyNegativeBase && isSpanishCompany)
|
if (hasAnyNegativeBase && isSpanishCompany)
|
||||||
return tx.rollback();
|
throw new UserError('Negative basis');
|
||||||
|
|
||||||
query = `SELECT invoiceSerial(?, ?, ?) AS serial`;
|
query = `SELECT invoiceSerial(?, ?, ?) AS serial`;
|
||||||
const [invoiceSerial] = await Self.rawSql(query, [
|
const [invoiceSerial] = await Self.rawSql(query, [
|
||||||
|
@ -110,37 +112,41 @@ module.exports = Self => {
|
||||||
if (newInvoice.id) {
|
if (newInvoice.id) {
|
||||||
await Self.rawSql('CALL invoiceOutBooking(?)', [newInvoice.id], myOptions);
|
await Self.rawSql('CALL invoiceOutBooking(?)', [newInvoice.id], myOptions);
|
||||||
|
|
||||||
invoiceId = newInvoice.id;
|
invoiceOut = await models.InvoiceOut.findById(newInvoice.id, {
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
const failedClient = {
|
|
||||||
id: client.id,
|
|
||||||
stacktrace: e
|
|
||||||
};
|
|
||||||
await notifyFailures(ctx, failedClient, myOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
invoiceOut = await models.InvoiceOut.findById(invoiceId, {
|
|
||||||
include: {
|
include: {
|
||||||
relation: 'client'
|
relation: 'client'
|
||||||
}
|
}
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
|
invoiceId = newInvoice.id;
|
||||||
|
}
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (tx) await tx.rollback();
|
if (tx) await tx.rollback();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (invoiceId) {
|
||||||
|
if (!invoiceOut.client().isToBeMailed) {
|
||||||
|
const query = `
|
||||||
|
CALL vn.report_print(
|
||||||
|
'invoice',
|
||||||
|
?,
|
||||||
|
account.myUser_getId(),
|
||||||
|
JSON_OBJECT('refFk', ?),
|
||||||
|
'normal'
|
||||||
|
);`;
|
||||||
|
await models.InvoiceOut.rawSql(query, [args.printerFk, invoiceOut.ref]);
|
||||||
|
} else {
|
||||||
ctx.args = {
|
ctx.args = {
|
||||||
reference: invoiceOut.ref,
|
reference: invoiceOut.ref,
|
||||||
recipientId: invoiceOut.clientFk,
|
recipientId: invoiceOut.clientFk,
|
||||||
recipient: invoiceOut.client().email
|
recipient: invoiceOut.client().email
|
||||||
};
|
};
|
||||||
try {
|
|
||||||
await models.InvoiceOut.invoiceEmail(ctx, invoiceOut.ref);
|
await models.InvoiceOut.invoiceEmail(ctx, invoiceOut.ref);
|
||||||
} catch (err) {}
|
}
|
||||||
|
}
|
||||||
return invoiceId;
|
return invoiceId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -148,13 +154,12 @@ module.exports = Self => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const query = 'SELECT hasAnyNegativeBase() AS base';
|
const query = 'SELECT hasAnyNegativeBase() AS base';
|
||||||
const [result] = await models.InvoiceOut.rawSql(query, null, options);
|
const [result] = await models.InvoiceOut.rawSql(query, null, options);
|
||||||
|
|
||||||
return result && result.base;
|
return result && result.base;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getIsSpanishCompany(companyId, options) {
|
async function getIsSpanishCompany(companyId, options) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const query = `SELECT COUNT(*) AS total
|
const query = `SELECT COUNT(*) isSpanishCompany
|
||||||
FROM supplier s
|
FROM supplier s
|
||||||
JOIN country c ON c.id = s.countryFk
|
JOIN country c ON c.id = s.countryFk
|
||||||
AND c.code = 'ES'
|
AND c.code = 'ES'
|
||||||
|
@ -163,28 +168,6 @@ module.exports = Self => {
|
||||||
companyId
|
companyId
|
||||||
], options);
|
], options);
|
||||||
|
|
||||||
return supplierCompany && supplierCompany.total;
|
return supplierCompany && supplierCompany.isSpanishCompany;
|
||||||
}
|
|
||||||
|
|
||||||
async function notifyFailures(ctx, failedClient, options) {
|
|
||||||
const models = Self.app.models;
|
|
||||||
const userId = ctx.req.accessToken.userId;
|
|
||||||
const $t = ctx.req.__; // $translate
|
|
||||||
|
|
||||||
const worker = await models.EmailUser.findById(userId, null, options);
|
|
||||||
const subject = $t('Global invoicing failed');
|
|
||||||
let body = $t(`Wasn't able to invoice the following clients`) + ':<br/><br/>';
|
|
||||||
|
|
||||||
body += `ID: <strong>${failedClient.id}</strong>
|
|
||||||
<br/> <strong>${failedClient.stacktrace}</strong><br/><br/>`;
|
|
||||||
|
|
||||||
await Self.rawSql(`
|
|
||||||
INSERT INTO vn.mail (sender, replyTo, sent, subject, body)
|
|
||||||
VALUES (?, ?, FALSE, ?, ?)`, [
|
|
||||||
worker.email,
|
|
||||||
worker.email,
|
|
||||||
subject,
|
|
||||||
body
|
|
||||||
], options);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
const {Report} = require('vn-print');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('invoiceOutPdf', {
|
||||||
|
description: 'Returns the invoice pdf',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'reference',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
description: 'The invoice reference',
|
||||||
|
http: {source: 'path'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: [
|
||||||
|
{
|
||||||
|
arg: 'body',
|
||||||
|
type: 'file',
|
||||||
|
root: true
|
||||||
|
}, {
|
||||||
|
arg: 'Content-Type',
|
||||||
|
type: 'String',
|
||||||
|
http: {target: 'header'}
|
||||||
|
}, {
|
||||||
|
arg: 'Content-Disposition',
|
||||||
|
type: 'String',
|
||||||
|
http: {target: 'header'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: '/:reference/invoice-out-pdf',
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.invoiceOutPdf = async(ctx, reference) => {
|
||||||
|
const args = Object.assign({}, ctx.args);
|
||||||
|
const params = {lang: ctx.req.getLocale()};
|
||||||
|
|
||||||
|
delete args.ctx;
|
||||||
|
for (const param in args)
|
||||||
|
params[param] = args[param];
|
||||||
|
|
||||||
|
const report = new Report('invoice', params);
|
||||||
|
const stream = await report.toPdfStream();
|
||||||
|
|
||||||
|
return [stream, 'application/pdf', `filename="doc-${reference}.pdf"`];
|
||||||
|
};
|
||||||
|
};
|
|
@ -65,7 +65,6 @@ describe('InvoiceOut filter()', () => {
|
||||||
await invoiceOut.updateAttribute('hasPdf', true, options);
|
await invoiceOut.updateAttribute('hasPdf', true, options);
|
||||||
|
|
||||||
const result = await models.InvoiceOut.filter(ctx, {id: invoiceOut.id}, options);
|
const result = await models.InvoiceOut.filter(ctx, {id: invoiceOut.id}, options);
|
||||||
console.log(result);
|
|
||||||
|
|
||||||
expect(result.length).toEqual(1);
|
expect(result.length).toEqual(1);
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
"InvoiceContainer": {
|
"InvoiceContainer": {
|
||||||
"dataSource": "invoiceStorage"
|
"dataSource": "invoiceStorage"
|
||||||
},
|
},
|
||||||
|
"Printer": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"TaxArea": {
|
"TaxArea": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,4 +15,6 @@ module.exports = Self => {
|
||||||
require('../methods/invoiceOut/exportationPdf')(Self);
|
require('../methods/invoiceOut/exportationPdf')(Self);
|
||||||
require('../methods/invoiceOut/invoiceCsv')(Self);
|
require('../methods/invoiceOut/invoiceCsv')(Self);
|
||||||
require('../methods/invoiceOut/invoiceCsvEmail')(Self);
|
require('../methods/invoiceOut/invoiceCsvEmail')(Self);
|
||||||
|
require('../methods/invoiceOut/invoiceOutPdf')(Self);
|
||||||
|
require('../methods/invoiceOut/getInvoiceDate')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"name": "Printer",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "printer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true,
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"isLabeler": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"acls": [{
|
||||||
|
"accessType": "READ",
|
||||||
|
"principalType": "ROLE",
|
||||||
|
"principalId": "$everyone",
|
||||||
|
"permission": "ALLOW"
|
||||||
|
}]
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
<vn-card
|
||||||
|
ng-if="$ctrl.status"
|
||||||
|
class="vn-w-lg vn-pa-md"
|
||||||
|
style="height: 80px; display: flex; align-items: center; justify-content: center; gap: 20px;">
|
||||||
|
<vn-spinner
|
||||||
|
enable="$ctrl.status != 'done'">
|
||||||
|
</vn-spinner>
|
||||||
|
<div>
|
||||||
|
<div ng-switch="$ctrl.status">
|
||||||
|
<span ng-switch-when="packageInvoicing" translate>
|
||||||
|
Build packaging tickets
|
||||||
|
</span>
|
||||||
|
<span ng-switch-when="invoicing">
|
||||||
|
{{'Invoicing client' | translate}} {{$ctrl.currentAddress.clientId}}
|
||||||
|
</span>
|
||||||
|
<span ng-switch-when="stopping" translate>
|
||||||
|
Stopping process
|
||||||
|
</span>
|
||||||
|
<span ng-switch-when="done" translate>
|
||||||
|
Ended process
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div ng-if="$ctrl.nAddresses" class="text-caption text-secondary">
|
||||||
|
{{$ctrl.percentage | percentage: 0}} ({{$ctrl.addressNumber}} {{'of' | translate}} {{$ctrl.nAddresses}})
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</vn-card>
|
||||||
|
<vn-card class="vn-w-lg vn-mt-md" ng-if="$ctrl.errors.length">
|
||||||
|
<vn-table>
|
||||||
|
<vn-thead>
|
||||||
|
<vn-tr>
|
||||||
|
<vn-th number>Id</vn-th>
|
||||||
|
<vn-th>Client</vn-th>
|
||||||
|
<vn-th number>Address id</vn-th>
|
||||||
|
<vn-th>Street</vn-th>
|
||||||
|
<vn-th>Error</vn-th>
|
||||||
|
</vn-tr>
|
||||||
|
</vn-thead>
|
||||||
|
<vn-tbody>
|
||||||
|
<vn-tr ng-repeat="error in $ctrl.errors">
|
||||||
|
<vn-td number>
|
||||||
|
<span
|
||||||
|
vn-click-stop="clientDescriptor.show($event, error.address.clientId)"
|
||||||
|
class="link">
|
||||||
|
{{::error.address.clientId}}
|
||||||
|
</span>
|
||||||
|
</vn-td>
|
||||||
|
<vn-td expand>
|
||||||
|
{{::error.address.clientName}}
|
||||||
|
</vn-td>
|
||||||
|
<vn-td number>
|
||||||
|
{{::error.address.id}}
|
||||||
|
</vn-td>
|
||||||
|
<vn-td expand>
|
||||||
|
{{::error.address.nickname}}
|
||||||
|
</vn-td>
|
||||||
|
<vn-td expand>
|
||||||
|
<span class="chip alert">{{::error.message}}</span>
|
||||||
|
</vn-td>
|
||||||
|
</vn-tr>
|
||||||
|
</vn-tbody>
|
||||||
|
</vn-table>
|
||||||
|
</vn-card>
|
||||||
|
<vn-side-menu side="right">
|
||||||
|
<vn-crud-model
|
||||||
|
auto-load="true"
|
||||||
|
url="InvoiceOutSerials"
|
||||||
|
data="invoiceOutSerials"
|
||||||
|
order="code">
|
||||||
|
</vn-crud-model>
|
||||||
|
<vn-crud-model
|
||||||
|
auto-load="true"
|
||||||
|
url="Companies"
|
||||||
|
data="companies"
|
||||||
|
order="code">
|
||||||
|
</vn-crud-model>
|
||||||
|
<form class="vn-pa-md">
|
||||||
|
<vn-vertical>
|
||||||
|
<vn-vertical class="vn-mb-sm">
|
||||||
|
<vn-radio
|
||||||
|
label="All clients"
|
||||||
|
val="all"
|
||||||
|
ng-model="$ctrl.clientsToInvoice">
|
||||||
|
</vn-radio>
|
||||||
|
<vn-radio
|
||||||
|
label="One client"
|
||||||
|
val="one"
|
||||||
|
ng-model="$ctrl.clientsToInvoice">
|
||||||
|
</vn-radio>
|
||||||
|
</vn-vertical>
|
||||||
|
<vn-autocomplete
|
||||||
|
url="Clients"
|
||||||
|
label="Client"
|
||||||
|
search-function="{or: [{id: $search}, {name: {like: '%'+$search+'%'}}]}"
|
||||||
|
order="id"
|
||||||
|
show-field="name"
|
||||||
|
value-field="id"
|
||||||
|
ng-model="$ctrl.clientId"
|
||||||
|
required="true"
|
||||||
|
ng-if="$ctrl.clientsToInvoice == 'one'">
|
||||||
|
<tpl-item>{{::id}} - {{::name}}</tpl-item>
|
||||||
|
</vn-autocomplete>
|
||||||
|
<vn-date-picker
|
||||||
|
vn-one
|
||||||
|
label="Invoice date"
|
||||||
|
ng-model="$ctrl.invoiceDate">
|
||||||
|
</vn-date-picker>
|
||||||
|
<vn-date-picker
|
||||||
|
vn-one
|
||||||
|
label="Max date"
|
||||||
|
ng-model="$ctrl.maxShipped">
|
||||||
|
</vn-date-picker>
|
||||||
|
<vn-autocomplete
|
||||||
|
url="Companies"
|
||||||
|
label="Company"
|
||||||
|
show-field="code"
|
||||||
|
value-field="id"
|
||||||
|
ng-model="$ctrl.companyFk">
|
||||||
|
</vn-autocomplete>
|
||||||
|
<vn-autocomplete
|
||||||
|
url="Printers"
|
||||||
|
label="Printer"
|
||||||
|
show-field="name"
|
||||||
|
value-field="id"
|
||||||
|
where="{isLabeler: false}"
|
||||||
|
ng-model="$ctrl.printerFk">
|
||||||
|
</vn-autocomplete>
|
||||||
|
<vn-submit
|
||||||
|
ng-if="!$ctrl.invoicing"
|
||||||
|
ng-click="$ctrl.makeInvoice()"
|
||||||
|
label="Invoice out">
|
||||||
|
</vn-submit>
|
||||||
|
<vn-submit
|
||||||
|
ng-if="$ctrl.invoicing"
|
||||||
|
label="Stop"
|
||||||
|
ng-click="$ctrl.stopInvoicing()">
|
||||||
|
</vn-submit>
|
||||||
|
</vn-vertical>
|
||||||
|
</form>
|
||||||
|
</vn-side-menu>
|
||||||
|
|
||||||
|
<vn-client-descriptor-popover
|
||||||
|
vn-id="clientDescriptor">
|
||||||
|
</vn-client-descriptor-popover>
|
|
@ -0,0 +1,133 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import Section from 'salix/components/section';
|
||||||
|
import UserError from 'core/lib/user-error';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
class Controller extends Section {
|
||||||
|
$onInit() {
|
||||||
|
const date = Date.vnNew();
|
||||||
|
Object.assign(this, {
|
||||||
|
maxShipped: new Date(date.getFullYear(), date.getMonth(), 0),
|
||||||
|
clientsToInvoice: 'all',
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$http.get('UserConfigs/getUserConfig')
|
||||||
|
.then(res => {
|
||||||
|
this.companyFk = res.data.companyFk;
|
||||||
|
const params = {
|
||||||
|
companyFk: this.companyFk
|
||||||
|
};
|
||||||
|
return this.$http.get('InvoiceOuts/getInvoiceDate', {params});
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
this.minInvoicingDate = res.data.issued ? new Date(res.data.issued) : null;
|
||||||
|
this.invoiceDate = this.minInvoicingDate;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
stopInvoicing() {
|
||||||
|
this.status = 'stopping';
|
||||||
|
}
|
||||||
|
|
||||||
|
makeInvoice() {
|
||||||
|
this.invoicing = true;
|
||||||
|
this.status = 'packageInvoicing';
|
||||||
|
this.errors = [];
|
||||||
|
this.addresses = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (this.clientsToInvoice == 'one' && !this.clientId)
|
||||||
|
throw new UserError('Choose a valid client');
|
||||||
|
if (!this.invoiceDate || !this.maxShipped)
|
||||||
|
throw new UserError('Invoice date and the max date should be filled');
|
||||||
|
if (this.invoiceDate < this.maxShipped)
|
||||||
|
throw new UserError('Invoice date can\'t be less than max date');
|
||||||
|
if (this.invoiceDate.getTime() < this.minInvoicingDate.getTime())
|
||||||
|
throw new UserError('Exists an invoice with a previous date');
|
||||||
|
if (!this.companyFk)
|
||||||
|
throw new UserError('Choose a valid company');
|
||||||
|
if (!this.printerFk)
|
||||||
|
throw new UserError('Choose a valid printer');
|
||||||
|
|
||||||
|
if (this.clientsToInvoice == 'all')
|
||||||
|
this.clientId = undefined;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
invoiceDate: this.invoiceDate,
|
||||||
|
maxShipped: this.maxShipped,
|
||||||
|
clientId: this.clientId,
|
||||||
|
companyFk: this.companyFk
|
||||||
|
};
|
||||||
|
this.$http.post(`InvoiceOuts/clientsToInvoice`, params)
|
||||||
|
.then(res => {
|
||||||
|
this.addresses = res.data;
|
||||||
|
if (!this.addresses.length)
|
||||||
|
throw new UserError(`There aren't tickets to invoice`);
|
||||||
|
|
||||||
|
this.addressIndex = 0;
|
||||||
|
return this.invoiceOut();
|
||||||
|
})
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
|
} catch (err) {
|
||||||
|
this.handleError(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleError(err) {
|
||||||
|
this.invoicing = false;
|
||||||
|
this.status = null;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
invoiceOut() {
|
||||||
|
if (this.addressIndex == this.addresses.length || this.status == 'stopping') {
|
||||||
|
this.invoicing = false;
|
||||||
|
this.status = 'done';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.status = 'invoicing';
|
||||||
|
const address = this.addresses[this.addressIndex];
|
||||||
|
this.currentAddress = address;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
clientId: address.clientId,
|
||||||
|
addressId: address.id,
|
||||||
|
invoiceDate: this.invoiceDate,
|
||||||
|
maxShipped: this.maxShipped,
|
||||||
|
companyFk: this.companyFk,
|
||||||
|
printerFk: this.printerFk,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$http.post(`InvoiceOuts/invoiceClient`, params)
|
||||||
|
.catch(res => {
|
||||||
|
this.errors.unshift({
|
||||||
|
address,
|
||||||
|
message: res.data.error.message
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.addressIndex++;
|
||||||
|
this.invoiceOut();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get nAddresses() {
|
||||||
|
if (!this.addresses) return 0;
|
||||||
|
return this.addresses.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
get addressNumber() {
|
||||||
|
return Math.min(this.addressIndex + 1, this.nAddresses);
|
||||||
|
}
|
||||||
|
|
||||||
|
get percentage() {
|
||||||
|
const len = this.nAddresses;
|
||||||
|
return Math.min(this.addressIndex, len) / len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnInvoiceOutGlobalInvoicing', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Controller
|
||||||
|
});
|
|
@ -0,0 +1,74 @@
|
||||||
|
import './index';
|
||||||
|
|
||||||
|
describe('InvoiceOut', () => {
|
||||||
|
describe('Component vnInvoiceOutGlobalInvoicing', () => {
|
||||||
|
let controller;
|
||||||
|
let $httpBackend;
|
||||||
|
|
||||||
|
beforeEach(ngModule('invoiceOut'));
|
||||||
|
|
||||||
|
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
|
||||||
|
$httpBackend = _$httpBackend_;
|
||||||
|
const $scope = $rootScope.$new();
|
||||||
|
const $element = angular.element('<vn-invoice-out-global-invoicing></vn-invoice-out-global-invoicing>');
|
||||||
|
|
||||||
|
controller = $componentController('vnInvoiceOutGlobalInvoicing', {$element, $scope});
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('makeInvoice()', () => {
|
||||||
|
it('should throw an error when invoiceDate or maxShipped properties are not filled in', () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showError');
|
||||||
|
controller.clientsToInvoice = 'all';
|
||||||
|
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
controller.makeInvoice();
|
||||||
|
} catch (e) {
|
||||||
|
error = e.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedError = 'Invoice date and the max date should be filled';
|
||||||
|
|
||||||
|
expect(error).toBe(expectedError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error when select one client and clientId is not filled in', () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showError');
|
||||||
|
controller.clientsToInvoice = 'one';
|
||||||
|
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
controller.makeInvoice();
|
||||||
|
} catch (e) {
|
||||||
|
error = e.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedError = 'Choose a valid client';
|
||||||
|
|
||||||
|
expect(error).toBe(expectedError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should make an http POST query and then call to the showSuccess() method', () => {
|
||||||
|
const date = Date.vnNew();
|
||||||
|
Object.assign(controller, {
|
||||||
|
invoiceDate: date,
|
||||||
|
maxShipped: date,
|
||||||
|
minInvoicingDate: date,
|
||||||
|
clientsToInvoice: 'one',
|
||||||
|
clientId: 1101,
|
||||||
|
companyFk: 442,
|
||||||
|
printerFk: 1
|
||||||
|
});
|
||||||
|
$httpBackend.expectPOST(`InvoiceOuts/clientsToInvoice`).respond([{
|
||||||
|
clientId: 1101,
|
||||||
|
id: 121
|
||||||
|
}]);
|
||||||
|
$httpBackend.expectPOST(`InvoiceOuts/invoiceClient`).respond();
|
||||||
|
controller.makeInvoice();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.status).toEqual('done');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,20 @@
|
||||||
|
There aren't tickets to invoice: No existen tickets para facturar
|
||||||
|
Max date: Fecha límite
|
||||||
|
Invoice date: Fecha de factura
|
||||||
|
Invoice date can't be less than max date: La fecha de factura no puede ser inferior a la fecha límite
|
||||||
|
Invoice date and the max date should be filled: La fecha de factura y la fecha límite deben rellenarse
|
||||||
|
Choose a valid company: Selecciona un empresa válida
|
||||||
|
Choose a valid printer: Selecciona una impresora válida
|
||||||
|
All clients: Todos los clientes
|
||||||
|
Build packaging tickets: Generando tickets de embalajes
|
||||||
|
Address id: Id dirección
|
||||||
|
Printer: Impresora
|
||||||
|
of: de
|
||||||
jgallego
commented
Esta traducció es rara, es gasta sola en algun lloc¿? Esta traducció es rara, es gasta sola en algun lloc¿?
|
|||||||
|
Client: Cliente
|
||||||
|
Current client id: Id cliente actual
|
||||||
|
Invoicing client: Facturando cliente
|
||||||
|
Ended process: Proceso finalizado
|
||||||
|
Invoice out: Facturar
|
||||||
|
One client: Un solo cliente
|
||||||
|
Choose a valid client: Selecciona un cliente válido
|
||||||
|
Stop: Parar
|
|
@ -0,0 +1,17 @@
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
|
vn-invoice-out-global-invoicing{
|
||||||
|
|
||||||
|
h5{
|
||||||
|
color: $color-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
#error {
|
||||||
|
line-break: normal;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,4 +9,4 @@ import './descriptor';
|
||||||
import './descriptor-popover';
|
import './descriptor-popover';
|
||||||
import './descriptor-menu';
|
import './descriptor-menu';
|
||||||
import './index/manual';
|
import './index/manual';
|
||||||
import './index/global-invoicing';
|
import './global-invoicing';
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
<tpl-title translate>
|
|
||||||
Create global invoice
|
|
||||||
</tpl-title>
|
|
||||||
<tpl-body id="manifold-form">
|
|
||||||
<vn-crud-model
|
|
||||||
auto-load="true"
|
|
||||||
url="InvoiceOutSerials"
|
|
||||||
data="invoiceOutSerials"
|
|
||||||
order="code">
|
|
||||||
</vn-crud-model>
|
|
||||||
<vn-crud-model
|
|
||||||
auto-load="true"
|
|
||||||
url="Companies"
|
|
||||||
data="companies"
|
|
||||||
order="code">
|
|
||||||
</vn-crud-model>
|
|
||||||
<div
|
|
||||||
class="progress vn-my-md"
|
|
||||||
ng-if="$ctrl.packageInvoicing">
|
|
||||||
<vn-horizontal>
|
|
||||||
<div>
|
|
||||||
{{'Calculating packages to invoice...' | translate}}
|
|
||||||
</div>
|
|
||||||
</vn-horizontal>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="progress vn-my-md"
|
|
||||||
ng-if="$ctrl.lastClientId">
|
|
||||||
<vn-horizontal>
|
|
||||||
<div>
|
|
||||||
{{'Id Client' | translate}}: {{$ctrl.currentClientId}}
|
|
||||||
{{'of' | translate}} {{::$ctrl.lastClientId}}
|
|
||||||
</div>
|
|
||||||
</vn-horizontal>
|
|
||||||
</div>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-date-picker
|
|
||||||
vn-one
|
|
||||||
label="Invoice date"
|
|
||||||
ng-model="$ctrl.invoice.invoiceDate">
|
|
||||||
</vn-date-picker>
|
|
||||||
<vn-date-picker
|
|
||||||
vn-one
|
|
||||||
label="Max date"
|
|
||||||
ng-model="$ctrl.invoice.maxShipped">
|
|
||||||
</vn-date-picker>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-radio
|
|
||||||
label="All clients"
|
|
||||||
val="allClients"
|
|
||||||
ng-model="$ctrl.clientsNumber"
|
|
||||||
ng-click="$ctrl.$onInit()">
|
|
||||||
</vn-radio>
|
|
||||||
<vn-radio
|
|
||||||
label="Clients range"
|
|
||||||
val="clientsRange"
|
|
||||||
ng-model="$ctrl.clientsNumber">
|
|
||||||
</vn-radio>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal ng-show="$ctrl.clientsNumber == 'clientsRange'">
|
|
||||||
<vn-autocomplete
|
|
||||||
url="Clients"
|
|
||||||
label="From client"
|
|
||||||
search-function="{or: [{id: $search}, {name: {like: '%'+$search+'%'}}]}"
|
|
||||||
order="id"
|
|
||||||
show-field="name"
|
|
||||||
value-field="id"
|
|
||||||
ng-model="$ctrl.invoice.fromClientId">
|
|
||||||
<tpl-item>{{::id}} - {{::name}}</tpl-item>
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-autocomplete
|
|
||||||
url="Clients"
|
|
||||||
label="To client"
|
|
||||||
search-function="{or: [{id: $search}, {name: {like: '%'+$search+'%'}}]}"
|
|
||||||
order="id"
|
|
||||||
show-field="name"
|
|
||||||
value-field="id"
|
|
||||||
ng-model="$ctrl.invoice.toClientId">
|
|
||||||
<tpl-item>{{::id}} - {{::name}}</tpl-item>
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-autocomplete
|
|
||||||
url="Companies"
|
|
||||||
label="Company"
|
|
||||||
show-field="code"
|
|
||||||
value-field="id"
|
|
||||||
ng-model="$ctrl.invoice.companyFk">
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-horizontal>
|
|
||||||
</tpl-body>
|
|
||||||
<tpl-buttons>
|
|
||||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
|
||||||
<button vn-id="invoiceButton" response="accept" translate>Invoice</button>{{$ctrl.isInvoicing}}
|
|
||||||
</tpl-buttons>
|
|
|
@ -1,129 +0,0 @@
|
||||||
import ngModule from '../../module';
|
|
||||||
import Dialog from 'core/components/dialog';
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
class Controller extends Dialog {
|
|
||||||
constructor($element, $, $transclude) {
|
|
||||||
super($element, $, $transclude);
|
|
||||||
this.invoice = {
|
|
||||||
maxShipped: Date.vnNew()
|
|
||||||
};
|
|
||||||
this.clientsNumber = 'allClients';
|
|
||||||
}
|
|
||||||
|
|
||||||
$onInit() {
|
|
||||||
this.getMinClientId();
|
|
||||||
this.getMaxClientId();
|
|
||||||
}
|
|
||||||
|
|
||||||
getMinClientId() {
|
|
||||||
this.getClientId('min')
|
|
||||||
.then(res => this.invoice.fromClientId = res.data.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
getMaxClientId() {
|
|
||||||
this.getClientId('max')
|
|
||||||
.then(res => this.invoice.toClientId = res.data.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
getClientId(func) {
|
|
||||||
const order = func == 'min' ? 'ASC' : 'DESC';
|
|
||||||
const params = {
|
|
||||||
filter: {
|
|
||||||
order: 'id ' + order,
|
|
||||||
limit: 1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return this.$http.get('Clients/findOne', {params});
|
|
||||||
}
|
|
||||||
|
|
||||||
get companyFk() {
|
|
||||||
return this.invoice.companyFk;
|
|
||||||
}
|
|
||||||
|
|
||||||
set companyFk(value) {
|
|
||||||
this.invoice.companyFk = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
restartValues() {
|
|
||||||
this.lastClientId = null;
|
|
||||||
this.$.invoiceButton.disabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
cancelRequest() {
|
|
||||||
this.canceler = this.$q.defer();
|
|
||||||
return {timeout: this.canceler.promise};
|
|
||||||
}
|
|
||||||
|
|
||||||
invoiceOut(invoice, clientsAndAddresses) {
|
|
||||||
const [clientAndAddress] = clientsAndAddresses;
|
|
||||||
if (!clientAndAddress) return;
|
|
||||||
this.currentClientId = clientAndAddress.clientId;
|
|
||||||
const params = {
|
|
||||||
clientId: clientAndAddress.clientId,
|
|
||||||
addressId: clientAndAddress.addressId,
|
|
||||||
invoiceDate: invoice.invoiceDate,
|
|
||||||
maxShipped: invoice.maxShipped,
|
|
||||||
companyFk: invoice.companyFk,
|
|
||||||
minShipped: invoice.minShipped,
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const options = this.cancelRequest();
|
|
||||||
|
|
||||||
return this.$http.post(`InvoiceOuts/invoiceClient`, params, options)
|
|
||||||
.then(() => {
|
|
||||||
clientsAndAddresses.shift();
|
|
||||||
return this.invoiceOut(invoice, clientsAndAddresses);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
responseHandler(response) {
|
|
||||||
try {
|
|
||||||
if (response !== 'accept')
|
|
||||||
return super.responseHandler(response);
|
|
||||||
|
|
||||||
if (!this.invoice.invoiceDate || !this.invoice.maxShipped)
|
|
||||||
throw new Error('Invoice date and the max date should be filled');
|
|
||||||
|
|
||||||
if (!this.invoice.fromClientId || !this.invoice.toClientId)
|
|
||||||
throw new Error('Choose a valid clients range');
|
|
||||||
|
|
||||||
this.on('close', () => {
|
|
||||||
if (this.canceler) this.canceler.resolve();
|
|
||||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$.invoiceButton.disabled = true;
|
|
||||||
this.packageInvoicing = true;
|
|
||||||
const options = this.cancelRequest();
|
|
||||||
|
|
||||||
this.$http.post(`InvoiceOuts/clientsToInvoice`, this.invoice, options)
|
|
||||||
.then(res => {
|
|
||||||
this.packageInvoicing = false;
|
|
||||||
const invoice = res.data.invoice;
|
|
||||||
const clientsAndAddresses = res.data.clientsAndAddresses;
|
|
||||||
if (!clientsAndAddresses.length) return super.responseHandler(response);
|
|
||||||
this.lastClientId = clientsAndAddresses[clientsAndAddresses.length - 1].clientId;
|
|
||||||
return this.invoiceOut(invoice, clientsAndAddresses);
|
|
||||||
})
|
|
||||||
.then(() => super.responseHandler(response))
|
|
||||||
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')))
|
|
||||||
.finally(() => this.restartValues());
|
|
||||||
} catch (e) {
|
|
||||||
this.vnApp.showError(this.$t(e.message));
|
|
||||||
this.restartValues();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Controller.$inject = ['$element', '$scope', '$transclude'];
|
|
||||||
|
|
||||||
ngModule.vnComponent('vnInvoiceOutGlobalInvoicing', {
|
|
||||||
slotTemplate: require('./index.html'),
|
|
||||||
controller: Controller,
|
|
||||||
bindings: {
|
|
||||||
companyFk: '<?'
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,118 +0,0 @@
|
||||||
import './index';
|
|
||||||
|
|
||||||
describe('InvoiceOut', () => {
|
|
||||||
describe('Component vnInvoiceOutGlobalInvoicing', () => {
|
|
||||||
let controller;
|
|
||||||
let $httpBackend;
|
|
||||||
let $httpParamSerializer;
|
|
||||||
|
|
||||||
beforeEach(ngModule('invoiceOut'));
|
|
||||||
|
|
||||||
beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => {
|
|
||||||
$httpBackend = _$httpBackend_;
|
|
||||||
$httpParamSerializer = _$httpParamSerializer_;
|
|
||||||
let $scope = $rootScope.$new();
|
|
||||||
const $element = angular.element('<vn-invoice-out-global-invoicing></vn-invoice-out-global-invoicing>');
|
|
||||||
const $transclude = {
|
|
||||||
$$boundTransclude: {
|
|
||||||
$$slots: []
|
|
||||||
}
|
|
||||||
};
|
|
||||||
controller = $componentController('vnInvoiceOutGlobalInvoicing', {$element, $scope, $transclude});
|
|
||||||
controller.$.invoiceButton = {disabled: false};
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('getMinClientId()', () => {
|
|
||||||
it('should set the invoice fromClientId property', () => {
|
|
||||||
const filter = {
|
|
||||||
order: 'id ASC',
|
|
||||||
limit: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
const serializedParams = $httpParamSerializer({filter});
|
|
||||||
$httpBackend.expectGET(`Clients/findOne?${serializedParams}`).respond(200, {id: 1101});
|
|
||||||
|
|
||||||
controller.getMinClientId();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.invoice.fromClientId).toEqual(1101);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getMaxClientId()', () => {
|
|
||||||
it('should set the invoice toClientId property', () => {
|
|
||||||
const filter = {
|
|
||||||
order: 'id DESC',
|
|
||||||
limit: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
const serializedParams = $httpParamSerializer({filter});
|
|
||||||
$httpBackend.expectGET(`Clients/findOne?${serializedParams}`).respond(200, {id: 1112});
|
|
||||||
|
|
||||||
controller.getMaxClientId();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.invoice.toClientId).toEqual(1112);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('responseHandler()', () => {
|
|
||||||
it('should throw an error when invoiceDate or maxShipped properties are not filled in', () => {
|
|
||||||
jest.spyOn(controller.vnApp, 'showError');
|
|
||||||
|
|
||||||
controller.invoice = {
|
|
||||||
fromClientId: 1101,
|
|
||||||
toClientId: 1101
|
|
||||||
};
|
|
||||||
|
|
||||||
controller.responseHandler('accept');
|
|
||||||
|
|
||||||
const expectedError = 'Invoice date and the max date should be filled';
|
|
||||||
|
|
||||||
expect(controller.vnApp.showError).toHaveBeenCalledWith(expectedError);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error when fromClientId or toClientId properties are not filled in', () => {
|
|
||||||
jest.spyOn(controller.vnApp, 'showError');
|
|
||||||
|
|
||||||
controller.invoice = {
|
|
||||||
invoiceDate: Date.vnNew(),
|
|
||||||
maxShipped: Date.vnNew()
|
|
||||||
};
|
|
||||||
|
|
||||||
controller.responseHandler('accept');
|
|
||||||
|
|
||||||
expect(controller.vnApp.showError).toHaveBeenCalledWith(`Choose a valid clients range`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should make an http POST query and then call to the showSuccess() method', () => {
|
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
|
||||||
|
|
||||||
const minShipped = Date.vnNew();
|
|
||||||
minShipped.setFullYear(minShipped.getFullYear() - 1);
|
|
||||||
minShipped.setMonth(1);
|
|
||||||
minShipped.setDate(1);
|
|
||||||
minShipped.setHours(0, 0, 0, 0);
|
|
||||||
controller.invoice = {
|
|
||||||
invoiceDate: Date.vnNew(),
|
|
||||||
maxShipped: Date.vnNew(),
|
|
||||||
fromClientId: 1101,
|
|
||||||
toClientId: 1101,
|
|
||||||
companyFk: 442,
|
|
||||||
minShipped: minShipped
|
|
||||||
};
|
|
||||||
const response = {
|
|
||||||
clientsAndAddresses: [{clientId: 1101, addressId: 121}],
|
|
||||||
invoice: controller.invoice
|
|
||||||
};
|
|
||||||
|
|
||||||
$httpBackend.expect('POST', `InvoiceOuts/clientsToInvoice`).respond(response);
|
|
||||||
$httpBackend.expect('POST', `InvoiceOuts/invoiceClient`).respond({id: 1});
|
|
||||||
controller.responseHandler('accept');
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,14 +0,0 @@
|
||||||
Create global invoice: Crear factura global
|
|
||||||
Some fields are required: Algunos campos son obligatorios
|
|
||||||
Max date: Fecha límite
|
|
||||||
Adding invoices to queue...: Añadiendo facturas a la cola...
|
|
||||||
Invoice date: Fecha de factura
|
|
||||||
From client: Desde el cliente
|
|
||||||
To client: Hasta el cliente
|
|
||||||
Invoice date and the max date should be filled: La fecha de factura y la fecha límite deben rellenarse
|
|
||||||
Choose a valid clients range: Selecciona un rango válido de clientes
|
|
||||||
of: de
|
|
||||||
Id Client: Id Cliente
|
|
||||||
All clients: Todos los clientes
|
|
||||||
Clients range: Rango de clientes
|
|
||||||
Calculating packages to invoice...: Calculando paquetes a factura...
|
|
|
@ -1,17 +0,0 @@
|
||||||
@import "variables";
|
|
||||||
|
|
||||||
.vn-invoice-out-global-invoicing {
|
|
||||||
tpl-body {
|
|
||||||
width: 500px;
|
|
||||||
|
|
||||||
.progress {
|
|
||||||
font-weight: bold;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
color: $color-primary;
|
|
||||||
vn-horizontal {
|
|
||||||
justify-content: center
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -68,29 +68,13 @@
|
||||||
</vn-card>
|
</vn-card>
|
||||||
</vn-data-viewer>
|
</vn-data-viewer>
|
||||||
<div fixed-bottom-right>
|
<div fixed-bottom-right>
|
||||||
<vn-vertical style="align-items: center;">
|
|
||||||
<vn-button class="round sm vn-mb-sm"
|
<vn-button class="round sm vn-mb-sm"
|
||||||
icon="add"
|
icon="add"
|
||||||
ng-click="invoicingOptions.show($event)"
|
ng-click="manualInvoicing.show()"
|
||||||
vn-tooltip="Make invoice..."
|
vn-tooltip="Make invoice..."
|
||||||
tooltip-position="left"
|
|
||||||
vn-acl="invoicing"
|
vn-acl="invoicing"
|
||||||
vn-acl-action="remove">
|
vn-acl-action="remove">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
|
|
||||||
<vn-menu vn-id="invoicingOptions">
|
|
||||||
<vn-item translate
|
|
||||||
name="manualInvoice"
|
|
||||||
ng-click="manualInvoicing.show()">
|
|
||||||
Manual invoicing
|
|
||||||
</vn-item>
|
|
||||||
<vn-item translate
|
|
||||||
name="globalInvoice"
|
|
||||||
ng-click="globalInvoicing.show()">
|
|
||||||
Global invoicing
|
|
||||||
</vn-item>
|
|
||||||
</vn-menu>
|
|
||||||
</vn-vertical>
|
|
||||||
</div>
|
</div>
|
||||||
<vn-popup vn-id="summary">
|
<vn-popup vn-id="summary">
|
||||||
<vn-invoice-out-summary
|
<vn-invoice-out-summary
|
||||||
|
@ -103,7 +87,3 @@
|
||||||
<vn-invoice-out-manual
|
<vn-invoice-out-manual
|
||||||
vn-id="manual-invoicing">
|
vn-id="manual-invoicing">
|
||||||
</vn-invoice-out-manual>
|
</vn-invoice-out-manual>
|
||||||
<vn-invoice-out-global-invoicing
|
|
||||||
vn-id="global-invoicing"
|
|
||||||
company-fk="$ctrl.vnConfig.companyFk">
|
|
||||||
</vn-invoice-out-global-invoicing>
|
|
|
@ -6,7 +6,9 @@
|
||||||
"dependencies": ["worker", "client", "ticket"],
|
"dependencies": ["worker", "client", "ticket"],
|
||||||
"menus": {
|
"menus": {
|
||||||
"main": [
|
"main": [
|
||||||
{"state": "invoiceOut.index", "icon": "icon-invoice-out"}
|
{"state": "invoiceOut.index", "icon": "icon-invoice-out"},
|
||||||
|
{"state": "invoiceOut.global-invoicing", "icon": "contact_support"}
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"routes": [
|
"routes": [
|
||||||
|
@ -24,6 +26,12 @@
|
||||||
"component": "vn-invoice-out-index",
|
"component": "vn-invoice-out-index",
|
||||||
"description": "InvoiceOut"
|
"description": "InvoiceOut"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"url": "/global-invoicing?q",
|
||||||
|
"state": "invoiceOut.global-invoicing",
|
||||||
|
"component": "vn-invoice-out-global-invoicing",
|
||||||
|
"description": "Global invoicing"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "/summary",
|
"url": "/summary",
|
||||||
"state": "invoiceOut.card.summary",
|
"state": "invoiceOut.card.summary",
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
'pink': sale.preparingList.hasSaleGroupDetail,
|
'pink': sale.preparingList.hasSaleGroupDetail,
|
||||||
'none': !sale.preparingList.hasSaleGroupDetail,
|
'none': !sale.preparingList.hasSaleGroupDetail,
|
||||||
}"
|
}"
|
||||||
class="circle"
|
class="circleState"
|
||||||
vn-tooltip="has saleGroupDetail"
|
vn-tooltip="has saleGroupDetail"
|
||||||
>
|
>
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
|
@ -37,28 +37,28 @@
|
||||||
'notice': sale.preparingList.isPreviousSelected,
|
'notice': sale.preparingList.isPreviousSelected,
|
||||||
'none': !sale.preparingList.isPreviousSelected,
|
'none': !sale.preparingList.isPreviousSelected,
|
||||||
}"
|
}"
|
||||||
class="circle"
|
class="circleState"
|
||||||
vn-tooltip="is previousSelected">
|
vn-tooltip="is previousSelected">
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
<vn-chip ng-class="::{
|
<vn-chip ng-class="::{
|
||||||
'dark-notice': sale.preparingList.isPrevious,
|
'dark-notice': sale.preparingList.isPrevious,
|
||||||
'none': !sale.preparingList.isPrevious,
|
'none': !sale.preparingList.isPrevious,
|
||||||
}"
|
}"
|
||||||
class="circle"
|
class="circleState"
|
||||||
vn-tooltip="is previous">
|
vn-tooltip="is previous">
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
<vn-chip ng-class="::{
|
<vn-chip ng-class="::{
|
||||||
'warning': sale.preparingList.isPrepared,
|
'warning': sale.preparingList.isPrepared,
|
||||||
'none': !sale.preparingList.isPrepared,
|
'none': !sale.preparingList.isPrepared,
|
||||||
}"
|
}"
|
||||||
class="circle"
|
class="circleState"
|
||||||
vn-tooltip="is prepared">
|
vn-tooltip="is prepared">
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
<vn-chip ng-class="::{
|
<vn-chip ng-class="::{
|
||||||
'yellow': sale.preparingList.isControled,
|
'yellow': sale.preparingList.isControled,
|
||||||
'none': !sale.preparingList.isControled,
|
'none': !sale.preparingList.isControled,
|
||||||
}"
|
}"
|
||||||
class="circle"
|
class="circleState"
|
||||||
vn-tooltip="is controled">
|
vn-tooltip="is controled">
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
|
|
|
@ -1,6 +1,15 @@
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
.circle {
|
vn-sale-tracking {
|
||||||
|
.chip {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 15px;
|
||||||
|
min-height: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.circleState {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -11,7 +11,8 @@ module.exports = {
|
||||||
this.client = await this.findOneFromDef('client', [this.reference]);
|
this.client = await this.findOneFromDef('client', [this.reference]);
|
||||||
this.taxes = await this.rawSqlFromDef(`taxes`, [this.reference]);
|
this.taxes = await this.rawSqlFromDef(`taxes`, [this.reference]);
|
||||||
this.hasIntrastat = await this.findValueFromDef(`hasIntrastat`, [this.reference]);
|
this.hasIntrastat = await this.findValueFromDef(`hasIntrastat`, [this.reference]);
|
||||||
this.intrastat = await this.rawSqlFromDef(`intrastat`, [this.reference, this.reference, this.reference, this.reference]);
|
this.intrastat = await this.rawSqlFromDef(`intrastat`,
|
||||||
|
[this.reference, this.reference, this.reference, this.reference]);
|
||||||
this.rectified = await this.rawSqlFromDef(`rectified`, [this.reference]);
|
this.rectified = await this.rawSqlFromDef(`rectified`, [this.reference]);
|
||||||
this.hasIncoterms = await this.findValueFromDef(`hasIncoterms`, [this.reference]);
|
this.hasIncoterms = await this.findValueFromDef(`hasIncoterms`, [this.reference]);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
verb set is irregular set set set