This commit is contained in:
parent
01ade6eb03
commit
4cc6485cbd
|
@ -4,5 +4,6 @@
|
|||
"files.eol": "\n",
|
||||
"editor.codeActionsOnSave": {
|
||||
"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');
|
|
@ -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
|
||||
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 seted');
|
||||
END IF;
|
||||
|
||||
IF vComponentCost IS NULL THEN
|
||||
CALL util.throw('Component cost not seted');
|
||||
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 ;
|
|
@ -572,14 +572,13 @@ INSERT INTO `vn`.`taxArea` (`code`, `claveOperacionFactura`, `CodigoTransaccion`
|
|||
('NATIONAL', 0, 1),
|
||||
('WORLD', 2, 15);
|
||||
|
||||
INSERT INTO `vn`.`invoiceOutSerial` (`code`, `description`, `isTaxed`, `taxAreaFk`, `isCEE`)
|
||||
INSERT INTO `vn`.`invoiceOutSerial` (`code`, `description`, `isTaxed`, `taxAreaFk`, `isCEE`, `type`)
|
||||
VALUES
|
||||
('A', 'Global nacional', 1, 'NATIONAL', 0),
|
||||
('T', 'Española rapida', 1, 'NATIONAL', 0),
|
||||
('V', 'Intracomunitaria global', 0, 'CEE', 1),
|
||||
('M', 'Múltiple nacional', 1, 'NATIONAL', 0),
|
||||
('E', 'Exportación rápida', 0, 'WORLD', 0);
|
||||
;
|
||||
('A', 'Global nacional', 1, 'NATIONAL', 0, 'global'),
|
||||
('T', 'Española rapida', 1, 'NATIONAL', 0, 'quick'),
|
||||
('V', 'Intracomunitaria global', 0, 'CEE', 1, 'global'),
|
||||
('M', 'Múltiple nacional', 1, 'NATIONAL', 0, 'quick'),
|
||||
('E', 'Exportación rápida', 0, 'WORLD', 0, 'quick');
|
||||
|
||||
INSERT INTO `vn`.`invoiceOut`(`id`, `serial`, `amount`, `issued`,`clientFk`, `created`, `companyFk`, `dued`, `booked`, `bankFk`, `hasPdf`)
|
||||
VALUES
|
||||
|
|
|
@ -260,10 +260,11 @@
|
|||
"Aplicación bloqueada por el usuario 9": "Aplicación bloqueada por el usuario 9",
|
||||
"Failed to upload file": "Error al subir archivo",
|
||||
"The DOCUWARE PDF document does not exists": "El documento PDF Docuware no existe",
|
||||
"It is not possible to modify tracked sales": "No es posible modificar líneas de pedido que se hayan empezado a preparar",
|
||||
"It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo",
|
||||
"It is not possible to modify cloned sales": "No es posible modificar líneas de pedido clonadas",
|
||||
"It is not possible to modify tracked sales": "No es posible modificar líneas de pedido que se hayan empezado a preparar",
|
||||
"It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo",
|
||||
"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.",
|
||||
"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"
|
||||
}
|
|
@ -4,47 +4,37 @@ module.exports = Self => {
|
|||
accessType: 'WRITE',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'clientId',
|
||||
type: 'number',
|
||||
description: 'The client id'
|
||||
}, {
|
||||
arg: 'invoiceDate',
|
||||
type: 'date',
|
||||
description: 'The invoice date'
|
||||
},
|
||||
{
|
||||
description: 'The invoice date',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'maxShipped',
|
||||
type: 'date',
|
||||
description: 'The maximum shipped date'
|
||||
},
|
||||
{
|
||||
arg: 'fromClientId',
|
||||
type: 'number',
|
||||
description: 'The minimum client id'
|
||||
},
|
||||
{
|
||||
arg: 'toClientId',
|
||||
type: 'number',
|
||||
description: 'The maximum client id'
|
||||
},
|
||||
{
|
||||
description: 'The maximum shipped date',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'companyFk',
|
||||
type: 'number',
|
||||
description: 'The company id to invoice'
|
||||
}
|
||||
description: 'The company id to invoice',
|
||||
required: true
|
||||
},
|
||||
],
|
||||
returns: [{
|
||||
arg: 'clientsAndAddresses',
|
||||
type: ['object']
|
||||
returns: {
|
||||
type: 'Object',
|
||||
root: true
|
||||
},
|
||||
{
|
||||
arg: 'invoice',
|
||||
type: 'object'
|
||||
}],
|
||||
http: {
|
||||
path: '/clientsToInvoice',
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.clientsToInvoice = async(ctx, options) => {
|
||||
const args = ctx.args;
|
||||
Self.clientsToInvoice = async(ctx, clientId, invoiceDate, maxShipped, companyFk, options) => {
|
||||
let tx;
|
||||
const myOptions = {};
|
||||
|
||||
|
@ -56,121 +46,52 @@ module.exports = Self => {
|
|||
myOptions.transaction = tx;
|
||||
}
|
||||
|
||||
let query;
|
||||
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
|
||||
const vIsAllInvoiceable = false;
|
||||
const clientsWithPackaging = await getClientsWithPackaging(ctx, myOptions);
|
||||
for (let client of clientsWithPackaging) {
|
||||
await Self.rawSql('CALL packageInvoicing(?, ?, ?, ?, @newTicket)', [
|
||||
client.id,
|
||||
args.invoiceDate,
|
||||
args.companyFk,
|
||||
vIsAllInvoiceable
|
||||
], myOptions);
|
||||
}
|
||||
await Self.rawSql('CALL ticketPackaging_add(?, ?, ?, ?)', [
|
||||
clientId,
|
||||
invoiceDate,
|
||||
companyFk,
|
||||
vIsAllInvoiceable
|
||||
], myOptions);
|
||||
|
||||
const invoiceableClients = await getInvoiceableClients(ctx, minShipped, 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 => {
|
||||
return {
|
||||
clientId: invoiceableClient.id,
|
||||
addressId: invoiceableClient.addressFk
|
||||
};
|
||||
}
|
||||
);
|
||||
const addresses = await Self.rawSql(query, [
|
||||
minShipped,
|
||||
maxShipped,
|
||||
clientId,
|
||||
clientId,
|
||||
companyFk
|
||||
], myOptions);
|
||||
|
||||
if (tx) await tx.commit();
|
||||
|
||||
return [
|
||||
clientsAndAddresses,
|
||||
{
|
||||
invoiceDate: args.invoiceDate,
|
||||
maxShipped: args.maxShipped,
|
||||
fromClientId: args.fromClientId,
|
||||
toClientId: args.toClientId,
|
||||
companyFk: args.companyFk,
|
||||
minShipped: minShipped
|
||||
}
|
||||
];
|
||||
return addresses;
|
||||
} catch (e) {
|
||||
if (tx) await tx.rollback();
|
||||
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, minShipped, options) {
|
||||
const models = Self.app.models;
|
||||
const args = ctx.args;
|
||||
minShipped.setFullYear(minShipped.getFullYear() - 1);
|
||||
|
||||
const query = `SELECT c.id,
|
||||
c.hasToInvoiceByAddress,
|
||||
a.id addressFk,
|
||||
sum(t.totalWithVat) totalAmount
|
||||
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.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 totalAmount > 0;`;
|
||||
|
||||
return models.InvoiceOut.rawSql(query, [
|
||||
minShipped,
|
||||
args.maxShipped,
|
||||
args.companyFk
|
||||
], options);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethod('getInvoiceDate', {
|
||||
description: 'Returns default Invoice Date',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'year',
|
||||
type: 'number',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'companyFk',
|
||||
type: 'number',
|
||||
required: true
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: ['object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/getInvoiceDate`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.getInvoiceDate = async(year, 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 BETWEEN MAKEDATE(?, 1) AND
|
||||
util.lastDayOfYear(MAKEDATE(?, 1))
|
||||
AND io.companyFk = ?`,
|
||||
[year, year, companyFk]);
|
||||
return invoiceDate;
|
||||
};
|
||||
};
|
|
@ -4,48 +4,39 @@ module.exports = Self => {
|
|||
Self.remoteMethodCtx('invoiceClient', {
|
||||
description: 'Make a invoice of a client',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'clientId',
|
||||
type: 'number',
|
||||
description: 'The client id to invoice',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'addressId',
|
||||
type: 'number',
|
||||
description: 'The address id to invoice',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'invoiceDate',
|
||||
type: 'date',
|
||||
description: 'The invoice date',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'maxShipped',
|
||||
type: 'date',
|
||||
description: 'The maximum shipped date',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'companyFk',
|
||||
type: 'number',
|
||||
description: 'The company id to invoice',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'minShipped',
|
||||
type: 'date',
|
||||
description: 'The minium shupped date',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'printerFk',
|
||||
type: 'number',
|
||||
description: 'The printer to print',
|
||||
required: true
|
||||
}],
|
||||
accepts: [
|
||||
{
|
||||
arg: 'clientId',
|
||||
type: 'number',
|
||||
description: 'The client id to invoice',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'addressId',
|
||||
type: 'number',
|
||||
description: 'The address id to invoice',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'invoiceDate',
|
||||
type: 'date',
|
||||
description: 'The invoice date',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'maxShipped',
|
||||
type: 'date',
|
||||
description: 'The maximum shipped date',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'companyFk',
|
||||
type: 'number',
|
||||
description: 'The company id to invoice',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'printerFk',
|
||||
type: 'number',
|
||||
description: 'The printer to print',
|
||||
required: true
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
|
@ -70,6 +61,9 @@ module.exports = Self => {
|
|||
myOptions.transaction = tx;
|
||||
}
|
||||
|
||||
const minShipped = Date.vnNew();
|
||||
minShipped.setFullYear(args.maxShipped.getFullYear() - 1);
|
||||
|
||||
let invoiceId;
|
||||
let invoiceOut;
|
||||
try {
|
||||
|
@ -79,7 +73,7 @@ module.exports = Self => {
|
|||
|
||||
if (client.hasToInvoiceByAddress) {
|
||||
await Self.rawSql('CALL ticketToInvoiceByAddress(?, ?, ?, ?)', [
|
||||
args.minShipped,
|
||||
minShipped,
|
||||
args.maxShipped,
|
||||
args.addressId,
|
||||
args.companyFk
|
||||
|
@ -133,26 +127,26 @@ module.exports = Self => {
|
|||
throw e;
|
||||
}
|
||||
|
||||
if (invoiceId && !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]);
|
||||
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 = {
|
||||
reference: invoiceOut.ref,
|
||||
recipientId: invoiceOut.clientFk,
|
||||
recipient: invoiceOut.client().email
|
||||
};
|
||||
await models.InvoiceOut.invoiceEmail(ctx, invoiceOut.ref);
|
||||
}
|
||||
}
|
||||
|
||||
if (invoiceId && invoiceOut.client().isToBeMailed) {
|
||||
ctx.args = {
|
||||
reference: invoiceOut.ref,
|
||||
recipientId: invoiceOut.clientFk,
|
||||
recipient: invoiceOut.client().email
|
||||
};
|
||||
await models.InvoiceOut.invoiceEmail(ctx, invoiceOut.ref);
|
||||
}
|
||||
|
||||
return invoiceId;
|
||||
};
|
||||
|
||||
|
@ -160,13 +154,12 @@ module.exports = Self => {
|
|||
const models = Self.app.models;
|
||||
const query = 'SELECT hasAnyNegativeBase() AS base';
|
||||
const [result] = await models.InvoiceOut.rawSql(query, null, options);
|
||||
|
||||
return result && result.base;
|
||||
}
|
||||
|
||||
async function getIsSpanishCompany(companyId, options) {
|
||||
const models = Self.app.models;
|
||||
const query = `SELECT COUNT(*) AS total
|
||||
const query = `SELECT COUNT(*) isSpanishCompany
|
||||
FROM supplier s
|
||||
JOIN country c ON c.id = s.countryFk
|
||||
AND c.code = 'ES'
|
||||
|
@ -175,6 +168,6 @@ module.exports = Self => {
|
|||
companyId
|
||||
], options);
|
||||
|
||||
return supplierCompany && supplierCompany.total;
|
||||
return supplierCompany && supplierCompany.isSpanishCompany;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -16,4 +16,5 @@ module.exports = Self => {
|
|||
require('../methods/invoiceOut/invoiceCsv')(Self);
|
||||
require('../methods/invoiceOut/invoiceCsvEmail')(Self);
|
||||
require('../methods/invoiceOut/invoiceOutPdf')(Self);
|
||||
require('../methods/invoiceOut/getInvoiceDate')(Self);
|
||||
};
|
||||
|
|
|
@ -1,59 +1,66 @@
|
|||
<vn-card class="vn-w-lg vn-ma-md">
|
||||
<h5 ng-if="$ctrl.packageInvoicing" translate text-center class="vn-pa-md">{{'Calculating packages to invoice...'}}</h5>
|
||||
<vn-vertical ng-if="$ctrl.clients.length" text-center class="vn-pa-md">
|
||||
<h5 translate ng-if="$ctrl.percentage != 100">Invoicing</h5>
|
||||
<h5 translate ng-if="$ctrl.percentage == 100">Ended process</h5>
|
||||
<div ng-if="$ctrl.percentage != 100">
|
||||
{{'Current client id' | translate}}: {{$ctrl.currentClientId}}
|
||||
</div>
|
||||
<h6>
|
||||
{{($ctrl.percentage / 100) | percentage: 0}} ({{$ctrl.currentClient}} {{'of' | translate}} {{$ctrl.clients.length}})
|
||||
</h6>
|
||||
</vn-vertical>
|
||||
<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-table ng-if="data.length">
|
||||
<vn-card class="vn-w-lg vn-mt-md" ng-if="$ctrl.errors.length">
|
||||
<vn-table>
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-th field="clientId" number>Client id</vn-th>
|
||||
<vn-th field="nickname">Nickname</vn-th>
|
||||
<vn-th field="addressId" number>Address id</vn-th>
|
||||
<vn-th field="street" expand>Street</vn-th>
|
||||
<vn-th field="error" expand>Error</vn-th>
|
||||
<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="client in data">
|
||||
<vn-tr ng-repeat="error in $ctrl.errors">
|
||||
<vn-td number>
|
||||
<span
|
||||
vn-click-stop="clientDescriptor.show($event, client.id)"
|
||||
vn-click-stop="clientDescriptor.show($event, error.address.clientId)"
|
||||
class="link">
|
||||
{{::client.id}}
|
||||
{{::error.address.clientId}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td expand>
|
||||
{{::client.address.nickname}}
|
||||
{{::error.address.clientName}}
|
||||
</vn-td>
|
||||
<vn-td number>
|
||||
{{::client.address.id}}
|
||||
{{::error.address.id}}
|
||||
</vn-td>
|
||||
<vn-td expand>
|
||||
{{::client.address.street}}
|
||||
{{::error.address.nickname}}
|
||||
</vn-td>
|
||||
<vn-td expand id="error">
|
||||
<vn-spinner
|
||||
ng-if="client.status == 'waiting'"
|
||||
enable="true">
|
||||
</vn-spinner>
|
||||
{{::client.error}}
|
||||
<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"
|
||||
|
@ -69,22 +76,46 @@
|
|||
</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.invoice.invoiceDate">
|
||||
ng-model="$ctrl.invoiceDate">
|
||||
</vn-date-picker>
|
||||
<vn-date-picker
|
||||
vn-one
|
||||
label="Max date"
|
||||
ng-model="$ctrl.invoice.maxShipped">
|
||||
ng-model="$ctrl.maxShipped">
|
||||
</vn-date-picker>
|
||||
<vn-autocomplete
|
||||
url="Companies"
|
||||
label="Company"
|
||||
show-field="code"
|
||||
value-field="id"
|
||||
ng-model="$ctrl.invoice.companyFk">
|
||||
ng-model="$ctrl.companyFk">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
url="Printers"
|
||||
|
@ -92,9 +123,18 @@
|
|||
show-field="name"
|
||||
value-field="id"
|
||||
where="{isLabeler: false}"
|
||||
ng-model="$ctrl.invoice.printerFk">
|
||||
ng-model="$ctrl.printerFk">
|
||||
</vn-autocomplete>
|
||||
<vn-submit vn-id="invoiceButton" ng-click="$ctrl.makeInvoice()" label="Invoice out"></vn-submit>
|
||||
<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>
|
||||
|
|
|
@ -4,128 +4,131 @@ import UserError from 'core/lib/user-error';
|
|||
import './style.scss';
|
||||
|
||||
class Controller extends Section {
|
||||
constructor($element, $, $transclude) {
|
||||
super($element, $, $transclude);
|
||||
this.invoice = {
|
||||
maxShipped: Date.vnNew(),
|
||||
companyFk: this.vnConfig.companyFk
|
||||
};
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
this.getMinClientId();
|
||||
this.getMaxClientId();
|
||||
}
|
||||
const date = Date.vnNew();
|
||||
Object.assign(this, {
|
||||
maxShipped: new Date(date.getFullYear(), date.getMonth(), 0),
|
||||
clientsToInvoice: 'all',
|
||||
});
|
||||
|
||||
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});
|
||||
}
|
||||
|
||||
getPercentage() {
|
||||
this.percentage = ((this.currentClient - 1) * 100) / this.clients.length;
|
||||
}
|
||||
|
||||
restartValues() {
|
||||
this.$.invoiceButton.disabled = false;
|
||||
this.packageInvoicing = false;
|
||||
}
|
||||
|
||||
invoiceOut(invoice, clientsAndAddresses) {
|
||||
const [clientAndAddress] = clientsAndAddresses;
|
||||
if (!clientAndAddress) {
|
||||
this.percentage = 100;
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentClientId = clientAndAddress.clientId;
|
||||
this.currentClient = ++this.currentClient;
|
||||
this.getPercentage();
|
||||
|
||||
const params = {
|
||||
clientId: clientAndAddress.clientId,
|
||||
addressId: clientAndAddress.addressId,
|
||||
invoiceDate: invoice.invoiceDate,
|
||||
maxShipped: invoice.maxShipped,
|
||||
companyFk: invoice.companyFk,
|
||||
minShipped: invoice.minShipped,
|
||||
printerFk: this.invoice.printerFk,
|
||||
|
||||
};
|
||||
this.$http.get(`Addresses/${clientAndAddress.addressId}`)
|
||||
this.$http.get('UserConfigs/getUserConfig')
|
||||
.then(res => {
|
||||
this.address = res.data;
|
||||
return this.$http.post(`InvoiceOuts/invoiceClient`, params)
|
||||
.catch(res => {
|
||||
this.$.data.unshift({
|
||||
id: clientAndAddress.clientId,
|
||||
address: this.address,
|
||||
status: 'error',
|
||||
error: res.data.error.message
|
||||
});
|
||||
}).finally(() => {
|
||||
clientsAndAddresses.shift();
|
||||
return this.invoiceOut(invoice, clientsAndAddresses);
|
||||
});
|
||||
this.companyFk = res.data.companyFk;
|
||||
const params = {
|
||||
year: this.maxShipped.getFullYear(),
|
||||
companyFk: this.companyFk
|
||||
};
|
||||
return this.$http.get('InvoiceOuts/getInvoiceDate', {params});
|
||||
})
|
||||
.then(res => {
|
||||
this.minInvoicingDate = new Date(res.data.issued);
|
||||
this.invoiceDate = this.minInvoicingDate;
|
||||
});
|
||||
}
|
||||
|
||||
stopInvoicing() {
|
||||
this.status = 'stopping';
|
||||
}
|
||||
|
||||
makeInvoice() {
|
||||
this.invoicing = true;
|
||||
this.status = 'packageInvoicing';
|
||||
this.errors = [];
|
||||
this.addresses = null;
|
||||
|
||||
try {
|
||||
if (!this.invoice.invoiceDate || !this.invoice.maxShipped)
|
||||
throw new Error('Invoice date and the max date should be filled');
|
||||
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.invoice.companyFk)
|
||||
throw new Error('Choose a valid company');
|
||||
if (this.clientsToInvoice == 'all')
|
||||
this.clientId = undefined;
|
||||
|
||||
if (!this.invoice.printerFk)
|
||||
throw new Error('Choose a valid printer');
|
||||
|
||||
this.$.invoiceButton.disabled = true;
|
||||
this.$.data = [];
|
||||
this.packageInvoicing = true;
|
||||
|
||||
this.$http.post(`InvoiceOuts/clientsToInvoice`, this.invoice)
|
||||
const params = {
|
||||
invoiceDate: this.invoiceDate,
|
||||
maxShipped: this.maxShipped,
|
||||
clientId: this.clientId,
|
||||
companyFk: this.companyFk
|
||||
};
|
||||
this.$http.post(`InvoiceOuts/clientsToInvoice`, params)
|
||||
.then(res => {
|
||||
this.packageInvoicing = false;
|
||||
const invoice = res.data.invoice;
|
||||
this.currentClient = 0;
|
||||
console.log(res.data);
|
||||
this.addresses = res.data;
|
||||
console.log(this.address);
|
||||
if (!this.addresses.length)
|
||||
throw new UserError(`There aren't tickets to invoice`);
|
||||
|
||||
const clientsAndAddresses = res.data.clientsAndAddresses;
|
||||
if (!clientsAndAddresses.length) throw new UserError(`There aren't clients to invoice`);
|
||||
|
||||
this.clients = [];
|
||||
for (const clientAndAddress of clientsAndAddresses)
|
||||
this.clients.push(clientAndAddress.clientId);
|
||||
|
||||
return this.invoiceOut(invoice, clientsAndAddresses);
|
||||
this.addressIndex = 0;
|
||||
return this.invoiceOut();
|
||||
})
|
||||
.finally(() => this.restartValues());
|
||||
} catch (e) {
|
||||
this.vnApp.showError(this.$t(e.message));
|
||||
this.restartValues();
|
||||
return false;
|
||||
.catch(err => this.handleError(err));
|
||||
} catch (err) {
|
||||
this.handleError(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$element', '$scope', '$transclude'];
|
||||
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'),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import './index';
|
||||
|
||||
const UserError = require('vn-loopback/util/user-error');
|
||||
describe('InvoiceOut', () => {
|
||||
describe('Component vnInvoiceOutGlobalInvoicing', () => {
|
||||
let controller;
|
||||
|
@ -22,99 +22,60 @@ describe('InvoiceOut', () => {
|
|||
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('makeInvoice()', () => {
|
||||
it('should throw an error when invoiceDate or maxShipped properties are not filled in', () => {
|
||||
jest.spyOn(controller.vnApp, 'showError');
|
||||
controller.clientsToInvoice = 'all';
|
||||
|
||||
controller.invoice = {
|
||||
fromClientId: 1101,
|
||||
toClientId: 1101
|
||||
};
|
||||
|
||||
controller.makeInvoice();
|
||||
let error;
|
||||
try {
|
||||
controller.makeInvoice();
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
}
|
||||
|
||||
const expectedError = 'Invoice date and the max date should be filled';
|
||||
|
||||
expect(controller.vnApp.showError).toHaveBeenCalledWith(expectedError);
|
||||
expect(error).toBe(expectedError);
|
||||
});
|
||||
|
||||
it('should throw an error when fromClientId or toClientId properties are not filled in', () => {
|
||||
it('should throw an error when select one client and clientId is not filled in', () => {
|
||||
jest.spyOn(controller.vnApp, 'showError');
|
||||
controller.clientsToInvoice = 'one';
|
||||
|
||||
controller.invoice = {
|
||||
invoiceDate: Date.vnNew(),
|
||||
maxShipped: Date.vnNew()
|
||||
};
|
||||
let error;
|
||||
try {
|
||||
controller.makeInvoice();
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
}
|
||||
|
||||
controller.makeInvoice();
|
||||
const expectedError = 'Choose a valid client';
|
||||
|
||||
expect(controller.vnApp.showError).toHaveBeenCalledWith(`Choose a valid clients range`);
|
||||
expect(error).toBe(expectedError);
|
||||
});
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
const address = {id: 121};
|
||||
|
||||
const date = Date.vnNew();
|
||||
date.setDate(date.getDate() + 1);
|
||||
controller.invoiceDate = date;
|
||||
controller.maxShipped = date;
|
||||
controller.minInvoicingDate = Date.vnNew();
|
||||
controller.clientsToInvoice = 'one';
|
||||
controller.clientId = 1101;
|
||||
controller.companyFk = 442;
|
||||
controller.printerFk = 1;
|
||||
const response = [{
|
||||
clientId: 1101,
|
||||
id: 121
|
||||
}];
|
||||
$httpBackend.expect('POST', `InvoiceOuts/clientsToInvoice`).respond(response);
|
||||
$httpBackend.expect('GET', `Addresses/${response.clientsAndAddresses[0].addressId}`).respond(address);
|
||||
$httpBackend.expect('POST', `InvoiceOuts/invoiceClient`).respond({id: 1});
|
||||
$httpBackend.expect('POST', `InvoiceOuts/invoiceClient`).respond(1);
|
||||
controller.makeInvoice();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||
expect(controller.status).toEqual('done');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
There aren't clients to invoice: No existen clientes para facturar
|
||||
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 clients range: Selecciona un rango válido de clientes
|
||||
Choose a valid company: Selecciona un empresa válida
|
||||
Choose a valid printer: Selecciona una impresora válida
|
||||
Clients range: Rango de clientes
|
||||
All clients: Todos los clientes
|
||||
Calculating packages to invoice...: Calculando paquetes a facturar...
|
||||
Clean: Limpiar
|
||||
From client: Desde cliente
|
||||
To client: Hasta cliente
|
||||
Build packaging tickets: Generando tickets de embalajes
|
||||
Address id: Id dirección
|
||||
Printer: Impresora
|
||||
of: de
|
||||
Client: Cliente
|
||||
Current client id: Id cliente actual
|
||||
Invoicing: Facturando
|
||||
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
|
|
@ -1,7 +1,9 @@
|
|||
@import "variables";
|
||||
|
||||
.chip {
|
||||
display: inline-block;
|
||||
min-width: 15px;
|
||||
min-height: 25px;
|
||||
vn-sale-tracking {
|
||||
.chip {
|
||||
display: inline-block;
|
||||
min-width: 15px;
|
||||
min-height: 25px;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue