Merge branch 'dev' into 8266-changeExpeditionItemName
gitea/salix/pipeline/pr-dev This commit looks good Details

This commit is contained in:
JOSE ANTONIO TUBAU RODRIGUEZ 2024-12-24 10:55:10 +00:00
commit 0665453465
80 changed files with 1450 additions and 255 deletions

View File

@ -0,0 +1,112 @@
const UserError = require('vn-loopback/util/user-error');
const axios = require('axios');
module.exports = Self => {
Self.remoteMethod('optimize', {
description: 'Return optimized coords',
accessType: 'READ',
accepts: [{
arg: 'addressIds',
type: 'array',
required: true
}, {
arg: 'firstAddressId',
type: 'number',
required: false
}, {
arg: 'lastAddressId',
type: 'number',
required: false
}],
returns: {
type: 'object',
root: true
},
http: {
path: `/optimize`,
verb: 'GET'
}
});
Self.optimize = async(addressIds, firstAddressId, lastAddressId) => {
const models = Self.app.models;
try {
const osrmConfig = await models.OsrmConfig.findOne();
if (!osrmConfig) throw new UserError(`OSRM service is not configured`);
let coords = [];
if (firstAddressId) {
const address = await models.Address.findById(firstAddressId);
if (address.latitude && address.longitude) {
coords.push({
addressId: address.id,
latitude: address.latitude.toFixed(6),
longitude: address.longitude.toFixed(6)
});
}
}
for (const addressId of addressIds) {
const address = await models.Address.findById(addressId);
if (address.latitude && address.longitude) {
coords.push({
addressId,
latitude: address.latitude.toFixed(6),
longitude: address.longitude.toFixed(6)
});
}
}
if (lastAddressId) {
const firstAddress = await models.Address.findById(lastAddressId);
if (firstAddress.latitude && firstAddress.longitude) {
coords.push({
addressId: firstAddress.id,
latitude: firstAddress.latitude.toFixed(6),
longitude: firstAddress.longitude.toFixed(6)
});
}
}
if (!coords.length) throw new UserError('No address has coordinates');
const concatCoords = coords
.map(coord => `${coord.longitude},${coord.latitude}`)
.join(';');
const response = await axios.post(`
${osrmConfig.url}/trip/v1/driving/${concatCoords}?source=first&destination=last&roundtrip=true
`);
const tolerance = osrmConfig.tolerance;
for (const waypoint of response.data.waypoints) {
const longitude = waypoint.location[0];
const latitude = waypoint.location[1];
const matchedAddress = coords.find(coord =>
coord.position === undefined &&
Math.abs(coord.latitude - latitude) <= tolerance &&
Math.abs(coord.longitude - longitude) <= tolerance
);
if (matchedAddress)
matchedAddress.position = waypoint.waypoint_index;
}
coords.sort((a, b) => {
const posA = a.position !== undefined ? a.position : Infinity;
const posB = b.position !== undefined ? b.position : Infinity;
return posA - posB;
});
return coords;
} catch (err) {
switch (err.response?.data?.code) {
case 'NoTrips':
throw new UserError('No trips found because input coordinates are not connected');
case 'NotImplemented':
throw new UserError('This request is not supported');
case 'InvalidOptions':
throw new UserError('Invalid options or too many coordinates');
default:
throw err;
}
}
};
};

View File

@ -0,0 +1,33 @@
const models = require('vn-loopback/server/server').models;
describe('osrmConfig optimize()', function() {
it('should send coords, receive OSRM response, and return a correctly ordered result', async function() {
const result = await models.OsrmConfig.optimize([4, 3], 1, 2);
// Verifications
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(4);
// Check the order
expect(result[0].addressId).toBe(1);
expect(result[1].addressId).toBe(4);
expect(result[2].addressId).toBe(3);
expect(result[3].addressId).toBe(2);
// Check the coordinates format
expect(result[0].latitude).toBe('10.111111');
expect(result[0].longitude).toBe('-74.111111');
});
it('should throw an error if no addresses are provided', async function() {
let error;
try {
await models.OsrmConfig.optimize([], null);
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toBe('No address has coordinates');
});
});

View File

@ -88,6 +88,9 @@
"Language": { "Language": {
"dataSource": "vn" "dataSource": "vn"
}, },
"OsrmConfig": {
"dataSource": "vn"
},
"Machine": { "Machine": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -0,0 +1,4 @@
module.exports = Self => {
require('../methods/osrm-config/optimize')(Self);
};

View File

@ -0,0 +1,24 @@
{
"name": "OsrmConfig",
"base": "VnModel",
"options": {
"mysql": {
"table": "osrmConfig"
}
},
"properties": {
"id": {
"type": "number",
"id": true,
"required": true
},
"url": {
"type": "string",
"required": true
},
"tolerance": {
"type": "number",
"required": false
}
}
}

View File

@ -428,10 +428,10 @@ INSERT INTO `vn`.`clientConfig`(`id`, `riskTolerance`, `maxCreditRows`, `maxPric
INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `provinceFk`, `phone`, `mobile`, `isActive`, `clientFk`, `agencyModeFk`, `longitude`, `latitude`, `isEqualizated`, `isDefaultAddress`) INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `provinceFk`, `phone`, `mobile`, `isActive`, `clientFk`, `agencyModeFk`, `longitude`, `latitude`, `isEqualizated`, `isDefaultAddress`)
VALUES VALUES
(1, 'Bruce Wayne', '1007 Mountain Drive, Gotham', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1101, 2, NULL, NULL, 0, 1), (1, 'Bruce Wayne', '1007 Mountain Drive, Gotham', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1101, 2, -74.1111111, 10.1111111, 0, 1),
(2, 'Petter Parker', '20 Ingram Street', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1102, 2, NULL, NULL, 0, 1), (2, 'Petter Parker', '20 Ingram Street', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1102, 2, -74.2222222, 10.2222222, 0, 1),
(3, 'Clark Kent', '344 Clinton Street', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1103, 2, NULL, NULL, 0, 1), (3, 'Clark Kent', '344 Clinton Street', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1103, 2, -74.3333333, 10.3333333, 0, 1),
(4, 'Tony Stark', '10880 Malibu Point', 'Gotham', 46460, 1, 1111111111, 222222222, 1 , 1104, 2, NULL, NULL, 0, 1), (4, 'Tony Stark', '10880 Malibu Point', 'Gotham', 46460, 1, 1111111111, 222222222, 1 , 1104, 2, -74.4444444, 10.4444444, 0, 1),
(5, 'Max Eisenhardt', 'Unknown Whereabouts', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1105, 2, NULL, NULL, 0, 1), (5, 'Max Eisenhardt', 'Unknown Whereabouts', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1105, 2, NULL, NULL, 0, 1),
(6, 'DavidCharlesHaller', 'Evil hideout', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1106, 2, NULL, NULL, 0, 1), (6, 'DavidCharlesHaller', 'Evil hideout', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1106, 2, NULL, NULL, 0, 1),
(7, 'Hank Pym', 'Anthill', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1107, 2, NULL, NULL, 0, 1), (7, 'Hank Pym', 'Anthill', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1107, 2, NULL, NULL, 0, 1),
@ -462,7 +462,7 @@ INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `pr
(120, 'Somewhere in Montortal', 'address 20', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1109, 2, NULL, NULL, 0, 0), (120, 'Somewhere in Montortal', 'address 20', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1109, 2, NULL, NULL, 0, 0),
(121, 'the bat cave', 'address 21', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1101, 2, NULL, NULL, 0, 0), (121, 'the bat cave', 'address 21', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1101, 2, NULL, NULL, 0, 0),
(122, 'NY roofs', 'address 22', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1102, 2, NULL, NULL, 0, 0), (122, 'NY roofs', 'address 22', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1102, 2, NULL, NULL, 0, 0),
(123, 'The phone box', 'address 23', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1103, 2, NULL, NULL, 0, 0), (123, 'The phone box', 'address 23', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1103, 2, -74.555555, 10.555555, 0, 0),
(124, 'Stark tower Gotham', 'address 24', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1104, 2, NULL, NULL, 0, 0), (124, 'Stark tower Gotham', 'address 24', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1104, 2, NULL, NULL, 0, 0),
(125, 'The plastic cell', 'address 25', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1105, 2, NULL, NULL, 0, 0), (125, 'The plastic cell', 'address 25', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1105, 2, NULL, NULL, 0, 0),
(126, 'Many places', 'address 26', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1106, 2, NULL, NULL, 0, 0), (126, 'Many places', 'address 26', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1106, 2, NULL, NULL, 0, 0),
@ -1520,17 +1520,18 @@ INSERT INTO `vn`.`travel`(`id`,`shipped`, `landed`, `warehouseInFk`, `warehouseO
INSERT INTO `vn`.`entry`(`id`, `supplierFk`, `created`, `travelFk`, `isConfirmed`, `companyFk`, `invoiceNumber`, `reference`, `isExcludedFromAvailable`, `evaNotes`, `typeFk`) INSERT INTO `vn`.`entry`(`id`, `supplierFk`, `created`, `travelFk`, `isConfirmed`, `companyFk`, `invoiceNumber`, `reference`, `isExcludedFromAvailable`, `evaNotes`, `typeFk`)
VALUES VALUES
(1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1, 1, 442, 'IN2001', 'Movement 1', 0, '', 'packaging'), (1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1, 1, 442, 'IN2001', 'Movement 1', 0, '', 'packaging'),
(2, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 2, 0, 442, 'IN2002', 'Movement 2', 0, 'observation two', 'product'), (2, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 2, 0, 442, 'IN2002', 'Movement 2', 0, 'observation two' , 'product'),
(3, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 3, 0, 442, 'IN2003', 'Movement 3', 0, 'observation three', 'product'), (3, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 3, 0, 442, 'IN2003', 'Movement 3', 0, 'observation three', 'product'),
(4, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 2, 0, 69, 'IN2004', 'Movement 4', 0, 'observation four', 'product'), (4, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 2, 0, 69, 'IN2004', 'Movement 4', 0, 'observation four' , 'product'),
(5, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 5, 0, 442, 'IN2005', 'Movement 5', 0, 'observation five', 'product'), (5, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 5, 0, 442, 'IN2005', 'Movement 5', 0, 'observation five' , 'product'),
(6, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 6, 0, 442, 'IN2006', 'Movement 6', 0, 'observation six', 'product'), (6, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 6, 0, 442, 'IN2006', 'Movement 6', 0, 'observation six' , 'product'),
(7, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 7, 0, 442, 'IN2007', 'Movement 7', 0, 'observation seven', 'product'), (7, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 7, 0, 442, 'IN2007', 'Movement 7', 0, 'observation seven', 'product'),
(8, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 7, 0, 442, 'IN2008', 'Movement 8', 1, '', 'product'), (8, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 7, 0, 442, 'IN2008', 'Movement 8', 1, '', 'product'),
(9, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL +2 DAY), 10, 0, 442, 'IN2009', 'Movement 9', 1, '', 'product'), (9, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL +2 DAY), 10, 0, 442, 'IN2009', 'Movement 9', 1, '', 'product'),
(10, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL +2 DAY), 10, 0, 442, 'IN2009', 'Movement 10', 1, '', 'product'), (10, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL +2 DAY), 10, 0, 442, 'IN2009', 'Movement 10',1, '', 'product'),
(99, 69, util.VN_CURDATE() - INTERVAL 1 MONTH, 11, 0, 442, 'IN2009', 'Movement 99', 0, '', 'product'); (11, 4, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1, 1, 442, 'IN2001', 'Movement 11',0, '', 'product'),
(99, 69, util.VN_CURDATE() - INTERVAL 1 MONTH, 11, 0, 442, 'IN2009', 'Movement 99',0, '', 'product');
INSERT INTO `vn`.`entryConfig` (`defaultEntry`, `inventorySupplierFk`, `defaultSupplierFk`) INSERT INTO `vn`.`entryConfig` (`defaultEntry`, `inventorySupplierFk`, `defaultSupplierFk`)
VALUES (2, 4, 1); VALUES (2, 4, 1);
@ -1570,7 +1571,8 @@ INSERT INTO `bs`.`waste`(`buyerFk`, `year`, `week`, `itemFk`, `itemTypeFk`, `sal
(13, 7, 1, 50, 0, 3, 1, 2.000, 2.000, 0.000, 1, 1, 'packing', NULL, 0.00, 99.6, 99.4, 0, 1, 0, 4, util.VN_CURDATE()), (13, 7, 1, 50, 0, 3, 1, 2.000, 2.000, 0.000, 1, 1, 'packing', NULL, 0.00, 99.6, 99.4, 0, 1, 0, 4, util.VN_CURDATE()),
(14, 7, 2, 5, 0, 3, 1, 2.000, 2.000, 0.000, 10, 10, 'grouping', NULL, 0.00, 7.30, 7.00, 0, 1, 0, 4, util.VN_CURDATE()), (14, 7, 2, 5, 0, 3, 1, 2.000, 2.000, 0.000, 10, 10, 'grouping', NULL, 0.00, 7.30, 7.00, 0, 1, 0, 4, util.VN_CURDATE()),
(15, 7, 4, 1.25, 0, 3, 1, 2.000, 2.000, 0.000, 10, 10, 'grouping', NULL, 0.00, 1.75, 1.67, 0, 1, 0, 4, util.VN_CURDATE()), (15, 7, 4, 1.25, 0, 3, 1, 2.000, 2.000, 0.000, 10, 10, 'grouping', NULL, 0.00, 1.75, 1.67, 0, 1, 0, 4, util.VN_CURDATE()),
(16, 99,1,50.0000, 5000, 4, 1, 1.500, 1.500, 0.000, 1, 1, 'packing', NULL, 0.00, 99.60, 99.40, 0, 1, 0, 1.00, '2024-07-30 08:13:51.000'); (16, 99,1,50.0000, 5000, 4, 1, 1.500, 1.500, 0.000, 1, 1, 'packing', NULL, 0.00, 99.60, 99.40, 0, 1, 0, 1.00, '2024-07-30 08:13:51.000'),
(17, 11, 1, 50, 5000, 4, 1, 1.500, 1.500, 0.000, 1, 1, 'packing', NULL, 0.00, 99.6, 99.4, 0, 1, 0, 1, util.VN_CURDATE() - INTERVAL 2 MONTH);
INSERT INTO `hedera`.`order`(`id`, `date_send`, `customer_id`, `delivery_method_id`, `agency_id`, `address_id`, `company_id`, `note`, `source_app`, `confirmed`,`total`, `date_make`, `first_row_stamp`, `confirm_date`) INSERT INTO `hedera`.`order`(`id`, `date_send`, `customer_id`, `delivery_method_id`, `agency_id`, `address_id`, `company_id`, `note`, `source_app`, `confirmed`,`total`, `date_make`, `first_row_stamp`, `confirm_date`)
VALUES VALUES
@ -1920,7 +1922,7 @@ INSERT INTO `vn`.`claimDestination`(`id`, `description`, `addressFk`)
INSERT INTO `vn`.`claimDevelopment`(`id`, `claimFk`, `claimResponsibleFk`, `workerFk`, `claimReasonFk`, `claimResultFk`, `claimRedeliveryFk`, `claimDestinationFk`) INSERT INTO `vn`.`claimDevelopment`(`id`, `claimFk`, `claimResponsibleFk`, `workerFk`, `claimReasonFk`, `claimResultFk`, `claimRedeliveryFk`, `claimDestinationFk`)
VALUES VALUES
(1, 1, 1, 21, 1, 1, 2, 5), (1, 1, 1, 21, 7, 1, 2, 5),
(2, 1, 2, 21, 7, 2, 2, 5), (2, 1, 2, 21, 7, 2, 2, 5),
(3, 2, 7, 21, 9, 3, 2, 5), (3, 2, 7, 21, 9, 3, 2, 5),
(4, 3, 7, 21, 15, 8, 2, 5), (4, 3, 7, 21, 15, 8, 2, 5),
@ -4035,3 +4037,15 @@ INSERT IGNORE INTO vn.saySimpleConfig (url, defaultChannel)
INSERT INTO vn.workerIrpf (workerFk,spouseNif, geographicMobilityDate) INSERT INTO vn.workerIrpf (workerFk,spouseNif, geographicMobilityDate)
VALUES (1106,'26493101E','2019-09-20'); VALUES (1106,'26493101E','2019-09-20');
INSERT INTO vn.referenceRate (currencyFk, dated, value)
VALUES (2, '2000-12-01', 1.0495),
(2, '2001-01-01', 1.0531),
(2, '2001-02-01', 7.6347);
INSERT IGNORE INTO vn.osrmConfig (id,url,tolerance)
VALUES (1,'https://router.project-osrm.org', 0.002);
INSERT IGNORE INTO vn.inventoryConfig
SET id = 1,
supplierFk = 4;

View File

@ -107,7 +107,7 @@ BEGIN
) INTO vHas0Amount; ) INTO vHas0Amount;
IF vHas0Amount THEN IF vHas0Amount THEN
CALL util.throw('Hay líneas vacías. Por favor, elimínelas'); CALL util.throw('orderLinesWithZero');
END IF; END IF;
START TRANSACTION; START TRANSACTION;

View File

@ -4,10 +4,10 @@ BEGIN
/** /**
* Traslada la info de contabilidad relacionada con las facturas recibidas * Traslada la info de contabilidad relacionada con las facturas recibidas
* *
* @vInvoiceInFk Factura recibida * @param vInvoiceInFk Factura recibida
* @vXDiarioFk Id tabla XDiario * @param vXDiarioFk Id tabla XDiario
*/ */
DECLARE vInvoiceInOriginalFk INT; DECLARE vInvoiceInOriginalFk INT;
DECLARE vDone BOOL DEFAULT FALSE; DECLARE vDone BOOL DEFAULT FALSE;
DECLARE vBase DOUBLE; DECLARE vBase DOUBLE;
DECLARE vVat DOUBLE; DECLARE vVat DOUBLE;
@ -25,7 +25,7 @@ BEGIN
DECLARE vIsInformativeExportation BOOL DEFAULT FALSE; DECLARE vIsInformativeExportation BOOL DEFAULT FALSE;
DECLARE vCursor CURSOR FOR DECLARE vCursor CURSOR FOR
SELECT it.taxableBase, SELECT SUM(it.taxableBase),
CAST(SUM((( it.taxableBase / 100) * t.PorcentajeIva)) AS DECIMAL (10,2)), CAST(SUM((( it.taxableBase / 100) * t.PorcentajeIva)) AS DECIMAL (10,2)),
t.PorcentajeIva, t.PorcentajeIva,
it.transactionTypeSageFk, it.transactionTypeSageFk,
@ -205,9 +205,9 @@ BEGIN
WHERE correctingFk = vInvoiceInFk; WHERE correctingFk = vInvoiceInFk;
IF vInvoiceInOriginalFk THEN IF vInvoiceInOriginalFk THEN
UPDATE movContaIVA mci UPDATE movContaIVA mci
JOIN vn.invoiceInRefund iir ON iir.invoiceInRefundFk = vInvoiceInFk JOIN vn.invoiceInCorrection iic ON iic.correctingFk = vInvoiceInFk
JOIN vn.siiTypeInvoiceIn st ON st.id = iic.siiTypeInvoiceInFk
JOIN (SELECT issued, JOIN (SELECT issued,
SUM(sub.taxableBase) taxableBase, SUM(sub.taxableBase) taxableBase,
SUM(ROUND((sub.taxableBase * sub.PorcentajeIva) / 100 , 2)) vat SUM(ROUND((sub.taxableBase * sub.PorcentajeIva) / 100 , 2)) vat
@ -216,7 +216,7 @@ BEGIN
ti.PorcentajeIva ti.PorcentajeIva
FROM vn.invoiceIn i FROM vn.invoiceIn i
JOIN vn.invoiceInTax iit ON iit.invoiceInFk = i.id JOIN vn.invoiceInTax iit ON iit.invoiceInFk = i.id
JOIN sage.TiposIva ti ON ti.CodigoIva = iit.taxTypeSageFk JOIN TiposIva ti ON ti.CodigoIva = iit.taxTypeSageFk
WHERE i.id = vInvoiceInOriginalFk WHERE i.id = vInvoiceInOriginalFk
GROUP BY ti.CodigoIva)sub GROUP BY ti.CodigoIva)sub
)invoiceInOriginal )invoiceInOriginal
@ -229,7 +229,6 @@ BEGIN
mci.CuotaIvaOriginal = invoiceInOriginal.vat, mci.CuotaIvaOriginal = invoiceInOriginal.vat,
mci.ClaveOperacionFactura = co.ClaveOperacionFactura_ mci.ClaveOperacionFactura = co.ClaveOperacionFactura_
WHERE mci.id = vXDiarioFk; WHERE mci.id = vXDiarioFk;
END IF; END IF;
END$$ END$$
DELIMITER ; DELIMITER ;

