Merge pull request '8389-testToMaster' (!3357) from 8389-testToMaster into master
gitea/salix/pipeline/head This commit looks good Details

Reviewed-on: #3357
Reviewed-by: Guillermo Bonet <guillermo@verdnatura.es>
This commit is contained in:
Alex Moreno 2025-01-14 06:31:07 +00:00
commit 43edb1f82e
27 changed files with 243 additions and 130 deletions

View File

@ -1,3 +1,31 @@
# Version 25.00 - 2025-01-14
### Added 🆕
- feat: refs #7235 add serialType parameter to getInvoiceDate and implement corresponding tests by:jgallego
- feat: refs #7301 update lastEntriesFilter to include landedDate and enhance test cases (origin/7301-removeRedundantInventories) by:pablone
- feat: refs #7880 error code and translations by:ivanm
- feat: refs #7924 add isCustomInspectionRequired field to item and update related logic by:jgallego
- feat: refs #8167 update canBeInvoiced method to include active status check and improve test cases by:jgallego
- feat: refs #8167 update locale and improve invoicing logic with error handling by:jgallego
- feat: refs #8246 added relation for the front's new field by:Jon
- feat: refs #8266 added itemFk and needed fixtures by:jtubau
- feat: refs #8324 country unique by:Carlos Andrés
### Changed 📦
### Fixed 🛠️
- feat: refs #8266 added itemFk and needed fixtures by:jtubau
- fix: add isCustomInspectionRequired column to item table for customs inspection indication by:jgallego
- fix: canBeInvoiced only in makeInvoice by:alexm
- fix: hotFix getMondayWeekYear by:alexm
- fix: refs #6598 update ACL property assignment by:jorgep
- fix: refs #6861 refs#6861 addPrevOK by:sergiodt
- fix: refs #7301 remove debug console log and update test cases in lastEntriesFilter by:pablone
- fix: refs #7301 update SQL fixtures and improve lastEntriesFilter logic by:pablone
# Version 24.52 - 2024-01-07 # Version 24.52 - 2024-01-07
### Added 🆕 ### Added 🆕

View File

@ -67,7 +67,6 @@ module.exports = Self => {
INSERT INTO util.debug (variable, value) INSERT INTO util.debug (variable, value)
VALUES ('sendCheckingPresence_error', ?) VALUES ('sendCheckingPresence_error', ?)
`, [`User: ${userId}, recipient: ${recipientId}, message: ${message}, error: ${e}`]); `, [`User: ${userId}, recipient: ${recipientId}, message: ${message}, error: ${e}`]);
throw e;
} }
}; };
}; };

View File

