diff --git a/Jenkinsfile b/Jenkinsfile
index 5e46b456a..1e8f3e87f 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -121,7 +121,7 @@ pipeline {
steps {
script {
def packageJson = readJSON file: 'package.json'
- env.VERSION = packageJson.version
+ env.VERSION = "${packageJson.version}-vn${env.BUILD_ID}"
}
sh 'docker-compose build back'
}
@@ -159,7 +159,7 @@ pipeline {
steps {
script {
def packageJson = readJSON file: 'package.json'
- env.VERSION = packageJson.version
+ env.VERSION = "${packageJson.version}-vn${env.BUILD_ID}"
}
sh 'gulp build'
sh 'docker-compose build front'
@@ -179,7 +179,7 @@ pipeline {
steps {
script {
def packageJson = readJSON file: 'package.json'
- env.VERSION = packageJson.version
+ env.VERSION = "${packageJson.version}-vn${env.BUILD_ID}"
}
sh 'docker login --username $CREDENTIALS_USR --password $CREDENTIALS_PSW $REGISTRY'
sh 'docker-compose push'
@@ -210,7 +210,7 @@ pipeline {
steps {
script {
def packageJson = readJSON file: 'package.json'
- env.VERSION = packageJson.version
+ env.VERSION = "${packageJson.version}-vn${env.BUILD_ID}"
}
withKubeConfig([
serverUrl: "$KUBERNETES_API",
diff --git a/back/methods/mrw-config/createShipment.ejs b/back/methods/mrw-config/createShipment.ejs
index b7a1cd897..7468218f2 100644
--- a/back/methods/mrw-config/createShipment.ejs
+++ b/back/methods/mrw-config/createShipment.ejs
@@ -26,10 +26,11 @@
<%= expeditionData.fi %>
<%= expeditionData.clientName %>
<%= expeditionData.phone %>
+ <%= expeditionData.deliveryObservation %>
<%= expeditionData.created %>
- <%= expeditionData.expeditionDataId %>
+ <%= expeditionData.reference %>
<%= expeditionData.serviceType %>
1
<%= expeditionData.weekDays %>
diff --git a/back/methods/mrw-config/createShipment.js b/back/methods/mrw-config/createShipment.js
index 081a83382..b5bea648d 100644
--- a/back/methods/mrw-config/createShipment.js
+++ b/back/methods/mrw-config/createShipment.js
@@ -45,7 +45,7 @@ module.exports = Self => {
`SELECT
CASE co.code
WHEN 'ES' THEN a.postalCode
- WHEN 'PT' THEN LEFT(a.postalCode, 4)
+ WHEN 'PT' THEN LEFT(a.postalCode, mc.portugalPostCodeTrim)
WHEN 'AD' THEN REPLACE(a.postalCode, 'AD', '00')
END postalCode,
a.city,
@@ -56,18 +56,23 @@ module.exports = Self => {
c.phone,
DATE_FORMAT(t.shipped, '%d/%m/%Y') created,
t.shipped,
- e.id expeditionId,
- LPAD(IF(mw.params IS NULL, ms.serviceType, mw.serviceType), 4 ,'0') serviceType,
- IF(mw.weekdays, 'S', 'N') weekDays
+ CONCAT( e.ticketFk, LPAD(e.counter, mc.counterWidth, '0')) reference,
+ LPAD(IF(mw.serviceType IS NULL, ms.serviceType, mw.serviceType), mc.serviceTypeWidth,'0') serviceType,
+ IF(mw.weekdays, 'S', 'N') weekDays,
+ oa.description deliveryObservation
FROM expedition e
JOIN ticket t ON e.ticketFk = t.id
JOIN agencyMode am ON am.id = t.agencyModeFk
JOIN mrwService ms ON ms.agencyModeCodeFk = am.code
- LEFT JOIN mrwServiceWeekday mw ON mw.weekdays = DATE_FORMAT(t.shipped, '%a')
+ LEFT JOIN mrwServiceWeekday mw ON mw.weekdays | 1 << WEEKDAY(t.landed)
JOIN client c ON t.clientFk = c.id
JOIN address a ON t.addressFk = a.id
+ LEFT JOIN addressObservation oa ON oa.addressFk = a.id
+ LEFT JOIN observationType ot ON ot.id = oa.observationTypeFk
+ AND ot.code = 'delivery'
JOIN province p ON a.provinceFk = p.id
JOIN country co ON co.id = p.countryFk
+ JOIN mrwConfig mc
WHERE e.id = ?
LIMIT 1`;
diff --git a/back/models/collection.json b/back/models/collection.json
index cb8dc3d7c..8a8afeb89 100644
--- a/back/models/collection.json
+++ b/back/models/collection.json
@@ -1,6 +1,16 @@
{
"name": "Collection",
"base": "VnModel",
+ "properties": {
+ "id": {
+ "id": true,
+ "type": "number",
+ "required": true
+ },
+ "workerFk": {
+ "type": "number"
+ }
+ },
"options": {
"mysql": {
"table": "collection"
diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql
index 6e8313a66..30f1ceb5e 100644
--- a/db/dump/fixtures.before.sql
+++ b/db/dump/fixtures.before.sql
@@ -762,7 +762,12 @@ INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeF
(30, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL, NULL),
(31, 1, 8, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL + 2 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL, NULL),
(32, 1, 8, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL + 2 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL, NULL),
- (33, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1102, 'NY roofs', 122, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL, NULL);
+ (33, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1102, 'NY roofs', 122, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL, NULL),
+ (34, 1, 1, 1, 3, util.VN_CURDATE(), util.VN_CURDATE(), 1103, 'BEJAR', 123, NULL, 0, 1, 16, 0, util.VN_CURDATE(), NULL, NULL),
+ (35, 1, 1, 1, 3, util.VN_CURDATE(), util.VN_CURDATE(), 1102, 'Somewhere in Philippines', 123, NULL, 0, 1, 16, 0, util.VN_CURDATE(), NULL, NULL),
+ (36, 1, 1, 1, 3, util.VN_CURDATE(), util.VN_CURDATE(), 1102, 'Ant-Man Adventure', 123, NULL, 0, 1, 16, 0, util.VN_CURDATE(), NULL, NULL),
+ (37, 1, 1, 1, 3, util.VN_CURDATE(), util.VN_CURDATE(), 1110, 'Deadpool swords', 123, NULL, 0, 1, 16, 0, util.VN_CURDATE(), NULL, NULL);
+
INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `description`)
VALUES
(1, 11, 1, 'ready'),
@@ -808,7 +813,10 @@ INSERT INTO `vn`.`ticketTracking`(`ticketFk`, `stateFk`, `userFk`, `created`)
(21, 1, 19, DATE_ADD(util.VN_NOW(), INTERVAL +1 MONTH)),
(22, 1, 19, DATE_ADD(util.VN_NOW(), INTERVAL +1 MONTH)),
(23, 16, 21, util.VN_NOW()),
- (24, 16, 21, util.VN_NOW());
+ (24, 16, 21, util.VN_NOW()),
+ (34, 14, 49, util.VN_NOW()),
+ (35, 14, 18, util.VN_NOW()),
+ (36, 14, 18, util.VN_NOW());
INSERT INTO `vn`.`deliveryPoint` (`id`, `name`, `ubication`)
VALUES
@@ -1068,7 +1076,10 @@ INSERT INTO `vn`.`sale`(`id`, `itemFk`, `ticketFk`, `concept`, `quantity`, `pric
(37, 4, 31, 'Melee weapon heavy shield 100cm', 20, 1.72, 0, 0, 0, util.VN_CURDATE()),
(36, 4, 30, 'Melee weapon heavy shield 100cm', 20, 1.72, 0, 0, 0, util.VN_CURDATE()),
(38, 2, 32, 'Melee weapon combat fist 15cm', 30, 7.07, 0, 0, 0, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH)),
- (39, 1, 32, 'Ranged weapon longbow 200cm', 2, 103.49, 0, 0, 0, util.VN_CURDATE());
+ (39, 1, 32, 'Ranged weapon longbow 200cm', 2, 103.49, 0, 0, 0, util.VN_CURDATE()),
+ (40, 2, 34, 'Melee weapon combat fist 15cm', 10.00, 3.91, 0, 0, 0, util.VN_CURDATE()),
+ (41, 2, 35, 'Melee weapon combat fist 15cm', 8.00, 3.01, 0, 0, 0, util.VN_CURDATE()),
+ (42, 2, 36, 'Melee weapon combat fist 15cm', 6.00, 2.50, 0, 0, 0, util.VN_CURDATE());
INSERT INTO `vn`.`saleComponent`(`saleFk`, `componentFk`, `value`)
VALUES
@@ -1247,14 +1258,20 @@ INSERT INTO `vn`.`operator` (`workerFk`, `numberOfWagons`, `trainFk`, `itemPacki
INSERT INTO `vn`.`collection`(`id`, `workerFk`, `stateFk`, `created`, `trainFk`)
VALUES
(1, 1106, 5, DATE_ADD(util.VN_CURDATE(),INTERVAL +1 DAY), 1),
- (2, 1106, 14, util.VN_CURDATE(), 1);
+ (2, 1106, 14, util.VN_CURDATE(), 1),
+ (4, 49, 5, util.VN_CURDATE(), 1),
+ (5, 18, 5, util.VN_CURDATE(), 1),
+ (6, 18, 5, util.VN_CURDATE(), 1);
INSERT INTO `vn`.`ticketCollection`(`ticketFk`, `collectionFk`, `level`)
VALUES
(1, 1, 1),
(2, 1, NULL),
(3, 2, NULL),
- (23, 1, NULL);
+ (23, 1, NULL),
+ (34, 4, 1),
+ (35, 5, 1),
+ (8, 6, 1);
INSERT INTO `vn`.`genus`(`id`, `name`)
VALUES
@@ -3712,7 +3729,8 @@ INSERT IGNORE INTO vn.saleGroup
SET id = 4,
userFk = 1,
parkingFk = 9,
- sectorFk = 9992;
+ sectorFk = 9992,
+ ticketFk = 36;
INSERT IGNORE INTO vn.sectorCollectionSaleGroup
SET id = 9999,
@@ -3814,3 +3832,27 @@ INSERT INTO `vn`.`ledgerCompany` SET
INSERT INTO `vn`.`ledgerConfig` SET
maxTolerance = 0.01;
+
+INSERT INTO vn.sectorCollection
+ SET id = 2,
+ userFk = 18,
+ sectorFk = 1;
+
+INSERT INTO vn.sectorCollectionSaleGroup
+ SET id = 8,
+ sectorCollectionFk = 2,
+ saleGroupFk = 4;
+
+INSERT INTO vn.saleGroup (userFk, parkingFk, sectorFk, ticketFk)
+ VALUES
+ (1, 1, 1, 37);
+
+INSERT INTO vn.sectorCollection
+ SET id = 3,
+ userFk = 18,
+ sectorFk = 1;
+
+INSERT INTO vn.sectorCollectionSaleGroup
+ SET id = 9,
+ sectorCollectionFk = 3,
+ saleGroupFk = 6;
\ No newline at end of file
diff --git a/db/routines/vn/functions/client_getFromPhone.sql b/db/routines/vn/functions/client_getFromPhone.sql
index c6ded691b..5e4daa532 100644
--- a/db/routines/vn/functions/client_getFromPhone.sql
+++ b/db/routines/vn/functions/client_getFromPhone.sql
@@ -11,10 +11,7 @@ BEGIN
*/
DECLARE vClient INT DEFAULT NULL;
- -- SET vPhone = vPhone COLLATE 'utf8_unicode_ci';
-
- DROP TEMPORARY TABLE IF EXISTS tClient;
- CREATE TEMPORARY TABLE tClient
+ CREATE OR REPLACE TEMPORARY TABLE tClient
ENGINE = MEMORY
SELECT id clientFk
FROM `client`
@@ -27,13 +24,14 @@ BEGIN
OR mobile = vPhone
UNION
SELECT clientFk
- FROM vn.clientContact
+ FROM clientContact
WHERE phone = vPhone;
SELECT t.clientFk INTO vClient
FROM tClient t
JOIN `client` c ON c.id = t.clientFk
WHERE c.isActive
+ AND c.salesPersonFk
LIMIT 1;
DROP TEMPORARY TABLE tClient;
diff --git a/db/versions/11015-silverBamboo/00-photoMotivation.sql b/db/versions/11015-silverBamboo/00-photoMotivation.sql
new file mode 100644
index 000000000..366694e12
--- /dev/null
+++ b/db/versions/11015-silverBamboo/00-photoMotivation.sql
@@ -0,0 +1 @@
+ALTER TABLE vn.item ADD COLUMN photoMotivation VARCHAR(255);
\ No newline at end of file
diff --git a/db/versions/11060-tealGalax/00-createRoleReviewer.sql b/db/versions/11060-tealGalax/00-createRoleReviewer.sql
new file mode 100644
index 000000000..bf2984702
--- /dev/null
+++ b/db/versions/11060-tealGalax/00-createRoleReviewer.sql
@@ -0,0 +1,46 @@
+use account;
+
+INSERT INTO role
+ SET name = 'reviewer',
+ description = 'Revisor de producción',
+ hasLogin = TRUE,
+ created = util.VN_CURDATE(),
+ modified = util.VN_CURDATE(),
+ editorFk = NULL;
+
+INSERT INTO roleInherit(
+ role,
+ inheritsFrom
+)
+ SELECT r1.id,
+ r2.id
+ FROM role r1
+ JOIN role r2
+ WHERE r1.name = 'reviewer'
+ AND r2.name = 'production'
+ UNION
+ SELECT ri.role,
+ r2.id
+ FROM roleInherit ri
+ JOIN role r1 ON r1.id = ri.role
+ JOIN role r2 ON r2.name = 'reviewer'
+ WHERE r1.name IN ('claimManager', 'productionBoss')
+ GROUP BY ri.role;
+
+DELETE ri
+ FROM roleInherit ri
+ JOIN role r1 ON ri.role = r1.id
+ JOIN role r2 ON ri.inheritsFrom = r2.id
+ WHERE r1.name = 'replenisher'
+ AND r2.name = 'buyer';
+
+UPDATE salix.ACL
+ SET principalId = 'reviewer'
+ WHERE property = 'isInPreparing';
+
+UPDATE user u
+ JOIN vn.workerDepartment wd ON wd.workerFk = u.id
+ JOIN vn.department d ON wd.departmentFk = d.id
+ JOIN role r ON r.name = 'reviewer'
+ SET u.role = r.id
+ WHERE d.name IN ('REVISION', 'PREVIA');
\ No newline at end of file
diff --git a/db/versions/11078-goldenFern/00-firstScript.sql b/db/versions/11078-goldenFern/00-firstScript.sql
new file mode 100644
index 000000000..63888a1bb
--- /dev/null
+++ b/db/versions/11078-goldenFern/00-firstScript.sql
@@ -0,0 +1,2 @@
+ALTER TABLE vn.productionConfig ADD scannablePreviusCodeType enum('qr','barcode')
+ CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT 'barcode' NOT NULL;
\ No newline at end of file
diff --git a/db/versions/11079-goldenFern/00-firstScript.sql b/db/versions/11079-goldenFern/00-firstScript.sql
new file mode 100644
index 000000000..14d9ddb5f
--- /dev/null
+++ b/db/versions/11079-goldenFern/00-firstScript.sql
@@ -0,0 +1,3 @@
+-- Place your SQL code here
+ALTER TABLE vn.mrwConfig ADD IF NOT EXISTS expeditionDeadLine TIME NULL
+COMMENT 'This field stores the latest time by which expeditions can be generated to be sent today';
\ No newline at end of file
diff --git a/db/versions/11080-maroonAnthurium/00-firstScript.sql b/db/versions/11080-maroonAnthurium/00-firstScript.sql
new file mode 100644
index 000000000..651cd4c7c
--- /dev/null
+++ b/db/versions/11080-maroonAnthurium/00-firstScript.sql
@@ -0,0 +1,9 @@
+-- Place your SQL code here
+ALTER TABLE vn.mrwConfig ADD IF NOT EXISTS counterWidth INT UNSIGNED NULL
+ COMMENT 'If it does not reach the required value, it will be padded with zeros on the left to meet the specified length.';
+
+ALTER TABLE vn.mrwConfig ADD IF NOT EXISTS serviceTypeWidth INT UNSIGNED NULL
+ COMMENT 'If it does not reach the required value, it will be padded with zeros on the left to meet the specified length.';
+
+ALTER TABLE vn.mrwConfig ADD IF NOT EXISTS portugalPostCodeTrim INT UNSIGNED NULL
+ COMMENT 'It will trim the last characters of the postal code';
diff --git a/db/versions/11083-purpleBamboo/00-firstScript.sql b/db/versions/11083-purpleBamboo/00-firstScript.sql
new file mode 100644
index 000000000..95e5c30a1
--- /dev/null
+++ b/db/versions/11083-purpleBamboo/00-firstScript.sql
@@ -0,0 +1,3 @@
+
+INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
+ VALUES ('Ticket','refund','WRITE','ALLOW','ROLE','logistic');
diff --git a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
index 4f54ad860..e0f32fc3a 100644
--- a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
+++ b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
@@ -225,7 +225,7 @@ describe('Ticket Edit sale path', () => {
});
it('should show error trying to delete a ticket with a refund', async() => {
- await page.loginAndModule('production', 'ticket');
+ await page.loginAndModule('salesPerson', 'ticket');
await page.accessToSearchResult('8');
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuDeleteTicket);
diff --git a/front/salix/components/upload-photo/index.js b/front/salix/components/upload-photo/index.js
index c9774d037..7779c81e1 100644
--- a/front/salix/components/upload-photo/index.js
+++ b/front/salix/components/upload-photo/index.js
@@ -164,6 +164,7 @@ export default class UploadPhoto extends Component {
const options = {
type: 'blob',
+ size: 'original'
};
return this.editor.result(options)
.then(blob => this.newPhoto.blob = blob)
diff --git a/loopback/locale/en.json b/loopback/locale/en.json
index b65b9c852..cb9e1d12c 100644
--- a/loopback/locale/en.json
+++ b/loopback/locale/en.json
@@ -61,7 +61,8 @@
"Changed sale discount": "I have changed the following lines discounts from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Created claim": "I have created the claim [{{claimId}}]({{{claimUrl}}}) for the following lines from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Changed sale price": "I have changed the price of [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) from {{oldPrice}}€ ➔ *{{newPrice}}€* of the ticket [{{ticketId}}]({{{ticketUrl}}})",
- "Changed sale quantity": "I have changed the quantity of [{{itemId}} {{concept}}]({{{itemUrl}}}) from {{oldQuantity}} ➔ *{{newQuantity}}* of the ticket [{{ticketId}}]({{{ticketUrl}}})",
+ "Changed sale quantity": "I have changed {{changes}} of the ticket [{{ticketId}}]({{{ticketUrl}}})",
+ "Changes in sales": "the quantity of [{{itemId}} {{concept}}]({{{itemUrl}}}) from {{oldQuantity}} ➔ *{{newQuantity}}*",
"Changed sale reserved state": "I have changed the following lines reserved state from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Bought units from buy request": "Bought {{quantity}} units of [{{itemId}} {{concept}}]({{{urlItem}}}) for the ticket id [{{ticketId}}]({{{url}}})",
"MESSAGE_INSURANCE_CHANGE": "I have changed the insurence credit of client [{{clientName}} ({{clientId}})]({{{url}}}) to *{{credit}} €*",
diff --git a/loopback/locale/es.json b/loopback/locale/es.json
index 14b40f5de..f94e21d7b 100644
--- a/loopback/locale/es.json
+++ b/loopback/locale/es.json
@@ -124,7 +124,8 @@
"Changed sale discount": "He cambiado el descuento de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Created claim": "He creado la reclamación [{{claimId}}]({{{claimUrl}}}) de las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Changed sale price": "He cambiado el precio de [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* del ticket [{{ticketId}}]({{{ticketUrl}}})",
- "Changed sale quantity": "He cambiado la cantidad de [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}* del ticket [{{ticketId}}]({{{ticketUrl}}})",
+ "Changed sale quantity": "He cambiado {{changes}} del ticket [{{ticketId}}]({{{ticketUrl}}})",
+ "Changes in sales": "la cantidad de [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}*",
"State": "Estado",
"regular": "normal",
"reserved": "reservado",
@@ -358,8 +359,8 @@
"Select ticket or client": "Elija un ticket o un client",
"It was not able to create the invoice": "No se pudo crear la factura",
"ticketCommercial": "El ticket {{ ticket }} para el vendedor {{ salesMan }} está en preparación. (mensaje generado automáticamente)",
- "This PDA is already assigned to another user": "Este PDA ya está asignado a otro usuario",
- "You can only have one PDA": "Solo puedes tener un PDA",
- "Incoterms and Customs agent are required for a non UEE member": "Se requieren Incoterms y agente de aduanas para un no miembro de la UEE",
+ "This PDA is already assigned to another user": "Esta PDA ya está asignado a otro usuario",
+ "You can only have one PDA": "Solo puedes tener una PDA",
+ "Incoterms and Customs agent are required for a non UEE member": "Se requieren Incoterms y agente de aduanas para un no miembro de la UEE",
"You can not use the same password": "No puedes usar la misma contraseña"
}
diff --git a/loopback/locale/fr.json b/loopback/locale/fr.json
index 44f5e35d3..6f3919e18 100644
--- a/loopback/locale/fr.json
+++ b/loopback/locale/fr.json
@@ -123,8 +123,9 @@
"Added sale to ticket": "J'ai ajouté la ligne suivante au ticket [{{ticketId}}]({{{ticketUrl}}}): {{{addition}}}",
"Changed sale discount": "J'ai changé le rabais des lignes suivantes du ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Created claim": "J'ai créé la réclamation [{{claimId}}]({{{claimUrl}}}) des lignes suivantes du ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
- "Changed sale price": "J'ai changé le prix de [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* du ticket [{{ticketId}}]({{{ticketUrl}}})",
- "Changed sale quantity": "J'ai changé la quantité de {{itemId}} {{concept}} de {{oldQuantity}} ➔ {{newQuantity}} du ticket [{{ticketId}}]({{{ticketUrl}}})",
+ "Changed sale price": " le prix de [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* du ticket [{{ticketId}}]({{{ticketUrl}}})",,
+ "Changed sale quantity": "J'ai changé {{changes}} du ticket [{{ticketId}}]({{{ticketUrl}}})",
+ "Changes in sales": "la quantité de {{itemId}} {{concept}} de {{oldQuantity}} ➔ {{newQuantity}}",
"State": "État",
"regular": "normal",
"reserved": "réservé",
@@ -357,4 +358,4 @@
"This workCenter is already assigned to this agency": "Ce centre de travail est déjà assigné à cette agence",
"Select ticket or client": "Choisissez un ticket ou un client",
"It was not able to create the invoice": "Il n'a pas été possible de créer la facture"
-}
\ No newline at end of file
+}
diff --git a/loopback/locale/pt.json b/loopback/locale/pt.json
index b11eeefc6..3c156506c 100644
--- a/loopback/locale/pt.json
+++ b/loopback/locale/pt.json
@@ -124,7 +124,8 @@
"Changed sale discount": "Desconto da venda alterado no ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Created claim": "Reclamação criada [{{claimId}}]({{{claimUrl}}}) no ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Changed sale price": "Preço da venda alterado para [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* no ticket [{{ticketId}}]({{{ticketUrl}}})",
- "Changed sale quantity": "Quantidade da venda alterada para [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}* no ticket [{{ticketId}}]({{{ticketUrl}}})",
+ "Changed sale quantity": "Quantidade da venda alterada para {{changes}} no ticket [{{ticketId}}]({{{ticketUrl}}})",
+ "Changes in sales": " [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}* ",
"State": "Estado",
"regular": "normal",
"reserved": "reservado",
@@ -357,4 +358,4 @@
"This workCenter is already assigned to this agency": "Este centro de trabalho já está atribuído a esta agência",
"Select ticket or client": "Selecione um ticket ou cliente",
"It was not able to create the invoice": "Não foi possível criar a fatura"
-}
\ No newline at end of file
+}
diff --git a/loopback/util/log.js b/loopback/util/log.js
index 76e87781d..d2b78ce70 100644
--- a/loopback/util/log.js
+++ b/loopback/util/log.js
@@ -85,8 +85,12 @@ exports.translateValues = async(instance, changes, options = {}) => {
exports.getChanges = (original, changes) => {
const oldChanges = {};
const newChanges = {};
+ const dateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
for (let property in changes) {
+ if (dateRegex.test(original[property]))
+ original[property] = new Date(Date.parse(original[property]));
+
const firstChar = property.substring(0, 1);
const isPrivate = firstChar == '$';
if (isPrivate) return;
diff --git a/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.spec.js b/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.spec.js
index b05b2ac15..156caaeec 100644
--- a/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.spec.js
+++ b/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.spec.js
@@ -6,6 +6,7 @@ describe('claimBeginning', () => {
const claimManagerId = 72;
const activeCtx = {
accessToken: {userId: claimManagerId},
+ __: value => value
};
const ctx = {req: activeCtx};
diff --git a/modules/claim/back/methods/claim/updateClaim.js b/modules/claim/back/methods/claim/updateClaim.js
index a206d7f3e..c99e0e2f1 100644
--- a/modules/claim/back/methods/claim/updateClaim.js
+++ b/modules/claim/back/methods/claim/updateClaim.js
@@ -88,15 +88,17 @@ module.exports = Self => {
const updatedClaim = await claim.updateAttributes(args, myOptions);
// When pickup has been changed
- if (salesPerson && changedPickup && updatedClaim.pickup)
- await notifyPickUp(ctx, salesPerson.id, claim);
+ if (salesPerson) {
+ if (changedPickup && updatedClaim.pickup)
+ await notifyPickUp(ctx, salesPerson.id, claim);
- // When claimState has been changed
- if (args.claimStateFk) {
- const newState = await models.ClaimState.findById(args.claimStateFk, null, myOptions);
- await notifyStateChange(ctx, salesPerson.id, claim, newState.description);
- if (newState.code == 'canceled')
- await notifyStateChange(ctx, claim.workerFk, claim, newState.description);
+ // When claimState has been changed
+ if (args.claimStateFk) {
+ const newState = await models.ClaimState.findById(args.claimStateFk, null, myOptions);
+ await notifyStateChange(ctx, salesPerson.id, claim, newState.description);
+ if (newState.code == 'canceled')
+ await notifyStateChange(ctx, claim.workerFk, claim, newState.description);
+ }
}
if (tx) await tx.commit();
diff --git a/modules/entry/back/model-config.json b/modules/entry/back/model-config.json
index 7b6e23685..dc7fd86be 100644
--- a/modules/entry/back/model-config.json
+++ b/modules/entry/back/model-config.json
@@ -22,5 +22,8 @@
},
"EntryObservation": {
"dataSource": "vn"
+ },
+ "EntryType": {
+ "dataSource": "vn"
}
-}
+}
\ No newline at end of file
diff --git a/modules/entry/back/models/entry-type.json b/modules/entry/back/models/entry-type.json
new file mode 100644
index 000000000..989aa3a8a
--- /dev/null
+++ b/modules/entry/back/models/entry-type.json
@@ -0,0 +1,25 @@
+{
+ "name": "EntryType",
+ "base": "VnModel",
+ "mixins": {
+ "Loggable": true
+ },
+ "options": {
+ "mysql": {
+ "table": "entryType"
+ }
+ },
+ "properties": {
+ "code": {
+ "type": "string",
+ "id": true,
+ "description": "Identifier"
+ },
+ "description": {
+ "type": "string"
+ },
+ "isInformal": {
+ "type": "boolean"
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/entry/back/models/entry.json b/modules/entry/back/models/entry.json
index ab451219e..833edf14d 100644
--- a/modules/entry/back/models/entry.json
+++ b/modules/entry/back/models/entry.json
@@ -35,9 +35,9 @@
},
"isVirtual": {
"type": "boolean",
- "mysql": {
- "columnName": "isRaid"
- }
+ "mysql": {
+ "columnName": "isRaid"
+ }
},
"isRaid": {
"type": "boolean"
@@ -53,9 +53,9 @@
},
"observation": {
"type": "string",
- "mysql": {
- "columnName": "evaNotes"
- }
+ "mysql": {
+ "columnName": "evaNotes"
+ }
},
"loadPriority": {
"type": "number"
@@ -101,6 +101,11 @@
"type": "belongsTo",
"model": "Account",
"foreignKey": "observationEditorFk"
+ },
+ "entryType": {
+ "type": "belongsTo",
+ "model": "EntryType",
+ "foreignKey": "typeFk"
}
}
-}
+}
\ No newline at end of file
diff --git a/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js b/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js
index 3ad06b242..989b1d4a2 100644
--- a/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js
+++ b/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js
@@ -54,6 +54,20 @@ module.exports = Self => {
value: rate
});
}
+ const monday = 1;
+ if (xmlDateWithoutTime.getDay() === monday) {
+ const saturday = new Date(xmlDateWithoutTime);
+ saturday.setDate(xmlDateWithoutTime.getDate() - 2);
+ const sunday = new Date(xmlDateWithoutTime);
+ sunday.setDate(xmlDateWithoutTime.getDate() - 1);
+
+ for (const date of [saturday, sunday]) {
+ await models.ReferenceRate.upsertWithWhere(
+ {currencyFk: currency.id, dated: date},
+ {currencyFk: currency.id, dated: date, value: rate}
+ );
+ }
+ }
}
}
}
diff --git a/modules/item/back/methods/item-barcode/toItem.js b/modules/item/back/methods/item-barcode/toItem.js
index 96e9d5981..d189b77c3 100644
--- a/modules/item/back/methods/item-barcode/toItem.js
+++ b/modules/item/back/methods/item-barcode/toItem.js
@@ -4,7 +4,7 @@ module.exports = Self => {
accessType: 'READ',
accepts: [{
arg: 'barcode',
- type: 'number',
+ type: 'string',
required: true,
description: 'barcode'
}],
@@ -18,7 +18,7 @@ module.exports = Self => {
}
});
- Self.toItem = async(barcode, options) => {
+ Self.toItem = async (barcode, options) => {
const myOptions = {};
if (typeof options == 'object')
diff --git a/modules/item/back/methods/item/specs/getBalance.spec.js b/modules/item/back/methods/item/specs/getBalance.spec.js
index 5e5148595..728b5f33e 100644
--- a/modules/item/back/methods/item/specs/getBalance.spec.js
+++ b/modules/item/back/methods/item/specs/getBalance.spec.js
@@ -64,7 +64,7 @@ describe('item getBalance()', () => {
const secondItemBalance = await models.Item.getBalance(ctx, secondFilter, options);
expect(firstItemBalance[9].claimFk).toEqual(null);
- expect(secondItemBalance[4].claimFk).toEqual(2);
+ expect(secondItemBalance[7].claimFk).toEqual(2);
await tx.rollback();
} catch (e) {
diff --git a/modules/item/back/models/item.json b/modules/item/back/models/item.json
index 9d48dcbfb..10cff3e04 100644
--- a/modules/item/back/models/item.json
+++ b/modules/item/back/models/item.json
@@ -155,6 +155,9 @@
"minQuantity": {
"type": "number",
"description": "Min quantity"
+ },
+ "photoMotivation": {
+ "type": "string"
}
},
"relations": {
diff --git a/modules/monitor/back/methods/sales-monitor/specs/salesFilter.spec.js b/modules/monitor/back/methods/sales-monitor/specs/salesFilter.spec.js
index bdafd14e2..c3da7f08b 100644
--- a/modules/monitor/back/methods/sales-monitor/specs/salesFilter.spec.js
+++ b/modules/monitor/back/methods/sales-monitor/specs/salesFilter.spec.js
@@ -151,7 +151,7 @@ describe('SalesMonitor salesFilter()', () => {
const result = await models.SalesMonitor.salesFilter(ctx, filter, options);
const firstRow = result[0];
- expect(result.length).toEqual(12);
+ expect(result.length).toEqual(15);
expect(firstRow.alertLevel).not.toEqual(0);
await tx.rollback();
diff --git a/modules/shelving/back/model-config.json b/modules/shelving/back/model-config.json
index 89a0832b0..6f3ffb5ea 100644
--- a/modules/shelving/back/model-config.json
+++ b/modules/shelving/back/model-config.json
@@ -11,6 +11,12 @@
"Sector": {
"dataSource": "vn"
},
+ "SectorCollection": {
+ "dataSource": "vn"
+ },
+ "SectorCollectionSaleGroup": {
+ "dataSource": "vn"
+ },
"Train": {
"dataSource": "vn"
}
diff --git a/modules/shelving/back/models/sectorCollection.json b/modules/shelving/back/models/sectorCollection.json
new file mode 100644
index 000000000..bf2cc7985
--- /dev/null
+++ b/modules/shelving/back/models/sectorCollection.json
@@ -0,0 +1,24 @@
+{
+ "name": "SectorCollection",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "sectorCollection"
+ }
+ },
+ "properties": {
+ "id": {
+ "type": "number",
+ "id": true
+ },
+ "created": {
+ "type": "date"
+ },
+ "userFk": {
+ "type": "number"
+ },
+ "sectorFk": {
+ "type": "number"
+ }
+ }
+}
diff --git a/modules/shelving/back/models/sectorCollectionSaleGroup.json b/modules/shelving/back/models/sectorCollectionSaleGroup.json
new file mode 100644
index 000000000..421bdc885
--- /dev/null
+++ b/modules/shelving/back/models/sectorCollectionSaleGroup.json
@@ -0,0 +1,30 @@
+{
+ "name": "SectorCollectionSaleGroup",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "sectorCollectionSaleGroup"
+ }
+ },
+ "properties": {
+ "id": {
+ "type": "number",
+ "id": true
+ },
+ "created": {
+ "type": "date"
+ }
+ },
+ "relations": {
+ "sectorCollection": {
+ "type": "belongsTo",
+ "model": "SectorCollection",
+ "foreignKey": "sectorCollectionFk"
+ },
+ "saleGroup": {
+ "type": "belongsTo",
+ "model": "SaleGroup",
+ "foreignKey": "saleGroupFk"
+ }
+ }
+}
diff --git a/modules/supplier/back/methods/supplier/getItemsPackaging.js b/modules/supplier/back/methods/supplier/getItemsPackaging.js
index c06195a55..8a27c89c4 100644
--- a/modules/supplier/back/methods/supplier/getItemsPackaging.js
+++ b/modules/supplier/back/methods/supplier/getItemsPackaging.js
@@ -33,7 +33,7 @@ module.exports = Self => {
JOIN vn.item i ON i.id = b.itemFk
WHERE e.id = ? AND e.supplierFk = ?
GROUP BY i.id
- ) SELECT i.id, i.name, et.quantity, SUM(b.quantity) quantityTotal, et.printedStickers
+ ) SELECT i.id, i.name, et.quantity, SUM(b.quantity) quantityTotal, et.printedStickers, ic.url
FROM vn.buy b
JOIN vn.item i ON i.id = b.itemFk
JOIN vn.entry e ON e.id = b.entryFk
@@ -41,6 +41,7 @@ module.exports = Self => {
JOIN vn.buyConfig bc ON bc.monthsAgo
JOIN vn.travel t ON t.id = e.travelFk
LEFT JOIN entryTmp et ON et.id = i.id
+ JOIN hedera.imageConfig ic
WHERE e.supplierFk = ?
AND i.family IN ('EMB', 'CONT')
AND b.created > (util.VN_CURDATE() - INTERVAL bc.monthsAgo MONTH)
diff --git a/modules/ticket/back/methods/ticket-tracking/specs/setDelivered.spec.js b/modules/ticket/back/methods/ticket-tracking/specs/setDelivered.spec.js
index 3d37221c4..3d39ea278 100644
--- a/modules/ticket/back/methods/ticket-tracking/specs/setDelivered.spec.js
+++ b/modules/ticket/back/methods/ticket-tracking/specs/setDelivered.spec.js
@@ -2,10 +2,12 @@ const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('ticket setDelivered()', () => {
- const userId = 50;
+ const userId = 49;
const activeCtx = {
accessToken: {userId: userId},
+ __: value => value
};
+ const ctx = {req: activeCtx};
beforeAll(async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
@@ -19,8 +21,6 @@ describe('ticket setDelivered()', () => {
try {
const options = {transaction: tx};
- const ctx = {req: {accessToken: {userId: 49}}};
-
const originalTicketOne = await models.Ticket.findById(8, null, options);
const originalTicketTwo = await models.Ticket.findById(10, null, options);
diff --git a/modules/ticket/back/methods/ticket/addSale.js b/modules/ticket/back/methods/ticket/addSale.js
index 826de6e12..8dc7a633c 100644
--- a/modules/ticket/back/methods/ticket/addSale.js
+++ b/modules/ticket/back/methods/ticket/addSale.js
@@ -10,8 +10,8 @@ module.exports = Self => {
http: {source: 'path'}
},
{
- arg: 'itemId',
- type: 'number',
+ arg: 'barcode',
+ type: 'any',
required: true
},
{
@@ -29,7 +29,7 @@ module.exports = Self => {
}
});
- Self.addSale = async(ctx, id, itemId, quantity, options) => {
+ Self.addSale = async(ctx, id, barcode, quantity, options) => {
const $t = ctx.req.__; // $translate
const models = Self.app.models;
const myOptions = {userId: ctx.req.accessToken.userId};
@@ -46,7 +46,9 @@ module.exports = Self => {
try {
await models.Ticket.isEditableOrThrow(ctx, id, myOptions);
+ const itemId = await models.ItemBarcode.toItem(barcode, myOptions);
const item = await models.Item.findById(itemId, null, myOptions);
+
const ticket = await models.Ticket.findById(id, {
include: {
relation: 'client',
diff --git a/modules/ticket/back/methods/ticket/addSaleByCode.js b/modules/ticket/back/methods/ticket/addSaleByCode.js
deleted file mode 100644
index ca3d2cb07..000000000
--- a/modules/ticket/back/methods/ticket/addSaleByCode.js
+++ /dev/null
@@ -1,56 +0,0 @@
-const UserError = require('vn-loopback/util/user-error');
-module.exports = Self => {
- Self.remoteMethodCtx('addSaleByCode', {
- description: 'Add a collection',
- accessType: 'WRITE',
- accepts: [
- {
- arg: 'barcode',
- type: 'string',
- required: true
- }, {
- arg: 'quantity',
- type: 'number',
- required: true
- }, {
- arg: 'ticketFk',
- type: 'number',
- required: true
- }, {
- arg: 'warehouseFk',
- type: 'number',
- required: true
- },
-
- ],
- http: {
- path: `/addSaleByCode`,
- verb: 'POST'
- },
- });
-
- Self.addSaleByCode = async(ctx, barcode, quantity, ticketFk, warehouseFk, options) => {
- const myOptions = {userId: ctx.req.accessToken.userId};
- let tx;
-
- if (typeof options == 'object')
- Object.assign(myOptions, options);
-
- if (!myOptions.transaction) {
- tx = await Self.beginTransaction({});
- myOptions.transaction = tx;
- }
-
- try {
- const [[item]] = await Self.rawSql('CALL vn.item_getInfo(?,?)', [barcode, warehouseFk], myOptions);
- if (!item?.available) throw new UserError('We do not have availability for the selected item');
-
- await Self.rawSql('CALL vn.collection_addItem(?, ?, ?)', [item.id, quantity, ticketFk], myOptions);
-
- if (tx) await tx.commit();
- } catch (e) {
- if (tx) await tx.rollback();
- throw e;
- }
- };
-};
diff --git a/modules/ticket/back/methods/ticket/isEditableOrThrow.js b/modules/ticket/back/methods/ticket/isEditableOrThrow.js
index 16cff84f1..555063093 100644
--- a/modules/ticket/back/methods/ticket/isEditableOrThrow.js
+++ b/modules/ticket/back/methods/ticket/isEditableOrThrow.js
@@ -8,18 +8,13 @@ module.exports = Self => {
if (typeof options == 'object')
Object.assign(myOptions, options);
- const state = await models.TicketState.findOne({
- where: {ticketFk: id}
- }, myOptions);
-
+ const state = await models.TicketState.findOne({where: {ticketFk: id}}, myOptions);
const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*');
+ const isProductionReviewer = await models.ACL.checkAccessAcl(ctx, 'Sale', 'isInPreparing', '*');
const canEditWeeklyTicket = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'canEditWeekly', 'WRITE');
const alertLevel = state ? state.alertLevel : null;
const ticket = await models.Ticket.findById(id, {
- fields: ['clientFk'],
- include: {
- relation: 'client'
- }
+ fields: ['clientFk'], include: {relation: 'client'}
}, myOptions);
const isLocked = await models.Ticket.isLocked(id, myOptions);
@@ -29,10 +24,24 @@ module.exports = Self => {
const isNormalClient = ticket && ticket.client().typeFk == 'normal';
const isEditable = !(alertLevelGreaterThanZero && isNormalClient);
+ const ticketCollection = await models.TicketCollection.findOne({
+ include: {relation: 'collection'}, where: {ticketFk: id}
+ }, myOptions);
+ let isOwner = ticketCollection?.collection()?.workerFk === ctx.req.accessToken.userId;
+
+ if (!isOwner) {
+ const saleGroup = await models.SaleGroup.findOne({fields: ['id'], where: {ticketFk: id}}, myOptions);
+ const sectorCollectionSaleGroup = saleGroup && await models.SectorCollectionSaleGroup.findOne({
+ include: {relation: 'sectorCollection'}, where: {saleGroupFk: saleGroup.id}
+ }, myOptions);
+
+ isOwner = sectorCollectionSaleGroup?.sectorCollection()?.userFk === ctx.req.accessToken.userId;
+ }
+
if (!ticket)
throw new ForbiddenError(`The ticket doesn't exist.`);
- if (!isEditable && !isRoleAdvanced)
+ if (!isEditable && !isRoleAdvanced && !isProductionReviewer && !isOwner)
throw new ForbiddenError(`This ticket is not editable.`);
if (isLocked && !isWeekly)
diff --git a/modules/ticket/back/methods/ticket/specs/addSaleByCode.spec.js b/modules/ticket/back/methods/ticket/specs/addSaleByCode.spec.js
deleted file mode 100644
index b97139178..000000000
--- a/modules/ticket/back/methods/ticket/specs/addSaleByCode.spec.js
+++ /dev/null
@@ -1,39 +0,0 @@
-const {models} = require('vn-loopback/server/server');
-const LoopBackContext = require('loopback-context');
-
-describe('Ticket addSaleByCode()', () => {
- const quantity = 3;
- const ticketFk = 13;
- const warehouseFk = 1;
- beforeAll(async() => {
- activeCtx = {
- req: {
- accessToken: {userId: 9},
- headers: {origin: 'http://localhost'},
- __: value => value
- }
- };
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
- active: activeCtx
- });
- });
-
- it('should add a new sale', async() => {
- const tx = await models.Ticket.beginTransaction({});
-
- try {
- const options = {transaction: tx};
- const code = '1111111111';
-
- const salesBefore = await models.Sale.find(null, options);
- await models.Ticket.addSaleByCode(activeCtx, code, quantity, ticketFk, warehouseFk, options);
- const salesAfter = await models.Sale.find(null, options);
-
- expect(salesAfter.length).toEqual(salesBefore.length + 1);
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
- });
-});
diff --git a/modules/ticket/back/methods/ticket/specs/filter.spec.js b/modules/ticket/back/methods/ticket/specs/filter.spec.js
index e495a41f5..8008acfaf 100644
--- a/modules/ticket/back/methods/ticket/specs/filter.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/filter.spec.js
@@ -68,7 +68,7 @@ describe('ticket filter()', () => {
const filter = {};
const result = await models.Ticket.filter(ctx, filter, options);
- expect(result.length).toEqual(7);
+ expect(result.length).toEqual(10);
await tx.rollback();
} catch (e) {
diff --git a/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js b/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js
index e5c06b6dd..7dc1c8ed2 100644
--- a/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js
@@ -42,7 +42,7 @@ describe('sale priceDifference()', () => {
try {
const options = {transaction: tx};
- const ctx = {req: {accessToken: {userId: 1106}}};
+ const ctx = {req: {accessToken: {userId: 1105}}};
ctx.args = {
id: 1,
landed: Date.vnNew(),
@@ -84,7 +84,7 @@ describe('sale priceDifference()', () => {
const {items} = await models.Ticket.priceDifference(ctx, options);
- expect(items[0].movable).toEqual(410);
+ expect(items[0].movable).toEqual(386);
expect(items[1].movable).toEqual(1810);
await tx.rollback();
diff --git a/modules/ticket/back/methods/ticket/specs/state.spec.js b/modules/ticket/back/methods/ticket/specs/state.spec.js
index 947e72b79..d908aa2ef 100644
--- a/modules/ticket/back/methods/ticket/specs/state.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/state.spec.js
@@ -7,6 +7,7 @@ describe('ticket state()', () => {
const productionId = 49;
const activeCtx = {
accessToken: {userId: 9},
+ __: value => value
};
const ctx = {req: activeCtx};
const now = Date.vnNew();
@@ -88,7 +89,8 @@ describe('ticket state()', () => {
const ticket = await models.Ticket.create(sampleTicket, options);
activeCtx.accessToken.userId = productionId;
- const params = {ticketFk: ticket.id, stateFk: 3};
+ const stateOk = await models.State.findOne({where: {code: 'OK'}}, options);
+ const params = {ticketFk: ticket.id, stateFk: stateOk.id};
const ticketTracking = await models.Ticket.state(ctx, params, options);
@@ -112,16 +114,68 @@ describe('ticket state()', () => {
const options = {transaction: tx};
const ticket = await models.Ticket.create(sampleTicket, options);
- const ctx = {req: {accessToken: {userId: 18}}};
+ activeCtx.accessToken.userId = salesPersonId;
const assignedState = await models.State.findOne({where: {code: 'PICKER_DESIGNED'}}, options);
- const params = {ticketFk: ticket.id, stateFk: assignedState.id, userFk: 1};
- const res = await models.Ticket.state(ctx, params, options);
+ const paramsAssigned = {ticketFk: ticket.id, stateFk: assignedState.id, userFk: 1};
+ const resAssigned = await models.Ticket.state(ctx, paramsAssigned, options);
- expect(res.ticketFk).toBe(params.ticketFk);
- expect(res.stateFk).toBe(params.stateFk);
- expect(res.userFk).toBe(params.userFk);
- expect(res.userFk).toBe(1);
- expect(res.id).toBeDefined();
+ expect(resAssigned.ticketFk).toBe(paramsAssigned.ticketFk);
+ expect(resAssigned.stateFk).toBe(paramsAssigned.stateFk);
+ expect(resAssigned.userFk).toBe(paramsAssigned.userFk);
+ expect(resAssigned.userFk).toBe(1);
+ expect(resAssigned.id).toBeDefined();
+
+ activeCtx.accessToken.userId = productionId;
+ const packedState = await models.State.findOne({where: {code: 'PACKED'}}, options);
+ const paramsPacked = {ticketFk: ticket.id, stateFk: packedState.id, userFk: salesPersonId};
+ const resPacked = await models.Ticket.state(ctx, paramsPacked, options);
+
+ expect(resPacked.stateFk).toBe(paramsPacked.stateFk);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ it('Should equalize the quantities of quantity and originalQuantity' +
+ ' if they are different', async() => {
+ const tx = await models.TicketTracking.beginTransaction({});
+
+ try {
+ const options = {transaction: tx};
+
+ const ticket = await models.Ticket.create(sampleTicket, options);
+ activeCtx.accessToken.userId = salesPersonId;
+
+ const sampleSale = {
+ ticketFk: ticket.id,
+ itemFk: 1,
+ concept: 'Test',
+ quantity: 10,
+ originalQuantity: 6
+ };
+ await models.Sale.create(sampleSale, options);
+ const assignedState = await models.State.findOne({where: {code: 'PICKER_DESIGNED'}}, options);
+ const paramsAssigned = {ticketFk: ticket.id, stateFk: assignedState.id, userFk: 1};
+ const resAssigned = await models.Ticket.state(ctx, paramsAssigned, options);
+
+ expect(resAssigned.ticketFk).toBe(paramsAssigned.ticketFk);
+ expect(resAssigned.stateFk).toBe(paramsAssigned.stateFk);
+ expect(resAssigned.userFk).toBe(paramsAssigned.userFk);
+ expect(resAssigned.userFk).toBe(1);
+ expect(resAssigned.id).toBeDefined();
+
+ activeCtx.accessToken.userId = productionId;
+ const packedState = await models.State.findOne({where: {code: 'PACKED'}}, options);
+ const paramsPacked = {ticketFk: ticket.id, stateFk: packedState.id, userFk: salesPersonId};
+ const resPacked = await models.Ticket.state(ctx, paramsPacked, options);
+
+ const sale = await models.Sale.findOne({where: {ticketFk: ticket.id}}, options);
+
+ expect(resPacked.stateFk).toBe(paramsPacked.stateFk);
+ expect(sale.quantity).toBe(sale.originalQuantity);
await tx.rollback();
} catch (e) {
diff --git a/modules/ticket/back/methods/ticket/state.js b/modules/ticket/back/methods/ticket/state.js
index fea9475f8..9b0b862f2 100644
--- a/modules/ticket/back/methods/ticket/state.js
+++ b/modules/ticket/back/methods/ticket/state.js
@@ -64,7 +64,63 @@ module.exports = Self => {
if ((ticketState && !oldStateAllowed) || !newStateAllowed)
throw new UserError(`You don't have enough privileges`, 'ACCESS_DENIED');
- await Self.rawSql(`CALL vn.ticket_setState(?, ?)`, [params.ticketFk, params.code], myOptions);
+ const ticket = await models.Ticket.findById(params.ticketFk, {
+ include: [{
+ relation: 'client',
+ scope: {
+ fields: ['salesPersonFk']
+ }
+ }],
+ fields: ['id', 'clientFk']
+ }, myOptions);
+
+ const salesPersonFk = ticket.client().salesPersonFk;
+ if (salesPersonFk) {
+ const sales = await Self.rawSql(`
+ SELECT DISTINCT s.id,
+ s.itemFk,
+ s.concept,
+ s.originalQuantity AS oldQuantity,
+ s.quantity AS newQuantity
+ FROM vn.sale s
+ JOIN vn.saleTracking st ON st.saleFk = s.id
+ JOIN vn.ticket t ON t.id = s.ticketFk
+ JOIN vn.client c ON c.id = t.clientFk
+ JOIN vn.ticketState ts ON ts.ticketFk = t.id
+ JOIN vn.state s2 ON s2.id = ts.stateFk
+ WHERE s.ticketFk = ?
+ AND st.isChecked
+ AND s.originalQuantity IS NOT NULL
+ AND s.originalQuantity <> s.quantity
+ AND s2.\`order\` < (SELECT \`order\` FROM vn.state WHERE code = 'CHECKED')
+ ORDER BY st.created DESC
+ `, [params.ticketFk], myOptions);
+
+ let changes = '';
+ const url = await models.Url.getUrl();
+ const $t = ctx.req.__;
+ for (let sale of sales) {
+ changes += `\r\n-` + $t('Changes in sales', {
+ itemId: sale.itemFk,
+ concept: sale.concept,
+ oldQuantity: sale.oldQuantity,
+ newQuantity: sale.newQuantity,
+ itemUrl: `${url}item/${sale.itemFk}/summary`
+ });
+ const currentSale = await models.Sale.findById(sale.id, null, myOptions);
+ await currentSale.updateAttributes({
+ originalQuantity: currentSale.quantity
+ }, myOptions);
+ }
+
+ const message = $t('Changed sale quantity', {
+ ticketId: ticket.id,
+ changes: changes,
+ ticketUrl: `${url}ticket/${ticket.id}/sale`
+ });
+ await models.Chat.sendCheckingPresence(ctx, salesPersonFk, message, myOptions);
+ }
+ await Self.rawSql(`CALL vn.ticket_setState(?, ?)`, [ticket.id, params.code], myOptions);
const ticketTracking = await models.TicketTracking.findOne({
where: {ticketFk: params.ticketFk},
diff --git a/modules/ticket/back/models/saleGroup.json b/modules/ticket/back/models/saleGroup.json
index d5cf82cb5..aa78b4167 100644
--- a/modules/ticket/back/models/saleGroup.json
+++ b/modules/ticket/back/models/saleGroup.json
@@ -14,6 +14,9 @@
},
"parkingFk": {
"type": "number"
+ },
+ "ticketFk": {
+ "type": "number"
}
},
"relations": {
diff --git a/modules/ticket/back/models/specs/sale.spec.js b/modules/ticket/back/models/specs/sale.spec.js
index d078dc8e2..1aa40802b 100644
--- a/modules/ticket/back/models/specs/sale.spec.js
+++ b/modules/ticket/back/models/specs/sale.spec.js
@@ -1,361 +1,318 @@
/* eslint max-len: ["error", { "code": 150 }]*/
-
-const models = require('vn-loopback/server/server').models;
+const {models} = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
describe('sale model ', () => {
- const ctx = {
- req: {
- accessToken: {userId: 9},
- headers: {origin: 'localhost:5000'},
- __: () => {}
- }
- };
- function getActiveCtx(userId) {
- return {
- active: {
- accessToken: {userId},
- http: {
- req: {
- headers: {origin: 'http://localhost'}
- }
- }
- }
- };
+ const developerId = 9;
+ const buyerId = 35;
+ const employeeId = 1;
+ const productionId = 49;
+ const salesPersonId = 18;
+ const reviewerId = 130;
+
+ const barcode = '4444444444';
+ const ticketCollectionProd = 34;
+ const ticketCollectionSalesPerson = 35;
+ const previaTicketSalesPerson = 36;
+ const previaTicketProd = 37;
+ const notEditableError = 'This ticket is not editable.';
+
+ const ctx = getCtx(developerId);
+ let tx;
+ let opts;
+
+ function getCtx(userId, active = false) {
+ const accessToken = {userId};
+ const headers = {origin: 'localhost:5000'};
+ if (!active) return {req: {accessToken, headers, __: () => {}}};
+ return {active: {accessToken, http: {req: {headers}}}};
}
+ beforeEach(async() => {
+ tx = await models.Sale.beginTransaction({});
+ opts = {transaction: tx};
+ });
+
+ afterEach(async() => await tx.rollback());
+
describe('quantity field ', () => {
it('should add quantity if the quantity is greater than it should be and is role advanced', async() => {
const saleId = 17;
- const buyerId = 35;
- const ctx = {
- req: {
- accessToken: {userId: buyerId},
- headers: {origin: 'localhost:5000'},
- __: () => {}
- }
- };
- const tx = await models.Sale.beginTransaction({});
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(buyerId));
- spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
+ const ctx = getCtx(buyerId);
+
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(buyerId, true));
+ spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => {
if (sqlStatement.includes('catalog_calcFromItem')) {
sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
SELECT 100 as available;`;
params = null;
}
- return models.Ticket.rawSql(sqlStatement, params, options);
+ return models.Ticket.rawSql(sqlStatement, params, opts);
});
- try {
- const options = {transaction: tx};
+ const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*');
- const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*');
+ expect(isRoleAdvanced).toEqual(true);
- expect(isRoleAdvanced).toEqual(true);
+ const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts);
- const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
+ expect(originalLine.quantity).toEqual(30);
- expect(originalLine.quantity).toEqual(30);
+ const newQuantity = originalLine.quantity + 1;
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
- const newQuantity = originalLine.quantity + 1;
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
+ const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts);
- const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
-
- expect(modifiedLine.quantity).toEqual(newQuantity);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
+ expect(modifiedLine.quantity).toEqual(newQuantity);
});
it('should update the quantity of a given sale current line', async() => {
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(9));
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(developerId, true));
- const tx = await models.Sale.beginTransaction({});
const saleId = 25;
const newQuantity = 4;
- try {
- const options = {transaction: tx};
+ const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts);
- const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
+ expect(originalLine.quantity).toEqual(20);
- expect(originalLine.quantity).toEqual(20);
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
+ const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts);
- const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
-
- expect(modifiedLine.quantity).toEqual(newQuantity);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
+ expect(modifiedLine.quantity).toEqual(newQuantity);
});
it('should throw an error if the quantity is negative and it is not a refund ticket', async() => {
- const ctx = {
- req: {
- accessToken: {userId: 1},
- headers: {origin: 'localhost:5000'},
- __: () => {}
- }
- };
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true));
const saleId = 17;
const newQuantity = -10;
- const tx = await models.Sale.beginTransaction({});
-
- let error;
try {
- const options = {transaction: tx};
-
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
-
- await tx.rollback();
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
} catch (e) {
- await tx.rollback();
- error = e;
+ expect(e).toEqual(new Error('You can only add negative amounts in refund tickets'));
}
-
- expect(error).toEqual(new Error('You can only add negative amounts in refund tickets'));
});
it('should update a negative quantity when is a ticket refund', async() => {
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(9));
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(developerId, true));
- const tx = await models.Sale.beginTransaction({});
const saleId = 32;
const newQuantity = -10;
- try {
- const options = {transaction: tx};
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
+ const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts);
- const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
-
- expect(modifiedLine.quantity).toEqual(newQuantity);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
+ expect(modifiedLine.quantity).toEqual(newQuantity);
});
it('should throw an error if the quantity is less than the minimum quantity of the item', async() => {
- const ctx = {
- req: {
- accessToken: {userId: 1},
- headers: {origin: 'localhost:5000'},
- __: () => {}
- }
- };
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true));
- const tx = await models.Sale.beginTransaction({});
const itemId = 2;
const saleId = 17;
const minQuantity = 30;
const newQuantity = minQuantity - 1;
- let error;
try {
- const options = {transaction: tx};
-
- const item = await models.Item.findById(itemId, null, options);
- await item.updateAttribute('minQuantity', minQuantity, options);
- spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
+ const item = await models.Item.findById(itemId, null, opts);
+ await item.updateAttribute('minQuantity', minQuantity, opts);
+ spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => {
if (sqlStatement.includes('catalog_calcFromItem')) {
sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
SELECT 100 as available;`;
params = null;
}
- return models.Ticket.rawSql(sqlStatement, params, options);
+ return models.Ticket.rawSql(sqlStatement, params, opts);
});
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
-
- await tx.rollback();
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
} catch (e) {
- await tx.rollback();
- error = e;
+ expect(e).toEqual(new Error('The amount cannot be less than the minimum'));
}
-
- expect(error).toEqual(new Error('The amount cannot be less than the minimum'));
});
it('should change quantity if has minimum quantity and new quantity is equal than item available', async() => {
- const ctx = {
- req: {
- accessToken: {userId: 1},
- headers: {origin: 'localhost:5000'},
- __: () => {}
- }
- };
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true));
- const tx = await models.Sale.beginTransaction({});
const itemId = 2;
const saleId = 17;
const minQuantity = 30;
const newQuantity = minQuantity - 1;
- try {
- const options = {transaction: tx};
+ const item = await models.Item.findById(itemId, null, opts);
+ await item.updateAttribute('minQuantity', minQuantity, opts);
- const item = await models.Item.findById(itemId, null, options);
- await item.updateAttribute('minQuantity', minQuantity, options);
-
- spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
- if (sqlStatement.includes('catalog_calcFromItem')) {
- sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
+ spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => {
+ if (sqlStatement.includes('catalog_calcFromItem')) {
+ sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
SELECT ${newQuantity} as available;`;
- params = null;
- }
- return models.Ticket.rawSql(sqlStatement, params, options);
- });
+ params = null;
+ }
+ return models.Ticket.rawSql(sqlStatement, params, opts);
+ });
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
});
describe('newPrice', () => {
it('should increase quantity if you have enough available and the new price is the same as the previous one', async() => {
- const ctx = {
- req: {
- accessToken: {userId: 1},
- headers: {origin: 'localhost:5000'},
- __: () => {}
- }
- };
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true));
- const tx = await models.Sale.beginTransaction({});
const itemId = 2;
const saleId = 17;
const minQuantity = 30;
const newQuantity = 31;
- try {
- const options = {transaction: tx};
-
- const item = await models.Item.findById(itemId, null, options);
- await item.updateAttribute('minQuantity', minQuantity, options);
- spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
- if (sqlStatement.includes('catalog_calcFromItem')) {
- sqlStatement = `
+ const item = await models.Item.findById(itemId, null, opts);
+ await item.updateAttribute('minQuantity', minQuantity, opts);
+ spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => {
+ if (sqlStatement.includes('catalog_calcFromItem')) {
+ sqlStatement = `
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available;
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 7.07 as price;`;
- params = null;
- }
- return models.Ticket.rawSql(sqlStatement, params, options);
- });
+ params = null;
+ }
+ return models.Ticket.rawSql(sqlStatement, params, opts);
+ });
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
});
it('should increase quantity when the new price is lower than the previous one', async() => {
- const ctx = {
- req: {
- accessToken: {userId: 1},
- headers: {origin: 'localhost:5000'},
- __: () => {}
- }
- };
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true));
- const tx = await models.Sale.beginTransaction({});
const itemId = 2;
const saleId = 17;
const minQuantity = 30;
const newQuantity = 31;
- try {
- const options = {transaction: tx};
-
- const item = await models.Item.findById(itemId, null, options);
- await item.updateAttribute('minQuantity', minQuantity, options);
- spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
- if (sqlStatement.includes('catalog_calcFromItem')) {
- sqlStatement = `
+ const item = await models.Item.findById(itemId, null, opts);
+ await item.updateAttribute('minQuantity', minQuantity, opts);
+ spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => {
+ if (sqlStatement.includes('catalog_calcFromItem')) {
+ sqlStatement = `
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available;
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 1 as price;`;
- params = null;
- }
- return models.Ticket.rawSql(sqlStatement, params, options);
- });
+ params = null;
+ }
+ return models.Ticket.rawSql(sqlStatement, params, opts);
+ });
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
});
it('should throw error when increase quantity and the new price is higher than the previous one', async() => {
- const ctx = {
- req: {
- accessToken: {userId: 1},
- headers: {origin: 'localhost:5000'},
- __: () => {}
- }
- };
- spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true));
- const tx = await models.Sale.beginTransaction({});
const itemId = 2;
const saleId = 17;
const minQuantity = 30;
const newQuantity = 31;
- let error;
try {
- const options = {transaction: tx};
-
- const item = await models.Item.findById(itemId, null, options);
- await item.updateAttribute('minQuantity', minQuantity, options);
- spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
+ const item = await models.Item.findById(itemId, null, opts);
+ await item.updateAttribute('minQuantity', minQuantity, opts);
+ spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => {
if (sqlStatement.includes('catalog_calcFromItem')) {
sqlStatement = `
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available;
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 100000 as price;`;
params = null;
}
- return models.Ticket.rawSql(sqlStatement, params, options);
+ return models.Ticket.rawSql(sqlStatement, params, opts);
});
- await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
-
- await tx.rollback();
+ await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
} catch (e) {
- await tx.rollback();
- error = e;
+ expect(e).toEqual(new Error('The price of the item changed'));
}
-
- expect(error).toEqual(new Error('The price of the item changed'));
});
});
});
+
+ describe('add a sale from a collection or previa ticket', () => {
+ it('if is allocated to them and alert level higher than 0 as Production role', async() => {
+ const ctx = getCtx(productionId);
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(productionId, true));
+
+ const beforeSalesCollection = await models.Sale.count({ticketFk: ticketCollectionProd}, opts);
+ await models.Ticket.addSale(ctx, ticketCollectionProd, barcode, 20, opts);
+ const afterSalesCollection = await models.Sale.count({ticketFk: ticketCollectionProd}, opts);
+
+ expect(afterSalesCollection).toEqual(beforeSalesCollection + 1);
+
+ const beforeSalesPrevia = await models.Sale.count({ticketFk: previaTicketProd}, opts);
+ await models.Ticket.addSale(ctx, previaTicketProd, barcode, 20, opts);
+ const afterSalesPrevia = await models.Sale.count({ticketFk: previaTicketProd}, opts);
+
+ expect(afterSalesPrevia).toEqual(beforeSalesPrevia + 1);
+ });
+
+ it('should throw an error if is not allocated to them and alert level higher than 0 as Production role', async() => {
+ const ctx = getCtx(productionId);
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(productionId, true));
+
+ try {
+ await models.Ticket.addSale(ctx, ticketCollectionSalesPerson, barcode, 20, opts);
+ } catch ({message}) {
+ expect(message).toEqual(notEditableError);
+ }
+
+ try {
+ await models.Ticket.addSale(ctx, previaTicketSalesPerson, barcode, 20, opts);
+ } catch ({message}) {
+ expect(message).toEqual(notEditableError);
+ }
+ });
+
+ it('if is allocated to them and alert level higher than 0 as salesPerson role', async() => {
+ const ctx = getCtx(salesPersonId);
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(salesPersonId, true));
+
+ const beforeSalesCollection = await models.Sale.count({ticketFk: ticketCollectionSalesPerson}, opts);
+ await models.Ticket.addSale(ctx, ticketCollectionSalesPerson, barcode, 20, opts);
+ const afterSalesCollection = await models.Sale.count({ticketFk: ticketCollectionSalesPerson}, opts);
+
+ expect(afterSalesCollection).toEqual(beforeSalesCollection + 1);
+
+ const beforeSalesPrevia = await models.Sale.count({ticketFk: previaTicketSalesPerson}, opts);
+ await models.Ticket.addSale(ctx, previaTicketSalesPerson, barcode, 20, opts);
+ const afterSalesPrevia = await models.Sale.count({ticketFk: previaTicketSalesPerson}, opts);
+
+ expect(afterSalesPrevia).toEqual(beforeSalesPrevia + 1);
+ });
+
+ it('should throw an error if is not allocated to them and alert level higher than 0 as salesPerson role', async() => {
+ const ctx = getCtx(salesPersonId);
+
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(salesPersonId, true));
+
+ try {
+ await models.Ticket.addSale(ctx, ticketCollectionProd, barcode, 20, opts);
+ } catch ({message}) {
+ expect(message).toEqual(notEditableError);
+ }
+ });
+
+ it('if is a reviewer', async() => {
+ const ctx = getCtx(reviewerId);
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(reviewerId, true));
+
+ const beforeSalesCollection = await models.Sale.count({ticketFk: ticketCollectionSalesPerson}, opts);
+ await models.Ticket.addSale(ctx, ticketCollectionSalesPerson, barcode, 20, opts);
+ const afterSalesCollection = await models.Sale.count({ticketFk: ticketCollectionSalesPerson}, opts);
+
+ expect(afterSalesCollection).toEqual(beforeSalesCollection + 1);
+
+ const beforeSalesPrevia = await models.Sale.count({ticketFk: previaTicketSalesPerson}, opts);
+ await models.Ticket.addSale(ctx, previaTicketSalesPerson, barcode, 20, opts);
+ const afterSalesPrevia = await models.Sale.count({ticketFk: previaTicketSalesPerson}, opts);
+
+ expect(afterSalesPrevia).toEqual(beforeSalesPrevia + 1);
+ });
+ });
});
diff --git a/modules/ticket/back/models/ticket-methods.js b/modules/ticket/back/models/ticket-methods.js
index 0ae2ce3b4..5582dde5c 100644
--- a/modules/ticket/back/models/ticket-methods.js
+++ b/modules/ticket/back/models/ticket-methods.js
@@ -46,6 +46,5 @@ module.exports = function(Self) {
require('../methods/ticket/invoiceTicketsAndPdf')(Self);
require('../methods/ticket/docuwareDownload')(Self);
require('../methods/ticket/myLastModified')(Self);
- require('../methods/ticket/addSaleByCode')(Self);
require('../methods/ticket/clone')(Self);
};
diff --git a/modules/ticket/front/card/index.js b/modules/ticket/front/card/index.js
index fa4ad4e39..34ab109c5 100644
--- a/modules/ticket/front/card/index.js
+++ b/modules/ticket/front/card/index.js
@@ -1,5 +1,6 @@
import ngModule from '../module';
import ModuleCard from 'salix/components/module-card';
+import UserError from 'core/lib/user-error';
class Controller extends ModuleCard {
reload() {
@@ -59,6 +60,10 @@ class Controller extends ModuleCard {
],
};
+ if (!this.$params.id) {
+ this.$state.go('ticket.index');
+ throw new UserError(`You must select a ticket`);
+ }
return this.$http.get(`Tickets/${this.$params.id}`, {filter})
.then(res => this.onData(res.data));
}
diff --git a/modules/ticket/front/descriptor-menu/index.html b/modules/ticket/front/descriptor-menu/index.html
index cb7eeb8ee..3583b1202 100644
--- a/modules/ticket/front/descriptor-menu/index.html
+++ b/modules/ticket/front/descriptor-menu/index.html
@@ -152,7 +152,7 @@
diff --git a/modules/ticket/front/locale/es.yml b/modules/ticket/front/locale/es.yml
index 748ba210f..2f448c034 100644
--- a/modules/ticket/front/locale/es.yml
+++ b/modules/ticket/front/locale/es.yml
@@ -64,6 +64,7 @@ You are going to delete this ticket: Vas a eliminar este ticket
Ticket deleted. You can undo this action within the first hour: Ticket eliminado. Puedes deshacer esta acción durante la primera hora
Search ticket by id or alias: Buscar tickets por identificador o alias
ticket: ticket
+You must select a ticket: Debes seleccionar un ticket
#sections
List: Listado
diff --git a/modules/ticket/front/sale/index.js b/modules/ticket/front/sale/index.js
index 1cd5560a4..7ff8d89e3 100644
--- a/modules/ticket/front/sale/index.js
+++ b/modules/ticket/front/sale/index.js
@@ -476,7 +476,7 @@ class Controller extends Section {
*/
addSale(sale) {
const data = {
- itemId: sale.itemFk,
+ barcode: sale.itemFk,
quantity: sale.quantity
};
const query = `tickets/${this.ticket.id}/addSale`;
diff --git a/modules/ticket/front/sale/index.spec.js b/modules/ticket/front/sale/index.spec.js
index fb1c925d4..8200d6b89 100644
--- a/modules/ticket/front/sale/index.spec.js
+++ b/modules/ticket/front/sale/index.spec.js
@@ -659,7 +659,7 @@ describe('Ticket', () => {
jest.spyOn(controller, 'resetChanges').mockReturnThis();
const newSale = {itemFk: 4, quantity: 10};
- const expectedParams = {itemId: 4, quantity: 10};
+ const expectedParams = {barcode: 4, quantity: 10};
const expectedResult = {
id: 30,
quantity: 10,
diff --git a/modules/worker/back/models/operator.js b/modules/worker/back/models/operator.js
index d46f3d934..70e20af5b 100644
--- a/modules/worker/back/models/operator.js
+++ b/modules/worker/back/models/operator.js
@@ -20,7 +20,7 @@ module.exports = Self => {
const notifications = await models.NotificationQueue.find(
{where: {created: {gte: new Date(Date.vnNow() - (backupPrinterNotificationDelay * 1000))},
notificationFk: notificationName,
- status: 'sent'
+ status: {neq: 'error'}
}
});
diff --git a/modules/worker/front/basic-data/index.html b/modules/worker/front/basic-data/index.html
index aa3f6ca79..bece1b6fd 100644
--- a/modules/worker/front/basic-data/index.html
+++ b/modules/worker/front/basic-data/index.html
@@ -54,8 +54,8 @@
diff --git a/modules/zone/back/methods/zone/specs/deleteZone.spec.js b/modules/zone/back/methods/zone/specs/deleteZone.spec.js
index 968685fec..08dafd181 100644
--- a/modules/zone/back/methods/zone/specs/deleteZone.spec.js
+++ b/modules/zone/back/methods/zone/specs/deleteZone.spec.js
@@ -5,6 +5,7 @@ describe('zone deletezone()', () => {
const userId = 9;
const activeCtx = {
accessToken: {userId: userId},
+ __: value => value
};
const ctx = {req: activeCtx};
const zoneId = 9;
@@ -15,19 +16,15 @@ describe('zone deletezone()', () => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
- try {
- const originalTickets = await models.Ticket.find({
- where: {
- zoneFk: zoneId
- }
- });
- ticketIDs = originalTickets.map(ticket => ticket.id);
- originalTicketStates = await models.TicketState.find({where: {
- ticketFk: {inq: ticketIDs},
- code: 'FIXING'}});
- } catch (error) {
- console.error(error);
- }
+ const originalTickets = await models.Ticket.find({
+ where: {
+ zoneFk: zoneId
+ }
+ });
+ ticketIDs = originalTickets.map(ticket => ticket.id);
+ originalTicketStates = await models.TicketState.find({where: {
+ ticketFk: {inq: ticketIDs},
+ code: 'FIXING'}});
});
it('should delete a zone and update their tickets', async() => {
diff --git a/package.json b/package.json
index fc8b709c9..be361ce7b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "salix-back",
- "version": "24.24.1",
+ "version": "24.24.3",
"author": "Verdnatura Levante SL",
"description": "Salix backend",
"license": "GPL-3.0",