View File

@ -169,6 +169,7 @@ BEGIN
UPDATE movContaIVA mci UPDATE movContaIVA mci
JOIN vn.invoiceOut i ON i.id = vInvoiceOutCorrectedFk JOIN vn.invoiceOut i ON i.id = vInvoiceOutCorrectedFk
JOIN vn.invoiceCorrection ic ON ic.correctedFk = vInvoiceOutCorrectedFk JOIN vn.invoiceCorrection ic ON ic.correctedFk = vInvoiceOutCorrectedFk
JOIN vn.siiTypeInvoiceOut st ON st.id = ic.siiTypeInvoiceOutFk
JOIN (SELECT SUM(IF(IFNULL(e.vatFk, TRUE), iot.taxableBase, 0)) taxableBase, JOIN (SELECT SUM(IF(IFNULL(e.vatFk, TRUE), iot.taxableBase, 0)) taxableBase,
SUM(IF(IFNULL(e.vatFk, TRUE), iot.vat, 0)) vat, SUM(IF(IFNULL(e.vatFk, TRUE), iot.vat, 0)) vat,
SUM(IF(IFNULL(e.vatFk, TRUE), 0, iot.vat)) equ SUM(IF(IFNULL(e.vatFk, TRUE), 0, iot.vat)) equ
@ -177,8 +178,8 @@ BEGIN
WHERE iot.invoiceOutFk = vInvoiceOutCorrectedFk WHERE iot.invoiceOutFk = vInvoiceOutCorrectedFk
) tax ) tax
JOIN ClavesOperacion co ON co.Descripcion = 'Factura rectificativa' JOIN ClavesOperacion co ON co.Descripcion = 'Factura rectificativa'
SET mci.TipoRectificativa = 2, SET mci.TipoRectificativa = ic.cplusRectificationTypeFk,
mci.ClaseAbonoRectificativas = 1, mci.ClaseAbonoRectificativas = REGEXP_REPLACE(st.`code`, '[^0-9]', ''),
mci.FechaFacturaOriginal = i.issued, mci.FechaFacturaOriginal = i.issued,
mci.FechaOperacion = i.issued, mci.FechaOperacion = i.issued,
mci.BaseImponibleOriginal = tax.taxableBase, mci.BaseImponibleOriginal = tax.taxableBase,

View File

@ -11,6 +11,7 @@ BEGIN
DECLARE vCurrentCommission INT; DECLARE vCurrentCommission INT;
DECLARE vIsNotEUR INT; DECLARE vIsNotEUR INT;
DECLARE vLastEntryFk INT; DECLARE vLastEntryFk INT;
DECLARE vLanded INT;
SELECT count(*) INTO vIsNotEUR SELECT count(*) INTO vIsNotEUR
FROM currency c FROM currency c
@ -26,23 +27,25 @@ BEGIN
RETURN IFNULL(vCommission, 0); RETURN IFNULL(vCommission, 0);
ELSE ELSE
SELECT landed INTO vLanded
FROM travel
WHERE id = vTravelFk;
SELECT e.id INTO vLastEntryFk SELECT e.id INTO vLastEntryFk
FROM `entry` e FROM `entry` e
JOIN travel tr ON tr.id = e.travelFk JOIN travel tr ON tr.id = e.travelFk
WHERE e.supplierFk = vSupplierFk WHERE e.supplierFk = vSupplierFk
ORDER BY tr.landed DESC ORDER BY (vLanded <= tr.landed), tr.landed DESC
LIMIT 1; LIMIT 1;
IF vLastEntryFk THEN IF vLastEntryFk THEN
SELECT commission INTO vCurrentCommission SELECT commission INTO vCurrentCommission
FROM `entry` FROM `entry`
WHERE id = vLastEntryFk; WHERE id = vLastEntryFk;
ELSE ELSE
SELECT commission INTO vCurrentCommission SELECT commission INTO vCurrentCommission
FROM supplier s FROM supplier s
WHERE s.id = vSupplierFk; WHERE s.id = vSupplierFk;
END IF; END IF;
RETURN vCurrentCommission; RETURN vCurrentCommission;

View File

@ -46,7 +46,7 @@ BEGIN
JOIN claimDestination cd ON cd.id = ce.claimDestinationFk JOIN claimDestination cd ON cd.id = ce.claimDestinationFk
JOIN claim c ON c.id = ce.claimFk JOIN claim c ON c.id = ce.claimFk
JOIN claimState cs ON cs.id = c.claimStateFk JOIN claimState cs ON cs.id = c.claimStateFk
WHERE cd.description NOT IN ('Bueno', 'Corregido') WHERE cd.code NOT IN ('good', 'corrected', 'supplierClaim')
AND NOT ce.isGreuge AND NOT ce.isGreuge
AND cs.code = 'resolved'; AND cs.code = 'resolved';
@ -71,7 +71,7 @@ BEGIN
JOIN claimDestination cd ON cd.id = ce.claimDestinationFk JOIN claimDestination cd ON cd.id = ce.claimDestinationFk
JOIN claim c ON c.id = ce.claimFk JOIN claim c ON c.id = ce.claimFk
JOIN claimState cs ON cs.id = c.claimStateFk JOIN claimState cs ON cs.id = c.claimStateFk
WHERE cd.description NOT IN ('Bueno', 'Corregido') WHERE cd.code NOT IN ('good', 'corrected', 'supplierClaim')
AND NOT ce.isGreuge AND NOT ce.isGreuge
AND cs.code = 'resolved' AND cs.code = 'resolved'
AND c.isChargedToMana; AND c.isChargedToMana;
@ -82,7 +82,7 @@ BEGIN
JOIN claim c ON c.id = ce.claimFk JOIN claim c ON c.id = ce.claimFk
JOIN claimState cs ON cs.id = c.claimStateFk JOIN claimState cs ON cs.id = c.claimStateFk
SET ce.isGreuge = TRUE SET ce.isGreuge = TRUE
WHERE cd.description NOT IN ('Bueno', 'Corregido') WHERE cd.code NOT IN ('good', 'corrected', 'supplierClaim')
AND NOT ce.isGreuge AND NOT ce.isGreuge
AND cs.code = 'resolved'; AND cs.code = 'resolved';
@ -161,7 +161,7 @@ BEGIN
JOIN claimDestination cd ON cd.id = ce.claimDestinationFk JOIN claimDestination cd ON cd.id = ce.claimDestinationFk
JOIN claim c ON c.id = ce.claimFk JOIN claim c ON c.id = ce.claimFk
JOIN claimState cs ON cs.id = c.claimStateFk JOIN claimState cs ON cs.id = c.claimStateFk
WHERE cd.description NOT IN ('Bueno', 'Corregido') WHERE cd.code NOT IN ('good', 'corrected', 'supplierClaim')
AND cs.code = 'resolved' AND cs.code = 'resolved'
AND c.ticketCreated >= util.VN_CURDATE() - INTERVAL 1 YEAR AND c.ticketCreated >= util.VN_CURDATE() - INTERVAL 1 YEAR
GROUP BY c.clientFk GROUP BY c.clientFk