@ -19,7 +19,7 @@ module.exports = Self => {
if (acl.principalType == 'ROLE' && acl.permission == 'ALLOW') { if (acl.principalType == 'ROLE' && acl.permission == 'ALLOW') {
const staticAcl = { const staticAcl = {
model: model.name, model: model.name,
property: '*', property: acl.property,
accessType: acl.accessType, accessType: acl.accessType,
permission: acl.permission, permission: acl.permission,
principalType: acl.principalType, principalType: acl.principalType,

View File

@ -974,26 +974,30 @@ INSERT INTO `vn`.`itemFamily`(`code`, `description`)
('SER', 'Services'), ('SER', 'Services'),
('VT', 'Sales'); ('VT', 'Sales');
INSERT INTO `vn`.`item`(`id`, `typeFk`, `stems`, `originFk`, `description`, `producerFk`, `intrastatFk`, `expenseFk`, INSERT INTO `vn`.`item`(
`comment`, `relevancy`, `image`, `subName`, `minPrice`, `family`, `isFloramondo`, `genericFk`, `itemPackingTypeFk`, `hasMinPrice`, `weightByPiece`) `id`, `typeFk`, `stems`, `originFk`, `description`, `producerFk`, `intrastatFk`, `expenseFk`,
VALUES `comment`, `relevancy`, `image`, `subName`, `minPrice`, `family`, `isFloramondo`, `genericFk`,
(1, 2, 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '1', NULL, 0, 'EMB', 0, NULL, 'V', 0, 3), `itemPackingTypeFk`, `hasMinPrice`, `weightByPiece`, `isCustomInspectionRequired`
(2, 2, 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '2', NULL, 0, 'VT', 0, NULL, 'H', 0, 2), )
(3, 1, 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '3', NULL, 0, 'VT', 0, NULL, NULL, 0, 5), VALUES
(4, 1, 1, 1, 'Increases block', 1, 05080000, 4751000000, NULL, 0, '4', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL), (1, 2, 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '1', NULL, 0, 'EMB', 0, NULL, 'V', 0, 3, 1),
(5, 3, 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '5', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL), (2, 2, 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '2', NULL, 0, 'VT', 0, NULL, 'H', 0, 2, 1),
(6, 5, 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '6', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL), (3, 1, 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '3', NULL, 0, 'VT', 0, NULL, NULL, 0, 5, 0),
(7, 5, 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '7', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL), (4, 1, 1, 1, 'Increases block', 1, 05080000, 4751000000, NULL, 0, '4', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL, 0),
(8, 2, 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '8', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL), (5, 3, 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '5', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL, 0),
(9, 2, 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '9', NULL, 0, 'VT', 1, NULL, NULL, 0, NULL), (6, 5, 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '6', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL, 0),
(10, 1, 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '10', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL), (7, 5, 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '7', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL, 0),
(11, 1, 1, 1, NULL, 1, 05080000, 4751000000, NULL, 0, '11', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL), (8, 2, 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '8', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL, 0),
(12, 3, 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '12', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL), (9, 2, 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '9', NULL, 0, 'VT', 1, NULL, NULL, 0, NULL, 0),
(13, 5, 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '13', NULL, 1, 'VT', 1, NULL, NULL, 1, NULL), (10, 1, 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '10', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL, 0),
(14, 5, 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 'VT', 1, NULL, NULL, 0, NULL), (11, 1, 1, 1, NULL, 1, 05080000, 4751000000, NULL, 0, '11', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL, 0),
(15, 4, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 'EMB', 0, NULL, NULL, 0, NULL), (12, 3, 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '12', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL, 0),
(16, 6, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 'EMB', 0, NULL, NULL, 0, NULL), (13, 5, 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '13', NULL, 1, 'VT', 1, NULL, NULL, 1, NULL, 0),
(71, 6, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL); (14, 5, 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 'VT', 1, NULL, NULL, 0, NULL, 0),
(15, 4, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 'EMB', 0, NULL, NULL, 0, NULL, 0),
(16, 6, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 'EMB', 0, NULL, NULL, 0, NULL, 0),
(71, 6, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 'VT', 0, NULL, NULL, 0, NULL, 0);
-- Update the taxClass after insert of the items -- Update the taxClass after insert of the items
UPDATE `vn`.`itemTaxCountry` SET `taxClassFk` = 2 UPDATE `vn`.`itemTaxCountry` SET `taxClassFk` = 2
@ -3968,7 +3972,7 @@ VALUES(1, '');
INSERT INTO dipole.expedition_PrintOut (expeditionFk, ticketFk, addressFk, street, postalCode, city, shopName, isPrinted, created, printerFk, routeFk, parkingCode, INSERT INTO dipole.expedition_PrintOut (expeditionFk, ticketFk, addressFk, street, postalCode, city, shopName, isPrinted, created, printerFk, routeFk, parkingCode,
truckName, clientFk, phone, province, agency, m3, workerCode, itemFk, quantity, longName, shelvingFk, comments) truckName, clientFk, phone, province, agency, m3, workerCode, itemFk, quantity, longName, shelvingFk, comments)
VALUES(1, 1, 0, ' ', ' ', ' ', ' ', 0, '2001-01-01 00:00:00', 1, 0, ' ', ' ', 0, NULL, '', NULL, 0.000, NULL, 10, NULL, NULL, 'NCC', NULL); VALUES(1, 1, 0, ' ', ' ', ' ', ' ', 0, '2001-01-01 00:00:00', 1, 0, ' ', ' ', 0, NULL, '', NULL, 0.000, NULL, 10, NULL, 'Ranged Reinforced weapon sniper rifle 700mm' , 'NCC', NULL);
INSERT INTO vn.accountDetail INSERT INTO vn.accountDetail
(id, value, accountDetailTypeFk, supplierAccountFk) (id, value, accountDetailTypeFk, supplierAccountFk)

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

@ -16,10 +16,11 @@ BEGIN
TRUE, TRUE,
sc.userFk, sc.userFk,
s.id s.id
FROM vn.sectorCollection sc FROM sectorCollection sc
JOIN vn.sectorCollectionSaleGroup scsg ON scsg.sectorCollectionFk = sc.id JOIN sectorCollectionSaleGroup scsg ON scsg.sectorCollectionFk = sc.id
JOIN vn.saleGroupDetail sgd ON sgd.saleGroupFk = scsg.saleGroupFk JOIN saleGroupDetail sgd ON sgd.saleGroupFk = scsg.saleGroupFk
JOIN vn.state s ON s.code = 'OK PREVIOUS' JOIN state s ON s.code = 'OK PREVIOUS'
JOIN itemShelvingSale iss ON iss.saleFk = sgd.saleFk
WHERE sc.id = vSectorCollectionFk; WHERE sc.id = vSectorCollectionFk;
END$$ END$$
DELIMITER ; DELIMITER ;

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,3 @@
ALTER TABLE vn.country
ADD CONSTRAINT country_unique_name UNIQUE KEY (name);

View File

@ -0,0 +1,2 @@
ALTER TABLE `vn`.`item`
ADD COLUMN `isCustomInspectionRequired` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Indicates if the item requires physical inspection at customs';

View File

@ -211,6 +211,7 @@
"Name should be uppercase": "Name should be uppercase", "Name should be uppercase": "Name should be uppercase",
"You cannot update these fields": "You cannot update these fields", "You cannot update these fields": "You cannot update these fields",
"CountryFK cannot be empty": "Country cannot be empty", "CountryFK cannot be empty": "Country cannot be empty",
"No tickets to invoice": "There are no tickets to invoice that meet the invoicing requirements",
"You are not allowed to modify the alias": "You are not allowed to modify the alias", "You are not allowed to modify the alias": "You are not allowed to modify the alias",
"You already have the mailAlias": "You already have the mailAlias", "You already have the mailAlias": "You already have the mailAlias",
"This machine is already in use.": "This machine is already in use.", "This machine is already in use.": "This machine is already in use.",
@ -249,5 +250,6 @@
"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",
"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" "There are tickets to be invoiced": "There are tickets to be invoiced",
"The address of the customer must have information about Incoterms and Customs Agent": "The address of the customer must have information about Incoterms and Customs Agent"
} }

View File

@ -339,7 +339,7 @@
"Incorrect pin": "Pin incorrecto.", "Incorrect pin": "Pin incorrecto.",
"You already have the mailAlias": "Ya tienes este alias de correo", "You already have the mailAlias": "Ya tienes este alias de correo",
"The alias cant be modified": "Este alias de correo no puede ser modificado", "The alias cant be modified": "Este alias de correo no puede ser modificado",
"No tickets to invoice": "No hay tickets para facturar", "No tickets to invoice": "No hay tickets para facturar que cumplan los requisitos de facturación",
"this warehouse has not dms": "El Almacén no acepta documentos", "this warehouse has not dms": "El Almacén no acepta documentos",
"This ticket already has a cmr saved": "Este ticket ya tiene un cmr guardado", "This ticket already has a cmr saved": "Este ticket ya tiene un cmr guardado",
"Name should be uppercase": "El nombre debe ir en mayúscula", "Name should be uppercase": "El nombre debe ir en mayúscula",

View File

@ -339,7 +339,7 @@
"Incorrect pin": "Pin incorrect.", "Incorrect pin": "Pin incorrect.",
"You already have the mailAlias": "Vous avez déjà cet alias de courrier", "You already have the mailAlias": "Vous avez déjà cet alias de courrier",
"The alias cant be modified": "Cet alias de courrier ne peut pas être modifié", "The alias cant be modified": "Cet alias de courrier ne peut pas être modifié",
"No tickets to invoice": "Pas de tickets à facturer", "No tickets to invoice": "Il n'y a pas de tickets à facturer qui répondent aux exigences de facturation",
"this warehouse has not dms": "L'entrepôt n'accepte pas les documents", "this warehouse has not dms": "L'entrepôt n'accepte pas les documents",
"This ticket already has a cmr saved": "Ce ticket a déjà un cmr enregistré", "This ticket already has a cmr saved": "Ce ticket a déjà un cmr enregistré",
"Name should be uppercase": "Le nom doit être en majuscules", "Name should be uppercase": "Le nom doit être en majuscules",

View File

@ -339,7 +339,7 @@
"Incorrect pin": "PIN incorreto.", "Incorrect pin": "PIN incorreto.",
"You already have the mailAlias": "Você já tem o alias de e-mail", "You already have the mailAlias": "Você já tem o alias de e-mail",
"The alias cant be modified": "O alias não pode ser modificado", "The alias cant be modified": "O alias não pode ser modificado",
"No tickets to invoice": "Não há tickets para faturar", "No tickets to invoice": "Não há bilhetes para faturar que atendam aos requisitos de faturamento",
"this warehouse has not dms": "Este armazém não tem DMS", "this warehouse has not dms": "Este armazém não tem DMS",
"This ticket already has a cmr saved": "Este ticket já tem um CMR salvo", "This ticket already has a cmr saved": "Este ticket já tem um CMR salvo",
"Name should be uppercase": "O nome deve estar em maiúsculas", "Name should be uppercase": "O nome deve estar em maiúsculas",

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

@ -2,7 +2,7 @@ const UserError = require('vn-loopback/util/user-error');
module.exports = function(Self) { module.exports = function(Self) {
Self.remoteMethod('canBeInvoiced', { Self.remoteMethod('canBeInvoiced', {
description: 'Change property isEqualizated in all client addresses', description: 'Check if a client can be invoiced',
accessType: 'READ', accessType: 'READ',
accepts: [ accepts: [
{ {
@ -38,7 +38,7 @@ module.exports = function(Self) {
Object.assign(myOptions, options); Object.assign(myOptions, options);
const client = await models.Client.findById(id, { const client = await models.Client.findById(id, {
fields: ['id', 'isTaxDataChecked', 'hasToInvoice', 'payMethodFk'], fields: ['id', 'isTaxDataChecked', 'hasToInvoice', 'payMethodFk', 'isActive'],
include: include:
{ {
relation: 'payMethod', relation: 'payMethod',
@ -53,9 +53,6 @@ module.exports = function(Self) {
if (client.payMethod().code === 'wireTransfer' && !company.supplierAccountFk) if (client.payMethod().code === 'wireTransfer' && !company.supplierAccountFk)
throw new UserError('The company has not informed the supplier account for bank transfers'); throw new UserError('The company has not informed the supplier account for bank transfers');
if (client.isTaxDataChecked && client.hasToInvoice) return client.isTaxDataChecked && client.hasToInvoice && client.isActive;
return true;
return false;
}; };
}; };

View File

@ -8,6 +8,8 @@ describe('client canBeInvoiced()', () => {
const activeCtx = { const activeCtx = {
accessToken: {userId: userId} accessToken: {userId: userId}
}; };
let tx;
let options;
beforeAll(async() => { beforeAll(async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
@ -15,60 +17,45 @@ describe('client canBeInvoiced()', () => {
}); });
}); });
beforeEach(async() => {
tx = await models.Client.beginTransaction({});
options = {transaction: tx};
});
afterEach(async() => {
await tx.rollback();
});
it('should return falsy for a client without the data checked', async() => { it('should return falsy for a client without the data checked', async() => {
const tx = await models.Client.beginTransaction({});
try {
const options = {transaction: tx};
const client = await models.Client.findById(clientId, null, options); const client = await models.Client.findById(clientId, null, options);
await client.updateAttribute('isTaxDataChecked', false, options); await client.updateAttribute('isTaxDataChecked', false, options);
const canBeInvoiced = await models.Client.canBeInvoiced(clientId, companyId, options); const canBeInvoiced = await models.Client.canBeInvoiced(clientId, companyId, options);
expect(canBeInvoiced).toEqual(false); expect(canBeInvoiced).toEqual(false);
});
await tx.rollback(); it('should return falsy for a client not active', async() => {
} catch (e) { const client = await models.Client.findById(clientId, null, options);
await tx.rollback(); await client.updateAttribute('isActive', false, options);
throw e;
} const canBeInvoiced = await models.Client.canBeInvoiced(clientId, companyId, options);
expect(canBeInvoiced).toEqual(false);
}); });
it('should return falsy for a client with invoicing disabled', async() => { it('should return falsy for a client with invoicing disabled', async() => {
const tx = await models.Client.beginTransaction({});
try {
const options = {transaction: tx};
const client = await models.Client.findById(clientId, null, options); const client = await models.Client.findById(clientId, null, options);
await client.updateAttribute('hasToInvoice', false, options); await client.updateAttribute('hasToInvoice', false, options);
const canBeInvoiced = await models.Client.canBeInvoiced(clientId, companyId, options); const canBeInvoiced = await models.Client.canBeInvoiced(clientId, companyId, options);
expect(canBeInvoiced).toEqual(false); expect(canBeInvoiced).toEqual(false);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should return truthy for an invoiceable client', async() => { it('should return truthy for an invoiceable client', async() => {
const tx = await models.Client.beginTransaction({});
try {
const options = {transaction: tx};
const canBeInvoiced = await models.Client.canBeInvoiced(clientId, companyId, options); const canBeInvoiced = await models.Client.canBeInvoiced(clientId, companyId, options);
expect(canBeInvoiced).toEqual(true); expect(canBeInvoiced).toEqual(true);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -84,6 +84,11 @@
"type": "belongsTo", "type": "belongsTo",
"model": "CustomsAgent", "model": "CustomsAgent",
"foreignKey": "customsAgentFk" "foreignKey": "customsAgentFk"
},
"postcode": {
"type": "belongsTo",
"model": "Postcode",
"foreignKey": "postalCode"
} }
} }
} }

View File

@ -1,3 +1,5 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('clientsToInvoice', { Self.remoteMethodCtx('clientsToInvoice', {
description: 'Get the clients to make global invoicing', description: 'Get the clients to make global invoicing',
@ -47,7 +49,6 @@ module.exports = Self => {
} }
try { try {
// Packaging liquidation
const vIsAllInvoiceable = false; const vIsAllInvoiceable = false;
await Self.rawSql('CALL ticketPackaging_add(?, ?, ?, ?)', [ await Self.rawSql('CALL ticketPackaging_add(?, ?, ?, ?)', [
clientId, clientId,
@ -71,9 +72,6 @@ module.exports = Self => {
AND t.shipped BETWEEN ? AND util.dayEnd(?) AND t.shipped BETWEEN ? AND util.dayEnd(?)
AND (t.clientFk = ? OR ? IS NULL ) AND (t.clientFk = ? OR ? IS NULL )
AND t.companyFk = ? AND t.companyFk = ?
AND c.hasToInvoice
AND c.isTaxDataChecked
AND c.isActive
AND NOT t.isDeleted AND NOT t.isDeleted
GROUP BY IF(c.hasToInvoiceByAddress, a.id, c.id) GROUP BY IF(c.hasToInvoiceByAddress, a.id, c.id)
HAVING SUM(t.totalWithVat) > 0;`; HAVING SUM(t.totalWithVat) > 0;`;

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

@ -1,14 +1,15 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('InvoiceOut clientsToInvoice()', () => { describe('InvoiceOut clientsToInvoice()', () => {
const userId = 1; const userId = 1;
const clientId = 1101; const clientId = 1101;
const companyFk = 442; const companyFk = 442;
const maxShipped = new Date(); const maxShipped = Date.vnNew();
maxShipped.setMonth(11); maxShipped.setMonth(11);
maxShipped.setDate(31); maxShipped.setDate(31);
maxShipped.setHours(23, 59, 59, 999); maxShipped.setHours(23, 59, 59, 999);
const invoiceDate = new Date(); const invoiceDate = Date.vnNew();
const activeCtx = { const activeCtx = {
getLocale: () => { getLocale: () => {
return 'en'; return 'en';
@ -20,6 +21,21 @@ describe('InvoiceOut clientsToInvoice()', () => {
headers: {origin: 'http://localhost'} headers: {origin: 'http://localhost'}
}; };
const ctx = {req: activeCtx}; const ctx = {req: activeCtx};
let tx;
let options;
beforeEach(async() => {
LoopBackContext.getCurrentContext = () => ({
active: activeCtx,
});
tx = await models.InvoiceOut.beginTransaction({});
options = {transaction: tx};
});
afterEach(async() => {
await tx.rollback();
});
it('should return a list of clients to invoice', async() => { it('should return a list of clients to invoice', async() => {
spyOn(models.InvoiceOut, 'rawSql').and.callFake(query => { spyOn(models.InvoiceOut, 'rawSql').and.callFake(query => {
@ -37,10 +53,6 @@ describe('InvoiceOut clientsToInvoice()', () => {
} }
}); });
const tx = await models.InvoiceOut.beginTransaction({});
const options = {transaction: tx};
try {
const addresses = await models.InvoiceOut.clientsToInvoice( const addresses = await models.InvoiceOut.clientsToInvoice(
ctx, clientId, invoiceDate, maxShipped, companyFk, options); ctx, clientId, invoiceDate, maxShipped, companyFk, options);
@ -49,12 +61,6 @@ describe('InvoiceOut clientsToInvoice()', () => {
expect(addresses[0].clientName).toBe('Test Client'); expect(addresses[0].clientName).toBe('Test Client');
expect(addresses[0].id).toBe(1); expect(addresses[0].id).toBe(1);
expect(addresses[0].nickname).toBe('Address 1'); expect(addresses[0].nickname).toBe('Address 1');
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should handle errors and rollback transaction', async() => { it('should handle errors and rollback transaction', async() => {
@ -62,14 +68,20 @@ describe('InvoiceOut clientsToInvoice()', () => {
return Promise.reject(new Error('Test Error')); return Promise.reject(new Error('Test Error'));
}); });
const tx = await models.InvoiceOut.beginTransaction({});
const options = {transaction: tx};
try { try {
await models.InvoiceOut.clientsToInvoice(ctx, clientId, invoiceDate, maxShipped, companyFk, options); await models.InvoiceOut.clientsToInvoice(ctx, clientId, invoiceDate, maxShipped, companyFk, options);
} catch (e) { } catch (e) {
expect(e.message).toBe('Test Error'); expect(e.message).toBe('Test Error');
await tx.rollback();
} }
}); });
it('should return all list', async() => {
const minShipped = Date.vnNew();
minShipped.setFullYear(maxShipped.getFullYear() - 1);
const toInvoice = await models.InvoiceOut.clientsToInvoice(
ctx, null, invoiceDate, maxShipped, companyFk, options);
expect(toInvoice).toBeDefined();
});
}); });

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

@ -154,6 +154,9 @@
}, },
"photoMotivation": { "photoMotivation": {
"type": "string" "type": "string"
},
"isCustomInspectionRequired": {
"type": "boolean"
} }
}, },
"relations": { "relations": {

View File

@ -50,7 +50,8 @@ module.exports = Self => {
su.name scannerUserName, su.name scannerUserName,
es.scanned, es.scanned,
est.description state, est.description state,
de.longName de.longName,
de.itemFk
FROM vn.expedition e FROM vn.expedition e
LEFT JOIN vn.expeditionStateType est ON est.id = e.stateTypeFk LEFT JOIN vn.expeditionStateType est ON est.id = e.stateTypeFk
INNER JOIN vn.item i1 ON i1.id = e.freightItemFk INNER JOIN vn.item i1 ON i1.id = e.freightItemFk

View File

@ -77,6 +77,8 @@ describe('ticket makeInvoice()', () => {
await tx.rollback(); await tx.rollback();
} }
expect(error.message).toEqual(`The address of the customer must have information about Incoterms and Customs Agent`); expect(error.message).toEqual(
`The address of the customer must have information about Incoterms and Customs Agent`
);
}); });
}); });

View File

@ -189,7 +189,8 @@ module.exports = Self => {
b.stickers * b.stickers *
IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000
) AS DECIMAL(10,0) ) AS DECIMAL(10,0)
) as volumeKg ) as volumeKg,
MAX(i.isCustomInspectionRequired) isCustomInspectionRequired
FROM tmp.travel tr FROM tmp.travel tr
JOIN entry e ON e.travelFk = tr.id JOIN entry e ON e.travelFk = tr.id
JOIN buy b ON b.entryFk = e.id JOIN buy b ON b.entryFk = e.id

View File

@ -112,4 +112,17 @@ describe('Travel extraCommunityFilter()', () => {
expect(result.length).toEqual(2); expect(result.length).toEqual(2);
}); });
it('should return field isCustomInspectionRequired true', async() => {
const ctx = {
args: {
id: 2
}
};
const result = await app.models.Travel.extraCommunityFilter(ctx, filter);
expect(result[0].entries[0].isCustomInspectionRequired).toBeTruthy();
expect(result[0].entries[1].isCustomInspectionRequired).toBeFalsy();
});
}); });

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",