diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1b938797e..a672e7986 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [24.20.01] - 2024-05-14
+### Fixed
+- (Worker -> time-control) Corrección de errores
+- (InvoiceOut -> Crear factura) Cuando falla al crear una factura, se devuelve un error
+
## [24.18.01] - 2024-05-07
## [24.16.01] - 2024-04-18
diff --git a/back/model-config.json b/back/model-config.json
index ebcdb7bce..f7c598d43 100644
--- a/back/model-config.json
+++ b/back/model-config.json
@@ -124,6 +124,9 @@
"Postcode": {
"dataSource": "vn"
},
+ "ReferenceRate": {
+ "dataSource": "vn"
+ },
"SageWithholding": {
"dataSource": "vn"
},
@@ -178,4 +181,4 @@
"ProductionConfig": {
"dataSource": "vn"
}
-}
\ No newline at end of file
+}
diff --git a/back/models/collection.json b/back/models/collection.json
index 3e428ef60..cb8dc3d7c 100644
--- a/back/models/collection.json
+++ b/back/models/collection.json
@@ -1,6 +1,11 @@
{
"name": "Collection",
"base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "collection"
+ }
+ },
"acls": [{
"property": "validations",
"accessType": "EXECUTE",
@@ -9,4 +14,3 @@
"permission": "ALLOW"
}]
}
-
\ No newline at end of file
diff --git a/back/models/reference-rate.json b/back/models/reference-rate.json
new file mode 100644
index 000000000..fe732f3ef
--- /dev/null
+++ b/back/models/reference-rate.json
@@ -0,0 +1,36 @@
+{
+ "name": "ReferenceRate",
+ "base": "PersistedModel",
+ "options": {
+ "mysql": {
+ "table": "referenceRate"
+ }
+ },
+ "properties": {
+ "id": {
+ "type": "number",
+ "id": true,
+ "description": "Identifier"
+ },
+ "currencyFk": {
+ "type": "number",
+ "required": true
+ },
+ "dated": {
+ "type": "date",
+ "required": true
+ },
+ "value": {
+ "type": "number",
+ "required": true
+ }
+ },
+ "acls": [
+ {
+ "accessType": "READ",
+ "principalType": "ROLE",
+ "principalId": "$everyone",
+ "permission": "ALLOW"
+ }
+ ]
+}
diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql
index ff58af2e2..fe619d4a1 100644
--- a/db/dump/fixtures.before.sql
+++ b/db/dump/fixtures.before.sql
@@ -160,7 +160,8 @@ INSERT INTO `vn`.`currency`(`id`, `code`, `name`, `ratio`)
(1, 'EUR', 'Euro', 1),
(2, 'USD', 'Dollar USA', 1.4),
(3, 'GBP', 'Libra', 1),
- (4, 'JPY', 'Yen Japones', 1);
+ (4, 'JPY', 'Yen Japones', 1),
+ (5, 'CNY', 'Yuan Chino', 1.2);
INSERT INTO `vn`.`country`(`id`, `country`, `isUeeMember`, `code`, `currencyFk`, `ibanLength`, `continentFk`, `hasDailyInvoice`, `CEE`)
VALUES
diff --git a/db/routines/vn/procedures/item_getBalance.sql b/db/routines/vn/procedures/item_getBalance.sql
index 95596d3bc..11af7e570 100644
--- a/db/routines/vn/procedures/item_getBalance.sql
+++ b/db/routines/vn/procedures/item_getBalance.sql
@@ -155,27 +155,28 @@ BEGIN
SET @currentLineFk := 0;
SET @shipped := '';
- SELECT DATE(@shipped:= shipped) shipped,
- alertLevel,
- stateName,
- origin,
- reference,
- clientFk,
- name,
- `in` invalue,
- `out`,
- @a := @a + IFNULL(`in`, 0) - IFNULL(`out`, 0) balance,
+ SELECT DATE(@shipped:= t.shipped) shipped,
+ t.alertLevel,
+ t.stateName,
+ t.origin,
+ t.reference,
+ t.clientFk,
+ t.name,
+ t.`in` invalue,
+ t.`out`,
+ @a := @a + IFNULL(t.`in`, 0) - IFNULL(t.`out`, 0) balance,
@currentLineFk := IF (@shipped < util.VN_CURDATE()
- OR (@shipped = util.VN_CURDATE() AND (isPicked OR a.`code` >= 'ON_PREPARATION')),
- lineFk,
+ OR (@shipped = util.VN_CURDATE() AND (t.isPicked OR a.`code` >= 'ON_PREPARATION')),
+ t.lineFk,
@currentLineFk) lastPreparedLineFk,
- isTicket,
- lineFk,
- isPicked,
- clientType,
- claimFk
- FROM tItemDiary
- LEFT JOIN alertLevel a ON a.id = tItemDiary.alertLevel;
+ t.isTicket,
+ t.lineFk,
+ t.isPicked,
+ t.clientType,
+ t.claimFk,
+ t.`order`
+ FROM tItemDiary t
+ LEFT JOIN alertLevel a ON a.id = t.alertLevel;
ELSE
SELECT SUM(`in`) - SUM(`out`) INTO @a
@@ -197,7 +198,8 @@ BEGIN
0 lineFk,
0 isPicked,
0 clientType,
- 0 claimFk
+ 0 claimFk,
+ NULL `order`
UNION ALL
SELECT shipped,
alertlevel,
@@ -213,7 +215,8 @@ BEGIN
lineFk,
isPicked,
clientType,
- claimFk
+ claimFk,
+ `order`
FROM tItemDiary
WHERE shipped >= vDate;
END IF;
diff --git a/db/routines/vn/procedures/ticket_canAdvance.sql b/db/routines/vn/procedures/ticket_canAdvance.sql
index 8852a3010..d1ca7b5e2 100644
--- a/db/routines/vn/procedures/ticket_canAdvance.sql
+++ b/db/routines/vn/procedures/ticket_canAdvance.sql
@@ -8,38 +8,14 @@ BEGIN
* @param vDateToAdvance Fecha a cuando se quiere adelantar.
* @param vWarehouseFk Almacén
*/
- DECLARE vDateInventory DATE;
- SELECT inventoried INTO vDateInventory FROM config;
-
- CREATE OR REPLACE TEMPORARY TABLE tmp.stock
- (itemFk INT PRIMARY KEY,
- amount INT)
- ENGINE = MEMORY;
-
- INSERT INTO tmp.stock(itemFk, amount)
- SELECT itemFk, SUM(quantity) amount FROM
- (
- SELECT itemFk, quantity
- FROM itemTicketOut
- WHERE shipped >= vDateInventory
- AND shipped < vDateFuture
- AND warehouseFk = vWarehouseFk
- UNION ALL
- SELECT itemFk, quantity
- FROM itemEntryIn
- WHERE landed >= vDateInventory
- AND landed <= vDateToAdvance
- AND isVirtualStock = FALSE
- AND warehouseInFk = vWarehouseFk
- UNION ALL
- SELECT itemFk, quantity
- FROM itemEntryOut
- WHERE shipped >= vDateInventory
- AND shipped < vDateFuture
- AND warehouseOutFk = vWarehouseFk
- ) t
- GROUP BY itemFk HAVING amount != 0;
+ CALL item_getStock(vWarehouseFk, vDateToAdvance, NULL);
+ CALL item_getMinacum(
+ vWarehouseFk,
+ vDateToAdvance,
+ DATEDIFF(DATE_SUB(vDateFuture, INTERVAL 1 DAY), vDateToAdvance),
+ NULL
+ );
CREATE OR REPLACE TEMPORARY TABLE tmp.filter
(INDEX (id))
@@ -87,7 +63,7 @@ BEGIN
count(s.id) futureLines,
GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) futureIpt,
CAST(SUM(litros) AS DECIMAL(10,0)) futureLiters,
- SUM((s.quantity <= IFNULL(st.amount,0))) hasStock,
+ SUM(s.quantity <= (IFNULL(il.stock,0) + IFNULL(im.amount, 0))) hasStock,
z.id futureZoneFk,
z.name futureZoneName,
st.classColor,
@@ -107,7 +83,9 @@ BEGIN
JOIN agencyMode am ON t.agencyModeFk = am.id
JOIN zone z ON t.zoneFk = z.id
LEFT JOIN itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
- LEFT JOIN tmp.stock st ON st.itemFk = i.id
+ LEFT JOIN tmp.itemMinacum im ON im.itemFk = i.id
+ AND im.warehouseFk = vWarehouseFk
+ LEFT JOIN tmp.itemList il ON il.itemFk = i.id
WHERE t.shipped BETWEEN vDateFuture AND util.dayend(vDateFuture)
AND t.warehouseFk = vWarehouseFk
GROUP BY t.id
@@ -146,6 +124,8 @@ BEGIN
) dest ON dest.addressFk = origin.addressFk
WHERE origin.hasStock;
- DROP TEMPORARY TABLE tmp.stock;
+ DROP TEMPORARY TABLE IF EXISTS
+ tmp.itemList,
+ tmp.itemMinacum;
END$$
DELIMITER ;
diff --git a/db/routines/vn/procedures/ticket_getMovable.sql b/db/routines/vn/procedures/ticket_getMovable.sql
index eee165538..512151bd4 100644
--- a/db/routines/vn/procedures/ticket_getMovable.sql
+++ b/db/routines/vn/procedures/ticket_getMovable.sql
@@ -21,7 +21,7 @@ BEGIN
WHERE t.id = vTicketFk;
-- Añadimos un dia más para calcular el stock hasta vNewShipped inclusive
- CALL item_getStock(vWarehouseFk, DATE_ADD(vNewShipped, INTERVAL 1 DAY), NULL);
+ CALL item_getStock(vWarehouseFk, vNewShipped, NULL);
CALL item_getMinacum(
vWarehouseFk,
vNewShipped,
@@ -38,7 +38,7 @@ BEGIN
s.discount,
i.image,
i.subName,
- il.stock + IFNULL(im.amount, 0) AS movable
+ IFNULL(il.stock,0) + IFNULL(im.amount, 0) AS movable
FROM ticket t
JOIN sale s ON s.ticketFk = t.id
JOIN item i ON i.id = s.itemFk
@@ -48,8 +48,8 @@ BEGIN
WHERE t.id = vTicketFk;
DROP TEMPORARY TABLE IF EXISTS
- tmp.itemList,
- tmp.itemMinacum;
+ tmp.itemList,
+ tmp.itemMinacum;
END$$
DELIMITER ;
diff --git a/db/routines/vn/procedures/workerTimeControl_clockIn.sql b/db/routines/vn/procedures/workerTimeControl_clockIn.sql
index e58528487..a1ce53bc2 100644
--- a/db/routines/vn/procedures/workerTimeControl_clockIn.sql
+++ b/db/routines/vn/procedures/workerTimeControl_clockIn.sql
@@ -121,15 +121,17 @@ BEGIN
CALL util.throw(vErrorCode);
END IF;
+
-- DIRECCION CORRECTA
CALL workerTimeControl_direction(vWorkerFk, vTimed);
IF (SELECT
- IF(IF(option1 IN ('inMiddle', 'outMiddle'),
+ IF((IF(option1 IN ('inMiddle', 'outMiddle'),
'middle',
option1) <> vDirection
AND IF(option2 IN ('inMiddle', 'outMiddle'),
'middle',
- IFNULL(option2, '')) <> vDirection,
+ IFNULL(option2, '')) <> vDirection)
+ OR (option1 IS NULL AND option2 IS NULL),
TRUE ,
FALSE)
FROM tmp.workerTimeControlDirection
@@ -137,12 +139,17 @@ BEGIN
SET vIsError = TRUE;
END IF;
- DROP TEMPORARY TABLE tmp.workerTimeControlDirection;
+
IF vIsError THEN
SET vErrorCode = 'WRONG_DIRECTION';
+ IF(SELECT option1 IS NULL AND option2 IS NULL
+ FROM tmp.workerTimeControlDirection) THEN
+
+ SET vErrorCode = 'DAY_MAX_TIME';
+ END IF;
CALL util.throw(vErrorCode);
END IF;
-
+ DROP TEMPORARY TABLE tmp.workerTimeControlDirection;
-- FICHADAS IMPARES
SELECT timed INTO vLastIn
FROM workerTimeControl
diff --git a/db/versions/10992-goldenIvy/00-acl.sql b/db/versions/10992-goldenIvy/00-acl.sql
new file mode 100644
index 000000000..1d1c3ce91
--- /dev/null
+++ b/db/versions/10992-goldenIvy/00-acl.sql
@@ -0,0 +1,2 @@
+INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
+ VALUES ('InvoiceIn', 'exchangeRateUpdate', '*', 'ALLOW', 'ROLE', 'employee');
diff --git a/db/versions/10992-goldenIvy/00-referenceRate.sql b/db/versions/10992-goldenIvy/00-referenceRate.sql
new file mode 100644
index 000000000..db53f328f
--- /dev/null
+++ b/db/versions/10992-goldenIvy/00-referenceRate.sql
@@ -0,0 +1,4 @@
+ALTER TABLE vn.referenceRate DROP INDEX `PRIMARY`;
+ALTER TABLE vn.referenceRate ADD id INT auto_increment PRIMARY KEY;
+ALTER TABLE vn.referenceRate CHANGE id id int(11) auto_increment NOT NULL FIRST;
+CREATE UNIQUE INDEX referenceRate_currencyFk_IDX USING BTREE ON vn.referenceRate (currencyFk,dated);
diff --git a/db/versions/11007-greenRose/00-firstScript.sql b/db/versions/11007-greenRose/00-firstScript.sql
index 69959f0b4..154a75532 100644
--- a/db/versions/11007-greenRose/00-firstScript.sql
+++ b/db/versions/11007-greenRose/00-firstScript.sql
@@ -1,4 +1,3 @@
-
CREATE OR REPLACE TABLE `vn`.`farmingDeliveryNote` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`farmingFk` int(10) unsigned NOT NULL,
@@ -10,3 +9,8 @@ CREATE OR REPLACE TABLE `vn`.`farmingDeliveryNote` (
CONSTRAINT `farmingDeliveryNoteFk_FK` FOREIGN KEY (`deliveryNoteFk`) REFERENCES `deliveryNote` (`id`),
CONSTRAINT `farmingDeliveryNoteFk_FK_1` FOREIGN KEY (`farmingFk`) REFERENCES `farming` (`id`)
);
+
+INSERT IGNORE INTO `vn`.`farmingDeliveryNote` (farmingFk, deliveryNoteFk, amount)
+ SELECT farmingFk, id, amount
+ FROM vn.deliveryNote dn
+ WHERE farmingFk;
\ No newline at end of file
diff --git a/db/versions/11014-orangePalmetto/00-firstScript.sql b/db/versions/11014-orangePalmetto/00-firstScript.sql
new file mode 100644
index 000000000..fe85c7ec6
--- /dev/null
+++ b/db/versions/11014-orangePalmetto/00-firstScript.sql
@@ -0,0 +1,2 @@
+-- Place your SQL code here
+ALTER TABLE vn.claimBeginning MODIFY COLUMN quantity double DEFAULT 0 NULL;
diff --git a/e2e/paths/09-invoice-out/03_manualInvoice.spec.js b/e2e/paths/09-invoice-out/03_manualInvoice.spec.js
index dfaa55ef9..6addbbe56 100644
--- a/e2e/paths/09-invoice-out/03_manualInvoice.spec.js
+++ b/e2e/paths/09-invoice-out/03_manualInvoice.spec.js
@@ -40,7 +40,7 @@ describe('InvoiceOut manual invoice path', () => {
await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm);
- await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceClient, 'Max Eisenhardt');
+ await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceClient, 'Bruce Wayne');
await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceSerial, 'Global nacional');
await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceTaxArea, 'national');
await page.waitToClick(selectors.invoiceOutIndex.saveInvoice);
diff --git a/loopback/locale/es.json b/loopback/locale/es.json
index d7f9564fe..61b2d1425 100644
--- a/loopback/locale/es.json
+++ b/loopback/locale/es.json
@@ -353,5 +353,7 @@
"This password can only be changed by the user themselves": "Esta contraseña solo puede ser modificada por el propio usuario",
"They're not your subordinate": "No es tu subordinado/a.",
"No results found": "No se han encontrado resultados",
- "InvoiceIn is already booked": "La factura recibida está contabilizada"
-}
\ No newline at end of file
+ "InvoiceIn is already booked": "La factura recibida está contabilizada",
+ "Select ticket or client": "Elija un ticket o un client",
+ "It was not able to create the invoice": "No se pudo crear la factura"
+}
diff --git a/modules/claim/back/methods/claim/createFromSales.js b/modules/claim/back/methods/claim/createFromSales.js
index 30093e43d..1af479dbb 100644
--- a/modules/claim/back/methods/claim/createFromSales.js
+++ b/modules/claim/back/methods/claim/createFromSales.js
@@ -83,7 +83,6 @@ module.exports = Self => {
const newClaimBeginning = models.ClaimBeginning.create({
saleFk: sale.id,
claimFk: newClaim.id,
- quantity: sale.quantity
}, myOptions);
promises.push(newClaimBeginning);
diff --git a/modules/claim/back/methods/claim/specs/createFromSales.spec.js b/modules/claim/back/methods/claim/specs/createFromSales.spec.js
index fe009c1c3..25414d1db 100644
--- a/modules/claim/back/methods/claim/specs/createFromSales.spec.js
+++ b/modules/claim/back/methods/claim/specs/createFromSales.spec.js
@@ -37,7 +37,7 @@ describe('Claim createFromSales()', () => {
let claimBeginning = await models.ClaimBeginning.findOne({where: {claimFk: claim.id}}, options);
expect(claimBeginning.saleFk).toEqual(newSale[0].id);
- expect(claimBeginning.quantity).toEqual(newSale[0].quantity);
+ expect(claimBeginning.quantity).toEqual(0);
await tx.rollback();
} catch (e) {
@@ -67,7 +67,7 @@ describe('Claim createFromSales()', () => {
const claimBeginning = await models.ClaimBeginning.findOne({where: {claimFk: claim.id}}, options);
expect(claimBeginning.saleFk).toEqual(newSale[0].id);
- expect(claimBeginning.quantity).toEqual(newSale[0].quantity);
+ expect(claimBeginning.quantity).toEqual(0);
await tx.rollback();
} catch (e) {
diff --git a/modules/claim/back/models/claim-beginning.json b/modules/claim/back/models/claim-beginning.json
index d224586da..ba6e83808 100644
--- a/modules/claim/back/models/claim-beginning.json
+++ b/modules/claim/back/models/claim-beginning.json
@@ -16,8 +16,7 @@
"description": "Identifier"
},
"quantity": {
- "type": "number",
- "required": true
+ "type": "number"
}
},
"relations": {
diff --git a/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js b/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js
new file mode 100644
index 000000000..3ad06b242
--- /dev/null
+++ b/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js
@@ -0,0 +1,64 @@
+const axios = require('axios');
+const {DOMParser} = require('xmldom');
+const UserError = require('vn-loopback/util/user-error');
+
+module.exports = Self => {
+ Self.remoteMethod('exchangeRateUpdate', {
+ description: 'Updates the exchange rates from an XML feed',
+ accessType: 'WRITE',
+ accepts: [],
+ http: {
+ path: '/exchangeRateUpdate',
+ verb: 'post'
+ }
+ });
+
+ Self.exchangeRateUpdate = async() => {
+ const response = await axios.get('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml');
+ const xmlData = response.data;
+
+ const doc = new DOMParser({errorHandler: {warning: () => {}}})?.parseFromString(xmlData, 'text/xml');
+ const cubes = doc?.getElementsByTagName('Cube');
+ if (!cubes || cubes.length === 0)
+ throw new UserError('No cubes found. Exiting the method.');
+
+ const models = Self.app.models;
+
+ const maxDateRecord = await models.ReferenceRate.findOne({order: 'dated DESC'});
+
+ const maxDate = maxDateRecord?.dated ? new Date(maxDateRecord.dated) : null;
+
+ for (const cube of Array.from(cubes)) {
+ if (cube.nodeType === doc.ELEMENT_NODE && cube.attributes.getNamedItem('time')) {
+ const xmlDate = new Date(cube.getAttribute('time'));
+ const xmlDateWithoutTime = new Date(xmlDate.getFullYear(), xmlDate.getMonth(), xmlDate.getDate());
+ if (!maxDate || maxDate < xmlDateWithoutTime) {
+ for (const rateCube of Array.from(cube.childNodes)) {
+ if (rateCube.nodeType === doc.ELEMENT_NODE) {
+ const currencyCode = rateCube.getAttribute('currency');
+ const rate = rateCube.getAttribute('rate');
+ if (['USD', 'CNY', 'GBP'].includes(currencyCode)) {
+ const currency = await models.Currency.findOne({where: {code: currencyCode}});
+ if (!currency) throw new UserError(`Currency not found for code: ${currencyCode}`);
+ const existingRate = await models.ReferenceRate.findOne({
+ where: {currencyFk: currency.id, dated: xmlDate}
+ });
+
+ if (existingRate) {
+ if (existingRate.value !== rate)
+ await existingRate.updateAttributes({value: rate});
+ } else {
+ await models.ReferenceRate.create({
+ currencyFk: currency.id,
+ dated: xmlDate,
+ value: rate
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+};
diff --git a/modules/invoiceIn/back/methods/invoice-in/specs/exchangeRateUpdate.spec.js b/modules/invoiceIn/back/methods/invoice-in/specs/exchangeRateUpdate.spec.js
new file mode 100644
index 000000000..0fd7ea165
--- /dev/null
+++ b/modules/invoiceIn/back/methods/invoice-in/specs/exchangeRateUpdate.spec.js
@@ -0,0 +1,52 @@
+describe('exchangeRateUpdate functionality', function() {
+ const axios = require('axios');
+ const models = require('vn-loopback/server/server').models;
+
+ beforeEach(function() {
+ spyOn(axios, 'get').and.returnValue(Promise.resolve({
+ data: `
+
+
+
+
+ `
+ }));
+ });
+
+ it('should process XML data and update or create rates in the database', async function() {
+ spyOn(models.ReferenceRate, 'findOne').and.returnValue(Promise.resolve(null));
+ spyOn(models.ReferenceRate, 'create').and.returnValue(Promise.resolve());
+
+ await models.InvoiceIn.exchangeRateUpdate();
+
+ expect(models.ReferenceRate.create).toHaveBeenCalledTimes(2);
+ });
+
+ it('should not create or update rates when no XML data is available', async function() {
+ axios.get.and.returnValue(Promise.resolve({}));
+ spyOn(models.ReferenceRate, 'create');
+
+ let thrownError = null;
+ try {
+ await models.InvoiceIn.exchangeRateUpdate();
+ } catch (error) {
+ thrownError = error;
+ }
+
+ expect(thrownError.message).toBe('No cubes found. Exiting the method.');
+ });
+
+ it('should handle errors gracefully', async function() {
+ axios.get.and.returnValue(Promise.reject(new Error('Network error')));
+ let error;
+
+ try {
+ await models.InvoiceIn.exchangeRateUpdate();
+ } catch (e) {
+ error = e;
+ }
+
+ expect(error).toBeDefined();
+ expect(error.message).toBe('Network error');
+ });
+});
diff --git a/modules/invoiceIn/back/models/invoice-in.js b/modules/invoiceIn/back/models/invoice-in.js
index af5efbcdf..31cdc1abe 100644
--- a/modules/invoiceIn/back/models/invoice-in.js
+++ b/modules/invoiceIn/back/models/invoice-in.js
@@ -10,6 +10,7 @@ module.exports = Self => {
require('../methods/invoice-in/invoiceInEmail')(Self);
require('../methods/invoice-in/getSerial')(Self);
require('../methods/invoice-in/corrective')(Self);
+ require('../methods/invoice-in/exchangeRateUpdate')(Self);
Self.rewriteDbError(function(err) {
if (err.code === 'ER_ROW_IS_REFERENCED_2' && err.sqlMessage.includes('vehicleInvoiceIn'))
diff --git a/modules/invoiceOut/back/methods/invoiceOut/createManualInvoice.js b/modules/invoiceOut/back/methods/invoiceOut/createManualInvoice.js
index 043dfbead..c46da0ba5 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/createManualInvoice.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/createManualInvoice.js
@@ -46,12 +46,11 @@ module.exports = Self => {
}
});
- Self.createManualInvoice = async(ctx, options) => {
+ Self.createManualInvoice = async(ctx, clientFk, ticketFk, maxShipped, serial, taxArea, reference, options) => {
+ if (!clientFk && !ticketFk) throw new UserError(`Select ticket or client`);
const models = Self.app.models;
- const args = ctx.args;
-
- let tx;
const myOptions = {userId: ctx.req.accessToken.userId};
+ let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
@@ -61,18 +60,15 @@ module.exports = Self => {
myOptions.transaction = tx;
}
- const ticketId = args.ticketFk;
- let clientId = args.clientFk;
- let maxShipped = args.maxShipped;
let companyId;
let newInvoice;
let query;
try {
- if (ticketId) {
- const ticket = await models.Ticket.findById(ticketId, null, myOptions);
+ if (ticketFk) {
+ const ticket = await models.Ticket.findById(ticketFk, null, myOptions);
const company = await models.Company.findById(ticket.companyFk, null, myOptions);
- clientId = ticket.clientFk;
+ clientFk = ticket.clientFk;
maxShipped = ticket.shipped;
companyId = ticket.companyFk;
@@ -85,7 +81,7 @@ module.exports = Self => {
throw new UserError(`A ticket with an amount of zero can't be invoiced`);
// Validates ticket nagative base
- const hasNegativeBase = await getNegativeBase(maxShipped, clientId, companyId, myOptions);
+ const hasNegativeBase = await getNegativeBase(maxShipped, clientFk, companyId, myOptions);
if (hasNegativeBase && company.code == 'VNL')
throw new UserError(`A ticket with a negative base can't be invoiced`);
} else {
@@ -95,7 +91,7 @@ module.exports = Self => {
const company = await models.Ticket.findOne({
fields: ['companyFk'],
where: {
- clientFk: clientId,
+ clientFk: clientFk,
shipped: {lte: maxShipped}
}
}, myOptions);
@@ -103,7 +99,7 @@ module.exports = Self => {
}
// Validate invoiceable client
- const isClientInvoiceable = await isInvoiceable(clientId, myOptions);
+ const isClientInvoiceable = await isInvoiceable(clientFk, myOptions);
if (!isClientInvoiceable)
throw new UserError(`This client is not invoiceable`);
@@ -114,27 +110,27 @@ module.exports = Self => {
if (maxShipped >= tomorrow)
throw new UserError(`Can't invoice to future`);
- const maxInvoiceDate = await getMaxIssued(args.serial, companyId, myOptions);
+ const maxInvoiceDate = await getMaxIssued(serial, companyId, myOptions);
if (Date.vnNew() < maxInvoiceDate)
throw new UserError(`Can't invoice to past`);
- if (ticketId) {
+ if (ticketFk) {
query = `CALL invoiceOut_newFromTicket(?, ?, ?, ?, @newInvoiceId)`;
await Self.rawSql(query, [
- ticketId,
- args.serial,
- args.taxArea,
- args.reference
+ ticketFk,
+ serial,
+ taxArea,
+ reference
], myOptions);
} else {
query = `CALL invoiceOut_newFromClient(?, ?, ?, ?, ?, ?, @newInvoiceId)`;
await Self.rawSql(query, [
- clientId,
- args.serial,
+ clientFk,
+ serial,
maxShipped,
companyId,
- args.taxArea,
- args.reference
+ taxArea,
+ reference
], myOptions);
}
@@ -146,26 +142,27 @@ module.exports = Self => {
throw e;
}
- if (newInvoice.id)
- await Self.createPdf(ctx, newInvoice.id);
+ if (!newInvoice.id) throw new UserError('It was not able to create the invoice');
+
+ await Self.createPdf(ctx, newInvoice.id);
return newInvoice;
};
- async function isInvoiceable(clientId, options) {
+ async function isInvoiceable(clientFk, options) {
const models = Self.app.models;
const query = `SELECT (hasToInvoice AND isTaxDataChecked) AS invoiceable
FROM client
WHERE id = ?`;
- const [result] = await models.InvoiceOut.rawSql(query, [clientId], options);
+ const [result] = await models.InvoiceOut.rawSql(query, [clientFk], options);
return result.invoiceable;
}
- async function getNegativeBase(maxShipped, clientId, companyId, options) {
+ async function getNegativeBase(maxShipped, clientFk, companyId, options) {
const models = Self.app.models;
await models.InvoiceOut.rawSql('CALL invoiceOut_exportationFromClient(?,?,?)',
- [maxShipped, clientId, companyId], options
+ [maxShipped, clientFk, companyId], options
);
const query = 'SELECT vn.hasAnyNegativeBase() AS base';
const [result] = await models.InvoiceOut.rawSql(query, [], options);
diff --git a/modules/invoiceOut/back/methods/invoiceOut/negativeBases.js b/modules/invoiceOut/back/methods/invoiceOut/negativeBases.js
index 66440616c..fc8830885 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/negativeBases.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/negativeBases.js
@@ -70,7 +70,7 @@ module.exports = Self => {
c.hasToInvoice,
c.isTaxDataChecked,
w.id comercialId,
- CONCAT(w.firstName, ' ', w.lastName) comercialName
+ u.name workerName
FROM vn.ticket t
JOIN vn.company co ON co.id = t.companyFk
JOIN vn.sale s ON s.ticketFk = t.id
diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/createManualInvoice.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/createManualInvoice.spec.js
index b166caf78..55739e570 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/specs/createManualInvoice.spec.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/specs/createManualInvoice.spec.js
@@ -1,13 +1,10 @@
-const models = require('vn-loopback/server/server').models;
+const {models} = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
describe('InvoiceOut createManualInvoice()', () => {
- const userId = 1;
const ticketId = 16;
const clientId = 1106;
- const activeCtx = {
- accessToken: {userId: userId},
- };
+ const activeCtx = {accessToken: {userId: 1}};
const ctx = {req: activeCtx};
it('should throw an error trying to invoice again', async() => {
@@ -18,13 +15,8 @@ describe('InvoiceOut createManualInvoice()', () => {
let error;
try {
- ctx.args = {
- ticketFk: ticketId,
- serial: 'T',
- taxArea: 'CEE'
- };
- await models.InvoiceOut.createManualInvoice(ctx, options);
- await models.InvoiceOut.createManualInvoice(ctx, options);
+ await createInvoice(ctx, options, undefined, ticketId);
+ await createInvoice(ctx, options, undefined, ticketId);
await tx.rollback();
} catch (e) {
@@ -47,17 +39,9 @@ describe('InvoiceOut createManualInvoice()', () => {
let error;
try {
const ticket = await models.Ticket.findById(ticketId, null, options);
- await ticket.updateAttributes({
- totalWithVat: 0
- }, options);
-
- ctx.args = {
- ticketFk: ticketId,
- serial: 'T',
- taxArea: 'CEE'
- };
- await models.InvoiceOut.createManualInvoice(ctx, options);
+ await ticket.updateAttributes({totalWithVat: 0}, options);
+ await createInvoice(ctx, options, undefined, ticketId);
await tx.rollback();
} catch (e) {
error = e;
@@ -75,13 +59,7 @@ describe('InvoiceOut createManualInvoice()', () => {
let error;
try {
- ctx.args = {
- clientFk: clientId,
- serial: 'T',
- taxArea: 'CEE'
- };
- await models.InvoiceOut.createManualInvoice(ctx, options);
-
+ await createInvoice(ctx, options, clientId);
await tx.rollback();
} catch (e) {
error = e;
@@ -103,16 +81,9 @@ describe('InvoiceOut createManualInvoice()', () => {
let error;
try {
const client = await models.Client.findById(clientId, null, options);
- await client.updateAttributes({
- isTaxDataChecked: false
- }, options);
+ await client.updateAttributes({isTaxDataChecked: false}, options);
- ctx.args = {
- ticketFk: ticketId,
- serial: 'T',
- taxArea: 'CEE'
- };
- await models.InvoiceOut.createManualInvoice(ctx, options);
+ await createInvoice(ctx, options, undefined, ticketId);
await tx.rollback();
} catch (e) {
@@ -130,12 +101,7 @@ describe('InvoiceOut createManualInvoice()', () => {
const options = {transaction: tx};
try {
- ctx.args = {
- ticketFk: ticketId,
- serial: 'T',
- taxArea: 'CEE'
- };
- const result = await models.InvoiceOut.createManualInvoice(ctx, options);
+ const result = await createInvoice(ctx, options, undefined, ticketId);
expect(result.id).toEqual(jasmine.any(Number));
@@ -146,3 +112,18 @@ describe('InvoiceOut createManualInvoice()', () => {
}
});
});
+
+function createInvoice(
+ ctx,
+ options,
+ clientFk = undefined,
+ ticketFk = undefined,
+ maxShipped = undefined,
+ serial = 'T',
+ taxArea = 'CEE',
+ reference = undefined
+) {
+ return models.InvoiceOut.createManualInvoice(
+ ctx, clientFk, ticketFk, maxShipped, serial, taxArea, reference, options
+ );
+}
diff --git a/modules/invoiceOut/back/models/cplus-rectification-type.json b/modules/invoiceOut/back/models/cplus-rectification-type.json
index e7bfb957f..06a57ea67 100644
--- a/modules/invoiceOut/back/models/cplus-rectification-type.json
+++ b/modules/invoiceOut/back/models/cplus-rectification-type.json
@@ -15,5 +15,13 @@
"description": {
"type": "string"
}
- }
+ },
+ "acls": [
+ {
+ "accessType": "READ",
+ "principalType": "ROLE",
+ "principalId": "$everyone",
+ "permission": "ALLOW"
+ }
+ ]
}
\ No newline at end of file
diff --git a/modules/invoiceOut/front/negative-bases/index.html b/modules/invoiceOut/front/negative-bases/index.html
index 26f67c7d4..499b6bfe0 100644
--- a/modules/invoiceOut/front/negative-bases/index.html
+++ b/modules/invoiceOut/front/negative-bases/index.html
@@ -114,7 +114,7 @@
- {{::client.comercialName | dashIfEmpty}}
+ {{::client.workerName | dashIfEmpty}}
diff --git a/modules/ticket/back/methods/ticket-request/filter.js b/modules/ticket/back/methods/ticket-request/filter.js
index 10aaf02e5..5364cef9a 100644
--- a/modules/ticket/back/methods/ticket-request/filter.js
+++ b/modules/ticket/back/methods/ticket-request/filter.js
@@ -1,4 +1,3 @@
-
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const buildFilter = require('vn-loopback/util/filter').buildFilter;
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
@@ -135,7 +134,8 @@ module.exports = Self => {
tr.requesterFk,
tr.isOk,
s.quantity saleQuantity,
- s.itemFk,
+ s.itemFk saleItemFk,
+ i.id itemFk,
i.name itemDescription,
t.shipped,
DATE(t.shipped) shippedDate,
diff --git a/modules/ticket/back/methods/ticket/componentUpdate.js b/modules/ticket/back/methods/ticket/componentUpdate.js
index 0786b72c8..8bea731b7 100644
--- a/modules/ticket/back/methods/ticket/componentUpdate.js
+++ b/modules/ticket/back/methods/ticket/componentUpdate.js
@@ -150,7 +150,7 @@ module.exports = Self => {
const salesNewTicket = salesMovable.filter(sale => (sale.movable ? sale.movable : 0) >= sale.quantity);
const salesNewTicketLength = salesNewTicket.length;
- if (salesNewTicketLength && sales.length != salesNewTicketLength) {
+ if (salesNewTicketLength && (args.newTicket || sales.length != salesNewTicketLength)) {
const newTicket = await models.Ticket.transferSales(
ctx,
args.id,
diff --git a/modules/ticket/back/methods/ticket/priceDifference.js b/modules/ticket/back/methods/ticket/priceDifference.js
index 7dc85bd3d..7db03e268 100644
--- a/modules/ticket/back/methods/ticket/priceDifference.js
+++ b/modules/ticket/back/methods/ticket/priceDifference.js
@@ -118,7 +118,7 @@ module.exports = Self => {
const [salesMovable] = await Self.rawSql(query, params, myOptions);
const itemMovable = new Map();
- for (sale of salesMovable) {
+ for (let sale of salesMovable) {
const saleMovable = sale.movable ? sale.movable : 0;
itemMovable.set(sale.id, saleMovable);
}
@@ -129,7 +129,7 @@ module.exports = Self => {
const [difComponents] = await Self.rawSql(query, params, myOptions);
const map = new Map();
- for (difComponent of difComponents)
+ for (let difComponent of difComponents)
map.set(difComponent.saleFk, difComponent);
for (sale of salesObj.items) {
diff --git a/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js b/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js
index d01f0c1bb..e5c06b6dd 100644
--- a/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js
@@ -1,5 +1,4 @@
const models = require('vn-loopback/server/server').models;
-const UserError = require('vn-loopback/util/user-error');
const ForbiddenError = require('vn-loopback/util/forbiddenError');
describe('sale priceDifference()', () => {
@@ -83,12 +82,10 @@ describe('sale priceDifference()', () => {
warehouseId: 1
};
- const result = await models.Ticket.priceDifference(ctx, options);
- const firstItem = result.items[0];
- const secondtItem = result.items[1];
+ const {items} = await models.Ticket.priceDifference(ctx, options);
- expect(firstItem.movable).toEqual(380);
- expect(secondtItem.movable).toEqual(1790);
+ expect(items[0].movable).toEqual(410);
+ expect(items[1].movable).toEqual(1810);
await tx.rollback();
} catch (e) {
diff --git a/modules/worker/back/methods/worker-time-control/specs/clockIn.spec.js b/modules/worker/back/methods/worker-time-control/specs/clockIn.spec.js
index 343eb2a71..daf7284ac 100644
--- a/modules/worker/back/methods/worker-time-control/specs/clockIn.spec.js
+++ b/modules/worker/back/methods/worker-time-control/specs/clockIn.spec.js
@@ -45,7 +45,7 @@ describe('workerTimeControl clockIn()', () => {
throw e;
}
});
-
+
it('should throw an error trying to change a middle hour to out not resting 12h', async() => {
activeCtx.accessToken.userId = HHRRId;
const workerId = teamBossId;
@@ -99,6 +99,32 @@ describe('workerTimeControl clockIn()', () => {
}
});
+ it('should throw an error trying to add an "in" entry if the last clockIn is not out', async() => {
+ activeCtx.accessToken.userId = HHRRId;
+ const workerId = teamBossId;
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ try {
+ const options = {transaction: tx};
+
+ ctx.args = {timed: "2000-12-25T21:00:00.000Z", direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ ctx.args = {timed: "2000-12-25T22:00:00.000Z", direction: 'middle'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ ctx.args = {timed: "2000-12-25T22:30:00.000Z", direction: 'middle'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ ctx.args = {timed: "2000-12-26T01:00:00.000Z", direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ await tx.rollback();
+ } catch (e) {
+ expect(e.message).toBe('Dirección incorrecta');
+ await tx.rollback();
+ }
+ });
+
describe('as Role errors', () => {
it('should add if the current user is team boss and the target user is himself', async() => {
activeCtx.accessToken.userId = teamBossId;