View File

@ -1,48 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`inventoryFailureAdd`()
BEGIN
DECLARE done BOOL DEFAULT FALSE;
DECLARE vTicketFk INT;
DECLARE rs CURSOR FOR
SELECT id FROM vn.ticket
WHERE shipped = util.yesterday()
AND clientFk = 400
AND warehouseFk IN (1,44);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN rs;
FETCH rs INTO vTicketFk;
WHILE NOT done DO
INSERT INTO vn.inventoryFailure(dated, itemFk, quantity, value, warehouseFk, throwerFk)
SELECT t.shipped,
s.itemFk,
s.quantity,
b.buyingValue + b.freightValue + b.packageValue + b.comissionValue,
t.warehouseFk,
w.id
FROM vn.ticket t
JOIN vn.sale s ON s.ticketFk = t.id
LEFT JOIN cache.last_buy lb ON lb.warehouse_id = t.warehouseFk AND item_id = s.itemFk
LEFT JOIN vn.buy b ON b.id = lb.buy_id
LEFT JOIN vn.worker w ON w.code = LEFT(s.concept, 3)
WHERE t.id = vTicketFk
AND s.quantity > 0;
FETCH rs INTO vTicketFk;
END WHILE;
CLOSE rs;
END$$
DELIMITER ;

View File

@ -43,7 +43,7 @@ BEGIN
ii.cplusTaxBreakFk, ii.cplusTaxBreakFk,
ii.cplusSubjectOpFk, ii.cplusSubjectOpFk,
ii.siiTypeInvoiceInFk, ii.siiTypeInvoiceInFk,
ii.cplusRectificationTypeFk, ic.cplusRectificationTypeFk,
ii.booked, ii.booked,
IFNULL(a.isUeeMember, c.isUeeMember) isUeeMember, IFNULL(a.isUeeMember, c.isUeeMember) isUeeMember,
(c.id = cc.id) isSameCountry, (c.id = cc.id) isSameCountry,
@ -66,6 +66,7 @@ BEGIN
e.name expenseName e.name expenseName
FROM invoiceIn ii FROM invoiceIn ii
JOIN supplier s ON s.id = ii.supplierFk JOIN supplier s ON s.id = ii.supplierFk
LEFT JOIN invoiceInCorrection ic ON ic.correctingFk = ii.id
LEFT JOIN province p ON p.id = s.provinceFk LEFT JOIN province p ON p.id = s.provinceFk
LEFT JOIN autonomy a ON a.id = p.autonomyFk LEFT JOIN autonomy a ON a.id = p.autonomyFk
JOIN country c ON c.id = s.countryFk JOIN country c ON c.id = s.countryFk

View File

@ -164,10 +164,6 @@ BEGIN
SET itemFk = vItemNew SET itemFk = vItemNew
WHERE itemFk = vItemOld; WHERE itemFk = vItemOld;
UPDATE inventoryFailure
SET itemFk = vItemNew
WHERE itemFk = vItemOld;
UPDATE genericAllocation UPDATE genericAllocation
SET itemFk = vItemNew SET itemFk = vItemNew
WHERE itemFk = vItemOld; WHERE itemFk = vItemOld;

View File

@ -52,7 +52,8 @@ BEGIN
IFNULL(dest.nickname, origin.nickname) nickname, IFNULL(dest.nickname, origin.nickname) nickname,
dest.landed, dest.landed,
dest.preparation, dest.preparation,
origin.departmentFk origin.departmentFk,
origin.saleClonedFk
FROM ( FROM (
SELECT s.ticketFk, SELECT s.ticketFk,
c.salesPersonFk workerFk, c.salesPersonFk workerFk,
@ -73,11 +74,13 @@ BEGIN
t.warehouseFk, t.warehouseFk,
t.companyFk, t.companyFk,
t.agencyModeFk, t.agencyModeFk,
wd.departmentFk wd.departmentFk,
sc.saleClonedFk
FROM ticket t FROM ticket t
JOIN client c ON c.id = t.clientFk JOIN client c ON c.id = t.clientFk
JOIN workerDepartment wd ON wd.workerFk = c.salesPersonFk JOIN workerDepartment wd ON wd.workerFk = c.salesPersonFk
JOIN sale s ON s.ticketFk = t.id JOIN sale s ON s.ticketFk = t.id
LEFT JOIN saleCloned sc ON sc.saleClonedFk = s.id
JOIN saleVolume sv ON sv.saleFk = s.id JOIN saleVolume sv ON sv.saleFk = s.id
JOIN item i ON i.id = s.itemFk JOIN item i ON i.id = s.itemFk
JOIN ticketState ts ON ts.ticketFk = t.id JOIN ticketState ts ON ts.ticketFk = t.id

View File

@ -10,8 +10,8 @@ BEGIN
* @param vIsRaid idRaid value * @param vIsRaid idRaid value
* @param vDaysInForward daysInForward value * @param vDaysInForward daysInForward value
*/ */
IF (NOT vIsRaid AND vDaysInForward IS NOT NULL) OR (vIsRaid AND vDaysInForward IS NULL) THEN IF NOT vIsRaid AND vDaysInForward THEN
CALL util.throw('The raid information is not correct'); CALL util.throw('If daysInForward has a value, the raid cannot be unchecked');
END IF; END IF;
END$$ END$$
DELIMITER ; DELIMITER ;

View File

@ -8,22 +8,15 @@ BEGIN
DECLARE vDone BOOL DEFAULT FALSE; DECLARE vDone BOOL DEFAULT FALSE;
DECLARE vBuyerEmail VARCHAR(40); DECLARE vBuyerEmail VARCHAR(40);
DECLARE vTravelLink TEXT; DECLARE vTravelLink TEXT;
DECLARE vMailBody TEXT DEFAULT ''; DECLARE vMailBody TEXT;
DECLARE vDaysBetweenDates INT;
DECLARE vSubject VARCHAR(30);
DECLARE vCur CURSOR FOR DECLARE vTravels CURSOR FOR
SELECT GROUP_CONCAT(DISTINCT SELECT GROUP_CONCAT(DISTINCT travelLink ORDER BY id SEPARATOR '\n\r'),
CONCAT('https://salix.verdnatura.es/#!/travel/', buyerEmail
ttm.travelFk, FROM tTravelToMove
'/summary ') GROUP BY buyerEmail;
ORDER BY ttm.travelFk SEPARATOR '\n\r') travelLink,
CONCAT(u.name, '@verdnatura.es') buyerEmail
FROM tTravelToMove ttm
JOIN entry e ON e.travelFk = ttm.travelFk
JOIN buy b ON b.entryFk = e.id
JOIN item i ON i.id = b.itemFk
JOIN itemType it ON it.id = i.typeFk
JOIN account.user u ON u.id = it.workerFk
GROUP BY u.name;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE; DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
@ -34,35 +27,50 @@ BEGIN
END; END;
CREATE OR REPLACE TEMPORARY TABLE tTravelToMove CREATE OR REPLACE TEMPORARY TABLE tTravelToMove
SELECT id travelFk, WITH travels AS (
util.VN_CURDATE() + INTERVAL daysInForward DAY newLanded SELECT id,
FROM travel CONCAT('https://salix.verdnatura.es/#!/travel/', id,'/summary') travelLink,
util.VN_CURDATE() + INTERVAL daysInForward DAY newLanded,
util.VN_CURDATE() - INTERVAL DATEDIFF(landed, shipped) + daysInForward DAY newShipped
FROM vn.travel
WHERE isRaid WHERE isRaid
AND daysInForward; AND daysInForward
)SELECT t.id,
t.travelLink,
t.newLanded,
t.newShipped,
CONCAT(u.name, '@verdnatura.es') buyerEmail
FROM travels t
STRAIGHT_JOIN vn.entry e ON e.travelFk = t.id
JOIN vn.buy b ON b.entryFk = e.id
JOIN vn.item i ON i.id = b.itemFk
JOIN vn.itemType it ON it.id = i.typeFk
JOIN account.user u ON u.id = it.workerFk
GROUP BY t.id;
START TRANSACTION; START TRANSACTION;
UPDATE travel tr UPDATE travel tr
JOIN tTravelToMove ttm ON ttm.travelFk = tr.id JOIN tTravelToMove ttm ON ttm.id = tr.id
SET tr.landed = ttm.newLanded; SET tr.landed = ttm.newLanded,
tr.shipped = ttm.newShipped;
OPEN vCur; OPEN vTravels;
l: LOOP l: LOOP
SET vDone = FALSE; SET vDone = FALSE;
FETCH vCur INTO vTravelLink, vBuyerEmail; FETCH vTravels INTO vTravelLink, vBuyerEmail;
IF vDone THEN IF vDone THEN
LEAVE l; LEAVE l;
END IF; END IF;
CALL `vn`.`mail_insert`( SET vSubject = 'Cambio de fecha en Redadas',
vBuyerEmail, vMailBody = CONCAT('Se ha movido los siguientes travels: \n\r ', vTravelLink);
'noreply@verdnatura.es',
'Cambio de fecha en Redadas', CALL mail_insert(vBuyerEmail, 'noreply@verdnatura.es', vSubject, vMailBody);
CONCAT('Se ha movido los siguientes travels: \n\r ', vTravelLink));
END LOOP; END LOOP;
CLOSE vCur; CLOSE vTravels;
COMMIT; COMMIT;
DROP TEMPORARY TABLE tTravelToMove; DROP TEMPORARY TABLE tTravelToMove;
END$$ END$$

View File

@ -3,24 +3,30 @@ CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`invoiceIn_afterUpdate`
AFTER UPDATE ON `invoiceIn` AFTER UPDATE ON `invoiceIn`
FOR EACH ROW FOR EACH ROW
BEGIN BEGIN
IF NEW.issued != OLD.issued DECLARE vIsEuro BOOL;
OR NEW.currencyFk != OLD.currencyFk THEN
SELECT `code` = 'EUR' INTO vIsEuro
FROM currency
WHERE id = NEW.currencyFk;
IF (NOT NEW.issued <=> OLD.issued
OR NEW.currencyFk <> OLD.currencyFk) THEN
UPDATE invoiceInTax iit UPDATE invoiceInTax iit
JOIN invoiceIn ii ON ii.id = iit.invoiceInFk JOIN invoiceIn ii ON ii.id = iit.invoiceInFk
LEFT JOIN referenceRate rr ON rr.dated = ii.issued LEFT JOIN referenceRate rr ON rr.dated = ii.issued
AND rr.currencyFk = ii.currencyFk AND rr.currencyFk = ii.currencyFk
SET iit.taxableBase = IF(iit.foreignValue IS NULL, iit.taxableBase, iit.foreignValue / rr.value) SET iit.taxableBase = IF(vIsEuro, iit.taxableBase, iit.foreignValue / rr.value),
iit.foreignValue = IF(vIsEuro, NULL, iit.foreignValue)
WHERE ii.id = NEW.id; WHERE ii.id = NEW.id;
UPDATE invoiceInDueDay iidd UPDATE invoiceInDueDay iidd
JOIN invoiceIn ii ON ii.id = iidd.invoiceInFk JOIN invoiceIn ii ON ii.id = iidd.invoiceInFk
LEFT JOIN referenceRate rr ON rr.dated = ii.issued LEFT JOIN referenceRate rr ON rr.dated = ii.issued
AND rr.currencyFk = ii.currencyFk AND rr.currencyFk = ii.currencyFk
SET iidd.amount = IF(iidd.foreignValue IS NULL, iidd.amount, iidd.foreignValue / rr.value) SET iidd.amount = IF(vIsEuro, iidd.amount, iidd.foreignValue / rr.value),
iidd.foreignValue = IF(vIsEuro, NULL, iidd.foreignValue)
WHERE ii.id = NEW.id; WHERE ii.id = NEW.id;
END IF; END IF;
END$$ END$$
DELIMITER ; DELIMITER ;

View File

@ -20,6 +20,10 @@ BEGIN
CALL travel_checkWarehouseIsFeedStock(NEW.warehouseInFk); CALL travel_checkWarehouseIsFeedStock(NEW.warehouseInFk);
END IF; END IF;
IF NOT (NEW.isRaid <=> OLD.isRaid) OR NOT (NEW.daysInForward <=> OLD.daysInForward) THEN
CALL travel_checkRaid(NEW.isRaid, NEW.daysInForward);
END IF;
IF NOT (NEW.awbFk <=> OLD.awbFk)THEN IF NOT (NEW.awbFk <=> OLD.awbFk)THEN
SELECT COUNT(*) INTO vHasAnyInvoiceBooked SELECT COUNT(*) INTO vHasAnyInvoiceBooked
FROM travel t FROM travel t

View File

@ -0,0 +1,11 @@
INSERT INTO hedera.message (code, description)
VALUES ('orderLinesWithZero','There are empty lines. Please delete them');
INSERT INTO hedera.messageI18n (code, lang, description)
VALUES ('orderLinesWithZero','es','Hay líneas vacías. Por favor, elimínelas');
INSERT INTO hedera.messageI18n (code, lang, description)
VALUES ('orderLinesWithZero','fr','Il y a des lignes vides. Veuillez les supprimer');
INSERT INTO hedera.messageI18n (code, lang, description)
VALUES ('orderLinesWithZero','pt','Existem linhas vazias. Por favor, apague-os');

View File

@ -0,0 +1,185 @@
CREATE TABLE IF NOT EXISTS `vn`.`sim` (
`code` VARCHAR(25) COMMENT 'No se ha puesto BIGINT por incompatibilidad con Access',
`line` VARCHAR(15) NOT NULL CHECK (`line` REGEXP '^[0-9]+$'),
`ext` INT(4) NOT NULL,
`pin` VARCHAR(4) NOT NULL CHECK (`pin` REGEXP '^[0-9]+$'),
`puk` VARCHAR(15) NOT NULL CHECK (`pin` REGEXP '^[0-9]+$'),
PRIMARY KEY (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
ALTER TABLE vn.deviceProductionUser CHANGE simSerialNumber simFk VARCHAR(25) DEFAULT NULL NULL;
ALTER TABLE vn.deviceProductionUser MODIFY COLUMN simFk VARCHAR(25) DEFAULT NULL NULL;
INSERT IGNORE INTO `vn`.`sim` (`line`, `ext`, `pin`, `code`, `puk`) VALUES
('621188151', 2209, '1486', '3456985220092508','14213470'),
('621188152', 2210, '8765', '3456985220092509','99473093'),
('621188153', 2211, '3064', '3456985220092510','52967210'),
('621188759', 2081, '3700', '3456985220123637','56600999'),
('621188760', 2082, '3259', '345698522023638','87492404'),
('621188761', 2083, '2790', '3456985220123639','94009456'),
('621188762', 2084, '2480', '3456985220123644','1484999'),
('621188763', 2085, '6876', '3456985220123641','36577064'),
('621188766', 2086, '7775', '3456985220123642','80761698'),
('621188769', 2088, '4027', '3456985220123643','37921712'),
('621188771', 2089, '8797', '3456985220123640','63092540'),
('621188772', 2090, '8404', '3456985220123645','21014997'),
('621188773', 2091, '5481', '3456985220123646','16317277'),
('621188774', 2092, '9632', '3456985220123647','22235994'),
('621188775', 2093, '4654', '3456985220123648','28506486'),
('621188838', 2094, '1392', '3456985220123649','29498627'),
('621188839', 2095, '7774', '3456985220123650','46263490'),
('621188840', 2096, '7304', '3456985220123658','8212044'),
('621188841', 2097, '5569', '3456985220123652','81597658'),
('621188842', 2098, '4944', '3456985220123653','24961501'),
('621188843', 2099, '5142', '3456985220123654','17035634'),
('621188844', 2111, '7245', '3456985220123655','90231951'),
('621188846', 2110, '6590', '3456985220123656','72201537'),
('667680207', 2564, '4042', '34569832200759166','48401979'),
('667680315', 2565, '7143', '34569832200759372','32143252'),
('667680318', 2566, '6342', '34569832200759364','39597112'),
('667680413', 2567, '5580', '34569832200759356','32786992'),
('667680463', 2568, '0171', '34569832200759349','34240853'),
('667688217', 2569, '2500', '34569832200759331','5687589'),
('633603945', 2212, '7129', '34569832200759323','51554019'),
('622130186', 2213, '4826', '34569832200759307','19623551'),
('633973424', 2214, '8535', '34569832200759299','94619307'),
('633703828', 2215, '8628', '34569832200759281','22468012'),
('622025110', 2216, '2399', '34569832200759273','34602918'),
('622924867', 2217, '5665', '34569832200759265','26920216'),
('722409630', 2218, '5211', '34569832200759240','93750137'),
('623590529', 2219, '0493', '34569832200759208','47077088'),
('633243462', 2220, '6902', '34569832200759174','6421962'),
('633047286', 2221, '5592', '34569832200759182','32069439'),
('744716801', 2112, '9184', '34569832200759190','57049814'),
('655995021', 2131, '8896', '34569852202049093','19497356'),
('685522718', 2132, '1955', '34569852202049101','28519879'),
('674587213', 2994, '2006', '34569332200223743','62360135'),
('674587227', 2993, '9271', '34569332200223750','81628192'),
('674587229', 2993, '0900', '34569332200223768','91119071'),
('674587231', 2992, '5007', '34569332200223776','45826232'),
('674587234', 2991, '1378', '34569332200223784','91245744'),
('674587240', 2990, '0905', '34569332200223792','13083224'),
('674587245', 2989, '9059', '34569332200223800','15291807'),
('674587250', 2988, '8188', '34569332200223818','83017918'),
('674587254', 2987, '2962', '34569332200223826','92809271'),
('674587256', 2986, '0358', '34569332200223834','81067040'),
('674592713', 2570, '2537', '34569332200230672','82325850'),
('697832478', 2579, '0936', '34568732200494825','49658372'),
('697832176', 2571, '5944', '34568732200494742','19039461'),
('697832477', 2572, '5138', '34568732200494759','25712504'),
('697832178', 2573, '4597', '34568732200494767','66241760'),
('697832182', 2574, '9241', '34568732200494775','07342562'),
('697832196', 2575, '2995', '34568732200494783','53929026'),
('697832214', 2576, '7434', '34568732200494791','49698432'),
('697832230', 2577, '7004', '34568732200494809','21578612'),
('697832235', 2578, '9674', '34568732200494817','93090700'),
('673420375', 2599, '5430', '34562052300117259','35911412'),
('673420367', 2598, '8402', '34562052300117242','924654'),
('673420361', 2597, '5125', '34562052300117234','12027970'),
('673420355', 2596, '5069', '34562052300117226','34978149'),
('673420348', 2595, '8911', '34562052300117218','4228121'),
('673420346', 2594, '2461', '34562052300117200','67670772'),
('673420345', 2593, '2226', '34562052300117192','90586404'),
('673420306', 2592, '3355', '34562052300117184','97850017'),
('673420257', 2591, '9395', '34562052300117176','50713786'),
('673420231', 2590, '1378', '34562052300117168','50151763'),
('673420223', 2589, '9580', '34562052300117150','99534550'),
('673420216', 2588, '4955', '34562052300117143','317554'),
('673420203', 2587, '6742', '34562052300117135','69321531'),
('673420201', 2586, '1659', '34562052300117127','54720480'),
('673420199', 2585, '7823', '34562052300117119','22923796'),
('673420198', 2584, '1787', '34562052300117101','54414630'),
('673420168', 2583, '6334', '34562052300117093','50694894'),
('673420147', 2582, '8951', '34562052300117085','1402535'),
('673420125', 2581, '3068', '34562052300117077','86216200'),
('673420124', 2580, '9517', '34562052300117069','42504099'),
('600294609', 2715, '7474', '34569832304894588','55923317'),
('600084713', 2703, '8342', '34569832304894570','8392636'),
('600084732', 2704, '1625', '34569832304894513','75477452'),
('600084850', 2705, '9896', '34569832304894653','28589813'),
('600084951', 2706, '5520', '34569832304894661','75353012'),
('600084978', 2707, '2698', '34569832304894679','9005523'),
('600085403', 2708, '0837', '34569832304894646','77051152'),
('600085513', 2709, '3106', '34569832304894687','41571002'),
('600293916', 2712, '8990', '34569832304894620','95188676'),
('600294160', 2714, '6376', '34569832304894703','79879896'),
('671919529', 2975, '9184', '34569832304806236','7535392'),
('671919942', 2981, '0328', '34569832304806269','31052894'),
('671919530', 2976, '0344', '34569832304806251','89860304'),
('671919533', 2977, '0668', '34569832304806244','42921771'),
('671919535', 2978, '0105', '34569832304806277','31009417'),
('671919537', 2979, '0881', '34569832304806285','33479769'),
('671919540', 2980, '9874', '34569832304806293','14103929'),
('671919525', 2972, '2089', '34569832304806301','45903729'),
('671919527', 2973, '8206', '34569832304806368','1586035'),
('671919528', 2974, '2532', '34569832304806327','62310124'),
('673668717', 2836, '7973', '34562032301044223','15635496'),
('673668734', 2837, '4457', '34562032301044231','18313118'),
('673668738', 2824, '2911', '34562032301044249','30875583'),
('673668745', 2838, '7253', '34562032301044256','62754222'),
('673668796', 2839, '0068', '34562032301044264','15556829'),
('673668803', 2840, '2386', '34562032301044272','17572287'),
('673669591', 2850, '3833', '34562032301044280','34828896'),
('673668808', 2841, '3584', '34562032301044298','16234497'),
('673670102', 2851, '3554', '34562032301044306','23652625'),
('673670131', 2852, '4412', '34562032301044314','88611709'),
('673670135', 2827, '6058', '34562032301044322','53918579'),
('673670201', 2828, '8066', '34562032301044330','92369343'),
('673670225', 2829, '4592', '34562032301044348','24126635'),
('673670236', 2830, '2974', '34562032301044355','88608465'),
('673671485', 2849, '0349', '34562032301044363','44944874'),
('673461977', 2871, '1728', '34562032400157090','46975780'),
('673461975', 2870, '4734', '34562032400157082','69628432'),
('673461972', 2867, '6276', '34562032400157058','53338365'),
('673461979', 2872, '6043', '34562032400157108','36525197'),
('673461958', 2859, '3164', '34562032400156977','58947831'),
('673461957', 2857, '8685', '34562032400156969','15826386'),
('673461944', 2853, '1073', '34562032400156910','20452195'),
('673461974', 2869, '7121', '34562032400157074','32044645'),
('673461973', 2868, '8022', '34562032400157066','29282044'),
('673461971', 2866, '3089', '34562032400157041','66149978'),
('673461969', 2865, '7555', '34562032400157033','78391293'),
('673461960', 2860, '5203', '34562032400156985','37138232'),
('673461952', 2855, '6915', '34562032400156936','62724661'),
('673461949', 2854, '8706', '34562032400156928','5594345'),
('673461966', 2863, '2496', '34562032400157017','93450666'),
('673461968', 2864, '3703', '34562032400157025','23208841'),
('673461963', 2862, '9364', '34562032400157009','29712130'),
('673462719', 2873, '9387', '34562032400156951','50434348'),
('673461962', 2861, '8441', '34562032400156993','39686909'),
('673461956', 2826, '5392', '34562032400156944','5496107'),
('673465284', 2694, '1523', '34562032400171349','14554994'),
('673465282', 2692, '4645', '34562032400171323','24871187'),
('673465283', 2693, '5253', '34562032400171331','28303238'),
('673465841', 2696, '0849', '34562032400171257','21673222'),
('673465258', 2679, '4140', '34562032400171174','39793881'),
('673465263', 2680, '6922', '34562032400171182','12253261'),
('673465265', 2681, '9112', '34562032400171190','93894366'),
('673465267', 2682, '3259', '34562032400171208','2342189'),
('673465268', 2683, '8540', '34562032400171216','63886925'),
('673465285', 2695, '4167', '34562032400171356','79227618'),
('673465270', 2684, '4292', '34562032400171224','19216349'),
('673465272', 2685, '4007', '34562032400171232','14396903'),
('673465273', 2686, '6894', '34562032400171240','13569394'),
('673465274', 2687, '5268', '34562032400171265','59453667'),
('673465275', 2688, '0232', '34562032400171273','62324713'),
('673465276', 2689, '2720', '34562032400171281','65977200'),
('673465843', 2698, '4773', '34562032400171364','78387158'),
('673465842', 2697, '3729', '34562032400171315','94201789'),
('673465280', 2691, '0503', '34562032400171307','12298533'),
('673465279', 2690, '8239', '34562032400171299','76183877');
UPDATE vn.deviceProductionUser
SET simFk = NULL
WHERE id IN (
SELECT dpu.id
FROM vn.deviceProductionUser dpu
LEFT JOIN vn.sim s ON s.code = dpu.simFk
WHERE s.code IS NULL
AND dpu.simFk IS NOT NULL
);
ALTER TABLE vn.deviceProductionUser ADD CONSTRAINT deviceProductionUser_sim_FK
FOREIGN KEY (simFk) REFERENCES vn.sim(code) ON DELETE RESTRICT ON UPDATE CASCADE;
GRANT SELECT, INSERT, DELETE, UPDATE ON TABLE vn.sim TO hr;

View File

@ -0,0 +1,3 @@
-- Place your SQL code here
INSERT IGNORE INTO salix.ACL (model, property, accessType, permission, principalType, principalId)
VALUES('InventoryConfig', 'find', 'READ', 'ALLOW', 'ROLE', 'buyer');

View File

@ -0,0 +1,48 @@
USE vn;
DROP TRIGGER IF EXISTS invoiceIn_beforeUpdate;
UPDATE invoiceIn
SET cplusRectificationTypeFk = NULL
WHERE cplusRectificationTypeFk = 1;
DELETE IGNORE FROM cplusRectificationType WHERE id = 1;
UPDATE cplusRectificationType
SET id = 1
WHERE id = 3;
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`invoiceIn_beforeUpdate`
BEFORE UPDATE ON `invoiceIn`
FOR EACH ROW
BEGIN
DECLARE vWithholdingSageFk INT;
IF NOT (NEW.supplierRef <=> OLD.supplierRef) AND NOT util.checkPrintableChars(NEW.supplierRef) THEN
CALL util.throw('The invoiceIn reference contains invalid characters');
END IF;
SET NEW.editorFk = account.myUser_getId();
IF (SELECT COUNT(*) FROM invoiceIn
WHERE supplierRef = NEW.supplierRef
AND supplierFk = NEW.supplierFk
AND YEAR(issued) = YEAR(NEW.issued)
AND id <> NEW.id
) THEN
CALL util.throw('reference duplicated');
END IF;
IF NEW.supplierFk != OLD.supplierFk THEN
CALL supplier_checkIsActive(NEW.supplierFk);
SELECT withholdingSageFk INTO vWithholdingSageFk
FROM supplier
WHERE id = NEW.supplierFk;
SET NEW.withholdingSageFk = vWithholdingSageFk;
END IF;
END$$
DELIMITER ;

View File

@ -0,0 +1,23 @@
INSERT INTO salix.ACL (model, property, accessType, permission, principalType, principalId)
VALUES('SiiTypeInvoiceIn', 'find', 'READ', 'ALLOW', 'ROLE', 'salesPerson');
DROP TABLE IF EXISTS vn.invoiceInCorrection;
CREATE TABLE `invoiceInCorrection` (
`correctingFk` mediumint(8) unsigned NOT NULL COMMENT 'Factura rectificativa',
`correctedFk` mediumint(8) unsigned NOT NULL COMMENT 'Factura rectificada',
`cplusRectificationTypeFk` int(10) unsigned NOT NULL,
`siiTypeInvoiceInFk` int(10) unsigned NOT NULL,
`invoiceCorrectionTypeFk` int(11) NOT NULL DEFAULT 3,
PRIMARY KEY (`correctingFk`),
KEY `invoiceInCorrection_correctedFk` (`correctedFk`),
KEY `invoiceInCorrection_cplusRectificationTypeFk` (`cplusRectificationTypeFk`),
KEY `invoiceInCorrection_siiTypeInvoiceIn` (`siiTypeInvoiceInFk`),
KEY `invoiceInCorrection_invoiceCorrectionTypeFk` (`invoiceCorrectionTypeFk`),
CONSTRAINT `invoiceInCorrection_correctedFk` FOREIGN KEY (`correctedFk`) REFERENCES `invoiceIn` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `invoiceInCorrection_correctingFk` FOREIGN KEY (`correctingFk`) REFERENCES `invoiceIn` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `invoiceInCorrection_cplusRectificationTypeFk` FOREIGN KEY (`cplusRectificationTypeFk`) REFERENCES `cplusRectificationType` (`id`) ON UPDATE CASCADE,
CONSTRAINT `invoiceInCorrection_invoiceCorrectionTypeFk` FOREIGN KEY (`invoiceCorrectionTypeFk`) REFERENCES `invoiceCorrectionType` (`id`) ON UPDATE CASCADE,
CONSTRAINT `invoiceInCorrection_siiTypeInvoiceIn` FOREIGN KEY (`siiTypeInvoiceInFk`) REFERENCES `siiTypeInvoiceIn` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;

View File

@ -0,0 +1,20 @@
CREATE TABLE `vn`.`osrmConfig` (
`id` int(10) unsigned NOT NULL,
`url` varchar(100) NOT NULL COMMENT 'Dirección base de la API',
`tolerance` decimal(6,6) NOT NULL DEFAULT 0 COMMENT 'Tolerancia entre las coordenadas enviadas y las retornadas',
PRIMARY KEY (`id`),
CONSTRAINT `osrmConfig_check` CHECK (`id` = 1)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
-- Para que no de error al añadir la FK de zone
UPDATE vn.zone
SET price = 0.1
WHERE price = 0;
ALTER TABLE vn.`zone`
ADD addressFk int(11) DEFAULT NULL COMMENT 'Punto de distribución de donde salen para repartir',
ADD CONSTRAINT zone_address_FK FOREIGN KEY (addressFk) REFERENCES vn.address(id) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE vn.zoneConfig
ADD defaultAddressFk int(11) DEFAULT NULL NULL COMMENT 'Punto de distribución por defecto',
ADD CONSTRAINT zoneConfig_address_FK FOREIGN KEY (defaultAddressFk) REFERENCES vn.address(id) ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -0,0 +1,5 @@
INSERT IGNORE INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('OsrmConfig','optimize','READ','ALLOW','ROLE','employee'),
('Route', 'optimizePriority','*','ALLOW','ROLE','employee');
INSERT IGNORE INTO vn.osrmConfig (id,url,tolerance)
VALUES (1,'https://router.project-osrm.org', 0.002);

View File

@ -0,0 +1,3 @@
ALTER TABLE vn.mistakeType
ADD `time` int(10) NULL COMMENT 'Segundos que se suelen tardar en arreglar el fallo',
ADD code varchar(50) DEFAULT NULL NULL AFTER id;

View File

@ -0,0 +1,5 @@
RENAME TABLE vn.inventoryFailure TO vn.inventoryFailure__;
ALTER TABLE vn.inventoryFailure__ COMMENT='@deprecated 2024-12-16';
RENAME TABLE vn.inventoryFailureCause TO vn.inventoryFailureCause__;
ALTER TABLE vn.inventoryFailureCause__ COMMENT='@deprecated 2024-12-16';

View File

@ -0,0 +1,3 @@
ALTER TABLE vn.country
ADD CONSTRAINT country_unique_name UNIQUE KEY (name);

View File

@ -248,6 +248,6 @@
"Payment method is required": "Payment method is required", "Payment method is required": "Payment method is required",
"Sales already moved": "Sales already moved", "Sales already moved": "Sales already moved",
"Holidays to past days not available": "Holidays to past days not available", "Holidays to past days not available": "Holidays to past days not available",
"There are tickets to be invoiced": "There are tickets to be invoiced for this zone, please delete them first", "Price cannot be blank": "Price cannot be blank",
"Price cannot be blank": "Price cannot be blank" "There are tickets to be invoiced": "There are tickets to be invoiced"
} }

View File

@ -390,8 +390,13 @@
"The web user's email already exists": "El correo del usuario web ya existe", "The web user's email already exists": "El correo del usuario web ya existe",
"Sales already moved": "Ya han sido transferidas", "Sales already moved": "Ya han sido transferidas",
"The raid information is not correct": "La información de la redada no es correcta", "The raid information is not correct": "La información de la redada no es correcta",
"There are tickets to be invoiced": "Hay tickets para esta zona, borralos primero", "No trips found because input coordinates are not connected": "No se encontraron rutas porque las coordenadas de entrada no están conectadas",
"Price cannot be blank": "Price cannot be blank", "This request is not supported": "Esta solicitud no es compatible",
"Invalid options or too many coordinates": "Opciones invalidas o demasiadas coordenadas",
"No address has coordinates": "Ninguna dirección tiene coordenadas",
"An item type with the same code already exists": "Un tipo con el mismo código ya existe", "An item type with the same code already exists": "Un tipo con el mismo código ya existe",
"Holidays to past days not available": "Las vacaciones a días pasados no están disponibles" "Holidays to past days not available": "Las vacaciones a días pasados no están disponibles",
"All tickets have a route order": "Todos los tickets tienen orden de ruta",
"Price cannot be blank": "Price cannot be blank",
"There are tickets to be invoiced": "La zona tiene tickets por facturar"
} }

View File

@ -36,7 +36,7 @@ module.exports = Self => {
if (!myOptions.transaction) { if (!myOptions.transaction) {
tx = await Self.beginTransaction({}); tx = await Self.beginTransaction({});
myOptions.transaction = tx; myOptions.transaction = tx;
}; }
try { try {
const user = await models.VnUser.findOne({ const user = await models.VnUser.findOne({

View File

@ -80,6 +80,12 @@ module.exports = Self => {
description: 'The claimResponsible id', description: 'The claimResponsible id',
http: {source: 'query'} http: {source: 'query'}
}, },
{
arg: 'zoneFk',
type: 'string',
description: 'The zone name',
http: {source: 'query'}
},
{ {
arg: 'myTeam', arg: 'myTeam',
type: 'boolean', type: 'boolean',
@ -174,6 +180,8 @@ module.exports = Self => {
to.setHours(23, 59, 59, 999); to.setHours(23, 59, 59, 999);
return {'cl.created': {between: [value, to]}}; return {'cl.created': {between: [value, to]}};
case 'zoneFk':
return {'t.zoneFk': value};
case 'myTeam': case 'myTeam':
if (value) if (value)
return {'cl.workerFk': {inq: teamMembersId}}; return {'cl.workerFk': {inq: teamMembersId}};
@ -195,11 +203,15 @@ module.exports = Self => {
u.name AS workerName, u.name AS workerName,
cs.code stateCode, cs.code stateCode,
cs.description stateDescription, cs.description stateDescription,
cl.created cl.created,
z.name zoneName,
z.id zoneId
FROM claim cl FROM claim cl
LEFT JOIN client c ON c.id = cl.clientFk LEFT JOIN client c ON c.id = cl.clientFk
LEFT JOIN account.user u ON u.id = cl.workerFk LEFT JOIN account.user u ON u.id = cl.workerFk
LEFT JOIN claimState cs ON cs.id = cl.claimStateFk` LEFT JOIN claimState cs ON cs.id = cl.claimStateFk
LEFT JOIN ticket t ON t.id = cl.ticketFk
LEFT JOIN zone z ON z.id = t.zoneFk`
); );
stmt.merge(conn.makeSuffix(filter)); stmt.merge(conn.makeSuffix(filter));

View File

@ -72,6 +72,14 @@ module.exports = function(Self) {
{ {
arg: 'isLogifloraAllowed', arg: 'isLogifloraAllowed',
type: 'boolean' type: 'boolean'
},
{
arg: 'longitude',
type: 'any',
},
{
arg: 'latitude',
type: 'any',
} }
], ],
returns: { returns: {

View File

@ -38,8 +38,9 @@ describe('Entry filter()', () => {
}; };
const result = await models.Entry.filter(ctx, options); const result = await models.Entry.filter(ctx, options);
const resultWithCurrency = result.filter(entry => entry.currencyFk === 1);
expect(result.length).toEqual(12); expect(result.length).toEqual(resultWithCurrency.length);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
@ -141,18 +142,21 @@ describe('Entry filter()', () => {
it('should return the entry matching the company', async() => { it('should return the entry matching the company', async() => {
const tx = await models.Entry.beginTransaction({}); const tx = await models.Entry.beginTransaction({});
const options = {transaction: tx}; const options = {transaction: tx};
const companyFk = 442;
try { try {
const ctx = { const ctx = {
args: { args: {
companyFk: 442 companyFk
}, },
req: {accessToken: {userId: 9}} req: {accessToken: {userId: 9}}
}; };
const result = await models.Entry.filter(ctx, options); const result = await models.Entry.filter(ctx, options);
expect(result.length).toEqual(11); const resultWithCurrency = result.filter(entry => entry.companyFk === companyFk);
expect(result.length).toEqual(resultWithCurrency.length);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -28,5 +28,8 @@
}, },
"StockBought": { "StockBought": {
"dataSource": "vn" "dataSource": "vn"
},
"InventoryConfig": {
"dataSource": "vn"
} }
} }

View File

@ -0,0 +1,18 @@
{
"name": "InventoryConfig",
"base": "VnModel",
"options": {
"mysql": {
"table": "inventoryConfig"
}
},
"properties": {
"id": {
"type": "number",
"id": true
},
"supplierFk": {
"type": "number"
}
}
}

View File

@ -18,3 +18,5 @@ columns:
withholdingSageFk: withholding withholdingSageFk: withholding
expenseFkDeductible: expense deductible expenseFkDeductible: expense deductible
editorFk: editor editorFk: editor
siiTrasCendencyInvoiceInFk: SII tax regime
siiTypeInvoiceInFk: SII Type

View File

@ -5,7 +5,7 @@ columns:
serial: serie serial: serie
supplierFk: proveedor supplierFk: proveedor
issued: fecha emisión issued: fecha emisión
supplierRef: referéncia proveedor supplierRef: referencia proveedor
isBooked: facturado isBooked: facturado
currencyFk: moneda currencyFk: moneda
created: creado created: creado
@ -18,3 +18,5 @@ columns:
withholdingSageFk: código de retención withholdingSageFk: código de retención
expenseFkDeductible: gasto deducible expenseFkDeductible: gasto deducible
editorFk: editor editorFk: editor
siiTrasCendencyInvoiceInFk: régimen fiscal SII
siiTypeInvoiceInFk: tipo SII

View File

@ -44,7 +44,7 @@ module.exports = Self => {
correctingFk: clone.id, correctingFk: clone.id,
correctedFk: id, correctedFk: id,
cplusRectificationTypeFk: invoiceType, cplusRectificationTypeFk: invoiceType,
siiTypeInvoiceOutFk: invoiceClass, siiTypeInvoiceInFk: invoiceClass,
invoiceCorrectionTypeFk: invoiceReason invoiceCorrectionTypeFk: invoiceReason
}, myOptions); }, myOptions);

View File

@ -91,6 +91,10 @@ module.exports = Self => {
{ {
arg: 'supplierActivityFk', arg: 'supplierActivityFk',
type: 'string', type: 'string',
},
{
arg: 'companyFk',
type: 'number',
} }
], ],
returns: { returns: {
@ -161,8 +165,8 @@ module.exports = Self => {
: {'ii.id': {nin: correcteds.map(x => x.correctingFk)}}; : {'ii.id': {nin: correcteds.map(x => x.correctingFk)}};
case 'correctedFk': case 'correctedFk':
return {'ii.id': {inq: correctings.map(x => x.correctingFk)}}; return {'ii.id': {inq: correctings.map(x => x.correctingFk)}};
case 'supplierActivityFk': case 'companyFk':
return {'s.supplierActivityFk': value}; return {'ii.companyFk': value};
} }
}); });
@ -184,7 +188,9 @@ module.exports = Self => {
s.name supplierName, s.name supplierName,
s.account, s.account,
SUM(iid.amount) amount, SUM(iid.amount) amount,
sub.code awbCode sub.code awbCode,
c.code,
MIN(iid.dueDated) dueDated
FROM invoiceIn ii FROM invoiceIn ii
JOIN supplier s ON s.id = ii.supplierFk JOIN supplier s ON s.id = ii.supplierFk
LEFT JOIN invoiceInDueDay iid ON iid.invoiceInFk = ii.id LEFT JOIN invoiceInDueDay iid ON iid.invoiceInFk = ii.id
@ -199,7 +205,8 @@ module.exports = Self => {
GROUP BY de.duaFk GROUP BY de.duaFk
) sub ON sub.duaFk = d.id ) sub ON sub.duaFk = d.id
LEFT JOIN company co ON co.id = ii.companyFk LEFT JOIN company co ON co.id = ii.companyFk
LEFT JOIN dms dm ON dm.id = ii.docFk` LEFT JOIN dms dm ON dm.id = ii.docFk
JOIN company c ON c.id = ii.companyFk`,
); );
const sqlWhere = conn.makeWhere(filter.where); const sqlWhere = conn.makeWhere(filter.where);

View File

@ -15,11 +15,11 @@ describe('invoiceIn corrective()', () => {
await tx.rollback(); await tx.rollback();
}); });
it('La función corrective debería devolver un id cuando se ejecuta correctamente', async() => { it('should return an id when executed correctly', async() => {
const originalId = 1; const originalId = 1;
const invoiceReason = 3; const invoiceReason = 3;
const invoiceType = 2; const invoiceType = 2;
const invoiceClass = 1; const invoiceClass = 8;
const cloneId = await models.InvoiceIn.corrective(ctx, const cloneId = await models.InvoiceIn.corrective(ctx,
originalId, invoiceReason, invoiceType, invoiceClass, options); originalId, invoiceReason, invoiceType, invoiceClass, options);
@ -30,7 +30,7 @@ describe('invoiceIn corrective()', () => {
}, options); }, options);
expect(correction.cplusRectificationTypeFk).toEqual(invoiceType); expect(correction.cplusRectificationTypeFk).toEqual(invoiceType);
expect(correction.siiTypeInvoiceOutFk).toEqual(invoiceClass); expect(correction.siiTypeInvoiceInFk).toEqual(invoiceClass);
expect(correction.invoiceCorrectionTypeFk).toEqual(invoiceReason); expect(correction.invoiceCorrectionTypeFk).toEqual(invoiceReason);
}); });
}); });

View File

@ -166,4 +166,21 @@ describe('InvoiceIn filter()', () => {
throw e; throw e;
} }
}); });
it('should return the invoice in matching companyFk', async() => {
const tx = await models.InvoiceIn.beginTransaction({});
const options = {transaction: tx};
try {
const company = await models.Company.findOne({}, options);
const invoicesByCompany = await models.InvoiceIn.find({where: {companyFk: company.id}}, options);
const filteredInvoices = await models.InvoiceIn.filter({args: {companyFk: company.id}}, {}, options);
expect(filteredInvoices.length).toEqual(invoicesByCompany.length);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
}); });

View File

@ -0,0 +1,63 @@
const models = require('vn-loopback/server/server').models;
describe('invoiceIn', () => {
let options;
let tx;
const invoiceId = 1;
const supplierId = 791;
const currencyId = 1;
const companyId = 442;
beforeEach(async() => {
tx = await models.InvoiceIn.beginTransaction({});
options = {transaction: tx};
});
afterEach(async() => {
await tx.rollback();
});
it('should allow insert for new instance', async() => {
const newInvoice = {
supplierFk: supplierId,
issued: Date.vnNew(),
operated: Date.vnNew(),
currencyFk: currencyId,
companyFk: companyId,
isBooked: false
};
const createdInvoice = await models.InvoiceIn.create(newInvoice, options);
expect(createdInvoice).toBeDefined();
expect(createdInvoice.id).toBeDefined();
});
it('should throw an error if trying to update a booked invoice', async() => {
const invoice = await models.InvoiceIn.findById(invoiceId, null, options);
await invoice.updateAttribute('isBooked', true, options);
let error;
try {
await invoice.updateAttribute('supplierFk', supplierId, options);
} catch (err) {
error = err;
}
expect(error.message).toBe('InvoiceIn is already booked');
});
it('should throw an error if trying to delete a booked invoice', async() => {
const invoice = await models.InvoiceIn.findById(invoiceId, null, options);
await invoice.updateAttribute('isBooked', true, options);
let error;
try {
await models.InvoiceIn.deleteById(invoiceId, options);
} catch (err) {
error = err;
}
expect(error.message).toBe('InvoiceIn is already booked');
});
});

View File

@ -0,0 +1,74 @@
const models = require('vn-loopback/server/server').models;
describe('invoiceInTax', () => {
let options;
let tx;
const invoiceInId = 1;
const invoiceInTaxId = 1;
beforeEach(async() => {
tx = await models.InvoiceInTax.beginTransaction({});
options = {transaction: tx};
});
afterEach(async() => {
await tx.rollback();
});
it('should throw an error if trying to save a tax from a booked invoice', async() => {
const invoiceIn = await models.InvoiceIn.findById(invoiceInId, null, options);
await invoiceIn.updateAttributes({isBooked: true}, options);
const invoiceInTax = await models.InvoiceInTax.findById(invoiceInTaxId, null, options);
let error;
try {
await invoiceInTax.updateAttribute('taxableBase', 100, options);
} catch (err) {
error = err;
}
expect(error.message).toBe('InvoiceIn is already booked');
});
it('should allow save if the invoice is not booked', async() => {
const invoiceIn = await models.InvoiceIn.findById(invoiceInId, null, options);
await invoiceIn.updateAttribute('isBooked', false, options);
const invoiceInTax = await models.InvoiceInTax.findById(invoiceInTaxId, null, options);
await invoiceInTax.updateAttribute('taxableBase', 100, options);
const updatedInvoiceInTax = await models.InvoiceInTax.findById(invoiceInTaxId, null, options);
expect(updatedInvoiceInTax.taxableBase).toBe(100);
});
it('should throw an error if trying to delete a tax from a booked invoice', async() => {
const invoiceIn = await models.InvoiceIn.findById(invoiceInId, null, options);
await invoiceIn.updateAttribute('isBooked', true, options);
let error;
try {
await models.InvoiceInTax.destroyById(invoiceInTaxId, options);
} catch (err) {
error = err;
}
expect(error).toBeDefined();
expect(error.message).toBe('InvoiceIn is already booked');
});
it('should allow delete if the invoice is not booked', async() => {
const invoiceIn = await models.InvoiceIn.findById(invoiceInId, null, options);
await invoiceIn.updateAttribute('isBooked', false, options);
let error;
try {
await models.InvoiceInTax.destroyById(invoiceInTaxId, options);
} catch (err) {
error = err;
}
const deletedInvoiceInTax = await models.InvoiceInTax.findById(invoiceInTaxId, null, options);
expect(error).toBeUndefined();
expect(deletedInvoiceInTax).toBeNull();
});
});

View File

@ -0,0 +1,59 @@
const models = require('vn-loopback/server/server').models;
const invoiceInId = 1;
const supplierId = 791;
describe('invoiceIn updateInvoiceIn()', () => {
const ctx = beforeAll.getCtx();
let options;
let tx;
beforeEach(async() => {
options = {transaction: tx};
tx = await models.Sale.beginTransaction({});
options.transaction = tx;
});
afterEach(async() => {
await tx.rollback();
});
it('should update the invoice', async() => {
const invoiceBefore = await models.InvoiceIn.findById(invoiceInId, null, options);
await update(ctx, options);
const invoiceAfter = await models.InvoiceIn.findById(invoiceInId, null, options);
expect(invoiceAfter.supplierFk).not.toBe(invoiceBefore.supplierFk);
expect(invoiceAfter.supplierFk).toBe(supplierId);
});
it('should not update the invoice if is booked', async() => {
let error;
try {
await models.InvoiceIn.toBook(ctx, invoiceInId, options);
await update(ctx, options);
} catch (e) {
error = e;
}
expect(error.message).toBe('InvoiceIn is already booked');
});
});
async function update(ctx, opts) {
const supplierRef = 'mockRef';
const currencyId = 1;
await models.InvoiceIn.updateInvoiceIn(ctx,
invoiceInId,
supplierId,
supplierRef,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
currencyId,
undefined,
undefined,
opts);
}

View File

@ -37,7 +37,13 @@ module.exports = Self => {
{ {
relation: 'supplier', relation: 'supplier',
scope: { scope: {
fields: ['id', 'name'] fields: ['id', 'name', 'isVies', 'countryFk'],
include: [{
relation: 'country',
scope: {
fields: ['id', 'code']
}
}]
} }
}, },
{ {

View File

@ -52,7 +52,8 @@ module.exports = Self => {
accountingEntries = await models.Xdiario.count({ASIEN: asien}, myOptions); accountingEntries = await models.Xdiario.count({ASIEN: asien}, myOptions);
await models.Xdiario.destroyAll({ASIEN: asien}, myOptions); await models.Xdiario.destroyAll({ASIEN: asien}, myOptions);
await Self.updateAll({id: invoiceInId}, {isBooked: false}, myOptions); const invoiceIn = await Self.findById(invoiceInId, myOptions);
await invoiceIn.updateAttribute('isBooked', false, myOptions);
} else { } else {
const linkedBookEntry = await models.Xdiario.findOne({ const linkedBookEntry = await models.Xdiario.findOne({
fields: ['ASIEN'], fields: ['ASIEN'],

View File

@ -82,7 +82,7 @@ module.exports = Self => {
try { try {
const invoiceIn = await Self.findById(id, null, myOptions); const invoiceIn = await Self.findById(id, null, myOptions);
invoiceIn.updateAttributes({supplierFk, await invoiceIn.updateAttributes({supplierFk,
supplierRef, supplierRef,
issued, issued,
operated, operated,
@ -94,6 +94,7 @@ module.exports = Self => {
companyFk, companyFk,
withholdingSageFk withholdingSageFk
}, myOptions); }, myOptions);
if (tx) await tx.commit(); if (tx) await tx.commit();
return invoiceIn; return invoiceIn;
} catch (e) { } catch (e) {

View File

@ -28,11 +28,10 @@
"model": "InvoiceCorrectionType", "model": "InvoiceCorrectionType",
"foreignKey": "invoiceCorrectionTypeFk" "foreignKey": "invoiceCorrectionTypeFk"
}, },
"siiTypeInvoiceOut": { "siiTypeInvoiceIn": {
"type": "belongsTo", "type": "belongsTo",
"model": "SiiTypeInvoiceOut", "model": "SiiTypeInvoiceIn",
"foreignKey": "siiTypeInvoiceOutFk" "foreignKey": "siiTypeInvoiceInFk"
} }
} }
} }

View File

@ -0,0 +1,18 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.observe('before save', async function(ctx) {
if (ctx.isNewInstance) return;
const models = Self.app.models;
const invoiceIn = await models.InvoiceIn.findById(ctx.currentInstance.invoiceInFk, null, ctx.options);
if (invoiceIn.isBooked) throw new UserError('InvoiceIn is already booked');
});
Self.observe('before delete', async function(ctx) {
const models = Self.app.models;
const invoiceInTax = await Self.findById(ctx.where.id, null, ctx.options);
const invoiceIn = await models.InvoiceIn.findById(invoiceInTax.invoiceInFk, null, ctx.options);
if (invoiceIn.isBooked) throw new UserError('InvoiceIn is already booked');
});
};

View File

@ -22,12 +22,11 @@
"type": "number" "type": "number"
}, },
"expenseFk": { "expenseFk": {
"type": "number" "type": "string"
}, },
"created": { "created": {
"type": "date" "type": "date"
} }
}, },
"relations": { "relations": {
"invoiceIn": { "invoiceIn": {

View File

@ -19,4 +19,25 @@ module.exports = Self => {
return new UserError(`This invoice has a linked vehicle.`); return new UserError(`This invoice has a linked vehicle.`);
return err; return err;
}); });
Self.observe('before save', async function(ctx) {
if (ctx.isNewInstance) return;
const changes = ctx.data || ctx.instance;
const orgData = ctx.currentInstance;
let isNotEditable = orgData.isBooked || (!orgData.isBooked && changes.isBooked);
if (isNotEditable) {
for (const [key, value] of Object.entries(changes)) {
if (key !== 'isBooked' && value !== orgData[key])
throw new UserError('InvoiceIn is already booked');
}
}
});
Self.observe('before delete', async function(ctx) {
const invoiceIn = await Self.findById(ctx.where.id, null, ctx.options);
if (invoiceIn.isBooked) throw new UserError('InvoiceIn is already booked');
});
}; };

View File

@ -7,7 +7,12 @@ module.exports = Self => {
arg: 'companyFk', arg: 'companyFk',
type: 'number', type: 'number',
required: true required: true
} },
{
arg: 'serialType',
type: 'string',
required: true
},
], ],
returns: { returns: {
type: ['object'], type: ['object'],
@ -19,16 +24,16 @@ module.exports = Self => {
} }
}); });
Self.getInvoiceDate = async companyFk => { Self.getInvoiceDate = async(companyFk, serialType) => {
const models = Self.app.models; const models = Self.app.models;
const [invoiceDate] = await models.InvoiceOut.rawSql( const [invoiceDate] = await models.InvoiceOut.rawSql(
`SELECT MAX(io.issued) issued `SELECT MAX(io.issued) issued
FROM invoiceOut io FROM invoiceOut io
JOIN invoiceOutSerial ios ON ios.code = io.serial JOIN invoiceOutSerial ios ON ios.code = io.serial
WHERE ios.type = 'global' WHERE ios.type = ?
AND io.issued AND io.issued
AND io.companyFk = ?`, AND io.companyFk = ?`,
[companyFk] [serialType, companyFk]
); );
return invoiceDate; return invoiceDate;
}; };

View File

@ -0,0 +1,39 @@
const models = require('vn-loopback/server/server').models;
const moment = require('moment');
describe('getInvoiceDate()', () => {
const companyFk = 442;
let tx;
let options;
beforeEach(async() => {
tx = await models.InvoiceOut.beginTransaction({});
options = {transaction: tx};
});
afterEach(async() => {
await tx.rollback();
});
it('should return a correct date for serialType "global"', async() => {
const serialType = 'global';
const result = await models.InvoiceOut.getInvoiceDate(companyFk, serialType, options);
expect(moment(result.issued).format('YYYY-MM-DD')).toEqual('2000-12-01');
});
it('should return null for serialType "multiple"', async() => {
const serialType = 'multiple';
const result = await models.InvoiceOut.getInvoiceDate(companyFk, serialType, options);
expect(result.issued).toBeNull();
});
it('should return correct date for serialType "quick"', async() => {
const serialType = 'quick';
const result = await models.InvoiceOut.getInvoiceDate(companyFk, serialType, options);
expect(moment(result.issued).format('YYYY-MM-DD')).toEqual('2001-01-01');
});
});

View File

@ -43,5 +43,8 @@
}, },
"SiiTypeInvoiceOut": { "SiiTypeInvoiceOut": {
"dataSource": "vn" "dataSource": "vn"
},
"SiiTypeInvoiceIn": {
"dataSource": "vn"
} }
} }

View File

@ -0,0 +1,22 @@
{
"name": "SiiTypeInvoiceIn",
"base": "VnModel",
"options": {
"mysql": {
"table": "siiTypeInvoiceIn"
}
},
"properties": {
"id": {
"id": true,
"type": "number",
"description": "Identifier"
},
"code": {
"type": "string"
},
"description": {
"type": "string"
}
}
}

View File

@ -17,9 +17,6 @@
}, },
"description": { "description": {
"type": "string" "type": "string"
},
"code": {
"type": "string"
} }
} }
} }

View File

@ -54,7 +54,8 @@ module.exports = Self => {
b.packageValue, b.packageValue,
b.packagingFk , b.packagingFk ,
s.id AS supplierFk, s.id AS supplierFk,
s.name AS supplier s.name AS supplier,
b.printedStickers
FROM itemType it FROM itemType it
RIGHT JOIN (entry e RIGHT JOIN (entry e
LEFT JOIN supplier s ON s.id = e.supplierFk LEFT JOIN supplier s ON s.id = e.supplierFk

View File

@ -34,10 +34,31 @@ describe('item lastEntriesFilter()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
try { try {
const filter = {where: {itemFk: 1, landed: {between: [minDate, maxDate]}}}; const itemFk = 1;
const filter = {where: {itemFk, landed: {between: [minDate, maxDate]}}};
const result = await models.Item.lastEntriesFilter(filter, options); const result = await models.Item.lastEntriesFilter(filter, options);
const minDateUtc = new Date(minDate).getTime();
const maxDateUtc = new Date(maxDate).getTime();
expect(result.length).toEqual(6); const resultMatch = (
await Promise.all(
result.map(async item => {
const itemRecord = await models.Buy.findOne({
fields: ['id'],
where: {id: item.id},
options,
});
const isItemFkValid = itemRecord?.id === itemFk;
const landedDate = new Date(item.landed).getTime();
const isLandedValid = landedDate >= minDateUtc && landedDate <= maxDateUtc;
return isItemFkValid && isLandedValid;
})
)
).filter(Boolean).length;
expect(result.length).toEqual(resultMatch);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -98,6 +98,11 @@ module.exports = Self => {
arg: 'countryFk', arg: 'countryFk',
type: 'number', type: 'number',
description: 'The country id filter' description: 'The country id filter'
},
{
arg: 'payMethod',
type: 'string',
description: 'The payment method filter'
} }
], ],
returns: { returns: {
@ -165,6 +170,8 @@ module.exports = Self => {
case 'clientFk': case 'clientFk':
param = `t.${param}`; param = `t.${param}`;
return {[param]: value}; return {[param]: value};
case 'payMethod':
return {'c.payMethodFk': value};
} }
}); });
@ -205,6 +212,8 @@ module.exports = Self => {
u.name userName, u.name userName,
c.salesPersonFk, c.salesPersonFk,
c.credit, c.credit,
c.payMethodFk payMethodFk,
pm.id payMethodId,
pm.name payMethod, pm.name payMethod,
z.hour zoneLanding, z.hour zoneLanding,
z.name zoneName, z.name zoneName,

View File

@ -0,0 +1,122 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethod('optimizePriority', {
description: 'Updates the ticket priority of tickets without priority',
accepts: {
arg: 'id',
type: 'number',
required: true,
description: 'Route id',
http: {source: 'path'}
},
returns: {
type: 'object',
root: true
},
http: {
path: '/:id/optimizePriority',
verb: 'POST'
}
});
Self.optimizePriority = async(id, options) => {
const models = Self.app.models;
const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
const tickets = await models.Ticket.find({
where: {routeFk: id}
}, myOptions);
let ticketAddress = [];
for (const ticket of tickets) {
ticketAddress.push({
ticketId: ticket.id,
addressId: ticket.addressFk,
zoneId: ticket.zoneFk,
priority: ticket.priority
});
}
// Igualamos los priority del mismo addressId
const addressPriorityMap = ticketAddress.reduce((acc, {addressId, priority}) => {
if (priority !== null) {
acc[addressId] = acc[addressId] === undefined
? priority
: Math.max(acc[addressId], priority);
}
return acc;
});
ticketAddress.forEach(item => {
const maxPriority = addressPriorityMap[item.addressId];
if (maxPriority) item.priority = maxPriority;
});
// Añadimos las direcciones a optimizar
let addressIds = [];
ticketAddress.forEach(h => {
if (!addressIds.includes(h.addressId) && !h.priority)
addressIds.push(h.addressId);
});
if (!addressIds.length) throw new UserError('All tickets have a route order');
// Obtenemos el zoneId más frecuente
const zoneFrequency = ticketAddress.reduce((acc, {zoneId}) => {
if (zoneId != null) acc[zoneId] = (acc[zoneId] || 0) + 1;
return acc;
}, {});
const [mostFrequentZoneId] = Object.entries(zoneFrequency)
.reduce((maxEntry, entry) => entry[1] > maxEntry[1] ? entry : maxEntry, [null, 0]);
// Obtenemos los address inicio y fin
const maxPosition = Math.max(...ticketAddress.map(g => g.priority));
let firstAddress = (await models.Zone.findById(mostFrequentZoneId, myOptions))?.addressFk
|| (await models.ZoneConfig.findOne())?.defaultAddressFk;
const lastAddress = firstAddress;
if (maxPosition) firstAddress = ticketAddress.find(g => g.priority === maxPosition)?.addressId;
// Revisamos las coincidencias y actualizamos la prioridad en el array
const addressPositions = await models.OsrmConfig.optimize(addressIds, firstAddress, lastAddress, myOptions);
await Promise.all(ticketAddress.map(async i => {
const foundPosition = addressPositions.find(item => item.addressId === i.addressId);
if (foundPosition) i.priority = foundPosition.position + (maxPosition + 1);
}));
// Suavizado de prioridad para que no hayan escalones
const allPriorities = ticketAddress
.map(item => item.priority)
.filter(p => p !== null);
const uniquePriorities = [...new Set(allPriorities)].sort((a, b) => a - b);
const priorityMap = {};
uniquePriorities.forEach((p, index) => {
priorityMap[p] = index + 1;
});
ticketAddress.forEach(item => {
if (item.priority !== null) item.priority = priorityMap[item.priority];
});
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
// Realizamos el update en la base de datos
try {
await Promise.all(ticketAddress.map(async y => {
if (y.priority) {
const ticket = await models.Ticket.findById(y.ticketId);
await ticket.updateAttribute('priority', y.priority, myOptions);
}
}));
if (tx) await tx.commit();
return;
} catch (err) {
if (tx) await tx.rollback();
throw err;
}
};
};

View File

@ -0,0 +1,36 @@
const models = require('vn-loopback/server/server').models;
const routeId = 1;
describe('route optimizePriority())', function() {
it('should execute without throwing errors', async function() {
const tx = await models.Route.beginTransaction({});
let error;
try {
const options = {transaction: tx};
await models.Ticket.updateAll(
{routeFk: routeId},
{priority: null},
options
);
await models.Route.optimizePriority(routeId, options);
await tx.rollback();
} catch (e) {
error = e;
await tx.rollback();
}
expect(error).toBeUndefined();
});
it('should execute with error', async function() {
let error;
try {
await models.Route.optimizePriority(routeId);
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toBe('All tickets have a route order');
});
});

View File

@ -16,4 +16,5 @@ module.exports = Self => {
require('../methods/route/downloadZip')(Self); require('../methods/route/downloadZip')(Self);
require('../methods/route/getExpeditionSummary')(Self); require('../methods/route/getExpeditionSummary')(Self);
require('../methods/route/getByWorker')(Self); require('../methods/route/getByWorker')(Self);
require('../methods/route/optimizePriority')(Self);
}; };

View File

@ -82,7 +82,7 @@ module.exports = Self => {
const message = $t('Changed sale quantity', { const message = $t('Changed sale quantity', {
ticketId: sale.ticket().id, ticketId: sale.ticket().id,
changes: change, changes: JSON.stringify(change),
ticketUrl: `${url}ticket/${sale.ticket().id}/sale`, ticketUrl: `${url}ticket/${sale.ticket().id}/sale`,
}); });

View File

@ -54,9 +54,17 @@ module.exports = Self => {
throw new UserError(`That item doesn't exists`); throw new UserError(`That item doesn't exists`);
const request = await models.TicketRequest.findById(ctx.args.id, { const request = await models.TicketRequest.findById(ctx.args.id, {
include: {relation: 'ticket'} include: {
relation: 'ticket',
scope: {
include: {
relation: 'client',
scope: {
fields: ['id', 'name', 'salesPersonFk']
}
}
}}
}, myOptions); }, myOptions);
const itemStock = await models.Item.getVisibleAvailable( const itemStock = await models.Item.getVisibleAvailable(
ctx.args.itemFk, ctx.args.itemFk,
request.ticket().warehouseFk, request.ticket().warehouseFk,
@ -89,19 +97,19 @@ module.exports = Self => {
const query = `CALL vn.sale_calculateComponent(?, NULL)`; const query = `CALL vn.sale_calculateComponent(?, NULL)`;
await Self.rawSql(query, [sale.id], myOptions); await Self.rawSql(query, [sale.id], myOptions);
const url = await Self.app.models.Url.getUrl(); const salesPerson = request.ticket().client().salesPersonFk;
const requesterId = request.requesterFk; if (salesPerson) {
const url = await Self.app.models.Url.getUrl();
const message = $t('Bought units from buy request', { const message = $t('Bought units from buy request', {
quantity: sale.quantity, quantity: sale.quantity,
concept: sale.concept, concept: sale.concept,
itemId: sale.itemFk, itemId: sale.itemFk,
ticketId: sale.ticketFk, ticketId: sale.ticketFk,
url: `${url}ticket/${sale.ticketFk}/summary`, url: `${url}ticket/${sale.ticketFk}/summary`,
urlItem: `${url}item/${sale.itemFk}/summary` urlItem: `${url}item/${sale.itemFk}/summary`
}); });
await models.Chat.sendCheckingPresence(ctx, requesterId, message, myOptions); await models.Chat.sendCheckingPresence(ctx, salesPerson, message, myOptions);
}
if (tx) await tx.commit(); if (tx) await tx.commit();
return sale; return sale;

View File

@ -1,18 +1,22 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('deny', { Self.remoteMethodCtx('deny', {
description: 'sets a ticket request to denied and returns the changes', description: 'Sets a ticket request to denied and returns the changes',
accessType: 'WRITE', accessType: 'WRITE',
accepts: [{ accepts: [
arg: 'id', {
type: 'number', arg: 'id',
required: true, type: 'number',
description: 'The request ID', required: true,
}, { description: 'The request ID',
arg: 'observation', },
type: 'String', {
required: true, arg: 'observation',
description: 'The request observation', type: 'string',
}], required: true,
description: 'The request observation',
}
],
returns: { returns: {
type: 'number', type: 'number',
root: true root: true
@ -29,7 +33,7 @@ module.exports = Self => {
const myOptions = {}; const myOptions = {};
let tx; let tx;
if (typeof options == 'object') if (typeof options === 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
if (!myOptions.transaction) { if (!myOptions.transaction) {
@ -39,7 +43,7 @@ module.exports = Self => {
try { try {
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const worker = await Self.app.models.Worker.findOne({where: {id: userId}}, myOptions); const worker = await models.Worker.findById(userId, {fields: ['id']}, myOptions);
const params = { const params = {
isOk: false, isOk: false,
@ -47,19 +51,32 @@ module.exports = Self => {
response: ctx.args.observation, response: ctx.args.observation,
}; };
const request = await Self.app.models.TicketRequest.findById(ctx.args.id, null, myOptions); const request = await models.TicketRequest.findById(ctx.args.id, {
await request.updateAttributes(params, myOptions); include: {
relation: 'ticket',
scope: {
include: {
relation: 'client',
scope: {
fields: ['id', 'name', 'salesPersonFk']
}
}
}
}
}, myOptions);
const url = await Self.app.models.Url.getUrl(); const salesPerson = request.ticket().client().salesPersonFk;
const requesterId = request.requesterFk; if (salesPerson) {
const url = await models.Url.getUrl();
const message = $t('Deny buy request', {
ticketId: request.ticketFk,
url: `${url}ticket/${request.ticketFk}/request/index`,
observation: params.response
});
const message = $t('Deny buy request', { await models.Chat.sendCheckingPresence(ctx, salesPerson, message, myOptions);
ticketId: request.ticketFk, await request.updateAttributes(params, myOptions);
url: `${url}ticket/${request.ticketFk}/request/index`, }
observation: params.response
});
await models.Chat.sendCheckingPresence(ctx, requesterId, message, myOptions);
if (tx) await tx.commit(); if (tx) await tx.commit();

View File

@ -64,6 +64,11 @@ module.exports = Self => {
arg: 'myTeam', arg: 'myTeam',
type: 'boolean', type: 'boolean',
description: `Team partners` description: `Team partners`
},
{
arg: 'daysOnward',
type: 'number',
description: 'The days onward'
} }
], ],
returns: { returns: {
@ -104,6 +109,9 @@ module.exports = Self => {
teamMembersId.push(userId); teamMembersId.push(userId);
} }
const today = Date.vnNew();
const future = Date.vnNew();
let where = buildFilter(ctx.args, (param, value) => { let where = buildFilter(ctx.args, (param, value) => {
switch (param) { switch (param) {
case 'search': case 'search':
@ -140,9 +148,13 @@ module.exports = Self => {
return {'tr.requesterFk': {inq: teamMembersId}}; return {'tr.requesterFk': {inq: teamMembersId}};
else else
return {'tr.requesterFk': {nin: teamMembersId}}; return {'tr.requesterFk': {nin: teamMembersId}};
case 'daysOnward':
today.setHours(0, 0, 0, 0);
future.setDate(today.getDate() + value);
future.setHours(23, 59, 59, 999);
return {'t.shipped': {between: [today, future]}};
} }
}); });
if (!where) if (!where)
where = {}; where = {};
where['tw.ticketFk'] = null; where['tw.ticketFk'] = null;

View File

@ -55,6 +55,11 @@ module.exports = Self => {
type: 'number', type: 'number',
description: 'Department identifier' description: 'Department identifier'
}, },
{
arg: 'onlyWithDestination',
type: 'Boolean',
description: 'True when only tickets with destination are returned'
},
{ {
arg: 'filter', arg: 'filter',
type: 'object', type: 'object',
@ -103,6 +108,8 @@ module.exports = Self => {
return {'f.isFullMovable': value}; return {'f.isFullMovable': value};
case 'departmentFk': case 'departmentFk':
return {'f.departmentFk': value}; return {'f.departmentFk': value};
case 'onlyWithDestination':
return {'f.id': value ? {neq: null} : null};
} }
}); });

View File

@ -8,6 +8,7 @@ describe('TicketFuture getTicketsAdvance()', () => {
tomorrow.setDate(today.getDate() + 1); tomorrow.setDate(today.getDate() + 1);
const salesDeptId = 43; const salesDeptId = 43;
const spain1DeptId = 95; const spain1DeptId = 95;
const warehouseId = 1;
beforeAll.mockLoopBackContext(); beforeAll.mockLoopBackContext();
it('should return the tickets passing the required data', async() => { it('should return the tickets passing the required data', async() => {
@ -19,7 +20,7 @@ describe('TicketFuture getTicketsAdvance()', () => {
const args = { const args = {
dateFuture: tomorrow, dateFuture: tomorrow,
dateToAdvance: today, dateToAdvance: today,
warehouseFk: 1, warehouseFk: warehouseId,
}; };
ctx.args = args; ctx.args = args;
@ -42,7 +43,7 @@ describe('TicketFuture getTicketsAdvance()', () => {
const args = { const args = {
dateFuture: tomorrow, dateFuture: tomorrow,
dateToAdvance: today, dateToAdvance: today,
warehouseFk: 1, warehouseFk: warehouseId,
isFullMovable: true isFullMovable: true
}; };
@ -67,7 +68,7 @@ describe('TicketFuture getTicketsAdvance()', () => {
const args = { const args = {
dateFuture: tomorrow, dateFuture: tomorrow,
dateToAdvance: today, dateToAdvance: today,
warehouseFk: 1, warehouseFk: warehouseId,
isFullMovable: false isFullMovable: false
}; };
@ -92,7 +93,7 @@ describe('TicketFuture getTicketsAdvance()', () => {
const args = { const args = {
dateFuture: tomorrow, dateFuture: tomorrow,
dateToAdvance: today, dateToAdvance: today,
warehouseFk: 1, warehouseFk: warehouseId,
ipt: 'V' ipt: 'V'
}; };
@ -117,7 +118,7 @@ describe('TicketFuture getTicketsAdvance()', () => {
const args = { const args = {
dateFuture: tomorrow, dateFuture: tomorrow,
dateToAdvance: today, dateToAdvance: today,
warehouseFk: 1, warehouseFk: warehouseId,
tfIpt: 'V' tfIpt: 'V'
}; };
@ -141,7 +142,7 @@ describe('TicketFuture getTicketsAdvance()', () => {
ctx.args = { ctx.args = {
dateFuture: tomorrow, dateFuture: tomorrow,
dateToAdvance: today, dateToAdvance: today,
warehouseFk: 1, warehouseFk: warehouseId,
}; };
await models.Ticket.updateAll({id: {inq: [12, 31]}}, {clientFk: 1}, options); await models.Ticket.updateAll({id: {inq: [12, 31]}}, {clientFk: 1}, options);
@ -167,4 +168,56 @@ describe('TicketFuture getTicketsAdvance()', () => {
throw e; throw e;
} }
}); });
it('should return the tickets with only destination', async() => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const args = {
dateFuture: today,
dateToAdvance: today.setHours(23, 59, 59, 999),
warehouseFk: warehouseId,
};
ctx.args = args;
const allTickets = await models.Ticket.getTicketsAdvance(ctx, options);
ctx.args.onlyWithDestination = true;
const withDestinationTickets = await models.Ticket.getTicketsAdvance(ctx, options);
expect(allTickets.filter(ticket => ticket.id).length).toBe(withDestinationTickets.length);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the tickets without only destination', async() => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const args = {
dateFuture: today,
dateToAdvance: today.setHours(23, 59, 59, 999),
warehouseFk: warehouseId,
};
ctx.args = args;
const allTickets = await models.Ticket.getTicketsAdvance(ctx, options);
ctx.args.onlyWithDestination = false;
const withoutDestinationTickets = await models.Ticket.getTicketsAdvance(ctx, options);
expect(allTickets.filter(ticket => !ticket.id).length).toBe(withoutDestinationTickets.length);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
}); });

View File

@ -83,6 +83,14 @@ module.exports = Self => {
arg: 'daysOnward', arg: 'daysOnward',
type: 'number', type: 'number',
description: 'The days onward' description: 'The days onward'
}, {
arg: 'shipped',
type: 'date',
description: 'The shipped date'
}, {
arg: 'landed',
type: 'date',
description: 'The landed date'
} }
], ],
returns: { returns: {
@ -108,6 +116,10 @@ module.exports = Self => {
: {'t.ref': {like: `%${value}%`}}; : {'t.ref': {like: `%${value}%`}};
case 'ref': case 'ref':
return {'t.ref': {like: `%${value}%`}}; return {'t.ref': {like: `%${value}%`}};
case 'shipped':
return {'t.shipped': value};
case 'landed':
return {'t.landed': value};
case 'shippedFrom': case 'shippedFrom':
return {'t.shipped': {gte: value}}; return {'t.shipped': {gte: value}};
case 'shippedTo': case 'shippedTo':

View File

@ -3,9 +3,10 @@ const models = require('vn-loopback/server/server').models;
describe('travel getEntries()', () => { describe('travel getEntries()', () => {
const travelId = 1; const travelId = 1;
it('should check the response contains the id', async() => { it('should check the response contains the id', async() => {
const entries = await models.Travel.getEntries(travelId); const result = await models.Travel.getEntries(travelId);
const entries = await models.Entry.find({where: {travelFk: travelId}});
expect(entries.length).toEqual(1); expect(entries.length).toEqual(result.length);
expect(entries[0].id).toEqual(1); expect(entries[0].id).toEqual(1);
}); });

View File

@ -128,7 +128,10 @@ module.exports = Self => {
const account = await models.VnUser.findById(userId, null, myOptions); const account = await models.VnUser.findById(userId, null, myOptions);
const subordinated = await models.VnUser.findById(id, null, myOptions); const subordinated = await models.VnUser.findById(id, null, myOptions);
const worker = await models.Worker.findById(subordinated.id, null, myOptions); const worker = await models.Worker.findById(subordinated.id, null, myOptions);
const departmentBoss = await models.VnUser.findById(worker.bossFk, null, myOptions); const receiver = await models.EmailUser.findOne({
fields: ['email'],
where: {userFk: worker.bossFk}
}, myOptions);
const url = await Self.app.models.Url.getUrl(); const url = await Self.app.models.Url.getUrl();
const body = $t('Created absence', { const body = $t('Created absence', {
author: account.nickname, author: account.nickname,
@ -140,7 +143,7 @@ module.exports = Self => {
await models.Mail.create({ await models.Mail.create({
subject: $t('Absence change notification on the labour calendar'), subject: $t('Absence change notification on the labour calendar'),
body: body, body: body,
receiver: departmentBoss.email receiver: receiver.email
}, myOptions); }, myOptions);
if (tx) await tx.commit(); if (tx) await tx.commit();

View File

@ -25,7 +25,7 @@
"userFk": { "userFk": {
"type": "number" "type": "number"
}, },
"simSerialNumber": { "simFk": {
"type": "string" "type": "string"
}, },
"created": { "created": {

View File

@ -23,7 +23,7 @@
"type" : "number" "type" : "number"
}, },
"isDependend": { "isDependend": {
"type" : "number" "type" : "boolean"
}, },
"familySituation": { "familySituation": {
"type" : "number" "type" : "number"
@ -35,15 +35,17 @@
"type" : "number" "type" : "number"
}, },
"hasHousingPaymentBefore": { "hasHousingPaymentBefore": {
"type" : "number" "type" : "boolean"
}, },
"hasHousingPaymentAfter": { "hasHousingPaymentAfter": {
"type" : "number" "type" : "boolean"
}, },
"updated": { "updated": {
"type" : "date" "type" : "date"
},
"hasExtendedWorking": {
"type" : "boolean"
} }
}, },
"relations": { "relations": {
"disabilityGrade": { "disabilityGrade": {

View File

@ -51,7 +51,7 @@ module.exports = Self => {
}; };
const ticketList = await models.Ticket.find(filter, myOptions); const ticketList = await models.Ticket.find(filter, myOptions);
const hasRefFk = ticketList.some(ticket => ticket.refFk); const hasRefFk = ticketList.some(ticket => !ticket.refFk);
if (hasRefFk) if (hasRefFk)
throw new UserError('There are tickets to be invoiced'); throw new UserError('There are tickets to be invoiced');

View File

@ -17,6 +17,9 @@
"ZoneClosure": { "ZoneClosure": {
"dataSource": "vn" "dataSource": "vn"
}, },
"ZoneConfig": {
"dataSource": "vn"
},
"ZoneEvent": { "ZoneEvent": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -0,0 +1,28 @@
{
"name": "ZoneConfig",
"options": {
"mysql": {
"table": "zoneConfig"
}
},
"properties": {
"id": {
"type": "number",
"id": true,
"description": "Identifier"
},
"scope": {
"type": "number"
},
"forwardDays": {
"type": "number"
}
},
"relations": {
"address": {
"type": "belongsTo",
"model": "Address",
"foreignKey": "defaultAddressFk"
}
}
}

View File

@ -1,9 +1,9 @@
{ {
"name": "Zone", "name": "Zone",
"base": "VnModel", "base": "VnModel",
"mixins": { "mixins": {
"Loggable": true "Loggable": true
}, },
"options": { "options": {
"mysql": { "mysql": {
"table": "zone" "table": "zone"
@ -48,30 +48,35 @@
} }
}, },
"relations": { "relations": {
"agencyMode": { "agencyMode": {
"type": "belongsTo", "type": "belongsTo",
"model": "AgencyMode", "model": "AgencyMode",
"foreignKey": "agencyModeFk" "foreignKey": "agencyModeFk"
}, },
"events": { "events": {
"type": "hasMany", "type": "hasMany",
"model": "ZoneEvent", "model": "ZoneEvent",
"foreignKey": "zoneFk" "foreignKey": "zoneFk"
}, },
"exclusions": { "exclusions": {
"type": "hasMany", "type": "hasMany",
"model": "ZoneExclusion", "model": "ZoneExclusion",
"foreignKey": "zoneFk" "foreignKey": "zoneFk"
}, },
"warehouses": { "warehouses": {
"type": "hasMany", "type": "hasMany",
"model": "ZoneWarehouse", "model": "ZoneWarehouse",
"foreignKey": "zoneFk" "foreignKey": "zoneFk"
}, },
"closures": { "closures": {
"type": "hasMany", "type": "hasMany",
"model": "ZoneClosure", "model": "ZoneClosure",
"foreignKey": "zoneFk" "foreignKey": "zoneFk"
} },
} "address": {
"type": "belongsTo",
"model": "Address",
"foreignKey": "addressFk"
}
}
} }

View File

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